Compare commits
4
Commits
c8fb983749
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf687e24cc | ||
|
|
b4c7274dce | ||
|
|
6dd557ee97 | ||
|
|
035ab2fc7f |
No files matched your search
@@ -0,0 +1,25 @@
|
|||||||
|
from default_thai_company.tax_withholding import company_ready, get_or_create_account, thai_companies
|
||||||
|
|
||||||
|
# Thai banks charge transfer and collection fees that customers net off their
|
||||||
|
# payments and that the bank nets off ours; Payment Entry books them as
|
||||||
|
# deductions, which need an expense account. The Standard chart has none, so
|
||||||
|
# every Thai company gets one under Indirect Expenses.
|
||||||
|
BANK_FEE_ACCOUNT = "Bank Fee"
|
||||||
|
BANK_FEE_ACCOUNT_GROUPS = ("Indirect Expenses", "Expenses")
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_bank_fee_account(company):
|
||||||
|
return get_or_create_account(company, BANK_FEE_ACCOUNT, "Expense", "", BANK_FEE_ACCOUNT_GROUPS)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_company(doc, method=None):
|
||||||
|
"""Company.on_update: Bank Fee account for a Thai company."""
|
||||||
|
if company_ready(doc):
|
||||||
|
ensure_bank_fee_account(doc.name)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_companies():
|
||||||
|
"""after_install and the create_bank_fee_accounts patch: companies that
|
||||||
|
exist before this code did never pass through `setup_company`."""
|
||||||
|
for company in thai_companies():
|
||||||
|
ensure_bank_fee_account(company)
|
||||||
@@ -157,6 +157,7 @@ after_install = [
|
|||||||
"default_thai_company.vat.setup_companies",
|
"default_thai_company.vat.setup_companies",
|
||||||
"default_thai_company.shipping.setup_companies",
|
"default_thai_company.shipping.setup_companies",
|
||||||
"default_thai_company.payroll.setup_companies",
|
"default_thai_company.payroll.setup_companies",
|
||||||
|
"default_thai_company.banking.setup_companies",
|
||||||
]
|
]
|
||||||
# Fixture import re-inserts the Location tree; restore lft/rgt for user-added nodes.
|
# Fixture import re-inserts the Location tree; restore lft/rgt for user-added nodes.
|
||||||
after_migrate = "default_thai_company.assets.rebuild_locations"
|
after_migrate = "default_thai_company.assets.rebuild_locations"
|
||||||
@@ -234,8 +235,13 @@ doc_events = {
|
|||||||
"default_thai_company.assets.setup_company",
|
"default_thai_company.assets.setup_company",
|
||||||
"default_thai_company.shipping.setup_company",
|
"default_thai_company.shipping.setup_company",
|
||||||
"default_thai_company.payroll.setup_company",
|
"default_thai_company.payroll.setup_company",
|
||||||
|
"default_thai_company.banking.setup_company",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
# HRMS doctype; the hook only fires on sites that have it.
|
||||||
|
"Salary Component": {
|
||||||
|
"validate": "default_thai_company.payroll.set_component_accounts",
|
||||||
|
},
|
||||||
"Payment Entry": {
|
"Payment Entry": {
|
||||||
"validate": "default_thai_company.tax_withholding.set_customer_withholding",
|
"validate": "default_thai_company.tax_withholding.set_customer_withholding",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,3 +8,6 @@ default_thai_company.patches.create_vat_accounts
|
|||||||
default_thai_company.patches.create_vat_templates
|
default_thai_company.patches.create_vat_templates
|
||||||
default_thai_company.patches.create_shipping_rules
|
default_thai_company.patches.create_shipping_rules
|
||||||
default_thai_company.patches.create_payroll_periods
|
default_thai_company.patches.create_payroll_periods
|
||||||
|
default_thai_company.patches.setup_salary_components
|
||||||
|
default_thai_company.patches.create_bank_fee_accounts
|
||||||
|
default_thai_company.patches.create_employer_social_security_accounts
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from default_thai_company.banking import setup_companies
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
setup_companies()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from default_thai_company.payroll import setup_companies
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
setup_companies()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from default_thai_company.payroll import setup_companies
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
setup_companies()
|
||||||
@@ -1,18 +1,106 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
from default_thai_company.tax_withholding import thai_companies
|
from default_thai_company.tax_withholding import company_ready, get_or_create_account, thai_companies
|
||||||
|
|
||||||
# Thai personal income tax (P.N.D.1/91) is assessed per calendar year whatever
|
# Thai personal income tax (P.N.D.1/91) is assessed per calendar year whatever
|
||||||
# the company's fiscal year, so the Payroll Period is January to December.
|
# the company's fiscal year, so the Payroll Period is January to December.
|
||||||
# Payroll Period is named by prompt (site-wide unique) but bound to one company,
|
# Payroll Period is named by prompt (site-wide unique) but bound to one company,
|
||||||
# so each Thai company gets "<year> - <abbr>", like the Shipping Rule.
|
# so each Thai company gets "<year> - <abbr>", like the Shipping Rule.
|
||||||
|
#
|
||||||
|
# Payroll Entry refuses to post a component without an account for its company,
|
||||||
|
# so every Salary Component gets one for every Thai company (Salary
|
||||||
|
# Component.validate for the components HRMS seeds with the first company and
|
||||||
|
# the ones users add; Company.on_update for companies added later).
|
||||||
|
# Earnings expense to "Salary" (Standard chart, Indirect Expenses). The income
|
||||||
|
# tax component is tax withheld from employees, remitted on P.N.D.1; Thai books
|
||||||
|
# carry it as ภาษีหัก ณ ที่จ่ายค้างจ่าย, split by return form when split at all,
|
||||||
|
# so it gets its own withholding account beside the P.N.D.3/53 one the Tax
|
||||||
|
# Withholding Categories post to. Other deductions (social security, loans)
|
||||||
|
# are held in "Salary Deductions" (Current Liabilities) until remitted.
|
||||||
|
EARNING_ACCOUNT = "Salary"
|
||||||
|
EARNING_ACCOUNT_GROUPS = ("Indirect Expenses", "Expenses")
|
||||||
|
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.
|
||||||
|
# Thai books carry the withheld amount as ประกันสังคมค้างจ่าย until it is
|
||||||
|
# remitted by the 15th, so it gets its own liability. The employer's equal share
|
||||||
|
# is not an employee deduction: it is booked against the same liability from
|
||||||
|
# its own expense account, which every Thai company gets whether or not it runs
|
||||||
|
# payroll here.
|
||||||
|
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"
|
||||||
|
EMPLOYER_SOCIAL_SECURITY_ACCOUNT = "Social Security Contribution (Employer)"
|
||||||
|
EMPLOYER_SOCIAL_SECURITY_ACCOUNT_GROUPS = ("Indirect Expenses", "Expenses")
|
||||||
|
|
||||||
|
|
||||||
def hrms_installed():
|
def hrms_installed():
|
||||||
return "hrms" in frappe.get_installed_apps()
|
return "hrms" in frappe.get_installed_apps()
|
||||||
|
|
||||||
|
|
||||||
|
def salary_component_account(company, component):
|
||||||
|
if component.type == "Earning":
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
def link_company(component, company):
|
||||||
|
"""Append the company's account row to the component if missing. Statistical
|
||||||
|
components never post, so they get none."""
|
||||||
|
if component.statistical_component or any(row.company == company for row in component.accounts):
|
||||||
|
return False
|
||||||
|
component.append(
|
||||||
|
"accounts", {"company": company, "account": salary_component_account(company, component)}
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_component_accounts(doc, method=None):
|
||||||
|
"""Salary Component.validate: an account for every Thai company."""
|
||||||
|
for company in thai_companies():
|
||||||
|
link_company(doc, company)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_component_accounts(company):
|
||||||
|
for name in frappe.get_all("Salary Component", pluck="name"):
|
||||||
|
component = frappe.get_doc("Salary Component", name)
|
||||||
|
if link_company(component, 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):
|
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}"
|
||||||
@@ -38,25 +126,51 @@ def ensure_payroll_period(company, year):
|
|||||||
).insert(ignore_permissions=True)
|
).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_employer_social_security_account(company):
|
||||||
|
return get_or_create_account(
|
||||||
|
company,
|
||||||
|
EMPLOYER_SOCIAL_SECURITY_ACCOUNT,
|
||||||
|
"Expense",
|
||||||
|
"",
|
||||||
|
EMPLOYER_SOCIAL_SECURITY_ACCOUNT_GROUPS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup_company(doc, method=None):
|
def setup_company(doc, method=None):
|
||||||
"""Company.on_update: this year's Payroll Period for a Thai company."""
|
"""Company.on_update: the employer's Social Security expense account, this
|
||||||
if doc.country == "Thailand" and hrms_installed():
|
year's Payroll Period, the Social Security component and the Salary
|
||||||
|
Component accounts for a Thai company."""
|
||||||
|
if doc.country != "Thailand":
|
||||||
|
return
|
||||||
|
if company_ready(doc):
|
||||||
|
ensure_employer_social_security_account(doc.name)
|
||||||
|
if 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)
|
||||||
|
|
||||||
|
|
||||||
def setup_companies():
|
def setup_companies():
|
||||||
"""after_install, the daily scheduler, and the create_payroll_periods patch:
|
"""after_install, the daily scheduler, and the setup_salary_components and
|
||||||
this year's Payroll Period for every Thai company. The scheduler rolls the
|
create_employer_social_security_accounts patches: the employer's Social
|
||||||
default over on 1 January."""
|
Security expense account, 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."""
|
||||||
|
companies = thai_companies()
|
||||||
|
for company in companies:
|
||||||
|
ensure_employer_social_security_account(company)
|
||||||
if not hrms_installed():
|
if not hrms_installed():
|
||||||
return
|
return
|
||||||
year = getdate().year
|
year = getdate().year
|
||||||
for company in 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)
|
||||||
|
|
||||||
|
|
||||||
def after_app_install(app_name):
|
def after_app_install(app_name):
|
||||||
"""Payroll Period only exists once hrms is installed; catch companies that
|
"""Payroll Period and Salary Component accounts only exist once hrms is
|
||||||
were created before it."""
|
installed; catch companies that were created before it."""
|
||||||
if app_name == "hrms":
|
if app_name == "hrms":
|
||||||
setup_companies()
|
setup_companies()
|
||||||
@@ -74,6 +74,13 @@ Rounded Total,ยอดรวมปัดเศษ
|
|||||||
Terms and Conditions Details,รายละเอียดข้อตกลงและเงื่อนไข
|
Terms and Conditions Details,รายละเอียดข้อตกลงและเงื่อนไข
|
||||||
Page {0} of {1},หน้า {0} จาก {1}
|
Page {0} of {1},หน้า {0} จาก {1}
|
||||||
Withholding Tax Payable,ภาษีหัก ณ ที่จ่ายค้างจ่าย
|
Withholding Tax Payable,ภาษีหัก ณ ที่จ่ายค้างจ่าย
|
||||||
|
Withholding Tax Payable (P.N.D.1),ภาษีหัก ณ ที่จ่ายค้างจ่าย (ภ.ง.ด.1)
|
||||||
|
Salary Deductions,รายการหักจากเงินเดือน
|
||||||
|
Social Security,ประกันสังคม
|
||||||
|
Social Security Payable,ประกันสังคมค้างจ่าย
|
||||||
|
Social Security Contribution (Employer),เงินสมทบประกันสังคม (ส่วนนายจ้าง)
|
||||||
|
Social Security Fund contribution (Sec. 33 employee),เงินสมทบกองทุนประกันสังคม (ผู้ประกันตนมาตรา 33)
|
||||||
|
Bank Fee,ค่าธรรมเนียมธนาคาร
|
||||||
Withholding Tax Receivable,ภาษีถูกหัก ณ ที่จ่าย
|
Withholding Tax Receivable,ภาษีถูกหัก ณ ที่จ่าย
|
||||||
Output VAT,ภาษีขาย
|
Output VAT,ภาษีขาย
|
||||||
Input VAT,ภาษีซื้อ
|
Input VAT,ภาษีซื้อ
|
||||||
|
|||||||
|
Reference in new issue
Block a user