From 33d38ba3a71f00a62688d31722f3dc934f95dc6f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 23 Apr 2024 10:38:37 +0530 Subject: [PATCH 01/30] fix: mode of payment has precedance Mode of Payment is given precedence over company/party bank account (cherry picked from commit 4aef96987988c66ef2f1c41392661711f9b6e98b) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 309141fe4c27..c0716ff19aed 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -1088,7 +1088,9 @@ frappe.ui.form.on('Payment Entry', { }, callback: function(r) { if (r.message) { - frm.set_value(field, r.message.account); + if (!frm.doc.mode_of_payment) { + frm.set_value(field, r.message.account); + } frm.set_value('bank', r.message.bank); frm.set_value('bank_account_no', r.message.bank_account_no); } From 113351e8507075196cb37350bb34fa9fb5fb2484 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 24 Apr 2024 22:44:31 +0530 Subject: [PATCH 02/30] perf: timeout issue while submitting purchase receipt (v14) --- erpnext/stock/doctype/serial_no/serial_no.py | 89 ++++++++++++++++---- 1 file changed, 72 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 50f476878282..10d5ae643b0b 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -8,16 +8,7 @@ from frappe import ValidationError, _ from frappe.model.naming import make_autoname from frappe.query_builder.functions import Coalesce -from frappe.utils import ( - add_days, - cint, - cstr, - flt, - get_link_to_form, - getdate, - nowdate, - safe_json_loads, -) +from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, now, nowdate, safe_json_loads from erpnext.controllers.stock_controller import StockController from erpnext.stock.get_item_details import get_reserved_qty_for_so @@ -618,16 +609,14 @@ def auto_make_serial_nos(args): voucher_type = args.get("voucher_type") item_code = args.get("item_code") for serial_no in serial_nos: - is_new = False if frappe.db.exists("Serial No", serial_no): sr = frappe.get_cached_doc("Serial No", serial_no) - elif args.get("actual_qty", 0) > 0: - sr = frappe.new_doc("Serial No") - is_new = True + sr = update_args_for_serial_no(sr, serial_no, args) + elif args.get("actual_qty", 0) > 0 and serial_no: + created_numbers.append(serial_no) - sr = update_args_for_serial_no(sr, serial_no, args, is_new=is_new) - if is_new: - created_numbers.append(sr.name) + if created_numbers: + make_bulk_serial_nos(args, created_numbers) form_links = list(map(lambda d: get_link_to_form("Serial No", d), created_numbers)) @@ -647,6 +636,72 @@ def auto_make_serial_nos(args): frappe.msgprint(message, multiple_title) +def make_bulk_serial_nos(args, serial_nos): + # for field in ["item_code", "work_order", "company", "batch_no", "supplier", "location"]: + + if isinstance(args, dict): + args = frappe._dict(args) + + serial_nos_details = [] + item_details = frappe.get_cached_value("Item", args.item_code, ["item_name", "description"], as_dict=1) + + supplier = None + if args.voucher_type in ["Purchase Receipt", "Purchase Invoice"]: + supplier = frappe.get_cached_value(args.voucher_type, args.voucher_no, "supplier") + + for serial_no in serial_nos: + serial_nos_details.append( + ( + serial_no, + serial_no, + now(), + now(), + frappe.session.user, + frappe.session.user, + args.warehouse, + args.company, + args.item_code, + item_details.item_name, + item_details.description, + "Active", + args.batch_no, + args.get("work_order"), + supplier, + args.voucher_type, + args.voucher_no, + args.posting_date, + args.posting_time, + flt(args.incoming_rate), + ) + ) + + if serial_nos_details: + fields = [ + "name", + "serial_no", + "creation", + "modified", + "owner", + "modified_by", + "warehouse", + "company", + "item_code", + "item_name", + "description", + "status", + "batch_no", + "work_order", + "supplier", + "purchase_document_type", + "purchase_document_no", + "purchase_date", + "purchase_time", + "purchase_rate", + ] + + frappe.db.bulk_insert("Serial No", fields=fields, values=set(serial_nos_details)) + + def get_items_html(serial_nos, item_code): body = ", ".join(serial_nos) return f"""
From 59010c9a617a6e91b3b90b1970c18c7a226b5238 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 17:26:29 +0530 Subject: [PATCH 03/30] fix: handle stock balance unbuffered_cursor error (backport #41186) (#41187) fix: handle stock balance unbuffered_cursor error (#41186) (cherry picked from commit 341fb6d8f3b507700638c59548093784b12dde04) Co-authored-by: Ankush Menat --- erpnext/stock/report/stock_balance/stock_balance.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index f0fddac2c648..5a79cdf1827a 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -141,6 +141,8 @@ def get_item_warehouse_map(self): if self.filters.get("show_stock_ageing_data"): self.sle_entries = self.sle_query.run(as_dict=True) + # HACK: This is required to avoid causing db query in flt + _system_settings = frappe.get_cached_doc("System Settings") with frappe.db.unbuffered_cursor(): if not self.filters.get("show_stock_ageing_data"): self.sle_entries = self.sle_query.run(as_dict=True, as_iterator=True) From 3fcdcef1786fac193cf1f6065e927bfe22d2747a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 26 Apr 2024 10:43:30 +0530 Subject: [PATCH 04/30] fix: duplicate column in the stock ledger report (cherry picked from commit be7fd6bfb42081e3e3fbb24e82dd5b3dbdc681d8) --- erpnext/stock/report/stock_ledger/stock_ledger.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index a9f74149d51d..bde1434e600a 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -156,13 +156,6 @@ def get_columns(filters): "width": 100, "convertible": "qty", }, - { - "label": _("Voucher #"), - "fieldname": "voucher_no", - "fieldtype": "Dynamic Link", - "options": "voucher_type", - "width": 150, - }, { "label": _("Warehouse"), "fieldname": "warehouse", From 1a7b3c437dcd18da3425696af597528dc2e9d751 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Thu, 25 Apr 2024 16:42:38 +0530 Subject: [PATCH 05/30] fix: args when get the delivery note in delivery trip (cherry picked from commit 2f359e201d9cbc7e06ccec13ef87280eb6314880) # Conflicts: # erpnext/stock/doctype/delivery_note/delivery_note.py --- erpnext/stock/doctype/delivery_note/delivery_note.py | 6 ++++++ erpnext/stock/doctype/delivery_trip/delivery_trip.js | 1 + 2 files changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 2ad3f485d08c..995e1b6e85d4 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -739,6 +739,7 @@ def get_pending_qty(item_row): @frappe.whitelist() +<<<<<<< HEAD def make_delivery_trip(source_name, target_doc=None): def update_stop_details(source_doc, target_doc, source_parent): target_doc.customer = source_parent.customer @@ -753,6 +754,11 @@ def update_stop_details(source_doc, target_doc, source_parent): delivery_notes = [] +======= +def make_delivery_trip(source_name, target_doc=None, kwargs=None): + if not target_doc: + target_doc = frappe.new_doc("Delivery Trip") +>>>>>>> 2f359e201d (fix: args when get the delivery note in delivery trip) doclist = get_mapped_doc( "Delivery Note", source_name, diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js index 4f8649c0bfa2..ba4a3b3486cb 100755 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js @@ -51,6 +51,7 @@ frappe.ui.form.on("Delivery Trip", { frm.add_custom_button( __("Delivery Note"), () => { + frm.clear_table('delivery_stops'); erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", source_doctype: "Delivery Note", From abe64aa1aba81ac4147b835b45cd8ae6f38dcd96 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Thu, 25 Apr 2024 16:46:29 +0530 Subject: [PATCH 06/30] fix: args when get the delivery note in delivery trip (cherry picked from commit ca577f7aaa595ec05754d54ac3a672ad4187c8af) --- erpnext/stock/doctype/delivery_trip/delivery_trip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js index ba4a3b3486cb..77eae534d17d 100755 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js @@ -51,7 +51,7 @@ frappe.ui.form.on("Delivery Trip", { frm.add_custom_button( __("Delivery Note"), () => { - frm.clear_table('delivery_stops'); + frm.clear_table("delivery_stops"); erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", source_doctype: "Delivery Note", From 2f6fee98776dd54145ed5ca13a4343e73518df09 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 26 Apr 2024 13:15:32 +0530 Subject: [PATCH 07/30] chore: fix conflicts --- erpnext/stock/doctype/delivery_note/delivery_note.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 995e1b6e85d4..18adf6ab1f98 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -739,8 +739,7 @@ def get_pending_qty(item_row): @frappe.whitelist() -<<<<<<< HEAD -def make_delivery_trip(source_name, target_doc=None): +def make_delivery_trip(source_name, target_doc=None, kwargs=None): def update_stop_details(source_doc, target_doc, source_parent): target_doc.customer = source_parent.customer target_doc.address = source_parent.shipping_address_name @@ -754,11 +753,6 @@ def update_stop_details(source_doc, target_doc, source_parent): delivery_notes = [] -======= -def make_delivery_trip(source_name, target_doc=None, kwargs=None): - if not target_doc: - target_doc = frappe.new_doc("Delivery Trip") ->>>>>>> 2f359e201d (fix: args when get the delivery note in delivery trip) doclist = get_mapped_doc( "Delivery Note", source_name, From 949aa9346c4d28b13dc43a2c31b573572fdddfea Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 26 Apr 2024 13:25:46 +0530 Subject: [PATCH 08/30] fix: 'NoneType' object has no attribute '_read_rowdata_packet_unbuffered' --- .../batch_wise_balance_history.py | 1 + erpnext/stock/report/stock_ageing/stock_ageing.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index c8c26fd66cb5..8e27b8c4f69c 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -131,6 +131,7 @@ def get_stock_ledger_entries(filters): def get_item_warehouse_batch_map(filters, float_precision): + _system_settings = frappe.get_cached_doc("System Settings") with frappe.db.unbuffered_cursor(): sle = get_stock_ledger_entries(filters) sle = sle.run(as_dict=True, as_iterator=True) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 26bf99e1ed77..c09137e645bd 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -229,11 +229,12 @@ def generate(self) -> dict: """ stock_ledger_entries = self.sle + _system_settings = frappe.get_cached_doc("System Settings") with frappe.db.unbuffered_cursor(): - if self.sle is None: - self.sle = self.__get_stock_ledger_entries() + if stock_ledger_entries is None: + stock_ledger_entries = self.__get_stock_ledger_entries() - for d in self.sle: + for d in stock_ledger_entries: key, fifo_queue, transferred_item_key = self.__init_key_stores(d) if d.voucher_type == "Stock Reconciliation": From 1e1319351dfad56e586ebf8be58e5db7a35de82b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:25:51 +0530 Subject: [PATCH 09/30] feat: allow to do reposting for all stock transactions (audit) (backport #41165) (#41205) * feat: allow to do reposting for all transactions (audit) (cherry picked from commit aefbe21b464d7895b412562fb04d91ae6feed373) # Conflicts: # erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json # erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: Rohit Waghchaure --- erpnext/controllers/stock_controller.py | 7 ++- erpnext/stock/doctype/bin/bin.py | 2 +- .../stock_reposting_settings.json | 11 ++++- .../stock_reposting_settings.py | 4 ++ .../test_stock_reposting_settings.py | 48 +++++++++++++++++++ 5 files changed, 68 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 15a79c8efb41..410851817204 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1008,7 +1008,12 @@ def is_reposting_pending(): ) -def future_sle_exists(args, sl_entries=None): +def future_sle_exists(args, sl_entries=None, allow_force_reposting=True): + if allow_force_reposting and frappe.db.get_single_value( + "Stock Reposting Settings", "do_reposting_for_each_stock_transaction" + ): + return True + key = (args.voucher_type, args.voucher_no) if not hasattr(frappe.local, "future_sle"): frappe.local.future_sle = {} diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 75753c8b0f5e..c7b36b6aa3f5 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -202,7 +202,7 @@ def update_qty(bin_name, args): sle = frappe.qb.DocType("Stock Ledger Entry") # actual qty is not up to date in case of backdated transaction - if future_sle_exists(args): + if future_sle_exists(args, allow_force_reposting=False): last_sle_qty = ( frappe.qb.from_(sle) .select(sle.qty_after_transaction) diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json index 68afd996b498..cbbb0ce09906 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json @@ -13,6 +13,7 @@ "end_time", "limits_dont_apply_on", "item_based_reposting", + "do_reposting_for_each_stock_transaction", "errors_notification_section", "notify_reposting_error_to_role" ], @@ -65,12 +66,18 @@ "fieldname": "errors_notification_section", "fieldtype": "Section Break", "label": "Errors Notification" + }, + { + "default": "0", + "fieldname": "do_reposting_for_each_stock_transaction", + "fieldtype": "Check", + "label": "Do reposting for each Stock Transaction" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-11-01 16:14:29.080697", + "modified": "2024-04-24 12:19:40.204888", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reposting Settings", @@ -91,4 +98,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py index 51fb5ac4c409..10104fb9a609 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py @@ -11,6 +11,10 @@ class StockRepostingSettings(Document): def validate(self): self.set_minimum_reposting_time_slot() + def before_save(self): + if self.do_reposting_for_each_stock_transaction: + self.item_based_reposting = 1 + def set_minimum_reposting_time_slot(self): """Ensure that timeslot for reposting is at least 12 hours.""" if not self.limit_reposting_timeslot: diff --git a/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py index a6dc72d7a425..e53659c17359 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py +++ b/erpnext/stock/doctype/stock_reposting_settings/test_stock_reposting_settings.py @@ -38,3 +38,51 @@ def test_notify_reposting_error_to_role(self): users = get_recipients() self.assertTrue(user in users) + + def test_do_reposting_for_each_stock_transaction(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + frappe.db.set_single_value("Stock Reposting Settings", "do_reposting_for_each_stock_transaction", 1) + if frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting"): + frappe.db.set_single_value("Stock Reposting Settings", "item_based_reposting", 0) + + item = make_item( + "_Test item for reposting check for each transaction", properties={"is_stock_item": 1} + ).name + + stock_entry = make_stock_entry( + item_code=item, + qty=1, + rate=100, + stock_entry_type="Material Receipt", + target="_Test Warehouse - _TC", + ) + + riv = frappe.get_all("Repost Item Valuation", filters={"voucher_no": stock_entry.name}, pluck="name") + self.assertTrue(riv) + + frappe.db.set_single_value("Stock Reposting Settings", "do_reposting_for_each_stock_transaction", 0) + + def test_do_not_reposting_for_each_stock_transaction(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + frappe.db.set_single_value("Stock Reposting Settings", "do_reposting_for_each_stock_transaction", 0) + if frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting"): + frappe.db.set_single_value("Stock Reposting Settings", "item_based_reposting", 0) + + item = make_item( + "_Test item for do not reposting check for each transaction", properties={"is_stock_item": 1} + ).name + + stock_entry = make_stock_entry( + item_code=item, + qty=1, + rate=100, + stock_entry_type="Material Receipt", + target="_Test Warehouse - _TC", + ) + + riv = frappe.get_all("Repost Item Valuation", filters={"voucher_no": stock_entry.name}, pluck="name") + self.assertFalse(riv) From 866b0c6ac75c2e02e83513a168b17cccf98c14d0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:41:54 +0530 Subject: [PATCH 10/30] chore: delete invalid translations (backport #41227) (#41228) chore: delete invalid translations (#41227) (cherry picked from commit 067419b7cdbe43d4845e1a98c0889ba3c8de6ce2) Co-authored-by: Ankush Menat --- erpnext/translations/en.csv | 1 - 1 file changed, 1 deletion(-) delete mode 100644 erpnext/translations/en.csv diff --git a/erpnext/translations/en.csv b/erpnext/translations/en.csv deleted file mode 100644 index 7fac9e1785b1..000000000000 --- a/erpnext/translations/en.csv +++ /dev/null @@ -1 +0,0 @@ -Married,既婚, From 54313b5db902c108458392b6d389d16e9213b55a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 29 Apr 2024 20:53:10 +0530 Subject: [PATCH 11/30] fix: multiple pricing rules with discount amount and discount percentage not working (#41211) --- .../doctype/pricing_rule/pricing_rule.py | 16 ++++++ .../doctype/pricing_rule/test_pricing_rule.py | 53 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index f39aa026b471..bc30118a0c6a 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -486,6 +486,22 @@ def apply_price_discount_rule(pricing_rule, item_details, args): if pricing_rule.apply_discount_on_rate and item_details.get("discount_percentage"): # Apply discount on discounted rate item_details[field] += (100 - item_details[field]) * (pricing_rule.get(field, 0) / 100) + elif args.price_list_rate: + value = pricing_rule.get(field, 0) + calculate_discount_percentage = False + if field == "discount_percentage": + field = "discount_amount" + value = args.price_list_rate * (value / 100) + calculate_discount_percentage = True + + if field not in item_details: + item_details.setdefault(field, 0) + + item_details[field] += value if pricing_rule else args.get(field, 0) + if calculate_discount_percentage and args.price_list_rate and item_details.discount_amount: + item_details.discount_percentage = flt( + (flt(item_details.discount_amount) / flt(args.price_list_rate)) * 100 + ) else: if field not in item_details: item_details.setdefault(field, 0) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index ab52d6c97291..cb0c223bffad 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -978,6 +978,59 @@ def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self): self.assertEqual(so.items[1].item_code, "_Test Item") self.assertEqual(so.items[1].qty, 4) + def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule 1", + "name": "_Test Pricing Rule 1", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "price_or_product_discount": "Price", + "rate_or_discount": "Discount Percentage", + "discount_percentage": 10, + "apply_multiple_pricing_rules": 1, + "company": "_Test Company", + } + + frappe.get_doc(test_record.copy()).insert() + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule 2", + "name": "_Test Pricing Rule 2", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "price_or_product_discount": "Price", + "rate_or_discount": "Discount Amount", + "discount_amount": 100, + "apply_multiple_pricing_rules": 1, + "company": "_Test Company", + } + + frappe.get_doc(test_record.copy()).insert() + + so = make_sales_order(item_code="_Test Item", qty=1, price_list_rate=1000, do_not_submit=True) + self.assertEqual(so.items[0].discount_amount, 200) + self.assertEqual(so.items[0].rate, 800) + + frappe.delete_doc_if_exists("Sales Order", so.name) + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + test_dependencies = ["Campaign"] From eb22fb9326c93cd7e6d990f2fb85e05f698983df Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 26 Apr 2024 10:50:51 +0530 Subject: [PATCH 12/30] fix: incorrectly applying TDS when Advance is in previous FY (cherry picked from commit b195f519e2266a63d4a38ec7c39c5d23392dad36) --- .../tax_withholding_category.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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 58ce2840b4b6..d8b2079e5acc 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -253,6 +253,14 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N if taxable_vouchers: tax_deducted = get_deducted_tax(taxable_vouchers, tax_details) + # If advance is outside the current tax withholding period (usually a fiscal year), `get_deducted_tax` won't fetch it. + # updating `tax_deducted` with correct advance tax value (from current and previous previous withholding periods), will allow the + # rest of the below logic to function properly + # ---FY 2023-------------||---------------------FY 2024-----------------------||-- + # ---Advance-------------||---------Inv_1--------Inv_2------------------------||-- + if tax_deducted_on_advances: + tax_deducted += get_advance_tax_across_fiscal_year(tax_deducted_on_advances, tax_details) + tax_amount = 0 if party_type == "Supplier": @@ -389,7 +397,7 @@ def get_taxes_deducted_on_advances_allocated(inv, tax_details): frappe.qb.from_(at) .inner_join(pe) .on(pe.name == at.parent) - .select(at.parent, at.name, at.tax_amount, at.allocated_amount) + .select(pe.posting_date, at.parent, at.name, at.tax_amount, at.allocated_amount) .where(pe.tax_withholding_category == tax_details.get("tax_withholding_category")) .where(at.parent.isin(advances)) .where(at.account_head == tax_details.account_head) @@ -414,6 +422,16 @@ def get_deducted_tax(taxable_vouchers, tax_details): return sum(entries) +def get_advance_tax_across_fiscal_year(tax_deducted_on_advances, tax_details): + """ + Only applies for Taxes deducted on Advance Payments + """ + advance_tax_from_across_fiscal_year = sum( + [adv.tax_amount for adv in tax_deducted_on_advances if adv.posting_date < tax_details.from_date] + ) + return advance_tax_from_across_fiscal_year + + def get_tds_amount(ldc, parties, inv, tax_details, vouchers): tds_amount = 0 invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1} From 4d56c46446e6cbd0f979b3452e823e76f2abf148 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 29 Apr 2024 10:25:07 +0530 Subject: [PATCH 13/30] test: TDS deduction across fiscal year (cherry picked from commit 2f9a144023433e8b2a0d5f4be4c98c97ab6e2cab) --- .../test_tax_withholding_category.py | 140 +++++++++++++++++- 1 file changed, 134 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index a5509619583d..19e6f4d31884 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -1,19 +1,22 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt +import datetime import unittest import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_fields -from frappe.tests.utils import change_settings -from frappe.utils import today +from frappe.tests.utils import FrappeTestCase, change_settings +from frappe.utils import add_days, today +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.utils import get_fiscal_year +from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice test_dependencies = ["Supplier Group", "Customer Group"] -class TestTaxWithholdingCategory(unittest.TestCase): +class TestTaxWithholdingCategory(FrappeTestCase): @classmethod def setUpClass(self): # create relevant supplier, etc @@ -22,7 +25,7 @@ def setUpClass(self): make_pan_no_field() def tearDown(self): - cancel_invoices() + frappe.db.rollback() def test_cumulative_threshold_tds(self): frappe.db.set_value( @@ -322,8 +325,6 @@ def test_tds_calculation_on_net_total_partial_tds(self): d.cancel() def test_tds_deduction_for_po_via_payment_entry(self): - from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - frappe.db.set_value( "Supplier", "Test TDS Supplier8", "tax_withholding_category", "Cumulative Threshold TDS" ) @@ -490,6 +491,133 @@ def test_lower_deduction_certificate_application(self): pi2.cancel() pi3.cancel() + def set_previous_fy_and_tax_category(self): + test_company = "_Test Company" + category = "Cumulative Threshold TDS" + + def add_company_to_fy(fy, company): + if not [x.company for x in fy.companies if x.company == company]: + fy.append("companies", {"company": company}) + fy.save() + + # setup previous fiscal year + fiscal_year = get_fiscal_year(today(), company=test_company) + if prev_fiscal_year := get_fiscal_year(add_days(fiscal_year[1], -10)): + self.prev_fy = frappe.get_doc("Fiscal Year", prev_fiscal_year[0]) + add_company_to_fy(self.prev_fy, test_company) + else: + # make previous fiscal year + start = datetime.date(fiscal_year[1].year - 1, fiscal_year[1].month, fiscal_year[1].day) + end = datetime.date(fiscal_year[2].year - 1, fiscal_year[2].month, fiscal_year[2].day) + self.prev_fy = frappe.get_doc( + { + "doctype": "Fiscal Year", + "year_start_date": start, + "year_end_date": end, + "companies": [{"company": test_company}], + } + ) + self.prev_fy.save() + + # setup tax withholding category for previous fiscal year + cat = frappe.get_doc("Tax Withholding Category", category) + cat.append( + "rates", + { + "from_date": self.prev_fy.year_start_date, + "to_date": self.prev_fy.year_end_date, + "tax_withholding_rate": 10, + "single_threshold": 0, + "cumulative_threshold": 30000, + }, + ) + cat.save() + + def test_tds_across_fiscal_year(self): + """ + Advance TDS on previous fiscal year should be properly allocated on Invoices in upcoming fiscal year + --||-----FY 2023-----||-----FY 2024-----||-- + --||-----Advance-----||---Inv1---Inv2---||-- + """ + self.set_previous_fy_and_tax_category() + supplier = "Test TDS Supplier" + # Cumulative threshold 30000 and tax rate 10% + category = "Cumulative Threshold TDS" + frappe.db.set_value( + "Supplier", + supplier, + { + "tax_withholding_category": category, + "pan": "ABCTY1234D", + }, + ) + po_and_advance_posting_date = add_days(self.prev_fy.year_end_date, -10) + po = create_purchase_order(supplier=supplier, qty=10, rate=10000) + po.transaction_date = po_and_advance_posting_date + po.taxes = [] + po.apply_tds = False + po.tax_withholding_category = None + po.save().submit() + + # Partial advance + payment = get_payment_entry(po.doctype, po.name) + payment.posting_date = po_and_advance_posting_date + payment.paid_amount = 60000 + payment.apply_tax_withholding_amount = 1 + payment.tax_withholding_category = category + payment.references = [] + payment.taxes = [] + payment.save().submit() + + self.assertEqual(len(payment.taxes), 1) + self.assertEqual(payment.taxes[0].tax_amount, 6000) + + # Multiple partial invoices + payment.reload() + pi1 = make_purchase_invoice(source_name=po.name) + pi1.apply_tds = True + pi1.tax_withholding_category = category + pi1.items[0].qty = 3 + pi1.items[0].rate = 10000 + advances = pi1.get_advance_entries() + pi1.append( + "advances", + { + "reference_type": advances[0].reference_type, + "reference_name": advances[0].reference_name, + "advance_amount": advances[0].amount, + "allocated_amount": 30000, + }, + ) + pi1.save().submit() + pi1.reload() + payment.reload() + self.assertEqual(pi1.taxes, []) + self.assertEqual(payment.taxes[0].tax_amount, 6000) + self.assertEqual(payment.taxes[0].allocated_amount, 3000) + + pi2 = make_purchase_invoice(source_name=po.name) + pi2.apply_tds = True + pi2.tax_withholding_category = category + pi2.items[0].qty = 3 + pi2.items[0].rate = 10000 + advances = pi2.get_advance_entries() + pi2.append( + "advances", + { + "reference_type": advances[0].reference_type, + "reference_name": advances[0].reference_name, + "advance_amount": advances[0].amount, + "allocated_amount": 30000, + }, + ) + pi2.save().submit() + pi2.reload() + payment.reload() + self.assertEqual(pi2.taxes, []) + self.assertEqual(payment.taxes[0].tax_amount, 6000) + self.assertEqual(payment.taxes[0].allocated_amount, 6000) + def cancel_invoices(): purchase_invoices = frappe.get_all( From 58b68b75971ec9a2a4460bdf0ad67020ff9554f3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 29 Apr 2024 19:31:21 +0530 Subject: [PATCH 14/30] fix: display term name for single term invoices (cherry picked from commit 5fa4cfee041b333bf112f9a435d9f3298399f693) --- .../report/accounts_receivable/accounts_receivable.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index dcfd85afddb3..25143e555d36 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -501,8 +501,9 @@ def get_payment_terms(self, row): # Deduct that from paid amount pre allocation row.paid -= flt(payment_terms_details[0].total_advance) - # If no or single payment terms, no need to split the row - if len(payment_terms_details) <= 1: + # If single payment terms, no need to split the row + if len(payment_terms_details) == 1 and payment_terms_details[0].payment_term: + self.append_payment_term(row, payment_terms_details[0], original_row) return for d in payment_terms_details: From 45c4167c867304dc59e4790368e5d02dad089d6c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Apr 2024 12:24:14 +0530 Subject: [PATCH 15/30] fix: permission issue when user permission restricts on company --- .../tax_withholding_account/tax_withholding_account.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.json b/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.json index 06d6b088e5b7..52a4f4c0eedb 100644 --- a/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.json +++ b/erpnext/accounts/doctype/tax_withholding_account/tax_withholding_account.json @@ -21,7 +21,7 @@ "fieldname": "company", "fieldtype": "Link", "hidden": 0, - "ignore_user_permissions": 0, + "ignore_user_permissions": 1, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, @@ -53,7 +53,7 @@ "fieldname": "account", "fieldtype": "Link", "hidden": 0, - "ignore_user_permissions": 0, + "ignore_user_permissions": 1, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, @@ -87,7 +87,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-04-13 18:44:25.055382", + "modified": "2024-04-30 10:26:48.21829", "modified_by": "Administrator", "module": "Accounts", "name": "Tax Withholding Account", From 831828686587e42881ba0f9131a7c605569c1ba0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Apr 2024 17:06:48 +0530 Subject: [PATCH 16/30] fix: validation to prevent overallocation (cherry picked from commit bf755fab550134fccc344dd220143bf0a4fb4a8c) --- erpnext/accounts/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d966074b2ae0..8fc47b0b6796 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -479,6 +479,11 @@ def reconcile_against_document( # re-submit advance entry doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) gl_map = doc.build_gl_map() + from erpnext.accounts.general_ledger import process_debit_credit_difference + + # Make sure there is no overallocation + process_debit_credit_difference(gl_map) + create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) # Only update outstanding for newly linked vouchers From 9aa054c4004731c4f1731db93d1ce016d87d7e77 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 2 May 2024 09:38:33 +0530 Subject: [PATCH 17/30] fix: negative stock qty error for stock reconciliation (#41283) fix: negative stock qty error for stock reco --- erpnext/stock/stock_ledger.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 99930768ea53..4e624c9acb09 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1490,6 +1490,10 @@ def get_stock_reco_qty_shift(args): stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance) else: stock_reco_qty_shift = flt(args.actual_qty) + + elif args.get("serial_no") or args.get("batch_no"): + stock_reco_qty_shift = flt(args.actual_qty) + else: # reco is being submitted last_balance = get_previous_sle_of_current_voucher(args, "<=", exclude_current_voucher=True).get( @@ -1559,6 +1563,15 @@ def get_datetime_limit_condition(detail): def validate_negative_qty_in_future_sle(args, allow_negative_stock=False): if allow_negative_stock or is_negative_stock_allowed(item_code=args.item_code): return + + if ( + args.voucher_type == "Stock Reconciliation" + and args.actual_qty < 0 + and args.get("batch_no") + and frappe.db.get_value("Stock Reconciliation Item", args.voucher_detail_no, "qty") > 0 + ): + return + if not (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation"): return From 5ae29655f9e9799c2e9f4a57877db1481823630c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 2 May 2024 04:31:43 +0000 Subject: [PATCH 18/30] chore(release): Bumped to Version 14.69.0 # [14.69.0](https://github.com/frappe/erpnext/compare/v14.68.2...v14.69.0) (2024-05-02) ### Bug Fixes * 'NoneType' object has no attribute '_read_rowdata_packet_unbuffered' ([949aa93](https://github.com/frappe/erpnext/commit/949aa9346c4d28b13dc43a2c31b573572fdddfea)) * args when get the delivery note in delivery trip ([abe64aa](https://github.com/frappe/erpnext/commit/abe64aa1aba81ac4147b835b45cd8ae6f38dcd96)) * args when get the delivery note in delivery trip ([1a7b3c4](https://github.com/frappe/erpnext/commit/1a7b3c437dcd18da3425696af597528dc2e9d751)) * display term name for single term invoices ([58b68b7](https://github.com/frappe/erpnext/commit/58b68b75971ec9a2a4460bdf0ad67020ff9554f3)) * duplicate column in the stock ledger report ([3fcdcef](https://github.com/frappe/erpnext/commit/3fcdcef1786fac193cf1f6065e927bfe22d2747a)) * handle stock balance unbuffered_cursor error (backport [#41186](https://github.com/frappe/erpnext/issues/41186)) ([#41187](https://github.com/frappe/erpnext/issues/41187)) ([59010c9](https://github.com/frappe/erpnext/commit/59010c9a617a6e91b3b90b1970c18c7a226b5238)) * incorrectly applying TDS when Advance is in previous FY ([eb22fb9](https://github.com/frappe/erpnext/commit/eb22fb9326c93cd7e6d990f2fb85e05f698983df)) * mode of payment has precedance ([33d38ba](https://github.com/frappe/erpnext/commit/33d38ba3a71f00a62688d31722f3dc934f95dc6f)) * multiple pricing rules with discount amount and discount percentage not working ([#41211](https://github.com/frappe/erpnext/issues/41211)) ([54313b5](https://github.com/frappe/erpnext/commit/54313b5db902c108458392b6d389d16e9213b55a)) * negative stock qty error for stock reconciliation ([#41283](https://github.com/frappe/erpnext/issues/41283)) ([9aa054c](https://github.com/frappe/erpnext/commit/9aa054c4004731c4f1731db93d1ce016d87d7e77)) * permission issue when user permission restricts on company ([45c4167](https://github.com/frappe/erpnext/commit/45c4167c867304dc59e4790368e5d02dad089d6c)) * validation to prevent overallocation ([8318286](https://github.com/frappe/erpnext/commit/831828686587e42881ba0f9131a7c605569c1ba0)) ### Features * allow to do reposting for all stock transactions (audit) (backport [#41165](https://github.com/frappe/erpnext/issues/41165)) ([#41205](https://github.com/frappe/erpnext/issues/41205)) ([1e13193](https://github.com/frappe/erpnext/commit/1e1319351dfad56e586ebf8be58e5db7a35de82b)) ### Performance Improvements * timeout issue while submitting purchase receipt (v14) ([113351e](https://github.com/frappe/erpnext/commit/113351e8507075196cb37350bb34fa9fb5fb2484)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index cca85cd53ef3..2e7dab7a3836 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import frappe -__version__ = "14.68.2" +__version__ = "14.69.0" def get_default_company(user=None): From d727c52421c8a6da97f9688abe1cece3e330a441 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 17:53:52 +0530 Subject: [PATCH 19/30] =?UTF-8?q?fix:=20added=20brand=20column=20in=20Ware?= =?UTF-8?q?house=20wise=20Item=20Balance=20Age=20and=20Value=20=E2=80=A6?= =?UTF-8?q?=20(backport=20#41280)=20(#41281)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: added brand column in Warehouse wise Item Balance Age and Value … (#41280) fix: added brand coulmn in Warehouse wise Item Balance Age and Value report (cherry picked from commit 1cbc200770d2955f9d638c0047e0e58a514a5d23) Co-authored-by: rohitwaghchaure --- .../warehouse_wise_item_balance_age_and_value.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index e1cce31329ec..f5a059a7f61a 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -56,13 +56,14 @@ def execute(filters=None): item_value.setdefault((item, item_map[item]["item_group"]), []) item_value[(item, item_map[item]["item_group"])].append(total_stock_value) + itemwise_brand = frappe._dict(get_itemwise_brand(items)) # sum bal_qty by item for (item, item_group), wh_balance in item_balance.items(): if not item_ageing.get(item): continue total_stock_value = sum(item_value[(item, item_group)]) - row = [item, item_map[item]["item_name"], item_group, total_stock_value] + row = [item, item_map[item]["item_name"], item_group, itemwise_brand.get(item), total_stock_value] fifo_queue = item_ageing[item]["fifo_queue"] average_age = 0.00 @@ -85,6 +86,10 @@ def execute(filters=None): return columns, data +def get_itemwise_brand(items): + return frappe.get_all("Item", filters={"name": ("in", items)}, fields=["name", "brand"], as_list=1) + + def get_columns(filters): """return columns""" @@ -92,6 +97,7 @@ def get_columns(filters): _("Item") + ":Link/Item:150", _("Item Name") + ":Link/Item:150", _("Item Group") + "::120", + _("Brand") + ":Link/Brand:120", _("Value") + ":Currency:120", _("Age") + ":Float:120", ] From e82ea12cbc19f52491b4fdc3922abd829ab07f40 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 3 May 2024 18:34:27 +0530 Subject: [PATCH 20/30] fix: Cost center not getting saved in PSOA (cherry picked from commit 58f70396307846ac014398f3016cc94c0318d656) # Conflicts: # erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json # erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py --- .../psoa_cost_center/psoa_cost_center.json | 8 +++++++- .../psoa_cost_center/psoa_cost_center.py | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json index e292b60d68d6..9a19ff9ad90a 100644 --- a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json +++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json @@ -11,13 +11,19 @@ { "fieldname": "cost_center_name", "fieldtype": "Link", + "in_list_view": 1, "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "reqd": 1 } ], "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2020-08-03 16:56:45.744905", +======= + "modified": "2024-05-03 17:16:51.666461", +>>>>>>> 58f7039630 (fix: Cost center not getting saved in PSOA) "modified_by": "Administrator", "module": "Accounts", "name": "PSOA Cost Center", diff --git a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py index 877998a187c9..275d57a336f5 100644 --- a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py +++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py @@ -7,4 +7,21 @@ class PSOACostCenter(Document): +<<<<<<< HEAD +======= + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + cost_center_name: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + +>>>>>>> 58f7039630 (fix: Cost center not getting saved in PSOA) pass From 6aa8d5fb4b1bb44350dc72f04f48f55950941d2b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 6 May 2024 11:29:53 +0530 Subject: [PATCH 21/30] chore: resolve conflicts --- .../accounts/doctype/psoa_cost_center/psoa_cost_center.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json index 9a19ff9ad90a..ef4a55861fb2 100644 --- a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json +++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json @@ -19,11 +19,7 @@ ], "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2020-08-03 16:56:45.744905", -======= "modified": "2024-05-03 17:16:51.666461", ->>>>>>> 58f7039630 (fix: Cost center not getting saved in PSOA) "modified_by": "Administrator", "module": "Accounts", "name": "PSOA Cost Center", @@ -33,4 +29,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} From 7b9c22775c08740d50680a091be226aee92d2a69 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 6 May 2024 11:52:26 +0530 Subject: [PATCH 22/30] chore: resolve conflicts --- .../psoa_cost_center/psoa_cost_center.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py index 275d57a336f5..877998a187c9 100644 --- a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py +++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py @@ -7,21 +7,4 @@ class PSOACostCenter(Document): -<<<<<<< HEAD -======= - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - cost_center_name: DF.Link - parent: DF.Data - parentfield: DF.Data - parenttype: DF.Data - # end: auto-generated types - ->>>>>>> 58f7039630 (fix: Cost center not getting saved in PSOA) pass From 93b30d9f1176ee4bf45906c73ea2cb266621dc65 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 6 May 2024 12:07:33 +0530 Subject: [PATCH 23/30] fix: incorrect query for Purchase Invoice rate in GP (cherry picked from commit bd8382c59259a9115fdd2c04ccfc297c89946d5d) --- erpnext/accounts/report/gross_profit/gross_profit.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index f4d4772901ca..5d656bb0a793 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -717,20 +717,22 @@ def get_last_purchase_rate(self, item_code, row): frappe.qb.from_(purchase_invoice_item) .inner_join(purchase_invoice) .on(purchase_invoice.name == purchase_invoice_item.parent) - .select(purchase_invoice_item.base_rate / purchase_invoice_item.conversion_factor) + .select( + purchase_invoice.name, + purchase_invoice_item.base_rate / purchase_invoice_item.conversion_factor, + ) .where(purchase_invoice.docstatus == 1) .where(purchase_invoice.posting_date <= self.filters.to_date) .where(purchase_invoice_item.item_code == item_code) ) if row.project: - query.where(purchase_invoice_item.project == row.project) + query = query.where(purchase_invoice_item.project == row.project) if row.cost_center: - query.where(purchase_invoice_item.cost_center == row.cost_center) + query = query.where(purchase_invoice_item.cost_center == row.cost_center) - query.orderby(purchase_invoice.posting_date, order=frappe.qb.desc) - query.limit(1) + query = query.orderby(purchase_invoice.posting_date, order=frappe.qb.desc).limit(1) last_purchase_rate = query.run() return flt(last_purchase_rate[0][0]) if last_purchase_rate else 0 From cd33199da29b252a1fe54b5465fa19bf539cba78 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 May 2024 21:37:27 +0530 Subject: [PATCH 24/30] fix: reset rate for serial batch supplied items (#41293) --- .../subcontracting_receipt.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index c0235f9b85c5..aeae7c84679a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -8,6 +8,7 @@ import erpnext from erpnext.accounts.utils import get_account_currency from erpnext.controllers.subcontracting_controller import SubcontractingController +from erpnext.stock.utils import get_incoming_rate class SubcontractingReceipt(SubcontractingController): @@ -67,6 +68,7 @@ def before_validate(self): self.set_items_bom() self.set_items_cost_center() self.set_items_expense_account() + self.reset_rate_for_serial_batch_supplied_items() def validate(self): if ( @@ -124,6 +126,26 @@ def set_missing_values(self): self.calculate_supplied_items_qty_and_amount() self.calculate_items_qty_and_amount() + def reset_rate_for_serial_batch_supplied_items(self): + for item in self.supplied_items: + if item.serial_no or item.batch_no: + args = frappe._dict( + { + "item_code": item.rm_item_code, + "warehouse": self.supplier_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": flt(item.consumed_qty), + "serial_no": item.serial_no, + "batch_no": item.batch_no, + "voucher_type": self.doctype, + "voucher_no": self.name, + "company": self.company, + "allow_zero_valuation": 1, + } + ) + item.rate = get_incoming_rate(args) + def has_serial_batch_items(self): if not self.get("supplied_items"): return False From a26ae64385d65e1c20151c30cae2c6fd66a8699a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 21:46:18 +0530 Subject: [PATCH 25/30] fix: missing Item Name on Save for Quotation created from Item (backport #41233) (#41303) fix: missing Item Name on Save for Quotation created from Item (#41233) * fix: missing Item Name on Save for Quotation created from Item * fix: missing Item Name on Save for Quotation created from Item (cherry picked from commit c8e92cb1b2a5f9c0a116e2deb53eea440577cdb6) Co-authored-by: HENRY Florian --- erpnext/stock/doctype/item/item.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 29d41fe76251..a7972790b1af 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -15,6 +15,9 @@ frappe.ui.form.on("Item", { frm.add_fetch("tax_type", "tax_rate", "tax_rate"); frm.make_methods = { + Quotation: () => { + open_form(frm, "Quotation", "Quotation Item", "items"); + }, "Sales Order": () => { open_form(frm, "Sales Order", "Sales Order Item", "items"); }, From d2ce9278915f86fa4f553de3ff6de1163f09b8c3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 May 2024 09:37:24 +0530 Subject: [PATCH 26/30] fix: pricing rule rounding Consider a pricing rule of 20:1 with recursion enabled, free items should follow the below progression | Qty | Free item qty | |-------+---------------| | 0-19 | 0 | | 20-39 | 1 | | 40-59 | 2 | (cherry picked from commit 9bf37426c13d6a92db46a49d58b4d36faef048f2) --- erpnext/accounts/doctype/pricing_rule/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index ce96a3d12406..2a78bebd103d 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -6,6 +6,7 @@ import copy import json +import math import frappe from frappe import _, bold @@ -638,7 +639,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): if transaction_qty: qty = flt(transaction_qty) * qty / pricing_rule.recurse_for if pricing_rule.round_free_qty: - qty = round(qty) + qty = math.floor(qty) free_item_data_args = { "item_code": free_item, From e068bec212c03fd05966f6ca7a9afbaab349d785 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 May 2024 09:44:10 +0530 Subject: [PATCH 27/30] refactor(test): test floor based rounding (cherry picked from commit c41a037174c6f6136086de8b3c822ffff2bb9f60) --- erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py | 2 +- 1 file changed, 1 insertion(+), 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 cb0c223bffad..18658e6e4a66 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -976,7 +976,7 @@ def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item") - self.assertEqual(so.items[1].qty, 4) + self.assertEqual(so.items[1].qty, 3) def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") From 339256bc71edae761d11faf771d75db987446de5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 00:15:33 +0530 Subject: [PATCH 28/30] fix: filter validation for batch-wise balance history report (backport #41356) (#41360) fix: filter validation for batch-wise balance history report (#41356) fix: filter validation for batchwise balance history report (cherry picked from commit 544fc60093cfdc1a92c4ccced7ab5794626fd87c) Co-authored-by: rohitwaghchaure --- .../batch_wise_balance_history.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 8e27b8c4f69c..7c23e4c8f1e6 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -29,8 +29,15 @@ def execute(filters=None): sle_count = _estimate_table_row_count("Stock Ledger Entry") - if sle_count > SLE_COUNT_LIMIT and not filters.get("item_code") and not filters.get("warehouse"): - frappe.throw(_("Please select either the Item or Warehouse filter to generate the report.")) + if ( + sle_count > SLE_COUNT_LIMIT + and not filters.get("item_code") + and not filters.get("warehouse") + and not filters.get("warehouse_type") + ): + frappe.throw( + _("Please select either the Item or Warehouse or Warehouse Type filter to generate the report.") + ) if filters.from_date > filters.to_date: frappe.throw(_("From Date must be before To Date")) From 4647ec88929a29303aa3933165a54e555fc8043b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 11:38:02 +0530 Subject: [PATCH 29/30] perf: index on item code for the Pick List Item doctype (backport #41357) (#41362) * perf: index on item code for the Pick List Item doctype (#41357) (cherry picked from commit 0887161f2afcb76d88edb8d5c568b5c766568f71) # Conflicts: # erpnext/stock/doctype/pick_list_item/pick_list_item.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/pick_list_item/pick_list_item.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json index 883cdd19e7c7..bad79a02907b 100644 --- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -126,7 +126,8 @@ "in_list_view": 1, "label": "Item", "options": "Item", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "quantity_section", @@ -193,7 +194,7 @@ ], "istable": 1, "links": [], - "modified": "2023-07-25 11:56:23.361867", + "modified": "2024-05-07 15:32:42.905446", "modified_by": "Administrator", "module": "Stock", "name": "Pick List Item", @@ -204,4 +205,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From 9d5e4b3b3aa05ccb74462e5392755e017c75c063 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 9 May 2024 05:30:06 +0000 Subject: [PATCH 30/30] chore(release): Bumped to Version 14.69.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [14.69.1](https://github.com/frappe/erpnext/compare/v14.69.0...v14.69.1) (2024-05-09) ### Bug Fixes * added brand column in Warehouse wise Item Balance Age and Value … (backport [#41280](https://github.com/frappe/erpnext/issues/41280)) ([#41281](https://github.com/frappe/erpnext/issues/41281)) ([d727c52](https://github.com/frappe/erpnext/commit/d727c52421c8a6da97f9688abe1cece3e330a441)) * Cost center not getting saved in PSOA ([e82ea12](https://github.com/frappe/erpnext/commit/e82ea12cbc19f52491b4fdc3922abd829ab07f40)) * filter validation for batch-wise balance history report (backport [#41356](https://github.com/frappe/erpnext/issues/41356)) ([#41360](https://github.com/frappe/erpnext/issues/41360)) ([339256b](https://github.com/frappe/erpnext/commit/339256bc71edae761d11faf771d75db987446de5)) * incorrect query for Purchase Invoice rate in GP ([93b30d9](https://github.com/frappe/erpnext/commit/93b30d9f1176ee4bf45906c73ea2cb266621dc65)) * missing Item Name on Save for Quotation created from Item (backport [#41233](https://github.com/frappe/erpnext/issues/41233)) ([#41303](https://github.com/frappe/erpnext/issues/41303)) ([a26ae64](https://github.com/frappe/erpnext/commit/a26ae64385d65e1c20151c30cae2c6fd66a8699a)) * pricing rule rounding ([d2ce927](https://github.com/frappe/erpnext/commit/d2ce9278915f86fa4f553de3ff6de1163f09b8c3)) * reset rate for serial batch supplied items ([#41293](https://github.com/frappe/erpnext/issues/41293)) ([cd33199](https://github.com/frappe/erpnext/commit/cd33199da29b252a1fe54b5465fa19bf539cba78)) ### Performance Improvements * index on item code for the Pick List Item doctype (backport [#41357](https://github.com/frappe/erpnext/issues/41357)) ([#41362](https://github.com/frappe/erpnext/issues/41362)) ([4647ec8](https://github.com/frappe/erpnext/commit/4647ec88929a29303aa3933165a54e555fc8043b)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2e7dab7a3836..7d97964d7919 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import frappe -__version__ = "14.69.0" +__version__ = "14.69.1" def get_default_company(user=None):