From 81ae1f363cae879250be2618b6ba139ea52c48ce Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 12 Feb 2021 09:53:30 +0530 Subject: [PATCH 01/31] Removed debug flag --- .../doctype/payment_reconciliation/payment_reconciliation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index b324c8bcf19..375ec6fcd19 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -112,7 +112,7 @@ class PaymentReconciliation(Document): 'party_type': self.party_type, 'voucher_type': voucher_type, 'account': self.receivable_payable_account - }, as_dict=1, debug=1) + }, as_dict=1) def add_payment_entries(self, entries): self.set('payments', []) @@ -303,4 +303,4 @@ def reconcile_dr_cr_note(dr_cr_notes, company): ] }) - jv.submit() \ No newline at end of file + jv.submit() From edfbb5d3be9a15ba671c3d5c8cf5b3fc22b28e66 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Fri, 12 Feb 2021 12:40:52 +0530 Subject: [PATCH 02/31] fix: NoneType object has no attribute len() (#24617) --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 14fcb9de897..3fc3d401f9c 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -23,7 +23,7 @@ def validate_einvoice_fields(doc): invalid_doctype = doc.doctype != 'Sales Invoice' invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'] company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin') - no_taxes_applied = len(doc.get('taxes', [])) == 0 + no_taxes_applied = not doc.get('taxes') if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied: return From 37604c8d99481c235a9200596535b608b3221f0d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 12 Feb 2021 13:17:10 +0530 Subject: [PATCH 03/31] fix: incorrect incoming rate for the sales return --- erpnext/controllers/selling_controller.py | 25 ++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 5b7d76d0f08..123020b914f 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -310,12 +310,27 @@ class SellingController(StockController): if flt(d.conversion_factor)==0.0: d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0 return_rate = 0 - if cint(self.is_return) and self.return_against and self.docstatus==1: - against_document_no = (d.get("sales_invoice_item") - if self.doctype == "Sales Invoice" else d.get("delivery_note_item")) + if cint(self.is_return) and self.docstatus==1: + if self.return_against: + against_document_no = (d.get("sales_invoice_item") + if self.doctype == "Sales Invoice" else d.get("delivery_note_item")) - return_rate = self.get_incoming_rate_for_sales_return(d.item_code, - self.return_against, against_document_no) + return_rate = self.get_incoming_rate_for_sales_return(d.item_code, + self.return_against, against_document_no) + else: + # For standalone credit note + args = frappe._dict({ + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": flt(d.qty), + "serial_no": d.serial_no, + "company": d.company, + "allow_zero_valuation": d.allow_zero_valuation + }) + + return_rate = get_incoming_rate(args) # On cancellation or if return entry submission, make stock ledger entry for # target warehouse first, to update serial no values properly From 211a4cb8331c0e369bcab15b034757285ac65e3c Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 15 Feb 2021 14:16:00 +0530 Subject: [PATCH 04/31] fix(India): Add GST state code for Ladakh --- erpnext/patches.txt | 1 + .../patches/v12_0/add_state_code_for_ladakh.py | 16 ++++++++++++++++ erpnext/regional/india/__init__.py | 4 +++- erpnext/regional/india/gst_state_code_data.json | 5 +++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v12_0/add_state_code_for_ladakh.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 08eda7e2c43..b40549c5ba3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,3 +680,4 @@ erpnext.patches.v12_0.update_leave_application_status erpnext.patches.v12_0.update_payment_entry_status erpnext.patches.v12_0.add_transporter_address_field #2020-10-27 erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02 +erpnext.patches.v12_0.add_state_code_for_ladakh diff --git a/erpnext/patches/v12_0/add_state_code_for_ladakh.py b/erpnext/patches/v12_0/add_state_code_for_ladakh.py new file mode 100644 index 00000000000..d41101cc46a --- /dev/null +++ b/erpnext/patches/v12_0/add_state_code_for_ladakh.py @@ -0,0 +1,16 @@ +import frappe +from erpnext.regional.india import states + +def execute(): + + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + custom_fields = ['Address-gst_state', 'Tax Category-gst_state'] + + # Update options in gst_state custom fields + for field in custom_fields: + gst_state_field = frappe.get_doc('Custom Field', field) + gst_state_field.options = '\n'.join(states) + gst_state_field.save() diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py index d6221a80aa2..378b735e078 100644 --- a/erpnext/regional/india/__init__.py +++ b/erpnext/regional/india/__init__.py @@ -20,6 +20,7 @@ states = [ 'Jharkhand', 'Karnataka', 'Kerala', + 'Ladakh', 'Lakshadweep Islands', 'Madhya Pradesh', 'Maharashtra', @@ -59,6 +60,7 @@ state_numbers = { "Jharkhand": "20", "Karnataka": "29", "Kerala": "32", + "Ladakh": "38", "Lakshadweep Islands": "31", "Madhya Pradesh": "23", "Maharashtra": "27", @@ -80,4 +82,4 @@ state_numbers = { "West Bengal": "19", } -number_state_mapping = {v: k for k, v in iteritems(state_numbers)} \ No newline at end of file +number_state_mapping = {v: k for k, v in iteritems(state_numbers)} diff --git a/erpnext/regional/india/gst_state_code_data.json b/erpnext/regional/india/gst_state_code_data.json index ff88e0f9d6c..8481c279728 100644 --- a/erpnext/regional/india/gst_state_code_data.json +++ b/erpnext/regional/india/gst_state_code_data.json @@ -168,5 +168,10 @@ "state_number": "37", "state_code": "AD", "state_name": "Andhra Pradesh (New)" + }, + { + "state_number": "38", + "state_code": "LA", + "state_name": "Ladakh" } ] From b0e53230bd69d335241c2448f728d88c2e788506 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 16 Feb 2021 07:49:59 +0530 Subject: [PATCH 05/31] fix: Prorata factor fixes in subscription (#24637) * fix: Prorata factor fixes in subscription * fix: Test Case --- .../accounts/doctype/subscription/subscription.py | 15 +++++++++------ .../doctype/subscription/test_subscription.py | 7 +++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 8cc5cb8e99b..07fc68334a8 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -298,7 +298,8 @@ class Subscription(Document): Returns the `Item`s linked to `Subscription Plan` """ if prorate: - prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start) + prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start, + self.generate_invoice_at_period_start) items = [] customer = self.customer @@ -468,11 +469,13 @@ class Subscription(Document): if invoice: return invoice.precision('grand_total') - -def get_prorata_factor(period_end, period_start): - diff = flt(date_diff(nowdate(), period_start) + 1) - plan_days = flt(date_diff(period_end, period_start) + 1) - prorate_factor = diff / plan_days +def get_prorata_factor(period_end, period_start, is_prepaid): + if is_prepaid: + prorate_factor = 1 + else: + diff = flt(date_diff(nowdate(), period_start) + 1) + plan_days = flt(date_diff(period_end, period_start) + 1) + prorate_factor = diff / plan_days return prorate_factor diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 5d73e79035c..c0bc16abedc 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -291,7 +291,8 @@ class TestSubscription(unittest.TestCase): self.assertEqual( flt( - get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start), + get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start, + subscription.generate_invoice_at_period_start), 2), flt(prorate_factor, 2) ) @@ -528,9 +529,7 @@ class TestSubscription(unittest.TestCase): current_inv = subscription.get_current_invoice() self.assertEqual(current_inv.status, "Unpaid") - diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1) - plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1) - prorate_factor = flt(diff / plan_days) + prorate_factor = 1 self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2)) From 4f0c5e2cc73b46f761ce7b967578533ef396987f Mon Sep 17 00:00:00 2001 From: pateljannat Date: Thu, 18 Feb 2021 19:31:11 +0530 Subject: [PATCH 06/31] fix: excluding unidentified accounts from gstr-1 --- erpnext/regional/report/gstr_1/gstr_1.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 61c41bbddf4..96925f90fef 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -236,6 +236,7 @@ class Gstr1Report(object): self.cgst_sgst_invoices = [] unidentified_gst_accounts = [] + unidentified_gst_accounts_invoice = [] for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: if account in self.gst_accounts.cess_account: self.invoice_cess.setdefault(parent, tax_amount) @@ -251,6 +252,7 @@ class Gstr1Report(object): if not (cgst_or_sgst or account in self.gst_accounts.igst_account): if "gst" in account.lower() and account not in unidentified_gst_accounts: unidentified_gst_accounts.append(account) + unidentified_gst_accounts_invoice.append(parent) continue for item_code, tax_amounts in item_wise_tax_detail.items(): @@ -273,7 +275,7 @@ class Gstr1Report(object): # Build itemised tax for export invoices where tax table is blank for invoice, items in iteritems(self.invoice_items): - if invoice not in self.items_based_on_tax_rate \ + if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \ and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax": self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys()) From a49ea67f16beed79e335f7ce0c839b4049593380 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 19 Feb 2021 10:53:58 +0530 Subject: [PATCH 07/31] fix: Do not delete GST Accounts from GST Settings on clearing transactions from Company Master --- erpnext/setup/doctype/company/delete_company_transactions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py index 15b6d522e1d..d6155de80a9 100644 --- a/erpnext/setup/doctype/company/delete_company_transactions.py +++ b/erpnext/setup/doctype/company/delete_company_transactions.py @@ -26,8 +26,9 @@ def delete_company_transactions(company_name): tabDocField where fieldtype='Link' and options='Company'"""): if doctype not in ("Account", "Cost Center", "Warehouse", "Budget", "Party Account", "Employee", "Sales Taxes and Charges Template", - "Purchase Taxes and Charges Template", "POS Profile", 'BOM', - "Item default", "Customer", "Supplier"): + "Purchase Taxes and Charges Template", "POS Profile", "BOM", + "Company", "Bank Account", "Item Tax Template", "Mode Of Payment", + "Item Default", "Customer", "Supplier", "GST Account"): delete_for_doctype(doctype, company_name) # reset company values From 33539d49f14f88ba59d28d9462703ccfb9636021 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:26:02 +0530 Subject: [PATCH 08/31] fix: error message when making reverse journal entry (#24666) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 4d19a7c4537..709dccd2230 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -207,11 +207,11 @@ class JournalEntry(AccountsController): if d.reference_type=="Journal Entry": account_root_type = frappe.db.get_value("Account", d.account, "root_type") if account_root_type == "Asset" and flt(d.debit) > 0: - frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry") - .format(d.account)) + frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited") + .format(d.idx, d.account)) elif account_root_type == "Liability" and flt(d.credit) > 0: - frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry") - .format(d.account)) + frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited") + .format(d.idx, d.account)) if d.reference_name == self.name: frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column")) From c39e206fc7c7bebd7ce15b0b9b5ed4b4c4c194f6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 23 Feb 2021 14:26:55 +0530 Subject: [PATCH 09/31] fix: incorrect serial no issue in the stock reco --- erpnext/stock/utils.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index da4b529b01e..560165b532d 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -115,10 +115,17 @@ def get_serial_nos_data_after_transactions(args): order by posting_date, posting_time asc """, args, as_dict=1) for d in data: - if d.actual_qty > 0: - serial_nos.extend(get_serial_nos_data(d.serial_no)) - else: - serial_nos = list(set(serial_nos) - set(get_serial_nos_data(d.serial_no))) + for sn in get_serial_nos_data(d.serial_no): + if d.actual_qty > 0: + if sn not in serial_nos: + serial_nos.append(sn) + else: + serial_nos.remove(sn) + elif d.actual_qty < 0: + if sn in serial_nos: + serial_nos.remove(sn) + else: + serial_nos.append(sn) return '\n'.join(serial_nos) From 05a3c5a6bc89ecb71f7743f406d6ef76431aaaa4 Mon Sep 17 00:00:00 2001 From: pateljannat Date: Tue, 23 Feb 2021 18:15:50 +0530 Subject: [PATCH 10/31] fix: custom buttons in issue --- erpnext/support/doctype/issue/issue.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index bad40cc37fc..abb3c1b1110 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -39,8 +39,8 @@ frappe.ui.form.on("Issue", { refresh: function (frm) { - if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") { - if (frm.doc.service_level_agreement) { + if (frm.doc.status !== "Closed") { + if (frm.doc.service_level_agreement && frm.doc.agreement_fulfilled === "Ongoing") { set_time_to_resolve_and_response(frm); } From c18a8c58dd6cfbd421a06de0cdc1434e439c5b76 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 24 Feb 2021 14:39:00 +0530 Subject: [PATCH 11/31] fix: column names (#24713) --- .../report/bom_stock_calculated/bom_stock_calculated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py index c1204d36152..40b2e03c92a 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py @@ -88,7 +88,7 @@ def get_bom_stock(filters): GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1) def get_manufacturer_records(): - details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"]) + details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"]) manufacture_details = frappe._dict() for detail in details: dic = manufacture_details.setdefault(detail.get('parent'), {}) From 80d9576bb99cfbc4d97d2a582ad0f09ba0a8c57b Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 24 Feb 2021 16:51:35 +0530 Subject: [PATCH 12/31] fix: currency symbol in update items (#24727) --- erpnext/public/js/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 4547fc97a31..e8df13023c6 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -507,6 +507,7 @@ erpnext.utils.update_child_items = function(opts) { }, { fieldtype:'Currency', fieldname:"rate", + options: "currency", default: 0, read_only: 0, in_list_view: 1, From 9272ea838f9f4c146946186585d79bc53266121a Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 24 Feb 2021 18:13:29 +0530 Subject: [PATCH 13/31] fix: show custom button for saved projects (#24732) * fix: show custom button for saved projects * fix: slider --- erpnext/projects/doctype/project/project.js | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index cafa1bbb911..c52c615e4b5 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -75,23 +75,23 @@ frappe.ui.form.on("Project", { frm.add_custom_button(__('Cancelled'), () => { frm.events.set_status(frm, 'Cancelled'); }, __('Set Status')); - } - if (frappe.model.can_read("Task")) { - frm.add_custom_button(__("Gantt Chart"), function () { - frappe.route_options = { - "project": frm.doc.name - }; - frappe.set_route("List", "Task", "Gantt"); - }); - - frm.add_custom_button(__("Kanban Board"), () => { - frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', { - project: frm.doc.project_name - }).then(() => { - frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name); + if (frappe.model.can_read("Task")) { + frm.add_custom_button(__("Gantt Chart"), function () { + frappe.route_options = { + "project": frm.doc.name + }; + frappe.set_route("List", "Task", "Gantt"); }); - }); + + frm.add_custom_button(__("Kanban Board"), () => { + frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', { + project: frm.doc.project_name + }).then(() => { + frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name); + }); + }); + } } }, From 2b2e205b740a393056054d0d7c0f72cf447311db Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:05:29 +0530 Subject: [PATCH 14/31] fix: check if customer provided item while setting opening stock (#24633) --- erpnext/stock/doctype/item/item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index fe47e669261..6f454d5cb8b 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -177,7 +177,7 @@ class Item(WebsiteGenerator): if not self.valuation_rate and self.standard_rate: self.valuation_rate = self.standard_rate - if not self.valuation_rate: + if not self.valuation_rate and not self.is_customer_provided_item: frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered")) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry From 16193a614eab509570aa49bcccbd73e80331f41e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 24 Feb 2021 11:03:24 +0530 Subject: [PATCH 15/31] fix: make round off GLE always non-opening Opening GL entries can not be for profit and loss accounts. Round off accounts are by default P&L account. Hence when making opening entry, make round off entries as non-opening. Related issue: ISS-20-21-09677 --- erpnext/accounts/general_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 86504ba47fd..8d265080747 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -252,7 +252,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): if not round_off_gle: for k in ["voucher_type", "voucher_no", "company", - "posting_date", "remarks", "is_opening"]: + "posting_date", "remarks"]: round_off_gle[k] = gl_map[0][k] round_off_gle.update({ @@ -264,6 +264,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): "cost_center": round_off_cost_center, "party_type": None, "party": None, + "is_opening": "No", "against_voucher_type": None, "against_voucher": None }) From d3892612303bf51b86c4f362779da69494656a39 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 25 Feb 2021 18:04:58 +0530 Subject: [PATCH 16/31] fix(india): escape for special characters in JSON (bp #24695) (#24705) --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 3fc3d401f9c..43e1daab1fb 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -160,7 +160,7 @@ def get_item_list(invoice): item.update(d.as_dict()) item.sr_no = d.idx - item.description = d.item_name.replace('"', '\\"') + item.description = json.dumps(d.item_name)[1:-1] item.qty = abs(item.qty) item.discount_amount = 0 From 27fbfbf34eef80bcce9c3c934f5c8f4b5fd5f9fb Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 25 Feb 2021 19:01:58 +0530 Subject: [PATCH 17/31] fix: reference variable in pricing rule (#24714) --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 1fb6dd38bb6..646aec13559 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -131,7 +131,7 @@ class PricingRule(Document): for d in self.items: max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount") if max_discount and flt(self.discount_percentage) > flt(max_discount): - throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount)) + throw(_("Max discount allowed for item: {0} is {1}%").format(d.item_code, max_discount)) def validate_price_list_with_currency(self): if self.currency and self.for_price_list: From 5feb20d94bce2a28d68fd081dbfd9b82d5c04925 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 25 Feb 2021 19:05:25 +0530 Subject: [PATCH 18/31] feat: track changes to HR settings (#24740) related issue: FR-ISS-259927 --- erpnext/hr/doctype/hr_settings/hr_settings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index 6cb0e211f94..e91a160c0a8 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -175,7 +175,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-31 14:28:32.004121", + "modified": "2021-02-25 13:06:37.978785", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", @@ -192,5 +192,6 @@ } ], "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file From 27f120bc7cdceeb07e8b5241ab83b796304544a5 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 1 Mar 2021 10:46:17 +0530 Subject: [PATCH 19/31] fix(india): inflated item tax rate for e-invoicing (#24766) * fix(india): inflated item tax rate for e-invoicing * fix: credit note discount on net total --- erpnext/regional/india/e_invoice/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 43e1daab1fb..305f411cae1 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -196,9 +196,11 @@ def update_item_taxes(invoice, item): item[attr] = 0 for t in invoice.taxes: - # this contains item wise tax rate & tax amount (incl. discount) - item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code) - if t.account_head in gst_accounts_list: + is_applicable = t.tax_amount and t.account_head in gst_accounts_list + if is_applicable: + # this contains item wise tax rate & tax amount (incl. discount) + item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code) + item_tax_rate = item_tax_detail[0] # item tax amount excluding discount amount item_tax_amount = (item_tax_rate / 100) * item.base_net_amount @@ -223,7 +225,7 @@ def get_invoice_value_details(invoice): if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount: invoice_value_details.base_total = abs(invoice.base_total) - invoice_value_details.invoice_discount_amt = invoice.base_discount_amount + invoice_value_details.invoice_discount_amt = abs(invoice.base_discount_amount) else: invoice_value_details.base_total = abs(invoice.base_net_total) # since tax already considers discount amount From 2e8b06cf9fee3f3b6c67e095413421ab87d666ad Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 2 Mar 2021 18:20:47 +0530 Subject: [PATCH 20/31] fix: track setting changes --- .../doctype/buying_settings/buying_settings.json | 8 ++++++-- .../doctype/plaid_settings/plaid_settings.json | 7 +++---- .../doctype/shopify_settings/shopify_settings.json | 10 ++++++---- .../doctype/selling_settings/selling_settings.json | 4 ++-- .../shopping_cart_settings/shopping_cart_settings.json | 5 +++-- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index a492519591b..eb128f672aa 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -3,6 +3,7 @@ "description": "Settings for Buying Module", "doctype": "DocType", "document_type": "Other", + "engine": "InnoDB", "field_order": [ "supp_master_name", "supplier_group", @@ -92,7 +93,7 @@ "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-08-20 13:13:09.055189", + "modified": "2021-03-02 18:16:03.947813", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", @@ -107,5 +108,8 @@ "share": 1, "write": 1 } - ] + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json index 122aa41f4b9..a33f653e49a 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2018-10-25 10:02:48.656165", "doctype": "DocType", "editable_grid": 1, @@ -69,8 +68,7 @@ } ], "issingle": 1, - "links": [], - "modified": "2020-10-29 20:24:56.916104", + "modified": "2021-03-02 18:05:50.794105", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Plaid Settings", @@ -88,5 +86,6 @@ } ], "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 5339c99155b..d55583396ed 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -2,6 +2,7 @@ "creation": "2015-05-18 05:21:07.270859", "doctype": "DocType", "document_type": "System", + "engine": "InnoDB", "field_order": [ "status_html", "enable_shopify", @@ -258,8 +259,8 @@ } ], "issingle": 1, - "modified": "2020-05-28 12:32:11.384757", - "modified_by": "umair@erpnext.com", + "modified": "2021-03-02 18:06:00.868688", + "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Shopify Settings", "owner": "Administrator", @@ -276,5 +277,6 @@ } ], "sort_field": "modified", - "sort_order": "DESC" -} + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index dc2c4ce4a96..9541684a76c 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -573,7 +573,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-06-25 12:56:16.332039", + "modified": "2021-03-02 17:35:14.084342", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", @@ -605,7 +605,7 @@ "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 0, + "track_changes": 1, "track_seen": 0, "track_views": 0 } diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json index e22af9a601d..c19c4ddd7b8 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json @@ -161,7 +161,7 @@ "icon": "fa fa-shopping-cart", "idx": 1, "issingle": 1, - "modified": "2020-07-07 02:13:23.175604", + "modified": "2021-03-02 18:05:56.721122", "modified_by": "Administrator", "module": "Shopping Cart", "name": "Shopping Cart Settings", @@ -178,5 +178,6 @@ } ], "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file From aa90dc6aa3185f5d7c1985d4d13edaa31efd149f Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 9 Mar 2021 21:02:57 +0530 Subject: [PATCH 21/31] fix: do not send emails to disabled users from Employee Onboarding (#24796) --- erpnext/hr/utils.py | 52 ++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 5c95e000f9c..5a1959f11db 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -1,16 +1,19 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals -import frappe, erpnext -from frappe import _ -from frappe.utils import formatdate, format_datetime, getdate, get_datetime, nowdate, flt, cstr, add_days, today -from frappe.model.document import Document -from frappe.desk.form import assign_to +import erpnext +import frappe from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee +from frappe import _ +from frappe.desk.form import assign_to +from frappe.model.document import Document +from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate, + get_datetime, getdate, nowdate, today, unique) + class DuplicateDeclarationError(frappe.ValidationError): pass + class EmployeeBoardingController(Document): ''' Create the project and the task for the boarding process @@ -48,27 +51,38 @@ class EmployeeBoardingController(Document): continue task = frappe.get_doc({ - "doctype": "Task", - "project": self.project, - "subject": activity.activity_name + " : " + self.employee_name, - "description": activity.description, - "department": self.department, - "company": self.company, - "task_weight": activity.task_weight - }).insert(ignore_permissions=True) + "doctype": "Task", + "project": self.project, + "subject": activity.activity_name + " : " + self.employee_name, + "description": activity.description, + "department": self.department, + "company": self.company, + "task_weight": activity.task_weight + }).insert(ignore_permissions=True) activity.db_set("task", task.name) + users = [activity.user] if activity.user else [] if activity.role: - user_list = frappe.db.sql_list('''select distinct(parent) from `tabHas Role` - where parenttype='User' and role=%s''', activity.role) - users = users + user_list + user_list = frappe.db.sql_list(''' + SELECT + DISTINCT(has_role.parent) + FROM + `tabHas Role` has_role + LEFT JOIN `tabUser` user + ON has_role.parent = user.name + WHERE + has_role.parenttype = 'User' + AND user.enabled = 1 + AND has_role.role = %s + ''', activity.role) + users = unique(users + user_list) if "Administrator" in users: users.remove("Administrator") # assign the task the users if users: - self.assign_task_to_users(task, set(users)) + self.assign_task_to_users(task, users) def assign_task_to_users(self, task, users): for user in users: @@ -453,4 +467,4 @@ def get_previous_claimed_amount(employee, payroll_period, non_pro_rata=False, co }, as_dict=True) if sum_of_claimed_amount and flt(sum_of_claimed_amount[0].total_amount) > 0: total_claimed_amount = sum_of_claimed_amount[0].total_amount - return total_claimed_amount \ No newline at end of file + return total_claimed_amount From 86a163fce5f5eb22de40ea7a3f8f3cd0a3765098 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 11 Mar 2021 16:07:35 +0530 Subject: [PATCH 22/31] fix: use account_name only in consolidated report (#24834) Don't use account_number and only rely on account_name for preparing consolidated financial statement. Related issue: ISS-20-21-10217 --- .../consolidated_financial_statement.py | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index e15ae5cba9d..35c25109b73 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -224,8 +224,7 @@ def get_company_currency(filters=None): def calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters): for entries in gl_entries_by_account.values(): for entry in entries: - key = entry.account_number or entry.account_name - d = accounts_by_name.get(key) + d = accounts_by_name.get(entry.account_name) if d: for company in companies: # check if posting date is within the period @@ -240,7 +239,8 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies): """accumulate children's values in parent accounts""" for d in reversed(accounts): if d.parent_account: - account = d.parent_account.split('-')[0].strip() + account = d.parent_account_name + if not accounts_by_name.get(account): continue @@ -251,16 +251,34 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies): accounts_by_name[account]["opening_balance"] = \ accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0) + def get_account_heads(root_type, companies, filters): accounts = get_accounts(root_type, filters) if not accounts: return None, None + accounts = update_parent_account_names(accounts) + accounts, accounts_by_name, parent_children_map = filter_accounts(accounts) return accounts, accounts_by_name +def update_parent_account_names(accounts): + """Update parent_account_name in accounts list. + + parent_name is `name` of parent account which could have other prefix + of account_number and suffix of company abbr. This function adds key called + `parent_account_name` which does not have such prefix/suffix. + """ + name_to_account_map = { d.name : d.account_name for d in accounts } + + for account in accounts: + if account.parent_account: + account["parent_account_name"] = name_to_account_map[account.parent_account] + + return accounts + def get_companies(filters): companies = {} all_companies = get_subsidiary_companies(filters.get('company')) @@ -367,9 +385,9 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g convert_to_presentation_currency(gl_entries, currency_info) for entry in gl_entries: - key = entry.account_number or entry.account_name - validate_entries(key, entry, accounts_by_name, accounts) - gl_entries_by_account.setdefault(key, []).append(entry) + account_name = entry.account_name + validate_entries(account_name, entry, accounts_by_name, accounts) + gl_entries_by_account.setdefault(account_name, []).append(entry) return gl_entries_by_account @@ -438,8 +456,7 @@ def filter_accounts(accounts, depth=10): parent_children_map = {} accounts_by_name = {} for d in accounts: - key = d.account_number or d.account_name - accounts_by_name[key] = d + accounts_by_name[d.account_name] = d parent_children_map.setdefault(d.parent_account or None, []).append(d) filtered_accounts = [] From 5ae40871d875990fc463a77da6cfc57e79a98379 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 11 Mar 2021 16:08:40 +0530 Subject: [PATCH 23/31] fix: do not update PE title during data import (#24835) * fix: do not update PE title during data import Related issue: ISS-20-21-10132 * fix: make title field read only --- erpnext/accounts/doctype/payment_entry/payment_entry.json | 7 ++++--- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index acfc660c4f7..07e688dd45d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -531,7 +531,8 @@ "fieldtype": "Data", "hidden": 1, "label": "Title", - "print_hide": 1 + "print_hide": 1, + "read_only": 1 }, { "depends_on": "party", @@ -575,7 +576,7 @@ } ], "is_submittable": 1, - "modified": "2019-11-06 12:59:43.151721", + "modified": "2021-03-10 13:05:16.958866", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", @@ -619,4 +620,4 @@ "sort_order": "DESC", "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d1630f3ea69..73a946bd066 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -441,6 +441,10 @@ class PaymentEntry(AccountsController): .format(total_negative_outstanding), InvalidPaymentEntry) def set_title(self): + if frappe.flags.in_import and self.title: + # do not set title dynamically if title exists during data import. + return + if self.payment_type in ("Receive", "Pay"): self.title = self.party else: From 017d168b0371f449914f46beb5d7c778980fcfe7 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Sat, 13 Mar 2021 23:38:30 +0530 Subject: [PATCH 24/31] fix(Bank Reconciliation): Filter Bank Account drop-down list --- .../doctype/bank_reconciliation/bank_reconciliation.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js index 19fadbf6ded..cd727b1daaf 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js @@ -21,6 +21,14 @@ frappe.ui.form.on("Bank Reconciliation", { }; }); + frm.set_query("bank_account", function() { + return { + "filters": { + "is_company_account": 1 + } + }; + }); + frm.set_value("from_date", frappe.datetime.month_start()); frm.set_value("to_date", frappe.datetime.month_end()); }, From 55d45532b45e20acb70213ece84ba02aeec20d37 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Mon, 15 Mar 2021 13:24:50 +0100 Subject: [PATCH 25/31] fix: UOM length unit in global setup list is empty (#24853) * When ERPNext is installed in localized language the category of UOM Conversion Factor is tranlasted into installed languaes. Filter must be locallized * fix * fix --- erpnext/setup/doctype/global_defaults/global_defaults.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.js b/erpnext/setup/doctype/global_defaults/global_defaults.js index 552331aac89..942dd5989ea 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.js +++ b/erpnext/setup/doctype/global_defaults/global_defaults.js @@ -17,7 +17,7 @@ frappe.ui.form.on('Global Defaults', { method: "frappe.client.get_list", args: { doctype: "UOM Conversion Factor", - filters: { "category": "Length" }, + filters: { "category": __("Length") }, fields: ["to_uom"], limit_page_length: 500 }, From 2e2f3f9578240e62846c3e9a6a6938a8b7c9dac8 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Mon, 15 Mar 2021 08:27:37 -0400 Subject: [PATCH 26/31] fix: stock move dialog duplicate submit actions (#24859) * fix: duplicate submit actions * fix sider errors. --- erpnext/stock/dashboard/item_dashboard.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 1bfa2cf56c6..97f5494c5f3 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -156,7 +156,8 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb fieldtype: 'Float', description: __('Available {0}', [actual_qty]) }, {fieldname: 'rate', label: __('Rate'), fieldtype: 'Currency', hidden: 1 }, ], - }) + }); + var submitted = false; dialog.show(); dialog.get_field('item_code').set_input(item); @@ -180,6 +181,7 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb } dialog.set_primary_action(__('Submit'), function() { + if(submitted) return; var values = dialog.get_values(); if(!values) { return; @@ -192,6 +194,7 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb frappe.msgprint(__('Source and target warehouse must be different')); } + submitted = true; frappe.call({ method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry', args: values, From f9f10ed743aa6dd64b97e286c25b9dfe47248466 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 15 Mar 2021 17:58:17 +0530 Subject: [PATCH 27/31] fix: Add warning for invalid GST invoice numbers (#24837) * fix: Add warning for invalid GST invoice numbers GST Invoice numbers should be 16 characters alphanumeric with dash(/) or slash(-) only. Add check for doc.name before saving and warn about naming series. * refactor: move regex patterns to global variables Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/hooks.py | 3 +++ erpnext/regional/india/utils.py | 29 ++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 219e426d782..2d995ed0e67 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -261,6 +261,9 @@ doc_events = { ('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): { 'validate': ['erpnext.regional.india.utils.set_place_of_supply'] }, + ('Sales Invoice', 'Purchase Invoice'): { + 'validate': ['erpnext.regional.india.utils.validate_document_name'] + }, "Contact": { "on_trash": "erpnext.support.doctype.issue.issue.update_issue", "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information", diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 89677433392..99b37818a0a 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe, re, json from frappe import _ import erpnext -from frappe.utils import cstr, flt, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words +from frappe.utils import cstr, flt, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words, getdate from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount, calculate_outstanding_amount from erpnext.controllers.accounts_controller import get_taxes_and_charges @@ -15,6 +15,13 @@ from erpnext.accounts.utils import get_account_currency from frappe.contacts.doctype.address.address import get_address_display from frappe.model.utils import get_fetch_values + +GST_INVOICE_NUMBER_FORMAT = re.compile(r"^[a-zA-Z0-9\-/]+$") #alphanumeric and - / +GSTIN_FORMAT = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$") +GSTIN_UIN_FORMAT = re.compile("^[0-9]{4}[A-Z]{3}[0-9]{5}[0-9A-Z]{3}") +PAN_NUMBER_FORMAT = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}") + + def validate_gstin_for_india(doc, method): if hasattr(doc, 'gst_state') and doc.gst_state: doc.gst_state_number = state_numbers[doc.gst_state] @@ -38,12 +45,10 @@ def validate_gstin_for_india(doc, method): frappe.throw(_("Invalid GSTIN! A GSTIN must have 15 characters.")) if gst_category and gst_category == 'UIN Holders': - p = re.compile("^[0-9]{4}[A-Z]{3}[0-9]{5}[0-9A-Z]{3}") - if not p.match(doc.gstin): + if not GSTIN_UIN_FORMAT.match(doc.gstin): frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers")) else: - p = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$") - if not p.match(doc.gstin): + if not GSTIN_FORMAT.match(doc.gstin): frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.")) validate_gstin_check_digit(doc.gstin) @@ -162,6 +167,20 @@ def set_transporter_address(doc, method=None): doc.transporter_address = transporter_address doc.transporter_address_display = get_address_display(transporter_address) +def validate_document_name(doc, method=None): + """Validate GST invoice number requirements.""" + country = frappe.get_cached_value("Company", doc.company, "country") + + # Date was chosen as start of next FY to avoid irritating current users. + if country != "India" or getdate(doc.posting_date) < getdate("2021-04-01"): + return + + if len(doc.name) > 16: + frappe.throw(_("Maximum length of document number should be 16 characters as per GST rules. Please change the naming series.")) + + if not GST_INVOICE_NUMBER_FORMAT.match(doc.name): + frappe.throw(_("Document name should only contain alphanumeric values, dash(-) and slash(/) characters as per GST rules. Please change the naming series.")) + # don't remove this function it is used in tests def test_method(): '''test function''' From 386b7fd2c37cc5f1a5aa927b71b06197eb0bf5a8 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 15 Mar 2021 18:03:03 +0530 Subject: [PATCH 28/31] fix: use method name as title in error log (#24880) --- .../doctype/amazon_mws_settings/amazon_methods.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py index cc75a0afbe0..8309d4804f3 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py @@ -117,7 +117,7 @@ def call_mws_method(mws_method, *args, **kwargs): return response except Exception as e: delay = math.pow(4, x) * 125 - frappe.log_error(message=e, title=str(mws_method)) + frappe.log_error(message=e, title="Method {} failed".format(mws_method.__name__)) time.sleep(delay) continue From 6cf37d58f1a5f4a99fc2e919a9dd51433272d7be Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 15 Mar 2021 18:04:22 +0530 Subject: [PATCH 29/31] fix: Unequal debit and credit issue on RCM Invoice (#24839) * fix: Unequal debit and credit issue on RCM Invoice * fix: Travis --- erpnext/regional/india/utils.py | 42 ++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 99b37818a0a..a808048d17d 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -734,25 +734,12 @@ def update_grand_total_for_rcm(doc, method): if country != 'India': return - if not doc.total_taxes_and_charges: + gst_tax, base_gst_tax = get_gst_tax_amount(doc) + + if not base_gst_tax: return if doc.reverse_charge == 'Y': - gst_accounts = get_gst_accounts(doc.company) - gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ - + gst_accounts.get('igst_account') - - base_gst_tax = 0 - gst_tax = 0 - - for tax in doc.get('taxes'): - if tax.category not in ("Total", "Valuation and Total"): - continue - - if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list: - base_gst_tax += tax.base_tax_amount_after_discount_amount - gst_tax += tax.tax_amount_after_discount_amount - doc.taxes_and_charges_added -= gst_tax doc.total_taxes_and_charges -= gst_tax doc.base_taxes_and_charges_added -= base_gst_tax @@ -784,6 +771,11 @@ def make_regional_gl_entries(gl_entries, doc): if country != 'India': return gl_entries + gst_tax, base_gst_tax = get_gst_tax_amount(doc) + + if not base_gst_tax: + return gl_entries + if doc.reverse_charge == 'Y': gst_accounts = get_gst_accounts(doc.company) gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ @@ -811,3 +803,21 @@ def make_regional_gl_entries(gl_entries, doc): ) return gl_entries + +def get_gst_tax_amount(doc): + gst_accounts = get_gst_accounts(doc.company) + gst_account_list = gst_accounts.get('cgst_account', []) + gst_accounts.get('sgst_account', []) \ + + gst_accounts.get('igst_account', []) + + base_gst_tax = 0 + gst_tax = 0 + + for tax in doc.get('taxes'): + if tax.category not in ("Total", "Valuation and Total"): + continue + + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list: + base_gst_tax += tax.base_tax_amount_after_discount_amount + gst_tax += tax.tax_amount_after_discount_amount + + return gst_tax, base_gst_tax From c2e2cf49333f20fa4f84f02f1496c964f8544688 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 13 Mar 2021 12:48:14 +0530 Subject: [PATCH 30/31] fix: revert stock balance value calculation --- erpnext/stock/report/stock_balance/stock_balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 145562cdc1b..8ea98cba6f6 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -196,7 +196,7 @@ def get_item_warehouse_map(filters, sle): else: qty_diff = flt(d.actual_qty) - value_diff = flt(d.stock_value) - flt(qty_dict.bal_val) + value_diff = flt(d.stock_value_difference) if d.posting_date < from_date: qty_dict.opening_qty += qty_diff From 786d78bce29d5878bef21d3a8f3355679d50233f Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 19 Mar 2021 19:44:51 +0550 Subject: [PATCH 31/31] bumped to version 12.19.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3ed37046b08..a76090d609d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.18.0' +__version__ = '12.19.0' def get_default_company(user=None): '''Get default company for user'''