From a16347f325f70cecd4990537a6ebac609ae2c0df Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 8 Nov 2022 16:35:34 +0530 Subject: [PATCH 01/17] fix: add Document Date in E-Invoice print format --- .../accounts/print_format/gst_e_invoice/gst_e_invoice.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html index 605ce8383e4..e1b2def2dae 100644 --- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html +++ b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html @@ -50,6 +50,10 @@
{{ einvoice.DocDtls.No }}
+
+
+
{{ einvoice.DocDtls.Dt }}
+
From 1ed0b6e89b9dd98fb89d59c0ead9175a89037d53 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Wed, 9 Nov 2022 14:00:53 +0530 Subject: [PATCH 02/17] fix: Pricing rule item group consider UOM Pricing rule's apply_on_field == "item_group" didn't check for UOM. I have added the required checks. Same as #32566 --- .../doctype/pricing_rule/test_pricing_rule.py | 134 ++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 7 +- 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index c5dac722bad..4b4bfb18188 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -712,6 +712,140 @@ class TestPricingRule(unittest.TestCase): item.delete() + def test_item_group_price_with_blank_uom_pricing_rule(self): + group = frappe.get_doc( + doctype="Item Group", + item_group_name="_Test Pricing Rule Item Group", + parent_item_group="All Item Groups", + ) + group.save() + properties = { + "item_code": "Item with Group Blank UOM", + "item_group": "_Test Pricing Rule Item Group", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item with Group Blank UOM", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item with Group Blank UOM Rule", + "apply_on": "Item Group", + "item_groups": [ + { + "item_group": "_Test Pricing Rule Item Group", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, item_code="Item with Group Blank UOM", uom="Box", conversion_factor=10 + ) + si.selling_price_list = "_Test Price List" + si.save() + + # If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010 + self.assertEqual(si.items[0].price_list_rate, 1010) + self.assertEqual(si.items[0].rate, 1010) + + si.delete() + + si = create_sales_invoice(do_not_save=True, item_code="Item with Group Blank UOM", uom="Nos") + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101 + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item with Group Blank UOM"}).delete() + item.delete() + group.delete() + + def test_item_group_price_with_selling_uom_pricing_rule(self): + group = frappe.get_doc( + doctype="Item Group", + item_group_name="_Test Pricing Rule Item Group UOM", + parent_item_group="All Item Groups", + ) + group.save() + properties = { + "item_code": "Item with Group UOM other than Stock", + "item_group": "_Test Pricing Rule Item Group UOM", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item with Group UOM other than Stock", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item with Group UOM other than Stock Rule", + "apply_on": "Item Group", + "item_groups": [ + { + "item_group": "_Test Pricing Rule Item Group UOM", + "uom": "Box", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, + item_code="Item with Group UOM other than Stock", + uom="Box", + conversion_factor=10, + ) + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so apply pricing_rule only on Box UOM. + # Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor. + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + + si = create_sales_invoice( + do_not_save=True, item_code="Item with Group UOM other than Stock", uom="Nos" + ) + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so pricing_rule won't apply as selling_uom is Nos. + # As Pricing Rule is not applied price of 100 will be fetched from Item Price List. + self.assertEqual(si.items[0].price_list_rate, 100) + self.assertEqual(si.items[0].rate, 100) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item with Group UOM other than Stock"}).delete() + item.delete() + group.delete() + def test_pricing_rule_for_different_currency(self): make_item("Test Sanitizer Item") diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 25f5492d0d1..984a8bae008 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -127,7 +127,12 @@ def _get_pricing_rules(apply_on, args, values): values["variant_of"] = args.variant_of elif apply_on_field == "item_group": item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False) - + if args.get("uom", None): + item_conditions += ( + " and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format( + child_doc=child_doc, item_uom=args.get("uom") + ) + ) conditions += get_other_conditions(conditions, values, args) warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`") if warehouse_conditions: From 4a177113c6c64cee3ef6419f0341b511abd4bb42 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 10 Nov 2022 16:18:10 +0530 Subject: [PATCH 03/17] fix: don't set WIP Warehouse if is checked in WO (cherry picked from commit 9730cd0aecd25d32db0334a0f910bff16d996fc5) --- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 2802310250b..574ca185fee 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -145,7 +145,7 @@ class WorkOrder(Document): frappe.throw(_("Sales Order {0} is {1}").format(self.sales_order, status)) def set_default_warehouse(self): - if not self.wip_warehouse: + if not self.wip_warehouse and not self.skip_transfer: self.wip_warehouse = frappe.db.get_single_value( "Manufacturing Settings", "default_wip_warehouse" ) From f09e4273d9133b24bd3b046bdcf4c9db12c1fb7f Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 10 Nov 2022 16:34:10 +0530 Subject: [PATCH 04/17] fix: set `WIP Warehouse` in Job Card (cherry picked from commit e7fa2e08adbc888fe0ca06b74e3019f5231dd53c) --- erpnext/manufacturing/doctype/job_card/job_card.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index f9ed007aed5..3503a352bc9 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -54,6 +54,9 @@ class JobCard(Document): self.set_onload("job_card_excess_transfer", excess_transfer) self.set_onload("work_order_stopped", self.is_work_order_stopped()) + def before_validate(self): + self.set_wip_warehouse() + def validate(self): self.validate_time_logs() self.set_status() @@ -639,6 +642,12 @@ class JobCard(Document): if update_status: self.db_set("status", self.status) + def set_wip_warehouse(self): + if not self.wip_warehouse: + self.wip_warehouse = frappe.db.get_single_value( + "Manufacturing Settings", "default_wip_warehouse" + ) + def validate_operation_id(self): if ( self.get("operation_id") From e9e5ded36b227dc71ef27bb514bb0ea1b9a9c4c8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 11 Nov 2022 09:35:29 +0530 Subject: [PATCH 05/17] fix: ambiguous 'cost_center' on payment reconciliation --- .../payment_reconciliation.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index f7b8a77efa9..ee12fce7a94 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -47,6 +47,10 @@ class PaymentReconciliation(Document): def get_payment_entries(self): order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = self.get_conditions(get_payments=True) + + if self.get("cost_center"): + condition += " and cost_center = '{0}' ".format(self.cost_center) + payment_entries = get_advance_payment_entries( self.party_type, self.party, @@ -61,6 +65,10 @@ class PaymentReconciliation(Document): def get_jv_entries(self): condition = self.get_conditions() + + if self.get("cost_center"): + condition += " and t2.cost_center = '{0}' ".format(self.cost_center) + dr_or_cr = ( "credit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == "Receivable" @@ -113,6 +121,10 @@ class PaymentReconciliation(Document): def get_dr_or_cr_notes(self): condition = self.get_conditions(get_return_invoices=True) + + if self.get("cost_center"): + condition += " and doc.cost_center = '{0}' ".format(self.cost_center) + dr_or_cr = ( "credit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == "Receivable" @@ -172,6 +184,9 @@ class PaymentReconciliation(Document): condition = self.get_conditions(get_invoices=True) + if self.get("cost_center"): + condition += " and cost_center = '{0}' ".format(self.cost_center) + non_reconciled_invoices = get_outstanding_invoices( self.party_type, self.party, self.receivable_payable_account, condition=condition ) @@ -357,9 +372,6 @@ class PaymentReconciliation(Document): def get_conditions(self, get_invoices=False, get_payments=False, get_return_invoices=False): condition = " and company = '{0}' ".format(self.company) - if self.get("cost_center"): - condition = " and cost_center = '{0}' ".format(self.cost_center) - if get_invoices: condition += ( " and posting_date >= {0}".format(frappe.db.escape(self.from_invoice_date)) From a19031cf9adcd4168570cdc29c5159f82c7f16f4 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Mon, 7 Nov 2022 17:43:08 +0530 Subject: [PATCH 06/17] fix: repayment schedule regeneration (cherry picked from commit d6ab2b3b87f42c6cb4091da96d3fc081e1018145) --- .../doctype/loan_repayment/loan_repayment.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index c17de234556..9bc963ab931 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -577,8 +577,8 @@ def regenerate_repayment_schedule(loan, cancel=0): loan_doc = frappe.get_doc("Loan", loan) next_accrual_date = None accrued_entries = 0 - last_repayment_amount = 0 - last_balance_amount = 0 + last_repayment_amount = None + last_balance_amount = None for term in reversed(loan_doc.get("repayment_schedule")): if not term.is_accrued: @@ -586,9 +586,9 @@ def regenerate_repayment_schedule(loan, cancel=0): loan_doc.remove(term) else: accrued_entries += 1 - if not last_repayment_amount: + if last_repayment_amount is None: last_repayment_amount = term.total_payment - if not last_balance_amount: + if last_balance_amount is None: last_balance_amount = term.balance_loan_amount loan_doc.save() From 79d508f4ffed04c468bde057393cf3a63d04dbc5 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Sat, 12 Nov 2022 05:22:31 +0100 Subject: [PATCH 07/17] fix: wrong totals in hsn summary report --- .../hsn_wise_summary_of_outward_supplies.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py index 68815bf1edf..338a06653dc 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py @@ -35,7 +35,7 @@ def _execute(filters=None): data = [] added_item = [] for d in item_list: - if (d.parent, d.item_code) not in added_item: + if (d.parent, d.gst_hsn_code, d.item_code) not in added_item: row = [d.gst_hsn_code, d.description, d.stock_uom, d.stock_qty] total_tax = 0 tax_rate = 0 @@ -52,7 +52,7 @@ def _execute(filters=None): item_tax = itemised_tax.get((d.parent, d.item_code), {}).get(tax, {}) row += [item_tax.get("tax_amount", 0)] data.append(row) - added_item.append((d.parent, d.item_code)) + added_item.append((d.parent, d.gst_hsn_code, d.item_code)) if data: data = get_merged_data(columns, data) # merge same hsn code data return columns, data @@ -161,11 +161,9 @@ def get_items(filters): GROUP BY `tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code, - `tabSales Invoice Item`.gst_hsn_code, - `tabSales Invoice Item`.uom + `tabSales Invoice Item`.gst_hsn_code ORDER BY - `tabSales Invoice Item`.gst_hsn_code, - `tabSales Invoice Item`.uom + `tabSales Invoice Item`.gst_hsn_code """.format( conditions=conditions ), From bd2242b285e5d2b627fda77c9f974069b1273c0c Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Wed, 9 Nov 2022 18:56:09 +0530 Subject: [PATCH 08/17] fix: set stock UOM in args to ensure item price is fetched (cherry picked from commit 57038c396937cc4c17b6d2ab1234a34a5b05aa23) --- erpnext/stock/get_item_details.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 4db12dcb98b..d44b6ed93c9 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -315,6 +315,9 @@ def get_basic_details(args, item, overwrite_warehouse=True): else: args.uom = item.stock_uom + # Set stock UOM in args, so that it can be used while fetching item price + args.stock_uom = item.stock_uom + if args.get("batch_no") and item.name != frappe.get_cached_value( "Batch", args.get("batch_no"), "item" ): From e93ac3c9a8eb9b83b7fc5523c775cacdea22e57f Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 17 Oct 2022 14:29:03 +0200 Subject: [PATCH 09/17] feat: page break in SoA pdf --- .../process_statement_of_accounts.js | 8 +++++--- .../process_statement_of_accounts.json | 10 +++++++++- .../process_statement_of_accounts.py | 9 ++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js index 29f2e98e779..7dd5ef36f29 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js @@ -8,7 +8,8 @@ frappe.ui.form.on('Process Statement Of Accounts', { }, refresh: function(frm){ if(!frm.doc.__islocal) { - frm.add_custom_button('Send Emails',function(){ + frm.add_custom_button(__('Send Emails'), function(){ + if (frm.is_dirty()) frappe.throw(__("Please save before proceeding.")) frappe.call({ method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails", args: { @@ -24,8 +25,9 @@ frappe.ui.form.on('Process Statement Of Accounts', { } }); }); - frm.add_custom_button('Download',function(){ - var url = frappe.urllib.get_full_url( + frm.add_custom_button(__('Download'), function(){ + if (frm.is_dirty()) frappe.throw(__("Please save before proceeding.")) + let url = frappe.urllib.get_full_url( '/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?' + 'document_name='+encodeURIComponent(frm.doc.name)) $.ajax({ diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index a35374c6242..2f62a0295fb 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -27,6 +27,7 @@ "customers", "preferences", "orientation", + "include_break", "include_ageing", "ageing_based_on", "section_break_14", @@ -284,10 +285,16 @@ "fieldtype": "Link", "label": "Terms and Conditions", "options": "Terms and Conditions" + }, + { + "default": "1", + "fieldname": "include_break", + "fieldtype": "Check", + "label": "Page Break After Each SoA" } ], "links": [], - "modified": "2021-09-06 21:00:45.732505", + "modified": "2022-10-17 17:47:08.662475", "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", @@ -320,5 +327,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 01f716daa21..2256871e423 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -128,7 +128,8 @@ def get_report_pdf(doc, consolidated=True): if not bool(statement_dict): return False elif consolidated: - result = "".join(list(statement_dict.values())) + delimiter = '
' if doc.include_break else "" + result = delimiter.join(list(statement_dict.values())) return get_pdf(result, {"orientation": doc.orientation}) else: for customer, statement_html in statement_dict.items(): @@ -240,8 +241,8 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory): if int(primary_mandatory): if primary_email == "": continue - elif (billing_email == "") and (primary_email == ""): - continue + # elif (billing_email == "") and (primary_email == ""): + # continue customer_list.append( {"name": customer.name, "primary_email": primary_email, "billing_email": billing_email} @@ -313,6 +314,8 @@ def send_emails(document_name, from_scheduler=False): attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}] recipients, cc = get_recipients_and_cc(customer, doc) + if not recipients: + continue context = get_context(customer, doc) subject = frappe.render_template(doc.subject, context) message = frappe.render_template(doc.body, context) From 88ce59a1cacb354db8629597a27764409c593c75 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 17 Oct 2022 15:54:46 +0200 Subject: [PATCH 10/17] fix: query condition change --- .../process_statement_of_accounts.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 2256871e423..48bc3a1bdd2 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -6,6 +6,7 @@ import copy import frappe from frappe import _ +from frappe.desk.reportview import get_match_cond from frappe.model.document import Document from frappe.utils import add_days, add_months, format_date, getdate, today from frappe.utils.jinja import validate_template @@ -274,8 +275,12 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr link.link_doctype='Customer' and link.link_name=%s and contact.is_billing_contact=1 + {mcond} ORDER BY - contact.creation desc""", + contact.creation desc + """.format( + mcond=get_match_cond("Contact") + ), customer_name, ) From 95f81d3563bb7f57ff804351e7d977c0528203ae Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 27 Oct 2022 09:33:49 +0530 Subject: [PATCH 11/17] chore: remove commented line --- .../process_statement_of_accounts.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 48bc3a1bdd2..c6b0c57ce5c 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -242,8 +242,6 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory): if int(primary_mandatory): if primary_email == "": continue - # elif (billing_email == "") and (primary_email == ""): - # continue customer_list.append( {"name": customer.name, "primary_email": primary_email, "billing_email": billing_email} From d6901e51ade7e17f5581be49574bf03729070903 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 12 Nov 2022 17:32:04 +0530 Subject: [PATCH 12/17] chore: Remove raw SQL query (cherry picked from commit 42a59d5c171da96bccaf657eb87454040a9cc84c) --- .../doctype/bank_guarantee/bank_guarantee.js | 13 ++------- .../doctype/bank_guarantee/bank_guarantee.py | 28 +++++++++---------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js index febf85ca6c1..99cc0a72fb3 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js @@ -43,20 +43,13 @@ frappe.ui.form.on('Bank Guarantee', { reference_docname: function(frm) { if (frm.doc.reference_docname && frm.doc.reference_doctype) { - let fields_to_fetch = ["grand_total"]; let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier"; - if (frm.doc.reference_doctype == "Sales Order") { - fields_to_fetch.push("project"); - } - - fields_to_fetch.push(party_field); frappe.call({ - method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials", + method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details", args: { - "column_list": fields_to_fetch, - "doctype": frm.doc.reference_doctype, - "docname": frm.doc.reference_docname + "bank_guarantee_type": frm.doc.bg_type, + "reference_name": frm.doc.reference_docname }, callback: function(r) { if (r.message) { diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py index 9144a29c6ef..a57acda680c 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py @@ -2,11 +2,8 @@ # For license information, please see license.txt -import json - import frappe from frappe import _ -from frappe.desk.search import sanitize_searchfield from frappe.model.document import Document @@ -25,14 +22,17 @@ class BankGuarantee(Document): @frappe.whitelist() -def get_vouchar_detials(column_list, doctype, docname): - column_list = json.loads(column_list) - for col in column_list: - sanitize_searchfield(col) - return frappe.db.sql( - """ select {columns} from `tab{doctype}` where name=%s""".format( - columns=", ".join(column_list), doctype=doctype - ), - docname, - as_dict=1, - )[0] +def get_voucher_details(bank_guarantee_type, reference_name): + fields_to_fetch = ["grand_total"] + + doctype = "Sales Order" if bank_guarantee_type == "Receiving" else "Purchase Order" + + if doctype == "Sales Order": + fields_to_fetch.append("customer") + fields_to_fetch.append("project") + else: + fields_to_fetch.append("supplier") + + bg_doctype = frappe.qb.DocType("Bank Guarantee") + + return frappe.db.get_value(doctype, reference_name, fields_to_fetch, as_dict=True) From 9c9c6607f83b19e0ecb95ba134190ac1079a7602 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 13 Nov 2022 18:48:32 +0530 Subject: [PATCH 13/17] chore: Remove qb doc reference (cherry picked from commit 4b9921782b0427ae4882d186d37fdb960671eed7) --- erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py index a57acda680c..0e3f7d75716 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py @@ -33,6 +33,4 @@ def get_voucher_details(bank_guarantee_type, reference_name): else: fields_to_fetch.append("supplier") - bg_doctype = frappe.qb.DocType("Bank Guarantee") - return frappe.db.get_value(doctype, reference_name, fields_to_fetch, as_dict=True) From ad648f313c9adf66dfe841f5dfd0d897e8697ae6 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sun, 13 Nov 2022 19:58:49 +0530 Subject: [PATCH 14/17] fix: check type for reference name (cherry picked from commit b06345af46d8469b46cdf795389a589c58729439) --- .../accounts/doctype/bank_guarantee/bank_guarantee.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py index 0e3f7d75716..02eb599acc8 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py @@ -22,15 +22,18 @@ class BankGuarantee(Document): @frappe.whitelist() -def get_voucher_details(bank_guarantee_type, reference_name): +def get_voucher_details(bank_guarantee_type: str, reference_name: str): + if not isinstance(reference_name, str): + raise TypeError("reference_name must be a string") + fields_to_fetch = ["grand_total"] - doctype = "Sales Order" if bank_guarantee_type == "Receiving" else "Purchase Order" - - if doctype == "Sales Order": + if bank_guarantee_type == "Receiving": + doctype = "Sales Order" fields_to_fetch.append("customer") fields_to_fetch.append("project") else: + doctype = "Purchase Order" fields_to_fetch.append("supplier") return frappe.db.get_value(doctype, reference_name, fields_to_fetch, as_dict=True) From 78ca078474a45dc039029d17bc2d9ebc9ce24990 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sat, 12 Nov 2022 12:35:52 +0530 Subject: [PATCH 15/17] refactor: rewrite `job_card.py` queries in QB (cherry picked from commit 7df2921d385d588d2097889d34cd51cb020428b4) --- .../doctype/job_card/job_card.py | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 3503a352bc9..49e5569644f 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -7,6 +7,8 @@ import frappe from frappe import _, bold from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from frappe.query_builder import Criterion +from frappe.query_builder.functions import IfNull, Max, Min from frappe.utils import ( add_days, add_to_date, @@ -112,43 +114,44 @@ class JobCard(Document): def get_overlap_for(self, args, check_next_available_slot=False): production_capacity = 1 + jc = frappe.qb.DocType("Job Card") + jctl = frappe.qb.DocType("Job Card Time Log") + + time_conditions = [ + ((jctl.from_time < args.from_time) & (jctl.to_time > args.from_time)), + ((jctl.from_time < args.to_time) & (jctl.to_time > args.to_time)), + ((jctl.from_time >= args.from_time) & (jctl.to_time <= args.to_time)), + ] + + if check_next_available_slot: + time_conditions.append(((jctl.from_time >= args.from_time) & (jctl.to_time >= args.to_time))) + + query = ( + frappe.qb.from_(jctl) + .from_(jc) + .select(jc.name.as_("name"), jctl.to_time) + .where( + (jctl.parent == jc.name) + & (Criterion.any(time_conditions)) + & (jctl.name != f"{args.name or 'No Name'}") + & (jc.name != f"{args.parent or 'No Name'}") + & (jc.docstatus < 2) + ) + .orderby(jctl.to_time, order=frappe.qb.desc) + ) + if self.workstation: production_capacity = ( frappe.get_cached_value("Workstation", self.workstation, "production_capacity") or 1 ) - validate_overlap_for = " and jc.workstation = %(workstation)s " + query = query.where(jc.workstation == self.workstation) if args.get("employee"): # override capacity for employee production_capacity = 1 - validate_overlap_for = " and jctl.employee = %(employee)s " + query = query.where(jctl.employee == args.get("employee")) - extra_cond = "" - if check_next_available_slot: - extra_cond = " or (%(from_time)s <= jctl.from_time and %(to_time)s <= jctl.to_time)" - - existing = frappe.db.sql( - """select jc.name as name, jctl.to_time from - `tabJob Card Time Log` jctl, `tabJob Card` jc where jctl.parent = jc.name and - ( - (%(from_time)s > jctl.from_time and %(from_time)s < jctl.to_time) or - (%(to_time)s > jctl.from_time and %(to_time)s < jctl.to_time) or - (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0} - ) - and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1} - order by jctl.to_time desc""".format( - extra_cond, validate_overlap_for - ), - { - "from_time": args.from_time, - "to_time": args.to_time, - "name": args.name or "No Name", - "parent": args.parent or "No Name", - "employee": args.get("employee"), - "workstation": self.workstation, - }, - as_dict=True, - ) + existing = query.run(as_dict=True) if existing and production_capacity > len(existing): return @@ -488,18 +491,21 @@ class JobCard(Document): ) def update_work_order_data(self, for_quantity, time_in_mins, wo): - time_data = frappe.db.sql( - """ - SELECT - min(from_time) as start_time, max(to_time) as end_time - FROM `tabJob Card` jc, `tabJob Card Time Log` jctl - WHERE - jctl.parent = jc.name and jc.work_order = %s and jc.operation_id = %s - and jc.docstatus = 1 and IFNULL(jc.is_corrective_job_card, 0) = 0 - """, - (self.work_order, self.operation_id), - as_dict=1, - ) + jc = frappe.qb.DocType("Job Card") + jctl = frappe.qb.DocType("Job Card Time Log") + + time_data = ( + frappe.qb.from_(jc) + .from_(jctl) + .select(Min(jctl.from_time).as_("start_time"), Max(jctl.to_time).as_("end_time")) + .where( + (jctl.parent == jc.name) + & (jc.work_order == self.work_order) + & (jc.operation_id == self.operation_id) + & (jc.docstatus == 1) + & (IfNull(jc.is_corrective_job_card, 0) == 0) + ) + ).run(as_dict=True) for data in wo.operations: if data.get("name") == self.operation_id: From cbc8b2da0a47ff8d9b79453bdce7a239ac820b91 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 14 Nov 2022 20:39:40 +0530 Subject: [PATCH 16/17] fix: Label for applicable dimension table (cherry picked from commit 8c13f70fc53b807bd1b477126f80e0ecfe7bd4e8) --- .../accounting_dimension_filter.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index 750e129ba78..8a6b021b8ad 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -3,10 +3,6 @@ frappe.ui.form.on('Accounting Dimension Filter', { refresh: function(frm, cdt, cdn) { - if (frm.doc.accounting_dimension) { - frm.set_df_property('dimensions', 'label', frm.doc.accounting_dimension, cdn, 'dimension_value'); - } - let help_content = `
@@ -68,6 +64,7 @@ frappe.ui.form.on('Accounting Dimension Filter', { frm.clear_table("dimensions"); let row = frm.add_child("dimensions"); row.accounting_dimension = frm.doc.accounting_dimension; + frm.fields_dict["dimensions"].grid.update_docfield_property("dimension_value", "label", frm.doc.accounting_dimension); frm.refresh_field("dimensions"); frm.trigger('setup_filters'); }, From e1ecc9a81973d20084f5a053d8d0155354079b2e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 14 Nov 2022 18:11:27 +0530 Subject: [PATCH 17/17] chore(payment_entry): Remove dead validations --- .../doctype/payment_entry/payment_entry.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 904d7af757f..70a5f9e526c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -67,7 +67,6 @@ class PaymentEntry(AccountsController): self.set_missing_values() self.validate_payment_type() self.validate_party_details() - self.validate_bank_accounts() self.set_exchange_rate() self.validate_mandatory() self.validate_reference_documents() @@ -250,23 +249,6 @@ class PaymentEntry(AccountsController): if not frappe.db.exists(self.party_type, self.party): frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party)) - if self.party_account and self.party_type in ("Customer", "Supplier"): - self.validate_account_type( - self.party_account, [erpnext.get_party_account_type(self.party_type)] - ) - - def validate_bank_accounts(self): - if self.payment_type in ("Pay", "Internal Transfer"): - self.validate_account_type(self.paid_from, ["Bank", "Cash"]) - - if self.payment_type in ("Receive", "Internal Transfer"): - self.validate_account_type(self.paid_to, ["Bank", "Cash"]) - - def validate_account_type(self, account, account_types): - account_type = frappe.db.get_value("Account", account, "account_type") - # if account_type not in account_types: - # frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) - def set_exchange_rate(self, ref_doc=None): self.set_source_exchange_rate(ref_doc) self.set_target_exchange_rate(ref_doc)