Merge pull request #39857 from kunhimohamed/company_terms
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 8ffbaa1..dd27200 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -46,6 +46,9 @@
class StockController(AccountsController):
def validate(self):
super(StockController, self).validate()
+
+ if self.docstatus == 0:
+ self.validate_duplicate_serial_and_batch_bundle()
if not self.get("is_return"):
self.validate_inspection()
self.validate_serialized_batch()
@@ -55,6 +58,32 @@
self.validate_internal_transfer()
self.validate_putaway_capacity()
+ def validate_duplicate_serial_and_batch_bundle(self):
+ if sbb_list := [
+ item.get("serial_and_batch_bundle")
+ for item in self.items
+ if item.get("serial_and_batch_bundle")
+ ]:
+ SLE = frappe.qb.DocType("Stock Ledger Entry")
+ data = (
+ frappe.qb.from_(SLE)
+ .select(SLE.voucher_type, SLE.voucher_no, SLE.serial_and_batch_bundle)
+ .where(
+ (SLE.docstatus == 1)
+ & (SLE.serial_and_batch_bundle.notnull())
+ & (SLE.serial_and_batch_bundle.isin(sbb_list))
+ )
+ .limit(1)
+ ).run(as_dict=True)
+
+ if data:
+ data = data[0]
+ frappe.throw(
+ _("Serial and Batch Bundle {0} is already used in {1} {2}.").format(
+ frappe.bold(data.serial_and_batch_bundle), data.voucher_type, data.voucher_no
+ )
+ )
+
def make_gl_entries(self, gl_entries=None, from_repost=False):
if self.docstatus == 2:
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 079350b..3daec20 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -748,7 +748,7 @@
fields=["total_time_in_mins", "hour_rate"],
filters={"is_corrective_job_card": 1, "docstatus": 1, "work_order": self.work_order},
):
- wo.corrective_operation_cost += flt(row.total_time_in_mins) * flt(row.hour_rate)
+ wo.corrective_operation_cost += flt(row.total_time_in_mins / 60) * flt(row.hour_rate)
wo.calculate_operating_cost()
wo.flags.ignore_validate_update_after_submit = True
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index d07bf0f..06c1b49 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -38,7 +38,8 @@
"in_list_view": 1,
"label": "Item Code",
"options": "Item",
- "reqd": 1
+ "reqd": 1,
+ "search_index": 1
},
{
"fieldname": "item_name",
@@ -53,7 +54,8 @@
"in_standard_filter": 1,
"label": "For Warehouse",
"options": "Warehouse",
- "reqd": 1
+ "reqd": 1,
+ "search_index": 1
},
{
"columns": 1,
@@ -141,7 +143,8 @@
"fieldname": "from_warehouse",
"fieldtype": "Link",
"label": "From Warehouse",
- "options": "Warehouse"
+ "options": "Warehouse",
+ "search_index": 1
},
{
"fetch_from": "item_code.safety_stock",
@@ -199,7 +202,7 @@
],
"istable": 1,
"links": [],
- "modified": "2023-09-12 12:09:08.358326",
+ "modified": "2024-02-11 16:21:11.977018",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 257b60c..54c3893 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -298,7 +298,8 @@
"no_copy": 1,
"options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"fieldname": "amended_from",
@@ -436,7 +437,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-12-26 16:31:13.740777",
+ "modified": "2024-02-11 15:42:47.642481",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 6e9d1fc..b942842 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -1768,23 +1768,23 @@
return reserved_qty_for_production_plan - reserved_qty_for_production
+@frappe.request_cache
def get_non_completed_production_plans():
table = frappe.qb.DocType("Production Plan")
child = frappe.qb.DocType("Production Plan Item")
- query = (
+ return (
frappe.qb.from_(table)
.inner_join(child)
.on(table.name == child.parent)
.select(table.name)
+ .distinct()
.where(
(table.docstatus == 1)
& (table.status.notin(["Completed", "Closed"]))
& (child.planned_qty > child.ordered_qty)
)
- ).run(as_dict=True)
-
- return list(set([d.name for d in query]))
+ ).run(pluck="name")
def get_raw_materials_of_sub_assembly_items(
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 1996e19..63c74b6 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -447,7 +447,8 @@
"no_copy": 1,
"options": "Production Plan",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"fieldname": "production_plan_item",
@@ -592,7 +593,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
- "modified": "2023-08-11 18:35:49.852069",
+ "modified": "2024-02-11 15:47:13.454422",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
index f354d45..0f4d693 100644
--- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
+++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
@@ -36,7 +36,8 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item Code",
- "options": "Item"
+ "options": "Item",
+ "search_index": 1
},
{
"fieldname": "source_warehouse",
@@ -141,7 +142,7 @@
],
"istable": 1,
"links": [],
- "modified": "2022-09-28 10:50:43.512562",
+ "modified": "2024-02-11 15:45:32.318374",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Item",
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
index f430943..88b262a 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
@@ -4,7 +4,7 @@
import json
import frappe
-from frappe.tests.utils import FrappeTestCase
+from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
from erpnext.stock.doctype.item.test_item import make_item
@@ -521,6 +521,24 @@
make_serial_nos(item_code, serial_nos)
self.assertTrue(frappe.db.exists("Serial No", serial_no_id))
+ @change_settings("Stock Settings", {"auto_create_serial_and_batch_bundle_for_outward": 1})
+ def test_duplicate_serial_and_batch_bundle(self):
+ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+ item_code = make_item(properties={"is_stock_item": 1, "has_serial_no": 1}).name
+
+ serial_no = f"{item_code}-001"
+ serial_nos = [{"serial_no": serial_no, "qty": 1}]
+ make_serial_nos(item_code, serial_nos)
+
+ pr1 = make_purchase_receipt(item=item_code, qty=1, rate=500, serial_no=[serial_no])
+ pr2 = make_purchase_receipt(item=item_code, qty=1, rate=500, do_not_save=True)
+
+ pr1.reload()
+ pr2.items[0].serial_and_batch_bundle = pr1.items[0].serial_and_batch_bundle
+
+ self.assertRaises(frappe.exceptions.ValidationError, pr2.save)
+
def get_batch_from_bundle(bundle):
from erpnext.stock.serial_batch_bundle import get_batch_nos
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index f581fc6..276b2f4 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1040,7 +1040,7 @@
continue
bundle_doc = None
- if row.serial_and_batch_bundle and abs(row.qty) != abs(
+ if row.serial_and_batch_bundle and abs(row.transfer_qty) != abs(
frappe.get_cached_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "total_qty")
):
bundle_doc = SerialBatchCreation(
@@ -1050,7 +1050,7 @@
"serial_and_batch_bundle": row.serial_and_batch_bundle,
"type_of_transaction": "Outward",
"ignore_serial_nos": already_picked_serial_nos,
- "qty": row.qty * -1,
+ "qty": row.transfer_qty * -1,
}
).update_serial_and_batch_entries()
elif not row.serial_and_batch_bundle:
@@ -1062,7 +1062,7 @@
"posting_time": self.posting_time,
"voucher_type": self.doctype,
"voucher_detail_no": row.name,
- "qty": row.qty * -1,
+ "qty": row.transfer_qty * -1,
"ignore_serial_nos": already_picked_serial_nos,
"type_of_transaction": "Outward",
"company": self.company,