From 698d983eefb6cb068b9fffaaa5bfac1718b1ce0b Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 19 Aug 2020 14:59:46 +0530 Subject: [PATCH 1/6] fix: Item Tax Updation via Update Items in SO/PO --- erpnext/controllers/accounts_controller.py | 16 +++++++++++++++- erpnext/controllers/taxes_and_totals.py | 12 ++++++++---- .../public/js/controllers/taxes_and_totals.js | 11 ++++++++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3091193b8d..791b932695 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -19,7 +19,7 @@ from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_t from erpnext.exceptions import InvalidCurrency from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions -from erpnext.stock.get_item_details import get_item_warehouse +from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map from erpnext.stock.doctype.packed_item.packed_item import make_packing_list force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") @@ -1157,6 +1157,18 @@ def get_supplier_block_status(party_name): } return info +def set_child_tax_template_and_map(item, child_item, parent_doc): + args = { + 'item_code': item.item_code, + 'posting_date': parent_doc.transaction_date, + 'tax_category': parent_doc.get('tax_category'), + 'company': parent_doc.get('company') + } + + child_item.item_tax_template = _get_item_tax_template(args, item.taxes) + if child_item.get("item_tax_template"): + child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True) + def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Sales Order Item child item containing the default values @@ -1170,6 +1182,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom + set_child_tax_template_and_map(item, child_item, p_doc) child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1192,6 +1205,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.uom = item.stock_uom child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation + set_child_tax_template_and_map(item, child_item, p_doc) return child_item def check_and_delete_children(parent, data): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 2a14be8532..59c60f7fdc 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -209,7 +209,7 @@ class calculate_taxes_and_totals(object): elif tax.charge_type == "On Previous Row Total": current_tax_fraction = (tax_rate / 100.0) * \ self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item - + elif tax.charge_type == "On Item Quantity": inclusive_tax_amount_per_qty = flt(tax_rate) @@ -220,10 +220,14 @@ class calculate_taxes_and_totals(object): return current_tax_fraction, inclusive_tax_amount_per_qty def _get_tax_rate(self, tax, item_tax_map): - if tax.account_head in item_tax_map: - return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) + if item_tax_map: + if tax.account_head in item_tax_map: + return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) + else: + return tax.rate else: - return tax.rate + # If no item tax template against item dont calculate tax against it + return 0 def calculate_net_total(self): self.doc.total_qty = self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 6951539026..9659124960 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -223,8 +223,13 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, _get_tax_rate: function(tax, item_tax_map) { - return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? - flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; + if(!$.isEmptyObject(item_tax_map)) { + return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? + flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; + } else { + // If no item tax template against item dont calculate tax against it + return 0 + } }, calculate_net_total: function() { @@ -595,7 +600,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ $.each(actual_taxes_dict, function(key, value) { if (value) total_actual_tax += value; }); - + return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total")); } }, From 22d3729427671e5d412c5634163413ae0ba507c1 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 19 Aug 2020 15:42:40 +0530 Subject: [PATCH 2/6] fix: Codacy --- erpnext/public/js/controllers/taxes_and_totals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 9659124960..87f6d6a947 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -228,7 +228,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; } else { // If no item tax template against item dont calculate tax against it - return 0 + return 0; } }, From b7cd84196d79f4a728a9a3aaf1504e62601aac5e Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 09:42:51 +0530 Subject: [PATCH 3/6] fix: Revert tax calculation changes --- erpnext/controllers/taxes_and_totals.py | 10 +++------- erpnext/public/js/controllers/taxes_and_totals.js | 9 ++------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 59c60f7fdc..f578e7eac4 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -220,14 +220,10 @@ class calculate_taxes_and_totals(object): return current_tax_fraction, inclusive_tax_amount_per_qty def _get_tax_rate(self, tax, item_tax_map): - if item_tax_map: - if tax.account_head in item_tax_map: - return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) - else: - return tax.rate + if tax.account_head in item_tax_map: + return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) else: - # If no item tax template against item dont calculate tax against it - return 0 + return tax.rate def calculate_net_total(self): self.doc.total_qty = self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 87f6d6a947..3bc70216db 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -223,13 +223,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, _get_tax_rate: function(tax, item_tax_map) { - if(!$.isEmptyObject(item_tax_map)) { - return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? - flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; - } else { - // If no item tax template against item dont calculate tax against it - return 0; - } + return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? + flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; }, calculate_net_total: function() { From 4bb8d0c193ab9e967ad224a0e37743f1ef2cd648 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 11:18:02 +0530 Subject: [PATCH 4/6] chore: Test for PO --- .../purchase_order/test_purchase_order.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 813286f7fa..71d100dd05 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -202,6 +202,49 @@ class TestPurchaseOrder(unittest.TestCase): self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) frappe.set_user("Administrator") + def test_update_child_with_tax_template(self): + tax_template = "_Test Account Excise Duty @ 10" + item = "_Test Item Home Desktop 100" + + if not frappe.db.exists("Item Tax", {"parent":item, "item_tax_template":tax_template}): + item_doc = frappe.get_doc("Item", item) + item_doc.append("taxes", { + "item_tax_template": tax_template, + "valid_from": nowdate() + }) + item_doc.save() + else: + # update valid from + frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = CURDATE() + where parent = %(item)s and item_tax_template = %(tax)s""", + {"item": item, "tax": tax_template}) + + po = create_purchase_order(item_code=item, qty=1, do_not_save=1) + + po.append("taxes", { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Purchase Taxes and Charges", + "rate": 10 + }) + po.insert() + po.submit() + + self.assertEqual(po.taxes[0].tax_amount, 50) + self.assertEqual(po.taxes[0].total, 550) + + items = json.dumps([ + {'item_code' : item, 'rate' : 500, 'qty' : 1, 'docname': po.items[0].name}, + {'item_code' : item, 'rate' : 100, 'qty' : 1} # added item + ]) + update_child_qty_rate('Purchase Order', items, po.name) + + po.reload() + self.assertEqual(po.taxes[0].tax_amount, 60) + self.assertEqual(po.taxes[0].total, 660) + def test_update_qty(self): po = create_purchase_order() From 665c27af58dd2cdd581209eeecbcfd3bca1d2426 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 12:04:38 +0530 Subject: [PATCH 5/6] fix: Merge conflict missing line --- erpnext/controllers/accounts_controller.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 72e5df85e7..046fb2ca68 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1184,6 +1184,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor + set_child_tax_template_and_map(item, child_item, p_doc) child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1245,7 +1246,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil frappe.throw(_("You do not have permissions to {} items in a {}.") .format(actions[perm_type], parent_doctype), title=_("Insufficient Permissions")) - + def validate_workflow_conditions(doc): workflow = get_workflow_name(doc.doctype) if not workflow: @@ -1280,7 +1281,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] parent = frappe.get_doc(parent_doctype, parent_doctype_name) - + check_doc_permissions(parent, 'cancel') validate_and_delete_children(parent, data) @@ -1328,7 +1329,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item.conversion_factor = 1 else: child_item.conversion_factor = flt(d.get('conversion_factor')) - + if d.get("uom"): child_item.uom = d.get("uom") conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor")) From f995f517c95a9995e5d16333b341d9a79072f836 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 17:06:27 +0530 Subject: [PATCH 6/6] fix: Test --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 83f002e358..158799ce63 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -245,6 +245,10 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.taxes[0].tax_amount, 60) self.assertEqual(po.taxes[0].total, 660) + frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = NULL + where parent = %(item)s and item_tax_template = %(tax)s""", + {"item": item, "tax": tax_template}) + def test_update_child_uom_conv_factor_change(self): po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes") total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")])