Files
kamidso-back/tests/kamidso/test_skill_settings.py
javadandCursor b93ecf1691 Initial commit of the Kamidso API.
Keep source, config, and deploy files. Leave runtime data, uploads, and logs out of git.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 13:43:09 +03:30

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"]