fix: Add tds in PO and code cleanup

This commit is contained in:
Deepesh Garg 2020-11-29 21:40:04 +05:30
parent c26de28613
commit d18dde7757
11 changed files with 676 additions and 197 deletions

View File

@ -12,6 +12,7 @@
"account_head",
"col_break_1",
"description",
"included_in_paid_amount",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
@ -20,9 +21,11 @@
"section_break_9",
"tax_amount",
"total",
"allocated_amount",
"column_break_13",
"base_tax_amount",
"base_total"
"base_total",
"base_allocated_amount"
],
"fields": [
{
@ -185,12 +188,36 @@
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "included_in_paid_amount",
"fieldtype": "Check",
"label": "Included In Paid Amount",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"label": "Allocated Amount",
"options": "currency",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "base_allocated_amount",
"fieldtype": "Currency",
"label": "Allocated Amount (Company Currency)",
"options": "Company:company:default_currency",
"show_days": 1,
"show_seconds": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-10-27 20:02:34.734260",
"modified": "2020-11-29 19:06:14.666460",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Advance Taxes and Charges",

View File

@ -24,15 +24,12 @@
"party_bank_account",
"contact_person",
"contact_email",
"tds_details_section",
"apply_tax_withholding_amount",
"column_break_20",
"tax_withholding_category",
"payment_accounts_section",
"party_balance",
"paid_from",
"paid_from_account_currency",
"paid_from_account_balance",
"advance_tax_account",
"column_break_18",
"paid_to",
"paid_to_account_currency",
@ -45,8 +42,10 @@
"base_paid_amount_after_tax",
"column_break_21",
"received_amount",
"received_amount_after_tax",
"target_exchange_rate",
"base_received_amount",
"base_received_amount_after_tax",
"section_break_14",
"get_outstanding_invoice",
"references",
@ -61,6 +60,10 @@
"taxes_and_charges_section",
"purchase_taxes_and_charges_template",
"sales_taxes_and_charges_template",
"column_break_55",
"apply_tax_withholding_amount",
"tax_withholding_category",
"section_break_56",
"taxes",
"base_total_taxes_and_charges",
"total_taxes_and_charges",
@ -731,14 +734,6 @@
"fieldtype": "Check",
"label": "Custom Remarks"
},
{
"depends_on": "eval: doc.payment_type == 'Pay' && doc.party_type == 'Supplier'",
"fieldname": "tds_details_section",
"fieldtype": "Section Break",
"label": "TDS Details",
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.apply_tax_withholding_amount",
"fieldname": "tax_withholding_category",
@ -750,18 +745,13 @@
},
{
"default": "0",
"depends_on": "eval:doc.party_type == 'Supplier'",
"fieldname": "apply_tax_withholding_amount",
"fieldtype": "Check",
"label": "Apply Tax Withholding Amount",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "taxes_and_charges_section",
@ -829,6 +819,44 @@
"fieldtype": "Currency",
"label": "Paid Amount After Tax (Company Currency)",
"options": "Company:company:default_currency",
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_55",
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "section_break_56",
"fieldtype": "Section Break",
"hide_border": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "advance_tax_account",
"fieldtype": "Link",
"label": "Advance Tax Account",
"options": "Account",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "received_amount_after_tax",
"fieldtype": "Currency",
"label": "Received Amount After Tax",
"options": "paid_to_account_currency",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "base_received_amount_after_tax",
"fieldtype": "Currency",
"label": "Received Amount After Tax (Company Currency)",
"options": "Company:company:default_currency",
"show_days": 1,
"show_seconds": 1
}
@ -836,7 +864,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-10-25 20:50:14.896628",
"modified": "2020-11-29 20:03:57.772062",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@ -65,6 +65,7 @@ class PaymentEntry(AccountsController):
self.validate_allocated_amount()
self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked()
self.validate_advance_tax_account()
self.set_status()
def on_submit(self):
@ -310,6 +311,9 @@ class PaymentEntry(AccountsController):
+ "<br><br>" + _("If this is undesirable please cancel the corresponding Payment Entry."),
title=_("Warning"), indicator="orange")
def validate_advance_tax_account(self):
if self.get('taxes') and not self.advance_tax_account:
frappe.throw(_('Please select advance tax account'))
def validate_journal_entry(self):
for d in self.get("references"):
@ -396,11 +400,29 @@ class PaymentEntry(AccountsController):
if not self.apply_tax_withholding_amount:
return
reference_doclist = []
net_total = self.paid_amount
included_in_paid_amount = 0
if self.get('references'):
for doc in self.get('references'):
if doc.reference_doctype == 'Purchase Order':
reference_doclist.append(doc.reference_name)
if reference_doclist:
order_amount = frappe.db.get_all('Purchase Order', fields=['sum(net_total)'],
filters = {'name': ('in', reference_doclist), 'docstatus': 1,
'apply_tds': 1}, as_list=1)
if order_amount:
net_total = order_amount[0][0]
included_in_paid_amount = 1
args = frappe._dict({
'company': self.company,
'supplier': self.party,
'posting_date': self.posting_date,
'net_total': self.paid_amount
'net_total': net_total
})
tax_withholding_details = get_party_tax_withholding_details(args, self.tax_withholding_category)
@ -408,6 +430,8 @@ class PaymentEntry(AccountsController):
if not tax_withholding_details:
return
tax_withholding_details.update({'included_in_paid_amount': included_in_paid_amount})
accounts = []
for d in self.taxes:
if d.account_head == tax_withholding_details.get("account_head"):
@ -424,18 +448,37 @@ class PaymentEntry(AccountsController):
self.remove(d)
def set_amounts(self):
self.set_amounts_after_tax()
self.set_received_amount()
self.set_amounts_in_company_currency()
self.set_amounts_after_tax()
self.set_total_allocated_amount()
self.set_unallocated_amount()
self.set_difference_amount()
def set_received_amount(self):
self.base_received_amount = self.base_paid_amount
def set_amounts_after_tax(self):
self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(self.total_taxes_and_charges),
applicable_tax = 0
base_applicable_tax = 0
for tax in self.get('taxes'):
if not tax.included_in_paid_amount:
amount = -1 * tax.tax_amount if tax.add_deduct_tax == 'Deduct' else tax.tax_amount
base_amount = -1 * tax.base_tax_amount if tax.add_deduct_tax == 'Deduct' else tax.base_tax_amount
applicable_tax += amount
base_applicable_tax += base_amount
self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(applicable_tax),
self.precision("paid_amount_after_tax"))
self.base_paid_amount_after_tax = flt(flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate),
self.precision("base_paid_amount_after_tax"))
self.received_amount_after_tax = flt(flt(self.received_amount) + flt(applicable_tax),
self.precision("paid_amount_after_tax"))
self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.source_exchange_rate),
self.precision("base_paid_amount_after_tax"))
def set_amounts_in_company_currency(self):
self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
if self.paid_amount:
@ -465,14 +508,14 @@ class PaymentEntry(AccountsController):
if self.party:
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
if self.payment_type == "Receive" \
and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
self.unallocated_amount = (self.base_received_amount + total_deductions -
and self.base_total_allocated_amount < self.base_received_amount_after_tax + total_deductions \
and self.total_allocated_amount < self.paid_amount_after_tax + (total_deductions / self.source_exchange_rate):
self.unallocated_amount = (self.received_amount_after_tax + total_deductions -
self.base_total_allocated_amount) / self.source_exchange_rate
elif self.payment_type == "Pay" \
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \
and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate):
self.unallocated_amount = (self.base_paid_amount - (total_deductions +
and self.base_total_allocated_amount < (self.base_paid_amount_after_tax - total_deductions) \
and self.total_allocated_amount < self.received_amount_after_tax + (total_deductions / self.target_exchange_rate):
self.unallocated_amount = (self.base_paid_amount_after_tax - (total_deductions +
self.base_total_allocated_amount)) / self.target_exchange_rate
def set_difference_amount(self):
@ -482,11 +525,11 @@ class PaymentEntry(AccountsController):
base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
if self.payment_type == "Receive":
self.difference_amount = base_party_amount - self.base_received_amount
self.difference_amount = base_party_amount - self.base_received_amount_after_tax
elif self.payment_type == "Pay":
self.difference_amount = self.base_paid_amount - base_party_amount
self.difference_amount = self.base_paid_amount_after_tax - base_party_amount
else:
self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
self.difference_amount = self.base_paid_amount_after_tax - flt(self.base_received_amount_after_tax)
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
@ -616,7 +659,7 @@ class PaymentEntry(AccountsController):
gl_entries.append(gle)
if self.unallocated_amount:
base_unallocated_amount = base_unallocated_amount = self.unallocated_amount * \
base_unallocated_amount = self.unallocated_amount * \
(self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
gle = party_gl_dict.copy()
@ -646,8 +689,8 @@ class PaymentEntry(AccountsController):
"account": self.paid_to,
"account_currency": self.paid_to_account_currency,
"against": self.party if self.payment_type=="Receive" else self.paid_from,
"debit_in_account_currency": self.received_amount,
"debit": self.base_received_amount,
"debit_in_account_currency": self.received_amount_after_tax,
"debit": self.base_received_amount_after_tax,
"cost_center": self.cost_center
}, item=self)
)
@ -660,8 +703,10 @@ class PaymentEntry(AccountsController):
if self.payment_type == 'Pay':
dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
rev_dr_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
elif self.payment_type == 'Receive':
dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
rev_dr_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
gl_entries.append(
self.get_gl_dict({
@ -672,8 +717,18 @@ class PaymentEntry(AccountsController):
if account_currency==self.company_currency \
else d.tax_amount,
"cost_center": d.cost_center
}, account_currency, item=d)
)
}, account_currency, item=d))
gl_entries.append(
self.get_gl_dict({
"account": self.advance_tax_account,
"against": self.party if self.payment_type=="Receive" else self.paid_from,
rev_dr_cr: d.base_tax_amount,
rev_dr_cr + "_in_account_currency": d.base_tax_amount \
if account_currency==self.company_currency \
else d.tax_amount,
"cost_center": d.cost_center
}, account_currency, item=d))
def add_deductions_gl_entries(self, gl_entries):
for d in self.get("deductions"):

