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:
kurogeek committed 2026-11-04 09:54:01 +00:00
1 parent 035ab2fc7f
commit 6dd557ee97
2 files changed
+48 -5

No files matched your search

+45 -5
View File
@@ -24,6 +24,30 @@ INCOME_TAX_ACCOUNT = "Withholding Tax Payable (P.N.D.1)"
DEDUCTION_ACCOUNT = "Salary Deductions"
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():
@@ -35,6 +59,10 @@ def salary_component_account(company, component):
return get_or_create_account(company, EARNING_ACCOUNT, "Expense", "", EARNING_ACCOUNT_GROUPS)
if component.is_income_tax_component:
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)
@@ -62,6 +90,13 @@ def ensure_component_accounts(company):
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):
abbr = frappe.get_cached_value("Company", company, "abbr")
return f"{year} - {abbr}"
@@ -88,21 +123,26 @@ def ensure_payroll_period(company, year):
def setup_company(doc, method=None):
"""Company.on_update: this year's Payroll Period and the Salary Component
accounts for a Thai company."""
"""Company.on_update: this year's Payroll Period, the Social Security
component and the Salary Component accounts for a Thai company."""
if doc.country == "Thailand" and hrms_installed():
ensure_payroll_period(doc.name, getdate().year)
ensure_social_security_component()
ensure_component_accounts(doc.name)
def setup_companies():
"""after_install, the daily scheduler, and the setup_salary_components
patch: this year's Payroll Period and the Salary Component accounts for
every Thai company. The scheduler rolls the Payroll Period over on 1 January."""
patch: this year's Payroll Period, the Social Security component and the
Salary Component accounts for every Thai company. The scheduler rolls the
Payroll Period over on 1 January."""
if not hrms_installed():
return
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_component_accounts(company)
+3
View File
@@ -76,6 +76,9 @@ Page {0} of {1},หน้า {0} จาก {1}
Withholding Tax Payable,ภาษีหัก ณ ที่จ่ายค้างจ่าย
Withholding Tax Payable (P.N.D.1),ภาษีหัก ณ ที่จ่ายค้างจ่าย (ภ.ง.ด.1)
Salary Deductions,รายการหักจากเงินเดือน
Social Security,ประกันสังคม
Social Security Payable,ประกันสังคมค้างจ่าย
Social Security Fund contribution (Sec. 33 employee),เงินสมทบกองทุนประกันสังคม (ผู้ประกันตนมาตรา 33)
Withholding Tax Receivable,ภาษีถูกหัก ณ ที่จ่าย
Output VAT,ภาษีขาย
Input VAT,ภาษีซื้อ
1 Net Payable After Withholding Tax ยอดชำระสุทธิหลังหักภาษี ณ ที่จ่าย
76 Withholding Tax Payable ภาษีหัก ณ ที่จ่ายค้างจ่าย
77 Withholding Tax Payable (P.N.D.1) ภาษีหัก ณ ที่จ่ายค้างจ่าย (ภ.ง.ด.1)
78 Salary Deductions รายการหักจากเงินเดือน
79 Social Security ประกันสังคม
80 Social Security Payable ประกันสังคมค้างจ่าย
81 Social Security Fund contribution (Sec. 33 employee) เงินสมทบกองทุนประกันสังคม (ผู้ประกันตนมาตรา 33)
82 Withholding Tax Receivable ภาษีถูกหัก ณ ที่จ่าย
83 Output VAT ภาษีขาย
84 Input VAT ภาษีซื้อ