Merge pull request #49781 from rohitwaghchaure/fixed-extra-tramsfer-materials

fix: additional material transfer
This commit is contained in:
rohitwaghchaure
2025-09-29 17:27:42 +05:30
committed by GitHub
5 changed files with 86 additions and 14 deletions

View File

@@ -756,12 +756,26 @@ erpnext.work_order = {
}); });
start_btn.addClass("btn-primary"); start_btn.addClass("btn-primary");
} else if (transfer_extra_materials && allowed_qty) { } else if (transfer_extra_materials && allowed_qty) {
let qty = allowed_qty - flt(frm.doc.material_transferred_for_manufacturing); let qty =
allowed_qty -
flt(
flt(frm.doc.material_transferred_for_manufacturing) +
flt(frm.doc.additional_transferred_qty)
);
if (qty > 0) { if (qty > 0) {
frm.add_custom_button(__("Transfer Extra Material"), function () { frm.add_custom_button(
erpnext.work_order.make_se(frm, "Material Transfer for Manufacture", qty); __("Additional Transfer"),
}); function () {
erpnext.work_order.make_se(
frm,
"Material Transfer for Manufacture",
qty,
1
);
},
__("Make")
);
} }
} }
} }
@@ -988,13 +1002,14 @@ erpnext.work_order = {
}); });
}, },
make_se: function (frm, purpose, qty) { make_se: function (frm, purpose, qty, is_additional_transfer_entry) {
if (qty) { if (qty) {
frappe frappe
.xcall("erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", { .xcall("erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", {
work_order_id: frm.doc.name, work_order_id: frm.doc.name,
purpose: purpose, purpose: purpose,
qty: qty, qty: qty,
is_additional_transfer_entry: is_additional_transfer_entry || 0,
}) })
.then((stock_entry) => { .then((stock_entry) => {
frappe.model.sync(stock_entry); frappe.model.sync(stock_entry);

View File

@@ -19,13 +19,15 @@
"column_break1", "column_break1",
"company", "company",
"qty", "qty",
"material_transferred_for_manufacturing",
"produced_qty",
"disassembled_qty",
"process_loss_qty",
"project", "project",
"track_semi_finished_goods", "track_semi_finished_goods",
"reserve_stock", "reserve_stock",
"column_break_agjv",
"material_transferred_for_manufacturing",
"additional_transferred_qty",
"produced_qty",
"disassembled_qty",
"process_loss_qty",
"warehouses", "warehouses",
"source_warehouse", "source_warehouse",
"wip_warehouse", "wip_warehouse",
@@ -609,6 +611,17 @@
"label": "MPS", "label": "MPS",
"options": "Master Production Schedule", "options": "Master Production Schedule",
"read_only": 1 "read_only": 1
},
{
"fieldname": "column_break_agjv",
"fieldtype": "Column Break"
},
{
"fieldname": "additional_transferred_qty",
"fieldtype": "Float",
"label": "Additional Transferred Qty",
"no_copy": 1,
"read_only": 1
} }
], ],
"grid_page_length": 50, "grid_page_length": 50,
@@ -617,7 +630,7 @@
"image_field": "image", "image_field": "image",
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-08-28 11:01:48.719824", "modified": "2025-09-29 15:57:47.022616",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Work Order", "name": "Work Order",

View File

@@ -82,6 +82,7 @@ class WorkOrder(Document):
actual_operating_cost: DF.Currency actual_operating_cost: DF.Currency
actual_start_date: DF.Datetime | None actual_start_date: DF.Datetime | None
additional_operating_cost: DF.Currency additional_operating_cost: DF.Currency
additional_transferred_qty: DF.Float
allow_alternative_item: DF.Check allow_alternative_item: DF.Check
amended_from: DF.Link | None amended_from: DF.Link | None
batch_size: DF.Float batch_size: DF.Float
@@ -481,6 +482,7 @@ class WorkOrder(Document):
for purpose, fieldname in ( for purpose, fieldname in (
("Manufacture", "produced_qty"), ("Manufacture", "produced_qty"),
("Material Transfer for Manufacture", "material_transferred_for_manufacturing"), ("Material Transfer for Manufacture", "material_transferred_for_manufacturing"),
("Material Transfer for Manufacture", "additional_transferred_qty"),
): ):
if ( if (
purpose == "Material Transfer for Manufacture" purpose == "Material Transfer for Manufacture"
@@ -489,7 +491,7 @@ class WorkOrder(Document):
): ):
continue continue
qty = self.get_transferred_or_manufactured_qty(purpose) qty = self.get_transferred_or_manufactured_qty(purpose, fieldname)
if not allowance_percentage and purpose == "Material Transfer for Manufacture": if not allowance_percentage and purpose == "Material Transfer for Manufacture":
allowance_percentage = flt( allowance_percentage = flt(
@@ -519,6 +521,30 @@ class WorkOrder(Document):
self.set_produced_qty_for_sub_assembly_item() self.set_produced_qty_for_sub_assembly_item()
self.update_production_plan_status() self.update_production_plan_status()
if self.additional_transferred_qty:
self.validate_additional_transferred_qty()
def validate_additional_transferred_qty(self):
transfer_extra_materials_percentage = frappe.db.get_single_value(
"Manufacturing Settings", "transfer_extra_materials_percentage"
)
allowed_qty = flt(self.qty) + flt(flt(self.qty) * flt(transfer_extra_materials_percentage) / 100)
actual_qty = flt(self.material_transferred_for_manufacturing) + flt(self.additional_transferred_qty)
precision = frappe.get_precision("Work Order", "qty")
if flt(allowed_qty - actual_qty, precision) < 0:
frappe.throw(
_(
"""Additional Transferred Qty {0}
cannot be greater than {1}.
To fix this, increase the percentage value
of the field 'Transfer Extra Raw Materials to WIP'
in Manufacturing Settings."""
).format(actual_qty, allowed_qty),
)
def update_disassembled_qty(self, qty, is_cancel=False): def update_disassembled_qty(self, qty, is_cancel=False):
if is_cancel: if is_cancel:
self.disassembled_qty = max(0, self.disassembled_qty - qty) self.disassembled_qty = max(0, self.disassembled_qty - qty)
@@ -531,7 +557,7 @@ class WorkOrder(Document):
self.db_set("disassembled_qty", self.disassembled_qty) self.db_set("disassembled_qty", self.disassembled_qty)
def get_transferred_or_manufactured_qty(self, purpose): def get_transferred_or_manufactured_qty(self, purpose, fieldname):
table = frappe.qb.DocType("Stock Entry") table = frappe.qb.DocType("Stock Entry")
query = frappe.qb.from_(table).where( query = frappe.qb.from_(table).where(
(table.work_order == self.name) & (table.docstatus == 1) & (table.purpose == purpose) (table.work_order == self.name) & (table.docstatus == 1) & (table.purpose == purpose)
@@ -542,6 +568,10 @@ class WorkOrder(Document):
else: else:
query = query.select(Sum(table.fg_completed_qty)) query = query.select(Sum(table.fg_completed_qty))
query = query.where(
table.is_additional_transfer_entry == cint(fieldname == "additional_transferred_qty")
)
return flt(query.run()[0][0]) return flt(query.run()[0][0])
def set_process_loss_qty(self): def set_process_loss_qty(self):
@@ -1991,7 +2021,9 @@ def set_work_order_ops(name):
@frappe.whitelist() @frappe.whitelist()
def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None): def make_stock_entry(
work_order_id, purpose, qty=None, target_warehouse=None, is_additional_transfer_entry=False
):
work_order = frappe.get_doc("Work Order", work_order_id) work_order = frappe.get_doc("Work Order", work_order_id)
if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"): if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"):
wip_warehouse = work_order.wip_warehouse wip_warehouse = work_order.wip_warehouse
@@ -2030,6 +2062,7 @@ def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None):
stock_entry.to_warehouse = target_warehouse or work_order.source_warehouse stock_entry.to_warehouse = target_warehouse or work_order.source_warehouse
stock_entry.set_stock_entry_type() stock_entry.set_stock_entry_type()
stock_entry.is_additional_transfer_entry = is_additional_transfer_entry
stock_entry.get_items(qty, work_order.production_item) stock_entry.get_items(qty, work_order.production_item)
if purpose != "Disassemble": if purpose != "Disassemble":

View File

@@ -30,6 +30,7 @@
"set_posting_time", "set_posting_time",
"inspection_required", "inspection_required",
"apply_putaway_rule", "apply_putaway_rule",
"is_additional_transfer_entry",
"bom_info_section", "bom_info_section",
"from_bom", "from_bom",
"use_multi_level_bom", "use_multi_level_bom",
@@ -699,6 +700,15 @@
"fieldtype": "Data", "fieldtype": "Data",
"is_virtual": 1, "is_virtual": 1,
"label": "Last Scanned Warehouse" "label": "Last Scanned Warehouse"
},
{
"default": "0",
"depends_on": "eval:doc.purpose == \"Material Transfer for Manufacture\"",
"fieldname": "is_additional_transfer_entry",
"fieldtype": "Check",
"hidden": 1,
"label": "Is Additional Transfer Entry",
"read_only": 1
} }
], ],
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
@@ -706,7 +716,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-08-04 19:21:03.338958", "modified": "2025-09-29 15:56:21.344296",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry", "name": "Stock Entry",

View File

@@ -110,6 +110,7 @@ class StockEntry(StockController):
from_bom: DF.Check from_bom: DF.Check
from_warehouse: DF.Link | None from_warehouse: DF.Link | None
inspection_required: DF.Check inspection_required: DF.Check
is_additional_transfer_entry: DF.Check
is_opening: DF.Literal["No", "Yes"] is_opening: DF.Literal["No", "Yes"]
is_return: DF.Check is_return: DF.Check
items: DF.Table[StockEntryDetail] items: DF.Table[StockEntryDetail]