mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-12 17:51:20 +00:00
Merge pull request #49781 from rohitwaghchaure/fixed-extra-tramsfer-materials
fix: additional material transfer
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user