View File

@ -456,6 +456,8 @@ class PurchaseInvoice(BuyingController):
self.make_tax_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
self.allocate_advance_taxes(gl_entries)
gl_entries = make_regional_gl_entries(gl_entries, self)
gl_entries = merge_similar_entries(gl_entries)
@ -899,6 +901,46 @@ class PurchaseInvoice(BuyingController):
"cost_center": self.cost_center
}, account_currency, item=self))
def allocate_advance_taxes(self, gl_entries):
tax_map = self.get_tax_map()
for pe in self.get('advances'):
pe = frappe.get_doc('Payment Entry', pe.reference_name)
for tax in pe.get('taxes'):
account_currency = get_account_currency(tax.account_head)
dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
unallocated_amount = tax.tax_amount - tax.allocated_amount
if tax_map.get(tax.account_head):
amount = tax_map.get(tax.account_head)
if amount < unallocated_amount:
unallocated_amount = amount
gl_entries.append(
self.get_gl_dict({
"account": tax.account_head,
"against": self.supplier,
dr_or_cr: unallocated_amount,
dr_or_cr + "_in_account_currency": unallocated_amount \
if account_currency==self.company_currency \
else unallocated_amount,
'cost_center': tax.cost_center
}, account_currency, item=tax))
gl_entries.append(
self.get_gl_dict({
"account": pe.advance_tax_account,
"against": self.supplier,
rev_dr_cr: unallocated_amount,
rev_dr_cr + "_in_account_currency": unallocated_amount \
if account_currency==self.company_currency \
else unallocated_amount,
'cost_center': tax.cost_center
}, account_currency, item=tax))
frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount)
tax_map[tax.account_head] -= unallocated_amount
def make_payment_gl_entries(self, gl_entries):
# Make Cash GL Entries
if cint(self.is_paid) and self.cash_bank_account and self.paid_amount:
@ -1088,10 +1130,10 @@ class PurchaseInvoice(BuyingController):
accounts = []
for d in self.taxes:
if d.account_head == tax_withholding_details.get("account_head") and not d.is_advance_tax:
if d.account_head == tax_withholding_details.get("account_head"):
d.update(tax_withholding_details)
if not d.is_advance_tax:
accounts.append(d.account_head)
accounts.append(d.account_head)
if not accounts or tax_withholding_details.get("account_head") not in accounts:
self.append("taxes", tax_withholding_details)

