feat: Withholding Tax Certificate print format for Purchase Invoice
The Revenue Department's Sec. 50 bis form (approve_wh3_081156.pdf) as the page background, with the invoice's data positioned in the form's fields; two copies per certificate. Its AcroForm fields carry no Thai font, so the PDF is not filled directly. get_withholding_certificate(doc) resolves the payer and payee (13-digit tax ID, one-line address), the certificate row from the category's income_type, the P.N.D. return from the supplier type and income, and the amounts in company currency with the tax in Thai words. Dates are Buddhist Era.
This commit is contained in:
@@ -11,8 +11,9 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
|
||||
normal_round,
|
||||
)
|
||||
from frappe import _
|
||||
from frappe.contacts.doctype.address.address import get_default_address
|
||||
from frappe.modules.utils import sync_customizations_for_doctype
|
||||
from frappe.utils import cint, flt
|
||||
from frappe.utils import cint, flt, fmt_money, getdate
|
||||
|
||||
from default_thai_company.utils import money_in_words
|
||||
|
||||
@@ -389,3 +390,128 @@ def get_customer_withholding(doc):
|
||||
else:
|
||||
description = _("Withholding tax deducted by customer")
|
||||
return flt(total, precision), description
|
||||
|
||||
|
||||
# Withholding tax certificate (หนังสือรับรองการหักภาษี ณ ที่จ่าย, Sec. 50 bis): the income
|
||||
# row the payment is reported on, from Tax Withholding Category.income_type (custom field).
|
||||
# Rows: 1 = Sec. 40(1), 2 = 40(2), 3 = 40(3), 4a = 40(4)(a), 4b = 40(4)(b) dividends
|
||||
# paid from profits taxed at the standard 20% rate (form row 4(b)(1)(1.3)), 5 = payments
|
||||
# withheld under Revenue Department orders issued under Sec. 3 tera, 6 = other (specified).
|
||||
CERTIFICATE_ROWS = {
|
||||
"Salary and Wages - Sec. 40(1)": "1",
|
||||
"Fees and Commissions - Sec. 40(2)": "2",
|
||||
"Royalties - Sec. 40(3)": "3",
|
||||
"Interest - Sec. 40(4)(a)": "4a",
|
||||
"Dividends - Sec. 40(4)(b)": "4b",
|
||||
"Sec. 3 Tera (Services, Rent, Contract Work etc.)": "5",
|
||||
}
|
||||
|
||||
THAI_MONTHS = (
|
||||
"มกราคม",
|
||||
"กุมภาพันธ์",
|
||||
"มีนาคม",
|
||||
"เมษายน",
|
||||
"พฤษภาคม",
|
||||
"มิถุนายน",
|
||||
"กรกฎาคม",
|
||||
"สิงหาคม",
|
||||
"กันยายน",
|
||||
"ตุลาคม",
|
||||
"พฤศจิกายน",
|
||||
"ธันวาคม",
|
||||
)
|
||||
|
||||
|
||||
def pnd_form(row, supplier_type):
|
||||
"""P.N.D. return the certificate row is filed on, as numbered on the form:
|
||||
1 = ภ.ง.ด.1ก, 2 = ภ.ง.ด.2, 3 = ภ.ง.ด.3, 53 = ภ.ง.ด.53.
|
||||
|
||||
Juristic payees (Company; Partnership, taken as registered) file on
|
||||
ภ.ง.ด.53 whatever the income; individuals by income type: salary on
|
||||
ภ.ง.ด.1ก, Sec. 40(3)/(4) on ภ.ง.ด.2, the rest on ภ.ง.ด.3.
|
||||
"""
|
||||
if supplier_type != "Individual":
|
||||
return "53"
|
||||
if row == "1":
|
||||
return "1"
|
||||
if row in ("3", "4a", "4b"):
|
||||
return "2"
|
||||
return "3"
|
||||
|
||||
|
||||
def one_line_address(address_name):
|
||||
"""Address in the order of the Thailand Address Template, on one line."""
|
||||
if not address_name:
|
||||
return None
|
||||
address = frappe.get_cached_doc("Address", address_name)
|
||||
parts = [
|
||||
address.address_line1,
|
||||
address.address_line2,
|
||||
address.county,
|
||||
address.city,
|
||||
address.state,
|
||||
address.pincode,
|
||||
]
|
||||
if address.country and address.country != "Thailand":
|
||||
parts.append(address.country)
|
||||
return " ".join(part.strip() for part in parts if part and part.strip())
|
||||
|
||||
|
||||
def tax_id_digits(tax_id):
|
||||
"""The 13 digits of a Thai tax ID for the form's boxes, or None when it is not one."""
|
||||
digits = "".join(ch for ch in (tax_id or "") if ch.isdigit())
|
||||
return digits if len(digits) == 13 else None
|
||||
|
||||
|
||||
def get_withholding_certificate(doc):
|
||||
"""Certificate data for a Purchase Invoice with Apply Tax Withholding Amount.
|
||||
|
||||
Amounts are in company currency; the form is Thai, so dates are Buddhist Era
|
||||
and the tax in words is Thai whatever the print language.
|
||||
"""
|
||||
company = frappe.get_cached_doc("Company", doc.company)
|
||||
currency = erpnext.get_company_currency(doc.company)
|
||||
precision = doc.precision("base_grand_total")
|
||||
|
||||
category = (
|
||||
frappe.get_cached_doc("Tax Withholding Category", doc.tax_withholding_category)
|
||||
if doc.tax_withholding_category
|
||||
else None
|
||||
)
|
||||
row = CERTIFICATE_ROWS.get(category.income_type if category else None, "6")
|
||||
supplier_type = frappe.get_cached_value("Supplier", doc.supplier, "supplier_type")
|
||||
|
||||
amount = flt(doc.base_tax_withholding_net_total, precision)
|
||||
tax = flt(
|
||||
sum(abs(flt(t.base_tax_amount)) for t in doc.get("taxes") if cint(t.is_tax_withholding_account)),
|
||||
precision,
|
||||
)
|
||||
posting_date = getdate(doc.posting_date)
|
||||
|
||||
return frappe._dict(
|
||||
payer=frappe._dict(
|
||||
name=company.company_name,
|
||||
tax_id=company.tax_id,
|
||||
tax_id_digits=tax_id_digits(company.tax_id),
|
||||
address=one_line_address(get_default_address("Company", doc.company)),
|
||||
),
|
||||
payee=frappe._dict(
|
||||
name=doc.supplier_name,
|
||||
tax_id=doc.tax_id,
|
||||
tax_id_digits=tax_id_digits(doc.tax_id),
|
||||
address=one_line_address(doc.supplier_address or get_default_address("Supplier", doc.supplier)),
|
||||
),
|
||||
row=row,
|
||||
# row 6 prints what was paid for; translations/th.csv carries the fixture categories
|
||||
row_note=_(category.category_name) if row == "6" and category else None,
|
||||
pnd=pnd_form(row, supplier_type),
|
||||
date=f"{posting_date.day:02d}/{posting_date.month:02d}/{posting_date.year + 543}",
|
||||
amount=fmt_money(amount, precision),
|
||||
tax=fmt_money(tax, precision),
|
||||
tax_in_words=money_in_words(tax, currency, lang="th"),
|
||||
issued=frappe._dict(
|
||||
day=posting_date.day,
|
||||
month=THAI_MONTHS[posting_date.month - 1],
|
||||
year=posting_date.year + 543,
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user