From 22a069d4b0b642fcd06ed4858731c30905930df9 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 16 Mar 2020 18:24:28 +0530 Subject: [PATCH 01/25] fix: Wrong status --- erpnext/hr/doctype/expense_claim/expense_claim.json | 4 +++- erpnext/hr/doctype/expense_claim/expense_claim.py | 2 +- erpnext/patches.txt | 1 + .../patches/v12_0/set_correct_status_for_expense_claim.py | 7 +++++++ 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v12_0/set_correct_status_for_expense_claim.py diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 5c2f4901713..770b47709dd 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -278,6 +278,7 @@ "label": "More Details" }, { + "allow_on_submit": 1, "default": "Draft", "fieldname": "status", "fieldtype": "Select", @@ -366,7 +367,8 @@ "icon": "fa fa-money", "idx": 1, "is_submittable": 1, - "modified": "2019-11-08 14:13:08.964547", + "links": [], + "modified": "2020-03-16 18:11:07.861985", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index d29b3f75879..e9f15cdfb9a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -45,7 +45,7 @@ class ExpenseClaim(AccountsController): paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount) precision = self.precision("grand_total") if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 - and flt(self.grand_total, precision) == flt(paid_amount, precision))) \ + and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \ and self.docstatus == 1 and self.approval_status == 'Approved': self.status = "Paid" elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved': diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4e72d5ecd6b..ed14ecd4897 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -650,3 +650,4 @@ erpnext.patches.v12_0.update_price_or_product_discount erpnext.patches.v12_0.add_export_type_field_in_party_master erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22 erpnext.patches.v12_0.create_irs_1099_field_united_states +erpnext.patches.v12_0.set_correct_status_for_expense_claim diff --git a/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py new file mode 100644 index 00000000000..3d3980743e6 --- /dev/null +++ b/erpnext/patches/v12_0/set_correct_status_for_expense_claim.py @@ -0,0 +1,7 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + +def execute(): + frappe.db.sql("update `tabExpense Claim` set status = 'Paid' where (total_advance_amount + total_amount_reimbursed ) = total_sanctioned_amount") \ No newline at end of file From 34288b0f38017b0ce9e5408f77e4b97afd229f25 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 6 Apr 2020 18:25:23 +0530 Subject: [PATCH 02/25] feat: (minor) purchase register filters --- .../purchase_register/purchase_register.js | 27 +++++++++++++++++ .../purchase_register/purchase_register.py | 29 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index 42b35c2a997..b2b95b2b81b 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -34,6 +34,33 @@ frappe.query_reports["Purchase Register"] = { "label": __("Mode of Payment"), "fieldtype": "Link", "options": "Mode of Payment" + }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center" + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse" + }, + { + "fieldname":"item_group", + "label": __("Item Group"), + "fieldtype": "Link", + "options": "Item Group" } ] } + +erpnext.dimension_filters.forEach((dimension) => { + frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); +}); \ No newline at end of file diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 3f8abb76e26..795dcb18811 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import flt from frappe import msgprint, _ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def execute(filters=None): return _execute(filters) @@ -134,6 +135,34 @@ def get_conditions(filters): if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s" + if filters.get("cost_center"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)""" + + if filters.get("warehouse"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)""" + + if filters.get("item_group"): + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)""" + + accounting_dimensions = get_accounting_dimensions(as_list=False) + + if accounting_dimensions: + for dimension in accounting_dimensions: + if filters.get(dimension.fieldname): + if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): + filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, + filters.get(dimension.fieldname)) + + conditions += """ and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname) + return conditions def get_invoices(filters, additional_query_columns): From c3bb5a97f81ccb2ed64cfbdccdc43658edb9078d Mon Sep 17 00:00:00 2001 From: vishdha Date: Tue, 7 Apr 2020 19:59:06 +0530 Subject: [PATCH 03/25] fix: item_name added for message --- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 5fe89d6e227..65d6c782b86 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -64,7 +64,7 @@ class StockLedgerEntry(Document): frappe.throw(_("Actual Qty is mandatory")) def validate_item(self): - item_det = frappe.db.sql("""select name, has_batch_no, docstatus, + item_det = frappe.db.sql("""select name, item_name, has_batch_no, docstatus, is_stock_item, has_variants, stock_uom, create_new_batch from tabItem where name=%s""", self.item_code, as_dict=True) @@ -80,9 +80,9 @@ class StockLedgerEntry(Document): if self.voucher_type != 'Stock Reconciliation': if item_det.has_batch_no ==1: if not self.batch_no: - frappe.throw(_("Batch number is mandatory for Item {0}").format(self.item_code)) + frappe.throw(_("Batch number is mandatory for Item {0}:{1}").format(self.item_code, item_det.item_name)) elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): - frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code)) + frappe.throw(_("{0} is not a valid Batch Number for Item {1}:{2}").format(self.batch_no, self.item_code, item_det.item_name)) elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No": frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) From 849ec84852dfb3437d65b579c8175c14dce89af4 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 7 Apr 2020 21:00:57 +0530 Subject: [PATCH 04/25] feat: Provision to set Default Item Manufacturer - Is Default checkbox added in Item Manufacturer - Default Item Manufacturer and Part No fields added to Item Master - Manufacturer Part No field editable in all child tables with validation - Manufacturer and Part No auto fetched via get_item_details in child table --- .../purchase_invoice_item.json | 5 +-- .../purchase_order_item.json | 5 +-- .../supplier_quotation_item.json | 7 ++-- erpnext/public/js/controllers/buying.js | 26 ++++++++++++- erpnext/stock/doctype/item/item.json | 16 +++++++- .../item_manufacturer/item_manufacturer.json | 14 ++++++- .../item_manufacturer/item_manufacturer.py | 38 +++++++++++++++++++ .../material_request_item.json | 5 +-- .../purchase_receipt_item.json | 5 +-- erpnext/stock/get_item_details.py | 3 ++ 10 files changed, 105 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 296c7049ce4..a8dfab6a54b 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -753,8 +753,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "depends_on": "is_fixed_asset", @@ -770,7 +769,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-01 14:20:17.297284", + "modified": "2020-04-07 18:34:35.104178", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index c409c1f46e0..5a964485a5d 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -698,8 +698,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "default": "0", @@ -712,7 +711,7 @@ ], "idx": 1, "istable": 1, - "modified": "2019-11-07 17:19:12.090355", + "modified": "2020-04-07 18:35:17.558928", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json index 7d7d6f4d3db..b50e834ec73 100644 --- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json +++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-05-22 12:43:10", "doctype": "DocType", @@ -522,8 +523,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "fieldname": "column_break_15", @@ -532,7 +532,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-06-02 05:32:46.019237", + "links": [], + "modified": "2020-04-07 18:35:51.175947", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation Item", diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 27a9de95e0e..afbdbc661d3 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -379,7 +379,31 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } }); } - } + }, + + manufacturer_part_no: function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + + if (row.manufacturer_part_no) { + frappe.model.get_value('Item Manufacturer', + { + 'item_code': row.item_code, + 'manufacturer': row.manufacturer, + 'manufacturer_part_no': row.manufacturer_part_no + }, + 'name', + function(data) { + if (!data) { + let msg = { + message: __("Manufacturer Part Number {0} is invalid", [row.manufacturer_part_no]), + title: __("Invalid Part Number") + } + frappe.throw(msg); + } + }); + + } + } }); cur_frm.add_fetch('project', 'cost_center', 'cost_center'); diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index aa6b2fedd7c..7d2e3112fb3 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -114,6 +114,8 @@ "is_sub_contracted_item", "column_break_74", "customer_code", + "default_item_manufacturer", + "default_manufacturer_part_no", "website_section", "show_in_website", "show_variant_in_website", @@ -1038,6 +1040,18 @@ "fieldname": "auto_create_assets", "fieldtype": "Check", "label": "Auto Create Assets on Purchase" + }, + { + "fieldname": "default_item_manufacturer", + "fieldtype": "Data", + "label": "Default Item Manufacturer", + "read_only": 1 + }, + { + "fieldname": "default_manufacturer_part_no", + "fieldtype": "Data", + "label": "Default Manufacturer Part No", + "read_only": 1 } ], "has_web_view": 1, @@ -1046,7 +1060,7 @@ "image_field": "image", "links": [], "max_attachments": 1, - "modified": "2020-03-24 16:14:36.950677", + "modified": "2020-04-07 15:56:06.195722", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json index 956c92e6738..0cef6eafaef 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "creation": "2019-06-02 04:41:37.332911", "doctype": "DocType", @@ -10,7 +11,8 @@ "manufacturer_part_no", "column_break_3", "item_name", - "description" + "description", + "is_default" ], "fields": [ { @@ -52,9 +54,17 @@ "fieldtype": "Small Text", "label": "Description", "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Default" } ], - "modified": "2019-06-06 19:07:31.175919", + "links": [], + "modified": "2020-04-07 20:25:55.507905", "modified_by": "Administrator", "module": "Stock", "name": "Item Manufacturer", diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py index 67eab82d970..c27d1be7892 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py @@ -11,6 +11,10 @@ from frappe.model.document import Document class ItemManufacturer(Document): def validate(self): self.validate_duplicate_entry() + self.manage_default_item_manufacturer() + + def on_trash(self): + self.manage_default_item_manufacturer(delete=True) def validate_duplicate_entry(self): if self.is_new(): @@ -24,6 +28,40 @@ class ItemManufacturer(Document): frappe.throw(_("Duplicate entry against the item code {0} and manufacturer {1}") .format(self.item_code, self.manufacturer)) + def manage_default_item_manufacturer(self, delete=False): + from frappe.model.utils import set_default + + item = frappe.get_doc("Item", self.item_code) + default_manufacturer = item.default_item_manufacturer + default_part_no = item.default_manufacturer_part_no + + if not self.is_default: + # if unchecked and default in Item master, clear it. + if default_manufacturer == self.manufacturer and default_part_no == self.manufacturer_part_no: + frappe.db.set_value("Item", item.name, + { + "default_item_manufacturer": None, + "default_manufacturer_part_no": None + }) + + elif self.is_default: + set_default(self, "item_code") + manufacturer, manufacturer_part_no = default_manufacturer, default_part_no + + if delete: + manufacturer, manufacturer_part_no = None, None + + elif (default_manufacturer != self.manufacturer) or \ + (default_manufacturer == self.manufacturer and default_part_no != self.manufacturer_part_no): + manufacturer = self.manufacturer + manufacturer_part_no = self.manufacturer_part_no + + frappe.db.set_value("Item", item.name, + { + "default_item_manufacturer": manufacturer, + "default_manufacturer_part_no": manufacturer_part_no + }) + @frappe.whitelist() def get_item_manufacturer_part_no(item_code, manufacturer): return frappe.db.get_value("Item Manufacturer", diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 9d1dafb136b..30206b62d0c 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -404,14 +404,13 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-02-25 03:09:10.698967", + "modified": "2020-04-07 18:37:54.495112", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index d5cf4cadb14..e1dc95be481 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -799,8 +799,7 @@ { "fieldname": "manufacturer_part_no", "fieldtype": "Data", - "label": "Manufacturer Part Number", - "read_only": 1 + "label": "Manufacturer Part Number" }, { "depends_on": "is_fixed_asset", @@ -823,7 +822,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-05 14:19:48.799370", + "modified": "2020-04-07 18:38:21.141558", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 54dc685e4ef..51f27fd3e78 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -339,6 +339,9 @@ def get_basic_details(args, item, overwrite_warehouse=True): else: out["manufacturer_part_no"] = None out["manufacturer"] = None + else: + out["manufacturer"], out["manufacturer_part_no"] = frappe.get_value("Item", item.name, + ["default_item_manufacturer", "default_manufacturer_part_no"] ) child_doctype = args.doctype + ' Item' meta = frappe.get_meta(child_doctype) From 073d1c4c13b13a914511e53bbdbea8662df7934c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 10 Apr 2020 20:22:42 +0530 Subject: [PATCH 05/25] fix: SQL ssyntax error --- .../report/purchase_register/purchase_register.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 795dcb18811..9399e707390 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -67,7 +67,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum total_tax += tax_amount row.append(tax_amount) - # total tax, grand total, rounded total & outstanding amount + # total tax, grand total, rounded total & outstanding amount row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount] data.append(row) @@ -153,15 +153,19 @@ def get_conditions(filters): accounting_dimensions = get_accounting_dimensions(as_list=False) if accounting_dimensions: + common_condition = """ + and exists(select name from `tabPurchase Invoice Item` + where parent=`tabPurchase Invoice`.name + """ for dimension in accounting_dimensions: if filters.get(dimension.fieldname): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters.get(dimension.fieldname)) - conditions += """ and exists(select name from `tabPurchase Invoice Item` - where parent=`tabPurchase Invoice`.name - and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname) + conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname) + else: + conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname) return conditions From 87c6b6d12f36d101eb351008e2800dc8f1db2720 Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Sat, 11 Apr 2020 00:44:12 +0530 Subject: [PATCH 06/25] fix: GSTR 3B Report tax amount calculation The tax amount after discount amount should be considered for tax calculation. Fixes #21231 Signed-off-by: Syed Mujeer Hashmi (cherry picked from commit 58a16f1a3bc3c5a05cfed9ed4bb52cacd638b341) --- erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 79dace7925e..15dcbd5e154 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -277,7 +277,7 @@ class GSTR3BReport(Document): def get_itc_details(self, reverse_charge='N'): itc_amount = frappe.db.sql(""" - select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge + select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s @@ -312,7 +312,7 @@ class GSTR3BReport(Document): and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) - inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount) as tax_amount, s.place_of_supply, s.gst_category + inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount_after_discount_amount) as tax_amount, s.place_of_supply, s.gst_category from `tabSales Invoice` s, `tabSales Taxes and Charges` t where t.parent = s.name and s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') @@ -385,7 +385,7 @@ class GSTR3BReport(Document): tax_template = 'Purchase Taxes and Charges' tax_amounts = frappe.db.sql(""" - select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head + select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head from `tab{doctype}` s , `tab{template}` t where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s From 1b15734bd8b2367f158c117b5b3835e283e707f3 Mon Sep 17 00:00:00 2001 From: vishdha Date: Mon, 13 Apr 2020 12:27:26 +0530 Subject: [PATCH 07/25] fix: batch message ux improved --- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 65d6c782b86..837dfd5bd30 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -79,10 +79,11 @@ class StockLedgerEntry(Document): # check if batch number is required if self.voucher_type != 'Stock Reconciliation': if item_det.has_batch_no ==1: + batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name if not self.batch_no: - frappe.throw(_("Batch number is mandatory for Item {0}:{1}").format(self.item_code, item_det.item_name)) + frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item)) elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): - frappe.throw(_("{0} is not a valid Batch Number for Item {1}:{2}").format(self.batch_no, self.item_code, item_det.item_name)) + frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item)) elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No": frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) From 8b31e3ec2adcb60ff8315654698f65599e6ff7c2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 14 Apr 2020 09:33:53 +0530 Subject: [PATCH 08/25] fix: consider revereted expired leaves entry (#21256) --- .../hr/doctype/leave_application/leave_application.py | 9 ++++++--- erpnext/hr/doctype/leave_encashment/leave_encashment.py | 1 - .../hr/doctype/leave_ledger_entry/leave_ledger_entry.py | 3 +++ .../employee_leave_balance/employee_leave_balance.py | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index a800923a8c2..c870cc5d5d0 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -575,19 +575,22 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): return leave_days def skip_expiry_leaves(leave_entry, date): - ''' Checks whether the expired leaves coincide with the to_date of leave balance check ''' + ''' Checks whether the expired leaves coincide with the to_date of leave balance check. + This allows backdated leave entry creation for non carry forwarded allocation ''' end_date = frappe.db.get_value("Leave Allocation", {'name': leave_entry.transaction_name}, ['to_date']) return True if end_date == date and not leave_entry.is_carry_forward else False def get_leave_entries(employee, leave_type, from_date, to_date): - ''' Returns leave entries between from_date and to_date ''' + ''' Returns leave entries between from_date and to_date. ''' return frappe.db.sql(""" SELECT employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, holiday_list, is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s - AND docstatus=1 AND leaves<0 + AND docstatus=1 + AND (leaves<0 + OR is_expired=1) AND (from_date between %(from_date)s AND %(to_date)s OR to_date between %(from_date)s AND %(to_date)s OR (from_date < %(from_date)s AND to_date > %(to_date)s)) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index ad2cc02fd7c..7d6fd422c0f 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -8,7 +8,6 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, nowdate, flt from erpnext.hr.utils import set_employee_name -from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index bfc6d95d172..9ed58c9e59b 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -141,6 +141,7 @@ def expire_allocation(allocation, expiry_date=None): leaves = get_remaining_leaves(allocation) expiry_date = expiry_date if expiry_date else allocation.to_date + # allows expired leaves entry to be created/reverted if leaves: args = dict( leaves=flt(leaves) * -1, @@ -160,6 +161,8 @@ def expire_carried_forward_allocation(allocation): from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date) leaves = flt(allocation.leaves) + flt(leaves_taken) + + # allow expired leaves entry to be created if leaves > 0: args = frappe._dict( transaction_name=allocation.name, diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index e967db87c4a..35c8630e8e2 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -76,7 +76,7 @@ def get_data(filters, leave_types): opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) # closing balance - closing = get_leave_balance_on(employee.name, leave_type, filters.to_date) + closing = max(opening - leaves_taken, 0) row += [opening, leaves_taken, closing] From 4b1d0d19fbb31f236dedb1651e2e3c9576b22e3f Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Tue, 14 Apr 2020 11:42:22 +0530 Subject: [PATCH 09/25] fix: str object not callable (#21228) --- .../chart_of_accounts_importer/chart_of_accounts_importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 00311c4f66d..3526ab5c821 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -108,7 +108,7 @@ def build_forest(data): error_messages = [] for i in data: - account_name, __, account_number, is_group, account_type, root_type = i + account_name, dummy, account_number, is_group, account_type, root_type = i if not account_name: error_messages.append("Row {0}: Please enter Account Name".format(line_no)) From 02a9b5a8a55520a41c323b1a9144bc701337cbb6 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 14 Apr 2020 11:45:56 +0530 Subject: [PATCH 10/25] chore: hide redundant base received amount (#21229) * fix: formatting * chore: hide redundant base received amount --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2192b7bf989..fbf11bca5de 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -154,8 +154,11 @@ frappe.ui.form.on('Payment Entry', { frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); - frm.toggle_display("base_received_amount", (frm.doc.paid_to_account_currency != company_currency && - frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)); + frm.toggle_display("base_received_amount", ( + frm.doc.paid_to_account_currency != company_currency && + frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency + && frm.doc.base_paid_amount != frm.doc.base_received_amount + )); frm.toggle_display("received_amount", (frm.doc.payment_type=="Internal Transfer" || frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)) @@ -486,6 +489,7 @@ frappe.ui.form.on('Payment Entry', { paid_amount: function(frm) { frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); frm.trigger("reset_received_amount"); + frm.events.hide_unhide_fields(frm); }, received_amount: function(frm) { @@ -509,6 +513,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); frm.set_paid_amount_based_on_received_amount = false; + frm.events.hide_unhide_fields(frm); }, reset_received_amount: function(frm) { From 7841653a030d6ed6bfd7a7f2c7ff65683b5fc4ea Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 14 Apr 2020 11:47:22 +0530 Subject: [PATCH 11/25] fix: Project Update Email Error (#21209) * fix: Project Update Email Error * fix: Removed mandatory depends on Co-authored-by: Himanshu --- erpnext/projects/doctype/project/project.js | 5 +++++ erpnext/projects/doctype/project/project.json | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 3570a0f2be4..9ba9ccfb4e8 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -118,6 +118,11 @@ frappe.ui.form.on("Project", { }); }, + collect_progress: function(frm) { + if (frm.doc.collect_progress) { + frm.set_df_property("message", "reqd", 1); + } + } }); function open_form(frm, doctype, child_doctype, parentfield) { diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 7d47db371d5..316fb1a4738 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -435,7 +435,7 @@ }, { "depends_on": "collect_progress", - "description": "Message will sent to users to get their status on the project", + "description": "Message will be sent to the users to get their status on the Project", "fieldname": "message", "fieldtype": "Text", "label": "Message" @@ -444,7 +444,7 @@ "icon": "fa fa-puzzle-piece", "idx": 29, "max_attachments": 4, - "modified": "2019-09-24 15:02:50.208301", + "modified": "2020-04-09 14:17:15.524529", "modified_by": "Administrator", "module": "Projects", "name": "Project", From 3d667bfe558c5df0441e426adf5058238db3c69b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 14 Apr 2020 11:48:56 +0530 Subject: [PATCH 12/25] fix: minor issues (#21203) --- .../chart_of_accounts_importer.py | 2 +- .../doctype/stock_reconciliation/stock_reconciliation.json | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 3526ab5c821..f496280317a 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -97,7 +97,7 @@ def build_forest(data): return [parent_account] elif account_name == child: parent_account_list = return_parent(data, parent_account) - if not parent_account_list: + if not parent_account_list and parent_account: frappe.throw(_("The parent account {0} does not exists") .format(parent_account)) return [child] + parent_account_list diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json index 7f4efba33f8..b7d1497319f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json @@ -4,6 +4,7 @@ "description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.", "doctype": "DocType", "document_type": "Document", + "engine": "InnoDB", "field_order": [ "naming_series", "company", @@ -44,11 +45,11 @@ "reqd": 1 }, { - "default": "Stock Reconciliation", "fieldname": "purpose", "fieldtype": "Select", "label": "Purpose", - "options": "Opening Stock\nStock Reconciliation" + "options": "\nOpening Stock\nStock Reconciliation", + "reqd": 1 }, { "fieldname": "col1", @@ -153,7 +154,7 @@ "idx": 1, "is_submittable": 1, "max_attachments": 1, - "modified": "2019-05-26 09:03:09.542141", + "modified": "2020-04-08 17:02:47.196206", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation", From 2833db559e3ada9e977765cec6c872c764180430 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 14 Apr 2020 11:50:54 +0530 Subject: [PATCH 13/25] fix: Set Price List in case of User Permissions (#21238) Frontport of #18968 --- erpnext/accounts/party.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 156f2181b87..3d9ff273ce2 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -163,8 +163,9 @@ def get_default_price_list(party): def set_price_list(party_details, party, party_type, given_price_list, pos=None): # price list price_list = get_permitted_documents('Price List') - - if price_list: + + # if there is only one permitted document based on user permissions, set it + if price_list and len(price_list) == 1: price_list = price_list[0] elif pos and party_type == 'Customer': customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list') From 335d35a98871cfe99113c49bb6e97fa2a23c224a Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 14 Apr 2020 11:53:00 +0530 Subject: [PATCH 14/25] fix: Lead Contact with blank first name via Customer (#21248) --- erpnext/selling/doctype/customer/customer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 4fca05ba7ea..493d8730602 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -150,7 +150,7 @@ class Customer(TransactionBase): contact.save() else: - lead.lead_name = lead.lead_name.split(" ") + lead.lead_name = lead.lead_name.lstrip().split(" ") lead.first_name = lead.lead_name[0] lead.last_name = " ".join(lead.lead_name[1:]) From 4eea8bd2c010e1183c1259e8a3a59beb1f45bd35 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 14 Apr 2020 12:49:38 +0530 Subject: [PATCH 15/25] fix: on changing qty free item not removed (#21250) --- .../accounts/doctype/pricing_rule/utils.py | 7 ++--- erpnext/public/js/controllers/transaction.js | 26 ++++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 2ffa27f6d99..100bb1d3e38 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -178,7 +178,8 @@ def filter_pricing_rules(args, pricing_rules, doc=None): if pricing_rules[0].mixed_conditions and doc: stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args) - pricing_rules[0].apply_rule_on_other_items = items + for pricing_rule_args in pricing_rules: + pricing_rule_args.apply_rule_on_other_items = items elif pricing_rules[0].is_cumulative: items = [args.get(frappe.scrub(pr_doc.get('apply_on')))] @@ -329,9 +330,9 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args): if pr_doc.mixed_conditions: amt = args.get('qty') * args.get("price_list_rate") if args.get("item_code") != row.get("item_code"): - amt = row.get('qty') * row.get("price_list_rate") + amt = row.get('qty') * (row.get("price_list_rate") or args.get("rate")) - sum_qty += row.get("stock_qty") or args.get("stock_qty") + sum_qty += row.get("stock_qty") or args.get("stock_qty") or args.get("qty") sum_amt += amt if pr_doc.is_cumulative: diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 6f0100ff7a8..5312bc06ecc 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -546,7 +546,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, () => me.conversion_factor(doc, cdt, cdn, true), - () => me.remove_pricing_rule(item) + () => me.remove_pricing_rule(item), + () => { + if (item.apply_rule_on_other_items) { + let key = item.name; + me.apply_rule_on_other_items({key: item}); + } + } ]); } } @@ -1356,20 +1362,22 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ apply_rule_on_other_items: function(args) { const me = this; - const fields = ["discount_percentage", "discount_amount", "rate"]; + const fields = ["discount_percentage", "pricing_rules", "discount_amount", "rate"]; for(var k in args) { let data = args[k]; - me.frm.doc.items.forEach(d => { - if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { - for(var k in data) { - if (in_list(fields, k)) { - frappe.model.set_value(d.doctype, d.name, k, data[k]); + if (data && data.apply_rule_on_other_items) { + me.frm.doc.items.forEach(d => { + if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { + for(var k in data) { + if (in_list(fields, k) && data[k]) { + frappe.model.set_value(d.doctype, d.name, k, data[k]); + } } } - } - }); + }); + } } }, From 954c6540fd77afeedcda9e70b064a81bde233f40 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Tue, 14 Apr 2020 14:05:14 +0530 Subject: [PATCH 16/25] fix: show stock UOM in POS --- .../page/point_of_sale/point_of_sale.js | 2 +- .../page/point_of_sale/point_of_sale.py | 29 ++++++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 33fbc229b6e..0b93324b19d 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -1093,7 +1093,7 @@ class POSCart { return `
+ data-batch-no="${batch_no}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty} ${item.stock_uom}">
${item.item_name}
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index bef2c53d6d4..5eb35d642b9 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -37,20 +37,33 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) # locate function is used to sort by closest match from the beginning of the value - result = [] - items_data = frappe.db.sql(""" SELECT name as item_code, - item_name, image as item_image, idx as idx,is_stock_item + items_data = frappe.db.sql(""" + SELECT + name AS item_code, + item_name, + stock_uom, + image AS item_image, + idx AS idx, + is_stock_item FROM `tabItem` WHERE - disabled = 0 and has_variants = 0 and is_sales_item = 1 - and item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt}) - and {condition} order by idx desc limit {start}, {page_length}""" + disabled = 0 + AND has_variants = 0 + AND is_sales_item = 1 + AND item_group in (SELECT name FROM `tabItem Group` WHERE lft >= {lft} AND rgt <= {rgt}) + AND {condition} + ORDER BY + idx desc + LIMIT + {start}, {page_length}""" .format( - start=start, page_length=page_length, - lft=lft, rgt=rgt, + start=start, + page_length=page_length, + lft=lft, + rgt=rgt, condition=condition ), as_dict=1) From 5771aabc4915a12de41ae9ea4284605f38eec1ae Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 14 Apr 2020 14:49:43 +0530 Subject: [PATCH 17/25] fix: stock reco test case --- .../doctype/stock_reconciliation/test_stock_reconciliation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index e6d7e3fea7d..51d027f22ef 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -240,6 +240,7 @@ def create_batch_or_serial_no_items(): def create_stock_reconciliation(**args): args = frappe._dict(args) sr = frappe.new_doc("Stock Reconciliation") + sr.purpose = args.purpose or "Stock Reconciliation" sr.posting_date = args.posting_date or nowdate() sr.posting_time = args.posting_time or nowtime() sr.set_posting_time = 1 From 25c26b5904d54cf37b0bc0f2913732da42b0b449 Mon Sep 17 00:00:00 2001 From: vishdha Date: Mon, 13 Apr 2020 08:22:34 +0530 Subject: [PATCH 18/25] fix: Chart of account importer UX improved --- erpnext/accounts/doctype/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 0a72d4fa4e6..bd013dc60e3 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -89,7 +89,7 @@ class Account(NestedSet): throw(_("Root cannot be edited."), RootNotEditable) if not self.parent_account and not self.is_group: - frappe.throw(_("Root Account must be a group")) + frappe.throw(_("Root Account must be a group for {0}".format(self.name))) def validate_root_company_and_sync_account_to_children(self): # ignore validation while creating new compnay or while syncing to child companies From af33f715626020ae7fa327ccaeda02b9425c8925 Mon Sep 17 00:00:00 2001 From: vishdha Date: Mon, 13 Apr 2020 18:43:14 +0530 Subject: [PATCH 19/25] fix: message bold --- erpnext/accounts/doctype/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index bd013dc60e3..9e29ce6748f 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -89,7 +89,7 @@ class Account(NestedSet): throw(_("Root cannot be edited."), RootNotEditable) if not self.parent_account and not self.is_group: - frappe.throw(_("Root Account must be a group for {0}".format(self.name))) + frappe.throw(_("Root Account must be a group for {0}".format(frappe.bold(self.name)))) def validate_root_company_and_sync_account_to_children(self): # ignore validation while creating new compnay or while syncing to child companies From b0f000b1c8b182414d1cf0401de11adc0d26995c Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 14 Apr 2020 19:42:21 +0530 Subject: [PATCH 20/25] fix: order_type validation restriction (#18096) (#21264) Co-authored-by: Don-Leopardo <46027152+Don-Leopardo@users.noreply.github.com> --- erpnext/controllers/selling_controller.py | 7 ------- erpnext/selling/doctype/quotation/quotation.py | 4 ---- erpnext/selling/doctype/sales_order/sales_order.py | 4 ---- 3 files changed, 15 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 9dbd5be9188..9a9f3d1d319 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -148,13 +148,6 @@ class SellingController(StockController): if sales_team and total != 100.0: throw(_("Total allocated percentage for sales team should be 100")) - def validate_order_type(self): - valid_types = ["Sales", "Maintenance", "Shopping Cart"] - if not self.order_type: - self.order_type = "Sales" - elif self.order_type not in valid_types: - throw(_("Order Type must be one of {0}").format(comma_or(valid_types))) - def validate_max_discount(self): for d in self.get("items"): if d.item_code: diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 00471c762c8..1571c734b7f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -26,7 +26,6 @@ class Quotation(SellingController): super(Quotation, self).validate() self.set_status() self.update_opportunity() - self.validate_order_type() self.validate_uom_is_integer("stock_uom", "qty") self.validate_valid_till() self.set_customer_name() @@ -40,9 +39,6 @@ class Quotation(SellingController): def has_sales_order(self): return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1}) - def validate_order_type(self): - super(Quotation, self).validate_order_type() - def update_lead(self): if self.quotation_to == "Lead" and self.party_name: frappe.get_doc("Lead", self.party_name).set_status(update=True) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 19cf65dee86..ef2d19ac546 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -34,7 +34,6 @@ class SalesOrder(SellingController): def validate(self): super(SalesOrder, self).validate() - self.validate_order_type() self.validate_delivery_date() self.validate_proj_cust() self.validate_po() @@ -100,9 +99,6 @@ class SalesOrder(SellingController): frappe.msgprint(_("Quotation {0} not of type {1}") .format(d.prevdoc_docname, self.order_type)) - def validate_order_type(self): - super(SalesOrder, self).validate_order_type() - def validate_delivery_date(self): if self.order_type == 'Sales' and not self.skip_delivery_note: delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date] From 09f6199b36965c8670b28bf5403c0c58b4a54ab5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 14 Apr 2020 19:43:26 +0530 Subject: [PATCH 21/25] fix: pick list test (#21267) --- erpnext/stock/doctype/pick_list/test_pick_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 6b4f73b140a..1b9ff41cc33 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -111,6 +111,7 @@ class TestPickList(unittest.TestCase): stock_reconciliation = frappe.get_doc({ 'doctype': 'Stock Reconciliation', + 'purpose': 'Stock Reconciliation', 'company': '_Test Company', 'items': [{ 'item_code': '_Test Serialized Item', From 8e1201d31e42dad7c973c636c1bc43efc4cde1cd Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 15 Apr 2020 21:19:04 +0530 Subject: [PATCH 22/25] fix: Fetch Material Requests with type Issue as well in Stock Entry via Get Items from --- erpnext/stock/doctype/stock_entry/stock_entry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index b54233f4585..c54e3cdfb87 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -220,8 +220,8 @@ frappe.ui.form.on('Stock Entry', { }, get_query_filters: { docstatus: 1, - material_request_type: "Material Transfer", - status: ['!=', 'Transferred'] + material_request_type: ["in", ["Material Transfer", "Material Issue"]], + status: ["not in", ["Transferred", "Issued"]] } }) }, __("Get items from")); From 773eb6ce68efb5f1f7d3e66bb7e0e35958eee94b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 15 Apr 2020 22:08:04 +0530 Subject: [PATCH 23/25] fix: pos not accessible without default customer --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b8c45aba1f5..5218b740edd 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -432,11 +432,12 @@ class SalesInvoice(SellingController): if pos.get("company_address"): self.company_address = pos.get("company_address") - customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) - - customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') - - selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') + if self.customer: + customer_price_list, customer_group = frappe.get_value("Customer", self.customer, ['default_price_list', 'customer_group']) + customer_group_price_list = frappe.get_value("Customer Group", customer_group, 'default_price_list') + selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list') + else: + selling_price_list = pos.get('selling_price_list') if selling_price_list: self.set('selling_price_list', selling_price_list) From 4879858657c8429ec6e66076039ad352ad7fddea Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 15 Apr 2020 22:08:12 +0530 Subject: [PATCH 24/25] fix: warehouse unset when cannot find item warehouse * cannot set delivery date when all items gets deleted and new are added --- erpnext/controllers/accounts_controller.py | 24 ++++---- erpnext/public/js/utils.js | 69 +++++++++++++--------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c17927a35c0..95fe6a2f3fa 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1123,36 +1123,39 @@ def get_supplier_block_status(party_name): } return info -def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): +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 """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", item_code) + item = frappe.get_doc("Item", trans_item.get('item_code')) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.reqd_by_date = p_doc.delivery_date + child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 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.") + .format(frappe.bold("default warehouse"), frappe.bold(item.item_code))) return child_item -def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code): +def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Purchase Order Item child item containing the default values """ p_doc = frappe.get_doc(parent_doctype, parent_doctype_name) child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname) - item = frappe.get_doc("Item", item_code) + item = frappe.get_doc("Item", trans_item.get('item_code')) child_item.item_code = item.item_code child_item.item_name = item.item_name child_item.description = item.description - child_item.schedule_date = p_doc.schedule_date + child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1196,9 +1199,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if not d.get("docname"): new_child_flag = True if parent_doctype == "Sales Order": - child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) + child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) if parent_doctype == "Purchase Order": - child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code")) + child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) else: child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): @@ -1243,6 +1246,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item.flags.ignore_validate_update_after_submit = True if new_child_flag: + parent.load_from_db() child_item.idx = len(parent.items) + 1 child_item.insert() else: diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 0a352648304..224f4f5525d 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -436,6 +436,44 @@ erpnext.utils.update_child_items = function(opts) { const cannot_add_row = (typeof opts.cannot_add_row === 'undefined') ? true : opts.cannot_add_row; const child_docname = (typeof opts.cannot_add_row === 'undefined') ? "items" : opts.child_docname; this.data = []; + const fields = [{ + fieldtype:'Data', + fieldname:"docname", + read_only: 1, + hidden: 1, + }, { + fieldtype:'Link', + fieldname:"item_code", + options: 'Item', + in_list_view: 1, + read_only: 0, + disabled: 0, + label: __('Item Code') + }, { + fieldtype:'Float', + fieldname:"qty", + default: 0, + read_only: 0, + in_list_view: 1, + label: __('Qty') + }, { + fieldtype:'Currency', + fieldname:"rate", + default: 0, + read_only: 0, + in_list_view: 1, + label: __('Rate') + }]; + + if (frm.doc.doctype == 'Sales Order' || frm.doc.doctype == 'Purchase Order' ) { + fields.splice(2, 0, { + fieldtype: 'Date', + fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", + in_list_view: 1, + label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") + }) + } + const dialog = new frappe.ui.Dialog({ title: __("Update Items"), fields: [ @@ -450,34 +488,7 @@ erpnext.utils.update_child_items = function(opts) { get_data: () => { return this.data; }, - fields: [{ - fieldtype:'Data', - fieldname:"docname", - read_only: 1, - hidden: 1, - }, { - fieldtype:'Link', - fieldname:"item_code", - options: 'Item', - in_list_view: 1, - read_only: 0, - disabled: 0, - label: __('Item Code') - }, { - fieldtype:'Float', - fieldname:"qty", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Qty') - }, { - fieldtype:'Currency', - fieldname:"rate", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Rate') - }] + fields: fields }, ], primary_action: function() { @@ -506,6 +517,8 @@ erpnext.utils.update_child_items = function(opts) { "docname": d.name, "name": d.name, "item_code": d.item_code, + "delivery_date": d.delivery_date, + "schedule_date": d.schedule_date, "qty": d.qty, "rate": d.rate, }); From c43e759b87f5a7891df902c5131837a3c6151a88 Mon Sep 17 00:00:00 2001 From: vishdha Date: Thu, 16 Apr 2020 11:09:54 +0530 Subject: [PATCH 25/25] fix: fix Transalation --- erpnext/accounts/doctype/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 9e29ce6748f..c6de6410ebc 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -89,7 +89,7 @@ class Account(NestedSet): throw(_("Root cannot be edited."), RootNotEditable) if not self.parent_account and not self.is_group: - frappe.throw(_("Root Account must be a group for {0}".format(frappe.bold(self.name)))) + frappe.throw(_("The root account {0} must be a group").format(frappe.bold(self.name))) def validate_root_company_and_sync_account_to_children(self): # ignore validation while creating new compnay or while syncing to child companies