From 106671a414436fe162d492e7619bcf9ff34236d1 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 08:35:48 +0100 Subject: [PATCH 01/43] fix: add strings for translation in timesheet.js (#44496) (cherry picked from commit 6585fabdb161513d4c4e3799d44780dba58fc905) --- erpnext/projects/doctype/timesheet/timesheet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 9aba75b3ce92..168b891e98c3 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -58,10 +58,10 @@ frappe.ui.form.on("Timesheet", { } if (frm.doc.docstatus < 1) { - let button = "Start Timer"; + let button = __("Start Timer"); $.each(frm.doc.time_logs || [], function (i, row) { if (row.from_time <= frappe.datetime.now_datetime() && !row.completed) { - button = "Resume Timer"; + button = __("Resume Timer"); } }); From 539c5b7974ffdaf4caf8acb6d4acc00fba626668 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:02:26 +0530 Subject: [PATCH 02/43] fix: duplicate required items in the CSV (backport #44498) (#44507) * fix: duplicate required items in the CSV (#44498) (cherry picked from commit b4534e56e4a1b62f6433b031671d62be9c28cf6a) # Conflicts: # erpnext/manufacturing/doctype/production_plan/production_plan.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.json | 8 ++-- .../production_plan/production_plan.py | 47 +++++++++++-------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 84bbad58c389..22971d4debdf 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -243,7 +243,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "download_materials_required", "fieldtype": "Button", - "label": "Download Materials Request Plan" + "label": "Download Required Materials" }, { "fieldname": "get_items_for_mr", @@ -398,7 +398,7 @@ "collapsible": 1, "fieldname": "download_materials_request_plan_section_section", "fieldtype": "Section Break", - "label": "Download Materials Request Plan Section" + "label": "Preview Required Materials" }, { "default": "0", @@ -439,7 +439,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-27 13:34:20.692211", + "modified": "2024-12-04 11:55:03.108971", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", @@ -463,4 +463,4 @@ "sort_field": "modified", "sort_order": "ASC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 3f82a75d3020..265f99e47d3c 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -44,9 +44,7 @@ class ProductionPlan(Document): from erpnext.manufacturing.doctype.material_request_plan_item.material_request_plan_item import ( MaterialRequestPlanItem, ) - from erpnext.manufacturing.doctype.production_plan_item.production_plan_item import ( - ProductionPlanItem, - ) + from erpnext.manufacturing.doctype.production_plan_item.production_plan_item import ProductionPlanItem from erpnext.manufacturing.doctype.production_plan_item_reference.production_plan_item_reference import ( ProductionPlanItemReference, ) @@ -1085,24 +1083,33 @@ def download_raw_materials(doc, warehouses=None): frappe.flags.show_qty_in_stock_uom = 1 items = get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True) + duplicate_item_wh_list = frappe._dict() + for d in items: - item_list.append( - [ - d.get("item_code"), - d.get("item_name"), - d.get("description"), - d.get("stock_uom"), - d.get("warehouse"), - d.get("required_bom_qty"), - d.get("projected_qty"), - d.get("actual_qty"), - d.get("ordered_qty"), - d.get("planned_qty"), - d.get("reserved_qty_for_production"), - d.get("safety_stock"), - d.get("quantity"), - ] - ) + key = (d.get("item_code"), d.get("warehouse")) + if key in duplicate_item_wh_list: + rm_data = duplicate_item_wh_list[key] + rm_data[12] += d.get("quantity") + continue + + rm_data = [ + d.get("item_code"), + d.get("item_name"), + d.get("description"), + d.get("stock_uom"), + d.get("warehouse"), + d.get("required_bom_qty"), + d.get("projected_qty"), + d.get("actual_qty"), + d.get("ordered_qty"), + d.get("planned_qty"), + d.get("reserved_qty_for_production"), + d.get("safety_stock"), + d.get("quantity"), + ] + + duplicate_item_wh_list[key] = rm_data + item_list.append(rm_data) if not doc.get("for_warehouse"): row = {"item_code": d.get("item_code")} From 07df87ab6972ed3bb23b8ce8029a787a367655cf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 19:02:45 +0530 Subject: [PATCH 03/43] fix: required by date in the reorder material request (backport #44497) (#44509) fix: required by date in the reorder material request (#44497) (cherry picked from commit 4001166ecc689ee877adf1d9babd4d0332ab4752) Co-authored-by: rohitwaghchaure --- .../doctype/stock_entry/test_stock_entry.py | 40 +++++++++++++++++++ erpnext/stock/reorder_item.py | 2 + 2 files changed, 42 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 469b865dd591..a26940462bf1 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1645,6 +1645,46 @@ def test_auto_reorder_level(self): mr.cancel() mr.delete() + def test_auto_reorder_level_with_lead_time_days(self): + from erpnext.stock.reorder_item import reorder_item + + item_doc = make_item( + "Test Auto Reorder Item - 002", + properties={"stock_uom": "Kg", "purchase_uom": "Nos", "is_stock_item": 1, "lead_time_days": 2}, + uoms=[{"uom": "Nos", "conversion_factor": 5}], + ) + + if not frappe.db.exists("Item Reorder", {"parent": item_doc.name}): + item_doc.append( + "reorder_levels", + { + "warehouse_reorder_level": 0, + "warehouse_reorder_qty": 10, + "warehouse": "_Test Warehouse - _TC", + "material_request_type": "Purchase", + }, + ) + + item_doc.save(ignore_permissions=True) + + frappe.db.set_single_value("Stock Settings", "auto_indent", 1) + + mr_list = reorder_item() + + frappe.db.set_single_value("Stock Settings", "auto_indent", 0) + mrs = frappe.get_all( + "Material Request Item", + fields=["schedule_date"], + filters={"item_code": item_doc.name, "uom": "Nos"}, + ) + + for mri in mrs: + self.assertEqual(getdate(mri.schedule_date), getdate(add_days(today(), 2))) + + for mr in mr_list: + mr.cancel() + mr.delete() + def test_use_serial_and_batch_fields(self): item = make_item( "Test Use Serial and Batch Item SN Item", diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index ed87906731e4..570dc3a34050 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -98,6 +98,7 @@ def add_to_material_request(**kwargs): "description": d.description, "stock_uom": d.stock_uom, "purchase_uom": d.purchase_uom, + "lead_time_days": d.lead_time_days, } ), ) @@ -129,6 +130,7 @@ def get_items_for_reorder() -> dict[str, list]: item_table.brand, item_table.variant_of, item_table.has_variants, + item_table.lead_time_days, ) .where( (item_table.disabled == 0) From 8733eda576fe6d27ea64dfb43ee52c2f1215b234 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:45:17 +0530 Subject: [PATCH 04/43] fix: inv dimensions fields not creating for standard doctype (backport #44504) (#44515) fix: inv dimensions fields not creating for standard doctype (#44504) (cherry picked from commit 353610ce619d8406c8bd613807cf69c76274dfe0) Co-authored-by: rohitwaghchaure --- .../inventory_dimension/inventory_dimension.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 4f8a166932d6..661605bdf5f0 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -214,13 +214,10 @@ def add_custom_fields(self): dimension_fields = [] if self.apply_to_all_doctypes: for doctype in get_inventory_documents(): - if field_exists(doctype[0], self.source_fieldname): - continue - dimension_fields = self.get_dimension_fields(doctype[0]) self.add_transfer_field(doctype[0], dimension_fields) custom_fields.setdefault(doctype[0], dimension_fields) - elif not field_exists(self.document_type, self.source_fieldname): + else: dimension_fields = self.get_dimension_fields() self.add_transfer_field(self.document_type, dimension_fields) @@ -239,8 +236,17 @@ def add_custom_fields(self): dimension_field["fieldname"] = self.target_fieldname custom_fields["Stock Ledger Entry"] = dimension_field + filter_custom_fields = {} if custom_fields: - create_custom_fields(custom_fields) + for doctype, fields in custom_fields.items(): + if isinstance(fields, dict): + fields = [fields] + + for field in fields: + if not field_exists(doctype, field["fieldname"]): + filter_custom_fields.setdefault(doctype, []).append(field) + + create_custom_fields(filter_custom_fields) def add_transfer_field(self, doctype, dimension_fields): if doctype not in [ From 283043eba7d305bd88d64fe5780a1b65ab3725c2 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 08:58:16 +0100 Subject: [PATCH 05/43] fix: add labels for translation in sales_order.js (cherry picked from commit d544328ffe629ed1dfb22a593db4044de8e30131) --- erpnext/selling/doctype/sales_order/sales_order.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 45f6b3647613..de4053458e47 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -146,7 +146,7 @@ frappe.ui.form.on("Sales Order", { target: frm, setters: [ { - label: "Supplier", + label: __("Supplier"), fieldname: "supplier", fieldtype: "Link", options: "Supplier", @@ -783,7 +783,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex target: me.frm, setters: [ { - label: "Customer", + label: __("Customer"), fieldname: "party_name", fieldtype: "Link", options: "Customer", @@ -838,7 +838,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } else { const fields = [ { - label: "Items", + label: __("Items"), fieldtype: "Table", fieldname: "items", description: __("Select BOM and Qty for Production"), @@ -1193,7 +1193,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex { fieldname: "items_for_po", fieldtype: "Table", - label: "Select Items", + label: __("Select Items"), fields: [ { fieldtype: "Data", From af97d3e341c903c4e1505e00edf36d465a4157a6 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 05:29:43 +0100 Subject: [PATCH 06/43] refactor: translatable label on pos payments (#42081) * Use better description in pos_payment.js Use Change Amount instead of Change and To Be Paid in pos_payment.js and be consistent with other strings * change_amount_pos_payment.js (cherry picked from commit 138ffc4e93f8d63f6784d4c48ccb16e825144d8f) --- erpnext/selling/page/point_of_sale/pos_payment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 9f389dfa81f8..bea1918fa201 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -589,7 +589,7 @@ erpnext.PointOfSale.Payment = class { const remaining = grand_total - doc.paid_amount; const change = doc.change_amount || remaining <= 0 ? -1 * remaining : undefined; const currency = doc.currency; - const label = change ? __("Change") : __("To Be Paid"); + const label = __("Change Amount"); this.$totals.html( `
From ceec5fdb6c094928cb566973547ebe571d655f51 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 09:07:09 +0100 Subject: [PATCH 07/43] fix: strings for translation in pos_past_order_summary.js (cherry picked from commit 23c846d4b92dbc121255c8dc98d11de7536e3e4e) --- .../selling/page/point_of_sale/pos_past_order_summary.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index c399005643c3..4a2d8911d1a8 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -46,7 +46,7 @@ erpnext.PointOfSale.PastOrderSummary = class { init_email_print_dialog() { const email_dialog = new frappe.ui.Dialog({ - title: "Email Receipt", + title: __("Email Receipt"), fields: [ { fieldname: "email_id", fieldtype: "Data", options: "Email", label: "Email ID", reqd: 1 }, { fieldname: "content", fieldtype: "Small Text", label: "Message (if any)" }, @@ -59,7 +59,7 @@ erpnext.PointOfSale.PastOrderSummary = class { this.email_dialog = email_dialog; const print_dialog = new frappe.ui.Dialog({ - title: "Print Receipt", + title: __("Print Receipt"), fields: [{ fieldname: "print", fieldtype: "Data", label: "Print Preview" }], primary_action: () => { this.print_receipt(); @@ -112,7 +112,7 @@ erpnext.PointOfSale.PastOrderSummary = class { get_discount_html(doc) { if (doc.discount_amount) { return `
-
Discount (${doc.additional_discount_percentage} %)
+
${__("Discount")} (${doc.additional_discount_percentage} %)
${format_currency(doc.discount_amount, doc.currency)}
`; } else { From ffdd7cfa8678ecb4880eb8c68d260673e3cbbc5d Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:40:55 +0100 Subject: [PATCH 08/43] fix: add label strings for translation in pos_controller.js (cherry picked from commit bd77a5557d9b3bac54122cf43d56050bc1ecba5e) --- erpnext/selling/page/point_of_sale/pos_controller.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index de3259ae2b74..c1127c39cc04 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -30,7 +30,7 @@ erpnext.PointOfSale.Controller = class { fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, - label: "Mode of Payment", + label: __("Mode of Payment"), options: "Mode of Payment", reqd: 1, }, @@ -38,7 +38,7 @@ erpnext.PointOfSale.Controller = class { fieldname: "opening_amount", fieldtype: "Currency", in_list_view: 1, - label: "Opening Amount", + label: __("Opening Amount"), options: "company:company_currency", change: function () { dialog.fields_dict.balance_details.df.data.some((d) => { @@ -87,7 +87,7 @@ erpnext.PointOfSale.Controller = class { { fieldname: "balance_details", fieldtype: "Table", - label: "Opening Balance Details", + label: __("Opening Balance Details"), cannot_add_rows: false, in_place_edit: true, reqd: 1, From 71d8dfb401a8a3f533e5e06eedb27cc9a1ae8d27 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:12:34 +0100 Subject: [PATCH 09/43] fix: add strings for translation in pos_item_cart.js (cherry picked from commit 4b72b60f1a75657c444f210fcb3dd7a055e4d9b8) --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index b808b4f8828d..4ed678e2db06 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -966,13 +966,13 @@ erpnext.PointOfSale.ItemCart = class { if (!res.length) { transaction_container.html( - `
No recent transactions found
` + `
${__("No recent transactions found")}
` ); return; } const elapsed_time = moment(res[0].posting_date + " " + res[0].posting_time).fromNow(); - this.$customer_section.find(".customer-desc").html(`Last transacted ${elapsed_time}`); + this.$customer_section.find(".customer-desc").html(`${__("Last transacted")} ${__(elapsed_time)}`); res.forEach((invoice) => { const posting_datetime = moment(invoice.posting_date + " " + invoice.posting_time).format( @@ -997,7 +997,7 @@ erpnext.PointOfSale.ItemCart = class {
- ${invoice.status} + ${__(invoice.status)}
From d540c99934c5594f16a4e811ea88b0d3a6eca259 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 5 Dec 2024 10:10:34 +0530 Subject: [PATCH 10/43] chore: linter fix (cherry picked from commit 31efaf6dbf8f0139a4dd291ae8abb3e27bdc91ac) --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 4ed678e2db06..6342b237f6e0 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -972,7 +972,9 @@ erpnext.PointOfSale.ItemCart = class { } const elapsed_time = moment(res[0].posting_date + " " + res[0].posting_time).fromNow(); - this.$customer_section.find(".customer-desc").html(`${__("Last transacted")} ${__(elapsed_time)}`); + this.$customer_section + .find(".customer-desc") + .html(`${__("Last transacted")} ${__(elapsed_time)}`); res.forEach((invoice) => { const posting_datetime = moment(invoice.posting_date + " " + invoice.posting_time).format( From f4aa4c94668a95a04b5a36dde582474df0f0be7b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:51:25 +0530 Subject: [PATCH 11/43] fix: incorrect stock UOM for BOM raw materials (backport #44528) (#44531) fix: incorrect stock UOM for BOM raw materials (#44528) fix: incorrect stock uom for BOM raw materials (cherry picked from commit 5413cf9f1fae48c4af47f5790ef7b5e8d81ebaf7) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 18 +++++++++++++++++ erpnext/manufacturing/doctype/bom/test_bom.py | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c3105b8e9eca..0c0874709015 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -260,6 +260,24 @@ def validate(self): self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False) self.set_process_loss_qty() self.validate_scrap_items() + self.set_default_uom() + + def set_default_uom(self): + if not self.get("items"): + return + + item_wise_uom = frappe._dict( + frappe.get_all( + "Item", + filters={"name": ("in", [item.item_code for item in self.items])}, + fields=["name", "stock_uom"], + as_list=1, + ) + ) + + for row in self.get("items"): + if row.stock_uom != item_wise_uom.get(row.item_code): + row.stock_uom = item_wise_uom.get(row.item_code) def get_context(self, context): context.parents = [{"name": "boms", "title": _("All BOMs")}] diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index d02b51ca6e7a..396a0b107d5d 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -755,6 +755,26 @@ def test_do_not_include_manufacturing_and_fixed_items(self): self.assertTrue("_Test RM Item 2 Fixed Asset Item" not in items) self.assertTrue("_Test RM Item 3 Manufacture Item" in items) + def test_bom_raw_materials_stock_uom(self): + rm_item = make_item( + properties={"is_stock_item": 1, "valuation_rate": 1000.0, "stock_uom": "Nos"} + ).name + fg_item = make_item(properties={"is_stock_item": 1}).name + + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + bom = make_bom(item=fg_item, raw_materials=[rm_item], do_not_submit=True) + for row in bom.items: + self.assertEqual(row.stock_uom, "Nos") + + frappe.db.set_value("Item", rm_item, "stock_uom", "Kg") + + bom.items[0].qty = 2 + bom.save() + + for row in bom.items: + self.assertEqual(row.stock_uom, "Kg") + def get_default_bom(item_code="_Test FG Item 2"): return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) From 3771c6eeae15644e459181128f41ebd04a8d0c19 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:46:23 +0530 Subject: [PATCH 12/43] fix: consider zero valuation rate for serial nos (backport #44532) (#44534) fix: consider zero valuation rate for serial nos (#44532) (cherry picked from commit 14f2b0ab0e5530395e269213a8594733dca4974a) Co-authored-by: rohitwaghchaure --- erpnext/stock/serial_batch_bundle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index dd459bb30bc4..05b3536c57ba 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -508,7 +508,7 @@ def calculate_stock_value_change(self): serial_nos = self.get_serial_nos() for serial_no in serial_nos: incoming_rate = self.get_incoming_rate_from_bundle(serial_no) - if not incoming_rate: + if incoming_rate is None: continue self.stock_value_change += incoming_rate @@ -553,7 +553,7 @@ def get_incoming_rate_from_bundle(self, serial_no) -> float: query = query.where(timestamp_condition) incoming_rate = query.run() - return flt(incoming_rate[0][0]) if incoming_rate else 0.0 + return flt(incoming_rate[0][0]) if incoming_rate else None def get_serial_nos(self): if self.sle.get("serial_nos"): From 843ff18cf9e45cc88042eb328b30907e888628c8 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:37:43 +0100 Subject: [PATCH 13/43] fix: add title for translation in asset.js (cherry picked from commit 61439132a4386c1867a08d141e0702e03484cf02) --- erpnext/assets/doctype/asset/asset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index c6e76abb0b59..21e307b480cf 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -416,7 +416,7 @@ frappe.ui.form.on("Asset", { } frm.dashboard.render_graph({ - title: "Asset Value", + title: __("Asset Value"), data: { labels: x_intervals, datasets: [ From 99a327155e4a13e5ea8e0ad188a1acd38f90efed Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:51:44 +0530 Subject: [PATCH 14/43] fix: update qty in SABB if qty changed in stock reco (backport #44542) (#44546) fix: update qty in SABB if qty changed in stock reco (#44542) (cherry picked from commit 7249cf0001bebe7a33933f9d379065f9cb8d7d61) Co-authored-by: rohitwaghchaure --- .../stock_reconciliation.py | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index f671c11712a4..c13b3620517f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -166,6 +166,24 @@ def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False if not frappe.db.exists("Item", item.item_code): frappe.throw(_("Item {0} does not exist").format(item.item_code)) + item_details = frappe.get_cached_value( + "Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 + ) + + if not (item_details.has_serial_no or item_details.has_batch_no): + continue + + if ( + not item.use_serial_batch_fields + and not item.reconcile_all_serial_batch + and not item.serial_and_batch_bundle + ): + frappe.throw( + _("Row # {0}: Please add Serial and Batch Bundle for Item {1}").format( + item.idx, frappe.bold(item.item_code) + ) + ) + if not item.reconcile_all_serial_batch and item.serial_and_batch_bundle: bundle = self.get_bundle_for_specific_serial_batch(item) item.current_serial_and_batch_bundle = bundle.name @@ -181,13 +199,6 @@ def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False if voucher_detail_no and voucher_detail_no != item.name: continue - item_details = frappe.get_cached_value( - "Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 - ) - - if not (item_details.has_serial_no or item_details.has_batch_no): - continue - if not item.current_serial_and_batch_bundle: serial_and_batch_bundle = frappe.get_doc( { @@ -400,6 +411,28 @@ def set_new_serial_and_batch_bundle(self): item.qty = bundle_doc.total_qty item.valuation_rate = bundle_doc.avg_rate + elif item.serial_and_batch_bundle and item.qty: + self.update_existing_serial_and_batch_bundle(item) + + def update_existing_serial_and_batch_bundle(self, item): + batch_details = frappe.get_all( + "Serial and Batch Entry", + fields=["batch_no", "qty", "name"], + filters={"parent": item.serial_and_batch_bundle, "batch_no": ("is", "set")}, + ) + + if batch_details and len(batch_details) == 1: + batch = batch_details[0] + if abs(batch.qty) == abs(item.qty): + return + + update_values = { + "qty": item.qty, + "stock_value_difference": flt(item.valuation_rate) * flt(item.qty), + } + + frappe.db.set_value("Serial and Batch Entry", batch.name, update_values) + def remove_items_with_no_change(self): """Remove items if qty or rate is not changed""" self.difference_amount = 0.0 From 5ef063c63471092f568ee5fdead5ae0f2f52a74d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:52:04 +0530 Subject: [PATCH 15/43] fix: not able to make PO for returned qty from material request (backport #44540) (#44547) fix: not able to make PO for returned qty from material request (#44540) (cherry picked from commit 024c44208786dc3da59f6cdabba125ceb6f7c8d6) Co-authored-by: rohitwaghchaure --- .../doctype/material_request/material_request.js | 16 ++++++++-------- .../doctype/material_request/material_request.py | 8 ++++++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index f04acc85ed54..c7485470cf07 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -107,6 +107,14 @@ frappe.ui.form.on("Material Request", { if (flt(frm.doc.per_received, precision) < 100) { frm.add_custom_button(__("Stop"), () => frm.events.update_status(frm, "Stopped")); + + if (frm.doc.material_request_type === "Purchase") { + frm.add_custom_button( + __("Purchase Order"), + () => frm.events.make_purchase_order(frm), + __("Create") + ); + } } if (flt(frm.doc.per_ordered, precision) < 100) { @@ -149,14 +157,6 @@ frappe.ui.form.on("Material Request", { ); } - if (frm.doc.material_request_type === "Purchase") { - frm.add_custom_button( - __("Purchase Order"), - () => frm.events.make_purchase_order(frm), - __("Create") - ); - } - if (frm.doc.material_request_type === "Purchase") { frm.add_custom_button( __("Request for Quotation"), diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 7bf3ca4d7285..23d289170dba 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -378,7 +378,9 @@ def set_missing_values(source, target_doc): def update_item(obj, target, source_parent): target.conversion_factor = obj.conversion_factor - target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty)) / target.conversion_factor + + qty = obj.received_qty or obj.ordered_qty + target.qty = flt(flt(obj.stock_qty) - flt(qty)) / target.conversion_factor target.stock_qty = target.qty * target.conversion_factor if getdate(target.schedule_date) < getdate(nowdate()): target.schedule_date = None @@ -430,7 +432,9 @@ def select_item(d): filtered_items = args.get("filtered_children", []) child_filter = d.name in filtered_items if filtered_items else True - return d.ordered_qty < d.stock_qty and child_filter + qty = d.received_qty or d.ordered_qty + + return qty < d.stock_qty and child_filter doclist = get_mapped_doc( "Material Request", From 63b1df38a81877168ea71047c9b29db144c4d8a9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:01:04 +0530 Subject: [PATCH 16/43] fix: variant qty while making work order from BOM (backport #44548) (#44551) fix: variant qty while making work order from BOM (#44548) (cherry picked from commit 1571dff3ef4c89c91e155967c0a187e8f855fe28) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 6267ee4d029d..5cfd170ab131 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -364,7 +364,7 @@ frappe.ui.form.on("BOM", { dialog.fields_dict.items.df.data.push({ item_code: d.item_code, variant_item_code: "", - qty: d.qty, + qty: (d.qty / frm.doc.quantity) * (dialog.fields_dict.qty.value || 1), source_warehouse: d.source_warehouse, operation: d.operation, }); From 06ac21dd856a5b6ecfb37ccf20c3d83fa4d79c05 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 22:57:09 +0530 Subject: [PATCH 17/43] chore: Ignore stock validation for non stock invoices (backport #44549) (#44554) chore: Ignore stock validation for non stock invoices (#44549) * chore: Ignore stock validation for non stock invoices * chore: Ignore stock validation for non stock invoices (cherry picked from commit 1ac292285e217c77d2c9f9ce37e4c018c2f5e849) Co-authored-by: Deepesh Garg --- erpnext/controllers/sales_and_purchase_return.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 7f9a5a35a730..696d404d16da 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -24,6 +24,10 @@ def validate_return(doc): if doc.return_against: validate_return_against(doc) + + if doc.doctype in ("Sales Invoice", "Purchase Invoice") and not doc.update_stock: + return + validate_returned_items(doc) From 6980d1e142d25a3151ea5b12625a9f6e62e39352 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:40:00 +0100 Subject: [PATCH 18/43] fix: add labels for translation in sales_order_analysis.py (cherry picked from commit 8a554a55382cf41855a0e22af89a55c8ec79a5e7) --- .../selling/report/sales_order_analysis/sales_order_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index 70b021a9cab1..8fcf29bd7a6c 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -206,7 +206,7 @@ def prepare_data(data, so_elapsed_time, filters): def prepare_chart_data(pending, completed): - labels = ["Amount to Bill", "Billed Amount"] + labels = [_("Amount to Bill"), _("Billed Amount")] return { "data": {"labels": labels, "datasets": [{"values": [pending, completed]}]}, From 773e03f84f9514b2d7558be14ea257b9596d4474 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:20:31 +0100 Subject: [PATCH 19/43] fix: add strings for translation payment_terms_status_for_sales_order.py (cherry picked from commit 7d244051c8dd231abbd3bcd9717d95f7046ab3fc) --- .../payment_terms_status_for_sales_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py index cf61a0e35f3b..1b57a6d7390e 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py @@ -270,11 +270,11 @@ def prepare_chart(s_orders): "labels": [term.payment_term for term in s_orders], "datasets": [ { - "name": "Payment Amount", + "name": _("Payment Amount"), "values": [x.base_payment_amount for x in s_orders], }, { - "name": "Paid Amount", + "name": _("Paid Amount"), "values": [x.paid_amount for x in s_orders], }, ], From 43fc8bf74e758ceb8c0a7170fff6fdc3c7ce0b9f Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:13:07 +0100 Subject: [PATCH 20/43] fix: add string for translation in delayed_tasks_summary.py (cherry picked from commit 84b54f549abc28b5b776c2833977044aa876fd34) --- .../report/delayed_tasks_summary/delayed_tasks_summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py index 766e40e319c5..dc3da2596622 100644 --- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py +++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py @@ -77,7 +77,7 @@ def get_chart_data(data): charts = { "data": { "labels": [_("On Track"), _("Delayed")], - "datasets": [{"name": "Delayed", "values": [on_track, delay]}], + "datasets": [{"name": _("Delayed"), "values": [on_track, delay]}], }, "type": "percentage", "colors": ["#84D5BA", "#CB4B5F"], From dd3a747af461a01b4fdafb877320460a85a864fe Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:17:35 +0100 Subject: [PATCH 21/43] fix: add labels for translation in quality_inspection_summary.py (cherry picked from commit 6ff4704345fcf299f959f61f05341da76680db69) --- .../quality_inspection_summary/quality_inspection_summary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py index 38e05852ee8d..7e0fcf14cc63 100644 --- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py @@ -42,7 +42,7 @@ def get_data(filters): def get_chart_data(periodic_data, columns): - labels = ["Rejected", "Accepted"] + labels = [_("Rejected"), _("Accepted")] status_wise_data = {"Accepted": 0, "Rejected": 0} @@ -53,7 +53,7 @@ def get_chart_data(periodic_data, columns): datasets.append( { - "name": "Qty Wise Chart", + "name": _("Qty Wise Chart"), "values": [status_wise_data.get("Rejected"), status_wise_data.get("Accepted")], } ) From 7a6cb0f157adb8a0e582e2d2c6806a2acc2e8a32 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:02:05 +0100 Subject: [PATCH 22/43] fix: add labels for translation in production_analytics.py (cherry picked from commit 9b09116576f7645b9bdfc4e8be3c1c90c7516935) --- .../production_analytics/production_analytics.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index c02c1e6fcd38..dc2b9ad62f36 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -131,11 +131,11 @@ def get_chart_data(periodic_data, columns): pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({"name": "All Work Orders", "values": all_data}) - datasets.append({"name": "Not Started", "values": not_start}) - datasets.append({"name": "Overdue", "values": overdue}) - datasets.append({"name": "Pending", "values": pending}) - datasets.append({"name": "Completed", "values": completed}) + datasets.append({"name": _("All Work Orders"), "values": all_data}) + datasets.append({"name": _("Not Started"), "values": not_start}) + datasets.append({"name": _("Overdue"), "values": overdue}) + datasets.append({"name": _("Pending"), "values": pending}) + datasets.append({"name": _("Completed"), "values": completed}) chart = {"data": {"labels": labels, "datasets": datasets}} chart["type"] = "line" From f06827c6fbdbbd74cf3c93a843a206e384cfdf5b Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:41:02 +0100 Subject: [PATCH 23/43] fix: add labels for translation in purchase_order_analysis.py (cherry picked from commit 342a398bec707688749c79b515462b5ea4bde734) --- .../report/purchase_order_analysis/purchase_order_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 1a250acd4d20..6d2034d18789 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -180,7 +180,7 @@ def prepare_data(data, filters): def prepare_chart_data(pending, completed): - labels = ["Amount to Bill", "Billed Amount"] + labels = [_("Amount to Bill"), _("Billed Amount")] return { "data": {"labels": labels, "datasets": [{"values": [pending, completed]}]}, From 31c2b818bdaf1e7b1dae39a045d8b2fdbd00d4e6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:57:48 +0100 Subject: [PATCH 24/43] fix(Bank Transaction): error in party matching should not block submitting (backport #44416) (#44574) fix(Bank Transaction): error in party matching should not block submitting (#44416) (cherry picked from commit 72256565bb2caf02f8e87b3ee0fbd0ee233c4f40) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../bank_transaction/bank_transaction.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 4354f238a422..c13dbe445f18 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -208,13 +208,17 @@ def auto_set_party(self): if self.party_type and self.party: return - result = AutoMatchParty( - bank_party_account_number=self.bank_party_account_number, - bank_party_iban=self.bank_party_iban, - bank_party_name=self.bank_party_name, - description=self.description, - deposit=self.deposit, - ).match() + result = None + try: + result = AutoMatchParty( + bank_party_account_number=self.bank_party_account_number, + bank_party_iban=self.bank_party_iban, + bank_party_name=self.bank_party_name, + description=self.description, + deposit=self.deposit, + ).match() + except Exception: + frappe.log_error(title=_("Error in party matching for Bank Transaction {0}").format(self.name)) if not result: return From 03ae9e27be5acca32d3e15f8434a3f6062dc8c30 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:50:49 +0530 Subject: [PATCH 25/43] fix: BOM name issue (backport #44575) (#44579) fix: BOM name issue (#44575) fix: bom name issue (cherry picked from commit b7a3c6b6ca902138007daff2343abe14f91157f7) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 0c0874709015..2f48a498ec38 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -176,9 +176,14 @@ def autoname(self): search_key = f"{self.doctype}-{self.item}%" existing_boms = frappe.get_all( - "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + "BOM", filters={"name": search_key, "amended_from": ["is", "not set"]}, pluck="name" ) + if not existing_boms: + existing_boms = frappe.get_all( + "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + ) + if existing_boms: index = self.get_next_version_index(existing_boms) else: From 0b268279cfbe720917bd73ff952771e1eaae05b4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:35:37 +0530 Subject: [PATCH 26/43] fix: BOM for variant items (backport #44580) (#44584) fix: BOM for variant items (#44580) (cherry picked from commit 93e9517f5d6b023cba91d93cb77340691dd24ac0) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 56 ++++++++++++------- .../doctype/work_order/work_order.py | 18 +++++- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 5cfd170ab131..d8024a428a34 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -182,25 +182,30 @@ frappe.ui.form.on("BOM", { }, make_work_order(frm) { - frm.events.setup_variant_prompt(frm, "Work Order", (frm, item, data, variant_items) => { - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", - args: { - bom_no: frm.doc.name, - item: item, - qty: data.qty || 0.0, - project: frm.doc.project, - variant_items: variant_items, - }, - freeze: true, - callback(r) { - if (r.message) { - let doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - }, - }); - }); + frm.events.setup_variant_prompt( + frm, + "Work Order", + (frm, item, data, variant_items, use_multi_level_bom) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", + args: { + bom_no: frm.doc.name, + item: item, + qty: data.qty || 0.0, + project: frm.doc.project, + variant_items: variant_items, + use_multi_level_bom: use_multi_level_bom, + }, + freeze: true, + callback(r) { + if (r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + }, + }); + } + ); }, make_variant_bom(frm) { @@ -248,6 +253,13 @@ frappe.ui.form.on("BOM", { }; }, }); + + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); } if (!skip_qty_field) { @@ -285,6 +297,7 @@ frappe.ui.form.on("BOM", { fieldname: "items", fieldtype: "Table", label: __("Raw Materials"), + depends_on: "eval:!doc.use_multi_level_bom", fields: [ { fieldname: "item_code", @@ -347,14 +360,15 @@ frappe.ui.form.on("BOM", { (data) => { let item = data.item || frm.doc.item; let variant_items = data.items || []; + let use_multi_level_bom = data.use_multi_level_bom || 0; variant_items.forEach((d) => { - if (!d.variant_item_code) { + if (!d.variant_item_code && !use_multi_level_bom) { frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); } }); - callback(frm, item, data, variant_items); + callback(frm, item, data, variant_items, use_multi_level_bom); }, __(title), __("Create") diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 1d0df26800df..e5219d7cb3e7 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1299,7 +1299,7 @@ def get_item_details(item, project=None, skip_bom_info=False, throw=True): @frappe.whitelist() -def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): +def make_work_order(bom_no, item, qty=0, project=None, variant_items=None, use_multi_level_bom=None): if not frappe.has_permission("Work Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -1309,12 +1309,13 @@ def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): wo_doc.production_item = item wo_doc.update(item_details) wo_doc.bom_no = bom_no + wo_doc.use_multi_level_bom = cint(use_multi_level_bom) if flt(qty) > 0: wo_doc.qty = flt(qty) wo_doc.get_items_and_operations_from_bom() - if variant_items: + if variant_items and not wo_doc.use_multi_level_bom: add_variant_item(variant_items, wo_doc, bom_no, "required_items") return wo_doc @@ -1358,7 +1359,18 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) args["uom"] = item_data.stock_uom - wo_doc.append(table_name, args) + + existing_row = get_template_rm_item(wo_doc, item.get("item_code")) + if existing_row: + existing_row.update(args) + else: + wo_doc.append(table_name, args) + + +def get_template_rm_item(wo_doc, item_code): + for row in wo_doc.required_items: + if row.item_code == item_code: + return row @frappe.whitelist() From a5cc30741703857556efe0f201a7bb91231eee0b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 7 Dec 2024 12:58:34 +0530 Subject: [PATCH 27/43] fix: BOM name issue (backport #44586) (#44589) fix: BOM name issue (#44586) (cherry picked from commit d871e21a40943b3e9d52e2dd7233a128fa0f0ff6) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 14 +++++----- erpnext/manufacturing/doctype/bom/bom.py | 34 ++++++++++-------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index d8024a428a34..36ada87111a3 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -253,15 +253,15 @@ frappe.ui.form.on("BOM", { }; }, }); - - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); } + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); + if (!skip_qty_field) { fields.push({ fieldtype: "Float", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 2f48a498ec38..730ff494801b 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -179,15 +179,7 @@ def autoname(self): "BOM", filters={"name": search_key, "amended_from": ["is", "not set"]}, pluck="name" ) - if not existing_boms: - existing_boms = frappe.get_all( - "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" - ) - - if existing_boms: - index = self.get_next_version_index(existing_boms) - else: - index = 1 + index = self.get_index_for_bom(existing_boms) prefix = self.doctype suffix = "%.3i" % index # convert index to string (1 -> "001") @@ -205,21 +197,23 @@ def autoname(self): name = f"{prefix}-{truncated_item_name}-{suffix}" if frappe.db.exists("BOM", name): - conflicting_bom = frappe.get_doc("BOM", name) - - if conflicting_bom.item != self.item: - msg = _("A BOM with name {0} already exists for item {1}.").format( - frappe.bold(name), frappe.bold(conflicting_bom.item) - ) + existing_boms = frappe.get_all( + "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + ) - frappe.throw( - _("{0}{1} Did you rename the item? Please contact Administrator / Tech support").format( - msg, "
" - ) - ) + index = self.get_index_for_bom(existing_boms) + suffix = "%.3i" % index + name = f"{prefix}-{self.item}-{suffix}" self.name = name + def get_index_for_bom(self, existing_boms): + index = 1 + if existing_boms: + index = self.get_next_version_index(existing_boms) + + return index + @staticmethod def get_next_version_index(existing_boms: list[str]) -> int: # split by "/" and "-" From 608eaaa482507cd5724e4b05ee0dc99e0b6f5004 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:06:41 +0530 Subject: [PATCH 28/43] fix: BOM has not attr required items (backport #44598) (#44600) fix: BOM has not attr required items (#44598) (cherry picked from commit 42d238da1425b5c8a72308c2522a9336d4ea89fb) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 28 +++++++++++++++---- .../doctype/work_order/work_order.py | 4 ++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 36ada87111a3..3a0bcf6b47ef 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -255,12 +255,14 @@ frappe.ui.form.on("BOM", { }); } - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); + if (!skip_qty_field) { + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); + } if (!skip_qty_field) { fields.push({ @@ -306,6 +308,13 @@ frappe.ui.form.on("BOM", { fieldtype: "Link", in_list_view: 1, reqd: 1, + get_query() { + return { + filters: { + has_variants: 1, + }, + }; + }, }, { fieldname: "variant_item_code", @@ -326,6 +335,13 @@ frappe.ui.form.on("BOM", { }, }; }, + change() { + let doc = this.doc; + if (!doc.qty) { + doc.qty = 1.0; + this.grid.set_value("qty", 1.0, doc); + } + }, }, { fieldname: "qty", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index e5219d7cb3e7..9af3403ffa36 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1360,7 +1360,9 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) args["uom"] = item_data.stock_uom - existing_row = get_template_rm_item(wo_doc, item.get("item_code")) + existing_row = ( + get_template_rm_item(wo_doc, item.get("item_code")) if table_name == "required_items" else None + ) if existing_row: existing_row.update(args) else: From 2a17ecdc3949380215b03e4ae296de40efc2c11d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:07:06 +0530 Subject: [PATCH 29/43] fix: currency symbol in SCO and SCR (backport #44577) (#44594) * fix: currency symbol in SCO and SCR (#44577) (cherry picked from commit 8806d17ef1daa0a581aec5f4f20489346802cc19) # Conflicts: # erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json # erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json # erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json # erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../subcontracting_order.json | 21 ++++++++------- .../subcontracting_order_item.json | 11 +++++--- .../subcontracting_order_item.py | 3 +++ .../subcontracting_receipt.json | 27 ++++++++++--------- .../subcontracting_receipt_item.json | 9 ++++--- .../subcontracting_receipt_item.py | 1 + 6 files changed, 42 insertions(+), 30 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json index b8bda8329832..206e3135dfb6 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json @@ -294,7 +294,7 @@ "fieldname": "total", "fieldtype": "Currency", "label": "Total", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -353,6 +353,7 @@ "fieldname": "total_additional_costs", "fieldtype": "Currency", "label": "Total Additional Costs", + "options": "Company:company:default_currency", "print_hide_if_no_value": 1, "read_only": 1 }, @@ -450,21 +451,21 @@ "options": "Project" }, { - "fieldname": "tab_other_info", - "fieldtype": "Tab Break", - "label": "Other Info" + "fieldname": "tab_other_info", + "fieldtype": "Tab Break", + "label": "Other Info" }, { - "fieldname": "tab_connections", - "fieldtype": "Tab Break", - "label": "Connections", - "show_dashboard": 1 + "fieldname": "tab_connections", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 } ], "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2024-01-03 20:56:04.670380", + "modified": "2024-12-06 15:21:49.924146", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order", @@ -519,4 +520,4 @@ "timeline_field": "supplier", "title_field": "supplier_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json index 1ca90c31654d..502a28b3ec62 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json @@ -185,7 +185,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Rate", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -199,7 +199,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -269,6 +269,7 @@ "fieldname": "service_cost_per_qty", "fieldtype": "Currency", "label": "Service Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -277,6 +278,7 @@ "fieldname": "additional_cost_per_qty", "fieldtype": "Currency", "label": "Additional Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -284,6 +286,7 @@ "fieldtype": "Currency", "label": "Raw Material Cost Per Qty", "no_copy": 1, + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -384,7 +387,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-30 15:29:43.744618", + "modified": "2024-12-06 15:23:05.252346", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order Item", @@ -397,4 +400,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py index fcd143c1dd94..7a426f91cb00 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py +++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.py @@ -26,8 +26,11 @@ class SubcontractingOrderItem(Document): include_exploded_items: DF.Check item_code: DF.Link item_name: DF.Data + job_card: DF.Link | None manufacturer: DF.Link | None manufacturer_part_no: DF.Data | None + material_request: DF.Link | None + material_request_item: DF.Data | None page_break: DF.Check parent: DF.Data parentfield: DF.Data diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index ad03171f29a2..b8bd95bcbcae 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -336,7 +336,7 @@ "fieldname": "total", "fieldtype": "Currency", "label": "Total", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -618,6 +618,7 @@ "fieldname": "total_additional_costs", "fieldtype": "Currency", "label": "Total Additional Costs", + "options": "Company:company:default_currency", "print_hide_if_no_value": 1, "read_only": 1 }, @@ -656,27 +657,27 @@ "fieldtype": "Column Break" }, { - "fieldname": "tab_other_info", - "fieldtype": "Tab Break", - "label": "Other Info" + "fieldname": "tab_other_info", + "fieldtype": "Tab Break", + "label": "Other Info" }, { - "collapsible": 1, - "fieldname": "order_status_section", - "fieldtype": "Section Break", - "label": "Order Status" + "collapsible": 1, + "fieldname": "order_status_section", + "fieldtype": "Section Break", + "label": "Order Status" }, { - "fieldname": "tab_connections", - "fieldtype": "Tab Break", - "label": "Connections", - "show_dashboard": 1 + "fieldname": "tab_connections", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 } ], "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2024-05-28 15:02:13.517969", + "modified": "2024-12-06 15:24:38.384232", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", 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 75e263e2c1cb..23a7e69669d3 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -207,7 +207,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Rate", - "options": "currency", + "options": "Company:company:default_currency", "print_width": "100px", "read_only": 1, "width": "100px" @@ -217,7 +217,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "currency", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -231,6 +231,7 @@ "fieldtype": "Currency", "label": "Raw Material Cost Per Qty", "no_copy": 1, + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -239,6 +240,7 @@ "fieldname": "service_cost_per_qty", "fieldtype": "Currency", "label": "Service Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1, "reqd": 1 }, @@ -248,6 +250,7 @@ "fieldname": "additional_cost_per_qty", "fieldtype": "Currency", "label": "Additional Cost Per Qty", + "options": "Company:company:default_currency", "read_only": 1 }, { @@ -582,7 +585,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-03-29 15:42:43.425544", + "modified": "2024-12-06 15:23:58.680169", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py index 1a4ce5b977a2..69f7ae73e7a5 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.py @@ -28,6 +28,7 @@ class SubcontractingReceiptItem(Document): is_scrap_item: DF.Check item_code: DF.Link item_name: DF.Data | None + job_card: DF.Link | None manufacturer: DF.Link | None manufacturer_part_no: DF.Data | None page_break: DF.Check From 47e1ed1eff0a14fef46881ac42843b43d5ce43d0 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:59:16 +0100 Subject: [PATCH 30/43] fix: add docstatus for translation (cherry picked from commit dda272220bcc65768ef0adcdb48d23baba576481) --- erpnext/templates/includes/issue_row.html | 2 +- erpnext/templates/includes/projects/project_row.html | 2 +- erpnext/templates/includes/transaction_row.html | 2 +- erpnext/templates/pages/timelog_info.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/templates/includes/issue_row.html b/erpnext/templates/includes/issue_row.html index a04f558509f6..b55712ab1894 100644 --- a/erpnext/templates/includes/issue_row.html +++ b/erpnext/templates/includes/issue_row.html @@ -18,7 +18,7 @@ {% if doc.status == "Open" %} {{ doc.priority }} {% else %} - {{ doc.status }} + {{ _(doc.status) }} {%- endif -%} diff --git a/erpnext/templates/includes/projects/project_row.html b/erpnext/templates/includes/projects/project_row.html index 686637a20146..ccb306afcdb0 100644 --- a/erpnext/templates/includes/projects/project_row.html +++ b/erpnext/templates/includes/projects/project_row.html @@ -20,7 +20,7 @@ {% else %} - {{ doc.status }} + {{ _(doc.status) }} {% endif %} {% if doc["_assign"] %} diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html index a498ba0eefaf..061c9bd1796e 100644 --- a/erpnext/templates/includes/transaction_row.html +++ b/erpnext/templates/includes/transaction_row.html @@ -8,7 +8,7 @@
- {{doc.status}} + {{ _(doc.status) }}
diff --git a/erpnext/templates/pages/timelog_info.html b/erpnext/templates/pages/timelog_info.html index be13826444c9..9f9445661a07 100644 --- a/erpnext/templates/pages/timelog_info.html +++ b/erpnext/templates/pages/timelog_info.html @@ -38,7 +38,7 @@

{{ doc.name }}

- + From 2560beb69581ef74551e892e54410cde0fb13a00 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 9 Dec 2024 12:58:20 +0530 Subject: [PATCH 31/43] fix: prevent set_payment_schedule on return documents --- erpnext/stock/doctype/delivery_note/delivery_note.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 0bc6b28fe681..2b4dad137c25 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -1046,7 +1046,7 @@ def get_pending_qty(item_row): automatically_fetch_payment_terms = cint( frappe.db.get_single_value("Accounts Settings", "automatically_fetch_payment_terms") ) - if automatically_fetch_payment_terms: + if automatically_fetch_payment_terms and not doc.is_return: doc.set_payment_schedule() return doc From cce1cc66747cdf3f4a8fe210b53298c41417526f Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 4 Dec 2024 20:40:08 +0530 Subject: [PATCH 32/43] fix: correct color for draft in list view (cherry picked from commit 143acf23303a1cf8a3dff7ec60be694974badb4a) --- .../accounts/doctype/payment_request/payment_request_list.js | 2 +- erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js | 2 +- erpnext/stock/doctype/pick_list/pick_list_list.js | 2 +- .../doctype/subcontracting_order/subcontracting_order_list.js | 2 +- .../subcontracting_receipt/subcontracting_receipt_list.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 1027385aaaf1..6e4aada66c61 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -1,7 +1,7 @@ const INDICATORS = { "Partially Paid": "orange", Cancelled: "red", - Draft: "gray", + Draft: "red", Failed: "red", Initiated: "green", Paid: "blue", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index f971f68a4546..3371a63cca28 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -15,7 +15,7 @@ frappe.listview_settings["Sales Invoice"] = { ], get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Unpaid: "orange", Paid: "green", Return: "gray", diff --git a/erpnext/stock/doctype/pick_list/pick_list_list.js b/erpnext/stock/doctype/pick_list/pick_list_list.js index 9cdbfe187205..eca6eece7855 100644 --- a/erpnext/stock/doctype/pick_list/pick_list_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list_list.js @@ -4,7 +4,7 @@ frappe.listview_settings["Pick List"] = { get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Open: "orange", Completed: "green", Cancelled: "red", diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js index 895ffdbf88c3..f5486bbd59d6 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js @@ -4,7 +4,7 @@ frappe.listview_settings["Subcontracting Order"] = { get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Open: "orange", "Partially Received": "yellow", Completed: "green", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js index be6c0d0b18f3..5f5a6db01e0a 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js @@ -4,7 +4,7 @@ frappe.listview_settings["Subcontracting Receipt"] = { get_indicator: function (doc) { const status_colors = { - Draft: "grey", + Draft: "red", Return: "gray", "Return Issued": "grey", Completed: "green", From 243bf542c43de476c8d0c26c34bd45d31e72f905 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:56:10 +0530 Subject: [PATCH 33/43] fix: description overwrite on qty change (backport #44606) (#44609) fix: description overwrite on qty change (#44606) (cherry picked from commit 9ad79625e02772a6c72b5eacfd7ebf5f38706675) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index c7485470cf07..09c01a0ee883 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -259,18 +259,21 @@ frappe.ui.form.on("Material Request", { }, callback: function (r) { const d = item; - const allow_to_change_fields = [ + let allow_to_change_fields = [ "actual_qty", "projected_qty", "min_order_qty", "item_name", - "description", "stock_uom", "uom", "conversion_factor", "stock_qty", ]; + if (overwrite_warehouse) { + allow_to_change_fields.push("description"); + } + if (!r.exc) { $.each(r.message, function (key, value) { if (!d[key] || allow_to_change_fields.includes(key)) { From 04b07a9e1c0235d5a8515973f55c2676532dfcc3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 19:11:49 +0530 Subject: [PATCH 34/43] fix: not able to make sales return entry (backport #44605) (#44613) fix: not able to make sales return entry (#44605) (cherry picked from commit 314c7b8d2afa6abda3386b5f8f7dcf2ce659ffc0) Co-authored-by: rohitwaghchaure --- .../doctype/serial_and_batch_bundle/serial_and_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fff9cbfc07d1..4bd8f5e84652 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 @@ -223,7 +223,7 @@ def set_valuation_rate_for_return_entry(self, return_aginst, save=False): else: valuation_rate = valuation_details["batches"].get(row.batch_no) - row.incoming_rate = valuation_rate + row.incoming_rate = flt(valuation_rate) row.stock_value_difference = flt(row.qty) * flt(row.incoming_rate) if save: From bdb60a51c3232dc14b052822463a43b67b16ed8f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 20:30:45 +0530 Subject: [PATCH 35/43] fix: do not allow to inward same serial nos multiple times (backport #44617) (#44619) * fix: do not allow to inward same serial nos multiple times (#44617) (cherry picked from commit 616bb383c5434e43b32099f5e21d679c0bb0baae) # Conflicts: # erpnext/patches.txt * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/patches.txt | 1 + .../v15_0/enable_allow_existing_serial_no.py | 6 ++ .../purchase_receipt/test_purchase_receipt.py | 36 ++++++++++ .../serial_and_batch_bundle.py | 68 +++++++++++++++++-- .../stock_settings/stock_settings.json | 11 ++- .../doctype/stock_settings/stock_settings.py | 1 + 6 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 erpnext/patches/v15_0/enable_allow_existing_serial_no.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f53769155bde..a916478d4b37 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -384,3 +384,4 @@ erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries erpnext.patches.v14_0.update_stock_uom_in_work_order_item erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions +erpnext.patches.v15_0.enable_allow_existing_serial_no diff --git a/erpnext/patches/v15_0/enable_allow_existing_serial_no.py b/erpnext/patches/v15_0/enable_allow_existing_serial_no.py new file mode 100644 index 000000000000..e13adc2b1875 --- /dev/null +++ b/erpnext/patches/v15_0/enable_allow_existing_serial_no.py @@ -0,0 +1,6 @@ +import frappe + + +def execute(): + if frappe.get_all("Company", filters={"country": "India"}, limit=1): + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 1) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 4b8d5101f43a..86d1a6948dee 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3948,6 +3948,42 @@ def test_purchase_return_partial_debit_note(self): self.assertEqual(return_pr.per_billed, 100) self.assertEqual(return_pr.status, "Completed") + def test_do_not_allow_to_inward_same_serial_no_multiple_times(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 0) + + item_code = make_item( + "Test Do Not Allow INWD Item 123", {"has_serial_no": 1, "serial_no_series": "SN-TDAISN-.#####"} + ).name + + pr = make_purchase_receipt(item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1) + serial_no = get_serial_nos_from_bundle(pr.items[0].serial_and_batch_bundle)[0] + + status = frappe.db.get_value("Serial No", serial_no, "status") + self.assertTrue(status == "Active") + + make_stock_entry( + item_code=item_code, + source=pr.items[0].warehouse, + qty=1, + serial_no=serial_no, + use_serial_batch_fields=1, + ) + + status = frappe.db.get_value("Serial No", serial_no, "status") + self.assertFalse(status == "Active") + + pr = make_purchase_receipt( + item_code=item_code, qty=1, rate=100, use_serial_batch_fields=1, do_not_submit=1 + ) + pr.items[0].serial_no = serial_no + pr.save() + + self.assertRaises(frappe.exceptions.ValidationError, pr.submit) + + frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 1) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier 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 4bd8f5e84652..a1f3135b70b8 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 @@ -89,6 +89,10 @@ def validate(self): self.validate_serial_and_batch_no() self.validate_duplicate_serial_and_batch_no() self.validate_voucher_no() + + if self.docstatus == 0: + self.allow_existing_serial_nos() + if self.type_of_transaction == "Maintenance": return @@ -102,6 +106,42 @@ def validate(self): self.set_incoming_rate() self.calculate_qty_and_amount() + def allow_existing_serial_nos(self): + if self.type_of_transaction == "Outward" or not self.has_serial_no: + return + + if frappe.db.get_single_value("Stock Settings", "allow_existing_serial_no"): + return + + if self.voucher_type not in ["Purchase Receipt", "Purchase Invoice", "Stock Entry"]: + return + + if self.voucher_type == "Stock Entry" and frappe.get_cached_value( + "Stock Entry", self.voucher_no, "purpose" + ) in ["Material Transfer", "Send to Subcontractor", "Material Transfer for Manufacture"]: + return + + serial_nos = [d.serial_no for d in self.entries if d.serial_no] + + data = frappe.get_all( + "Serial and Batch Entry", + filters={"serial_no": ("in", serial_nos), "docstatus": 1, "qty": ("<", 0)}, + fields=["serial_no", "parent"], + ) + + note = "

Note:
" + for row in data: + frappe.throw( + _( + "You can't process the serial number {0} as it has already been used in the SABB {1}. {2} if you want to inward same serial number multiple times then enabled 'Allow existing Serial No to be Manufactured/Received again' in the {3}" + ).format( + row.serial_no, + get_link_to_form("Serial and Batch Bundle", row.parent), + note, + get_link_to_form("Stock Settings", "Stock Settings"), + ) + ) + def reset_serial_batch_bundle(self): if self.is_new() and self.amended_from: for field in ["is_cancelled", "is_rejected"]: @@ -136,7 +176,12 @@ def validate_serial_nos_inventory(self): return serial_nos = [d.serial_no for d in self.entries if d.serial_no] - kwargs = {"item_code": self.item_code, "warehouse": self.warehouse} + kwargs = { + "item_code": self.item_code, + "warehouse": self.warehouse, + "check_serial_nos": True, + "serial_nos": serial_nos, + } if self.voucher_type == "POS Invoice": kwargs["ignore_voucher_nos"] = [self.voucher_no] @@ -177,6 +222,7 @@ def validate_serial_nos_duplicate(self): "posting_date": self.posting_date, "posting_time": self.posting_time, "serial_nos": serial_nos, + "check_serial_nos": True, } ) @@ -1683,7 +1729,7 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): serial_nos = set() data = get_stock_ledgers_for_serial_nos(kwargs) - bundle_wise_serial_nos = get_bundle_wise_serial_nos(data) + bundle_wise_serial_nos = get_bundle_wise_serial_nos(data, kwargs) for d in data: if d.serial_and_batch_bundle: if sns := bundle_wise_serial_nos.get(d.serial_and_batch_bundle): @@ -1707,16 +1753,21 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): return serial_nos -def get_bundle_wise_serial_nos(data): +def get_bundle_wise_serial_nos(data, kwargs): bundle_wise_serial_nos = defaultdict(list) bundles = [d.serial_and_batch_bundle for d in data if d.serial_and_batch_bundle] if not bundles: return bundle_wise_serial_nos + filters = {"parent": ("in", bundles), "docstatus": 1, "serial_no": ("is", "set")} + + if kwargs.get("check_serial_nos") and kwargs.get("serial_nos"): + filters["serial_no"] = ("in", kwargs.get("serial_nos")) + bundle_data = frappe.get_all( "Serial and Batch Entry", fields=["serial_no", "parent"], - filters={"parent": ("in", bundles), "docstatus": 1, "serial_no": ("is", "set")}, + filters=filters, ) for d in bundle_data: @@ -2277,6 +2328,8 @@ def get_ledgers_from_serial_batch_bundle(**kwargs) -> list[frappe._dict]: def get_stock_ledgers_for_serial_nos(kwargs): + from erpnext.stock.utils import get_combine_datetime + stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry") query = ( @@ -2287,15 +2340,16 @@ def get_stock_ledgers_for_serial_nos(kwargs): stock_ledger_entry.serial_and_batch_bundle, ) .where(stock_ledger_entry.is_cancelled == 0) + .orderby(stock_ledger_entry.posting_datetime) ) if kwargs.get("posting_date"): if kwargs.get("posting_time") is None: kwargs.posting_time = nowtime() - timestamp_condition = CombineDatetime( - stock_ledger_entry.posting_date, stock_ledger_entry.posting_time - ) <= CombineDatetime(kwargs.posting_date, kwargs.posting_time) + timestamp_condition = stock_ledger_entry.posting_datetime <= get_combine_datetime( + kwargs.posting_date, kwargs.posting_time + ) query = query.where(timestamp_condition) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 069e7da41cb3..e542a1582e38 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -49,12 +49,13 @@ "do_not_use_batchwise_valuation", "auto_create_serial_and_batch_bundle_for_outward", "pick_serial_and_batch_based_on", + "naming_series_prefix", "column_break_mhzc", "disable_serial_no_and_batch_selector", "use_naming_series", - "naming_series_prefix", "use_serial_batch_fields", "do_not_update_serial_batch_on_creation_of_auto_bundle", + "allow_existing_serial_no", "stock_planning_tab", "auto_material_request", "auto_indent", @@ -460,6 +461,12 @@ "fieldname": "over_picking_allowance", "fieldtype": "Percent", "label": "Over Picking Allowance" + }, + { + "default": "1", + "fieldname": "allow_existing_serial_no", + "fieldtype": "Check", + "label": "Allow existing Serial No to be Manufactured/Received again" } ], "icon": "icon-cog", @@ -467,7 +474,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-07-29 14:55:19.093508", + "modified": "2024-12-09 17:52:36.030456", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index 229ff9447507..b7a317cd66a3 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -25,6 +25,7 @@ class StockSettings(Document): action_if_quality_inspection_is_not_submitted: DF.Literal["Stop", "Warn"] action_if_quality_inspection_is_rejected: DF.Literal["Stop", "Warn"] + allow_existing_serial_no: DF.Check allow_from_dn: DF.Check allow_from_pr: DF.Check allow_internal_transfer_at_arms_length_price: DF.Check From 98bd5f20c4e20edb766ad4b0b4df23fda5552be9 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 5 Dec 2024 15:18:44 +0530 Subject: [PATCH 36/43] fix: restrict advance payment entries with flag where "book_advance_payments_in_separate_party_account" is true --- .../payment_reconciliation.py | 6 ++--- erpnext/controllers/accounts_controller.py | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 68e9eef711a7..db4a4b0f268a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -153,10 +153,7 @@ def get_nonreconciled_payment_entries(self): self.add_payment_entries(non_reconciled_payments) def get_payment_entries(self): - if self.default_advance_account: - party_account = [self.receivable_payable_account, self.default_advance_account] - else: - party_account = [self.receivable_payable_account] + party_account = [self.receivable_payable_account] order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = frappe._dict( @@ -187,6 +184,7 @@ def get_payment_entries(self): self.party, party_account, order_doctype, + default_advance_account=self.default_advance_account, against_all_orders=True, limit=self.payment_limit, condition=condition, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bc0cdde94dee..e1a7d5803c9a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2916,6 +2916,7 @@ def get_advance_payment_entries( party_account, order_doctype, order_list=None, + default_advance_account=None, include_unallocated=True, against_all_orders=False, limit=None, @@ -2929,6 +2930,7 @@ def get_advance_payment_entries( party_type, party, party_account, + default_advance_account, limit, condition, ) @@ -2952,6 +2954,7 @@ def get_advance_payment_entries( party_type, party, party_account, + default_advance_account, limit, condition, ) @@ -2967,6 +2970,7 @@ def get_common_query( party_type, party, party_account, + default_advance_account, limit, condition, ): @@ -2988,14 +2992,22 @@ def get_common_query( .where(payment_entry.docstatus == 1) ) - if payment_type == "Receive": - q = q.select((payment_entry.paid_from_account_currency).as_("currency")) - q = q.select(payment_entry.paid_from) - q = q.where(payment_entry.paid_from.isin(party_account)) + field = "paid_from" if payment_type == "Receive" else "paid_to" + + q = q.select((payment_entry[f"{field}_account_currency"]).as_("currency")) + q = q.select(payment_entry[field]) + account_condition = payment_entry[field].isin(party_account) + if default_advance_account: + q = q.where( + account_condition + | ( + (payment_entry[field] == default_advance_account) + & (payment_entry.book_advance_payments_in_separate_party_account == 1) + ) + ) + else: - q = q.select((payment_entry.paid_to_account_currency).as_("currency")) - q = q.select(payment_entry.paid_to) - q = q.where(payment_entry.paid_to.isin(party_account)) + q = q.where(account_condition) if payment_type == "Receive": q = q.select((payment_entry.source_exchange_rate).as_("exchange_rate")) From 5cd39941ee9d8067e606df169b1d773fa46cc580 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:57:20 +0530 Subject: [PATCH 37/43] fix: 'Use Multi-Level BOM' checkbox default value (backport #44618) (#44620) fix: 'Use Multi-Level BOM' checkbox default value (#44618) (cherry picked from commit f5c038cd1bb129609a048a62fe61bd20505abe32) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.js | 16 +++++++--------- erpnext/manufacturing/doctype/bom/bom.py | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 3a0bcf6b47ef..4b52859cabd9 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -255,15 +255,6 @@ frappe.ui.form.on("BOM", { }); } - if (!skip_qty_field) { - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); - } - if (!skip_qty_field) { fields.push({ fieldtype: "Float", @@ -291,6 +282,13 @@ frappe.ui.form.on("BOM", { cur_dialog.refresh(); }, }); + + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: frm.doc?.__onload.use_multi_level_bom, + }); } var has_template_rm = frm.doc.items.filter((d) => d.has_variants === 1) || []; diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 730ff494801b..78b95f2c80c0 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -214,6 +214,23 @@ def get_index_for_bom(self, existing_boms): return index + def onload(self): + super().onload() + + self.set_onload_for_muulti_level_bom() + + def set_onload_for_muulti_level_bom(self): + use_multi_level_bom = frappe.db.get_value( + "Property Setter", + {"field_name": "use_multi_level_bom", "doc_type": "Work Order", "property": "default"}, + "value", + ) + + if use_multi_level_bom is None: + use_multi_level_bom = 1 + + self.set_onload("use_multi_level_bom", cint(use_multi_level_bom)) + @staticmethod def get_next_version_index(existing_boms: list[str]) -> int: # split by "/" and "-" From ef5a5643dca2c5ee8f008088eb9bcc77079e72e9 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:54:02 +0530 Subject: [PATCH 38/43] fix: precision check for salvage value --- erpnext/assets/doctype/asset/asset.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index c788d5265a73..05d575ac8224 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -410,6 +410,9 @@ def set_depreciation_rate(self): ) def validate_asset_finance_books(self, row): + row.expected_value_after_useful_life = flt( + row.expected_value_after_useful_life, self.precision("gross_purchase_amount") + ) if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount): frappe.throw( _("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount").format( @@ -430,7 +433,10 @@ def validate_asset_finance_books(self, row): self.opening_accumulated_depreciation = 0 self.opening_number_of_booked_depreciations = 0 else: - depreciable_amount = flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life) + depreciable_amount = flt( + flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life), + self.precision("gross_purchase_amount"), + ) if flt(self.opening_accumulated_depreciation) > depreciable_amount: frappe.throw( _("Opening Accumulated Depreciation must be less than or equal to {0}").format( From 7e0ff851ab1a46479927b0a3e097aab5fa368ad8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 11 Dec 2024 15:40:56 +0530 Subject: [PATCH 39/43] refactor: consider against voucher when not filtered on voucher_no --- .../report/general_ledger/general_ledger.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 04df22c9c9c5..69e3d241f126 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -35,9 +35,6 @@ def execute(filters=None): if filters.get("party"): filters.party = frappe.parse_json(filters.get("party")) - if filters.get("voucher_no") and not filters.get("group_by"): - filters.group_by = "Group by Voucher (Consolidated)" - validate_filters(filters, account_details) validate_party(filters) @@ -373,16 +370,21 @@ def get_data_with_opening_closing(filters, account_details, accounting_dimension if acc_dict.entries: # opening data.append({"debit_in_transaction_currency": None, "credit_in_transaction_currency": None}) - if filters.get("group_by") != "Group by Voucher": + if (not filters.get("group_by") and not filters.get("voucher_no")) or ( + filters.get("group_by") and filters.get("group_by") != "Group by Voucher" + ): data.append(acc_dict.totals.opening) data += acc_dict.entries # totals - data.append(acc_dict.totals.total) + if filters.get("group_by") or not filters.voucher_no: + data.append(acc_dict.totals.total) # closing - if filters.get("group_by") != "Group by Voucher": + if (not filters.get("group_by") and not filters.get("voucher_no")) or ( + filters.get("group_by") and filters.get("group_by") != "Group by Voucher" + ): data.append(acc_dict.totals.closing) data.append({"debit_in_transaction_currency": None, "credit_in_transaction_currency": None}) From f88dc263d5746f1f3f9b9dafd75d050390df23a1 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Wed, 11 Dec 2024 15:55:22 +0530 Subject: [PATCH 40/43] fix: update free item qty while adding same item in seperate row --- erpnext/accounts/doctype/pricing_rule/utils.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 087e2bffa4bd..551eaa3d1ced 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -651,8 +651,17 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): qty = pricing_rule.free_qty or 1 if pricing_rule.is_recursive: - transaction_qty = (args.get("qty") if args else doc.total_qty) - pricing_rule.apply_recursion_over - if transaction_qty: + transaction_qty = sum( + [ + row.qty + for row in doc.items + if not row.is_free_item + and row.item_code == args.item_code + and row.pricing_rules == args.pricing_rules + ] + ) + transaction_qty = transaction_qty - pricing_rule.apply_recursion_over + if transaction_qty and transaction_qty > 0: qty = flt(transaction_qty) * qty / pricing_rule.recurse_for if pricing_rule.round_free_qty: qty = (flt(transaction_qty) // pricing_rule.recurse_for) * (pricing_rule.free_qty or 1) From 2466e4c2fdf887a435a035dad4095b1b3f9b0ba4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:33:36 +0530 Subject: [PATCH 41/43] fix: valuation rate for batch in stock reconciliation (backport #44657) (#44664) fix: valuation rate for batch in stock reconciliation (#44657) fix: valuation rate for batch in stock reco (cherry picked from commit 15c7d26378d195b9ffeee0e6b9efbbb3bc7748a0) Co-authored-by: rohitwaghchaure --- .../stock_reconciliation.js | 1 + .../stock_reconciliation.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index 319856780095..85acc7629692 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -207,6 +207,7 @@ frappe.ui.form.on("Stock Reconciliation", { posting_time: frm.doc.posting_time, batch_no: d.batch_no, row: d, + company: frm.doc.company, }, callback: function (r) { const row = frappe.model.get_doc(cdt, cdn); diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index c13b3620517f..12773a5555db 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -466,6 +466,7 @@ def _changed(item): batch_no=item.batch_no, inventory_dimensions_dict=inventory_dimensions_dict, row=item, + company=self.company, ) if ( @@ -974,6 +975,7 @@ def recalculate_current_qty(self, voucher_detail_no): self.posting_date, self.posting_time, row=row, + company=self.company, ) current_qty = item_dict.get("qty") @@ -1308,6 +1310,7 @@ def get_stock_balance_for( with_valuation_rate: bool = True, inventory_dimensions_dict=None, row=None, + company=None, ): frappe.has_permission("Stock Reconciliation", "write", throw=True) @@ -1367,6 +1370,21 @@ def get_stock_balance_for( or 0 ) + if row.use_serial_batch_fields and row.batch_no: + rate = get_incoming_rate( + frappe._dict( + { + "item_code": row.item_code, + "warehouse": row.warehouse, + "qty": row.qty * -1, + "batch_no": row.batch_no, + "company": company, + "posting_date": posting_date, + "posting_time": posting_time, + } + ) + ) + return { "qty": qty, "rate": rate, From bd9c84d3b76908b6756d0b3ade35e50722f30332 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:24:32 +0530 Subject: [PATCH 42/43] fix: make projected qty editable after submit (backport #44670) (#44671) * fix: make projected qty editable after submit (#44670) (cherry picked from commit 9ef9ff3de89d0e59a590792090ab112b18420634) # Conflicts: # erpnext/selling/doctype/quotation_item/quotation_item.json * chore: fix conflicts --------- Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com> Co-authored-by: rohitwaghchaure --- erpnext/selling/doctype/quotation_item/quotation_item.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index 1ea19aaaf560..9dd65a8b4f88 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -455,6 +455,7 @@ "fieldtype": "Column Break" }, { + "allow_on_submit": 1, "fieldname": "projected_qty", "fieldtype": "Float", "label": "Projected Qty", @@ -690,7 +691,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-11-24 15:18:43.952844", + "modified": "2024-12-12 13:49:17.765883", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", From 33273faccbd514dd635c821a9cb18c30f6513982 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:44:28 +0530 Subject: [PATCH 43/43] fix: validate returned serial nos and batches (backport #44669) (#44674) fix: validate returned serial nos and batches (#44669) (cherry picked from commit 4385349e3640bf8b3f5279b466a3b94330d9a3cd) Co-authored-by: rohitwaghchaure --- .../purchase_receipt/test_purchase_receipt.py | 63 +++++++++++++++++++ .../serial_and_batch_bundle.py | 36 ++++++++--- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 86d1a6948dee..b097c0e6441e 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3984,6 +3984,69 @@ def test_do_not_allow_to_inward_same_serial_no_multiple_times(self): frappe.db.set_single_value("Stock Settings", "allow_existing_serial_no", 1) + def test_seral_no_return_validation(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( + make_purchase_return, + ) + + sn_item_code = make_item( + "Test Serial No for Validation", {"has_serial_no": 1, "serial_no_series": "SN-TSNFVAL-.#####"} + ).name + + pr1 = make_purchase_receipt(item_code=sn_item_code, qty=5, rate=100, use_serial_batch_fields=1) + pr1_serial_nos = get_serial_nos_from_bundle(pr1.items[0].serial_and_batch_bundle) + + serial_no_pr = make_purchase_receipt( + item_code=sn_item_code, qty=5, rate=100, use_serial_batch_fields=1 + ) + serial_no_pr_serial_nos = get_serial_nos_from_bundle(serial_no_pr.items[0].serial_and_batch_bundle) + + sn_return = make_purchase_return(serial_no_pr.name) + sn_return.items[0].qty = -1 + sn_return.items[0].received_qty = -1 + sn_return.items[0].serial_no = pr1_serial_nos[0] + sn_return.save() + self.assertRaises(frappe.ValidationError, sn_return.submit) + + sn_return = make_purchase_return(serial_no_pr.name) + sn_return.items[0].qty = -1 + sn_return.items[0].received_qty = -1 + sn_return.items[0].serial_no = serial_no_pr_serial_nos[0] + sn_return.save() + sn_return.submit() + + def test_batch_no_return_validation(self): + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( + make_purchase_return, + ) + + batch_item_code = make_item( + "Test Batch No for Validation", + {"has_batch_no": 1, "batch_number_series": "BT-TSNFVAL-.#####", "create_new_batch": 1}, + ).name + + pr1 = make_purchase_receipt(item_code=batch_item_code, qty=5, rate=100, use_serial_batch_fields=1) + batch_no = get_batch_from_bundle(pr1.items[0].serial_and_batch_bundle) + + batch_no_pr = make_purchase_receipt( + item_code=batch_item_code, qty=5, rate=100, use_serial_batch_fields=1 + ) + original_batch_no = get_batch_from_bundle(batch_no_pr.items[0].serial_and_batch_bundle) + + batch_return = make_purchase_return(batch_no_pr.name) + batch_return.items[0].qty = -1 + batch_return.items[0].received_qty = -1 + batch_return.items[0].batch_no = batch_no + batch_return.save() + self.assertRaises(frappe.ValidationError, batch_return.submit) + + batch_return = make_purchase_return(batch_no_pr.name) + batch_return.items[0].qty = -1 + batch_return.items[0].received_qty = -1 + batch_return.items[0].batch_no = original_batch_no + batch_return.save() + batch_return.submit() + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier 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 a1f3135b70b8..dc2071b9ee07 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 @@ -252,8 +252,8 @@ def set_incoming_rate(self, parent=None, row=None, save=False, allow_negative_st ]: return - if return_aginst := self.get_return_aginst(parent=parent): - self.set_valuation_rate_for_return_entry(return_aginst, save) + if return_against := self.get_return_against(parent=parent): + self.set_valuation_rate_for_return_entry(return_against, save) elif self.type_of_transaction == "Outward": self.set_incoming_rate_for_outward_transaction( row, save, allow_negative_stock=allow_negative_stock @@ -261,9 +261,12 @@ def set_incoming_rate(self, parent=None, row=None, save=False, allow_negative_st else: self.set_incoming_rate_for_inward_transaction(row, save) - def set_valuation_rate_for_return_entry(self, return_aginst, save=False): - if valuation_details := self.get_valuation_rate_for_return_entry(return_aginst): + def set_valuation_rate_for_return_entry(self, return_against, save=False): + if valuation_details := self.get_valuation_rate_for_return_entry(return_against): for row in self.entries: + if valuation_details: + self.validate_returned_serial_batch_no(return_against, row, valuation_details) + if row.serial_no: valuation_rate = valuation_details["serial_nos"].get(row.serial_no) else: @@ -280,7 +283,22 @@ def set_valuation_rate_for_return_entry(self, return_aginst, save=False): } ) - def get_valuation_rate_for_return_entry(self, return_aginst): + def validate_returned_serial_batch_no(self, return_against, row, original_inv_details): + if row.serial_no and row.serial_no not in original_inv_details["serial_nos"]: + self.throw_error_message( + _( + "Serial No {0} is not present in the {1} {2}, hence you can't return it against the {1} {2}" + ).format(bold(row.serial_no), self.voucher_type, bold(return_against)) + ) + + if row.batch_no and row.batch_no not in original_inv_details["batches"]: + self.throw_error_message( + _( + "Batch No {0} is not present in the original {1} {2}, hence you can't return it against the {1} {2}" + ).format(bold(row.batch_no), self.voucher_type, bold(return_against)) + ) + + def get_valuation_rate_for_return_entry(self, return_against): valuation_details = frappe._dict( { "serial_nos": defaultdict(float), @@ -296,7 +314,7 @@ def get_valuation_rate_for_return_entry(self, return_aginst): "`tabSerial and Batch Entry`.`incoming_rate`", ], filters=[ - ["Serial and Batch Bundle", "voucher_no", "=", return_aginst], + ["Serial and Batch Bundle", "voucher_no", "=", return_against], ["Serial and Batch Entry", "docstatus", "=", 1], ["Serial and Batch Bundle", "is_cancelled", "=", 0], ["Serial and Batch Bundle", "item_code", "=", self.item_code], @@ -430,8 +448,8 @@ def get_sle_for_outward_transaction(self): return sle - def get_return_aginst(self, parent=None): - return_aginst = None + def get_return_against(self, parent=None): + return_against = None if parent and parent.get("is_return") and parent.get("return_against"): return parent.get("return_against") @@ -455,7 +473,7 @@ def get_return_aginst(self, parent=None): if voucher_details and voucher_details.get("is_return") and voucher_details.get("return_against"): return voucher_details.get("return_against") - return return_aginst + return return_against def set_incoming_rate_for_inward_transaction(self, row=None, save=False): valuation_field = "valuation_rate"