Skip to content

Commit

Permalink
feat: get RM costs from consumption entry in manufacture SE (#39822)
Browse files Browse the repository at this point in the history
  • Loading branch information
s-aga-r authored Feb 11, 2024
1 parent e5824fc commit 39067c7
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"field_order": [
"raw_materials_consumption_section",
"material_consumption",
"get_rm_cost_from_consumption_entry",
"column_break_3",
"backflush_raw_materials_based_on",
"capacity_planning",
Expand Down Expand Up @@ -202,13 +203,20 @@
"fieldname": "set_op_cost_and_scrape_from_sub_assemblies",
"fieldtype": "Check",
"label": "Set Operating Cost / Scrape Items From Sub-assemblies"
},
{
"default": "0",
"depends_on": "eval: doc.material_consumption",
"fieldname": "get_rm_cost_from_consumption_entry",
"fieldtype": "Check",
"label": "Get Raw Materials Cost from Consumption Entry"
}
],
"icon": "icon-wrench",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-12-28 16:37:44.874096",
"modified": "2024-02-08 19:00:37.561244",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class ManufacturingSettings(Document):
default_scrap_warehouse: DF.Link | None
default_wip_warehouse: DF.Link | None
disable_capacity_planning: DF.Check
get_rm_cost_from_consumption_entry: DF.Check
job_card_excess_transfer: DF.Check
make_serial_no_batch_from_work_order: DF.Check
material_consumption: DF.Check
Expand Down
46 changes: 46 additions & 0 deletions erpnext/manufacturing/doctype/work_order/test_work_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -1776,6 +1776,52 @@ def test_op_cost_and_scrap_based_on_sub_assemblies(self):
"Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies", 0
)

@change_settings(
"Manufacturing Settings", {"material_consumption": 1, "get_rm_cost_from_consumption_entry": 1}
)
def test_get_rm_cost_from_consumption_entry(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import (
make_stock_entry as make_stock_entry_test_record,
)

rm = make_item(properties={"is_stock_item": 1}).name
fg = make_item(properties={"is_stock_item": 1}).name

make_stock_entry_test_record(
purpose="Material Receipt",
item_code=rm,
target="Stores - _TC",
qty=10,
basic_rate=100,
)
make_stock_entry_test_record(
purpose="Material Receipt",
item_code=rm,
target="Stores - _TC",
qty=10,
basic_rate=200,
)

bom = make_bom(item=fg, raw_materials=[rm], rate=150).name
wo = make_wo_order_test_record(
production_item=fg,
bom_no=bom,
qty=10,
)

mte = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10))
mte.items[0].s_warehouse = "Stores - _TC"
mte.insert().submit()

mce = frappe.get_doc(make_stock_entry(wo.name, "Material Consumption for Manufacture", 10))
mce.insert().submit()

me = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10))
me.insert().submit()

valuation_rate = sum([item.valuation_rate * item.transfer_qty for item in mce.items]) / 10
self.assertEqual(me.items[0].valuation_rate, valuation_rate)


def prepare_boms_for_sub_assembly_test():
if not frappe.db.exists("BOM", {"item": "Test Final SF Item 1"}):
Expand Down
60 changes: 54 additions & 6 deletions erpnext/stock/doctype/stock_entry/stock_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,14 +904,62 @@ def get_basic_rate_for_repacked_items(self, finished_item_qty, outgoing_items_co
return flt(outgoing_items_cost / total_fg_qty)

def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0) -> float:
settings = frappe.get_single("Manufacturing Settings")
scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])

# Get raw materials cost from BOM if multiple material consumption entries
if not outgoing_items_cost and frappe.db.get_single_value(
"Manufacturing Settings", "material_consumption", cache=True
):
bom_items = self.get_bom_raw_materials(finished_item_qty)
outgoing_items_cost = sum([flt(row.qty) * flt(row.rate) for row in bom_items.values()])
if settings.material_consumption:
if settings.get_rm_cost_from_consumption_entry and self.work_order:

# Validate only if Material Consumption Entry exists for the Work Order.
if frappe.db.exists(
"Stock Entry",
{
"docstatus": 1,
"work_order": self.work_order,
"purpose": "Material Consumption for Manufacture",
},
):
for item in self.items:
if not item.is_finished_item and not item.is_scrap_item:
label = frappe.get_meta(settings.doctype).get_label("get_rm_cost_from_consumption_entry")
frappe.throw(
_(
"Row {0}: As {1} is enabled, raw materials cannot be added to {2} entry. Use {3} entry to consume raw materials."
).format(
item.idx,
frappe.bold(label),
frappe.bold("Manufacture"),
frappe.bold("Material Consumption for Manufacture"),
)
)

if frappe.db.exists(
"Stock Entry", {"docstatus": 1, "work_order": self.work_order, "purpose": "Manufacture"}
):
frappe.throw(
_("Only one {0} entry can be created against the Work Order {1}").format(
frappe.bold("Manufacture"), frappe.bold(self.work_order)
)
)

SE = frappe.qb.DocType("Stock Entry")
SE_ITEM = frappe.qb.DocType("Stock Entry Detail")

outgoing_items_cost = (
frappe.qb.from_(SE)
.left_join(SE_ITEM)
.on(SE.name == SE_ITEM.parent)
.select(Sum(SE_ITEM.valuation_rate * SE_ITEM.transfer_qty))
.where(
(SE.docstatus == 1)
& (SE.work_order == self.work_order)
& (SE.purpose == "Material Consumption for Manufacture")
)
).run()[0][0] or 0

elif not outgoing_items_cost:
bom_items = self.get_bom_raw_materials(finished_item_qty)
outgoing_items_cost = sum([flt(row.qty) * flt(row.rate) for row in bom_items.values()])

return flt((outgoing_items_cost - scrap_items_cost) / finished_item_qty)

Expand Down

0 comments on commit 39067c7

Please sign in to comment.