feat: get RM costs from consumption entry in manufacture SE (backport #39822) (backport #39847) (#39849)
feat: get RM costs from consumption entry in manufacture SE (backport #39822) (#39847) feat: get RM costs from consumption entry in manufacture SE (#39822) (cherry picked from commit39067c7614
) Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com> (cherry picked from commit2d5f186812
) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
db4efd3332
commit
beb4137dac
@ -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",
|
||||
@ -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",
|
||||
|
@ -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
|
||||
|
@ -1775,6 +1775,52 @@ class TestWorkOrder(FrappeTestCase):
|
||||
"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"}):
|
||||
|
@ -899,14 +899,62 @@ class StockEntry(StockController):
|
||||
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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user