View File

@ -28,8 +28,7 @@
"base_tax_amount",
"base_total",
"base_tax_amount_after_discount_amount",
"item_wise_tax_detail",
"is_advance_tax"
"item_wise_tax_detail"
],
"fields": [
{
@ -250,22 +249,12 @@
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "is_advance_tax",
"fieldtype": "Check",
"hidden": 1,
"label": "Is Advance Tax",
"read_only": 1,
"show_days": 1,
"show_seconds": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-10-25 18:24:43.487567",
"modified": "2020-11-29 19:11:58.826078",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges",

View File

@ -842,6 +842,8 @@ class SalesInvoice(SellingController):
self.make_tax_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
self.allocate_advance_taxes(gl_entries)
self.make_item_gl_entries(gl_entries)
# merge gl entries before adding pos entries
@ -911,6 +913,46 @@ class SalesInvoice(SellingController):
"cost_center": self.cost_center
}, account_currency, item=self))
def allocate_advance_taxes(self, gl_entries):
tax_map = self.get_tax_map()
for pe in self.get('advances'):
pe = frappe.get_doc('Payment Entry', pe.reference_name)
for tax in pe.get('taxes'):
account_currency = get_account_currency(tax.account_head)
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
unallocated_amount = tax.tax_amount - tax.allocated_amount
if tax_map.get(tax.account_head):
amount = tax_map.get(tax.account_head)
if amount < unallocated_amount:
unallocated_amount = amount
gl_entries.append(
self.get_gl_dict({
"account": tax.account_head,
"against": self.customer,
dr_or_cr: unallocated_amount,
dr_or_cr + "_in_account_currency": unallocated_amount \
if account_currency==self.company_currency \
else unallocated_amount,
'cost_center': tax.cost_center
}, account_currency, item=tax))
gl_entries.append(
self.get_gl_dict({
"account": pe.advance_tax_account,
"against": self.customer,
rev_dr_cr: unallocated_amount,
rev_dr_cr + "_in_account_currency": unallocated_amount \
if account_currency==self.company_currency \
else unallocated_amount,
'cost_center': tax.cost_center
}, account_currency, item=tax))
frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount + unallocated_amount)
tax_map[tax.account_head] -= unallocated_amount
def make_item_gl_entries(self, gl_entries):
# income account gl entries
for item in self.get("items"):

