From 3d7e87185c80ada44b6026ead663bf7ba86bba7b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 21 Feb 2024 05:29:45 +0000 Subject: [PATCH 01/19] chore(release): Bumped to Version 15.14.4 ## [15.14.4](https://github.com/frappe/erpnext/compare/v15.14.3...v15.14.4) (2024-02-21) ### Bug Fixes * 'NoneType' object is not iterable (backport [#39977](https://github.com/frappe/erpnext/issues/39977)) ([#39981](https://github.com/frappe/erpnext/issues/39981)) ([0f87ec1](https://github.com/frappe/erpnext/commit/0f87ec15adb5ce76ada846c4746b4fa3f6451b48)) * **Bank Transaction:** precision for `(un)allocated_amount` ([bf5d2f5](https://github.com/frappe/erpnext/commit/bf5d2f5fe4779d382dd343dbb250e8353d228285)) * batch filter not working in stock ledger report (backport [#39934](https://github.com/frappe/erpnext/issues/39934)) ([#39935](https://github.com/frappe/erpnext/issues/39935)) ([1513595](https://github.com/frappe/erpnext/commit/15135952fcc259469b2c90cbc63aad20444a993b)) * do not empty serial batch fields (backport [#39948](https://github.com/frappe/erpnext/issues/39948)) ([#39956](https://github.com/frappe/erpnext/issues/39956)) ([acd2e93](https://github.com/frappe/erpnext/commit/acd2e93f8cd4873f6e6f88b620f3308801e8f31a)) * fetch company terms ([14fe0af](https://github.com/frappe/erpnext/commit/14fe0af88752ca34e5dda9d260b3dd8d9d47634f)) * float division by zero ([b954bdf](https://github.com/frappe/erpnext/commit/b954bdfdf92c43c2f71bb4ca2f0efd0e377a7b9f)) * group node in warehouse filter in Item-wise Sales Register ([74819b8](https://github.com/frappe/erpnext/commit/74819b8e70bd712d04f960aee71e1cfee15d76af)) * **Issue:** create communication ([f5d7fbd](https://github.com/frappe/erpnext/commit/f5d7fbdaf8cadbd886368853526cd04c4f1425b2)) * no need call for company method in sales invoice js ([bef38f7](https://github.com/frappe/erpnext/commit/bef38f74fee5650c5805a327d1bb8e59deaa48af)) * not able to make purchase receipt ([3732946](https://github.com/frappe/erpnext/commit/37329469c3432acc5277171deddd5d40d9c72b96)) * party item code in Blanket Order ([518b22b](https://github.com/frappe/erpnext/commit/518b22bffcfdd803ae5c3dd5f91af90b5bbb3a8a)) * reposting failed status not updated (backport [#39970](https://github.com/frappe/erpnext/issues/39970)) ([#39972](https://github.com/frappe/erpnext/issues/39972)) ([46f7569](https://github.com/frappe/erpnext/commit/46f7569a5417966a368ab59d1f6b4df9fb9c6136)) * set batch created from bundle to batch field in stock transaction (backport [#39966](https://github.com/frappe/erpnext/issues/39966)) ([#39987](https://github.com/frappe/erpnext/issues/39987)) ([2ee51d3](https://github.com/frappe/erpnext/commit/2ee51d36ffd8b77e9fe460bb758ef302f1132c2c)) * show active bom in the dropdown while making stock entry and MR (backport [#39974](https://github.com/frappe/erpnext/issues/39974)) ([#39976](https://github.com/frappe/erpnext/issues/39976)) ([7201448](https://github.com/frappe/erpnext/commit/720144898f6d5a80458e7aa695beda33384782ac)) * update_dimension is required and not need party account method ([a56d5b8](https://github.com/frappe/erpnext/commit/a56d5b805c086b562f47f1ad4ec8c83153564b7e)) * use serial batch fields not enabled for new stock entry ([40d4e32](https://github.com/frappe/erpnext/commit/40d4e3261efd3eb8f98b05b2b7d35b08e1a50c18)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0eb42d222e2..48f2c15f8fb 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.14.3" +__version__ = "15.14.4" def get_default_company(user=None): From 5084b9bd4bb27d907edf5a70ad8d9dcd4913e3eb Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 21 Feb 2024 16:39:28 +0530 Subject: [PATCH 02/19] fix: check for pricing rules on item (cherry picked from commit ecd83b12abb19e0e41a50e914a89b320e1f577eb) (cherry picked from commit 32d96423795c99ed2cc46cf1cc04fbf636dac5c7) --- erpnext/public/js/controllers/transaction.js | 48 ++++++++++---------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index c0298a4f14e..1533c7055cf 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1509,31 +1509,33 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } remove_pricing_rule_for_item(item) { - let me = this; - return this.frm.call({ - method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.remove_pricing_rule_for_item", - args: { - pricing_rules: item.pricing_rules, - item_details: { - "doctype": item.doctype, - "name": item.name, - "item_code": item.item_code, - "pricing_rules": item.pricing_rules, - "parenttype": item.parenttype, - "parent": item.parent, - "price_list_rate": item.price_list_rate + if (item.pricing_rules){ + let me = this; + return this.frm.call({ + method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.remove_pricing_rule_for_item", + args: { + pricing_rules: item.pricing_rules, + item_details: { + "doctype": item.doctype, + "name": item.name, + "item_code": item.item_code, + "pricing_rules": item.pricing_rules, + "parenttype": item.parenttype, + "parent": item.parent, + "price_list_rate": item.price_list_rate + }, + item_code: item.item_code, + rate: item.price_list_rate, }, - item_code: item.item_code, - rate: item.price_list_rate, - }, - callback: function(r) { - if (!r.exc && r.message) { - me.remove_pricing_rule(r.message); - me.calculate_taxes_and_totals(); - if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on"); + callback: function(r) { + if (!r.exc && r.message) { + me.remove_pricing_rule(r.message); + me.calculate_taxes_and_totals(); + if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on"); + } } - } - }); + }); + } } apply_pricing_rule(item, calculate_taxes_and_totals) { From afc87a4486d0a2e232ec0c5052d33ea2e92e1fcd Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 22 Feb 2024 05:38:47 +0000 Subject: [PATCH 03/19] chore(release): Bumped to Version 15.14.5 ## [15.14.5](https://github.com/frappe/erpnext/compare/v15.14.4...v15.14.5) (2024-02-22) ### Bug Fixes * check for pricing rules on item ([5084b9b](https://github.com/frappe/erpnext/commit/5084b9bd4bb27d907edf5a70ad8d9dcd4913e3eb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 48f2c15f8fb..19fc07d3e23 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.14.4" +__version__ = "15.14.5" def get_default_company(user=None): From 6fc4dac4db0e9c6cbbb90f5848a35e98bbe4f422 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 18:10:34 +0530 Subject: [PATCH 04/19] fix: Issues regarding asset cancellation and deletion (cherry picked from commit 17f85de6fb6fbcd26993768b9fd29822189626d2) # Conflicts: # erpnext/assets/doctype/asset/depreciation.py # erpnext/assets/doctype/asset_capitalization/asset_capitalization.py --- erpnext/assets/doctype/asset/depreciation.py | 8 +++-- .../asset_capitalization.js | 2 +- .../asset_capitalization.py | 5 +++ .../asset_depreciation_schedule.py | 33 +++++++++---------- erpnext/controllers/buying_controller.py | 3 +- erpnext/patches.txt | 2 ++ ...te_orphaned_asset_movement_item_records.py | 10 ++++++ 7 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index dbb18b543f1..7422e56112c 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -561,13 +561,17 @@ def modify_depreciation_schedule_for_asset_repairs(asset, notes): def reverse_depreciation_entry_made_after_disposal(asset, date): for row in asset.get("finance_books"): asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book) +<<<<<<< HEAD +======= + if not asset_depr_schedule_doc or not asset_depr_schedule_doc.get("depreciation_schedule"): + continue +>>>>>>> 17f85de6fb (fix: Issues regarding asset cancellation and deletion) for schedule_idx, schedule in enumerate(asset_depr_schedule_doc.get("depreciation_schedule")): - if schedule.schedule_date == date: + if schedule.schedule_date == date and schedule.journal_entry: if not disposal_was_made_on_original_schedule_date( schedule_idx, row, date ) or disposal_happens_in_the_future(date): - reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 2f0de979392..110f2c4f2a1 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -6,7 +6,7 @@ frappe.provide("erpnext.assets"); erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController { setup() { - this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle']; + this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle', 'Asset Movement']; this.setup_posting_date_time_check(); } diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 66014904cc4..ca1d616be37 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -138,6 +138,7 @@ class AssetCapitalization(StockController): "Repost Item Valuation", "Serial and Batch Bundle", "Asset", + "Asset Movement", ) self.cancel_target_asset() self.update_stock_ledger() @@ -147,6 +148,10 @@ class AssetCapitalization(StockController): def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: asset_doc = frappe.get_doc("Asset", self.target_asset) +<<<<<<< HEAD +======= + asset_doc.db_set("capitalized_in", None) +>>>>>>> 17f85de6fb (fix: Issues regarding asset cancellation and deletion) if asset_doc.docstatus == 1: asset_doc.cancel() diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 146c03e8c3f..77469df8955 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -418,14 +418,13 @@ class AssetDepreciationSchedule(Document): ) # Adjust depreciation amount in the last period based on the expected value after useful life - if row.expected_value_after_useful_life and ( - ( - n == cint(final_number_of_depreciations) - 1 - and value_after_depreciation != row.expected_value_after_useful_life + if ( + n == cint(final_number_of_depreciations) - 1 + and flt(value_after_depreciation) != flt(row.expected_value_after_useful_life) + ) or flt(value_after_depreciation) < flt(row.expected_value_after_useful_life): + depreciation_amount += flt(value_after_depreciation) - flt( + row.expected_value_after_useful_life ) - or value_after_depreciation < row.expected_value_after_useful_life - ): - depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life skip_row = True if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0: @@ -813,15 +812,11 @@ def make_draft_asset_depr_schedules_if_not_present(asset_doc): asset_depr_schedules_names = [] for row in asset_doc.get("finance_books"): - draft_asset_depr_schedule_name = get_asset_depr_schedule_name( - asset_doc.name, "Draft", row.finance_book + asset_depr_schedule = get_asset_depr_schedule_name( + asset_doc.name, ["Draft", "Active"], row.finance_book ) - active_asset_depr_schedule_name = get_asset_depr_schedule_name( - asset_doc.name, "Active", row.finance_book - ) - - if not draft_asset_depr_schedule_name and not active_asset_depr_schedule_name: + if not asset_depr_schedule: name = make_draft_asset_depr_schedule(asset_doc, row) asset_depr_schedules_names.append(name) @@ -997,16 +992,20 @@ def get_asset_depr_schedule_doc(asset_name, status, finance_book=None): def get_asset_depr_schedule_name(asset_name, status, finance_book=None): - finance_book_filter = ["finance_book", "is", "not set"] - if finance_book: + if finance_book is None: + finance_book_filter = ["finance_book", "is", "not set"] + else: finance_book_filter = ["finance_book", "=", finance_book] + if isinstance(status, str): + status = [status] + return frappe.db.get_value( doctype="Asset Depreciation Schedule", filters=[ ["asset", "=", asset_name], finance_book_filter, - ["status", "=", status], + ["status", "in", status], ], ) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 27ac9d52f65..91ee53a796e 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -824,7 +824,8 @@ class BuyingController(SubcontractingController): if self.doctype == "Purchase Invoice" and not self.get("update_stock"): return - frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name) + asset_movement = frappe.db.get_value("Asset Movement", {"reference_name": self.name}, "name") + frappe.delete_doc("Asset Movement", asset_movement, force=1) def validate_schedule_date(self): if not self.get("items"): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4825e7648f3..56fdfcc0361 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -355,3 +355,5 @@ erpnext.patches.v14_0.update_total_asset_cost_field erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.patches.v14_0.set_maintain_stock_for_bom_item +erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment +erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records \ No newline at end of file diff --git a/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py b/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py new file mode 100644 index 00000000000..910504ed232 --- /dev/null +++ b/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py @@ -0,0 +1,10 @@ +import frappe + + +def execute(): + frappe.db.sql( + """ + DELETE FROM `tabAsset Movement Item` + WHERE parent NOT IN (SELECT name FROM `tabAsset Movement`) + """ + ) From 3c98368e4552eeff152526a3ce337b418190c4b7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 18:40:39 +0530 Subject: [PATCH 05/19] fix: removed unwanted patch (cherry picked from commit 85471533e9fa59b76d81613fad242e5365cbc9f7) --- erpnext/patches.txt | 1 - .../patches/v15_0/delete_orphaned_asset_movement_item_records.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 56fdfcc0361..b93b6cf7694 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -355,5 +355,4 @@ erpnext.patches.v14_0.update_total_asset_cost_field erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.patches.v14_0.set_maintain_stock_for_bom_item -erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records \ No newline at end of file diff --git a/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py b/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py index 910504ed232..a1d7dc9b3d4 100644 --- a/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py +++ b/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py @@ -2,6 +2,7 @@ import frappe def execute(): + # nosemgrep frappe.db.sql( """ DELETE FROM `tabAsset Movement Item` From 6662c321a5b32b4c165a8784d2c39f3a7fa59a2c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:40:46 +0530 Subject: [PATCH 06/19] fix: communication_date in party dashboards (backport #40005) (#40043) fix: accommodate for changed orderby statement (cherry picked from commit 87df7ff71772474bb858dac6ce9132280ce2ab82) Co-authored-by: Gursheen Anand --- erpnext/accounts/party.py | 41 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index a3fdf36cbd5..3aa2a04b633 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -9,7 +9,7 @@ from frappe import _, msgprint, qb, scrub from frappe.contacts.doctype.address.address import get_company_address, get_default_address from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values -from frappe.query_builder.functions import Abs, Date, Sum +from frappe.query_builder.functions import Abs, Count, Date, Sum from frappe.utils import ( add_days, add_months, @@ -784,34 +784,37 @@ def get_timeline_data(doctype, name): from frappe.desk.form.load import get_communication_data out = {} - fields = "creation, count(*)" after = add_years(None, -1).strftime("%Y-%m-%d") - group_by = "group by Date(creation)" data = get_communication_data( doctype, name, after=after, - group_by="group by creation", - fields="C.creation as creation, count(C.name)", + group_by="group by communication_date", + fields="C.communication_date as communication_date, count(C.name)", as_dict=False, ) # fetch and append data from Activity Log - data += frappe.db.sql( - """select {fields} - from `tabActivity Log` - where (reference_doctype=%(doctype)s and reference_name=%(name)s) - or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s) - or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s) - and status!='Success' and creation > {after} - {group_by} order by creation desc - """.format( - fields=fields, group_by=group_by, after=after - ), - {"doctype": doctype, "name": name}, - as_dict=False, - ) + activity_log = frappe.qb.DocType("Activity Log") + data += ( + frappe.qb.from_(activity_log) + .select(activity_log.communication_date, Count(activity_log.name)) + .where( + ( + ((activity_log.reference_doctype == doctype) & (activity_log.reference_name == name)) + | ((activity_log.timeline_doctype == doctype) & (activity_log.timeline_name == name)) + | ( + (activity_log.reference_doctype.isin(["Quotation", "Opportunity"])) + & (activity_log.timeline_name == name) + ) + ) + & (activity_log.status != "Success") + & (activity_log.creation > after) + ) + .groupby(activity_log.communication_date) + .orderby(activity_log.communication_date, order=frappe.qb.desc) + ).run() timeline_items = dict(data) From f723d7b5613418659f99c5ad961ecf43e926763e Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 22 Feb 2024 11:14:07 +0000 Subject: [PATCH 07/19] chore(release): Bumped to Version 15.14.6 ## [15.14.6](https://github.com/frappe/erpnext/compare/v15.14.5...v15.14.6) (2024-02-22) ### Bug Fixes * communication_date in party dashboards (backport [#40005](https://github.com/frappe/erpnext/issues/40005)) ([#40043](https://github.com/frappe/erpnext/issues/40043)) ([6662c32](https://github.com/frappe/erpnext/commit/6662c321a5b32b4c165a8784d2c39f3a7fa59a2c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 19fc07d3e23..8a66788a020 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.14.5" +__version__ = "15.14.6" def get_default_company(user=None): From d196aa5a361154f5c846bb7ef81c52704d5ec6eb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Feb 2024 12:21:25 +0530 Subject: [PATCH 08/19] fix: resolved conflict --- erpnext/assets/doctype/asset/depreciation.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 7422e56112c..42a93a85e36 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -561,11 +561,8 @@ def modify_depreciation_schedule_for_asset_repairs(asset, notes): def reverse_depreciation_entry_made_after_disposal(asset, date): for row in asset.get("finance_books"): asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book) -<<<<<<< HEAD -======= if not asset_depr_schedule_doc or not asset_depr_schedule_doc.get("depreciation_schedule"): continue ->>>>>>> 17f85de6fb (fix: Issues regarding asset cancellation and deletion) for schedule_idx, schedule in enumerate(asset_depr_schedule_doc.get("depreciation_schedule")): if schedule.schedule_date == date and schedule.journal_entry: From fc565ecd996da72e28b452598a784788008d299b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Feb 2024 12:21:57 +0530 Subject: [PATCH 09/19] fix: resolved conflict --- .../doctype/asset_capitalization/asset_capitalization.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index ca1d616be37..e27a492fa67 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -148,10 +148,7 @@ class AssetCapitalization(StockController): def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: asset_doc = frappe.get_doc("Asset", self.target_asset) -<<<<<<< HEAD -======= asset_doc.db_set("capitalized_in", None) ->>>>>>> 17f85de6fb (fix: Issues regarding asset cancellation and deletion) if asset_doc.docstatus == 1: asset_doc.cancel() From 792de1be03cc93e3a33378019d6a8cb42bda56cb Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 26 Feb 2024 07:33:28 +0000 Subject: [PATCH 10/19] chore(release): Bumped to Version 15.14.7 ## [15.14.7](https://github.com/frappe/erpnext/compare/v15.14.6...v15.14.7) (2024-02-26) ### Bug Fixes * Issues regarding asset cancellation and deletion ([6fc4dac](https://github.com/frappe/erpnext/commit/6fc4dac4db0e9c6cbbb90f5848a35e98bbe4f422)) * removed unwanted patch ([3c98368](https://github.com/frappe/erpnext/commit/3c98368e4552eeff152526a3ce337b418190c4b7)) * resolved conflict ([fc565ec](https://github.com/frappe/erpnext/commit/fc565ecd996da72e28b452598a784788008d299b)) * resolved conflict ([d196aa5](https://github.com/frappe/erpnext/commit/d196aa5a361154f5c846bb7ef81c52704d5ec6eb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8a66788a020..778bdd20f34 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.14.6" +__version__ = "15.14.7" def get_default_company(user=None): From 23011df86baaf4cf56d25371b08a14fb38e59f47 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 28 Feb 2024 05:13:40 +0000 Subject: [PATCH 11/19] chore(release): Bumped to Version 15.15.0 # [15.15.0](https://github.com/frappe/erpnext/compare/v15.14.7...v15.15.0) (2024-02-28) ### Bug Fixes * add flags for repost to ensure correct accounting from India Compliance App ([7d14ecf](https://github.com/frappe/erpnext/commit/7d14ecfcb8050589c2770d9660038d68dd781ba9)) * amount label according to party type ([d541ba3](https://github.com/frappe/erpnext/commit/d541ba3e61bee027b7b8e459708becf73e72aeaa)) * Cannot read properties of undefined (backport [#40081](https://github.com/frappe/erpnext/issues/40081)) ([#40083](https://github.com/frappe/erpnext/issues/40083)) ([53d943e](https://github.com/frappe/erpnext/commit/53d943ec7044dff444dccf7d8d30f97c5b8075b7)) * capacity planning issue in the job card (backport [#40092](https://github.com/frappe/erpnext/issues/40092)) ([#40101](https://github.com/frappe/erpnext/issues/40101)) ([27703b5](https://github.com/frappe/erpnext/commit/27703b54bd2457fcab833d7d5365fc75ee499168)) * change label name ([824df72](https://github.com/frappe/erpnext/commit/824df72eebaa95bb0a4e2f97f5dc3ac1d4955356)) * check for pricing rules on item ([32d9642](https://github.com/frappe/erpnext/commit/32d96423795c99ed2cc46cf1cc04fbf636dac5c7)) * check_credit_limit on_update_after_submit of Sales Order ([83d7111](https://github.com/frappe/erpnext/commit/83d71116492448a37c6b9befb1557fcc96548487)) * communication_date in party dashboards (backport [#40005](https://github.com/frappe/erpnext/issues/40005)) ([#40021](https://github.com/frappe/erpnext/issues/40021)) ([4269ef8](https://github.com/frappe/erpnext/commit/4269ef8c98fb7852556f904a063131e3f7e17afc)) * Completed Work Orders report not working ([ca03e9c](https://github.com/frappe/erpnext/commit/ca03e9cfd3cd3b20ee1984df7706fa262626fc24)) * Cr/Dr notes with POS Payments ([2e07b03](https://github.com/frappe/erpnext/commit/2e07b03307670f73745622b0b5301e9fcdae9c88)) * currency symbol in landed cost voucher and material request (backport [#40138](https://github.com/frappe/erpnext/issues/40138)) ([#40141](https://github.com/frappe/erpnext/issues/40141)) ([57bb031](https://github.com/frappe/erpnext/commit/57bb0316029c22089460c7c8453603cae2dae739)) * Data too long for column 'serial_no' at row 1 (backport [#40098](https://github.com/frappe/erpnext/issues/40098)) ([#40139](https://github.com/frappe/erpnext/issues/40139)) ([9d19ec4](https://github.com/frappe/erpnext/commit/9d19ec43c8653b84ac04a6799c897f2236923182)) * default taxable value for item not found in item list ([7e43f6b](https://github.com/frappe/erpnext/commit/7e43f6b7e0e81b3a4c8646edfd14ac3844b20a87)) * delete PLE containing invoice in against ([190bd45](https://github.com/frappe/erpnext/commit/190bd45bd76a9e48a023514e09f5803964496edf)) * do not make MR against raw materials of available sub assemblies (backport [#40085](https://github.com/frappe/erpnext/issues/40085)) ([#40087](https://github.com/frappe/erpnext/issues/40087)) ([cf5fa21](https://github.com/frappe/erpnext/commit/cf5fa210bbe1cbd6f88f0144595a6746758a2d21)) * Fiscal Year exception on demo data setup ([56ee843](https://github.com/frappe/erpnext/commit/56ee843233c6521a522db9af1bd01e2edf649401)) * incorrect item name in MR (backport [#40018](https://github.com/frappe/erpnext/issues/40018)) ([#40024](https://github.com/frappe/erpnext/issues/40024)) ([9f8f3db](https://github.com/frappe/erpnext/commit/9f8f3db953a5d801a597a2608605688459085bb6)) * Issues regarding asset cancellation and deletion ([8eb2f67](https://github.com/frappe/erpnext/commit/8eb2f6791054a317edf77460d5e893c23c9ceaca)) * negative stock error while making stock reconciliation (backport [#40016](https://github.com/frappe/erpnext/issues/40016)) ([#40026](https://github.com/frappe/erpnext/issues/40026)) ([c964c45](https://github.com/frappe/erpnext/commit/c964c45f4e50989485eef1f8e67039cdc065432b)) * on unreconciliation, update advance paid ([4d1f56c](https://github.com/frappe/erpnext/commit/4d1f56c4bd92b9dd54c2dec7da00882ce3ec6ad0)) * only check for delinked PLEs ([a75a69a](https://github.com/frappe/erpnext/commit/a75a69a01e4781215b23514fd2a4aedd45d729bc)) * only consider contributed qty towards achieved targets ([194f46b](https://github.com/frappe/erpnext/commit/194f46be572e23f5e38539903a8034c9ce43d4c8)) * parent warehouse checks in the production plan for sub-assemblies (backport [#40150](https://github.com/frappe/erpnext/issues/40150)) ([#40157](https://github.com/frappe/erpnext/issues/40157)) ([4784117](https://github.com/frappe/erpnext/commit/4784117a8f7a0b5b5f08c10c0f009311278aaaef)) * remove cancelled payment entry from Payment Period Based On Invoice Date ([72da308](https://github.com/frappe/erpnext/commit/72da3083e6b87ece431b9b2593efd2dbea239754)) * remove cancelled payment entry from PPBOID report ([0be5203](https://github.com/frappe/erpnext/commit/0be520331c68cbd7e4e0ccd77d78ce97374c3615)) * remove config for default bank account in test ([36b442a](https://github.com/frappe/erpnext/commit/36b442a951106395a0372c90e861fb6ec65fecc4)) * remove microsecond from posting datetime (backport [#40017](https://github.com/frappe/erpnext/issues/40017)) ([#40022](https://github.com/frappe/erpnext/issues/40022)) ([eaa3849](https://github.com/frappe/erpnext/commit/eaa3849df4b51038e404e12d605358a0665eb3c4)) * removed unwanted patch ([ee2d108](https://github.com/frappe/erpnext/commit/ee2d108bef460437cd35bac53749cb9196a4ad39)) * resolved conflict ([6928674](https://github.com/frappe/erpnext/commit/692867427cf5624b154f3e32170a2ca74f32b453)) * resolved conflict ([a0c0ab7](https://github.com/frappe/erpnext/commit/a0c0ab7709baff88fe78373d261734f4183d8bd4)) * skip max discount validation for rate adjustment ([3b96aae](https://github.com/frappe/erpnext/commit/3b96aaeead64ec7ab5c7964b156d5696c04e766f)) * skip SO & DN validation for debit note ([cd42089](https://github.com/frappe/erpnext/commit/cd42089e20df5776e8a716e395a946929a829341)) * Supplier users not able to see RFQ on the Portal (backport [#40161](https://github.com/frappe/erpnext/issues/40161)) ([#40165](https://github.com/frappe/erpnext/issues/40165)) ([6a63a6c](https://github.com/frappe/erpnext/commit/6a63a6c98a1c37238481a68714f107c9c00f39f9)) * timesheet per billed state edge case (backport [#40010](https://github.com/frappe/erpnext/issues/40010)) ([#40029](https://github.com/frappe/erpnext/issues/40029)) ([a543bf4](https://github.com/frappe/erpnext/commit/a543bf47efb48afd5b4ca01ecf38da3c2b033321)) * translatable columns in Sales Pipeline Analytics report ([1c5a7e2](https://github.com/frappe/erpnext/commit/1c5a7e29f229ac0db90482a0e2b73aa90fec3828)) * type error for missing frm obj ([6b5e1cf](https://github.com/frappe/erpnext/commit/6b5e1cfeb70f32c99b2f1dd8c4c98178c8e3cb31)) * unique gl account for plaid bank accounts ([65853da](https://github.com/frappe/erpnext/commit/65853da505a1d51a1e25b6caa22d1f349a6c9047)) * use correct variable name on hotfix branches ([0694fd1](https://github.com/frappe/erpnext/commit/0694fd19fd04f8aebf487a78e3f8a1dbbcac05e6)) * use frm instead of cur_frm ([341f903](https://github.com/frappe/erpnext/commit/341f9030f5aa4fecfbedc519a2ef1e39f967f6fa)) * use serial batch fields for packed items (backport [#40140](https://github.com/frappe/erpnext/issues/40140)) ([#40142](https://github.com/frappe/erpnext/issues/40142)) ([1860399](https://github.com/frappe/erpnext/commit/1860399ccb1a42c95498becb63090734760e5f07)) ### Features * show contributed qty in transaction summary ([38abfdb](https://github.com/frappe/erpnext/commit/38abfdb8ae025c9ff90e274db25fc50777b26de2)) * update billed amount in PO and PR ([e7e8149](https://github.com/frappe/erpnext/commit/e7e8149fbe8ea4829795308fd6e698567178d0f5)) ### Performance Improvements * new column Posting Datetime in SLE to optimize stock ledger related queries (backport [#39800](https://github.com/frappe/erpnext/issues/39800)) ([#40004](https://github.com/frappe/erpnext/issues/40004)) ([b9181e8](https://github.com/frappe/erpnext/commit/b9181e85dcd528959dc20d7ff7f141a19ec3e10f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 778bdd20f34..cdae94b06b7 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.14.7" +__version__ = "15.15.0" def get_default_company(user=None): From eb9b2e896e7d842728b124a37c13711a8425ebf3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 6 Mar 2024 03:31:25 +0000 Subject: [PATCH 12/19] chore(release): Bumped to Version 15.16.0 # [15.16.0](https://github.com/frappe/erpnext/compare/v15.15.0...v15.16.0) (2024-03-06) ### Bug Fixes * allow editable accounting dimensions for repostable doctypes ([cd24a2a](https://github.com/frappe/erpnext/commit/cd24a2a05d5dfb361ae83b6dccf803048c6f9514)) * allow gain/loss for Journals against Journals ([d8cf6ba](https://github.com/frappe/erpnext/commit/d8cf6ba38d42e401888a9018eb18afcd89d0aae9)) * check child rows before update ([e20c1ac](https://github.com/frappe/erpnext/commit/e20c1acb6ee377252aaa295d384e2e1d7646252a)) * disable editable account heads ([c55c0f7](https://github.com/frappe/erpnext/commit/c55c0f7173b61fce91b803ec6283b440e504280a)) * do not allow to cancel incomplete reposting (backport [#40224](https://github.com/frappe/erpnext/issues/40224)) ([#40230](https://github.com/frappe/erpnext/issues/40230)) ([63209f4](https://github.com/frappe/erpnext/commit/63209f4eac3a317b8358cf394d7997148e0532da)) * don't override reference exchange rate ([4513d83](https://github.com/frappe/erpnext/commit/4513d83f22fad18a9fcc9a3995f70be162a58566)) * handle partial invoice against provisional entry ([87596e6](https://github.com/frappe/erpnext/commit/87596e6e29250ec86e2a17c846ebd5c2379b4aeb)) * ignore self on GL account validation for Bank Account ([1947a67](https://github.com/frappe/erpnext/commit/1947a67f3d7388b1a1a7b8ff37983ddb90a20e7d)) * include Debit To/Credit To account while fetching advance ([23d7a1f](https://github.com/frappe/erpnext/commit/23d7a1fc7687fbbfaee65a57887440471224edf4)) * incorrect exchange rate if JE has multi parties ([b785901](https://github.com/frappe/erpnext/commit/b785901efa6299f9e84cf038efe7f09aea1ebdad)) * incorrect TCS on customer and suppliers with same name ([d74647d](https://github.com/frappe/erpnext/commit/d74647d5a589feacbbf9f4bea66a6abc94344b8a)) * make use of 'flt' to prevent really low precision exc gain/loss ([9057bff](https://github.com/frappe/erpnext/commit/9057bff7869035b7c79fd3c832fd066f38c9a4dd)) * make warning for previously existing SO an alert ([4f9b194](https://github.com/frappe/erpnext/commit/4f9b194fe9467ed59df1e412ca971fd93b376d22)) * over billing qty along with rate ([00d410c](https://github.com/frappe/erpnext/commit/00d410cc1dbc110b74612c504be0df581ec274e0)) * **Project:** filter department by company ([13e3343](https://github.com/frappe/erpnext/commit/13e334398e25a8866a3cd0e5da2b946cb7d6572f)) * provisional reverse entry amount ([0182b95](https://github.com/frappe/erpnext/commit/0182b95230d538c3810e93d9139829fe112a1a35)) * rate change on changing of the qty (backport [#40241](https://github.com/frappe/erpnext/issues/40241)) ([#40243](https://github.com/frappe/erpnext/issues/40243)) ([1d42171](https://github.com/frappe/erpnext/commit/1d421713bec16f1c4d2576248b15cdd47fe293c1)) * remove free item row only if pricing rule matches ([6352bfe](https://github.com/frappe/erpnext/commit/6352bfe34ebe6d96913ffb372579d52a0eba0066)) * report path from the Item and Putaway Rule list (backport [#40190](https://github.com/frappe/erpnext/issues/40190)) ([#40266](https://github.com/frappe/erpnext/issues/40266)) ([c7b96df](https://github.com/frappe/erpnext/commit/c7b96df69c627c76d293deac182986403cf70942)) * serial no valuation rate (backport [#40221](https://github.com/frappe/erpnext/issues/40221)) ([#40223](https://github.com/frappe/erpnext/issues/40223)) ([85ae9ee](https://github.com/frappe/erpnext/commit/85ae9eee250c5be1db00c272876dbd79b1be0038)) * **setup:** avoid duplicate entry for Analytics role (backport [#40183](https://github.com/frappe/erpnext/issues/40183)) ([#40185](https://github.com/frappe/erpnext/issues/40185)) ([9cb8d33](https://github.com/frappe/erpnext/commit/9cb8d3392328ec123cc9a2a4f4c86433a05fb036)) * test for repost accounting in JVs ([1634955](https://github.com/frappe/erpnext/commit/16349553c7ae96351935d9b04a472151df22358a)) * test for reposting pi ([6230bbc](https://github.com/frappe/erpnext/commit/6230bbc77d64ef41d1b06b8781a0be358435e599)) * uom wise price in sales or purchase transaction (backport [#40216](https://github.com/frappe/erpnext/issues/40216)) ([#40225](https://github.com/frappe/erpnext/issues/40225)) ([b398cc6](https://github.com/frappe/erpnext/commit/b398cc657995d6efb57356fd77bb92d745ad2785)) ### Features * add company filter to child table field ([0ede99b](https://github.com/frappe/erpnext/commit/0ede99b3cb508ec88c4773b82b0a5710c2e15a52)) * add patch for making repostable dimension fields editable ([4b57126](https://github.com/frappe/erpnext/commit/4b5712688a21f4f9e38c7bee27d4f01dd1ee13f4)) * allow on submit for selected fields ([6c3b5bb](https://github.com/frappe/erpnext/commit/6c3b5bb402f4918b3234ff5c1266d02fc2e52cee)) * repost ledger button in JV ([e5eeb21](https://github.com/frappe/erpnext/commit/e5eeb216e8a52b30fd8b7138dfd83ff65b3798b9)) * update after submit in JV ([cdab3fa](https://github.com/frappe/erpnext/commit/cdab3fa9709fc4bff2158ce5c6e8d860ba86959e)) * validate before allowing repost ([8585cfc](https://github.com/frappe/erpnext/commit/8585cfc533f2f2ff2191ed0da06035c55419b446)) ### Performance Improvements * serial and batch bundle valuation (reposting) (backport [#40255](https://github.com/frappe/erpnext/issues/40255)) ([#40262](https://github.com/frappe/erpnext/issues/40262)) ([93f3af7](https://github.com/frappe/erpnext/commit/93f3af7dbabe4ad3a690057be0478aeda0388896)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index cdae94b06b7..0c08efb9657 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.15.0" +__version__ = "15.16.0" def get_default_company(user=None): From 3814c5a54a5af923be27038f141ae93c7ca32516 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:17:01 +0530 Subject: [PATCH 13/19] fix: not able to cancel purchase receipt for old subcontracting flow (backport #40298) (backport #40302) (#40305) fix: not able to cancel purchase receipt for old subcontracting flow (backport #40298) (#40302) fix: not able to cancel purchase receipt for old subcontracting flow (#40298) (cherry picked from commit 48da952fd7d0a2d7b76b01f290e1ccb7584fa2d4) Co-authored-by: rohitwaghchaure (cherry picked from commit e0e80f7eedffe8e49248ef0d3e5ae8a1b1c91321) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/controllers/buying_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 91ee53a796e..821185766eb 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -559,7 +559,7 @@ class BuyingController(SubcontractingController): { "incoming_rate": incoming_rate, "recalculate_rate": 1 - if (self.is_subcontracted and (d.bom or d.fg_item)) or d.from_warehouse + if (self.is_subcontracted and (d.bom or d.get("fg_item"))) or d.from_warehouse else 0, } ) From 539eb794c034c5705159545e420c39da266f2a3c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:17:09 +0530 Subject: [PATCH 14/19] fix: stock ledger balance qty for the batch (backport #40274) (backport #40301) (#40304) fix: stock ledger balance qty for the batch (backport #40274) (#40301) fix: stock ledger balance qty for the batch (#40274) (cherry picked from commit e178ffc3c182f3339f82afb3625e18ec38445943) Co-authored-by: rohitwaghchaure (cherry picked from commit 62aefcef04b2e15ad51e50953a0e8e56b27c39db) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../serial_and_batch_bundle.py | 2 +- .../stock/report/stock_ledger/stock_ledger.js | 10 ++++++++- .../stock/report/stock_ledger/stock_ledger.py | 21 +++++++++++++------ erpnext/stock/serial_batch_bundle.py | 4 +--- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index d01dfefc926..dd59a5dd33f 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -847,7 +847,7 @@ class SerialandBatchBundle(Document): available_batches = get_available_batches_qty(available_batches) for batch_no in batches: - if batch_no not in available_batches or available_batches[batch_no] < 0: + if batch_no in available_batches and available_batches[batch_no] < 0: if flt(available_batches.get(batch_no)) < 0: self.validate_negative_batch(batch_no, available_batches[batch_no]) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.js b/erpnext/stock/report/stock_ledger/stock_ledger.js index 2ec757b2050..baccd552049 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.js +++ b/erpnext/stock/report/stock_ledger/stock_ledger.js @@ -58,7 +58,15 @@ frappe.query_reports["Stock Ledger"] = { "fieldname":"batch_no", "label": __("Batch No"), "fieldtype": "Link", - "options": "Batch" + "options": "Batch", + on_change() { + const batch_no = frappe.query_report.get_filter_value('batch_no'); + if (batch_no) { + frappe.query_report.set_filter_value('segregate_serial_batch_bundle', 1); + } else { + frappe.query_report.set_filter_value('segregate_serial_batch_bundle', 0); + } + } }, { "fieldname":"brand", diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index d859f4e4d92..2e4b08c3ea5 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -3,6 +3,7 @@ import copy +from collections import defaultdict import frappe from frappe import _ @@ -31,7 +32,7 @@ def execute(filters=None): bundle_details = {} if filters.get("segregate_serial_batch_bundle"): - bundle_details = get_serial_batch_bundle_details(sl_entries) + bundle_details = get_serial_batch_bundle_details(sl_entries, filters) data = [] conversion_factors = [] @@ -47,12 +48,13 @@ def execute(filters=None): available_serial_nos = {} inventory_dimension_filters_applied = check_inventory_dimension_filters_applied(filters) + batch_balance_dict = defaultdict(float) for sle in sl_entries: item_detail = item_details[sle.item_code] sle.update(item_detail) if bundle_info := bundle_details.get(sle.serial_and_batch_bundle): - data.extend(get_segregated_bundle_entries(sle, bundle_info)) + data.extend(get_segregated_bundle_entries(sle, bundle_info, batch_balance_dict)) continue if filters.get("batch_no") or inventory_dimension_filters_applied: @@ -85,7 +87,7 @@ def execute(filters=None): return columns, data -def get_segregated_bundle_entries(sle, bundle_details): +def get_segregated_bundle_entries(sle, bundle_details, batch_balance_dict): segregated_entries = [] qty_before_transaction = sle.qty_after_transaction - sle.actual_qty stock_value_before_transaction = sle.stock_value - sle.stock_value_difference @@ -93,7 +95,6 @@ def get_segregated_bundle_entries(sle, bundle_details): for row in bundle_details: new_sle = copy.deepcopy(sle) new_sle.update(row) - new_sle.update( { "in_out_rate": flt(new_sle.stock_value_difference / row.qty) if row.qty else 0, @@ -105,6 +106,10 @@ def get_segregated_bundle_entries(sle, bundle_details): } ) + if row.batch_no: + batch_balance_dict[row.batch_no] += row.qty + new_sle.update({"qty_after_transaction": batch_balance_dict[row.batch_no]}) + qty_before_transaction += row.qty stock_value_before_transaction += new_sle.stock_value_difference @@ -117,7 +122,7 @@ def get_segregated_bundle_entries(sle, bundle_details): return segregated_entries -def get_serial_batch_bundle_details(sl_entries): +def get_serial_batch_bundle_details(sl_entries, filters=None): bundle_details = [] for sle in sl_entries: if sle.serial_and_batch_bundle: @@ -126,10 +131,14 @@ def get_serial_batch_bundle_details(sl_entries): if not bundle_details: return frappe._dict({}) + query_filers = {"parent": ("in", bundle_details)} + if filters.get("batch_no"): + query_filers["batch_no"] = filters.batch_no + _bundle_details = frappe._dict({}) batch_entries = frappe.get_all( "Serial and Batch Entry", - filters={"parent": ("in", bundle_details)}, + filters=query_filers, fields=["parent", "qty", "incoming_rate", "stock_value_difference", "batch_no", "serial_no"], order_by="parent, idx", ) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 1fcc439fda3..12df0fabedb 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -325,9 +325,7 @@ class SerialBatchBundle: batches = frappe._dict({self.sle.batch_no: self.sle.actual_qty}) batches_qty = get_available_batches( - frappe._dict( - {"item_code": self.item_code, "warehouse": self.warehouse, "batch_no": list(batches.keys())} - ) + frappe._dict({"item_code": self.item_code, "batch_no": list(batches.keys())}) ) for batch_no in batches: From f21918d06c93b0ff48247e51af238d450583f774 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:17:20 +0530 Subject: [PATCH 15/19] fix: batch no not copied while making Material Consumption entry (backport #40290) (backport #40300) (#40303) fix: batch no not copied while making Material Consumption entry (backport #40290) (#40300) fix: batch no not copied while making Material Consumption entry (#40290) (cherry picked from commit 1eaa3866576793f429ccb52b2939bc24c4dc57f4) Co-authored-by: rohitwaghchaure (cherry picked from commit 6d1c144a6682562ee2c03f6df40aa59cb51372b3) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../doctype/work_order/test_work_order.py | 99 +++++++++++++++---- .../stock/doctype/stock_entry/stock_entry.py | 34 +++++-- 2 files changed, 107 insertions(+), 26 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 3514daba922..309408992d4 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1382,8 +1382,9 @@ class TestWorkOrder(FrappeTestCase): # Inward raw materials in Stores warehouse ste_doc.submit() + ste_doc.reload() - serial_nos_list = sorted(get_serial_nos(ste_doc.items[0].serial_no)) + serial_nos_list = sorted(get_serial_nos_from_bundle(ste_doc.items[0].serial_and_batch_bundle)) wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4) transferred_ste_doc = frappe.get_doc( @@ -1395,19 +1396,22 @@ class TestWorkOrder(FrappeTestCase): # First Manufacture stock entry manufacture_ste_doc1 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 1)) + manufacture_ste_doc1.submit() + manufacture_ste_doc1.reload() # Serial nos should be same as transferred Serial nos - self.assertEqual(get_serial_nos(manufacture_ste_doc1.items[0].serial_no), serial_nos_list[0:1]) + self.assertEqual( + sorted(get_serial_nos_from_bundle(manufacture_ste_doc1.items[0].serial_and_batch_bundle)), + serial_nos_list[0:1], + ) self.assertEqual(manufacture_ste_doc1.items[0].qty, 1) - manufacture_ste_doc1.submit() - # Second Manufacture stock entry - manufacture_ste_doc2 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 2)) + manufacture_ste_doc2 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 3)) # Serial nos should be same as transferred Serial nos - self.assertEqual(get_serial_nos(manufacture_ste_doc2.items[0].serial_no), serial_nos_list[1:3]) - self.assertEqual(manufacture_ste_doc2.items[0].qty, 2) + self.assertEqual(get_serial_nos(manufacture_ste_doc2.items[0].serial_no), serial_nos_list[1:4]) + self.assertEqual(manufacture_ste_doc2.items[0].qty, 3) def test_backflushed_serial_no_batch_raw_materials_based_on_transferred(self): frappe.db.set_single_value( @@ -1541,19 +1545,9 @@ class TestWorkOrder(FrappeTestCase): row.qty -= 2 row.transfer_qty -= 2 - if not row.serial_and_batch_bundle: - continue - - bundle_id = row.serial_and_batch_bundle - bundle_doc = frappe.get_doc("Serial and Batch Bundle", bundle_id) - if bundle_doc.has_serial_no: - bundle_doc.set("entries", bundle_doc.entries[0:5]) - else: - for bundle_row in bundle_doc.entries: - bundle_row.qty += 2 - - bundle_doc.save() - bundle_doc.load_from_db() + if row.serial_no: + serial_nos = get_serial_nos(row.serial_no) + row.serial_no = "\n".join(serial_nos[:5]) ste_doc.save() ste_doc.submit() @@ -1896,6 +1890,71 @@ class TestWorkOrder(FrappeTestCase): "Manufacturing Settings", {"disable_capacity_planning": 1, "mins_between_operations": 0} ) + def test_partial_material_consumption_with_batch(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import ( + make_stock_entry as make_stock_entry_test_record, + ) + + frappe.db.set_single_value("Manufacturing Settings", "material_consumption", 1) + frappe.db.set_single_value( + "Manufacturing Settings", + "backflush_raw_materials_based_on", + "Material Transferred for Manufacture", + ) + + fg_item = make_item( + "Test FG Item For Partial Material Consumption", + {"is_stock_item": 1}, + ).name + + rm_item = make_item( + "Test RM Item For Partial Material Consumption", + { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "TST-BATCH-PMCC-.####", + }, + ).name + + make_bom( + item=fg_item, + source_warehouse="Stores - _TC", + raw_materials=[rm_item], + ) + + make_stock_entry_test_record( + purpose="Material Receipt", + item_code=rm_item, + target="Stores - _TC", + qty=10, + basic_rate=100, + ) + + wo_order = make_wo_order_test_record(item=fg_item, qty=10) + + stock_entry = frappe.get_doc( + make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 10) + ) + stock_entry.submit() + stock_entry.reload() + + batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle) + + stock_entry = frappe.get_doc( + make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 10) + ) + + self.assertEqual(stock_entry.items[0].batch_no, batch_no) + self.assertEqual(stock_entry.items[0].use_serial_batch_fields, 1) + + frappe.db.set_single_value("Manufacturing Settings", "material_consumption", 0) + frappe.db.set_single_value( + "Manufacturing Settings", + "backflush_raw_materials_based_on", + "BOM", + ) + def make_operation(**kwargs): kwargs = frappe._dict(kwargs) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index dd56ef8e7fe..759fc080aca 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2163,23 +2163,42 @@ class StockEntry(StockController): if not qty: return + use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields") + ste_item_details = { "from_warehouse": item.warehouse, "to_warehouse": "", "qty": qty, "item_name": item.item_name, - "serial_and_batch_bundle": create_serial_and_batch_bundle(self, row, item, "Outward"), + "serial_and_batch_bundle": create_serial_and_batch_bundle(self, row, item, "Outward") + if not use_serial_batch_fields + else "", "description": item.description, "stock_uom": item.stock_uom, "expense_account": item.expense_account, "cost_center": item.buying_cost_center, "original_item": item.original_item, + "serial_no": "\n".join(row.serial_nos) + if row.serial_nos and not row.batches_to_be_consume + else "", + "use_serial_batch_fields": use_serial_batch_fields, } if self.is_return: ste_item_details["to_warehouse"] = item.s_warehouse - self.add_to_stock_entry_detail({item.item_code: ste_item_details}) + if use_serial_batch_fields and not row.serial_no and row.batches_to_be_consume: + for batch_no, batch_qty in row.batches_to_be_consume.items(): + ste_item_details.update( + { + "batch_no": batch_no, + "qty": batch_qty, + } + ) + + self.add_to_stock_entry_detail({item.item_code: ste_item_details}) + else: + self.add_to_stock_entry_detail({item.item_code: ste_item_details}) @staticmethod def get_serial_nos_based_on_transferred_batch(batch_no, serial_nos) -> list: @@ -2330,6 +2349,9 @@ class StockEntry(StockController): "item_name", "serial_and_batch_bundle", "allow_zero_valuation_rate", + "use_serial_batch_fields", + "batch_no", + "serial_no", ]: if item_row.get(field): se_child.set(field, item_row.get(field)) @@ -2978,7 +3000,7 @@ def get_available_materials(work_order) -> dict: if row.batch_no: item_data.batch_details[row.batch_no] += row.qty - if row.batch_nos: + elif row.batch_nos: for batch_no, qty in row.batch_nos.items(): item_data.batch_details[batch_no] += qty @@ -2986,7 +3008,7 @@ def get_available_materials(work_order) -> dict: item_data.serial_nos.extend(get_serial_nos(row.serial_no)) item_data.serial_nos.sort() - if row.serial_nos: + elif row.serial_nos: item_data.serial_nos.extend(get_serial_nos(row.serial_nos)) item_data.serial_nos.sort() else: @@ -2996,7 +3018,7 @@ def get_available_materials(work_order) -> dict: if row.batch_no: item_data.batch_details[row.batch_no] -= row.qty - if row.batch_nos: + elif row.batch_nos: for batch_no, qty in row.batch_nos.items(): item_data.batch_details[batch_no] += qty @@ -3004,7 +3026,7 @@ def get_available_materials(work_order) -> dict: for serial_no in get_serial_nos(row.serial_no): item_data.serial_nos.remove(serial_no) - if row.serial_nos: + elif row.serial_nos: for serial_no in get_serial_nos(row.serial_nos): item_data.serial_nos.remove(serial_no) From e357497bcb681b1509f8652b5bbc67262c5db9af Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:17:30 +0530 Subject: [PATCH 16/19] fix: use serial batch fields for subcontracting receipt (backport #40311) (backport #40315) (#40316) fix: use serial batch fields for subcontracting receipt (backport #40311) (#40315) fix: use serial batch fields for subcontracting receipt (#40311) (cherry picked from commit cef62913118dc7400066d1f04c81025d96daf015) Co-authored-by: rohitwaghchaure (cherry picked from commit 4b15c00b11ee9ed2271ef55d72982de436106f34) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/controllers/queries.py | 12 +++ erpnext/controllers/stock_controller.py | 80 ++++++++++--------- .../js/utils/serial_no_batch_selector.js | 9 ++- .../serial_and_batch_bundle.py | 7 ++ .../stock/doctype/stock_entry/stock_entry.py | 47 +---------- .../doctype/stock_entry/test_stock_entry.py | 30 ------- .../test_subcontracting_receipt.py | 71 ++++++++++++++++ 7 files changed, 143 insertions(+), 113 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 9904c75e98a..a597d5f20a5 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -434,9 +434,21 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): filtered_batches = get_filterd_batches(batches) + if filters.get("is_inward"): + filtered_batches.extend(get_empty_batches(filters)) + return filtered_batches +def get_empty_batches(filters): + return frappe.get_all( + "Batch", + fields=["name", "batch_qty"], + filters={"item": filters.get("item_code"), "batch_qty": 0.0}, + as_list=1, + ) + + def get_filterd_batches(data): batches = OrderedDict() diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 874cbb16550..63556cddca4 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -190,43 +190,23 @@ class StockController(AccountsController): if row.use_serial_batch_fields and ( not row.serial_and_batch_bundle and not row.get("rejected_serial_and_batch_bundle") ): - if self.doctype == "Stock Reconciliation": - qty = row.qty - type_of_transaction = "Inward" - warehouse = row.warehouse - elif table_name == "packed_items": - qty = row.qty - warehouse = row.warehouse - type_of_transaction = "Outward" - if self.is_return: - type_of_transaction = "Inward" - else: - qty = row.stock_qty if self.doctype != "Stock Entry" else row.transfer_qty - type_of_transaction = get_type_of_transaction(self, row) - warehouse = ( - row.warehouse if self.doctype != "Stock Entry" else row.s_warehouse or row.t_warehouse - ) + bundle_details = { + "item_code": row.item_code, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "voucher_type": self.doctype, + "voucher_no": self.name, + "voucher_detail_no": row.name, + "company": self.company, + "is_rejected": 1 if row.get("rejected_warehouse") else 0, + "serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None, + "batch_no": row.batch_no, + "use_serial_batch_fields": row.use_serial_batch_fields, + "do_not_submit": True, + } - sn_doc = SerialBatchCreation( - { - "item_code": row.item_code, - "warehouse": warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "voucher_type": self.doctype, - "voucher_no": self.name, - "voucher_detail_no": row.name, - "qty": qty, - "type_of_transaction": type_of_transaction, - "company": self.company, - "is_rejected": 1 if row.get("rejected_warehouse") else 0, - "serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None, - "batches": frappe._dict({row.batch_no: qty}) if row.batch_no else None, - "batch_no": row.batch_no, - "use_serial_batch_fields": row.use_serial_batch_fields, - "do_not_submit": True, - } - ).make_serial_and_batch_bundle() + self.update_bundle_details(bundle_details, table_name, row) + sn_doc = SerialBatchCreation(bundle_details).make_serial_and_batch_bundle() if sn_doc.is_rejected: row.rejected_serial_and_batch_bundle = sn_doc.name @@ -243,6 +223,34 @@ class StockController(AccountsController): } ) + def update_bundle_details(self, bundle_details, table_name, row): + # Since qty field is different for different doctypes + qty = row.get("qty") + warehouse = row.get("warehouse") + + if table_name == "packed_items": + type_of_transaction = "Inward" + if not self.is_return: + type_of_transaction = "Outward" + else: + type_of_transaction = get_type_of_transaction(self, row) + + if hasattr(row, "stock_qty"): + qty = row.stock_qty + + if self.doctype == "Stock Entry": + qty = row.transfer_qty + warehouse = row.s_warehouse or row.t_warehouse + + bundle_details.update( + { + "qty": qty, + "type_of_transaction": type_of_transaction, + "warehouse": warehouse, + "batches": frappe._dict({row.batch_no: qty}) if row.batch_no else None, + } + ) + def validate_serial_nos_and_batches_with_bundle(self, row): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index fccaf88c719..d0064b30c34 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -371,11 +371,18 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { label: __('Batch No'), in_list_view: 1, get_query: () => { + let is_inward = false; + if ((["Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype) && !this.frm.doc.is_return) + || (this.frm.doc.doctype === 'Stock Entry' && this.frm.doc.purpose === 'Material Receipt')) { + is_inward = true; + } + return { query : "erpnext.controllers.queries.get_batch_no", filters: { 'item_code': this.item.item_code, - 'warehouse': this.item.s_warehouse || this.item.t_warehouse, + 'warehouse': this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse, + 'is_inward': is_inward } } }, diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index dd59a5dd33f..08cb3ca3074 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -1263,6 +1263,13 @@ def get_type_of_transaction(parent_doc, child_row): if parent_doc.get("is_return"): type_of_transaction = "Inward" if type_of_transaction == "Outward" else "Outward" + if parent_doc.get("doctype") == "Subcontracting Receipt": + type_of_transaction = "Outward" + if child_row.get("doctype") == "Subcontracting Receipt Item": + type_of_transaction = "Inward" + elif parent_doc.get("doctype") == "Stock Reconciliation": + type_of_transaction = "Inward" + return type_of_transaction diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 759fc080aca..5a1d22ecd6d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -10,17 +10,7 @@ import frappe from frappe import _ from frappe.model.mapper import get_mapped_doc from frappe.query_builder.functions import Sum -from frappe.utils import ( - cint, - comma_or, - cstr, - flt, - format_time, - formatdate, - getdate, - month_diff, - nowdate, -) +from frappe.utils import cint, comma_or, cstr, flt, format_time, formatdate, getdate, nowdate import erpnext from erpnext.accounts.general_ledger import process_gl_map @@ -237,41 +227,6 @@ class StockEntry(StockController): self.reset_default_field_value("from_warehouse", "items", "s_warehouse") self.reset_default_field_value("to_warehouse", "items", "t_warehouse") - def submit(self): - if self.is_enqueue_action(): - frappe.msgprint( - _( - "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Draft stage" - ) - ) - self.queue_action("submit", timeout=2000) - else: - self._submit() - - def cancel(self): - if self.is_enqueue_action(): - frappe.msgprint( - _( - "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Submitted stage" - ) - ) - self.queue_action("cancel", timeout=2000) - else: - self._cancel() - - def is_enqueue_action(self, force=False) -> bool: - if force: - return True - - if frappe.flags.in_test: - return False - - # If line items are more than 100 or record is older than 6 months - if len(self.items) > 50 or month_diff(nowdate(), self.posting_date) > 6: - return True - - return False - def on_submit(self): self.validate_closed_subcontracting_order() self.make_bundle_using_old_serial_batch_fields() diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 699f5264c5e..902b8ffa90c 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1633,36 +1633,6 @@ class TestStockEntry(FrappeTestCase): self.assertRaises(frappe.ValidationError, sr_doc.submit) - def test_enqueue_action(self): - frappe.flags.in_test = False - item_code = "Test Enqueue Item - 001" - create_item(item_code=item_code, is_stock_item=1, valuation_rate=10) - - doc = make_stock_entry( - item_code=item_code, - posting_date=add_to_date(today(), months=-7), - posting_time="00:00:00", - purpose="Material Receipt", - qty=10, - to_warehouse="_Test Warehouse - _TC", - do_not_submit=True, - ) - - self.assertTrue(doc.is_enqueue_action()) - - doc = make_stock_entry( - item_code=item_code, - posting_date=today(), - posting_time="00:00:00", - purpose="Material Receipt", - qty=10, - to_warehouse="_Test Warehouse - _TC", - do_not_submit=True, - ) - - self.assertFalse(doc.is_enqueue_action()) - frappe.flags.in_test = True - def test_negative_batch(self): item_code = "Test Negative Batch Item - 001" make_item( diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 0450038d803..4738a700408 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -1061,6 +1061,77 @@ class TestSubcontractingReceipt(FrappeTestCase): self.assertTrue(frappe.db.get_value("Purchase Receipt", {"subcontracting_receipt": scr.name})) + def test_use_serial_batch_fields_for_subcontracting_receipt(self): + fg_item = make_item( + "Test Subcontracted Item With Batch No", + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-BNGS-.####", + "is_sub_contracted_item": 1, + }, + ).name + + make_item( + "Test Subcontracted Item With Batch No Service Item 1", + properties={"is_stock_item": 0}, + ) + + make_bom( + item=fg_item, + raw_materials=[ + make_item( + "Test Subcontracted Item With Batch No RM Item 1", + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-RM-BNGS-.####", + }, + ).name + ], + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Test Subcontracted Item With Batch No Service Item 1", + "qty": 1, + "rate": 100, + "fg_item": fg_item, + "fg_item_qty": 1, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + batch_no = "BATCH-BNGS-0001" + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": fg_item, + } + ).insert() + + scr = make_subcontracting_receipt(sco.name) + self.assertFalse(scr.items[0].serial_and_batch_bundle) + scr.items[0].use_serial_batch_fields = 1 + scr.items[0].batch_no = batch_no + + scr.save() + scr.submit() + scr.reload() + self.assertTrue(scr.items[0].serial_and_batch_bundle) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) From 643189a0cecc1efe6577fa01e9ada716873ed2f3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 6 Mar 2024 14:48:42 +0000 Subject: [PATCH 17/19] chore(release): Bumped to Version 15.16.1 ## [15.16.1](https://github.com/frappe/erpnext/compare/v15.16.0...v15.16.1) (2024-03-06) ### Bug Fixes * batch no not copied while making Material Consumption entry (backport [#40290](https://github.com/frappe/erpnext/issues/40290)) (backport [#40300](https://github.com/frappe/erpnext/issues/40300)) ([#40303](https://github.com/frappe/erpnext/issues/40303)) ([f21918d](https://github.com/frappe/erpnext/commit/f21918d06c93b0ff48247e51af238d450583f774)) * not able to cancel purchase receipt for old subcontracting flow (backport [#40298](https://github.com/frappe/erpnext/issues/40298)) (backport [#40302](https://github.com/frappe/erpnext/issues/40302)) ([#40305](https://github.com/frappe/erpnext/issues/40305)) ([3814c5a](https://github.com/frappe/erpnext/commit/3814c5a54a5af923be27038f141ae93c7ca32516)) * stock ledger balance qty for the batch (backport [#40274](https://github.com/frappe/erpnext/issues/40274)) (backport [#40301](https://github.com/frappe/erpnext/issues/40301)) ([#40304](https://github.com/frappe/erpnext/issues/40304)) ([539eb79](https://github.com/frappe/erpnext/commit/539eb794c034c5705159545e420c39da266f2a3c)) * use serial batch fields for subcontracting receipt (backport [#40311](https://github.com/frappe/erpnext/issues/40311)) (backport [#40315](https://github.com/frappe/erpnext/issues/40315)) ([#40316](https://github.com/frappe/erpnext/issues/40316)) ([e357497](https://github.com/frappe/erpnext/commit/e357497bcb681b1509f8652b5bbc67262c5db9af)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0c08efb9657..ea146f78849 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.16.0" +__version__ = "15.16.1" def get_default_company(user=None): From 5af335dc333def266da4533f6102924254b0d68a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:53:29 +0530 Subject: [PATCH 18/19] fix: use serial/batch field for rejected items (backport #40327) (backport #40329) (#40332) fix: use serial/batch field for rejected items (backport #40327) (#40329) fix: use serial/batch field for rejected items (#40327) (cherry picked from commit 01856a6e9d8c849242a897568f8f55afd373311a) Co-authored-by: rohitwaghchaure (cherry picked from commit 7ca72423b962a80fe7bf3660ed8f9ec4b4a2a60a) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../purchase_invoice/test_purchase_invoice.py | 91 ++++++++++++++++++- erpnext/controllers/stock_controller.py | 53 ++++++----- .../controllers/subcontracting_controller.py | 1 + .../purchase_receipt/test_purchase_receipt.py | 82 +++++++++++++++++ erpnext/stock/stock_ledger.py | 3 + .../test_subcontracting_receipt.py | 80 ++++++++++++++++ .../subcontracting_receipt_item.json | 5 +- 7 files changed, 289 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index e0fbef4a9b4..8b1e03c7426 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2098,6 +2098,92 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): return_pi.submit() self.assertEqual(return_pi.docstatus, 1) + def test_purchase_invoice_with_use_serial_batch_field_for_rejected_qty(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + batch_item = make_item( + "_Test Purchase Invoice Batch Item For Rejected Qty", + properties={"has_batch_no": 1, "create_new_batch": 1, "is_stock_item": 1}, + ).name + + serial_item = make_item( + "_Test Purchase Invoice Serial Item for Rejected Qty", + properties={"has_serial_no": 1, "is_stock_item": 1}, + ).name + + rej_warehouse = create_warehouse("_Test Purchase INV Warehouse For Rejected Qty") + + batch_no = "BATCH-PI-BNU-TPRBI-0001" + serial_nos = ["SNU-PI-TPRSI-0001", "SNU-PI-TPRSI-0002", "SNU-PI-TPRSI-0003"] + + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": batch_item, + } + ).insert() + + for serial_no in serial_nos: + if not frappe.db.exists("Serial No", serial_no): + frappe.get_doc( + { + "doctype": "Serial No", + "item_code": serial_item, + "serial_no": serial_no, + } + ).insert() + + pi = make_purchase_invoice( + item_code=batch_item, + received_qty=10, + qty=8, + rejected_qty=2, + update_stock=1, + rejected_warehouse=rej_warehouse, + use_serial_batch_fields=1, + batch_no=batch_no, + rate=100, + do_not_submit=1, + ) + + pi.append( + "items", + { + "item_code": serial_item, + "qty": 2, + "rate": 100, + "base_rate": 100, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "rejected_qty": 1, + "warehouse": pi.items[0].warehouse, + "rejected_warehouse": rej_warehouse, + "use_serial_batch_fields": 1, + "serial_no": "\n".join(serial_nos[:2]), + "rejected_serial_no": serial_nos[2], + }, + ) + + pi.save() + pi.submit() + + pi.reload() + + for row in pi.items: + self.assertTrue(row.serial_and_batch_bundle) + self.assertTrue(row.rejected_serial_and_batch_bundle) + + if row.item_code == batch_item: + self.assertEqual(row.batch_no, batch_no) + else: + self.assertEqual(row.serial_no, "\n".join(serial_nos[:2])) + self.assertEqual(row.rejected_serial_no, serial_nos[2]) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( @@ -2205,7 +2291,7 @@ def make_purchase_invoice(**args): pi.cost_center = args.parent_cost_center bundle_id = None - if args.get("batch_no") or args.get("serial_no"): + if not args.use_serial_batch_fields and ((args.get("batch_no") or args.get("serial_no"))): batches = {} qty = args.qty or 5 item_code = args.item or args.item_code or "_Test Item" @@ -2252,6 +2338,9 @@ def make_purchase_invoice(**args): "rejected_warehouse": args.rejected_warehouse or "", "asset_location": args.location or "", "allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0, + "use_serial_batch_fields": args.get("use_serial_batch_fields") or 0, + "batch_no": args.get("batch_no") if args.get("use_serial_batch_fields") else "", + "serial_no": args.get("serial_no") if args.get("use_serial_batch_fields") else "", }, ) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 63556cddca4..688600774cc 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -159,9 +159,6 @@ class StockController(AccountsController): row.serial_no = clean_serial_no_string(row.serial_no) def make_bundle_using_old_serial_batch_fields(self, table_name=None): - from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - from erpnext.stock.serial_batch_bundle import SerialBatchCreation - if self.get("_action") == "update_after_submit": return @@ -199,31 +196,21 @@ class StockController(AccountsController): "voucher_detail_no": row.name, "company": self.company, "is_rejected": 1 if row.get("rejected_warehouse") else 0, - "serial_nos": get_serial_nos(row.serial_no) if row.serial_no else None, - "batch_no": row.batch_no, "use_serial_batch_fields": row.use_serial_batch_fields, "do_not_submit": True, } - self.update_bundle_details(bundle_details, table_name, row) - sn_doc = SerialBatchCreation(bundle_details).make_serial_and_batch_bundle() + if row.qty: + self.update_bundle_details(bundle_details, table_name, row) + self.create_serial_batch_bundle(bundle_details, row) - if sn_doc.is_rejected: - row.rejected_serial_and_batch_bundle = sn_doc.name - row.db_set( - { - "rejected_serial_and_batch_bundle": sn_doc.name, - } - ) - else: - row.serial_and_batch_bundle = sn_doc.name - row.db_set( - { - "serial_and_batch_bundle": sn_doc.name, - } - ) + if row.get("rejected_qty"): + self.update_bundle_details(bundle_details, table_name, row, is_rejected=True) + self.create_serial_batch_bundle(bundle_details, row) + + def update_bundle_details(self, bundle_details, table_name, row, is_rejected=False): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - def update_bundle_details(self, bundle_details, table_name, row): # Since qty field is different for different doctypes qty = row.get("qty") warehouse = row.get("warehouse") @@ -242,15 +229,37 @@ class StockController(AccountsController): qty = row.transfer_qty warehouse = row.s_warehouse or row.t_warehouse + serial_nos = row.serial_no + if is_rejected: + serial_nos = row.get("rejected_serial_no") + type_of_transaction = "Inward" if not self.is_return else "Outward" + qty = row.get("rejected_qty") + warehouse = row.get("rejected_warehouse") + bundle_details.update( { "qty": qty, + "is_rejected": is_rejected, "type_of_transaction": type_of_transaction, "warehouse": warehouse, "batches": frappe._dict({row.batch_no: qty}) if row.batch_no else None, + "serial_nos": get_serial_nos(serial_nos) if serial_nos else None, + "batch_no": row.batch_no, } ) + def create_serial_batch_bundle(self, bundle_details, row): + from erpnext.stock.serial_batch_bundle import SerialBatchCreation + + sn_doc = SerialBatchCreation(bundle_details).make_serial_and_batch_bundle() + + field = "serial_and_batch_bundle" + if bundle_details.get("is_rejected"): + field = "rejected_serial_and_batch_bundle" + + row.set(field, sn_doc.name) + row.db_set({field: sn_doc.name}) + def validate_serial_nos_and_batches_with_bundle(self, row): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index eac35b0d39f..e66fe8bcdec 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -859,6 +859,7 @@ class SubcontractingController(StockController): item, { "warehouse": item.rejected_warehouse, + "serial_and_batch_bundle": item.get("rejected_serial_and_batch_bundle"), "actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor), "incoming_rate": 0.0, }, diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 053cb1d98bd..8c3c1f750ab 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2440,6 +2440,88 @@ class TestPurchaseReceipt(FrappeTestCase): pr.reload() self.assertEqual(pr.per_billed, 100) + def test_purchase_receipt_with_use_serial_batch_field_for_rejected_qty(self): + batch_item = make_item( + "_Test Purchase Receipt Batch Item For Rejected Qty", + properties={"has_batch_no": 1, "create_new_batch": 1, "is_stock_item": 1}, + ).name + + serial_item = make_item( + "_Test Purchase Receipt Serial Item for Rejected Qty", + properties={"has_serial_no": 1, "is_stock_item": 1}, + ).name + + rej_warehouse = create_warehouse("_Test Purchase Warehouse For Rejected Qty") + + batch_no = "BATCH-BNU-TPRBI-0001" + serial_nos = ["SNU-TPRSI-0001", "SNU-TPRSI-0002", "SNU-TPRSI-0003"] + + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": batch_item, + } + ).insert() + + for serial_no in serial_nos: + if not frappe.db.exists("Serial No", serial_no): + frappe.get_doc( + { + "doctype": "Serial No", + "item_code": serial_item, + "serial_no": serial_no, + } + ).insert() + + pr = make_purchase_receipt( + item_code=batch_item, + received_qty=10, + qty=8, + rejected_qty=2, + rejected_warehouse=rej_warehouse, + use_serial_batch_fields=1, + batch_no=batch_no, + rate=100, + do_not_submit=1, + ) + + pr.append( + "items", + { + "item_code": serial_item, + "qty": 2, + "rate": 100, + "base_rate": 100, + "item_name": serial_item, + "uom": "Nos", + "stock_uom": "Nos", + "conversion_factor": 1, + "rejected_qty": 1, + "warehouse": pr.items[0].warehouse, + "rejected_warehouse": rej_warehouse, + "use_serial_batch_fields": 1, + "serial_no": "\n".join(serial_nos[:2]), + "rejected_serial_no": serial_nos[2], + }, + ) + + pr.save() + pr.submit() + + pr.reload() + + for row in pr.items: + self.assertTrue(row.serial_and_batch_bundle) + self.assertTrue(row.rejected_serial_and_batch_bundle) + + if row.item_code == batch_item: + self.assertEqual(row.batch_no, batch_no) + else: + self.assertEqual(row.serial_no, "\n".join(serial_nos[:2])) + self.assertEqual(row.rejected_serial_no, serial_nos[2]) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 90c41f8e3f2..22113d294da 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -893,6 +893,9 @@ class update_entries_after(object): query.run() def calculate_valuation_for_serial_batch_bundle(self, sle): + if not frappe.db.exists("Serial and Batch Bundle", sle.serial_and_batch_bundle): + return + doc = frappe.get_cached_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle) doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 4738a700408..3e74ac91f4b 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -1132,6 +1132,86 @@ class TestSubcontractingReceipt(FrappeTestCase): scr.reload() self.assertTrue(scr.items[0].serial_and_batch_bundle) + def test_use_serial_batch_fields_for_subcontracting_receipt_with_rejected_qty(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + fg_item = make_item( + "Test Subcontracted Item With Batch No for Rejected Qty", + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-REJ-BNGS-.####", + "is_sub_contracted_item": 1, + }, + ).name + + make_item( + "Test Subcontracted Item With Batch No Service Item 2", + properties={"is_stock_item": 0}, + ) + + make_bom( + item=fg_item, + raw_materials=[ + make_item( + "Test Subcontracted Item With Batch No RM Item 2", + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BATCH-REJ-RM-BNGS-.####", + }, + ).name + ], + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Test Subcontracted Item With Batch No Service Item 2", + "qty": 10, + "rate": 100, + "fg_item": fg_item, + "fg_item_qty": 10, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + batch_no = "BATCH-REJ-BNGS-0001" + if not frappe.db.exists("Batch", batch_no): + frappe.get_doc( + { + "doctype": "Batch", + "batch_id": batch_no, + "item": fg_item, + } + ).insert() + + rej_warehouse = create_warehouse("_Test Subcontract Warehouse For Rejected Qty") + + scr = make_subcontracting_receipt(sco.name) + self.assertFalse(scr.items[0].serial_and_batch_bundle) + scr.items[0].use_serial_batch_fields = 1 + scr.items[0].batch_no = batch_no + scr.items[0].received_qty = 10 + scr.items[0].rejected_qty = 2 + scr.items[0].qty = 8 + scr.items[0].rejected_warehouse = rej_warehouse + + scr.save() + scr.submit() + scr.reload() + self.assertTrue(scr.items[0].serial_and_batch_bundle) + self.assertTrue(scr.items[0].rejected_serial_and_batch_bundle) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index f9e0a0b591c..ca14f09c9c5 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -335,8 +335,7 @@ "fieldtype": "Small Text", "label": "Rejected Serial No", "no_copy": 1, - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "subcontracting_order_item", @@ -569,7 +568,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-02-04 16:23:30.374865", + "modified": "2024-03-07 11:43:38.954262", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", From 3035dcab0741cfbf8d7806dcf64f887e5e577df0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 7 Mar 2024 09:25:12 +0000 Subject: [PATCH 19/19] chore(release): Bumped to Version 15.16.2 ## [15.16.2](https://github.com/frappe/erpnext/compare/v15.16.1...v15.16.2) (2024-03-07) ### Bug Fixes * use serial/batch field for rejected items (backport [#40327](https://github.com/frappe/erpnext/issues/40327)) (backport [#40329](https://github.com/frappe/erpnext/issues/40329)) ([#40332](https://github.com/frappe/erpnext/issues/40332)) ([5af335d](https://github.com/frappe/erpnext/commit/5af335dc333def266da4533f6102924254b0d68a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ea146f78849..dd1e7ef8703 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "15.16.1" +__version__ = "15.16.2" def get_default_company(user=None):