Merge pull request #26037 from deepeshgarg007/payment_entry_auto_tax_fixes
fix: Auto tax calculations in Payment Entry
This commit is contained in:
commit
5149101045
@ -1087,6 +1087,8 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
initialize_taxes: function(frm) {
|
||||
$.each(frm.doc["taxes"] || [], function(i, tax) {
|
||||
frm.events.validate_taxes_and_charges(tax);
|
||||
frm.events.validate_inclusive_tax(tax);
|
||||
tax.item_wise_tax_detail = {};
|
||||
let tax_fields = ["total", "tax_fraction_for_current_item",
|
||||
"grand_total_fraction_for_current_item"];
|
||||
@ -1101,6 +1103,73 @@ frappe.ui.form.on('Payment Entry', {
|
||||
});
|
||||
},
|
||||
|
||||
validate_taxes_and_charges: function(d) {
|
||||
let msg = "";
|
||||
|
||||
if (d.account_head && !d.description) {
|
||||
// set description from account head
|
||||
d.description = d.account_head.split(' - ').slice(0, -1).join(' - ');
|
||||
}
|
||||
|
||||
if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) {
|
||||
msg = __("Please select Charge Type first");
|
||||
d.row_id = "";
|
||||
d.rate = d.tax_amount = 0.0;
|
||||
} else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) {
|
||||
msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'");
|
||||
d.row_id = "";
|
||||
} else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) {
|
||||
if (d.idx == 1) {
|
||||
msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row");
|
||||
d.charge_type = '';
|
||||
} else if (!d.row_id) {
|
||||
msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]);
|
||||
d.row_id = "";
|
||||
} else if (d.row_id && d.row_id >= d.idx) {
|
||||
msg = __("Cannot refer row number greater than or equal to current row number for this Charge type");
|
||||
d.row_id = "";
|
||||
}
|
||||
}
|
||||
if (msg) {
|
||||
frappe.validated = false;
|
||||
refresh_field("taxes");
|
||||
frappe.throw(msg);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
validate_inclusive_tax: function(tax) {
|
||||
let actual_type_error = function() {
|
||||
let msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx])
|
||||
frappe.throw(msg);
|
||||
};
|
||||
|
||||
let on_previous_row_error = function(row_range) {
|
||||
let msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included",
|
||||
[tax.idx, __(tax.doctype), tax.charge_type, row_range])
|
||||
frappe.throw(msg);
|
||||
};
|
||||
|
||||
if(cint(tax.included_in_paid_amount)) {
|
||||
if(tax.charge_type == "Actual") {
|
||||
// inclusive tax cannot be of type Actual
|
||||
actual_type_error();
|
||||
} else if(tax.charge_type == "On Previous Row Amount" &&
|
||||
!cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_paid_amount)
|
||||
) {
|
||||
// referred row should also be an inclusive tax
|
||||
on_previous_row_error(tax.row_id);
|
||||
} else if(tax.charge_type == "On Previous Row Total") {
|
||||
let taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id),
|
||||
function(t) { return cint(t.included_in_paid_amount) ? null : t; });
|
||||
if(taxes_not_included.length > 0) {
|
||||
// all rows above this tax should be inclusive
|
||||
on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
determine_exclusive_rate: function(frm) {
|
||||
let has_inclusive_tax = false;
|
||||
$.each(frm.doc["taxes"] || [], function(i, row) {
|
||||
@ -1110,8 +1179,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
let cumulated_tax_fraction = 0.0;
|
||||
$.each(frm.doc["taxes"] || [], function(i, tax) {
|
||||
let current_tax_fraction = frm.events.get_current_tax_fraction(frm, tax);
|
||||
tax.tax_fraction_for_current_item = current_tax_fraction[0];
|
||||
tax.tax_fraction_for_current_item = frm.events.get_current_tax_fraction(frm, tax);
|
||||
|
||||
if(i==0) {
|
||||
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
|
||||
@ -1132,9 +1200,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
if(cint(tax.included_in_paid_amount)) {
|
||||
let tax_rate = tax.rate;
|
||||
|
||||
if (tax.charge_type == "Actual") {
|
||||
current_tax_fraction = tax.tax_amount/(frm.doc.paid_amount_after_tax + frm.doc.tax_amount);
|
||||
} else if(tax.charge_type == "On Paid Amount") {
|
||||
if(tax.charge_type == "On Paid Amount") {
|
||||
current_tax_fraction = (tax_rate / 100.0);
|
||||
} else if(tax.charge_type == "On Previous Row Amount") {
|
||||
current_tax_fraction = (tax_rate / 100.0) *
|
||||
@ -1147,7 +1213,6 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
|
||||
current_tax_fraction *= -1;
|
||||
inclusive_tax_amount_per_qty *= -1;
|
||||
}
|
||||
return current_tax_fraction;
|
||||
},
|
||||
@ -1207,10 +1272,8 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frappe.throw(
|
||||
__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"));
|
||||
}
|
||||
if (!tax.row_id) {
|
||||
tax.row_id = tax.idx - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(tax.charge_type == "Actual") {
|
||||
current_tax_amount = flt(tax.tax_amount, precision("tax_amount", tax))
|
||||
} else if(tax.charge_type == "On Paid Amount") {
|
||||
@ -1296,6 +1359,11 @@ frappe.ui.form.on('Advance Taxes and Charges', {
|
||||
included_in_paid_amount: function(frm) {
|
||||
frm.events.apply_taxes(frm);
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
},
|
||||
|
||||
charge_type: function(frm) {
|
||||
frm.events.apply_taxes(frm);
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext, json
|
||||
from frappe import _, scrub, ValidationError
|
||||
from frappe import _, scrub, ValidationError, throw
|
||||
from frappe.utils import flt, comma_or, nowdate, getdate, cint
|
||||
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
|
||||
from erpnext.accounts.party import get_party_account
|
||||
@ -16,9 +16,10 @@ from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_ac
|
||||
from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status
|
||||
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
|
||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||
|
||||
from six import string_types, iteritems
|
||||
|
||||
from erpnext.controllers.accounts_controller import validate_taxes_and_charges
|
||||
|
||||
class InvalidPaymentEntry(ValidationError):
|
||||
pass
|
||||
|
||||
@ -407,20 +408,6 @@ class PaymentEntry(AccountsController):
|
||||
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
|
||||
|
||||
# Adding args as purchase invoice to get TDS amount
|
||||
args = frappe._dict({
|
||||
'company': self.company,
|
||||
@ -719,9 +706,9 @@ class PaymentEntry(AccountsController):
|
||||
if account_currency != self.company_currency:
|
||||
frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency))
|
||||
|
||||
if (self.payment_type == 'Pay' and self.advance_tax_account) or self.payment_type == 'Receive':
|
||||
if self.payment_type == 'Pay':
|
||||
dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
|
||||
elif (self.payment_type == 'Receive' and self.advance_tax_account) or self.payment_type == 'Pay':
|
||||
elif self.payment_type == 'Receive':
|
||||
dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
|
||||
|
||||
payment_or_advance_account = self.get_party_account_for_taxes()
|
||||
@ -747,6 +734,8 @@ class PaymentEntry(AccountsController):
|
||||
if account_currency==self.company_currency
|
||||
else d.tax_amount,
|
||||
"cost_center": self.cost_center,
|
||||
"party_type": self.party_type,
|
||||
"party": self.party
|
||||
}, account_currency, item=d))
|
||||
|
||||
def add_deductions_gl_entries(self, gl_entries):
|
||||
@ -770,9 +759,9 @@ class PaymentEntry(AccountsController):
|
||||
def get_party_account_for_taxes(self):
|
||||
if self.advance_tax_account:
|
||||
return self.advance_tax_account
|
||||
elif self.payment_type == 'Pay':
|
||||
return self.paid_from
|
||||
elif self.payment_type == 'Receive':
|
||||
return self.paid_from
|
||||
elif self.payment_type == 'Pay':
|
||||
return self.paid_to
|
||||
|
||||
def update_advance_paid(self):
|
||||
@ -823,6 +812,9 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
def initialize_taxes(self):
|
||||
for tax in self.get("taxes"):
|
||||
validate_taxes_and_charges(tax)
|
||||
validate_inclusive_tax(tax, self)
|
||||
|
||||
tax_fields = ["total", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
|
||||
|
||||
if tax.charge_type != "Actual":
|
||||
@ -918,15 +910,11 @@ class PaymentEntry(AccountsController):
|
||||
if cint(tax.included_in_paid_amount):
|
||||
tax_rate = tax.rate
|
||||
|
||||
if tax.charge_type == 'Actual':
|
||||
current_tax_fraction = tax.tax_amount/ (self.paid_amount_after_tax + tax.tax_amount)
|
||||
elif tax.charge_type == "On Paid Amount":
|
||||
if tax.charge_type == "On Paid Amount":
|
||||
current_tax_fraction = tax_rate / 100.0
|
||||
|
||||
elif tax.charge_type == "On Previous Row Amount":
|
||||
current_tax_fraction = (tax_rate / 100.0) * \
|
||||
self.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
|
||||
|
||||
elif tax.charge_type == "On Previous Row Total":
|
||||
current_tax_fraction = (tax_rate / 100.0) * \
|
||||
self.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
|
||||
@ -936,6 +924,25 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
return current_tax_fraction
|
||||
|
||||
def validate_inclusive_tax(tax, doc):
|
||||
def _on_previous_row_error(row_range):
|
||||
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
|
||||
|
||||
if cint(getattr(tax, "included_in_paid_amount", None)):
|
||||
if tax.charge_type == "Actual":
|
||||
# inclusive tax cannot be of type Actual
|
||||
throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
|
||||
elif tax.charge_type == "On Previous Row Amount" and \
|
||||
not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount):
|
||||
# referred row should also be inclusive
|
||||
_on_previous_row_error(tax.row_id)
|
||||
elif tax.charge_type == "On Previous Row Total" and \
|
||||
not all([cint(t.included_in_paid_amount for t in doc.get("taxes")[:cint(tax.row_id) - 1])]):
|
||||
# all rows about the referred tax should be inclusive
|
||||
_on_previous_row_error("1 - %d" % (cint(tax.row_id),))
|
||||
elif tax.get("category") == "Valuation":
|
||||
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_outstanding_reference_documents(args):
|
||||
|
||||
@ -1626,6 +1633,13 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta
|
||||
paid_amount = received_amount * doc.get('conversion_rate', 1)
|
||||
if dt == "Employee Advance":
|
||||
paid_amount = received_amount * doc.get('exchange_rate', 1)
|
||||
|
||||
if dt == "Purchase Order" and doc.apply_tds:
|
||||
if party_account_currency == bank.account_currency:
|
||||
paid_amount = received_amount = doc.base_net_total
|
||||
else:
|
||||
paid_amount = received_amount = doc.base_net_total * doc.get('exchange_rate', 1)
|
||||
|
||||
return paid_amount, received_amount
|
||||
|
||||
def apply_early_payment_discount(paid_amount, received_amount, doc):
|
||||
|
@ -218,6 +218,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
|
||||
"description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
|
||||
"fieldname": "included_in_paid_amount",
|
||||
"fieldtype": "Check",
|
||||
@ -227,7 +228,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-09 11:48:25.335733",
|
||||
"modified": "2021-06-14 01:43:50.750455",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Taxes and Charges",
|
||||
|
@ -195,6 +195,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
|
||||
"description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
|
||||
"fieldname": "included_in_paid_amount",
|
||||
"fieldtype": "Check",
|
||||
@ -205,7 +206,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-09 11:48:04.691596",
|
||||
"modified": "2021-06-14 01:44:36.899147",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
|
@ -1192,7 +1192,7 @@ def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, c
|
||||
|
||||
|
||||
def validate_taxes_and_charges(tax):
|
||||
if tax.charge_type in ['Actual', 'On Net Total'] and tax.row_id:
|
||||
if tax.charge_type in ['Actual', 'On Net Total', 'On Paid Amount'] and tax.row_id:
|
||||
frappe.throw(_("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"))
|
||||
elif tax.charge_type in ['On Previous Row Amount', 'On Previous Row Total']:
|
||||
if cint(tax.idx) == 1:
|
||||
@ -1209,20 +1209,19 @@ def validate_taxes_and_charges(tax):
|
||||
|
||||
def validate_inclusive_tax(tax, doc):
|
||||
def _on_previous_row_error(row_range):
|
||||
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx,
|
||||
row_range))
|
||||
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
|
||||
|
||||
if cint(getattr(tax, "included_in_print_rate", None)):
|
||||
if tax.charge_type == "Actual":
|
||||
# inclusive tax cannot be of type Actual
|
||||
throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate").format(tax.idx))
|
||||
throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
|
||||
elif tax.charge_type == "On Previous Row Amount" and \
|
||||
not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate):
|
||||
# referred row should also be inclusive
|
||||
_on_previous_row_error(tax.row_id)
|
||||
elif tax.charge_type == "On Previous Row Total" and \
|
||||
not all([cint(t.included_in_print_rate) for t in doc.get("taxes")[:cint(tax.row_id) - 1]]):
|
||||
# all rows about the reffered tax should be inclusive
|
||||
# all rows about the referred tax should be inclusive
|
||||
_on_previous_row_error("1 - %d" % (tax.row_id,))
|
||||
elif tax.get("category") == "Valuation":
|
||||
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
|
||||
|
Loading…
Reference in New Issue
Block a user