View File

@ -45,6 +45,14 @@ frappe.ui.form.on("Purchase Order", {
});
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
},
apply_tds: function(frm) {
if (!frm.doc.apply_tds) {
frm.set_value("tax_withholding_category", '');
} else {
frm.set_value("tax_withholding_category", frm.supplier_tds);
}
}
});

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@ from erpnext.accounts.party import get_party_account_currency
from six import string_types
from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
unlink_inter_company_doc
@ -39,11 +40,18 @@ class PurchaseOrder(BuyingController):
'percent_join_field': 'material_request'
}]
def onload(self):
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
self.set_onload("supplier_tds", supplier_tds)
def validate(self):
super(PurchaseOrder, self).validate()
self.set_status()
# apply tax withholding only if checked and applicable
self.set_tax_withholding()
self.validate_supplier()
self.validate_schedule_date()
validate_for_items(self)
@ -87,6 +95,33 @@ class PurchaseOrder(BuyingController):
if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')):
self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]])
def set_tax_withholding(self):
if not self.apply_tds:
return
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
if not tax_withholding_details:
return
accounts = []
for d in self.taxes:
if d.account_head == tax_withholding_details.get("account_head"):
d.update(tax_withholding_details)
accounts.append(d.account_head)
if not accounts or tax_withholding_details.get("account_head") not in accounts:
self.append("taxes", tax_withholding_details)
to_remove = [d for d in self.taxes
if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
for d in to_remove:
self.remove(d)
# calculate totals again after applying TDS
self.calculate_taxes_and_totals()
def validate_supplier(self):
prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
if prevent_po:

View File

@ -700,6 +700,7 @@ class AccountsController(TransactionBase):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
self.update_allocated_advance_taxes()
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
@ -707,6 +708,35 @@ class AccountsController(TransactionBase):
if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
unlink_ref_doc_from_payment_entries(self)
def get_tax_map(self):
tax_map = {}
for tax in self.get('taxes'):
tax_map.setdefault(tax.account_head, 0.0)
tax_map[tax.account_head] += tax.tax_amount
return tax_map
def update_allocated_advance_taxes(self):
if self.get('advances'):
tax_accounts = [d.account_head for d in self.get('taxes')]
allocated_tax_map = frappe._dict(frappe.get_all('GL Entry', fields=['account', 'sum(credit - debit)'],
filters={'voucher_no': self.name, 'account': ('in', tax_accounts)},
group_by='account', as_list=1))
tax_map = self.get_tax_map()
for pe in self.get('advances'):
pe = frappe.get_doc('Payment Entry', pe.reference_name)
for tax in pe.get('taxes'):
allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head)
if allocated_amount > tax.tax_amount:
allocated_amount = tax.tax_amount
if allocated_amount:
frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount', tax.allocated_amount - allocated_amount)
tax_map[tax.account_head] -= allocated_amount
allocated_tax_map[tax.account_head] -= allocated_amount
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_allowance_for
item_allowance = {}

