feat: apply customer withholding automatically on new receipts
Tick Apply Tax Withholding Amount on unsaved customer receipts whose references include a Sales Invoice carrying withholding: in the get_payment_entry override (Create > Payment), in the allocate_amount_to_references doc method (Get Outstanding Invoices / Paid Amount changes, so the tax row updates live) and on first save. Saved entries keep the user's choice; unticking now removes the row. Client script recomputes the row when the checkbox or category changes.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import erpnext
|
||||
import frappe
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import PaymentEntry
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry as _get_payment_entry
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice
|
||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
|
||||
get_tax_withholding_details,
|
||||
@@ -171,6 +172,46 @@ class ThaiPaymentEntry(PaymentEntry):
|
||||
entry.post_net_value = True
|
||||
return gl_entries
|
||||
|
||||
@frappe.whitelist()
|
||||
def allocate_amount_to_references(self, *args, **kwargs):
|
||||
"""Called by the form after Get Outstanding Invoices and on Paid Amount
|
||||
changes; keep the withholding row in step with the allocation."""
|
||||
super().allocate_amount_to_references(*args, **kwargs)
|
||||
if self.party_type == "Customer" and self.payment_type == "Receive" and self.source_exchange_rate:
|
||||
auto_apply_customer_withholding(self)
|
||||
set_customer_withholding(self)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry(*args, **kwargs):
|
||||
"""Create > Payment from a Sales Invoice: arrive with withholding applied."""
|
||||
pe = _get_payment_entry(*args, **kwargs)
|
||||
if auto_apply_customer_withholding(pe) and pe.source_exchange_rate:
|
||||
set_customer_withholding(pe)
|
||||
return pe
|
||||
|
||||
|
||||
def auto_apply_customer_withholding(doc):
|
||||
"""Tick Apply Tax Withholding Amount on a new customer receipt whose
|
||||
references include a Sales Invoice that carries withholding. Only for
|
||||
unsaved entries: after the first save the checkbox is the user's."""
|
||||
if (
|
||||
doc.party_type != "Customer"
|
||||
or doc.payment_type != "Receive"
|
||||
or doc.apply_tax_withholding_amount
|
||||
or not doc.is_new()
|
||||
):
|
||||
return False
|
||||
|
||||
invoices = [r.reference_name for r in doc.references if r.reference_doctype == "Sales Invoice"]
|
||||
if not invoices or not frappe.db.exists(
|
||||
"Sales Invoice", {"name": ("in", invoices), "withholding_tax_amount": (">", 0)}
|
||||
):
|
||||
return False
|
||||
|
||||
doc.apply_tax_withholding_amount = 1
|
||||
return True
|
||||
|
||||
|
||||
def set_customer_withholding(doc, method=None):
|
||||
"""Payment Entry.validate: tax withheld by the customer on a Receive entry
|
||||
@@ -184,21 +225,32 @@ def set_customer_withholding(doc, method=None):
|
||||
the controller's validate, then re-runs the tax computation so the row's
|
||||
base amounts and totals are final within this save.
|
||||
"""
|
||||
if doc.party_type != "Customer" or doc.payment_type != "Receive" or not doc.apply_tax_withholding_amount:
|
||||
if doc.party_type != "Customer" or doc.payment_type != "Receive":
|
||||
return
|
||||
|
||||
if not doc.tax_withholding_category:
|
||||
doc.tax_withholding_category = frappe.db.get_value("Customer", doc.party, "tax_withholding_category")
|
||||
if method == "validate":
|
||||
auto_apply_customer_withholding(doc)
|
||||
|
||||
account = frappe.db.get_value(
|
||||
"Account",
|
||||
{"company": doc.company, "account_name": ASSET_ACCOUNT, "root_type": "Asset", "is_group": 0},
|
||||
)
|
||||
row = next((d for d in doc.taxes if d.account_head == account), None) if account else None
|
||||
|
||||
if not doc.apply_tax_withholding_amount:
|
||||
if row:
|
||||
doc.remove(row)
|
||||
doc.apply_taxes()
|
||||
doc.set_amounts_after_tax()
|
||||
return
|
||||
|
||||
if not account:
|
||||
frappe.throw(_("Account {0} not found for Company {1}").format(ASSET_ACCOUNT, doc.company))
|
||||
|
||||
if not doc.tax_withholding_category:
|
||||
doc.tax_withholding_category = frappe.db.get_value("Customer", doc.party, "tax_withholding_category")
|
||||
|
||||
amount, description = get_customer_withholding(doc)
|
||||
row = next((d for d in doc.taxes if d.account_head == account), None)
|
||||
|
||||
if not amount:
|
||||
if row:
|
||||
|
||||
Reference in New Issue
Block a user