From 5869fcbd861fbd5202c24badd06fa2a80b4bc820 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Jan 2023 05:21:13 +0000 Subject: [PATCH 01/26] fix: better comparision of difference value between stock and account (cherry picked from commit be05aea101fe34de22bd3ff14b71daa7fcf5af29) --- .../stock_and_account_value_comparison.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 99f820ecac6..106e877c4cd 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -41,7 +41,7 @@ def get_data(report_filters): key = (d.voucher_type, d.voucher_no) gl_data = voucher_wise_gl_data.get(key) or {} d.account_value = gl_data.get("account_value", 0) - d.difference_value = abs(d.stock_value - d.account_value) + d.difference_value = d.stock_value - d.account_value if abs(d.difference_value) > 0.1: data.append(d) From 120dca44adfbe8de67228385f832c589800e8e07 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 12 Jan 2023 20:31:24 +0530 Subject: [PATCH 02/26] chore: subcontracting validations (backport #33621) (#33626) * fix: `ZeroDivisionError: float division by zero` in SCR (cherry picked from commit 434aa594d57b986d078fb98025eda65f75b65e26) * chore: add row-index in error msgs (cherry picked from commit 6878f40d1df268fd179d7c5558e858387b27ae0a) * chore: update error msgs for Subcontracted PO (cherry picked from commit a0e2a93f3f502c29cb161736ef091a4e98c3dd53) * fix: validate accepted and rejected qty in SCR Item (cherry picked from commit f028bd6e69a571d0053f124334a0021904a7f365) * chore: linter (cherry picked from commit b26e96cdf40d4306faa158ad74644c5c2abce8c7) Co-authored-by: s-aga-r --- .../doctype/purchase_order/purchase_order.py | 12 ++++------- .../controllers/subcontracting_controller.py | 21 ++++++++++--------- .../subcontracting_receipt.py | 10 ++++++++- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 5a4168a573e..2415aec8cb9 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -219,20 +219,16 @@ class PurchaseOrder(BuyingController): else: if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"): frappe.throw( - _( - "Row #{0}: Finished Good Item {1} must be a sub-contracted item for service item {2}" - ).format(item.idx, item.fg_item, item.item_code) + _("Row #{0}: Finished Good Item {1} must be a sub-contracted item").format( + item.idx, item.fg_item + ) ) elif not frappe.get_value("Item", item.fg_item, "default_bom"): frappe.throw( _("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item) ) if not item.fg_item_qty: - frappe.throw( - _("Row #{0}: Finished Good Item Qty is not specified for service item {0}").format( - item.idx, item.item_code - ) - ) + frappe.throw(_("Row #{0}: Finished Good Item Qty can not be zero").format(item.idx)) else: for item in self.items: item.set("fg_item", None) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 335d92f43f3..a9561fe2dac 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -74,24 +74,25 @@ class SubcontractingController(StockController): ) if not is_stock_item: - msg = f"Item {item.item_name} must be a stock item." - frappe.throw(_(msg)) + frappe.throw(_("Row {0}: Item {1} must be a stock item.").format(item.idx, item.item_name)) if not is_sub_contracted_item: - msg = f"Item {item.item_name} must be a subcontracted item." - frappe.throw(_(msg)) + frappe.throw( + _("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name) + ) if item.bom: bom = frappe.get_doc("BOM", item.bom) if not bom.is_active: - msg = f"Please select an active BOM for Item {item.item_name}." - frappe.throw(_(msg)) + frappe.throw( + _("Row {0}: Please select an active BOM for Item {1}.").format(item.idx, item.item_name) + ) if bom.item != item.item_code: - msg = f"Please select an valid BOM for Item {item.item_name}." - frappe.throw(_(msg)) + frappe.throw( + _("Row {0}: Please select an valid BOM for Item {1}.").format(item.idx, item.item_name) + ) else: - msg = f"Please select a BOM for Item {item.item_name}." - frappe.throw(_(msg)) + frappe.throw(_("Row {0}: Please select a BOM for Item {1}.").format(item.idx, item.item_name)) def __get_data_before_save(self): item_dict = {} diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index bce53608beb..7e1915bb71a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -57,6 +57,7 @@ class SubcontractingReceipt(SubcontractingController): def before_validate(self): super(SubcontractingReceipt, self).before_validate() + self.validate_items_qty() self.set_items_bom() self.set_items_cost_center() self.set_items_expense_account() @@ -157,7 +158,7 @@ class SubcontractingReceipt(SubcontractingController): total_qty = total_amount = 0 for item in self.items: - if item.name in rm_supp_cost: + if item.qty and item.name in rm_supp_cost: item.rm_supp_cost = rm_supp_cost[item.name] item.rm_cost_per_qty = item.rm_supp_cost / item.qty rm_supp_cost.pop(item.name) @@ -194,6 +195,13 @@ class SubcontractingReceipt(SubcontractingController): ).format(item.idx) ) + def validate_items_qty(self): + for item in self.items: + if not (item.qty or item.rejected_qty): + frappe.throw( + _("Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time.").format(item.idx) + ) + def set_items_bom(self): if self.is_return: for item in self.items: From 28f2d357abb6f59762ff2d721944dc897709e21f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 12 Jan 2023 20:33:18 +0530 Subject: [PATCH 03/26] fix: only group similar items in print format if group_same_items is checked in pick list (backport #33627) (#33630) fix: only group similar items in print format if group_same_items is checked in pick list (#33627) * fix: only group similar items if group same items is checked in pick list * test: non grouping of locations if group_same_items is false Co-authored-by: Sagar Sharma (cherry picked from commit cfb0bb1eaa31c23ae8ce4c87c5a11c3e46d83789) Co-authored-by: Ritwik Puri Co-authored-by: Sagar Sharma --- erpnext/stock/doctype/pick_list/pick_list.py | 3 ++- erpnext/stock/doctype/pick_list/test_pick_list.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 65a792fb46b..9e6aead02db 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -230,7 +230,8 @@ class PickList(Document): frappe.throw(_("Qty of Finished Goods Item should be greater than 0.")) def before_print(self, settings=None): - self.group_similar_items() + if self.group_same_items: + self.group_similar_items() def group_similar_items(self): group_item_qty = defaultdict(float) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index f552299806c..71663e8ff47 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -445,6 +445,20 @@ class TestPickList(FrappeTestCase): pl.before_print() self.assertEqual(len(pl.locations), 4) + # grouping should not happen if group_same_items is False + pl = frappe.get_doc( + doctype="Pick List", + group_same_items=False, + locations=[ + _dict(item_code="A", warehouse="X", qty=5, picked_qty=1), + _dict(item_code="B", warehouse="Y", qty=4, picked_qty=2), + _dict(item_code="A", warehouse="X", qty=3, picked_qty=2), + _dict(item_code="B", warehouse="Y", qty=2, picked_qty=2), + ], + ) + pl.before_print() + self.assertEqual(len(pl.locations), 4) + # grouping should halve the number of items pl = frappe.get_doc( doctype="Pick List", From 1f0a569c7fefab9b4d5966dcc84cfe9ee6ed110e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 13 Jan 2023 01:34:31 +0530 Subject: [PATCH 04/26] chore: reuse doc object in test_pick_list_grouping_before_print (backport #33636) (#33637) chore: reuse doc object in test_pick_list_grouping_before_print (#33636) (cherry picked from commit e22d56484d7f4b8696bc48db2ac8dcc76e69e497) Co-authored-by: Ritwik Puri --- erpnext/stock/doctype/pick_list/test_pick_list.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index 71663e8ff47..43acdf08360 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -460,16 +460,7 @@ class TestPickList(FrappeTestCase): self.assertEqual(len(pl.locations), 4) # grouping should halve the number of items - pl = frappe.get_doc( - doctype="Pick List", - group_same_items=True, - locations=[ - _dict(item_code="A", warehouse="X", qty=5, picked_qty=1), - _dict(item_code="B", warehouse="Y", qty=4, picked_qty=2), - _dict(item_code="A", warehouse="X", qty=3, picked_qty=2), - _dict(item_code="B", warehouse="Y", qty=2, picked_qty=2), - ], - ) + pl.group_same_items = True pl.before_print() self.assertEqual(len(pl.locations), 2) From f915c181376f1c3e90ffd3ccde1ddf8a3c60ec66 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 12 Jan 2023 13:16:05 +0530 Subject: [PATCH 05/26] fix: Updating SO throws ordered_qty not allowed to change after submission (cherry picked from commit 391f42db0438fafb9f5b6200d11e012fdcd2555e) --- .../selling/doctype/sales_order_item/sales_order_item.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index b801de314cc..cfbd94b401b 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -639,6 +639,7 @@ "width": "70px" }, { + "allow_on_submit": 1, "fieldname": "ordered_qty", "fieldtype": "Float", "label": "Ordered Qty", @@ -865,7 +866,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-11-18 11:39:01.741665", + "modified": "2023-01-12 13:13:28.691585", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", @@ -876,4 +877,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From 2dfbc6e4ebb39efe8c497205c3351cf0f602622d Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sat, 14 Jan 2023 21:08:52 +0530 Subject: [PATCH 06/26] fix: zero rm-cost in SCR (cherry picked from commit f70d757b82ecafc69552a92b1e884546c89b409f) --- .../doctype/subcontracting_receipt/subcontracting_receipt.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 7e1915bb71a..e8faa4868f2 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -63,6 +63,11 @@ class SubcontractingReceipt(SubcontractingController): self.set_items_expense_account() def validate(self): + if ( + frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on") + == "BOM" + ): + self.supplied_items = [] super(SubcontractingReceipt, self).validate() self.set_missing_values() self.validate_posting_time() From 91b08f179a4e29f3b9400190faa50154cc587709 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 15 Jan 2023 17:30:32 +0530 Subject: [PATCH 07/26] feat: Date filters on bank reconciliation tool (#33271) * Update bank_reconciliation_tool.py Applying date filter on transactions and all the bank entries and also gives the filter the bank entries as per reference date. Sorted all transactions and entries as per date in ascending order. Also added posting date columns in all bank entries and default checkbox tick of journal entry, hide the sales invoice and purchase invoice checkbox. (cherry picked from commit e5a1189becad071f54c727bc6c0dba16bea2a12f) * Filters on Bank Reconciliation Applying date filter on transactions and all the bank entries and also gives the filter the bank entries as per reference date. Sorted all transactions and entries as per date in ascending order. Also added posting date columns in all bank entries and default checkbox tick of journal entry, hide the sales invoice and purchase invoice checkbox. (cherry picked from commit 447272aa4d1f49428717b1b0ae8e34a21fd0e752) * Update bank_reconciliation_tool.json Adding fields in bank reconciliation tool (cherry picked from commit 8e7c8a648221bdd35b9ecde52a9b56d78b412206) * Feat:Filter on Payment Entries and Journal Entries Applying filters on Payement entries and Journal Entries as per reference date and posting date (cherry picked from commit 408c89df030998fe36df135570c9edd90a522996) * feat:filters on bank reconciliation Added date filters on bank transactions, payment entries and journal entries and sorted list as per date in ascending order. (cherry picked from commit 05b6fce03d58d75df557f5dc5285da30b8baab38) * feat: added arguments of posting date and reference date (cherry picked from commit 645869e6ffec48b605cfdb68d763a7c9f21e3eec) * fix: linters (cherry picked from commit 6b5276398ef90a1a362ce709a92f47f4219f0134) * fix: json issue (cherry picked from commit 81e5f711725e42b25eb84f41790024798f6c535e) * fix: filtered as per reference date On bank reconciliation, transactions will be filtered as per date selected in 'from_date' and 'to_date' fields , In dialog, all the bank entries will be fetched as per the posting date selected and if filtered by reference date checkbox is tick then then there will be two fields 'from_reference_date' and 'to_reference_date' then all bank entries in dialog box came as per reference date, selected. And by default journal entry checkbox is tick. Also sorted the bank transactions and bank entries as per ascending order date wise. (cherry picked from commit 3aaa2f5326b13dbf390d86da9917fbb5def447ae) * fix: pre-commit (cherry picked from commit e2614b8a21117f47c0661f1e0080ed5f95d2e4f8) * fix: passing from_date and to_date filters in test cases passing from_date and to_date filters in test_linked_payments and test_debit_credit_output for unit testing (cherry picked from commit f1810803e1d874a22a49b5bd9b3351fbd2b5b843) * fix: pre-commit (cherry picked from commit 35c29e02267eafade500bdb91fb67a53ec554641) * fix: pre-commit (cherry picked from commit c764f14f53e4b6d87263681be9786166cd7998f9) * feat: consolidated auto bank reconciliation Added a button of Auto Reconcile, to reconcile the bank entries as per the matching reference number with the bank transaction and count of transactions reconciled message will be pop up on clicking the auto reconcile button. (cherry picked from commit d65243eb65fe53d5f5fad0bcb9ffeb1fce7e727a) * fix: data format (cherry picked from commit 12822f7c36fd8a3f5aeea146b0d5e7be46f14a47) * fix: remove comments (cherry picked from commit 917b2190aa25fff735ea3442cee797112147ccb1) * chore: fix fieldnames and order (cherry picked from commit 232726288acb230c49ddb023d453ad055a8b8215) Co-authored-by: sonali Co-authored-by: Deepesh Garg --- .../bank_reconciliation_tool.js | 32 ++- .../bank_reconciliation_tool.json | 35 ++- .../bank_reconciliation_tool.py | 263 +++++++++++++++--- .../bank_transaction/test_bank_transaction.py | 15 +- .../data_table_manager.js | 9 +- .../dialog_manager.js | 21 +- 6 files changed, 324 insertions(+), 51 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index 28e79b5d2c6..c083189eb27 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -21,13 +21,22 @@ frappe.ui.form.on("Bank Reconciliation Tool", { frm.trigger('bank_account'); }, + filter_by_reference_date: function (frm) { + if (frm.doc.filter_by_reference_date) { + frm.set_value("bank_statement_from_date", ""); + frm.set_value("bank_statement_to_date", ""); + } else { + frm.set_value("from_reference_date", ""); + frm.set_value("to_reference_date", ""); + } + }, + refresh: function (frm) { frappe.require("bank-reconciliation-tool.bundle.js", () => frm.trigger("make_reconciliation_tool") ); - frm.upload_statement_button = frm.page.set_secondary_action( - __("Upload Bank Statement"), - () => + + frm.add_custom_button(__("Upload Bank Statement"), () => frappe.call({ method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement", @@ -49,6 +58,20 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, }) ); + + frm.add_custom_button(__('Auto Reconcile'), function() { + frappe.call({ + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers", + args: { + bank_account: frm.doc.bank_account, + from_date: frm.doc.bank_statement_from_date, + to_date: frm.doc.bank_statement_to_date, + filter_by_reference_date: frm.doc.filter_by_reference_date, + from_reference_date: frm.doc.from_reference_date, + to_reference_date: frm.doc.to_reference_date, + }, + }) + }); }, after_save: function (frm) { @@ -160,6 +183,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", { ).$wrapper, bank_statement_from_date: frm.doc.bank_statement_from_date, bank_statement_to_date: frm.doc.bank_statement_to_date, + filter_by_reference_date: frm.doc.filter_by_reference_date, + from_reference_date: frm.doc.from_reference_date, + to_reference_date: frm.doc.to_reference_date, bank_statement_closing_balance: frm.doc.bank_statement_closing_balance, cards_manager: frm.cards_manager, diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json index f666101d3fd..80993d6608d 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json @@ -10,6 +10,9 @@ "column_break_1", "bank_statement_from_date", "bank_statement_to_date", + "from_reference_date", + "to_reference_date", + "filter_by_reference_date", "column_break_2", "account_opening_balance", "bank_statement_closing_balance", @@ -36,13 +39,13 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval: doc.bank_account", + "depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date", "fieldname": "bank_statement_from_date", "fieldtype": "Date", "label": "From Date" }, { - "depends_on": "eval: doc.bank_statement_from_date", + "depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date", "fieldname": "bank_statement_to_date", "fieldtype": "Date", "label": "To Date" @@ -81,14 +84,33 @@ }, { "fieldname": "no_bank_transactions", - "fieldtype": "HTML" + "fieldtype": "HTML", + "options": "
No Matching Bank Transactions Found
" + }, + { + "depends_on": "eval:doc.filter_by_reference_date", + "fieldname": "from_reference_date", + "fieldtype": "Date", + "label": "From Reference Date" + }, + { + "depends_on": "eval:doc.filter_by_reference_date", + "fieldname": "to_reference_date", + "fieldtype": "Date", + "label": "To Reference Date" + }, + { + "default": "0", + "fieldname": "filter_by_reference_date", + "fieldtype": "Check", + "label": "Filter by Reference Date" } ], "hide_toolbar": 1, "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-04-21 11:13:49.831769", + "modified": "2023-01-13 13:00:02.022919", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Reconciliation Tool", @@ -107,5 +129,6 @@ ], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" -} + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index 8b496e5ea33..57bc351f414 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -8,7 +8,7 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.query_builder.custom import ConstantColumn -from frappe.utils import flt +from frappe.utils import cint, flt from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import ( @@ -50,6 +50,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None): "party", ], filters=filters, + order_by="date", ) return transactions @@ -261,6 +262,80 @@ def create_payment_entry_bts( return reconcile_vouchers(bank_transaction.name, vouchers) +@frappe.whitelist() +def auto_reconcile_vouchers( + bank_account, + from_date=None, + to_date=None, + filter_by_reference_date=None, + from_reference_date=None, + to_reference_date=None, +): + frappe.flags.auto_reconcile_vouchers = True + document_types = ["payment_entry", "journal_entry"] + bank_transactions = get_bank_transactions(bank_account) + matched_transaction = [] + for transaction in bank_transactions: + linked_payments = get_linked_payments( + transaction.name, + document_types, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, + ) + vouchers = [] + for r in linked_payments: + vouchers.append( + { + "payment_doctype": r[1], + "payment_name": r[2], + "amount": r[4], + } + ) + transaction = frappe.get_doc("Bank Transaction", transaction.name) + account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") + matched_trans = 0 + for voucher in vouchers: + gl_entry = frappe.db.get_value( + "GL Entry", + dict( + account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"] + ), + ["credit", "debit"], + as_dict=1, + ) + gl_amount, transaction_amount = ( + (gl_entry.credit, transaction.deposit) + if gl_entry.credit > 0 + else (gl_entry.debit, transaction.withdrawal) + ) + allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount + transaction.append( + "payment_entries", + { + "payment_document": voucher["payment_doctype"], + "payment_entry": voucher["payment_name"], + "allocated_amount": allocated_amount, + }, + ) + matched_transaction.append(str(transaction.name)) + transaction.save() + transaction.update_allocations() + matched_transaction_len = len(set(matched_transaction)) + if matched_transaction_len == 0: + frappe.msgprint(_("No matching references found for auto reconciliation")) + elif matched_transaction_len == 1: + frappe.msgprint(_("{0} transaction is reconcilied").format(matched_transaction_len)) + else: + frappe.msgprint(_("{0} transactions are reconcilied").format(matched_transaction_len)) + + frappe.flags.auto_reconcile_vouchers = False + + return frappe.get_doc("Bank Transaction", transaction.name) + + @frappe.whitelist() def reconcile_vouchers(bank_transaction_name, vouchers): # updated clear date of all the vouchers based on the bank transaction @@ -323,20 +398,58 @@ def reconcile_vouchers(bank_transaction_name, vouchers): @frappe.whitelist() -def get_linked_payments(bank_transaction_name, document_types=None): +def get_linked_payments( + bank_transaction_name, + document_types=None, + from_date=None, + to_date=None, + filter_by_reference_date=None, + from_reference_date=None, + to_reference_date=None, +): # get all matching payments for a bank transaction transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) bank_account = frappe.db.get_values( "Bank Account", transaction.bank_account, ["account", "company"], as_dict=True )[0] (account, company) = (bank_account.account, bank_account.company) - matching = check_matching(account, company, transaction, document_types) + matching = check_matching( + account, + company, + transaction, + document_types, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, + ) return matching -def check_matching(bank_account, company, transaction, document_types): +def check_matching( + bank_account, + company, + transaction, + document_types, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, +): # combine all types of vouchers - subquery = get_queries(bank_account, company, transaction, document_types) + subquery = get_queries( + bank_account, + company, + transaction, + document_types, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, + ) filters = { "amount": transaction.unallocated_amount, "payment_type": "Receive" if transaction.deposit > 0 else "Pay", @@ -357,11 +470,20 @@ def check_matching(bank_account, company, transaction, document_types): filters, ) ) - return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else [] -def get_queries(bank_account, company, transaction, document_types): +def get_queries( + bank_account, + company, + transaction, + document_types, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, +): # get queries to get matching vouchers amount_condition = "=" if "exact_match" in document_types else "<=" account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from" @@ -377,6 +499,11 @@ def get_queries(bank_account, company, transaction, document_types): document_types, amount_condition, account_from_to, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, ) or [] ) @@ -385,15 +512,42 @@ def get_queries(bank_account, company, transaction, document_types): def get_matching_queries( - bank_account, company, transaction, document_types, amount_condition, account_from_to + bank_account, + company, + transaction, + document_types, + amount_condition, + account_from_to, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, ): queries = [] if "payment_entry" in document_types: - pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction) + pe_amount_matching = get_pe_matching_query( + amount_condition, + account_from_to, + transaction, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, + ) queries.extend([pe_amount_matching]) if "journal_entry" in document_types: - je_amount_matching = get_je_matching_query(amount_condition, transaction) + je_amount_matching = get_je_matching_query( + amount_condition, + transaction, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, + ) queries.extend([je_amount_matching]) if transaction.deposit > 0 and "sales_invoice" in document_types: @@ -500,47 +654,81 @@ def get_lr_matching_query(bank_account, amount_condition, filters): return vouchers -def get_pe_matching_query(amount_condition, account_from_to, transaction): +def get_pe_matching_query( + amount_condition, + account_from_to, + transaction, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, +): # get matching payment entries query if transaction.deposit > 0: currency_field = "paid_to_account_currency as currency" else: currency_field = "paid_from_account_currency as currency" + filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'" + order_by = " posting_date" + filter_by_reference_no = "" + if cint(filter_by_reference_date): + filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'" + order_by = " reference_date" + if frappe.flags.auto_reconcile_vouchers == True: + filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'" return f""" - SELECT - (CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END - + CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END - + 1 ) AS rank, - 'Payment Entry' as doctype, - name, - paid_amount, - reference_no, - reference_date, - party, - party_type, - posting_date, - {currency_field} - FROM - `tabPayment Entry` - WHERE - paid_amount {amount_condition} %(amount)s - AND docstatus = 1 - AND payment_type IN (%(payment_type)s, 'Internal Transfer') - AND ifnull(clearance_date, '') = "" - AND {account_from_to} = %(bank_account)s + SELECT + (CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END + + CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END + + 1 ) AS rank, + 'Payment Entry' as doctype, + name, + paid_amount, + reference_no, + reference_date, + party, + party_type, + posting_date, + {currency_field} + FROM + `tabPayment Entry` + WHERE + paid_amount {amount_condition} %(amount)s + AND docstatus = 1 + AND payment_type IN (%(payment_type)s, 'Internal Transfer') + AND ifnull(clearance_date, '') = "" + AND {account_from_to} = %(bank_account)s + {filter_by_date} + {filter_by_reference_no} + order by{order_by} + """ -def get_je_matching_query(amount_condition, transaction): +def get_je_matching_query( + amount_condition, + transaction, + from_date, + to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date, +): # get matching journal entry query - # We have mapping at the bank level # So one bank could have both types of bank accounts like asset and liability # So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" - + filter_by_date = f"AND je.posting_date between '{from_date}' and '{to_date}'" + order_by = " je.posting_date" + filter_by_reference_no = "" + if cint(filter_by_reference_date): + filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'" + order_by = " je.cheque_date" + if frappe.flags.auto_reconcile_vouchers == True: + filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'" return f""" - SELECT (CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END + 1) AS rank , @@ -564,6 +752,9 @@ def get_je_matching_query(amount_condition, transaction): AND jea.account = %(bank_account)s AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s AND je.docstatus = 1 + {filter_by_date} + {filter_by_reference_no} + order by {order_by} """ diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py index a5d04137995..f900e0775ce 100644 --- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -5,6 +5,7 @@ import json import unittest import frappe +from frappe import utils from frappe.tests.utils import FrappeTestCase from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( @@ -40,7 +41,12 @@ class TestBankTransaction(FrappeTestCase): "Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"), ) - linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"]) + linked_payments = get_linked_payments( + bank_transaction.name, + ["payment_entry", "exact_match"], + from_date=bank_transaction.date, + to_date=utils.today(), + ) self.assertTrue(linked_payments[0][6] == "Conrad Electronic") # This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment @@ -81,7 +87,12 @@ class TestBankTransaction(FrappeTestCase): "Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"), ) - linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"]) + linked_payments = get_linked_payments( + bank_transaction.name, + ["payment_entry", "exact_match"], + from_date=bank_transaction.date, + to_date=utils.today(), + ) self.assertTrue(linked_payments[0][3]) # Check error if already reconciled diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js index 9ef8ce6b63e..f7c19a1b7ff 100644 --- a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js @@ -5,7 +5,12 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { Object.assign(this, opts); this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager( this.company, - this.bank_account + this.bank_account, + this.bank_statement_from_date, + this.bank_statement_to_date, + this.filter_by_reference_date, + this.from_reference_date, + this.to_reference_date ); this.make_dt(); } @@ -17,6 +22,8 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions", args: { bank_account: this.bank_account, + from_date: this.bank_statement_from_date, + to_date: this.bank_statement_to_date }, callback: function (response) { me.format_data(response.message); diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index b5e6ab871d1..51664f8885e 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -5,8 +5,12 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { this.bank_account = bank_account; this.company = company; this.make_dialog(); + this.bank_statement_from_date = bank_statement_from_date; + this.bank_statement_to_date = bank_statement_to_date; + this.filter_by_reference_date = filter_by_reference_date; + this.from_reference_date = from_reference_date; + this.to_reference_date = to_reference_date; } - show_dialog(bank_transaction_name, update_dt_cards) { this.bank_transaction_name = bank_transaction_name; this.update_dt_cards = update_dt_cards; @@ -35,13 +39,13 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { if (r.message) { this.bank_transaction = r.message; r.message.payment_entry = 1; + r.message.journal_entry = 1; this.dialog.set_values(r.message); this.dialog.show(); } }, }); } - get_linked_vouchers(document_types) { frappe.call({ method: @@ -49,6 +53,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { args: { bank_transaction_name: this.bank_transaction_name, document_types: document_types, + from_date: this.bank_statement_from_date, + to_date: this.bank_statement_to_date, + filter_by_reference_date: this.filter_by_reference_date, + from_reference_date:this.from_reference_date, + to_reference_date:this.to_reference_date }, callback: (result) => { @@ -66,6 +75,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { row[1], row[2], reference_date, + row[8], format_currency(row[3], row[9]), row[6], row[4], @@ -101,6 +111,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { editable: false, width: 120, }, + { + name: "Posting Date", + editable: false, + width: 120, + }, { name: __("Amount"), editable: false, @@ -578,4 +593,4 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { } } -}; +}; \ No newline at end of file From 4d2497faf1d3987d0b87c6b48bafbd4c3e2df852 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 15 Jan 2023 17:33:39 +0530 Subject: [PATCH 08/26] fix: asset value in fixed asset register (#33608) fix: asset value in fixed asset register (cherry picked from commit aa1f2a7297b09a600f84d2b552316aa899d288f1) Co-authored-by: anandbaburajan --- .../fixed_asset_register.py | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 6b14dce084e..dd5dfca8a22 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -86,6 +86,7 @@ def get_data(filters): "status", "department", "cost_center", + "calculate_depreciation", "purchase_receipt", "asset_category", "purchase_date", @@ -98,11 +99,7 @@ def get_data(filters): assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) for asset in assets_record: - asset_value = ( - asset.gross_purchase_amount - - flt(asset.opening_accumulated_depreciation) - - flt(depreciation_amount_map.get(asset.name)) - ) + asset_value = get_asset_value(asset, filters.finance_book) row = { "asset_id": asset.asset_id, "asset_name": asset.asset_name, @@ -125,6 +122,21 @@ def get_data(filters): return data +def get_asset_value(asset, finance_book=None): + if not asset.calculate_depreciation: + return flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) + + finance_book_filter = ["finance_book", "is", "not set"] + if finance_book: + finance_book_filter = ["finance_book", "=", finance_book] + + return frappe.db.get_value( + doctype="Asset Finance Book", + filters=[["parent", "=", asset.asset_id], finance_book_filter], + fieldname="value_after_depreciation", + ) + + def prepare_chart_data(data, filters): labels_values_map = {} date_field = frappe.scrub(filters.date_based_on) From df41b006fc85f76dc064366fca07cac9da0f86d3 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sun, 15 Jan 2023 21:09:16 +0530 Subject: [PATCH 09/26] chore: `Sales Order` link in `Pick List` (cherry picked from commit b3759890d703764d8b4a40125fb6df466a2e4b82) --- erpnext/stock/doctype/pick_list/pick_list_dashboard.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py index 92e57bed220..7fbcbafbac1 100644 --- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py +++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py @@ -1,7 +1,10 @@ def get_data(): return { "fieldname": "pick_list", + "internal_links": { + "Sales Order": ["locations", "sales_order"], + }, "transactions": [ - {"items": ["Stock Entry", "Delivery Note"]}, + {"items": ["Stock Entry", "Sales Order", "Delivery Note"]}, ], } From 623c35dfe171248071eaf4f05bda80b4b26ec136 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 16 Jan 2023 08:45:31 +0530 Subject: [PATCH 10/26] refactor: use DocStatus (#33594) refactor: use DocStatus (#33594) (cherry picked from commit 67cf7e1728627069e94727faffe8e388955a4a6c) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../doctype/repost_payment_ledger/repost_payment_ledger.py | 2 +- erpnext/controllers/sales_and_purchase_return.py | 2 +- erpnext/controllers/taxes_and_totals.py | 7 ++++--- erpnext/selling/doctype/sales_order/sales_order.py | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py index 25c89a17a06..400e07da6ab 100644 --- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py +++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py @@ -26,7 +26,7 @@ def start_payment_ledger_repost(docname=None): """ if docname: repost_doc = frappe.get_doc("Repost Payment Ledger", docname) - if repost_doc.docstatus == 1 and repost_doc.repost_status in ["Queued", "Failed"]: + if repost_doc.docstatus.is_submitted() and repost_doc.repost_status in ["Queued", "Failed"]: try: for entry in repost_doc.repost_vouchers: doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 15c82af856b..8bd09982bf4 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -37,7 +37,7 @@ def validate_return_against(doc): if ( ref_doc.company == doc.company and ref_doc.get(party_type) == doc.get(party_type) - and ref_doc.docstatus == 1 + and ref_doc.docstatus.is_submitted() ): # validate posting date time return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00") diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index c6a634ba806..8c403aa9bfe 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -6,6 +6,7 @@ import json import frappe from frappe import _, scrub +from frappe.model.document import Document from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction import erpnext @@ -20,7 +21,7 @@ from erpnext.stock.get_item_details import _get_item_tax_template class calculate_taxes_and_totals(object): - def __init__(self, doc): + def __init__(self, doc: Document): self.doc = doc frappe.flags.round_off_applicable_accounts = [] get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts) @@ -677,7 +678,7 @@ class calculate_taxes_and_totals(object): ) def calculate_total_advance(self): - if self.doc.docstatus < 2: + if not self.doc.docstatus.is_cancelled(): total_allocated_amount = sum( flt(adv.allocated_amount, adv.precision("allocated_amount")) for adv in self.doc.get("advances") @@ -708,7 +709,7 @@ class calculate_taxes_and_totals(object): ) ) - if self.doc.docstatus == 0: + if self.doc.docstatus.is_draft(): if self.doc.get("write_off_outstanding_amount_automatically"): self.doc.write_off_amount = 0 diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 7c0601e3dd5..accf5f22a6f 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -208,7 +208,7 @@ class SalesOrder(SellingController): for quotation in set(d.prevdoc_docname for d in self.get("items")): if quotation: doc = frappe.get_doc("Quotation", quotation) - if doc.docstatus == 2: + if doc.docstatus.is_cancelled(): frappe.throw(_("Quotation {0} is cancelled").format(quotation)) doc.set_status(update=True) From 23b9f661b641f9727cfdbad24f58110d839da5a9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 16 Jan 2023 08:50:39 +0530 Subject: [PATCH 11/26] revert: Reverting changes done on 33495 (#33662) 'ordered_qty' will not be fetched from `tabBin` (cherry picked from commit be382054e5c14aab2b021feba780ef9f78225f81) --- erpnext/stock/doctype/item/test_item.py | 1 - erpnext/stock/get_item_details.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 7e426ae4af8..53f6b7f8f17 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -106,7 +106,6 @@ class TestItem(FrappeTestCase): "conversion_factor": 1.0, "reserved_qty": 1, "actual_qty": 5, - "ordered_qty": 10, "projected_qty": 14, } diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index f7fcb30acd2..363dc0a63f3 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1181,7 +1181,7 @@ def get_projected_qty(item_code, warehouse): @frappe.whitelist() def get_bin_details(item_code, warehouse, company=None, include_child_warehouses=False): - bin_details = {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0, "ordered_qty": 0} + bin_details = {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0} if warehouse: from frappe.query_builder.functions import Coalesce, Sum @@ -1197,7 +1197,6 @@ def get_bin_details(item_code, warehouse, company=None, include_child_warehouses Coalesce(Sum(bin.projected_qty), 0).as_("projected_qty"), Coalesce(Sum(bin.actual_qty), 0).as_("actual_qty"), Coalesce(Sum(bin.reserved_qty), 0).as_("reserved_qty"), - Coalesce(Sum(bin.ordered_qty), 0).as_("ordered_qty"), ) .where((bin.item_code == item_code) & (bin.warehouse.isin(warehouses))) ).run(as_dict=True)[0] From faea73a4eed9849b5942861f9755c197f433c6a6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 16 Jan 2023 08:49:35 +0530 Subject: [PATCH 12/26] Revert "fix: Updating SO throws ordered_qty not allowed to change after submission" (#33646) (cherry picked from commit 333907b7a52890856a2bcfa794e886ae11d2c340) --- erpnext/selling/doctype/sales_order_item/sales_order_item.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index cfbd94b401b..50ae3a3f1a9 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -639,7 +639,6 @@ "width": "70px" }, { - "allow_on_submit": 1, "fieldname": "ordered_qty", "fieldtype": "Float", "label": "Ordered Qty", @@ -866,7 +865,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-01-12 13:13:28.691585", + "modified": "2022-12-25 02:51:10.247569", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", From 8a498ed029b9ffc2adc8eadade070f154ff5d768 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 14 Jan 2023 12:22:22 +0530 Subject: [PATCH 13/26] perf: improve reconciliation speed on JE's with 1000's of rows 1. No need to keep old PLE's on reconciliation. 2. Added Validation to catch debit-credit mismatch on JE's 3. Only update outstanding amount for newly reconciled invoices (cherry picked from commit 11cf694d9a1833758788d87baf70db35a87c2f08) --- erpnext/accounts/utils.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 7ba433ea8c8..31885ac07b3 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -439,8 +439,7 @@ def reconcile_against_document(args): # nosemgrep # cancel advance entry doc = frappe.get_doc(voucher_type, voucher_no) frappe.flags.ignore_party_validation = True - gl_map = doc.build_gl_map() - create_payment_ledger_entry(gl_map, cancel=1, adv_adj=1) + _delete_pl_entries(voucher_type, voucher_no) for entry in entries: check_if_advance_entry_modified(entry) @@ -452,11 +451,23 @@ def reconcile_against_document(args): # nosemgrep else: update_reference_in_payment_entry(entry, doc, do_not_save=True) + if doc.doctype == "Journal Entry": + try: + doc.validate_total_debit_and_credit() + except Exception as validation_exception: + raise frappe.ValidationError(_(f"Validation Error for {doc.name}")) from validation_exception + doc.save(ignore_permissions=True) # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) gl_map = doc.build_gl_map() - create_payment_ledger_entry(gl_map, cancel=0, adv_adj=1) + create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) + + # Only update outstanding for newly linked vouchers + for entry in entries: + update_voucher_outstanding( + entry.against_voucher_type, entry.against_voucher, entry.account, entry.party_type, entry.party + ) frappe.flags.ignore_party_validation = False From bddf3307542cdf98d16016c94d11a775ace019db Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 15 Jan 2023 18:09:51 +0530 Subject: [PATCH 14/26] fix: minor filter issue while reconciliation tool from bench console (cherry picked from commit 828eaf09301d84ebf78afb69e620c8d38a51e036) --- .../doctype/payment_reconciliation/payment_reconciliation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index ac033f7db60..13712cee01d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -471,6 +471,7 @@ class PaymentReconciliation(Document): def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False): self.common_filter_conditions.clear() + self.accounting_dimension_filter_conditions.clear() self.ple_posting_date_filter.clear() ple = qb.DocType("Payment Ledger Entry") From fe513433b282ccce57745686254a26eeb66eaf3f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 16 Jan 2023 10:01:43 +0530 Subject: [PATCH 15/26] fix: allow to create sales order from expired quotation (#33582) fix: allow to create sales order from expired quotation (#33582) (cherry picked from commit dceef0397a55bdca84d0eda88ad688aed314f349) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/selling/doctype/quotation/quotation.py | 9 +-------- erpnext/selling/doctype/quotation/test_quotation.py | 11 +++++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 484b8c9f08d..6836d56647f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -194,14 +194,7 @@ def get_list_context(context=None): @frappe.whitelist() -def make_sales_order(source_name, target_doc=None): - quotation = frappe.db.get_value( - "Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1 - ) - if quotation.valid_till and ( - quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate()) - ): - frappe.throw(_("Validity period of this quotation has ended.")) +def make_sales_order(source_name: str, target_doc=None): return _make_sales_order(source_name, target_doc) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index b151dd5e79c..5aaba4fa435 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -136,17 +136,20 @@ class TestQuotation(FrappeTestCase): sales_order.payment_schedule[1].due_date, getdate(add_days(quotation.transaction_date, 30)) ) - def test_valid_till(self): - from erpnext.selling.doctype.quotation.quotation import make_sales_order - + def test_valid_till_before_transaction_date(self): quotation = frappe.copy_doc(test_records[0]) quotation.valid_till = add_days(quotation.transaction_date, -1) self.assertRaises(frappe.ValidationError, quotation.validate) + def test_so_from_expired_quotation(self): + from erpnext.selling.doctype.quotation.quotation import make_sales_order + + quotation = frappe.copy_doc(test_records[0]) quotation.valid_till = add_days(nowdate(), -1) quotation.insert() quotation.submit() - self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) + + make_sales_order(quotation.name) def test_shopping_cart_without_website_item(self): if frappe.db.exists("Website Item", {"item_code": "_Test Item Home Desktop 100"}): From 35fbd67a938b7ffb5bbb7a5a39b4c9a9558acd33 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 16 Jan 2023 10:03:12 +0530 Subject: [PATCH 16/26] fix: Return against internal purchase invoice (backport #33635) (#33658) fix: Return against internal purchase invoice (#33635) (cherry picked from commit 906ad10d16d2d154cb230b0b0f3fab3d12a35701) Co-authored-by: Deepesh Garg --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 788dc4982e5..6fa44c93c22 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -394,7 +394,7 @@ class AccountsController(TransactionBase): self.get("inter_company_reference") or self.get("inter_company_invoice_reference") or self.get("inter_company_order_reference") - ): + ) and not self.get("is_return"): msg = _("Internal Sale or Delivery Reference missing.") msg += _("Please create purchase from internal sale or delivery document itself") frappe.throw(msg, title=_("Internal Sales Reference Missing")) From 0431a57ff067e492fdedd3bd767c7acb1402e00d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 15 Jan 2023 20:51:54 +0530 Subject: [PATCH 17/26] fix: attribute error while submitting Repost PLE (cherry picked from commit 2c50f43cdd6c185e0a5474e9a73ab6d3e404d765) --- .../repost_payment_ledger/repost_payment_ledger.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py index 400e07da6ab..209cad4f905 100644 --- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py +++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py @@ -101,10 +101,9 @@ def execute_repost_payment_ledger(docname): job_name = "payment_ledger_repost_" + docname - if not frappe.utils.background_jobs.is_job_queued(job_name): - frappe.enqueue( - method="erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.start_payment_ledger_repost", - docname=docname, - is_async=True, - job_name=job_name, - ) + frappe.enqueue( + method="erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.start_payment_ledger_repost", + docname=docname, + is_async=True, + job_name=job_name, + ) From 6b3dc90560c52089b12d8cf886bc67d9a3c54a88 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 16 Jan 2023 18:58:17 +0530 Subject: [PATCH 18/26] refactor: picked qty in sales order item (cherry picked from commit 1bcff80074e4a79d5d37ecff8003c4a5379380ef) --- erpnext/stock/doctype/pick_list/pick_list.py | 168 +++++++++++++------ 1 file changed, 115 insertions(+), 53 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 9e6aead02db..9c6f4f4a352 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -4,7 +4,7 @@ import json from collections import OrderedDict, defaultdict from itertools import groupby -from typing import Dict, List, Set +from typing import Dict, List import frappe from frappe import _ @@ -41,7 +41,9 @@ class PickList(Document): ) def before_submit(self): - update_sales_orders = set() + self.validate_picked_items() + + def validate_picked_items(self): for item in self.locations: if self.scan_mode and item.picked_qty < item.stock_qty: frappe.throw( @@ -50,17 +52,14 @@ class PickList(Document): ).format(item.idx, item.stock_qty - item.picked_qty, item.stock_uom), title=_("Pick List Incomplete"), ) - elif not self.scan_mode and item.picked_qty == 0: + + if not self.scan_mode and item.picked_qty == 0: # if the user has not entered any picked qty, set it to stock_qty, before submit item.picked_qty = item.stock_qty - if item.sales_order_item: - # update the picked_qty in SO Item - self.update_sales_order_item(item, item.picked_qty, item.item_code) - update_sales_orders.add(item.sales_order) - if not frappe.get_cached_value("Item", item.item_code, "has_serial_no"): continue + if not item.serial_no: frappe.throw( _("Row #{0}: {1} does not have any available serial numbers in {2}").format( @@ -68,58 +67,96 @@ class PickList(Document): ), title=_("Serial Nos Required"), ) - if len(item.serial_no.split("\n")) == item.picked_qty: - continue - frappe.throw( - _( - "For item {0} at row {1}, count of serial numbers does not match with the picked quantity" - ).format(frappe.bold(item.item_code), frappe.bold(item.idx)), - title=_("Quantity Mismatch"), - ) - self.update_bundle_picked_qty() - self.update_sales_order_picking_status(update_sales_orders) - - def before_cancel(self): - """Deduct picked qty on cancelling pick list""" - updated_sales_orders = set() - - for item in self.get("locations"): - if item.sales_order_item: - self.update_sales_order_item(item, -1 * item.picked_qty, item.item_code) - updated_sales_orders.add(item.sales_order) - - self.update_bundle_picked_qty() - self.update_sales_order_picking_status(updated_sales_orders) - - def update_sales_order_item(self, item, picked_qty, item_code): - item_table = "Sales Order Item" if not item.product_bundle_item else "Packed Item" - stock_qty_field = "stock_qty" if not item.product_bundle_item else "qty" - - already_picked, actual_qty = frappe.db.get_value( - item_table, - item.sales_order_item, - ["picked_qty", stock_qty_field], - for_update=True, - ) - - if self.docstatus == 1: - if (((already_picked + picked_qty) / actual_qty) * 100) > ( - 100 + flt(frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance")) - ): + if len(item.serial_no.split("\n")) != item.picked_qty: frappe.throw( _( - "You are picking more than required quantity for {}. Check if there is any other pick list created for {}" - ).format(item_code, item.sales_order) + "For item {0} at row {1}, count of serial numbers does not match with the picked quantity" + ).format(frappe.bold(item.item_code), frappe.bold(item.idx)), + title=_("Quantity Mismatch"), ) - frappe.db.set_value(item_table, item.sales_order_item, "picked_qty", already_picked + picked_qty) + def on_submit(self): + self.update_bundle_picked_qty() + self.update_reference_qty() + self.update_sales_order_picking_status() + + def on_cancel(self): + self.update_bundle_picked_qty() + self.update_reference_qty() + self.update_sales_order_picking_status() + + def update_reference_qty(self): + packed_items = [] + so_items = [] + + for item in self.locations: + if item.product_bundle_item: + packed_items.append(item.sales_order_item) + elif item.sales_order_item: + so_items.append(item.sales_order_item) + + if packed_items: + self.update_packed_items_qty(packed_items) + + if so_items: + self.update_sales_order_item_qty(so_items) + + def update_packed_items_qty(self, packed_items): + picked_items = get_picked_items_qty(packed_items) + self.validate_picked_qty(picked_items) + + picked_qty = frappe._dict() + for d in picked_items: + picked_qty[d.sales_order_item] = d.picked_qty + + for packed_item in packed_items: + frappe.db.set_value( + "Packed Item", + packed_item, + "picked_qty", + flt(picked_qty.get(packed_item)), + update_modified=False, + ) + + def update_sales_order_item_qty(self, so_items): + picked_items = get_picked_items_qty(so_items) + self.validate_picked_qty(picked_items) + + picked_qty = frappe._dict() + for d in picked_items: + picked_qty[d.sales_order_item] = d.picked_qty + + for so_item in so_items: + frappe.db.set_value( + "Sales Order Item", + so_item, + "picked_qty", + flt(picked_qty.get(so_item)), + update_modified=False, + ) + + def update_sales_order_picking_status(self) -> None: + sales_orders = [] + for row in self.locations: + if row.sales_order and row.sales_order not in sales_orders: + sales_orders.append(row.sales_order) - @staticmethod - def update_sales_order_picking_status(sales_orders: Set[str]) -> None: for sales_order in sales_orders: - if sales_order: - frappe.get_doc("Sales Order", sales_order, for_update=True).update_picking_status() + frappe.get_doc("Sales Order", sales_order, for_update=True).update_picking_status() + + def validate_picked_qty(self, data): + over_delivery_receipt_allowance = 100 + flt( + frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance") + ) + + for row in data: + if (row.picked_qty / row.stock_qty) * 100 > over_delivery_receipt_allowance: + frappe.throw( + _( + f"You are picking more than required quantity for the item {row.item_code}. Check if there is any other pick list created for the sales order {row.sales_order}." + ) + ) @frappe.whitelist() def set_item_locations(self, save=False): @@ -309,6 +346,31 @@ class PickList(Document): return int(flt(min(possible_bundles), precision or 6)) +def get_picked_items_qty(items) -> List[Dict]: + return frappe.db.sql( + f""" + SELECT + sales_order_item, + item_code, + sales_order, + SUM(stock_qty) AS stock_qty, + SUM(picked_qty) AS picked_qty + FROM + `tabPick List Item` + WHERE + sales_order_item IN ( + {", ".join(frappe.db.escape(d) for d in items)} + ) + AND docstatus = 1 + GROUP BY + sales_order_item, + sales_order + FOR UPDATE + """, + as_dict=1, + ) + + def validate_item_locations(pick_list): if not pick_list.locations: frappe.throw(_("Add items in the Item Locations table")) From bc55f44de6719483b158dec193293d61eed04905 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 16 Jan 2023 23:36:46 +0530 Subject: [PATCH 19/26] fix: asset repair link --- erpnext/assets/doctype/asset/asset.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 8b6af47b049..5e6fe14ac04 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -513,7 +513,7 @@ { "group": "Repair", "link_doctype": "Asset Repair", - "link_fieldname": "asset_name" + "link_fieldname": "asset" }, { "group": "Value", @@ -521,7 +521,7 @@ "link_fieldname": "asset" } ], - "modified": "2022-12-05 16:21:30.024060", + "modified": "2023-01-16 23:35:37.423100", "modified_by": "Administrator", "module": "Assets", "name": "Asset", From 4d65d6f9bd73008db5682027e1ceb44e44d64e82 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 16 Jan 2023 23:29:13 +0530 Subject: [PATCH 20/26] feat: provision to select date type based on filter (cherry picked from commit 20c88732086771ca9f54f237fbe19c004d8cf584) --- .../work_order_summary/work_order_summary.js | 28 +++++-------------- .../work_order_summary/work_order_summary.py | 19 +++++++++++-- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index 832be2301c1..67bd24dd805 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -13,38 +13,24 @@ frappe.query_reports["Work Order Summary"] = { reqd: 1 }, { - fieldname: "fiscal_year", - label: __("Fiscal Year"), - fieldtype: "Link", - options: "Fiscal Year", - default: frappe.defaults.get_user_default("fiscal_year"), - reqd: 1, - on_change: function(query_report) { - var fiscal_year = query_report.get_values().fiscal_year; - if (!fiscal_year) { - return; - } - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { - var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); - frappe.query_report.set_filter_value({ - from_date: fy.year_start_date, - to_date: fy.year_end_date - }); - }); - } + label: __("Based On"), + fieldname:"based_on", + fieldtype: "Select", + options: "Creation Date\nPlanned Date\nActual Date", + default: "Creation Date" }, { label: __("From Posting Date"), fieldname:"from_date", fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: frappe.datetime.add_months(frappe.datetime.get_today(), -3), reqd: 1 }, { label: __("To Posting Date"), fieldname:"to_date", fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: frappe.datetime.get_today(), reqd: 1, }, { diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py index b69ad070e16..97f30ef62e9 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -31,6 +31,7 @@ def get_data(filters): "sales_order", "production_item", "qty", + "creation", "produced_qty", "planned_start_date", "planned_end_date", @@ -47,11 +48,17 @@ def get_data(filters): if filters.get(field): query_filters[field] = filters.get(field) - query_filters["planned_start_date"] = (">=", filters.get("from_date")) - query_filters["planned_end_date"] = ("<=", filters.get("to_date")) + if filters.get("based_on") == "Planned Date": + query_filters["planned_start_date"] = (">=", filters.get("from_date")) + query_filters["planned_end_date"] = ("<=", filters.get("to_date")) + elif filters.get("based_on") == "Actual Date": + query_filters["actual_start_date"] = (">=", filters.get("from_date")) + query_filters["actual_end_date"] = ("<=", filters.get("to_date")) + else: + query_filters["creation"] = ("between", [filters.get("from_date"), filters.get("to_date")]) data = frappe.get_all( - "Work Order", fields=fields, filters=query_filters, order_by="planned_start_date asc" + "Work Order", fields=fields, filters=query_filters, order_by="planned_start_date asc", debug=1 ) res = [] @@ -213,6 +220,12 @@ def get_columns(filters): "options": "Sales Order", "width": 90, }, + { + "label": _("Created On"), + "fieldname": "creation", + "fieldtype": "Date", + "width": 150, + }, { "label": _("Planned Start Date"), "fieldname": "planned_start_date", From a1bfa569e6076df5ceed4bc51dd645d282547b77 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 17 Jan 2023 09:16:26 +0530 Subject: [PATCH 21/26] chore: Typo in payment reconciliation (backport #33686) (#33691) chore: Typo in payment reconciliation (#33686) (cherry picked from commit 0639d9e32a4bea0d60b85e367837dc88528b770a) Co-authored-by: Deepesh Garg --- .../doctype/payment_reconciliation/payment_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 13712cee01d..12c0b7a7bf7 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -334,7 +334,7 @@ class PaymentReconciliation(Document): ) # Account Currency has balance - dr_or_cr = "debit" if self.party_type == "Customer" else "debit" + dr_or_cr = "debit" if self.party_type == "Customer" else "credit" reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" journal_account = frappe._dict( From 8a0403119f246de5b79eb860ee429ec21d65cc1c Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Mon, 16 Jan 2023 21:03:18 +0100 Subject: [PATCH 22/26] fix: Sales ORder Connections on Material Request (cherry picked from commit e19161a8eef71d9064fb3c432328a8f06bff9da0) --- erpnext/selling/doctype/sales_order/sales_order_dashboard.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index 5c4b57813d3..cbc40bbf90b 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -14,7 +14,6 @@ def get_data(): }, "internal_links": { "Quotation": ["items", "prevdoc_docname"], - "Material Request": ["items", "material_request"], }, "transactions": [ { From 79f171c31a520478d943507e6be4be3fc3c650cf Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 17 Jan 2023 10:58:38 +0530 Subject: [PATCH 23/26] chore: ignore b028 --- .github/helper/.flake8_strict | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/helper/.flake8_strict b/.github/helper/.flake8_strict index 198ec7bfe54..3e8f7dd11ab 100644 --- a/.github/helper/.flake8_strict +++ b/.github/helper/.flake8_strict @@ -66,7 +66,8 @@ ignore = F841, E713, E712, - B023 + B023, + B028 max-line-length = 200 From 192819516783c7575b30c8757a71b9765e6045f9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 17 Jan 2023 11:32:06 +0530 Subject: [PATCH 24/26] fix: patch item_reposting_for_incorrect_sl_and_gl (cherry picked from commit dbde3a34210bbf63ea5647d46e3fc74c8f69ca97) --- erpnext/patches.txt | 2 +- erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f4b1a06aea8..aaaaa8ce051 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -195,7 +195,6 @@ erpnext.patches.v13_0.update_project_template_tasks erpnext.patches.v13_0.convert_qi_parameter_to_link_field erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021 erpnext.patches.v13_0.update_payment_terms_outstanding -erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes erpnext.patches.v13_0.update_vehicle_no_reqd_condition erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings @@ -291,6 +290,7 @@ erpnext.patches.v13_0.update_exchange_rate_settings erpnext.patches.v14_0.delete_amazon_mws_doctype erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr erpnext.patches.v13_0.update_accounts_in_loan_docs +erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl erpnext.patches.v14_0.update_batch_valuation_flag erpnext.patches.v14_0.delete_non_profit_doctypes erpnext.patches.v13_0.add_cost_center_in_loans diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py index 75a5477be8f..c0d715063a8 100644 --- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -7,6 +7,7 @@ from erpnext.stock.stock_ledger import update_entries_after def execute(): doctypes_to_reload = [ + ("setup", "company"), ("stock", "repost_item_valuation"), ("stock", "stock_entry_detail"), ("stock", "purchase_receipt_item"), From 9fa4c1a3bd4c6a4122f85d57c7beafa926ce44d1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:59:13 +0530 Subject: [PATCH 25/26] fix: Rate from LDC in TDS reports (backport #33699) (#33700) fix: Rate from LDC in TDS reports (#33699) (cherry picked from commit db9beb3cddc78376ccd30b57efafa35381b482d6) Co-authored-by: Deepesh Garg --- .../tax_withholding_category/tax_withholding_category.py | 6 ++---- .../report/tds_payable_monthly/tds_payable_monthly.py | 7 +++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index b834d1404d0..1bce43fd310 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -259,9 +259,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N if tax_deducted: net_total = inv.tax_withholding_net_total if ldc: - tax_amount = get_tds_amount_from_ldc( - ldc, parties, pan_no, tax_details, posting_date, net_total - ) + tax_amount = get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total) else: tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0 @@ -538,7 +536,7 @@ def get_invoice_total_without_tcs(inv, tax_details): return inv.grand_total - tcs_tax_row_amount -def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net_total): +def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total): tds_amount = 0 limit_consumed = frappe.db.get_value( "Purchase Invoice", diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 98838907be1..bfe2a0fd2be 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.utils import flt def execute(filters=None): @@ -65,6 +66,12 @@ def get_result( else: total_amount_credited += entry.credit + ## Check if ldc is applied and show rate as per ldc + actual_rate = (tds_deducted / total_amount_credited) * 100 + + if flt(actual_rate) < flt(rate): + rate = actual_rate + if tds_deducted: row = { "pan" From f88c8c48c98fa44e2058bc66f6e63c4ca5153d7e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 17 Jan 2023 21:05:49 +0530 Subject: [PATCH 26/26] fix: Missing constructor args in Bank Reco Tool (#33705) fix: Missing constructor args in Bank Reco Tool (#33705) (cherry picked from commit 6b31c27ed6824e5422f6c349d5828fb67df9afe4) Co-authored-by: Deepesh Garg --- erpnext/accounts/doctype/journal_entry/journal_entry.json | 5 ++--- erpnext/public/js/bank_reconciliation_tool/dialog_manager.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 3f69d5c7cd8..498fc7c295f 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -137,8 +137,7 @@ "fieldname": "finance_book", "fieldtype": "Link", "label": "Finance Book", - "options": "Finance Book", - "read_only": 1 + "options": "Finance Book" }, { "fieldname": "2_add_edit_gl_entries", @@ -539,7 +538,7 @@ "idx": 176, "is_submittable": 1, "links": [], - "modified": "2022-11-28 17:40:01.241908", + "modified": "2023-01-17 12:53:53.280620", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index 51664f8885e..911343d8b64 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -1,7 +1,7 @@ frappe.provide("erpnext.accounts.bank_reconciliation"); erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { - constructor(company, bank_account) { + constructor(company, bank_account, bank_statement_from_date, bank_statement_to_date, filter_by_reference_date, from_reference_date, to_reference_date) { this.bank_account = bank_account; this.company = company; this.make_dialog();