feat: Type of Income Paid on Tax Withholding Category

Custom Select income_type: the row of the withholding tax certificate
(50 tawi) a payment under the category is reported on, set for every
shipped category.

Frappe syncs fixtures before customizations on install and migrate, so a
fixture value for a Custom Field that does not exist yet is dropped;
before_install / before_migrate create the category fields first.
This commit is contained in:
2026-09-22 03:29:39 +00:00
parent 270e44c080
commit 42857b1377
6 changed files with 148 additions and 27 deletions
@@ -0,0 +1,73 @@
{
"custom_fields": [
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"creation": "2026-09-22 09:00:00.000000",
"default": null,
"depends_on": null,
"description": "Row of the withholding tax certificate (50 Tawi) the payment is reported on; Other prints the category name in row 6.",
"docstatus": 0,
"dt": "Tax Withholding Category",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "income_type",
"fieldtype": "Select",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 2,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "category_name",
"is_system_generated": 0,
"is_virtual": 0,
"label": "Type of Income Paid",
"length": 0,
"link_filters": null,
"mandatory_depends_on": null,
"modified": "2026-09-22 09:00:00.000000",
"modified_by": "Administrator",
"module": null,
"name": "Tax Withholding Category-income_type",
"no_copy": 0,
"non_negative": 0,
"options": "\nSalary and Wages - Sec. 40(1)\nFees and Commissions - Sec. 40(2)\nRoyalties - Sec. 40(3)\nInterest - Sec. 40(4)(a)\nDividends - Sec. 40(4)(b)\nSec. 3 Tera (Services, Rent, Contract Work etc.)\nOther",
"owner": "Administrator",
"permlevel": 0,
"placeholder": null,
"precision": null,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"show_dashboard": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
}
],
"custom_perms": [],
"doctype": "Tax Withholding Category",
"links": [],
"property_setters": [],
"sync_on_migrate": 1
}
@@ -5,7 +5,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Fees and Commissions - Sec. 40(2)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 3% - Commission", "name": "WHT 3% - Commission",
"rates": [ "rates": [
{ {
@@ -31,7 +32,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Royalties - Sec. 40(3)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 3% - Royalty", "name": "WHT 3% - Royalty",
"rates": [ "rates": [
{ {
@@ -57,7 +59,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Interest - Sec. 40(4)(a)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 1% - Interest (Company)", "name": "WHT 1% - Interest (Company)",
"rates": [ "rates": [
{ {
@@ -83,7 +86,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Interest - Sec. 40(4)(a)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 15% - Interest (Individual)", "name": "WHT 15% - Interest (Individual)",
"rates": [ "rates": [
{ {
@@ -109,7 +113,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Dividends - Sec. 40(4)(b)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 10% - Dividend", "name": "WHT 10% - Dividend",
"rates": [ "rates": [
{ {
@@ -135,7 +140,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 5% - Rent", "name": "WHT 5% - Rent",
"rates": [ "rates": [
{ {
@@ -161,7 +167,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 1% - Ship Rental", "name": "WHT 1% - Ship Rental",
"rates": [ "rates": [
{ {
@@ -187,7 +194,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 3% - Professional Fee", "name": "WHT 3% - Professional Fee",
"rates": [ "rates": [
{ {
@@ -213,7 +221,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 3% - Contract Work", "name": "WHT 3% - Contract Work",
"rates": [ "rates": [
{ {
@@ -239,7 +248,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 3% - Service", "name": "WHT 3% - Service",
"rates": [ "rates": [
{ {
@@ -265,7 +275,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 3% - Sales Promotion", "name": "WHT 3% - Sales Promotion",
"rates": [ "rates": [
{ {
@@ -291,7 +302,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 2% - Advertising", "name": "WHT 2% - Advertising",
"rates": [ "rates": [
{ {
@@ -317,7 +329,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 1% - Transportation", "name": "WHT 1% - Transportation",
"rates": [ "rates": [
{ {
@@ -343,7 +356,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 1% - Non-Life Insurance", "name": "WHT 1% - Non-Life Insurance",
"rates": [ "rates": [
{ {
@@ -369,7 +383,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 5% - Prize", "name": "WHT 5% - Prize",
"rates": [ "rates": [
{ {
@@ -395,7 +410,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 5% - Public Entertainer", "name": "WHT 5% - Public Entertainer",
"rates": [ "rates": [
{ {
@@ -421,7 +437,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 0.75% - Agricultural Produce", "name": "WHT 0.75% - Agricultural Produce",
"rates": [ "rates": [
{ {
@@ -447,7 +464,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Sec. 3 Tera (Services, Rent, Contract Work etc.)",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 1% - Aquatic Animals", "name": "WHT 1% - Aquatic Animals",
"rates": [ "rates": [
{ {
@@ -473,7 +491,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Other",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 1% - Immovable Property (Company)", "name": "WHT 1% - Immovable Property (Company)",
"rates": [ "rates": [
{ {
@@ -499,7 +518,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Other",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 15% - Non-Resident Individual", "name": "WHT 15% - Non-Resident Individual",
"rates": [ "rates": [
{ {
@@ -525,7 +545,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Other",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 15% - Foreign Company", "name": "WHT 15% - Foreign Company",
"rates": [ "rates": [
{ {
@@ -551,7 +572,8 @@
"consider_party_ledger_amount": 0, "consider_party_ledger_amount": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Tax Withholding Category", "doctype": "Tax Withholding Category",
"modified": "2026-09-11 09:00:00.000000", "income_type": "Other",
"modified": "2026-09-22 12:00:00.000000",
"name": "WHT 10% - Foreign Company Dividend", "name": "WHT 10% - Foreign Company Dividend",
"rates": [ "rates": [
{ {
+4 -1
View File
@@ -138,7 +138,10 @@ fixtures = [
# Installation # Installation
# ------------ # ------------
# before_install = "default_thai_company.install.before_install" # Fixtures are synced before Custom Fields on install and migrate; the Tax Withholding
# Category fixtures carry values for the app's Custom Fields, so create those first.
before_install = "default_thai_company.tax_withholding.sync_category_customizations"
before_migrate = "default_thai_company.tax_withholding.sync_category_customizations"
# Fixtures are synced before this runs; VAT schemes and Shipping Rules for companies # Fixtures are synced before this runs; VAT schemes and Shipping Rules for companies
# that already exist. # that already exist.
after_install = [ after_install = [
+15
View File
@@ -1,3 +1,6 @@
import json
import os
import erpnext import erpnext
import frappe import frappe
from erpnext.accounts.doctype.payment_entry.payment_entry import PaymentEntry from erpnext.accounts.doctype.payment_entry.payment_entry import PaymentEntry
@@ -8,6 +11,7 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
normal_round, normal_round,
) )
from frappe import _ from frappe import _
from frappe.modules.utils import sync_customizations_for_doctype
from frappe.utils import cint, flt from frappe.utils import cint, flt
from default_thai_company.utils import money_in_words from default_thai_company.utils import money_in_words
@@ -122,6 +126,17 @@ def prepare_fixture_accounts(doc, method=None):
link_company(doc, company, ensure_company_accounts(company)) link_company(doc, company, ensure_company_accounts(company))
def sync_category_customizations():
"""before_install / before_migrate: create the Tax Withholding Category Custom
Fields (custom/tax_withholding_category.json) ahead of the fixture import.
Frappe syncs fixtures before customizations, and a fixture value for a field
that does not exist yet is dropped."""
folder = frappe.get_app_path("default_thai_company", "default_thai_company", "custom")
filename = "tax_withholding_category.json"
with open(os.path.join(folder, filename)) as f:
sync_customizations_for_doctype(json.load(f), folder, filename)
def company_ready(doc): def company_ready(doc):
"""A Thai company whose chart of accounts exists.""" """A Thai company whose chart of accounts exists."""
return doc.country == "Thailand" and frappe.db.exists("Account", {"company": doc.name}) return doc.country == "Thailand" and frappe.db.exists("Account", {"company": doc.name})
@@ -27,12 +27,13 @@ class TestTaxWithholding(FrappeTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
# Fixture categories must exist before the company is created so the # Custom Fields first: the category fixtures carry income_type. Fixture
# Company hook has something to link; force=True re-imports. # categories must exist before the company is created so the Company hook
import_file_by_path(FIXTURE, force=True, data_import=True) # has something to link; force=True re-imports.
for fname in ("sales_invoice.json", "payment_entry.json"): for fname in ("tax_withholding_category.json", "sales_invoice.json", "payment_entry.json"):
with open(f"{CUSTOM_DIR}/{fname}") as f: with open(f"{CUSTOM_DIR}/{fname}") as f:
sync_customizations_for_doctype(json.load(f), CUSTOM_DIR, fname) sync_customizations_for_doctype(json.load(f), CUSTOM_DIR, fname)
import_file_by_path(FIXTURE, force=True, data_import=True)
frappe.get_doc( frappe.get_doc(
{ {
+7
View File
@@ -60,3 +60,10 @@ Withholding Tax Payable,ภาษีหัก ณ ที่จ่ายค้า
Withholding Tax Receivable,ภาษีถูกหัก ณ ที่จ่าย Withholding Tax Receivable,ภาษีถูกหัก ณ ที่จ่าย
Output VAT,ภาษีขาย Output VAT,ภาษีขาย
Input VAT,ภาษีซื้อ Input VAT,ภาษีซื้อ
Type of Income Paid,ประเภทเงินได้พึงประเมินที่จ่าย
Salary and Wages - Sec. 40(1),เงินเดือน ค่าจ้าง ฯลฯ ตามมาตรา 40(1)
Fees and Commissions - Sec. 40(2),ค่าธรรมเนียม ค่านายหน้า ฯลฯ ตามมาตรา 40(2)
Royalties - Sec. 40(3),ค่าแห่งลิขสิทธิ์ ฯลฯ ตามมาตรา 40(3)
Interest - Sec. 40(4)(a),ดอกเบี้ย ฯลฯ ตามมาตรา 40(4)(ก)
Dividends - Sec. 40(4)(b),เงินปันผล เงินส่วนแบ่งกำไร ฯลฯ ตามมาตรา 40(4)(ข)
"Sec. 3 Tera (Services, Rent, Contract Work etc.)",ตามคำสั่งกรมสรรพากรที่ออกตามมาตรา 3 เตรส (ค่าบริการ ค่าเช่า ค่าจ้างทำของ ฯลฯ)
1 Net Payable After Withholding Tax ยอดชำระสุทธิหลังหักภาษี ณ ที่จ่าย
60 Withholding Tax Receivable ภาษีถูกหัก ณ ที่จ่าย
61 Output VAT ภาษีขาย
62 Input VAT ภาษีซื้อ
63 Type of Income Paid ประเภทเงินได้พึงประเมินที่จ่าย
64 Salary and Wages - Sec. 40(1) เงินเดือน ค่าจ้าง ฯลฯ ตามมาตรา 40(1)
65 Fees and Commissions - Sec. 40(2) ค่าธรรมเนียม ค่านายหน้า ฯลฯ ตามมาตรา 40(2)
66 Royalties - Sec. 40(3) ค่าแห่งลิขสิทธิ์ ฯลฯ ตามมาตรา 40(3)
67 Interest - Sec. 40(4)(a) ดอกเบี้ย ฯลฯ ตามมาตรา 40(4)(ก)
68 Dividends - Sec. 40(4)(b) เงินปันผล เงินส่วนแบ่งกำไร ฯลฯ ตามมาตรา 40(4)(ข)
69 Sec. 3 Tera (Services, Rent, Contract Work etc.) ตามคำสั่งกรมสรรพากรที่ออกตามมาตรา 3 เตรส (ค่าบริการ ค่าเช่า ค่าจ้างทำของ ฯลฯ)