mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-02 08:03:21 +00:00
* feat: Disassembly Order (#42655)
(cherry picked from commit 663a08e4cd)
# Conflicts:
# erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
* chore: fix conflicts
---------
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
This commit is contained in:
@@ -2053,6 +2053,55 @@ class TestWorkOrder(FrappeTestCase):
|
||||
"BOM",
|
||||
)
|
||||
|
||||
def test_disassemby_order(self):
|
||||
fg_item = "Test Disassembly Item"
|
||||
source_warehouse = "Stores - _TC"
|
||||
raw_materials = ["Test Disassembly RM Item 1", "Test Disassembly RM Item 2"]
|
||||
|
||||
make_item(fg_item, {"is_stock_item": 1})
|
||||
for item in raw_materials:
|
||||
make_item(item, {"is_stock_item": 1})
|
||||
test_stock_entry.make_stock_entry(
|
||||
item_code=item,
|
||||
target=source_warehouse,
|
||||
qty=1,
|
||||
basic_rate=100,
|
||||
)
|
||||
|
||||
make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials)
|
||||
|
||||
wo = make_wo_order_test_record(
|
||||
item=fg_item,
|
||||
qty=1,
|
||||
source_warehouse=source_warehouse,
|
||||
skip_transfer=1,
|
||||
)
|
||||
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1))
|
||||
for row in stock_entry.items:
|
||||
if row.item_code in raw_materials:
|
||||
row.s_warehouse = source_warehouse
|
||||
|
||||
stock_entry.submit()
|
||||
|
||||
wo.reload()
|
||||
self.assertEqual(wo.status, "Completed")
|
||||
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Disassemble", 1))
|
||||
stock_entry.save()
|
||||
|
||||
self.assertEqual(stock_entry.purpose, "Disassemble")
|
||||
|
||||
for row in stock_entry.items:
|
||||
if row.item_code == fg_item:
|
||||
self.assertTrue(row.s_warehouse)
|
||||
self.assertFalse(row.t_warehouse)
|
||||
else:
|
||||
self.assertFalse(row.s_warehouse)
|
||||
self.assertTrue(row.t_warehouse)
|
||||
|
||||
stock_entry.submit()
|
||||
|
||||
|
||||
def make_operation(**kwargs):
|
||||
kwargs = frappe._dict(kwargs)
|
||||
@@ -2370,6 +2419,7 @@ def make_wo_order_test_record(**args):
|
||||
wo_order.batch_size = args.batch_size or 0
|
||||
|
||||
if args.source_warehouse:
|
||||
wo_order.source_warehouse = args.source_warehouse
|
||||
for item in wo_order.get("required_items"):
|
||||
item.source_warehouse = args.source_warehouse
|
||||
|
||||
|
||||
@@ -183,13 +183,30 @@ frappe.ui.form.on("Work Order", {
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.doc.status == "Completed") {
|
||||
if (frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") {
|
||||
frm.add_custom_button(
|
||||
__("BOM"),
|
||||
() => {
|
||||
frm.trigger("make_bom");
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
frm.doc.status == "Completed" &&
|
||||
frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture"
|
||||
frm.doc.docstatus === 1 &&
|
||||
["Closed", "Completed"].includes(frm.doc.status) &&
|
||||
frm.doc.produced_qty > 0
|
||||
) {
|
||||
frm.add_custom_button(__("Create BOM"), () => {
|
||||
frm.trigger("make_bom");
|
||||
});
|
||||
frm.add_custom_button(
|
||||
__("Disassembly Order"),
|
||||
() => {
|
||||
frm.trigger("make_disassembly_order");
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
}
|
||||
|
||||
frm.trigger("add_custom_button_to_return_components");
|
||||
@@ -337,6 +354,23 @@ frappe.ui.form.on("Work Order", {
|
||||
});
|
||||
},
|
||||
|
||||
make_disassembly_order(frm) {
|
||||
erpnext.work_order
|
||||
.show_prompt_for_qty_input(frm, "Disassemble")
|
||||
.then((data) => {
|
||||
return frappe.xcall("erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", {
|
||||
work_order_id: frm.doc.name,
|
||||
purpose: "Disassemble",
|
||||
qty: data.qty,
|
||||
target_warehouse: data.target_warehouse,
|
||||
});
|
||||
})
|
||||
.then((stock_entry) => {
|
||||
frappe.model.sync(stock_entry);
|
||||
frappe.set_route("Form", stock_entry.doctype, stock_entry.name);
|
||||
});
|
||||
},
|
||||
|
||||
show_progress_for_items: function (frm) {
|
||||
var bars = [];
|
||||
var message = "";
|
||||
@@ -745,6 +779,10 @@ erpnext.work_order = {
|
||||
|
||||
get_max_transferable_qty: (frm, purpose) => {
|
||||
let max = 0;
|
||||
if (purpose === "Disassemble") {
|
||||
return flt(frm.doc.produced_qty);
|
||||
}
|
||||
|
||||
if (frm.doc.skip_transfer) {
|
||||
max = flt(frm.doc.qty) - flt(frm.doc.produced_qty);
|
||||
} else {
|
||||
@@ -759,15 +797,38 @@ erpnext.work_order = {
|
||||
|
||||
show_prompt_for_qty_input: function (frm, purpose) {
|
||||
let max = this.get_max_transferable_qty(frm, purpose);
|
||||
|
||||
let fields = [
|
||||
{
|
||||
fieldtype: "Float",
|
||||
label: __("Qty for {0}", [__(purpose)]),
|
||||
fieldname: "qty",
|
||||
description: __("Max: {0}", [max]),
|
||||
default: max,
|
||||
},
|
||||
];
|
||||
|
||||
if (purpose === "Disassemble") {
|
||||
fields.push({
|
||||
fieldtype: "Link",
|
||||
options: "Warehouse",
|
||||
fieldname: "target_warehouse",
|
||||
label: __("Target Warehouse"),
|
||||
default: frm.doc.source_warehouse || frm.doc.wip_warehouse,
|
||||
get_query() {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
is_group: 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
frappe.prompt(
|
||||
{
|
||||
fieldtype: "Float",
|
||||
label: __("Qty for {0}", [__(purpose)]),
|
||||
fieldname: "qty",
|
||||
description: __("Max: {0}", [max]),
|
||||
default: max,
|
||||
},
|
||||
fields,
|
||||
(data) => {
|
||||
max += (frm.doc.qty * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100;
|
||||
|
||||
|
||||
@@ -1359,7 +1359,7 @@ def set_work_order_ops(name):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_stock_entry(work_order_id, purpose, qty=None):
|
||||
def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None):
|
||||
work_order = frappe.get_doc("Work Order", work_order_id)
|
||||
if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"):
|
||||
wip_warehouse = work_order.wip_warehouse
|
||||
@@ -1389,9 +1389,16 @@ def make_stock_entry(work_order_id, purpose, qty=None):
|
||||
stock_entry.to_warehouse = work_order.fg_warehouse
|
||||
stock_entry.project = work_order.project
|
||||
|
||||
if purpose == "Disassemble":
|
||||
stock_entry.from_warehouse = work_order.fg_warehouse
|
||||
stock_entry.to_warehouse = target_warehouse or work_order.source_warehouse
|
||||
|
||||
stock_entry.set_stock_entry_type()
|
||||
stock_entry.get_items()
|
||||
stock_entry.set_serial_no_batch_for_finished_good()
|
||||
|
||||
if purpose != "Disassemble":
|
||||
stock_entry.set_serial_no_batch_for_finished_good()
|
||||
|
||||
return stock_entry.as_dict()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user