View File

@ -40,7 +40,6 @@ class calculate_taxes_and_totals(object):
self.validate_conversion_rate()
self.calculate_item_values()
self.validate_item_tax_template()
self.apply_advance_taxes()
self.initialize_taxes()
self.determine_exclusive_rate()
self.calculate_net_total()
@ -684,33 +683,6 @@ class calculate_taxes_and_totals(object):
self.calculate_paid_amount()
def apply_advance_taxes(self):
if cint(self.doc.get('adjust_advance_taxes')):
if self.doc.get('advances'):
payment_entry_list = [d.reference_name for d in self.doc.get('advances')]
advance_taxes = get_advance_taxes(payment_entry_list)
accounts = []
# Remove already added advance taxes if any
for tax in self.doc.get('taxes'):
if tax.is_advance_tax:
self.doc.remove(tax)
else:
accounts.append(tax.account_head)
for tax in advance_taxes:
# Reverse add deduct from payment entry in invoice
if tax.account_head in accounts:
add_deduct_tax = 'Deduct' if tax.add_deduct_tax == 'Add' else 'Add'
tax.update({
'add_deduct_tax': add_deduct_tax,
'category': tax.get('category') or 'Total',
'is_advance_tax': 1,
'charge_type': 'On Net Total' if tax.charge_type == 'On Paid Amount' else tax.charge_type
})
self.doc.append('taxes', tax)
def get_itemised_tax_breakup_html(doc):
if not doc.taxes:
return