mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-04 12:49:10 +00:00
chore: rename type field to secondary_item_type (#55469)
This commit is contained in:
@@ -1442,7 +1442,7 @@ class StockController(AccountsController):
|
|||||||
elif self.doctype == "Stock Entry" and row.t_warehouse:
|
elif self.doctype == "Stock Entry" and row.t_warehouse:
|
||||||
qi_required = True # inward stock needs inspection
|
qi_required = True # inward stock needs inspection
|
||||||
|
|
||||||
if row.get("type") or row.get("is_legacy_scrap_item"):
|
if row.get("secondary_item_type") or row.get("is_legacy_scrap_item"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if qi_required: # validate row only if inspection is required on item level
|
if qi_required: # validate row only if inspection is required on item level
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class SubcontractingController(StockController):
|
|||||||
).format(item.idx, get_link_to_form("Item", item.item_code))
|
).format(item.idx, get_link_to_form("Item", item.item_code))
|
||||||
)
|
)
|
||||||
|
|
||||||
if not item.get("type") and not item.get("is_legacy_scrap_item"):
|
if not item.get("secondary_item_type") and not item.get("is_legacy_scrap_item"):
|
||||||
if not is_sub_contracted_item:
|
if not is_sub_contracted_item:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
|
_("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
|
||||||
@@ -1243,10 +1243,10 @@ class SubcontractingController(StockController):
|
|||||||
total_amt = sum(
|
total_amt = sum(
|
||||||
flt(item.amount)
|
flt(item.amount)
|
||||||
for item in self.get("items")
|
for item in self.get("items")
|
||||||
if not item.get("type") and not item.get("is_legacy_scrap_item")
|
if not item.get("secondary_item_type") and not item.get("is_legacy_scrap_item")
|
||||||
)
|
)
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if not item.get("type") and not item.get("is_legacy_scrap_item"):
|
if not item.get("secondary_item_type") and not item.get("is_legacy_scrap_item"):
|
||||||
item.additional_cost_per_qty = (
|
item.additional_cost_per_qty = (
|
||||||
(item.amount * self.total_additional_costs) / total_amt
|
(item.amount * self.total_additional_costs) / total_amt
|
||||||
) / item.qty
|
) / item.qty
|
||||||
@@ -1254,15 +1254,15 @@ class SubcontractingController(StockController):
|
|||||||
total_qty = sum(
|
total_qty = sum(
|
||||||
flt(item.qty)
|
flt(item.qty)
|
||||||
for item in self.get("items")
|
for item in self.get("items")
|
||||||
if not item.get("type") and not item.get("is_legacy_scrap_item")
|
if not item.get("secondary_item_type") and not item.get("is_legacy_scrap_item")
|
||||||
)
|
)
|
||||||
additional_cost_per_qty = self.total_additional_costs / total_qty
|
additional_cost_per_qty = self.total_additional_costs / total_qty
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if not item.get("type") and not item.get("is_legacy_scrap_item"):
|
if not item.get("secondary_item_type") and not item.get("is_legacy_scrap_item"):
|
||||||
item.additional_cost_per_qty = additional_cost_per_qty
|
item.additional_cost_per_qty = additional_cost_per_qty
|
||||||
else:
|
else:
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if not item.get("type") and not item.get("is_legacy_scrap_item"):
|
if not item.get("secondary_item_type") and not item.get("is_legacy_scrap_item"):
|
||||||
item.additional_cost_per_qty = 0
|
item.additional_cost_per_qty = 0
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ class SubcontractingInwardController:
|
|||||||
item
|
item
|
||||||
for item in self.get("items")
|
for item in self.get("items")
|
||||||
if not item.is_finished_item
|
if not item.is_finished_item
|
||||||
and not item.type
|
and not item.secondary_item_type
|
||||||
and not item.is_legacy_scrap_item
|
and not item.is_legacy_scrap_item
|
||||||
and frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item")
|
and frappe.get_cached_value("Item", item.item_code, "is_customer_provided_item")
|
||||||
]
|
]
|
||||||
@@ -372,7 +372,7 @@ class SubcontractingInwardController:
|
|||||||
if self.purpose in ["Subcontracting Delivery", "Subcontracting Return", "Manufacture"]:
|
if self.purpose in ["Subcontracting Delivery", "Subcontracting Return", "Manufacture"]:
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if (
|
if (
|
||||||
item.is_finished_item or item.type or item.is_legacy_scrap_item
|
item.is_finished_item or item.secondary_item_type or item.is_legacy_scrap_item
|
||||||
) and item.valuation_rate == 0:
|
) and item.valuation_rate == 0:
|
||||||
item.allow_zero_valuation_rate = 1
|
item.allow_zero_valuation_rate = 1
|
||||||
|
|
||||||
@@ -472,7 +472,7 @@ class SubcontractingInwardController:
|
|||||||
self.validate_delivery_on_save()
|
self.validate_delivery_on_save()
|
||||||
else:
|
else:
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if not item.type and not item.is_legacy_scrap_item:
|
if not item.secondary_item_type and not item.is_legacy_scrap_item:
|
||||||
delivered_qty, returned_qty = frappe.get_value(
|
delivered_qty, returned_qty = frappe.get_value(
|
||||||
"Subcontracting Inward Order Item",
|
"Subcontracting Inward Order Item",
|
||||||
item.scio_detail,
|
item.scio_detail,
|
||||||
@@ -543,7 +543,7 @@ class SubcontractingInwardController:
|
|||||||
bold(
|
bold(
|
||||||
frappe.get_cached_value(
|
frappe.get_cached_value(
|
||||||
"Subcontracting Inward Order Item"
|
"Subcontracting Inward Order Item"
|
||||||
if not item.type and not item.is_legacy_scrap_item
|
if not item.secondary_item_type and not item.is_legacy_scrap_item
|
||||||
else "Subcontracting Inward Order Secondary Item",
|
else "Subcontracting Inward Order Secondary Item",
|
||||||
item.scio_detail,
|
item.scio_detail,
|
||||||
"stock_uom",
|
"stock_uom",
|
||||||
@@ -595,7 +595,7 @@ class SubcontractingInwardController:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for item in [item for item in self.items if not item.is_finished_item]:
|
for item in [item for item in self.items if not item.is_finished_item]:
|
||||||
if item.type or item.is_legacy_scrap_item:
|
if item.secondary_item_type or item.is_legacy_scrap_item:
|
||||||
scio_secondary_item = frappe.get_value(
|
scio_secondary_item = frappe.get_value(
|
||||||
"Subcontracting Inward Order Secondary Item",
|
"Subcontracting Inward Order Secondary Item",
|
||||||
{
|
{
|
||||||
@@ -655,7 +655,7 @@ class SubcontractingInwardController:
|
|||||||
for item in self.items:
|
for item in self.items:
|
||||||
doctype = (
|
doctype = (
|
||||||
"Subcontracting Inward Order Item"
|
"Subcontracting Inward Order Item"
|
||||||
if not item.type and not item.is_legacy_scrap_item
|
if not item.secondary_item_type and not item.is_legacy_scrap_item
|
||||||
else "Subcontracting Inward Order Secondary Item"
|
else "Subcontracting Inward Order Secondary Item"
|
||||||
)
|
)
|
||||||
qty_map[doctype][item.scio_detail] += (
|
qty_map[doctype][item.scio_detail] += (
|
||||||
@@ -781,7 +781,7 @@ class SubcontractingInwardController:
|
|||||||
items = [
|
items = [
|
||||||
item
|
item
|
||||||
for item in self.items
|
for item in self.items
|
||||||
if not item.is_finished_item and not item.type and not item.is_legacy_scrap_item
|
if not item.is_finished_item and not item.secondary_item_type and not item.is_legacy_scrap_item
|
||||||
]
|
]
|
||||||
item_code_wh = frappe._dict(
|
item_code_wh = frappe._dict(
|
||||||
{
|
{
|
||||||
@@ -884,7 +884,9 @@ class SubcontractingInwardController:
|
|||||||
|
|
||||||
def update_inward_order_secondary_items(self):
|
def update_inward_order_secondary_items(self):
|
||||||
if (scio := self.subcontracting_inward_order) and self.purpose == "Manufacture":
|
if (scio := self.subcontracting_inward_order) and self.purpose == "Manufacture":
|
||||||
secondary_items_list = [item for item in self.items if item.type or item.is_legacy_scrap_item]
|
secondary_items_list = [
|
||||||
|
item for item in self.items if item.secondary_item_type or item.is_legacy_scrap_item
|
||||||
|
]
|
||||||
|
|
||||||
secondary_items = defaultdict(float)
|
secondary_items = defaultdict(float)
|
||||||
for item in secondary_items_list:
|
for item in secondary_items_list:
|
||||||
@@ -958,7 +960,7 @@ class SubcontractingInwardController:
|
|||||||
stock_uom=secondary_item.stock_uom,
|
stock_uom=secondary_item.stock_uom,
|
||||||
warehouse=secondary_item.t_warehouse,
|
warehouse=secondary_item.t_warehouse,
|
||||||
produced_qty=secondary_item.transfer_qty,
|
produced_qty=secondary_item.transfer_qty,
|
||||||
type=secondary_item.type,
|
secondary_item_type=secondary_item.secondary_item_type,
|
||||||
delivered_qty=0,
|
delivered_qty=0,
|
||||||
reference_name=frappe.get_value(
|
reference_name=frappe.get_value(
|
||||||
"Work Order", self.work_order, "subcontracting_inward_order_item"
|
"Work Order", self.work_order, "subcontracting_inward_order_item"
|
||||||
|
|||||||
@@ -338,14 +338,14 @@ class BOM(WebsiteGenerator):
|
|||||||
if not item.qty:
|
if not item.qty:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{0}: Quantity should be greater than 0 for {1} Item {2}").format(
|
_("Row #{0}: Quantity should be greater than 0 for {1} Item {2}").format(
|
||||||
item.idx, item.type, get_link_to_form("Item", item.item_code)
|
item.idx, item.secondary_item_type, get_link_to_form("Item", item.item_code)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if item.process_loss_per >= 100:
|
if item.process_loss_per >= 100:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{0}: Process Loss Percentage should be less than 100% for {1} Item {2}").format(
|
_("Row #{0}: Process Loss Percentage should be less than 100% for {1} Item {2}").format(
|
||||||
item.idx, item.type, get_link_to_form("Item", item.item_code)
|
item.idx, item.secondary_item_type, get_link_to_form("Item", item.item_code)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1285,7 +1285,9 @@ class BOM(WebsiteGenerator):
|
|||||||
frappe.throw(msg, title=_("Invalid Process Loss Configuration"))
|
frappe.throw(msg, title=_("Invalid Process Loss Configuration"))
|
||||||
|
|
||||||
def has_scrap_items(self):
|
def has_scrap_items(self):
|
||||||
return any(d.get("type") == "Scrap" or d.get("is_legacy") for d in self.get("secondary_items"))
|
return any(
|
||||||
|
d.get("secondary_item_type") == "Scrap" or d.get("is_legacy") for d in self.get("secondary_items")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_bom_item_rate(args, bom_doc):
|
def get_bom_item_rate(args, bom_doc):
|
||||||
@@ -1453,7 +1455,7 @@ def get_bom_items_as_dict(
|
|||||||
query = query.format(
|
query = query.format(
|
||||||
table="BOM Secondary Item",
|
table="BOM Secondary Item",
|
||||||
where_conditions=")",
|
where_conditions=")",
|
||||||
select_columns=", item.description, bom_item.cost_allocation_per, bom_item.process_loss_per, bom_item.type, bom_item.name, bom_item.is_legacy",
|
select_columns=", item.description, bom_item.cost_allocation_per, bom_item.process_loss_per, bom_item.secondary_item_type, bom_item.name, bom_item.is_legacy",
|
||||||
is_stock_item=is_stock_item,
|
is_stock_item=is_stock_item,
|
||||||
qty_field="stock_qty",
|
qty_field="stock_qty",
|
||||||
group_by_cond=group_by_cond,
|
group_by_cond=group_by_cond,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
"stock_qty": 1.0,
|
"stock_qty": 1.0,
|
||||||
"rate": 2000.0,
|
"rate": 2000.0,
|
||||||
"stock_uom": "_Test UOM",
|
"stock_uom": "_Test UOM",
|
||||||
"type": "Scrap",
|
"secondary_item_type": "Scrap",
|
||||||
"is_legacy": 1
|
"is_legacy": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"type",
|
"secondary_item_type",
|
||||||
"rate",
|
"rate",
|
||||||
"column_break_gres",
|
"column_break_gres",
|
||||||
"is_legacy",
|
"is_legacy",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!doc.is_legacy",
|
"depends_on": "eval:!doc.is_legacy",
|
||||||
"fieldname": "type",
|
"fieldname": "secondary_item_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-03-11 12:12:29.208031",
|
"modified": "2026-06-01 10:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Secondary Item",
|
"name": "BOM Secondary Item",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class BOMSecondaryItem(Document):
|
|||||||
rate: DF.Currency
|
rate: DF.Currency
|
||||||
stock_qty: DF.Float
|
stock_qty: DF.Float
|
||||||
stock_uom: DF.Link | None
|
stock_uom: DF.Link | None
|
||||||
type: DF.Literal["", "Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
secondary_item_type: DF.Literal["", "Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
||||||
uom: DF.Link
|
uom: DF.Link
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ class JobCard(Document):
|
|||||||
"stock_qty": values.qty,
|
"stock_qty": values.qty,
|
||||||
"item_name": values.item_name,
|
"item_name": values.item_name,
|
||||||
"stock_uom": values.stock_uom,
|
"stock_uom": values.stock_uom,
|
||||||
"type": values.type,
|
"secondary_item_type": values.secondary_item_type,
|
||||||
"bom_secondary_item": values.name,
|
"bom_secondary_item": values.name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1532,7 +1532,7 @@ class JobCard(Document):
|
|||||||
add_additional_cost(ste.stock_entry, wo_doc, self)
|
add_additional_cost(ste.stock_entry, wo_doc, self)
|
||||||
ManufactureStockEntry(ste.stock_entry).add_secondary_items_from_job_card()
|
ManufactureStockEntry(ste.stock_entry).add_secondary_items_from_job_card()
|
||||||
for row in ste.stock_entry.items:
|
for row in ste.stock_entry.items:
|
||||||
if (row.type or row.is_legacy_scrap_item) and not row.t_warehouse:
|
if (row.secondary_item_type or row.is_legacy_scrap_item) and not row.t_warehouse:
|
||||||
row.t_warehouse = self.target_warehouse
|
row.t_warehouse = self.target_warehouse
|
||||||
|
|
||||||
if auto_submit:
|
if auto_submit:
|
||||||
|
|||||||
@@ -913,7 +913,7 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
"qty": 1,
|
"qty": 1,
|
||||||
"process_loss_per": 10,
|
"process_loss_per": 10,
|
||||||
"cost_allocation_per": 5,
|
"cost_allocation_per": 5,
|
||||||
"type": "Scrap",
|
"secondary_item_type": "Scrap",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if submit:
|
if submit:
|
||||||
@@ -996,7 +996,8 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
job_card.append(
|
job_card.append(
|
||||||
"secondary_items", {"item_code": scrap_extra.name, "stock_qty": 5, "type": "Co-Product"}
|
"secondary_items",
|
||||||
|
{"item_code": scrap_extra.name, "stock_qty": 5, "secondary_item_type": "Co-Product"},
|
||||||
)
|
)
|
||||||
job_card.submit()
|
job_card.submit()
|
||||||
|
|
||||||
@@ -1015,7 +1016,7 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
self.assertEqual(manufacturing_entry.items[2].qty, 9)
|
self.assertEqual(manufacturing_entry.items[2].qty, 9)
|
||||||
self.assertEqual(flt(manufacturing_entry.items[2].basic_rate, 3), 5.556)
|
self.assertEqual(flt(manufacturing_entry.items[2].basic_rate, 3), 5.556)
|
||||||
self.assertEqual(manufacturing_entry.items[3].item_code, scrap_extra.name)
|
self.assertEqual(manufacturing_entry.items[3].item_code, scrap_extra.name)
|
||||||
self.assertEqual(manufacturing_entry.items[3].type, "Co-Product")
|
self.assertEqual(manufacturing_entry.items[3].secondary_item_type, "Co-Product")
|
||||||
self.assertEqual(manufacturing_entry.items[3].qty, 5)
|
self.assertEqual(manufacturing_entry.items[3].qty, 5)
|
||||||
self.assertEqual(manufacturing_entry.items[3].basic_rate, 0)
|
self.assertEqual(manufacturing_entry.items[3].basic_rate, 0)
|
||||||
|
|
||||||
@@ -1060,7 +1061,9 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
)
|
)
|
||||||
|
|
||||||
job_card = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name})
|
job_card = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name})
|
||||||
job_card.append("secondary_items", {"item_code": "_Test Item", "stock_qty": 2, "type": "Scrap"})
|
job_card.append(
|
||||||
|
"secondary_items", {"item_code": "_Test Item", "stock_qty": 2, "secondary_item_type": "Scrap"}
|
||||||
|
)
|
||||||
job_card.append(
|
job_card.append(
|
||||||
"time_logs",
|
"time_logs",
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"type",
|
"secondary_item_type",
|
||||||
"description",
|
"description",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"item_code",
|
"item_code",
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "type",
|
"fieldname": "secondary_item_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-03-06 13:51:00.492621",
|
"modified": "2026-06-01 10:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Job Card Secondary Item",
|
"name": "Job Card Secondary Item",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class JobCardSecondaryItem(Document):
|
|||||||
parenttype: DF.Data
|
parenttype: DF.Data
|
||||||
stock_qty: DF.Float
|
stock_qty: DF.Float
|
||||||
stock_uom: DF.Link | None
|
stock_uom: DF.Link | None
|
||||||
type: DF.Literal["Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
secondary_item_type: DF.Literal["Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -2903,7 +2903,7 @@ def make_bom(**args):
|
|||||||
bom.append(
|
bom.append(
|
||||||
"secondary_items",
|
"secondary_items",
|
||||||
{
|
{
|
||||||
"type": "Scrap",
|
"secondary_item_type": "Scrap",
|
||||||
"item_code": item,
|
"item_code": item,
|
||||||
"item_name": item,
|
"item_name": item,
|
||||||
"uom": item_doc.stock_uom,
|
"uom": item_doc.stock_uom,
|
||||||
|
|||||||
@@ -1096,7 +1096,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
|||||||
|
|
||||||
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
|
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
|
||||||
for row in stock_entry.items:
|
for row in stock_entry.items:
|
||||||
if row.type or row.is_legacy_scrap_item:
|
if row.secondary_item_type or row.is_legacy_scrap_item:
|
||||||
self.assertEqual(row.qty, 1)
|
self.assertEqual(row.qty, 1)
|
||||||
|
|
||||||
# Partial Job Card 1 with qty 10
|
# Partial Job Card 1 with qty 10
|
||||||
@@ -1108,7 +1108,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
|||||||
|
|
||||||
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
|
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
|
||||||
for row in stock_entry.items:
|
for row in stock_entry.items:
|
||||||
if row.type or row.is_legacy_scrap_item:
|
if row.secondary_item_type or row.is_legacy_scrap_item:
|
||||||
self.assertEqual(row.qty, 2)
|
self.assertEqual(row.qty, 2)
|
||||||
|
|
||||||
# Partial Job Card 2 with qty 10
|
# Partial Job Card 2 with qty 10
|
||||||
@@ -2193,7 +2193,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
|||||||
self.assertTrue(se_doc.additional_costs)
|
self.assertTrue(se_doc.additional_costs)
|
||||||
secondary_items = []
|
secondary_items = []
|
||||||
for item in se_doc.items:
|
for item in se_doc.items:
|
||||||
if item.type or item.is_legacy_scrap_item:
|
if item.secondary_item_type or item.is_legacy_scrap_item:
|
||||||
secondary_items.append(item.item_code)
|
secondary_items.append(item.item_code)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -2658,7 +2658,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
|||||||
# Secondary/Scrap item: should be taken from scrap warehouse in disassembly
|
# Secondary/Scrap item: should be taken from scrap warehouse in disassembly
|
||||||
scrap_row = next((i for i in stock_entry.items if i.item_code == scrap_item), None)
|
scrap_row = next((i for i in stock_entry.items if i.item_code == scrap_item), None)
|
||||||
self.assertIsNotNone(scrap_row)
|
self.assertIsNotNone(scrap_row)
|
||||||
self.assertEqual(scrap_row.type, "Scrap")
|
self.assertEqual(scrap_row.secondary_item_type, "Scrap")
|
||||||
self.assertTrue(scrap_row.s_warehouse)
|
self.assertTrue(scrap_row.s_warehouse)
|
||||||
self.assertFalse(scrap_row.t_warehouse)
|
self.assertFalse(scrap_row.t_warehouse)
|
||||||
self.assertEqual(scrap_row.s_warehouse, wo.scrap_warehouse)
|
self.assertEqual(scrap_row.s_warehouse, wo.scrap_warehouse)
|
||||||
|
|||||||
@@ -483,3 +483,4 @@ erpnext.patches.v16_0.packed_item_inv_dimen
|
|||||||
erpnext.patches.v16_0.set_not_applicable_on_german_item_tax_templates
|
erpnext.patches.v16_0.set_not_applicable_on_german_item_tax_templates
|
||||||
erpnext.patches.v16_0.clear_procedures_from_receivable_report
|
erpnext.patches.v16_0.clear_procedures_from_receivable_report
|
||||||
erpnext.patches.v16_0.migrate_address_contact_custom_fields
|
erpnext.patches.v16_0.migrate_address_contact_custom_fields
|
||||||
|
erpnext.patches.v16_0.rename_secondary_item_type_field
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def insert_into_bom():
|
|||||||
"conversion_factor": 1,
|
"conversion_factor": 1,
|
||||||
"qty": item.stock_qty,
|
"qty": item.stock_qty,
|
||||||
"is_legacy": 1,
|
"is_legacy": 1,
|
||||||
"type": "Scrap",
|
"secondary_item_type": "Scrap",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
secondary_item.insert()
|
secondary_item.insert()
|
||||||
@@ -49,7 +49,14 @@ def insert_into_bom():
|
|||||||
|
|
||||||
def insert_into_job_card():
|
def insert_into_job_card():
|
||||||
fields = ["item_code", "item_name", "description", "stock_qty", "stock_uom"]
|
fields = ["item_code", "item_name", "description", "stock_qty", "stock_uom"]
|
||||||
bulk_insert("Job Card", "Job Card Scrap Item", "Job Card Secondary Item", fields, ["type"], ["Scrap"])
|
bulk_insert(
|
||||||
|
"Job Card",
|
||||||
|
"Job Card Scrap Item",
|
||||||
|
"Job Card Secondary Item",
|
||||||
|
fields,
|
||||||
|
["secondary_item_type"],
|
||||||
|
["Scrap"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def insert_into_subcontracting_inward():
|
def insert_into_subcontracting_inward():
|
||||||
@@ -67,7 +74,7 @@ def insert_into_subcontracting_inward():
|
|||||||
"Subcontracting Inward Order Scrap Item",
|
"Subcontracting Inward Order Scrap Item",
|
||||||
"Subcontracting Inward Order Secondary Item",
|
"Subcontracting Inward Order Secondary Item",
|
||||||
fields,
|
fields,
|
||||||
["type"],
|
["secondary_item_type"],
|
||||||
["Scrap"],
|
["Scrap"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
18
erpnext/patches/v16_0/rename_secondary_item_type_field.py
Normal file
18
erpnext/patches/v16_0/rename_secondary_item_type_field.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
doctypes = [
|
||||||
|
"BOM Secondary Item",
|
||||||
|
"Job Card Secondary Item",
|
||||||
|
"Stock Entry Detail",
|
||||||
|
"Subcontracting Inward Order Secondary Item",
|
||||||
|
"Subcontracting Receipt Item",
|
||||||
|
]
|
||||||
|
|
||||||
|
for doctype in doctypes:
|
||||||
|
if not frappe.db.has_column(doctype, "type"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
rename_field(doctype, "type", "secondary_item_type")
|
||||||
@@ -572,7 +572,7 @@ class StockEntry(StockController, SubcontractingInwardController):
|
|||||||
|
|
||||||
if self.bom_no:
|
if self.bom_no:
|
||||||
d.basic_rate *= frappe.get_value("BOM", self.bom_no, "cost_allocation_per") / 100
|
d.basic_rate *= frappe.get_value("BOM", self.bom_no, "cost_allocation_per") / 100
|
||||||
elif d.type and d.bom_secondary_item:
|
elif d.secondary_item_type and d.bom_secondary_item:
|
||||||
cost_allocation_per = frappe.get_value(
|
cost_allocation_per = frappe.get_value(
|
||||||
"BOM Secondary Item", d.bom_secondary_item, "cost_allocation_per"
|
"BOM Secondary Item", d.bom_secondary_item, "cost_allocation_per"
|
||||||
)
|
)
|
||||||
@@ -693,7 +693,7 @@ class StockEntry(StockController, SubcontractingInwardController):
|
|||||||
|
|
||||||
def _validate_no_raw_materials_in_manufacture_entry(self, settings):
|
def _validate_no_raw_materials_in_manufacture_entry(self, settings):
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if not item.is_finished_item and not item.type and not item.is_legacy_scrap_item:
|
if not item.is_finished_item and not item.secondary_item_type and not item.is_legacy_scrap_item:
|
||||||
label = frappe.get_meta(settings.doctype).get_label("get_rm_cost_from_consumption_entry")
|
label = frappe.get_meta(settings.doctype).get_label("get_rm_cost_from_consumption_entry")
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
@@ -835,7 +835,7 @@ class StockEntry(StockController, SubcontractingInwardController):
|
|||||||
d.is_finished_item = 1
|
d.is_finished_item = 1
|
||||||
else:
|
else:
|
||||||
d.is_finished_item = 0
|
d.is_finished_item = 0
|
||||||
d.type = ""
|
d.secondary_item_type = ""
|
||||||
|
|
||||||
def get_finished_item(self):
|
def get_finished_item(self):
|
||||||
finished_item = None
|
finished_item = None
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class DisassembleStockEntry(BaseStockEntry):
|
|||||||
"s_warehouse": s_warehouse,
|
"s_warehouse": s_warehouse,
|
||||||
"t_warehouse": t_warehouse,
|
"t_warehouse": t_warehouse,
|
||||||
"is_finished_item": source_row.is_finished_item,
|
"is_finished_item": source_row.is_finished_item,
|
||||||
"type": source_row.type,
|
"secondary_item_type": source_row.secondary_item_type,
|
||||||
"is_legacy_scrap_item": source_row.is_legacy_scrap_item,
|
"is_legacy_scrap_item": source_row.is_legacy_scrap_item,
|
||||||
"bom_secondary_item": source_row.bom_secondary_item,
|
"bom_secondary_item": source_row.bom_secondary_item,
|
||||||
"bom_no": source_row.bom_no,
|
"bom_no": source_row.bom_no,
|
||||||
@@ -185,7 +185,7 @@ class DisassembleStockEntry(BaseStockEntry):
|
|||||||
"conversion_factor",
|
"conversion_factor",
|
||||||
"item_group",
|
"item_group",
|
||||||
"description",
|
"description",
|
||||||
"type",
|
"secondary_item_type",
|
||||||
]
|
]
|
||||||
for field in fields:
|
for field in fields:
|
||||||
item_args[field] = row.get(field)
|
item_args[field] = row.get(field)
|
||||||
@@ -235,7 +235,7 @@ class DisassembleStockEntry(BaseStockEntry):
|
|||||||
SED.basic_rate,
|
SED.basic_rate,
|
||||||
SED.conversion_factor,
|
SED.conversion_factor,
|
||||||
SED.is_finished_item,
|
SED.is_finished_item,
|
||||||
SED.type,
|
SED.secondary_item_type,
|
||||||
SED.is_legacy_scrap_item,
|
SED.is_legacy_scrap_item,
|
||||||
SED.bom_secondary_item,
|
SED.bom_secondary_item,
|
||||||
SED.batch_no,
|
SED.batch_no,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class BaseManufactureStockEntry(BaseStockEntry):
|
|||||||
and self.doc.from_warehouse
|
and self.doc.from_warehouse
|
||||||
and not row.is_finished_item
|
and not row.is_finished_item
|
||||||
and not row.is_legacy_scrap_item
|
and not row.is_legacy_scrap_item
|
||||||
and not row.type
|
and not row.secondary_item_type
|
||||||
):
|
):
|
||||||
row.s_warehouse = self.doc.from_warehouse
|
row.s_warehouse = self.doc.from_warehouse
|
||||||
row.t_warehouse = None
|
row.t_warehouse = None
|
||||||
@@ -33,7 +33,7 @@ class BaseManufactureStockEntry(BaseStockEntry):
|
|||||||
elif (
|
elif (
|
||||||
not row.t_warehouse
|
not row.t_warehouse
|
||||||
and self.doc.to_warehouse
|
and self.doc.to_warehouse
|
||||||
and (row.is_finished_item or row.is_legacy_scrap_item or row.type)
|
and (row.is_finished_item or row.is_legacy_scrap_item or row.secondary_item_type)
|
||||||
):
|
):
|
||||||
row.t_warehouse = self.doc.to_warehouse
|
row.t_warehouse = self.doc.to_warehouse
|
||||||
row.s_warehouse = None
|
row.s_warehouse = None
|
||||||
@@ -83,10 +83,10 @@ class BaseManufactureStockEntry(BaseStockEntry):
|
|||||||
for row in secondary_items:
|
for row in secondary_items:
|
||||||
item_args = self.get_item_dict(row)
|
item_args = self.get_item_dict(row)
|
||||||
item_args["is_legacy_scrap_item"] = bool(row.get("is_legacy"))
|
item_args["is_legacy_scrap_item"] = bool(row.get("is_legacy"))
|
||||||
item_args["type"] = row.type
|
item_args["secondary_item_type"] = row.secondary_item_type
|
||||||
item_args["bom_secondary_item"] = row.name
|
item_args["bom_secondary_item"] = row.name
|
||||||
|
|
||||||
if row.type == "Scrap" and self.wo_doc and self.wo_doc.get("scrap_warehouse"):
|
if row.secondary_item_type == "Scrap" and self.wo_doc and self.wo_doc.get("scrap_warehouse"):
|
||||||
item_args["t_warehouse"] = self.wo_doc.scrap_warehouse
|
item_args["t_warehouse"] = self.wo_doc.scrap_warehouse
|
||||||
else:
|
else:
|
||||||
item_args["t_warehouse"] = self.doc.to_warehouse
|
item_args["t_warehouse"] = self.doc.to_warehouse
|
||||||
@@ -591,7 +591,7 @@ class ManufactureStockEntry(BaseManufactureStockEntry):
|
|||||||
row.s_warehouse = None
|
row.s_warehouse = None
|
||||||
row.t_warehouse = row.warehouse or self.doc.to_warehouse
|
row.t_warehouse = row.warehouse or self.doc.to_warehouse
|
||||||
row.is_legacy_scrap_item = row.is_legacy
|
row.is_legacy_scrap_item = row.is_legacy
|
||||||
row.type = row.get("type")
|
row.secondary_item_type = row.get("secondary_item_type")
|
||||||
|
|
||||||
self.doc.append("items", row)
|
self.doc.append("items", row)
|
||||||
|
|
||||||
@@ -633,7 +633,7 @@ class ManufactureStockEntry(BaseManufactureStockEntry):
|
|||||||
.select(sed.item_code, sed.qty)
|
.select(sed.item_code, sed.qty)
|
||||||
.where(
|
.where(
|
||||||
(se.work_order == self.doc.work_order)
|
(se.work_order == self.doc.work_order)
|
||||||
& ((sed.type.isnotnull()) | (sed.is_legacy_scrap_item == 1))
|
& ((sed.secondary_item_type.isnotnull()) | (sed.is_legacy_scrap_item == 1))
|
||||||
& (se.docstatus == 1)
|
& (se.docstatus == 1)
|
||||||
& (se.purpose.isin(["Repack", "Manufacture"]))
|
& (se.purpose.isin(["Repack", "Manufacture"]))
|
||||||
)
|
)
|
||||||
@@ -832,7 +832,7 @@ def _add_bom_table_specific_fields(query, doctype, table_name):
|
|||||||
doctype.cost_allocation_per,
|
doctype.cost_allocation_per,
|
||||||
doctype.uom,
|
doctype.uom,
|
||||||
doctype.process_loss_per,
|
doctype.process_loss_per,
|
||||||
doctype.type,
|
doctype.secondary_item_type,
|
||||||
doctype.is_legacy,
|
doctype.is_legacy,
|
||||||
doctype.conversion_factor,
|
doctype.conversion_factor,
|
||||||
)
|
)
|
||||||
@@ -891,7 +891,7 @@ def get_secondary_items_from_job_card(work_order, jc_name=None):
|
|||||||
job_card_secondary_item.item_name,
|
job_card_secondary_item.item_name,
|
||||||
job_card_secondary_item.description,
|
job_card_secondary_item.description,
|
||||||
job_card_secondary_item.stock_uom,
|
job_card_secondary_item.stock_uom,
|
||||||
job_card_secondary_item.type,
|
job_card_secondary_item.secondary_item_type,
|
||||||
job_card_secondary_item.bom_secondary_item,
|
job_card_secondary_item.bom_secondary_item,
|
||||||
)
|
)
|
||||||
.join(job_card_secondary_item)
|
.join(job_card_secondary_item)
|
||||||
@@ -901,7 +901,7 @@ def get_secondary_items_from_job_card(work_order, jc_name=None):
|
|||||||
& (job_card.work_order == work_order)
|
& (job_card.work_order == work_order)
|
||||||
& (job_card.docstatus == 1)
|
& (job_card.docstatus == 1)
|
||||||
)
|
)
|
||||||
.groupby(job_card_secondary_item.item_code, job_card_secondary_item.type)
|
.groupby(job_card_secondary_item.item_code, job_card_secondary_item.secondary_item_type)
|
||||||
.orderby(job_card_secondary_item.idx)
|
.orderby(job_card_secondary_item.idx)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -908,7 +908,9 @@ class TestStockEntry(ERPNextTestSuite):
|
|||||||
if d.s_warehouse:
|
if d.s_warehouse:
|
||||||
rm_cost += d.amount
|
rm_cost += d.amount
|
||||||
fg_cost = next(filter(lambda x: x.item_code == "_Test FG Item", s.get("items"))).amount
|
fg_cost = next(filter(lambda x: x.item_code == "_Test FG Item", s.get("items"))).amount
|
||||||
secondary_item_cost = next(filter(lambda x: x.type or x.is_legacy_scrap_item, s.get("items"))).amount
|
secondary_item_cost = next(
|
||||||
|
filter(lambda x: x.secondary_item_type or x.is_legacy_scrap_item, s.get("items"))
|
||||||
|
).amount
|
||||||
|
|
||||||
self.assertEqual(fg_cost, flt(rm_cost - secondary_item_cost, 2))
|
self.assertEqual(fg_cost, flt(rm_cost - secondary_item_cost, 2))
|
||||||
|
|
||||||
@@ -1027,7 +1029,7 @@ class TestStockEntry(ERPNextTestSuite):
|
|||||||
basic_rate=row.basic_rate or 100,
|
basic_rate=row.basic_rate or 100,
|
||||||
)
|
)
|
||||||
|
|
||||||
if row.type or row.is_legacy_scrap_item:
|
if row.secondary_item_type or row.is_legacy_scrap_item:
|
||||||
row.item_code = secondary_item
|
row.item_code = secondary_item
|
||||||
row.uom = frappe.db.get_value("Item", secondary_item, "stock_uom")
|
row.uom = frappe.db.get_value("Item", secondary_item, "stock_uom")
|
||||||
row.stock_uom = frappe.db.get_value("Item", secondary_item, "stock_uom")
|
row.stock_uom = frappe.db.get_value("Item", secondary_item, "stock_uom")
|
||||||
@@ -1035,10 +1037,16 @@ class TestStockEntry(ERPNextTestSuite):
|
|||||||
stock_entry.inspection_required = 1
|
stock_entry.inspection_required = 1
|
||||||
stock_entry.save()
|
stock_entry.save()
|
||||||
|
|
||||||
self.assertTrue([row.item_code for row in stock_entry.items if row.type or row.is_legacy_scrap_item])
|
self.assertTrue(
|
||||||
|
[
|
||||||
|
row.item_code
|
||||||
|
for row in stock_entry.items
|
||||||
|
if row.secondary_item_type or row.is_legacy_scrap_item
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
for row in stock_entry.items:
|
for row in stock_entry.items:
|
||||||
if not row.type and not row.is_legacy_scrap_item:
|
if not row.secondary_item_type and not row.is_legacy_scrap_item:
|
||||||
qc = frappe.get_doc(
|
qc = frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Quality Inspection",
|
"doctype": "Quality Inspection",
|
||||||
@@ -1058,7 +1066,7 @@ class TestStockEntry(ERPNextTestSuite):
|
|||||||
stock_entry.reload()
|
stock_entry.reload()
|
||||||
stock_entry.submit()
|
stock_entry.submit()
|
||||||
for row in stock_entry.items:
|
for row in stock_entry.items:
|
||||||
if row.type or row.is_legacy_scrap_item:
|
if row.secondary_item_type or row.is_legacy_scrap_item:
|
||||||
self.assertFalse(row.quality_inspection)
|
self.assertFalse(row.quality_inspection)
|
||||||
else:
|
else:
|
||||||
self.assertTrue(row.quality_inspection)
|
self.assertTrue(row.quality_inspection)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"col_break2",
|
"col_break2",
|
||||||
"is_finished_item",
|
"is_finished_item",
|
||||||
"is_legacy_scrap_item",
|
"is_legacy_scrap_item",
|
||||||
"type",
|
"secondary_item_type",
|
||||||
"quality_inspection",
|
"quality_inspection",
|
||||||
"subcontracted_item",
|
"subcontracted_item",
|
||||||
"against_fg",
|
"against_fg",
|
||||||
@@ -559,7 +559,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.is_legacy_scrap_item && !doc.type",
|
"depends_on": "eval:!doc.is_legacy_scrap_item && !doc.secondary_item_type",
|
||||||
"fieldname": "is_finished_item",
|
"fieldname": "is_finished_item",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Finished Item",
|
"label": "Is Finished Item",
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:parent.purpose == \"Manufacture\" && doc.t_warehouse && !doc.is_finished_item && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:parent.purpose == \"Manufacture\" && doc.t_warehouse && !doc.is_finished_item && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "type",
|
"fieldname": "secondary_item_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"options": "\nCo-Product\nBy-Product\nScrap\nAdditional Finished Good"
|
"options": "\nCo-Product\nBy-Product\nScrap\nAdditional Finished Good"
|
||||||
@@ -679,7 +679,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-04-27 11:40:38.294196",
|
"modified": "2026-06-01 10:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Entry Detail",
|
"name": "Stock Entry Detail",
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class StockEntryDetail(Document):
|
|||||||
t_warehouse: DF.Link | None
|
t_warehouse: DF.Link | None
|
||||||
transfer_qty: DF.Float
|
transfer_qty: DF.Float
|
||||||
transferred_qty: DF.Float
|
transferred_qty: DF.Float
|
||||||
type: DF.Literal["", "Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
secondary_item_type: DF.Literal["", "Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
||||||
uom: DF.Link
|
uom: DF.Link
|
||||||
use_serial_batch_fields: DF.Check
|
use_serial_batch_fields: DF.Check
|
||||||
valuation_rate: DF.Currency
|
valuation_rate: DF.Currency
|
||||||
|
|||||||
@@ -499,7 +499,7 @@ class SubcontractingInwardOrder(SubcontractingController):
|
|||||||
"s_warehouse": secondary_item.warehouse,
|
"s_warehouse": secondary_item.warehouse,
|
||||||
"stock_uom": secondary_item.stock_uom,
|
"stock_uom": secondary_item.stock_uom,
|
||||||
"scio_detail": secondary_item.name,
|
"scio_detail": secondary_item.name,
|
||||||
"type": secondary_item.type,
|
"secondary_item_type": secondary_item.secondary_item_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ class IntegrationTestSubcontractingInwardOrder(ERPNextTestSuite):
|
|||||||
def test_secondary_items_delivery(self):
|
def test_secondary_items_delivery(self):
|
||||||
new_bom = frappe.copy_doc(frappe.get_doc("BOM", "BOM-Basic FG Item-001"))
|
new_bom = frappe.copy_doc(frappe.get_doc("BOM", "BOM-Basic FG Item-001"))
|
||||||
new_bom.secondary_items.append(
|
new_bom.secondary_items.append(
|
||||||
frappe.new_doc("BOM Secondary Item", item_code="Basic RM 2", qty=1, type="Scrap")
|
frappe.new_doc("BOM Secondary Item", item_code="Basic RM 2", qty=1, secondary_item_type="Scrap")
|
||||||
)
|
)
|
||||||
new_bom.submit()
|
new_bom.submit()
|
||||||
sc_bom = frappe.get_doc("Subcontracting BOM", "SB-0001")
|
sc_bom = frappe.get_doc("Subcontracting BOM", "SB-0001")
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"column_break_rptg",
|
"column_break_rptg",
|
||||||
"type",
|
"secondary_item_type",
|
||||||
"reference_name",
|
"reference_name",
|
||||||
"column_break_jkzt",
|
"column_break_jkzt",
|
||||||
"item_code",
|
"item_code",
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "type",
|
"fieldname": "secondary_item_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-02-27 15:15:40.009957",
|
"modified": "2026-06-01 10:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Inward Order Secondary Item",
|
"name": "Subcontracting Inward Order Secondary Item",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class SubcontractingInwardOrderSecondaryItem(Document):
|
|||||||
produced_qty: DF.Float
|
produced_qty: DF.Float
|
||||||
reference_name: DF.Data
|
reference_name: DF.Data
|
||||||
stock_uom: DF.Link
|
stock_uom: DF.Link
|
||||||
type: DF.Literal["Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
secondary_item_type: DF.Literal["Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
||||||
warehouse: DF.Link
|
warehouse: DF.Link
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
|
|||||||
@@ -420,7 +420,7 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
self.append(
|
self.append(
|
||||||
"items",
|
"items",
|
||||||
{
|
{
|
||||||
"type": secondary_item.type,
|
"secondary_item_type": secondary_item.secondary_item_type,
|
||||||
"is_legacy_scrap_item": secondary_item.is_legacy,
|
"is_legacy_scrap_item": secondary_item.is_legacy,
|
||||||
"reference_name": item.name,
|
"reference_name": item.name,
|
||||||
"item_code": secondary_item.item_code,
|
"item_code": secondary_item.item_code,
|
||||||
@@ -448,7 +448,7 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
|
|
||||||
def remove_secondary_items(self):
|
def remove_secondary_items(self):
|
||||||
for item in list(self.items):
|
for item in list(self.items):
|
||||||
if item.type or item.is_legacy_scrap_item:
|
if item.secondary_item_type or item.is_legacy_scrap_item:
|
||||||
self.remove(item)
|
self.remove(item)
|
||||||
else:
|
else:
|
||||||
item.secondary_items_cost_per_qty = 0
|
item.secondary_items_cost_per_qty = 0
|
||||||
@@ -508,7 +508,7 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
|
|
||||||
secondary_items_cost_map = {}
|
secondary_items_cost_map = {}
|
||||||
for item in self.get("items") or []:
|
for item in self.get("items") or []:
|
||||||
if item.type or item.is_legacy_scrap_item:
|
if item.secondary_item_type or item.is_legacy_scrap_item:
|
||||||
qty = (
|
qty = (
|
||||||
flt(item.qty)
|
flt(item.qty)
|
||||||
if item.is_legacy_scrap_item
|
if item.is_legacy_scrap_item
|
||||||
@@ -523,7 +523,7 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
|
|
||||||
total_qty = total_amount = 0
|
total_qty = total_amount = 0
|
||||||
for item in self.get("items") or []:
|
for item in self.get("items") or []:
|
||||||
if not item.type and not item.is_legacy_scrap_item:
|
if not item.secondary_item_type and not item.is_legacy_scrap_item:
|
||||||
if item.qty:
|
if item.qty:
|
||||||
if item.name in rm_cost_map:
|
if item.name in rm_cost_map:
|
||||||
item.rm_supp_cost = rm_cost_map[item.name]
|
item.rm_supp_cost = rm_cost_map[item.name]
|
||||||
@@ -567,7 +567,7 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
|
|
||||||
def validate_secondary_items(self):
|
def validate_secondary_items(self):
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if item.type or item.is_legacy_scrap_item:
|
if item.secondary_item_type or item.is_legacy_scrap_item:
|
||||||
if not item.qty:
|
if not item.qty:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{0}: Secondary Item Qty cannot be zero").format(item.idx),
|
_("Row #{0}: Secondary Item Qty cannot be zero").format(item.idx),
|
||||||
|
|||||||
@@ -1220,7 +1220,7 @@ class TestSubcontractingReceipt(ERPNextTestSuite):
|
|||||||
scr.get_secondary_items()
|
scr.get_secondary_items()
|
||||||
|
|
||||||
scr_secondary_items = set(
|
scr_secondary_items = set(
|
||||||
[item.item_code for item in scr.items if item.type or item.is_legacy_scrap_item]
|
[item.item_code for item in scr.items if item.secondary_item_type or item.is_legacy_scrap_item]
|
||||||
)
|
)
|
||||||
self.assertEqual(len(scr.items), 3) # 1 FG Item + 2 Scrap Items
|
self.assertEqual(len(scr.items), 3) # 1 FG Item + 2 Scrap Items
|
||||||
self.assertEqual(scr_secondary_items, set(secondary_items))
|
self.assertEqual(scr_secondary_items, set(secondary_items))
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"item_code",
|
"item_code",
|
||||||
"is_legacy_scrap_item",
|
"is_legacy_scrap_item",
|
||||||
"type",
|
"secondary_item_type",
|
||||||
"column_break_2",
|
"column_break_2",
|
||||||
"item_name",
|
"item_name",
|
||||||
"section_break_4",
|
"section_break_4",
|
||||||
@@ -162,12 +162,12 @@
|
|||||||
"label": "Accepted Qty",
|
"label": "Accepted Qty",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only_depends_on": "eval:doc.type || doc.is_legacy_scrap_item",
|
"read_only_depends_on": "eval:doc.secondary_item_type || doc.is_legacy_scrap_item",
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 1,
|
"columns": 1,
|
||||||
"depends_on": "eval:!parent.is_return && !doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:!parent.is_return && !doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "rejected_qty",
|
"fieldname": "rejected_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only_depends_on": "eval:doc.type || doc.is_legacy_scrap_item",
|
"read_only_depends_on": "eval:doc.secondary_item_type || doc.is_legacy_scrap_item",
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -234,7 +234,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:!doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "rm_cost_per_qty",
|
"fieldname": "rm_cost_per_qty",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Raw Material Cost Per Qty",
|
"label": "Raw Material Cost Per Qty",
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:!doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "service_cost_per_qty",
|
"fieldname": "service_cost_per_qty",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Service Cost Per Qty",
|
"label": "Service Cost Per Qty",
|
||||||
@@ -254,7 +254,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:!doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "additional_cost_per_qty",
|
"fieldname": "additional_cost_per_qty",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Additional Cost Per Qty",
|
"label": "Additional Cost Per Qty",
|
||||||
@@ -278,7 +278,7 @@
|
|||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: !parent.is_return && !doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval: !parent.is_return && !doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "rejected_warehouse",
|
"fieldname": "rejected_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
@@ -290,7 +290,7 @@
|
|||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!doc.__islocal && !doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:!doc.__islocal && !doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "quality_inspection",
|
"fieldname": "quality_inspection",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Quality Inspection",
|
"label": "Quality Inspection",
|
||||||
@@ -372,7 +372,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "BOM",
|
"options": "BOM",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only_depends_on": "eval:doc.type || doc.is_legacy_scrap_item"
|
"read_only_depends_on": "eval:doc.secondary_item_type || doc.is_legacy_scrap_item"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "item_code.brand",
|
"fetch_from": "item_code.brand",
|
||||||
@@ -499,7 +499,7 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:(doc.use_serial_batch_fields === 0 || doc.docstatus === 1) && !doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:(doc.use_serial_batch_fields === 0 || doc.docstatus === 1) && !doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "rejected_serial_and_batch_bundle",
|
"fieldname": "rejected_serial_and_batch_bundle",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Rejected Serial and Batch Bundle",
|
"label": "Rejected Serial and Batch Bundle",
|
||||||
@@ -564,7 +564,7 @@
|
|||||||
"label": "Add Serial / Batch Bundle"
|
"label": "Add Serial / Batch Bundle"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.use_serial_batch_fields === 0 && !doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:doc.use_serial_batch_fields === 0 && !doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "add_serial_batch_for_rejected_qty",
|
"fieldname": "add_serial_batch_for_rejected_qty",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Add Serial / Batch No (Rejected Qty)"
|
"label": "Add Serial / Batch No (Rejected Qty)"
|
||||||
@@ -578,7 +578,7 @@
|
|||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:!doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "landed_cost_voucher_amount",
|
"fieldname": "landed_cost_voucher_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Landed Cost Voucher Amount",
|
"label": "Landed Cost Voucher Amount",
|
||||||
@@ -596,7 +596,7 @@
|
|||||||
"options": "Account"
|
"options": "Account"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "type",
|
"fieldname": "secondary_item_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Type",
|
"label": "Type",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
@@ -606,7 +606,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.type && !doc.is_legacy_scrap_item",
|
"depends_on": "eval:!doc.secondary_item_type && !doc.is_legacy_scrap_item",
|
||||||
"fieldname": "secondary_items_cost_per_qty",
|
"fieldname": "secondary_items_cost_per_qty",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Secondary Items Cost Per Qty",
|
"label": "Secondary Items Cost Per Qty",
|
||||||
@@ -635,7 +635,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-03-09 15:11:16.977539",
|
"modified": "2026-06-01 10:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Receipt Item",
|
"name": "Subcontracting Receipt Item",
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class SubcontractingReceiptItem(Document):
|
|||||||
subcontracting_order: DF.Link | None
|
subcontracting_order: DF.Link | None
|
||||||
subcontracting_order_item: DF.Data | None
|
subcontracting_order_item: DF.Data | None
|
||||||
subcontracting_receipt_item: DF.Data | None
|
subcontracting_receipt_item: DF.Data | None
|
||||||
type: DF.Literal["", "Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
secondary_item_type: DF.Literal["", "Co-Product", "By-Product", "Scrap", "Additional Finished Good"]
|
||||||
use_serial_batch_fields: DF.Check
|
use_serial_batch_fields: DF.Check
|
||||||
warehouse: DF.Link | None
|
warehouse: DF.Link | None
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|||||||
Reference in New Issue
Block a user