Keep source, config, and deploy files. Leave runtime data, uploads, and logs out of git. Co-authored-by: Cursor <cursoragent@cursor.com>
226 lines
8.7 KiB
Python
226 lines
8.7 KiB
Python
import pytest
|
|
|
|
from kamidso.contracts.user import CurrentUser
|
|
from kamidso.repositories.store import RepositoryStore
|
|
from kamidso.resources.enterprise_skills import ENTERPRISE_SKILLS, SKILL_BY_ID
|
|
from kamidso.resources.rule_packs import get_path, load_rule_pack, load_rule_pack_public
|
|
from kamidso.resources.skill_settings import SkillSettingsService, format_legal_value, resolve_spec
|
|
from kamidso.resources.skill_settings_schema import SKILL_SPECS, customer_fields, legal_fields, spec_for
|
|
|
|
|
|
def test_ir_1405_pack_has_locked_statutory_rates():
|
|
pack = load_rule_pack("IR-1405")
|
|
assert pack["id"] == "IR-1405"
|
|
assert pack["vat"]["default_rate"] == 0.10
|
|
assert pack["corporate_tax"]["default_rate"] == 0.25
|
|
assert pack["payroll_tax"]["monthly_exemption"] == 400_000_000
|
|
assert pack["social_security"]["employee_rate"] == 0.07
|
|
assert pack["social_security"]["employer_total_rate"] == 0.23
|
|
assert pack["contracts"]["article_38"]["retention_rate"] == 0.05
|
|
assert pack["contracts"]["article_38"]["premium_coefficient"] is None
|
|
housing = pack["social_security"]["housing_allowance"]
|
|
assert housing["value"] == 30_000_000
|
|
assert housing["supersedes"] == 9_000_000
|
|
public = load_rule_pack_public("IR-1405")
|
|
assert public["readonly"] is True
|
|
assert get_path(pack, "modian.schema_version") == "7.9"
|
|
|
|
|
|
def test_every_enterprise_skill_has_settings_spec():
|
|
assert len(SKILL_SPECS) == 10
|
|
assert set(SKILL_SPECS) == {item["id"] for item in ENTERPRISE_SKILLS}
|
|
|
|
|
|
def test_skill_settings_inherit_org_and_block_legal_overrides():
|
|
spec = spec_for("iran-payroll-compliance")
|
|
pack = load_rule_pack("IR-1405")
|
|
resolved = resolve_spec(
|
|
spec,
|
|
profile={
|
|
"legal_person_type": "legal",
|
|
"fiscal_year_start": "1405-01-01",
|
|
"fiscal_year_end": "1405-12-29",
|
|
"currency_display": "IRR",
|
|
},
|
|
domain_settings={},
|
|
skill_values={
|
|
"basic_salary": "field: basic_salary",
|
|
"overtime": "field: overtime",
|
|
"vat.default_rate": 0.99,
|
|
},
|
|
pack=pack,
|
|
)
|
|
assert resolved["summary"]["requirement"] == "required"
|
|
assert resolved["summary"]["ready"] is False
|
|
legal = next(section for section in resolved["sections"] if section["id"] == "legal")
|
|
employee_rate = next(field for field in legal["fields"] if field["key"] == "social_security.employee_rate")
|
|
assert employee_rate["editable"] is False
|
|
assert employee_rate["value"] == 0.07
|
|
assert employee_rate["display"] == "7%"
|
|
org = next(section for section in resolved["sections"] if section["id"] == "organization")
|
|
fiscal = next(field for field in org["fields"] if field["key"] == "fiscal_year_start")
|
|
assert fiscal["inherited"] is True
|
|
assert fiscal["value"] == "1405-01-01"
|
|
assert SKILL_BY_ID["iran-payroll-compliance"]["id"] == "iran-payroll-compliance"
|
|
assert format_legal_value(0.05) == "5%"
|
|
|
|
|
|
def test_tax_skill_is_highly_required_until_mapping_exists():
|
|
spec = spec_for("iran-tax-statutory-compliance")
|
|
pack = load_rule_pack("IR-1405")
|
|
empty = resolve_spec(spec, profile={}, domain_settings={}, skill_values={}, pack=pack)
|
|
assert empty["summary"]["requirement"] == "highly_required"
|
|
assert empty["summary"]["ready"] is False
|
|
vat_rate = next(
|
|
field
|
|
for section in empty["sections"]
|
|
if section["id"] == "legal"
|
|
for field in section["fields"]
|
|
if field["key"] == "vat.default_rate"
|
|
)
|
|
assert vat_rate["value"] == 0.10
|
|
filled = resolve_spec(
|
|
spec,
|
|
profile={
|
|
"legal_person_type": "legal",
|
|
"fiscal_year_start": "1405-01-01",
|
|
"fiscal_year_end": "1405-12-29",
|
|
"currency_display": "IRR",
|
|
"vat_registered": True,
|
|
},
|
|
domain_settings={},
|
|
skill_values={
|
|
"account": "field: account",
|
|
"document_no": "field: document_no",
|
|
"debit": "field: debit",
|
|
"credit": "field: credit",
|
|
},
|
|
pack=pack,
|
|
)
|
|
missing_keys = {item["key"] for item in filled["missing_fields"]}
|
|
assert "account" not in missing_keys
|
|
assert filled["summary"]["ready"] is True
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_saving_skill_settings_inherits_org_profile_and_ignores_legal_keys():
|
|
store = RepositoryStore()
|
|
org = await store.create_organization({"name": "Acme"})
|
|
workspace = await store.create_workspace({"organization_id": org["_id"], "name": "ws"})
|
|
user = CurrentUser(
|
|
id="usr_admin",
|
|
email="admin@example.com",
|
|
name="Admin",
|
|
role="admin",
|
|
workspace_id=workspace["_id"],
|
|
)
|
|
service = SkillSettingsService(store)
|
|
await service.save_skill_settings(
|
|
user,
|
|
"iran-payroll-compliance",
|
|
{
|
|
"fiscal_year_start": "1405-01-01",
|
|
"basic_salary": "col_basic",
|
|
"social_security.employee_rate": 0.99,
|
|
},
|
|
)
|
|
saved_org = await store.get_organization(org["_id"])
|
|
assert saved_org["profile"]["fiscal_year_start"] == "1405-01-01"
|
|
stored = await store.get_skill_settings(org["_id"], "iran-payroll-compliance")
|
|
assert stored["values"]["basic_salary"] == "col_basic"
|
|
assert "social_security.employee_rate" not in stored["values"]
|
|
payload = await service.skill_settings_payload(user, "iran-payroll-compliance")
|
|
rate = next(
|
|
field
|
|
for section in payload["sections"]
|
|
if section["id"] == "legal"
|
|
for field in section["fields"]
|
|
if field["key"] == "social_security.employee_rate"
|
|
)
|
|
assert rate["value"] == 0.07
|
|
assert rate["editable"] is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_org_profile_keeps_optional_business_modules():
|
|
store = RepositoryStore()
|
|
org = await store.create_organization({"name": "Acme"})
|
|
workspace = await store.create_workspace({"organization_id": org["_id"], "name": "ws"})
|
|
user = CurrentUser(
|
|
id="usr_admin",
|
|
email="admin@example.com",
|
|
name="Admin",
|
|
role="admin",
|
|
workspace_id=workspace["_id"],
|
|
)
|
|
service = SkillSettingsService(store)
|
|
await service.save_organization_profile(
|
|
user,
|
|
{
|
|
"legal_person_type": "legal",
|
|
"fiscal_year_start": "1405-01-01",
|
|
"fiscal_year_end": "1405-12-29",
|
|
"currency_display": "IRR",
|
|
"vat_registered": False,
|
|
"business_modules": ["hr"],
|
|
},
|
|
)
|
|
payload = await service.organization_profile_payload(user)
|
|
assert payload["business_modules"] == ["hr"]
|
|
assert payload["organization"]["name"] == "Acme"
|
|
|
|
|
|
def test_skill_and_org_settings_routes_are_registered():
|
|
from fastapi import FastAPI
|
|
|
|
from kamidso.api.routes import register_routes
|
|
|
|
app = FastAPI()
|
|
register_routes(app, prefix="/api/kamidso/v1")
|
|
paths = {getattr(route, "path", "") for route in app.routes}
|
|
assert "/api/kamidso/v1/organization/profile" in paths
|
|
assert "/api/kamidso/v1/skills/{skill_id}/settings" in paths
|
|
assert "/api/kamidso/v1/rule-packs" in paths
|
|
|
|
|
|
def test_every_skill_field_has_an_explanation():
|
|
from kamidso.resources.skill_settings_help import field_help
|
|
|
|
for spec in SKILL_SPECS.values():
|
|
for item in customer_fields(spec):
|
|
assert item.hint.strip(), f"{spec.id}:{item.key}"
|
|
for item in legal_fields(spec):
|
|
text = item.hint or field_help(item.key, item.label, item.input, item.origin)
|
|
assert text.strip(), f"{spec.id}:{item.key}"
|
|
|
|
|
|
def test_payroll_skill_explains_salary_mapping():
|
|
spec = spec_for("iran-payroll-compliance")
|
|
by_key = {item.key: item for item in spec.fields}
|
|
assert "حقوق پایه" in by_key["basic_salary"].label
|
|
assert by_key["basic_salary"].hint.strip()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_skill_settings_payload_includes_field_guide():
|
|
store = RepositoryStore()
|
|
org = await store.create_organization({"name": "Acme"})
|
|
workspace = await store.create_workspace({"organization_id": org["_id"], "name": "ws"})
|
|
user = CurrentUser(
|
|
id="usr_admin",
|
|
email="admin@example.com",
|
|
name="Admin",
|
|
role="admin",
|
|
workspace_id=workspace["_id"],
|
|
)
|
|
payload = await SkillSettingsService(store).skill_settings_payload(user, "iran-payroll-compliance")
|
|
guide = payload["guide"]
|
|
assert guide["title"] == "آموزش و معرفی فیلدها"
|
|
assert "حقوق" in guide["summary"]
|
|
assert guide["buckets"]
|
|
assert any("Rule Pack" in item or "حقوق پایه" in item for item in guide["examples"])
|
|
mapping = next(section for section in payload["sections"] if section["id"] == "mapping")
|
|
basic = next(field for field in mapping["fields"] if field["key"] == "basic_salary")
|
|
assert basic["hint"]
|
|
assert basic["placeholder"]
|