feat: Output VAT and Input VAT accounts for every Thai company
Thai VAT is filed on P.P.30 as output tax (ภาษีขาย, collected on sales) net of input tax (ภาษีซื้อ, paid on purchases); ERPNext's wizard makes a single VAT account. Both join the per-company tax accounts created on Company save and fixture import; a patch adds them to existing companies. Account names are translated for the chart of accounts tree.
This commit is contained in:
@@ -3,4 +3,5 @@
|
|||||||
# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
|
# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
|
||||||
|
|
||||||
[post_model_sync]
|
[post_model_sync]
|
||||||
# Patches added in this section will be executed after doctypes are migrated
|
# Patches added in this section will be executed after doctypes are migrated
|
||||||
|
default_thai_company.patches.create_vat_accounts
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from default_thai_company.tax_withholding import ensure_company_accounts, thai_companies
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
"""Output VAT / Input VAT were added to COMPANY_ACCOUNTS; Company.on_update
|
||||||
|
only runs on save, so give existing Thai companies the pair."""
|
||||||
|
for company in thai_companies():
|
||||||
|
ensure_company_accounts(company)
|
||||||
@@ -12,10 +12,23 @@ from frappe.utils import cint, flt
|
|||||||
|
|
||||||
from default_thai_company.utils import money_in_words
|
from default_thai_company.utils import money_in_words
|
||||||
|
|
||||||
# Liability: tax we withhold from suppliers and remit on P.N.D.3/53 (linked on every category).
|
# Tax accounts every Thai company gets (Company.on_update, fixture import, and the
|
||||||
# Asset: tax customers withhold from us, creditable against corporate income tax.
|
# create_vat_accounts patch for companies that predate the VAT pair).
|
||||||
|
# Withholding: liability = tax we withhold from suppliers, remitted on P.N.D.3/53
|
||||||
|
# (linked on every category); asset = tax customers withhold from us, creditable
|
||||||
|
# against corporate income tax.
|
||||||
|
# VAT: output (ภาษีขาย) collected on sales is payable, input (ภาษีซื้อ) paid on
|
||||||
|
# purchases is recoverable; the P.P.30 return nets the two, so they are kept apart.
|
||||||
LIABILITY_ACCOUNT = "Withholding Tax Payable"
|
LIABILITY_ACCOUNT = "Withholding Tax Payable"
|
||||||
ASSET_ACCOUNT = "Withholding Tax Receivable"
|
ASSET_ACCOUNT = "Withholding Tax Receivable"
|
||||||
|
OUTPUT_VAT_ACCOUNT = "Output VAT"
|
||||||
|
INPUT_VAT_ACCOUNT = "Input VAT"
|
||||||
|
COMPANY_ACCOUNTS = (
|
||||||
|
(LIABILITY_ACCOUNT, "Liability"),
|
||||||
|
(ASSET_ACCOUNT, "Asset"),
|
||||||
|
(OUTPUT_VAT_ACCOUNT, "Liability"),
|
||||||
|
(INPUT_VAT_ACCOUNT, "Asset"),
|
||||||
|
)
|
||||||
|
|
||||||
# Preferred parent groups by root type; first match wins, else the root account.
|
# Preferred parent groups by root type; first match wins, else the root account.
|
||||||
PARENT_GROUPS = {
|
PARENT_GROUPS = {
|
||||||
@@ -67,9 +80,10 @@ def find_parent_group(company, root_type):
|
|||||||
|
|
||||||
|
|
||||||
def ensure_company_accounts(company):
|
def ensure_company_accounts(company):
|
||||||
"""Create both withholding accounts for `company`; return the liability account name."""
|
"""Create every account in COMPANY_ACCOUNTS for `company`; return the
|
||||||
get_or_create_account(company, ASSET_ACCOUNT, "Asset")
|
withholding liability account name (the one Tax Withholding Categories link)."""
|
||||||
return get_or_create_account(company, LIABILITY_ACCOUNT, "Liability")
|
accounts = {name: get_or_create_account(company, name, root_type) for name, root_type in COMPANY_ACCOUNTS}
|
||||||
|
return accounts[LIABILITY_ACCOUNT]
|
||||||
|
|
||||||
|
|
||||||
def link_company(category, company, account):
|
def link_company(category, company, account):
|
||||||
@@ -105,7 +119,7 @@ def prepare_fixture_accounts(doc, method=None):
|
|||||||
|
|
||||||
|
|
||||||
def setup_company(doc, method=None):
|
def setup_company(doc, method=None):
|
||||||
"""Company.on_update: create withholding accounts and link every WHT category."""
|
"""Company.on_update: create the tax accounts and link every WHT category."""
|
||||||
if doc.country != "Thailand" or not frappe.db.exists("Account", {"company": doc.name}):
|
if doc.country != "Thailand" or not frappe.db.exists("Account", {"company": doc.name}):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ from frappe.utils import nowdate
|
|||||||
|
|
||||||
from default_thai_company.tax_withholding import (
|
from default_thai_company.tax_withholding import (
|
||||||
ASSET_ACCOUNT,
|
ASSET_ACCOUNT,
|
||||||
|
INPUT_VAT_ACCOUNT,
|
||||||
LIABILITY_ACCOUNT,
|
LIABILITY_ACCOUNT,
|
||||||
|
OUTPUT_VAT_ACCOUNT,
|
||||||
get_payment_entry,
|
get_payment_entry,
|
||||||
thai_companies,
|
thai_companies,
|
||||||
)
|
)
|
||||||
@@ -44,16 +46,7 @@ class TestTaxWithholding(FrappeTestCase):
|
|||||||
).insert()
|
).insert()
|
||||||
cls.payable = f"{LIABILITY_ACCOUNT} - {ABBR}"
|
cls.payable = f"{LIABILITY_ACCOUNT} - {ABBR}"
|
||||||
cls.receivable = f"{ASSET_ACCOUNT} - {ABBR}"
|
cls.receivable = f"{ASSET_ACCOUNT} - {ABBR}"
|
||||||
|
cls.vat = frappe.get_doc("Account", f"{OUTPUT_VAT_ACCOUNT} - {ABBR}")
|
||||||
cls.vat = frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Account",
|
|
||||||
"company": COMPANY,
|
|
||||||
"account_name": "Output VAT",
|
|
||||||
"parent_account": f"Duties and Taxes - {ABBR}",
|
|
||||||
"account_type": "Tax",
|
|
||||||
}
|
|
||||||
).insert()
|
|
||||||
|
|
||||||
frappe.get_doc(
|
frappe.get_doc(
|
||||||
{
|
{
|
||||||
@@ -137,14 +130,17 @@ class TestTaxWithholding(FrappeTestCase):
|
|||||||
|
|
||||||
def test_company_creation_adds_accounts_and_links_categories(self):
|
def test_company_creation_adds_accounts_and_links_categories(self):
|
||||||
self.assertIn(COMPANY, thai_companies())
|
self.assertIn(COMPANY, thai_companies())
|
||||||
self.assertEqual(
|
for account, expected in (
|
||||||
frappe.db.get_value("Account", self.payable, ["root_type", "parent_account"]),
|
(self.payable, ("Liability", f"Duties and Taxes - {ABBR}", "Tax")),
|
||||||
("Liability", f"Duties and Taxes - {ABBR}"),
|
(self.receivable, ("Asset", f"Tax Assets - {ABBR}", "Tax")),
|
||||||
)
|
(f"{OUTPUT_VAT_ACCOUNT} - {ABBR}", ("Liability", f"Duties and Taxes - {ABBR}", "Tax")),
|
||||||
self.assertEqual(
|
(f"{INPUT_VAT_ACCOUNT} - {ABBR}", ("Asset", f"Tax Assets - {ABBR}", "Tax")),
|
||||||
frappe.db.get_value("Account", self.receivable, ["root_type", "parent_account"]),
|
):
|
||||||
("Asset", f"Tax Assets - {ABBR}"),
|
self.assertEqual(
|
||||||
)
|
frappe.db.get_value("Account", account, ["root_type", "parent_account", "account_type"]),
|
||||||
|
expected,
|
||||||
|
account,
|
||||||
|
)
|
||||||
|
|
||||||
categories = frappe.get_all(
|
categories = frappe.get_all(
|
||||||
"Tax Withholding Category", filters={"name": ("like", "WHT %")}, pluck="name"
|
"Tax Withholding Category", filters={"name": ("like", "WHT %")}, pluck="name"
|
||||||
|
|||||||
@@ -53,3 +53,7 @@ Grand Total,ยอดรวมทั้งสิ้น
|
|||||||
Rounded Total,ยอดรวมปัดเศษ
|
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 Receivable,ภาษีถูกหัก ณ ที่จ่าย
|
||||||
|
Output VAT,ภาษีขาย
|
||||||
|
Input VAT,ภาษีซื้อ
|
||||||
|
|||||||
|
Reference in New Issue
Block a user