From 642819b955205e9faa64ae1e72c697c2a7950305 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 17 Nov 2020 09:47:10 +0530 Subject: [PATCH 01/18] fix: place of supply change when address changes --- erpnext/public/js/utils.js | 15 +++++++++++++++ erpnext/regional/india/utils.py | 1 + erpnext/selling/sales_common.js | 1 + 3 files changed, 17 insertions(+) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index ea2093eee1..b4fe412fe9 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -304,6 +304,21 @@ $.extend(erpnext.utils, { } frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); + }, + + set_place_of_supply: function(frm){ + frappe.call({ + method: "erpnext.regional.india.utils.get_place_of_supply", + args: { + "party_details": frm.doc, + "doctype": frm.doc.doctype + }, + callback: function(r){ + if(r.message){ + frm.set_value("place_of_supply", r.message) + } + } + }) } }); diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index dd87f0f660..c774cb03be 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -135,6 +135,7 @@ def test_method(): '''test function''' return 'overridden' +@frappe.whitelist() def get_place_of_supply(party_details, doctype): if not frappe.get_meta('Address').has_field('gst_state'): return diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 002cfe41e1..77bdf2912f 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -117,6 +117,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ customer_address: function() { erpnext.utils.get_address_display(this.frm, "customer_address"); erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name"); + erpnext.utils.set_place_of_supply(this.frm) }, shipping_address_name: function() { From 6b10d87d468acdf4e810d5a4b4da4b389def6a06 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 17 Nov 2020 20:34:51 +0530 Subject: [PATCH 02/18] fix: place of supply change on address change --- erpnext/public/js/controllers/buying.js | 1 + erpnext/public/js/utils.js | 28 ++++++++++++------------- erpnext/regional/india/utils.py | 3 +++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index cb76c87b62..cd5cc9282b 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -135,6 +135,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ supplier_address: function() { erpnext.utils.get_address_display(this.frm); erpnext.utils.set_taxes_from_address(this.frm, "supplier_address", "supplier_address", "supplier_address"); + erpnext.utils.set_place_of_supply(this.frm) }, buying_price_list: function() { diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index b4fe412fe9..1555896eac 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -116,6 +116,19 @@ $.extend(erpnext.utils, { } }, + set_place_of_supply: function(frm){ + frappe.call({ + method: "erpnext.regional.india.utils.get_place_of_supply", + args: { + "party_details": frm.doc, + "doctype": frm.doc.doctype + }, + callback: function(r){ + frm.set_value("place_of_supply", r.message) + } + }) + }, + add_indicator_for_multicompany: function(frm, info) { frm.dashboard.stats_area.removeClass('hidden'); frm.dashboard.stats_area_row.addClass('flex'); @@ -304,21 +317,6 @@ $.extend(erpnext.utils, { } frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); - }, - - set_place_of_supply: function(frm){ - frappe.call({ - method: "erpnext.regional.india.utils.get_place_of_supply", - args: { - "party_details": frm.doc, - "doctype": frm.doc.doctype - }, - callback: function(r){ - if(r.message){ - frm.set_value("place_of_supply", r.message) - } - } - }) } }); diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index c774cb03be..7ad1c07f93 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -138,6 +138,9 @@ def test_method(): @frappe.whitelist() def get_place_of_supply(party_details, doctype): if not frappe.get_meta('Address').has_field('gst_state'): return + if isinstance(party_details, string_types): + party_details = json.loads(party_details) + party_details = frappe._dict(party_details) if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): address_name = party_details.customer_address or party_details.shipping_address_name From 8c9b60edfec53a8a58ace5e5a3284efbdae7b724 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 18 Nov 2020 12:51:13 +0530 Subject: [PATCH 03/18] fix: reversing previous commits and adding condition in regional controller --- erpnext/public/js/controllers/buying.js | 1 - erpnext/public/js/utils.js | 13 ------------- erpnext/regional/india/taxes.js | 1 + erpnext/regional/india/utils.py | 23 +++++++++-------------- erpnext/selling/sales_common.js | 1 - 5 files changed, 10 insertions(+), 29 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index cd5cc9282b..cb76c87b62 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -135,7 +135,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ supplier_address: function() { erpnext.utils.get_address_display(this.frm); erpnext.utils.set_taxes_from_address(this.frm, "supplier_address", "supplier_address", "supplier_address"); - erpnext.utils.set_place_of_supply(this.frm) }, buying_price_list: function() { diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 1555896eac..ea2093eee1 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -116,19 +116,6 @@ $.extend(erpnext.utils, { } }, - set_place_of_supply: function(frm){ - frappe.call({ - method: "erpnext.regional.india.utils.get_place_of_supply", - args: { - "party_details": frm.doc, - "doctype": frm.doc.doctype - }, - callback: function(r){ - frm.set_value("place_of_supply", r.message) - } - }) - }, - add_indicator_for_multicompany: function(frm, info) { frm.dashboard.stats_area.removeClass('hidden'); frm.dashboard.stats_area_row.addClass('flex'); diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index 3b6a28f52c..ecfa9b7cdf 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -37,6 +37,7 @@ erpnext.setup_auto_gst_taxation = (doctype) => { callback: function(r) { if(r.message) { frm.set_value('taxes_and_charges', r.message.taxes_and_charges); + frm.set_value('place_of_supply', r.message.place_of_supply); } else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) { frm.set_value('taxes_and_charges', ''); frm.set_value('taxes', []); diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 7ad1c07f93..54083dea84 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -135,12 +135,8 @@ def test_method(): '''test function''' return 'overridden' -@frappe.whitelist() def get_place_of_supply(party_details, doctype): if not frappe.get_meta('Address').has_field('gst_state'): return - if isinstance(party_details, string_types): - party_details = json.loads(party_details) - party_details = frappe._dict(party_details) if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): address_name = party_details.customer_address or party_details.shipping_address_name @@ -164,7 +160,7 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N if is_internal_transfer(party_details, doctype): party_details.taxes_and_charges = '' party_details.taxes = '' - return + return party_details if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): master_doctype = "Sales Taxes and Charges Template" @@ -172,26 +168,26 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N get_tax_template_for_sez(party_details, master_doctype, company, 'Customer') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.company_gstin: - return + return party_details elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"): master_doctype = "Purchase Taxes and Charges Template" get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.supplier_gstin: - return + return party_details - if not party_details.place_of_supply: return + if not party_details.place_of_supply: return party_details - if not party_details.company_gstin: return + if not party_details.company_gstin: return party_details if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin and party_details.company_gstin[:2] != party_details.place_of_supply[:2]) or (doctype in ("Purchase Invoice", @@ -201,12 +197,11 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2]) if not default_tax: - return + return party_details party_details["taxes_and_charges"] = default_tax party_details.taxes = get_taxes_and_charges(master_doctype, default_tax) - if return_taxes: - return party_details + return party_details def is_internal_transfer(party_details, doctype): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 77bdf2912f..002cfe41e1 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -117,7 +117,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ customer_address: function() { erpnext.utils.get_address_display(this.frm, "customer_address"); erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name"); - erpnext.utils.set_place_of_supply(this.frm) }, shipping_address_name: function() { From 410db04b48291e310b981b541279d0930dcf4eed Mon Sep 17 00:00:00 2001 From: pateljannat Date: Wed, 18 Nov 2020 15:57:16 +0530 Subject: [PATCH 04/18] fix: linter issue for translation syntax --- erpnext/regional/india/utils.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 227af9cdeb..e189da7b11 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -86,7 +86,7 @@ def validate_gstin_check_digit(gstin, label='GSTIN'): factor = 2 if factor == 1 else 1 if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]: frappe.throw(_("""Invalid {0}! The check digit validation has failed. - Please ensure you've typed the {0} correctly.""".format(label))) + Please ensure you've typed the {0} correctly.""").format(label)) def get_itemised_tax_breakup_header(item_doctype, tax_accounts): if frappe.get_meta(item_doctype).has_field('gst_hsn_code'): @@ -160,7 +160,7 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N if is_internal_transfer(party_details, doctype): party_details.taxes_and_charges = '' party_details.taxes = '' - return party_details + return if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): master_doctype = "Sales Taxes and Charges Template" @@ -168,26 +168,26 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N get_tax_template_for_sez(party_details, master_doctype, company, 'Customer') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges'): + if party_details.get('taxes_and_charges') and return_taxes: return party_details if not party_details.company_gstin: - return party_details + return elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"): master_doctype = "Purchase Taxes and Charges Template" get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges'): + if party_details.get('taxes_and_charges') and return_taxes: return party_details if not party_details.supplier_gstin: - return party_details + return - if not party_details.place_of_supply: return party_details + if not party_details.place_of_supply: return - if not party_details.company_gstin: return party_details + if not party_details.company_gstin: return if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin and party_details.company_gstin[:2] != party_details.place_of_supply[:2]) or (doctype in ("Purchase Invoice", @@ -197,11 +197,12 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2]) if not default_tax: - return party_details + return party_details["taxes_and_charges"] = default_tax party_details.taxes = get_taxes_and_charges(master_doctype, default_tax) - return party_details + if return_taxes: + return party_details def is_internal_transfer(party_details, doctype): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): @@ -235,7 +236,7 @@ def get_tax_template(master_doctype, company, is_inter_state, state_code): if tax_category.gst_state == number_state_mapping[state_code] or \ (not default_tax and not tax_category.gst_state): default_tax = frappe.db.get_value(master_doctype, - {'company': company, 'disabled': 0, 'tax_category': tax_category.name}, 'name') + {'disabled': 0, 'tax_category': tax_category.name}, 'name') return default_tax def get_tax_template_for_sez(party_details, master_doctype, company, party_type): From cd05b34691d7ef50d06791820afddb184259e1b3 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 19 Nov 2020 11:37:08 +0530 Subject: [PATCH 05/18] fix: company filter added again --- erpnext/regional/india/utils.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index e189da7b11..c6620aa92b 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -160,7 +160,7 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N if is_internal_transfer(party_details, doctype): party_details.taxes_and_charges = '' party_details.taxes = '' - return + return party_details if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): master_doctype = "Sales Taxes and Charges Template" @@ -168,26 +168,26 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N get_tax_template_for_sez(party_details, master_doctype, company, 'Customer') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.company_gstin: - return + return party_details elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"): master_doctype = "Purchase Taxes and Charges Template" get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier') get_tax_template_based_on_category(master_doctype, company, party_details) - if party_details.get('taxes_and_charges') and return_taxes: + if party_details.get('taxes_and_charges'): return party_details if not party_details.supplier_gstin: - return + return party_details - if not party_details.place_of_supply: return + if not party_details.place_of_supply: return party_details - if not party_details.company_gstin: return + if not party_details.company_gstin: return party_details if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin and party_details.company_gstin[:2] != party_details.place_of_supply[:2]) or (doctype in ("Purchase Invoice", @@ -197,12 +197,11 @@ def get_regional_address_details(party_details, doctype, company, return_taxes=N default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2]) if not default_tax: - return + return party_details party_details["taxes_and_charges"] = default_tax party_details.taxes = get_taxes_and_charges(master_doctype, default_tax) - if return_taxes: - return party_details + return party_details def is_internal_transfer(party_details, doctype): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): @@ -236,7 +235,7 @@ def get_tax_template(master_doctype, company, is_inter_state, state_code): if tax_category.gst_state == number_state_mapping[state_code] or \ (not default_tax and not tax_category.gst_state): default_tax = frappe.db.get_value(master_doctype, - {'disabled': 0, 'tax_category': tax_category.name}, 'name') + {'company': company, 'disabled': 0, 'tax_category': tax_category.name}, 'name') return default_tax def get_tax_template_for_sez(party_details, master_doctype, company, party_type): From f0b1670abc331bf7aad8eb7d746482648b710e65 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 19 Nov 2020 18:40:13 +0530 Subject: [PATCH 06/18] fix: tds test case --- .../test_tax_withholding_category.py | 12 ++++++++---- .../buying/doctype/supplier/test_supplier.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index a0b0cbb995..ef77674372 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -7,6 +7,7 @@ import frappe import unittest from frappe.utils import today from erpnext.accounts.utils import get_fiscal_year +from erpnext.buying.doctype.supplier.test_supplier import create_supplier test_dependencies = ["Supplier Group"] @@ -103,17 +104,20 @@ class TestTaxWithholdingCategory(unittest.TestCase): def test_single_threshold_tds_with_previous_vouchers_and_no_tds(self): invoices = [] - frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS") - pi = create_purchase_invoice(supplier="Test TDS Supplier2") + doc = create_supplier(supplier_name = "Test TDS Supplier ABC", + tax_withholding_category="Single Threshold TDS") + supplier = doc.name + + pi = create_purchase_invoice(supplier=supplier) pi.submit() invoices.append(pi) # TDS not applied - pi = create_purchase_invoice(supplier="Test TDS Supplier2", do_not_apply_tds=True) + pi = create_purchase_invoice(supplier=supplier, do_not_apply_tds=True) pi.submit() invoices.append(pi) - pi = create_purchase_invoice(supplier="Test TDS Supplier2") + pi = create_purchase_invoice(supplier=supplier) pi.submit() invoices.append(pi) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index a377ec90f8..f9c8d35518 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -120,3 +120,20 @@ class TestSupplier(unittest.TestCase): # Rollback address.delete() + +def create_supplier(**args): + args = frappe._dict(args) + + try: + doc = frappe.get_doc({ + "doctype": "Supplier", + "supplier_name": args.supplier_name, + "supplier_group": args.supplier_group or "Services", + "supplier_type": args.supplier_type or "Company", + "tax_withholding_category": args.tax_withholding_category + }).insert() + + return doc + + except frappe.DuplicateEntryError: + return frappe.get_doc("Supplier", args.supplier_name) \ No newline at end of file From 1d5d863e9a7e71c540e9ba032ef042e218667a56 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 19 Nov 2020 20:11:45 +0530 Subject: [PATCH 07/18] fix: removing return_taxes condition --- erpnext/regional/india/taxes.js | 3 +-- erpnext/regional/india/utils.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index ecfa9b7cdf..3c156479c5 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -31,8 +31,7 @@ erpnext.setup_auto_gst_taxation = (doctype) => { args: { party_details: JSON.stringify(party_details), doctype: frm.doc.doctype, - company: frm.doc.company, - return_taxes: 1 + company: frm.doc.company }, callback: function(r) { if(r.message) { diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index c6620aa92b..8d89335717 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -150,7 +150,7 @@ def get_place_of_supply(party_details, doctype): return cstr(address.gst_state_number) + "-" + cstr(address.gst_state) @frappe.whitelist() -def get_regional_address_details(party_details, doctype, company, return_taxes=None): +def get_regional_address_details(party_details, doctype, company): if isinstance(party_details, string_types): party_details = json.loads(party_details) party_details = frappe._dict(party_details) From ceab692f7313acfb11704dd50a72738a9c3be9c0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Nov 2020 17:57:35 +0530 Subject: [PATCH 08/18] fix: incorrect delink serial no and batch --- erpnext/controllers/stock_controller.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f743d707f7..2d2fff8fd5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -229,9 +229,9 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ - Account in the Items table").format(item.idx, frappe.bold(item.item_code)), - title=_("Expense Account Missing")) + msg = _("Please set an Expense Account in the Items table") + frappe.throw(_("Row #{0}: Expense Account not set for the Item {1}. {2}") + .format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account", @@ -247,7 +247,9 @@ class StockController(AccountsController): for d in self.items: if not d.batch_no: continue - serial_nos = [sr.name for sr in frappe.get_all("Serial No", {'batch_no': d.batch_no})] + serial_nos = [sr.name for sr in frappe.get_all("Serial No", + {'batch_no': d.batch_no, 'status': 'Inactive'})] + if serial_nos: frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None) From 34d07b630669a6d18e3289df9561a50dcc3371fa Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Fri, 20 Nov 2020 19:22:35 +0530 Subject: [PATCH 09/18] fix: purchase receipt to purchase invoice bill date mapping (#23967) Co-authored-by: pateljannat --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d964669830..2cc4679c8c 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -572,7 +572,8 @@ def make_purchase_invoice(source_name, target_doc=None): "doctype": "Purchase Invoice", "field_map": { "supplier_warehouse":"supplier_warehouse", - "is_return": "is_return" + "is_return": "is_return", + "bill_date": "bill_date" }, "validation": { "docstatus": ["=", 1], From 610d9ca64937366da56ad2ef7610def4c5d36b57 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Nov 2020 13:47:49 +0530 Subject: [PATCH 10/18] fix: bom stock report color issue --- .../report/bom_stock_report/bom_stock_report.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 2ac6fa073b..45331c6af8 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -25,11 +25,11 @@ frappe.query_reports["BOM Stock Report"] = { ], "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.id == "Item"){ + if (column.id == "item"){ if (data["Enough Parts to Build"] > 0){ - value = `${data['Item']}` + value = `${data['item']}`; } else { - value = `${data['Item']}` + value = `${data['item']}`; } } return value From 34f381df172f7c40db4f51317a22d8f29868b0d3 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Mon, 23 Nov 2020 15:31:08 +0530 Subject: [PATCH 11/18] fix: enabling track changes for stock settings --- erpnext/stock/doctype/stock_settings/stock_settings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 067659f64a..a1666579d1 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -217,7 +217,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2020-10-13 10:33:29.147682", + "modified": "2020-11-23 15:26:54.225608", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", @@ -235,5 +235,6 @@ ], "quick_entry": 1, "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file From f9a44000d9b06517dc2fd10916eac3f8d0c02b8e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 23 Nov 2020 15:40:06 +0530 Subject: [PATCH 12/18] Update bom_stock_report.js --- .../manufacturing/report/bom_stock_report/bom_stock_report.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 45331c6af8..84f5c346ca 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -25,8 +25,8 @@ frappe.query_reports["BOM Stock Report"] = { ], "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.id == "item"){ - if (data["Enough Parts to Build"] > 0){ + if (column.id == "item") { + if (data["Enough Parts to Build"] > 0) { value = `${data['item']}`; } else { value = `${data['item']}`; From 6aa6ec1832d9c4caf00e4f113845b47f80dd92bb Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 24 Nov 2020 08:01:19 +0530 Subject: [PATCH 13/18] fix: clear error message when approval not available (#23971) --- .../department_approver/department_approver.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py index 9b2de0e1cb..d337959d53 100644 --- a/erpnext/hr/doctype/department_approver/department_approver.py +++ b/erpnext/hr/doctype/department_approver/department_approver.py @@ -20,7 +20,7 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): approvers = [] department_details = {} department_list = [] - employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver", "expense_approver", "shift_request_approver"], as_dict=True) + employee = frappe.get_value("Employee", filters.get("employee"), ["employee_name","department", "leave_approver", "expense_approver", "shift_request_approver"], as_dict=True) employee_department = filters.get("department") or employee.department if employee_department: @@ -59,11 +59,9 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True) if len(approvers) == 0: - frappe.throw(_("Please set {0} for the Employee or for Department: {1}"). - format( - field_name, frappe.bold(employee_department), - frappe.bold(employee.name) - ), - title=_(field_name + " Missing")) + error_msg = _("Please set {0} for the Employee: {1}").format(field_name, frappe.bold(employee.employee_name)) + if department_list: + error_msg += _(" or for Department: {0}").format(frappe.bold(employee_department)) + frappe.throw(error_msg, title=_(field_name + " Missing")) return set(tuple(approver) for approver in approvers) From d07447aa5fbeed93e72e882230e6b571a47b6611 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 24 Nov 2020 08:09:17 +0530 Subject: [PATCH 14/18] fix: Validation for duplicate Tax Category (#23978) * fix: Validation for duplicate Tax Category * Update utils.py Co-authored-by: Nabin Hait --- erpnext/hooks.py | 3 +++ erpnext/regional/india/utils.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b4c57d7c91..741176f33f 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -237,6 +237,9 @@ doc_events = { "Website Settings": { "validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products" }, + "Tax Category": { + "validate": "erpnext.regional.india.utils.validate_tax_category" + }, "Sales Invoice": { "on_submit": [ "erpnext.regional.create_transaction_log", diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index fc38ed0972..62487ba2aa 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -51,6 +51,13 @@ def validate_gstin_for_india(doc, method): frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.") .format(doc.gst_state_number)) +def validate_tax_category(doc, method): + if doc.get('gst_state') and frappe.db.get_value('Tax category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}): + if doc.is_inter_state: + frappe.throw(_("Inter State tax category for GST State {0} already exists").format(doc.gst_state)) + else: + frappe.throw(_("Intra State tax category for GST State {0} already exists").format(doc.gst_state)) + def update_gst_category(doc, method): for link in doc.links: if link.link_doctype in ['Customer', 'Supplier']: @@ -85,8 +92,7 @@ def validate_gstin_check_digit(gstin, label='GSTIN'): total += digit factor = 2 if factor == 1 else 1 if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]: - frappe.throw(_("""Invalid {0}! The check digit validation has failed. - Please ensure you've typed the {0} correctly.""").format(label)) + frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label)) def get_itemised_tax_breakup_header(item_doctype, tax_accounts): if frappe.get_meta(item_doctype).has_field('gst_hsn_code'): @@ -515,7 +521,7 @@ def get_address_details(data, doc, company_address, billing_address): data.transType = 1 data.actualToStateCode = data.toStateCode shipping_address = billing_address - + if doc.gst_category == 'SEZ': data.toStateCode = 99 @@ -754,4 +760,4 @@ def make_regional_gl_entries(gl_entries, doc): }, account_currency, item=tax) ) - return gl_entries \ No newline at end of file + return gl_entries From e09037ed2c1e5cf634fbd993e1d135cae84b0673 Mon Sep 17 00:00:00 2001 From: Krushnal Patel Date: Tue, 24 Nov 2020 12:53:30 +0530 Subject: [PATCH 15/18] docs: README build status badge (#23933) * fixed build status badge * changed build branch from `master` to `develop` * updated build status badge url * Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f6a52142b..15782a2e0c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

ERP made simple

-[![Build Status](https://travis-ci.com/frappe/erpnext.svg)](https://travis-ci.com/frappe/erpnext) +[![Build Status](https://api.travis-ci.com/frappe/erpnext.svg?branch=develop)](https://travis-ci.com/frappe/erpnext) [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext) [![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop) From 927106f5528bfebe8d43ae24ac627ae9965720d5 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 24 Nov 2020 15:02:52 +0530 Subject: [PATCH 16/18] fix: maintain stock can't be changed it there is product bundle --- erpnext/stock/doctype/item/item.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3b62c38b86..be845d9d9d 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -977,15 +977,20 @@ class Item(WebsiteGenerator): # For "Is Stock Item", following doctypes is important # because reserved_qty, ordered_qty and requested_qty updated from these doctypes if field == "is_stock_item": - linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"] + linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item", "Product Bundle"] for doctype in linked_doctypes: + filters={"item_code": self.name, "docstatus": 1} + + if doctype == "Product Bundle": + filters={"new_item_code": self.name} + if doctype in ("Purchase Invoice Item", "Sales Invoice Item",): # If Invoice has Stock impact, only then consider it. if self.stock_ledger_created(): return True - elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}): + elif frappe.db.get_value(doctype, filters): return True def validate_auto_reorder_enabled_in_stock_settings(self): From 43a830f3f593f463f695c7419e1b7961ff96d79d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 24 Nov 2020 15:10:36 +0530 Subject: [PATCH 17/18] fix: Old shopify order syncing date --- .../connectors/shopify_connection.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 8aa7453bd6..efbaa71924 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -149,26 +149,28 @@ def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=Fal si.shopify_order_number = shopify_order.get("name") si.set_posting_time = 1 si.posting_date = posting_date + si.due_date = posting_date si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-" si.flags.ignore_mandatory = True set_cost_center(si.items, shopify_settings.cost_center) si.insert(ignore_mandatory=True) si.submit() - make_payament_entry_against_sales_invoice(si, shopify_settings) + make_payament_entry_against_sales_invoice(si, shopify_settings, posting_date) frappe.db.commit() def set_cost_center(items, cost_center): for item in items: item.cost_center = cost_center -def make_payament_entry_against_sales_invoice(doc, shopify_settings): +def make_payament_entry_against_sales_invoice(doc, shopify_settings, posting_date=None): from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - payemnt_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account) - payemnt_entry.flags.ignore_mandatory = True - payemnt_entry.reference_no = doc.name - payemnt_entry.reference_date = nowdate() - payemnt_entry.insert(ignore_permissions=True) - payemnt_entry.submit() + payment_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account) + payment_entry.flags.ignore_mandatory = True + payment_entry.reference_no = doc.name + payment_entry.posting_date = posting_date or nowdate() + payment_entry.reference_date = posting_date or nowdate() + payment_entry.insert(ignore_permissions=True) + payment_entry.submit() def create_delivery_note(shopify_order, shopify_settings, so): if not cint(shopify_settings.sync_delivery_note): From b67ebc7636187f1e4ee508579a77cbb6a7e223d1 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 24 Nov 2020 15:37:30 +0530 Subject: [PATCH 18/18] fix: job card error handling for operations field --- .../doctype/job_card/job_card.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 4dfa78bf21..d15d81ed93 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -353,17 +353,19 @@ def get_operation_details(work_order, operation): @frappe.whitelist() def get_operations(doctype, txt, searchfield, start, page_len, filters): - if filters.get("work_order"): - args = {"parent": filters.get("work_order")} - if txt: - args["operation"] = ("like", "%{0}%".format(txt)) + if not filters.get("work_order"): + frappe.msgprint(_("Please select a Work Order first.")) + return [] + args = {"parent": filters.get("work_order")} + if txt: + args["operation"] = ("like", "%{0}%".format(txt)) - return frappe.get_all("Work Order Operation", - filters = args, - fields = ["distinct operation as operation"], - limit_start = start, - limit_page_length = page_len, - order_by="idx asc", as_list=1) + return frappe.get_all("Work Order Operation", + filters = args, + fields = ["distinct operation as operation"], + limit_start = start, + limit_page_length = page_len, + order_by="idx asc", as_list=1) @frappe.whitelist() def make_material_request(source_name, target_doc=None):