feat: Social Security deduction component
Employee's Social Security Fund contribution (Sec. 33): 5% of wages, wages taken as at least 1,650 and at most 17,500 a month (2026-2028 ceiling), fractions of a baht rounded half up. Computed from the Basic earning rather than `base` so a partial month is 5% of the wages actually paid and the ceiling applies after proration. The contribution is deductible from taxable income in full, so the component is exempted_from_income_tax. Withheld amounts are held in "Social Security Payable" (ประกันสังคมค้างจ่าย) until remitted. Created once a Thai company exists (Company.on_update, install hooks, scheduler, the setup_salary_components patch); the Salary Component validate hook links every Thai company.
This commit is contained in:
1 parent
035ab2fc7f
commit
6dd557ee97
2 files changed
+48
-5
No files matched your search
@@ -24,6 +24,30 @@ INCOME_TAX_ACCOUNT = "Withholding Tax Payable (P.N.D.1)"
|
|||||||
DEDUCTION_ACCOUNT = "Salary Deductions"
|
DEDUCTION_ACCOUNT = "Salary Deductions"
|
||||||
DEDUCTION_ACCOUNT_GROUPS = ("Current Liabilities",)
|
DEDUCTION_ACCOUNT_GROUPS = ("Current Liabilities",)
|
||||||
|
|
||||||
|
# Employee's Social Security Fund contribution (Sec. 33): 5% of wages, wages
|
||||||
|
# taken as at least 1,650 and at most 17,500 a month (2026-2028 ceiling; 20,000
|
||||||
|
# from 2029), fractions of a baht rounded half up. Computed from the Basic
|
||||||
|
# earning rather than `base` so a partial month is 5% of the wages actually paid
|
||||||
|
# and the ceiling applies after proration, as the Social Security Office
|
||||||
|
# assesses it. The contribution is deductible from taxable income in full, so
|
||||||
|
# the component is exempted_from_income_tax; the "Social Security Fund"
|
||||||
|
# exemption sub-category is for contributions not run through payroll here.
|
||||||
|
# The employer's equal share is not an employee deduction and is left to the
|
||||||
|
# accounts. Thai books carry the withheld amount as ประกันสังคมค้างจ่าย until
|
||||||
|
# it is remitted by the 15th, so it gets its own liability.
|
||||||
|
SOCIAL_SECURITY_COMPONENT = {
|
||||||
|
"doctype": "Salary Component",
|
||||||
|
"salary_component": "Social Security",
|
||||||
|
"salary_component_abbr": "SSO",
|
||||||
|
"type": "Deduction",
|
||||||
|
"description": "Social Security Fund contribution (Sec. 33 employee)",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
"exempted_from_income_tax": 1,
|
||||||
|
"condition": "B > 0",
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "int((1650 if B < 1650 else 17500 if B > 17500 else B) * 5 / 100 + 0.5)",
|
||||||
|
}
|
||||||
|
SOCIAL_SECURITY_ACCOUNT = "Social Security Payable"
|
||||||
|
|
||||||
|
|
||||||
def hrms_installed():
|
def hrms_installed():
|
||||||
@@ -35,6 +59,10 @@ def salary_component_account(company, component):
|
|||||||
return get_or_create_account(company, EARNING_ACCOUNT, "Expense", "", EARNING_ACCOUNT_GROUPS)
|
return get_or_create_account(company, EARNING_ACCOUNT, "Expense", "", EARNING_ACCOUNT_GROUPS)
|
||||||
if component.is_income_tax_component:
|
if component.is_income_tax_component:
|
||||||
return get_or_create_account(company, INCOME_TAX_ACCOUNT, "Liability")
|
return get_or_create_account(company, INCOME_TAX_ACCOUNT, "Liability")
|
||||||
|
if component.salary_component == SOCIAL_SECURITY_COMPONENT["salary_component"]:
|
||||||
|
return get_or_create_account(
|
||||||
|
company, SOCIAL_SECURITY_ACCOUNT, "Liability", "", DEDUCTION_ACCOUNT_GROUPS
|
||||||
|
)
|
||||||
return get_or_create_account(company, DEDUCTION_ACCOUNT, "Liability", "", DEDUCTION_ACCOUNT_GROUPS)
|
return get_or_create_account(company, DEDUCTION_ACCOUNT, "Liability", "", DEDUCTION_ACCOUNT_GROUPS)
|
||||||
|
|
||||||
|
|
||||||
@@ -62,6 +90,13 @@ def ensure_component_accounts(company):
|
|||||||
component.save(ignore_permissions=True)
|
component.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_social_security_component():
|
||||||
|
"""Social Security deduction; the validate hook links every Thai company."""
|
||||||
|
if frappe.db.exists("Salary Component", SOCIAL_SECURITY_COMPONENT["salary_component"]):
|
||||||
|
return
|
||||||
|
frappe.get_doc(SOCIAL_SECURITY_COMPONENT).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def payroll_period_name(company, year):
|
def payroll_period_name(company, year):
|
||||||
abbr = frappe.get_cached_value("Company", company, "abbr")
|
abbr = frappe.get_cached_value("Company", company, "abbr")
|
||||||
return f"{year} - {abbr}"
|
return f"{year} - {abbr}"
|
||||||
@@ -88,21 +123,26 @@ def ensure_payroll_period(company, year):
|
|||||||
|
|
||||||
|
|
||||||
def setup_company(doc, method=None):
|
def setup_company(doc, method=None):
|
||||||
"""Company.on_update: this year's Payroll Period and the Salary Component
|
"""Company.on_update: this year's Payroll Period, the Social Security
|
||||||
accounts for a Thai company."""
|
component and the Salary Component accounts for a Thai company."""
|
||||||
if doc.country == "Thailand" and hrms_installed():
|
if doc.country == "Thailand" and hrms_installed():
|
||||||
ensure_payroll_period(doc.name, getdate().year)
|
ensure_payroll_period(doc.name, getdate().year)
|
||||||
|
ensure_social_security_component()
|
||||||
ensure_component_accounts(doc.name)
|
ensure_component_accounts(doc.name)
|
||||||
|
|
||||||
|
|
||||||
def setup_companies():
|
def setup_companies():
|
||||||
"""after_install, the daily scheduler, and the setup_salary_components
|
"""after_install, the daily scheduler, and the setup_salary_components
|
||||||
patch: this year's Payroll Period and the Salary Component accounts for
|
patch: this year's Payroll Period, the Social Security component and the
|
||||||
every Thai company. The scheduler rolls the Payroll Period over on 1 January."""
|
Salary Component accounts for every Thai company. The scheduler rolls the
|
||||||
|
Payroll Period over on 1 January."""
|
||||||
if not hrms_installed():
|
if not hrms_installed():
|
||||||
return
|
return
|
||||||
year = getdate().year
|
year = getdate().year
|
||||||
for company in thai_companies():
|
companies = thai_companies()
|
||||||
|
if companies:
|
||||||
|
ensure_social_security_component()
|
||||||
|
for company in companies:
|
||||||
ensure_payroll_period(company, year)
|
ensure_payroll_period(company, year)
|
||||||
ensure_component_accounts(company)
|
ensure_component_accounts(company)
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ Page {0} of {1},หน้า {0} จาก {1}
|
|||||||
Withholding Tax Payable,ภาษีหัก ณ ที่จ่ายค้างจ่าย
|
Withholding Tax Payable,ภาษีหัก ณ ที่จ่ายค้างจ่าย
|
||||||
Withholding Tax Payable (P.N.D.1),ภาษีหัก ณ ที่จ่ายค้างจ่าย (ภ.ง.ด.1)
|
Withholding Tax Payable (P.N.D.1),ภาษีหัก ณ ที่จ่ายค้างจ่าย (ภ.ง.ด.1)
|
||||||
Salary Deductions,รายการหักจากเงินเดือน
|
Salary Deductions,รายการหักจากเงินเดือน
|
||||||
|
Social Security,ประกันสังคม
|
||||||
|
Social Security Payable,ประกันสังคมค้างจ่าย
|
||||||
|
Social Security Fund contribution (Sec. 33 employee),เงินสมทบกองทุนประกันสังคม (ผู้ประกันตนมาตรา 33)
|
||||||
Withholding Tax Receivable,ภาษีถูกหัก ณ ที่จ่าย
|
Withholding Tax Receivable,ภาษีถูกหัก ณ ที่จ่าย
|
||||||
Output VAT,ภาษีขาย
|
Output VAT,ภาษีขาย
|
||||||
Input VAT,ภาษีซื้อ
|
Input VAT,ภาษีซื้อ
|
||||||
|
|||||||
|
Reference in new issue
Block a user