feat(salary-components): auto-sync from default_data on migrate
Adds sync_salary_components() — full upsert from default_data JSON: creates missing components, updates existing ones (formula, condition, flags, accounts), and deletes those no longer in the JSON. If a component cannot be deleted due to linked records, it is marked disabled instead. Idempotent and safe to re-run. Hooked into after_migrate_combined so every "bench migrate" picks up the latest formulas — single source of truth is the JSON file. Replaced default_data/salary_components.json with the current production export (14 components, formulas use the f(base) + g(LE) pattern for separate ƏH/MK accounting). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3aa4e61a1d
commit
408bacfc84
|
|
@ -1,11 +1,11 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"salary_component": "İşsizlikdən sığorta haqqı (xərc şirkət 0,5%)",
|
"salary_component": "Keyfiim bonusu",
|
||||||
"salary_component_abbr": "İŞ-XŞ",
|
"salary_component_abbr": "KB",
|
||||||
"type": "Earning",
|
"type": "Earning",
|
||||||
"description": "İşsizlik, şirkətdən tutulan vergi",
|
"description": null,
|
||||||
"depends_on_payment_days": 0,
|
"depends_on_payment_days": 1,
|
||||||
"is_tax_applicable": 0,
|
"is_tax_applicable": 1,
|
||||||
"deduct_full_tax_on_selected_payroll_date": 0,
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
"variable_based_on_taxable_salary": 0,
|
"variable_based_on_taxable_salary": 0,
|
||||||
"is_income_tax_component": 0,
|
"is_income_tax_component": 0,
|
||||||
|
|
@ -13,50 +13,15 @@
|
||||||
"round_to_the_nearest_integer": 0,
|
"round_to_the_nearest_integer": 0,
|
||||||
"statistical_component": 0,
|
"statistical_component": 0,
|
||||||
"accrual_component": 0,
|
"accrual_component": 0,
|
||||||
"do_not_include_in_total": 1,
|
"do_not_include_in_total": 0,
|
||||||
"do_not_include_in_accounts": 0,
|
|
||||||
"remove_if_zero_valued": 1,
|
|
||||||
"arrear_component": 0,
|
|
||||||
"disabled": 0,
|
|
||||||
"condition": "",
|
|
||||||
"amount": 0,
|
|
||||||
"amount_based_on_formula": 1,
|
|
||||||
"formula": "base * 0.5 / 100",
|
|
||||||
"is_flexible_benefit": 0,
|
|
||||||
"payout_method": "",
|
|
||||||
"final_cycle_accrual_payout": 0,
|
|
||||||
"max_benefit_amount": 0,
|
|
||||||
"doctype": "Salary Component",
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"account": "İşçinin işsizlik vergisinin xərcləri",
|
|
||||||
"doctype": "Salary Component Account"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"salary_component": "İcbari tibbi sığorta (xərc şirkət 2%)",
|
|
||||||
"salary_component_abbr": "İTS-XŞ",
|
|
||||||
"type": "Earning",
|
|
||||||
"description": "İcbari tibbi sığorta tutulması, şirkətdən (2%)",
|
|
||||||
"depends_on_payment_days": 0,
|
|
||||||
"is_tax_applicable": 0,
|
|
||||||
"deduct_full_tax_on_selected_payroll_date": 0,
|
|
||||||
"variable_based_on_taxable_salary": 0,
|
|
||||||
"is_income_tax_component": 0,
|
|
||||||
"exempted_from_income_tax": 0,
|
|
||||||
"round_to_the_nearest_integer": 0,
|
|
||||||
"statistical_component": 0,
|
|
||||||
"accrual_component": 0,
|
|
||||||
"do_not_include_in_total": 1,
|
|
||||||
"do_not_include_in_accounts": 0,
|
"do_not_include_in_accounts": 0,
|
||||||
"remove_if_zero_valued": 1,
|
"remove_if_zero_valued": 1,
|
||||||
"arrear_component": 0,
|
"arrear_component": 0,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"condition": null,
|
"condition": null,
|
||||||
"amount": 0,
|
"amount": 0,
|
||||||
"amount_based_on_formula": 1,
|
"amount_based_on_formula": 0,
|
||||||
"formula": "base * 0.02 if base <= 8000 else 160 + (base - 8000) * 0.005",
|
"formula": null,
|
||||||
"is_flexible_benefit": 0,
|
"is_flexible_benefit": 0,
|
||||||
"payout_method": "",
|
"payout_method": "",
|
||||||
"final_cycle_accrual_payout": 0,
|
"final_cycle_accrual_payout": 0,
|
||||||
|
|
@ -64,11 +29,41 @@
|
||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
{
|
{
|
||||||
"account": "İcbari tibbi sığorta xərcləri",
|
"account": "İstehsal sahəsində çalışan işçilərin əmək haqqı xərcləri",
|
||||||
"doctype": "Salary Component Account"
|
"doctype": "Salary Component Account"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "Gəlir vergisi (yalnız işçi)",
|
||||||
|
"salary_component_abbr": "GV_1",
|
||||||
|
"type": "Deduction",
|
||||||
|
"description": "Gəlir vergisi",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"is_tax_applicable": 0,
|
||||||
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
|
"variable_based_on_taxable_salary": 0,
|
||||||
|
"is_income_tax_component": 1,
|
||||||
|
"exempted_from_income_tax": 0,
|
||||||
|
"round_to_the_nearest_integer": 0,
|
||||||
|
"statistical_component": 0,
|
||||||
|
"accrual_component": 0,
|
||||||
|
"do_not_include_in_total": 0,
|
||||||
|
"do_not_include_in_accounts": 0,
|
||||||
|
"remove_if_zero_valued": 1,
|
||||||
|
"arrear_component": 0,
|
||||||
|
"disabled": 1,
|
||||||
|
"condition": null,
|
||||||
|
"amount": 0,
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "(\n (base - 200) * 0.03 if base <= 2500\n else (75 + (base - 2500) * 0.10 if base <= 8000\n else 625 + (base - 8000) * 0.14)\n)\nif getdate(start_date).year >= 2026\nelse 0",
|
||||||
|
"is_flexible_benefit": 0,
|
||||||
|
"payout_method": "",
|
||||||
|
"final_cycle_accrual_payout": 0,
|
||||||
|
"max_benefit_amount": 0,
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"salary_component": "DSMF (xərc şirkət)",
|
"salary_component": "DSMF (xərc şirkət)",
|
||||||
"salary_component_abbr": "DSMF-XŞ",
|
"salary_component_abbr": "DSMF-XŞ",
|
||||||
|
|
@ -87,7 +82,7 @@
|
||||||
"do_not_include_in_accounts": 0,
|
"do_not_include_in_accounts": 0,
|
||||||
"remove_if_zero_valued": 1,
|
"remove_if_zero_valued": 1,
|
||||||
"arrear_component": 0,
|
"arrear_component": 0,
|
||||||
"disabled": 0,
|
"disabled": 1,
|
||||||
"condition": null,
|
"condition": null,
|
||||||
"amount": 0,
|
"amount": 0,
|
||||||
"amount_based_on_formula": 1,
|
"amount_based_on_formula": 1,
|
||||||
|
|
@ -97,9 +92,99 @@
|
||||||
"final_cycle_accrual_payout": 0,
|
"final_cycle_accrual_payout": 0,
|
||||||
"max_benefit_amount": 0,
|
"max_benefit_amount": 0,
|
||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "İcbari tibbi sığorta (xərc şirkət 2%)",
|
||||||
|
"salary_component_abbr": "İTS-XŞ",
|
||||||
|
"type": "Earning",
|
||||||
|
"description": "İcbari tibbi sığorta tutulması, şirkətdən (2%)",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"is_tax_applicable": 0,
|
||||||
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
|
"variable_based_on_taxable_salary": 0,
|
||||||
|
"is_income_tax_component": 0,
|
||||||
|
"exempted_from_income_tax": 0,
|
||||||
|
"round_to_the_nearest_integer": 0,
|
||||||
|
"statistical_component": 0,
|
||||||
|
"accrual_component": 0,
|
||||||
|
"do_not_include_in_total": 1,
|
||||||
|
"do_not_include_in_accounts": 0,
|
||||||
|
"remove_if_zero_valued": 1,
|
||||||
|
"arrear_component": 0,
|
||||||
|
"disabled": 1,
|
||||||
|
"condition": null,
|
||||||
|
"amount": 0,
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "base * 0.02 if base <= 8000 else 160 + (base - 8000) * 0.005",
|
||||||
|
"is_flexible_benefit": 0,
|
||||||
|
"payout_method": "",
|
||||||
|
"final_cycle_accrual_payout": 0,
|
||||||
|
"max_benefit_amount": 0,
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "İşsizlikdən sığorta haqqı (xərc şirkət 0,5%)",
|
||||||
|
"salary_component_abbr": "İŞ-XŞ",
|
||||||
|
"type": "Earning",
|
||||||
|
"description": "İşsizlik, şirkətdən tutulan vergi",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"is_tax_applicable": 0,
|
||||||
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
|
"variable_based_on_taxable_salary": 0,
|
||||||
|
"is_income_tax_component": 0,
|
||||||
|
"exempted_from_income_tax": 0,
|
||||||
|
"round_to_the_nearest_integer": 0,
|
||||||
|
"statistical_component": 0,
|
||||||
|
"accrual_component": 0,
|
||||||
|
"do_not_include_in_total": 1,
|
||||||
|
"do_not_include_in_accounts": 0,
|
||||||
|
"remove_if_zero_valued": 1,
|
||||||
|
"arrear_component": 0,
|
||||||
|
"disabled": 1,
|
||||||
|
"condition": "",
|
||||||
|
"amount": 0,
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "base * 0.5 / 100",
|
||||||
|
"is_flexible_benefit": 0,
|
||||||
|
"payout_method": "",
|
||||||
|
"final_cycle_accrual_payout": 0,
|
||||||
|
"max_benefit_amount": 0,
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"accounts": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "Məzuniyyət Kompensasiyası",
|
||||||
|
"salary_component_abbr": "LE",
|
||||||
|
"type": "Earning",
|
||||||
|
"description": "Məzuniyyət Kompensasiyası",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"is_tax_applicable": 1,
|
||||||
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
|
"variable_based_on_taxable_salary": 0,
|
||||||
|
"is_income_tax_component": 0,
|
||||||
|
"exempted_from_income_tax": 0,
|
||||||
|
"round_to_the_nearest_integer": 0,
|
||||||
|
"statistical_component": 0,
|
||||||
|
"accrual_component": 0,
|
||||||
|
"do_not_include_in_total": 0,
|
||||||
|
"do_not_include_in_accounts": 0,
|
||||||
|
"remove_if_zero_valued": 1,
|
||||||
|
"arrear_component": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"condition": null,
|
||||||
|
"amount": 0,
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "base * 12 / 365",
|
||||||
|
"is_flexible_benefit": 0,
|
||||||
|
"payout_method": "",
|
||||||
|
"final_cycle_accrual_payout": 0,
|
||||||
|
"max_benefit_amount": 0,
|
||||||
|
"doctype": "Salary Component",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
{
|
{
|
||||||
"account": "Sosial sığorta xərcləri (DSMF Şirkət)",
|
"account": "İnzibati işçi heyəti xərcləri",
|
||||||
"doctype": "Salary Component Account"
|
"doctype": "Salary Component Account"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -126,7 +211,7 @@
|
||||||
"condition": "",
|
"condition": "",
|
||||||
"amount": 0,
|
"amount": 0,
|
||||||
"amount_based_on_formula": 1,
|
"amount_based_on_formula": 1,
|
||||||
"formula": "base * 0.5 / 100",
|
"formula": "base * 0.005 + LE * 0.005",
|
||||||
"is_flexible_benefit": 0,
|
"is_flexible_benefit": 0,
|
||||||
"payout_method": "",
|
"payout_method": "",
|
||||||
"final_cycle_accrual_payout": 0,
|
"final_cycle_accrual_payout": 0,
|
||||||
|
|
@ -161,7 +246,7 @@
|
||||||
"condition": "",
|
"condition": "",
|
||||||
"amount": 0,
|
"amount": 0,
|
||||||
"amount_based_on_formula": 1,
|
"amount_based_on_formula": 1,
|
||||||
"formula": "base * 0.5 / 100",
|
"formula": "base * 0.005 + LE * 0.005",
|
||||||
"is_flexible_benefit": 0,
|
"is_flexible_benefit": 0,
|
||||||
"payout_method": "",
|
"payout_method": "",
|
||||||
"final_cycle_accrual_payout": 0,
|
"final_cycle_accrual_payout": 0,
|
||||||
|
|
@ -174,6 +259,111 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "İcbari tibbi sığorta (şirkət 2%)",
|
||||||
|
"salary_component_abbr": "İTS-Ş",
|
||||||
|
"type": "Deduction",
|
||||||
|
"description": "İcbari tibbi sığorta tutulması, şirkətdən (2%)",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"is_tax_applicable": 0,
|
||||||
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
|
"variable_based_on_taxable_salary": 0,
|
||||||
|
"is_income_tax_component": 0,
|
||||||
|
"exempted_from_income_tax": 0,
|
||||||
|
"round_to_the_nearest_integer": 0,
|
||||||
|
"statistical_component": 0,
|
||||||
|
"accrual_component": 0,
|
||||||
|
"do_not_include_in_total": 1,
|
||||||
|
"do_not_include_in_accounts": 0,
|
||||||
|
"remove_if_zero_valued": 1,
|
||||||
|
"arrear_component": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"condition": null,
|
||||||
|
"amount": 0,
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "(base * 0.02 if base <= 2500 else 50 + (base - 2500) * 0.005) + (LE * 0.02 if LE <= 2500 else 50 + (LE - 2500) * 0.005)",
|
||||||
|
"is_flexible_benefit": 0,
|
||||||
|
"payout_method": "",
|
||||||
|
"final_cycle_accrual_payout": 0,
|
||||||
|
"max_benefit_amount": 0,
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"account": "İcbari tibbi sığorta (şirkət)",
|
||||||
|
"doctype": "Salary Component Account"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "İcbari tibbi sığorta (işçi 2%)",
|
||||||
|
"salary_component_abbr": "İTS-İ",
|
||||||
|
"type": "Deduction",
|
||||||
|
"description": "İcbari tibbi sığorta tutulması, işçidən (2%)",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"is_tax_applicable": 0,
|
||||||
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
|
"variable_based_on_taxable_salary": 0,
|
||||||
|
"is_income_tax_component": 0,
|
||||||
|
"exempted_from_income_tax": 0,
|
||||||
|
"round_to_the_nearest_integer": 0,
|
||||||
|
"statistical_component": 0,
|
||||||
|
"accrual_component": 0,
|
||||||
|
"do_not_include_in_total": 0,
|
||||||
|
"do_not_include_in_accounts": 0,
|
||||||
|
"remove_if_zero_valued": 1,
|
||||||
|
"arrear_component": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"condition": "",
|
||||||
|
"amount": 0,
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "(base * 0.02 if base <= 2500 else 50 + (base - 2500) * 0.005) + (LE * 0.02 if LE <= 2500 else 50 + (LE - 2500) * 0.005)",
|
||||||
|
"is_flexible_benefit": 0,
|
||||||
|
"payout_method": "",
|
||||||
|
"final_cycle_accrual_payout": 0,
|
||||||
|
"max_benefit_amount": 0,
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"account": "İcbari tibbi sığorta (işçi)",
|
||||||
|
"doctype": "Salary Component Account"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "Gəlir vergisi",
|
||||||
|
"salary_component_abbr": "GV",
|
||||||
|
"type": "Deduction",
|
||||||
|
"description": "Gəlir vergisi",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"is_tax_applicable": 0,
|
||||||
|
"deduct_full_tax_on_selected_payroll_date": 0,
|
||||||
|
"variable_based_on_taxable_salary": 0,
|
||||||
|
"is_income_tax_component": 1,
|
||||||
|
"exempted_from_income_tax": 0,
|
||||||
|
"round_to_the_nearest_integer": 0,
|
||||||
|
"statistical_component": 0,
|
||||||
|
"accrual_component": 0,
|
||||||
|
"do_not_include_in_total": 0,
|
||||||
|
"do_not_include_in_accounts": 0,
|
||||||
|
"remove_if_zero_valued": 1,
|
||||||
|
"arrear_component": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"condition": null,
|
||||||
|
"amount": 0,
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "((base - 200) * 0.03 if base <= 2500 else 75 + (base - 2500) * 0.10 if base <= 8000 else 625 + (base - 8000) * 0.14) + (LE * 0.03 if LE <= 2500 else 75 + (LE - 2500) * 0.10 if LE <= 8000 else 625 + (LE - 8000) * 0.14)",
|
||||||
|
"is_flexible_benefit": 0,
|
||||||
|
"payout_method": "",
|
||||||
|
"final_cycle_accrual_payout": 0,
|
||||||
|
"max_benefit_amount": 0,
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"account": "Gəlir vergisi (işçi)",
|
||||||
|
"doctype": "Salary Component Account"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"salary_component": "Əsas əmək haqqı",
|
"salary_component": "Əsas əmək haqqı",
|
||||||
"salary_component_abbr": "ƏH",
|
"salary_component_abbr": "ƏH",
|
||||||
|
|
@ -204,42 +394,7 @@
|
||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
{
|
{
|
||||||
"account": "İşçilərin əmək haqqı xərcləri",
|
"account": "İnzibati işçi heyəti xərcləri",
|
||||||
"doctype": "Salary Component Account"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"salary_component": "İcbari tibbi sığorta (şirkət 2%)",
|
|
||||||
"salary_component_abbr": "İTS-Ş",
|
|
||||||
"type": "Deduction",
|
|
||||||
"description": "İcbari tibbi sığorta tutulması, şirkətdən (2%)",
|
|
||||||
"depends_on_payment_days": 0,
|
|
||||||
"is_tax_applicable": 0,
|
|
||||||
"deduct_full_tax_on_selected_payroll_date": 0,
|
|
||||||
"variable_based_on_taxable_salary": 0,
|
|
||||||
"is_income_tax_component": 0,
|
|
||||||
"exempted_from_income_tax": 0,
|
|
||||||
"round_to_the_nearest_integer": 0,
|
|
||||||
"statistical_component": 0,
|
|
||||||
"accrual_component": 0,
|
|
||||||
"do_not_include_in_total": 1,
|
|
||||||
"do_not_include_in_accounts": 0,
|
|
||||||
"remove_if_zero_valued": 1,
|
|
||||||
"arrear_component": 0,
|
|
||||||
"disabled": 0,
|
|
||||||
"condition": null,
|
|
||||||
"amount": 0,
|
|
||||||
"amount_based_on_formula": 1,
|
|
||||||
"formula": "base * 0.02 if base <= 8000 else 160 + (base - 8000) * 0.005",
|
|
||||||
"is_flexible_benefit": 0,
|
|
||||||
"payout_method": "",
|
|
||||||
"final_cycle_accrual_payout": 0,
|
|
||||||
"max_benefit_amount": 0,
|
|
||||||
"doctype": "Salary Component",
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"account": "İcbari tibbi sığorta (şirkət)",
|
|
||||||
"doctype": "Salary Component Account"
|
"doctype": "Salary Component Account"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -266,7 +421,7 @@
|
||||||
"condition": null,
|
"condition": null,
|
||||||
"amount": 0,
|
"amount": 0,
|
||||||
"amount_based_on_formula": 1,
|
"amount_based_on_formula": 1,
|
||||||
"formula": "200 * 0.22 if base <= 200 else 44 + (base - 200) * 0.15",
|
"formula": "(base * 0.22 if base <= 200 else 44 + (base - 200) * 0.15 if base <= 8000 else 1214 + (base - 8000) * 0.11) + (LE * 0.22 if LE <= 200 else 44 + (LE - 200) * 0.15 if LE <= 8000 else 1214 + (LE - 8000) * 0.11)",
|
||||||
"is_flexible_benefit": 0,
|
"is_flexible_benefit": 0,
|
||||||
"payout_method": "",
|
"payout_method": "",
|
||||||
"final_cycle_accrual_payout": 0,
|
"final_cycle_accrual_payout": 0,
|
||||||
|
|
@ -274,77 +429,7 @@
|
||||||
"doctype": "Salary Component",
|
"doctype": "Salary Component",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
{
|
{
|
||||||
"account": "Sosial sığorta öhdəlikləri (DSMF Şirkət)",
|
"account": "Sosial sığorta öhdəlikləri (DSMF şirkət)",
|
||||||
"doctype": "Salary Component Account"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"salary_component": "Gəlir vergisi (yalnız işçi)",
|
|
||||||
"salary_component_abbr": "GV",
|
|
||||||
"type": "Deduction",
|
|
||||||
"description": "Gəlir vergisi",
|
|
||||||
"depends_on_payment_days": 0,
|
|
||||||
"is_tax_applicable": 0,
|
|
||||||
"deduct_full_tax_on_selected_payroll_date": 0,
|
|
||||||
"variable_based_on_taxable_salary": 0,
|
|
||||||
"is_income_tax_component": 1,
|
|
||||||
"exempted_from_income_tax": 0,
|
|
||||||
"round_to_the_nearest_integer": 0,
|
|
||||||
"statistical_component": 0,
|
|
||||||
"accrual_component": 0,
|
|
||||||
"do_not_include_in_total": 0,
|
|
||||||
"do_not_include_in_accounts": 0,
|
|
||||||
"remove_if_zero_valued": 1,
|
|
||||||
"arrear_component": 0,
|
|
||||||
"disabled": 0,
|
|
||||||
"condition": null,
|
|
||||||
"amount": 0,
|
|
||||||
"amount_based_on_formula": 1,
|
|
||||||
"formula": "(\n (base - 200) * 0.03 if base <= 2500\n else (75 + (base - 2500) * 0.10 if base <= 8000\n else 625 + (base - 8000) * 0.14)\n)\nif getdate(start_date).year >= 2026\nelse 0",
|
|
||||||
"is_flexible_benefit": 0,
|
|
||||||
"payout_method": "",
|
|
||||||
"final_cycle_accrual_payout": 0,
|
|
||||||
"max_benefit_amount": 0,
|
|
||||||
"doctype": "Salary Component",
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"account": "Gəlir vergisi (işçi)",
|
|
||||||
"doctype": "Salary Component Account"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"salary_component": "İcbari tibbi sığorta (işçi 2%)",
|
|
||||||
"salary_component_abbr": "İTS-İ",
|
|
||||||
"type": "Deduction",
|
|
||||||
"description": "İcbari tibbi sığorta tutulması, işçidən (2%)",
|
|
||||||
"depends_on_payment_days": 0,
|
|
||||||
"is_tax_applicable": 0,
|
|
||||||
"deduct_full_tax_on_selected_payroll_date": 0,
|
|
||||||
"variable_based_on_taxable_salary": 0,
|
|
||||||
"is_income_tax_component": 0,
|
|
||||||
"exempted_from_income_tax": 0,
|
|
||||||
"round_to_the_nearest_integer": 0,
|
|
||||||
"statistical_component": 0,
|
|
||||||
"accrual_component": 0,
|
|
||||||
"do_not_include_in_total": 0,
|
|
||||||
"do_not_include_in_accounts": 0,
|
|
||||||
"remove_if_zero_valued": 1,
|
|
||||||
"arrear_component": 0,
|
|
||||||
"disabled": 0,
|
|
||||||
"condition": "",
|
|
||||||
"amount": 0,
|
|
||||||
"amount_based_on_formula": 1,
|
|
||||||
"formula": "base * 0.02 if base <= 8000 else 160 + (base - 8000) * 0.005",
|
|
||||||
"is_flexible_benefit": 0,
|
|
||||||
"payout_method": "",
|
|
||||||
"final_cycle_accrual_payout": 0,
|
|
||||||
"max_benefit_amount": 0,
|
|
||||||
"doctype": "Salary Component",
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"account": "İcbari tibbi sığorta (işçi)",
|
|
||||||
"doctype": "Salary Component Account"
|
"doctype": "Salary Component Account"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -371,7 +456,7 @@
|
||||||
"condition": "",
|
"condition": "",
|
||||||
"amount": 0,
|
"amount": 0,
|
||||||
"amount_based_on_formula": 1,
|
"amount_based_on_formula": 1,
|
||||||
"formula": "200 * 0.03 if base <= 200 else 6 + (base - 200) * 0.10",
|
"formula": "(base * 0.03 if base <= 200 else 6 + (base - 200) * 0.10 if base <= 8000 else 786 + (base - 8000) * 0.10) + (LE * 0.03 if LE <= 200 else 6 + (LE - 200) * 0.10 if LE <= 8000 else 786 + (LE - 8000) * 0.10)",
|
||||||
"is_flexible_benefit": 0,
|
"is_flexible_benefit": 0,
|
||||||
"payout_method": "",
|
"payout_method": "",
|
||||||
"final_cycle_accrual_payout": 0,
|
"final_cycle_accrual_payout": 0,
|
||||||
|
|
@ -383,40 +468,5 @@
|
||||||
"doctype": "Salary Component Account"
|
"doctype": "Salary Component Account"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"salary_component": "Məzuniyyət Kompensasiyası",
|
|
||||||
"salary_component_abbr": "LE",
|
|
||||||
"type": "Earning",
|
|
||||||
"description": "Məzuniyyət Kompensasiyası",
|
|
||||||
"depends_on_payment_days": 0,
|
|
||||||
"is_tax_applicable": 1,
|
|
||||||
"deduct_full_tax_on_selected_payroll_date": 0,
|
|
||||||
"variable_based_on_taxable_salary": 0,
|
|
||||||
"is_income_tax_component": 0,
|
|
||||||
"exempted_from_income_tax": 0,
|
|
||||||
"round_to_the_nearest_integer": 0,
|
|
||||||
"statistical_component": 0,
|
|
||||||
"accrual_component": 0,
|
|
||||||
"do_not_include_in_total": 0,
|
|
||||||
"do_not_include_in_accounts": 0,
|
|
||||||
"remove_if_zero_valued": 1,
|
|
||||||
"arrear_component": 0,
|
|
||||||
"disabled": 1,
|
|
||||||
"condition": null,
|
|
||||||
"amount": 0,
|
|
||||||
"amount_based_on_formula": 1,
|
|
||||||
"formula": "base * 12 / 365",
|
|
||||||
"is_flexible_benefit": 0,
|
|
||||||
"payout_method": "",
|
|
||||||
"final_cycle_accrual_payout": 0,
|
|
||||||
"max_benefit_amount": 0,
|
|
||||||
"doctype": "Salary Component",
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"account": "Əməyin ödənişi üzrə işçi heyətinə olan borclar",
|
|
||||||
"doctype": "Salary Component Account"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -112,6 +112,11 @@ def after_migrate_combined():
|
||||||
rebuild_tree("Tax Free Indicator")
|
rebuild_tree("Tax Free Indicator")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Tax Free Indicator NSM rebuild failed: {e}")
|
print(f"Tax Free Indicator NSM rebuild failed: {e}")
|
||||||
|
from jey_erp.setup.setup_wizard_handler import sync_salary_components
|
||||||
|
try:
|
||||||
|
sync_salary_components()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Salary Component sync failed: {e}")
|
||||||
# REMOVED: Asset categories creation moved to setup_wizard_complete hook
|
# REMOVED: Asset categories creation moved to setup_wizard_complete hook
|
||||||
# from jey_erp.custom.create_asset_categories import create_asset_categories
|
# from jey_erp.custom.create_asset_categories import create_asset_categories
|
||||||
# create_asset_categories()
|
# create_asset_categories()
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,100 @@ def create_default_salary_components(args):
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
SC_SYNC_FIELDS = (
|
||||||
|
"salary_component_abbr", "type", "description",
|
||||||
|
"depends_on_payment_days", "is_tax_applicable",
|
||||||
|
"deduct_full_tax_on_selected_payroll_date",
|
||||||
|
"variable_based_on_taxable_salary", "is_income_tax_component",
|
||||||
|
"exempted_from_income_tax", "round_to_the_nearest_integer",
|
||||||
|
"statistical_component", "accrual_component",
|
||||||
|
"do_not_include_in_total", "do_not_include_in_accounts",
|
||||||
|
"remove_if_zero_valued", "arrear_component", "disabled",
|
||||||
|
"condition", "amount", "amount_based_on_formula", "formula",
|
||||||
|
"is_flexible_benefit", "payout_method",
|
||||||
|
"final_cycle_accrual_payout", "max_benefit_amount",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sync_salary_components():
|
||||||
|
"""Полная синхронизация Salary Component с default_data/salary_components.json.
|
||||||
|
JSON — single source of truth: создаёт отсутствующие, обновляет существующие,
|
||||||
|
удаляет лишние (не в JSON). Если удалить нельзя из-за linked records —
|
||||||
|
компонент помечается disabled. Идемпотентна, безопасна для повторного запуска.
|
||||||
|
Запускается автоматически из after_migrate.
|
||||||
|
"""
|
||||||
|
if not os.path.exists(SALARY_COMPONENTS_JSON):
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(SALARY_COMPONENTS_JSON, encoding="utf-8") as f:
|
||||||
|
components = json.load(f)
|
||||||
|
|
||||||
|
companies = frappe.get_all("Company", pluck="name")
|
||||||
|
|
||||||
|
def resolve_accounts(json_accounts):
|
||||||
|
out = []
|
||||||
|
for row in json_accounts or []:
|
||||||
|
account_name = row.get("account")
|
||||||
|
if not account_name:
|
||||||
|
continue
|
||||||
|
for company in companies:
|
||||||
|
full = frappe.db.get_value(
|
||||||
|
"Account",
|
||||||
|
{"account_name": account_name, "company": company, "is_group": 0},
|
||||||
|
"name",
|
||||||
|
)
|
||||||
|
if full:
|
||||||
|
out.append({"company": company, "account": full})
|
||||||
|
return out
|
||||||
|
|
||||||
|
json_names = set()
|
||||||
|
for data in components:
|
||||||
|
name = data.get("salary_component")
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
json_names.add(name)
|
||||||
|
accounts = resolve_accounts(data.get("accounts"))
|
||||||
|
|
||||||
|
if frappe.db.exists("Salary Component", name):
|
||||||
|
doc = frappe.get_doc("Salary Component", name)
|
||||||
|
for field in SC_SYNC_FIELDS:
|
||||||
|
if field in data:
|
||||||
|
doc.set(field, data[field])
|
||||||
|
doc.set("accounts", [])
|
||||||
|
for acc in accounts:
|
||||||
|
doc.append("accounts", acc)
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
else:
|
||||||
|
new_data = {k: v for k, v in data.items() if k != "accounts"}
|
||||||
|
new_data["doctype"] = "Salary Component"
|
||||||
|
new_data["accounts"] = accounts
|
||||||
|
frappe.get_doc(new_data).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
# Удаляем то, чего больше нет в JSON. Если удалить нельзя — disable.
|
||||||
|
for name in frappe.get_all("Salary Component", pluck="name"):
|
||||||
|
if name in json_names:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
frappe.delete_doc("Salary Component", name, ignore_permissions=True)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
doc = frappe.get_doc("Salary Component", name)
|
||||||
|
doc.disabled = 1
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
frappe.log_error(
|
||||||
|
f"Cannot delete Salary Component '{name}' (linked records exist). "
|
||||||
|
f"Marked as disabled.",
|
||||||
|
"Salary Component Sync",
|
||||||
|
)
|
||||||
|
except Exception as inner:
|
||||||
|
frappe.log_error(
|
||||||
|
f"Failed to disable Salary Component '{name}': {inner}",
|
||||||
|
"Salary Component Sync",
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
|
|
||||||
def create_default_leave_types(args):
|
def create_default_leave_types(args):
|
||||||
if not os.path.exists(LEAVE_TYPES_JSON):
|
if not os.path.exists(LEAVE_TYPES_JSON):
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue