feat: provision to return non consumed components against the work order
This commit is contained in:
parent
f95de1fd4d
commit
d59ed24e6c
@ -17,6 +17,7 @@ from erpnext.manufacturing.doctype.work_order.work_order import (
|
|||||||
close_work_order,
|
close_work_order,
|
||||||
make_job_card,
|
make_job_card,
|
||||||
make_stock_entry,
|
make_stock_entry,
|
||||||
|
make_stock_return_entry,
|
||||||
stop_unstop,
|
stop_unstop,
|
||||||
)
|
)
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
@ -1408,6 +1409,77 @@ class TestWorkOrder(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(manufacture_ste_doc2.items[1].qty, 1)
|
self.assertEqual(manufacture_ste_doc2.items[1].qty, 1)
|
||||||
|
|
||||||
|
def test_non_consumed_material_return_against_work_order(self):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Manufacturing Settings",
|
||||||
|
None,
|
||||||
|
"backflush_raw_materials_based_on",
|
||||||
|
"Material Transferred for Manufacture",
|
||||||
|
)
|
||||||
|
|
||||||
|
item = make_item(
|
||||||
|
"Test FG Item To Test Return Case",
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
item_code = item.name
|
||||||
|
bom_doc = make_bom(
|
||||||
|
item=item_code,
|
||||||
|
source_warehouse="Stores - _TC",
|
||||||
|
raw_materials=["Test Batch MCC Keyboard", "Test Serial No BTT Headphone"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a work order
|
||||||
|
wo_doc = make_wo_order_test_record(production_item=item_code, qty=5)
|
||||||
|
wo_doc.save()
|
||||||
|
|
||||||
|
self.assertEqual(wo_doc.bom_no, bom_doc.name)
|
||||||
|
|
||||||
|
# Transfer material for manufacture
|
||||||
|
ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 5))
|
||||||
|
for row in ste_doc.items:
|
||||||
|
row.qty += 2
|
||||||
|
row.transfer_qty += 2
|
||||||
|
nste_doc = test_stock_entry.make_stock_entry(
|
||||||
|
item_code=row.item_code, target="Stores - _TC", qty=row.qty, basic_rate=100
|
||||||
|
)
|
||||||
|
|
||||||
|
row.batch_no = nste_doc.items[0].batch_no
|
||||||
|
row.serial_no = nste_doc.items[0].serial_no
|
||||||
|
|
||||||
|
ste_doc.save()
|
||||||
|
ste_doc.submit()
|
||||||
|
ste_doc.load_from_db()
|
||||||
|
|
||||||
|
# Create a stock entry to manufacture the item
|
||||||
|
ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 5))
|
||||||
|
for row in ste_doc.items:
|
||||||
|
if row.s_warehouse and not row.t_warehouse:
|
||||||
|
row.qty -= 2
|
||||||
|
row.transfer_qty -= 2
|
||||||
|
|
||||||
|
if row.serial_no:
|
||||||
|
serial_nos = get_serial_nos(row.serial_no)
|
||||||
|
row.serial_no = "\n".join(serial_nos[0:5])
|
||||||
|
|
||||||
|
ste_doc.save()
|
||||||
|
ste_doc.submit()
|
||||||
|
|
||||||
|
wo_doc.load_from_db()
|
||||||
|
for row in wo_doc.required_items:
|
||||||
|
self.assertEqual(row.transferred_qty, 7)
|
||||||
|
self.assertEqual(row.consumed_qty, 5)
|
||||||
|
|
||||||
|
self.assertEqual(wo_doc.status, "Completed")
|
||||||
|
return_ste_doc = make_stock_return_entry(wo_doc.name)
|
||||||
|
return_ste_doc.save()
|
||||||
|
|
||||||
|
self.assertTrue(return_ste_doc.is_return)
|
||||||
|
for row in return_ste_doc.items:
|
||||||
|
self.assertEqual(row.qty, 2)
|
||||||
|
|
||||||
|
|
||||||
def prepare_data_for_backflush_based_on_materials_transferred():
|
def prepare_data_for_backflush_based_on_materials_transferred():
|
||||||
batch_item_doc = make_item(
|
batch_item_doc = make_item(
|
||||||
|
@ -180,6 +180,37 @@ frappe.ui.form.on("Work Order", {
|
|||||||
frm.trigger("make_bom");
|
frm.trigger("make_bom");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.trigger("add_custom_button_to_return_components");
|
||||||
|
},
|
||||||
|
|
||||||
|
add_custom_button_to_return_components: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 1 && in_list(["Closed", "Completed"], frm.doc.status)) {
|
||||||
|
let non_consumed_items = frm.doc.required_items.filter(d =>{
|
||||||
|
return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (non_consumed_items && non_consumed_items.length) {
|
||||||
|
frm.add_custom_button(__("Return Components"), function() {
|
||||||
|
frm.trigger("create_stock_return_entry");
|
||||||
|
}).addClass("btn-primary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
create_stock_return_entry: function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.manufacturing.doctype.work_order.work_order.make_stock_return_entry",
|
||||||
|
args: {
|
||||||
|
"work_order": frm.doc.name,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if(!r.exc) {
|
||||||
|
let doc = frappe.model.sync(r.message);
|
||||||
|
frappe.set_route("Form", doc[0].doctype, doc[0].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
make_job_card: function(frm) {
|
make_job_card: function(frm) {
|
||||||
@ -517,7 +548,8 @@ frappe.ui.form.on("Work Order Operation", {
|
|||||||
erpnext.work_order = {
|
erpnext.work_order = {
|
||||||
set_custom_buttons: function(frm) {
|
set_custom_buttons: function(frm) {
|
||||||
var doc = frm.doc;
|
var doc = frm.doc;
|
||||||
if (doc.docstatus === 1 && doc.status != "Closed") {
|
|
||||||
|
if (doc.status !== "Closed") {
|
||||||
frm.add_custom_button(__('Close'), function() {
|
frm.add_custom_button(__('Close'), function() {
|
||||||
frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."),
|
frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."),
|
||||||
() => {
|
() => {
|
||||||
@ -525,7 +557,9 @@ erpnext.work_order = {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, __("Status"));
|
}, __("Status"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.docstatus === 1 && !in_list(["Closed", "Completed"], doc.status)) {
|
||||||
if (doc.status != 'Stopped' && doc.status != 'Completed') {
|
if (doc.status != 'Stopped' && doc.status != 'Completed') {
|
||||||
frm.add_custom_button(__('Stop'), function() {
|
frm.add_custom_button(__('Stop'), function() {
|
||||||
erpnext.work_order.change_work_order_status(frm, "Stopped");
|
erpnext.work_order.change_work_order_status(frm, "Stopped");
|
||||||
|
@ -20,6 +20,7 @@ from frappe.utils import (
|
|||||||
nowdate,
|
nowdate,
|
||||||
time_diff_in_hours,
|
time_diff_in_hours,
|
||||||
)
|
)
|
||||||
|
from pypika import functions as fn
|
||||||
|
|
||||||
from erpnext.manufacturing.doctype.bom.bom import (
|
from erpnext.manufacturing.doctype.bom.bom import (
|
||||||
get_bom_item_rate,
|
get_bom_item_rate,
|
||||||
@ -859,6 +860,7 @@ class WorkOrder(Document):
|
|||||||
if self.docstatus == 1:
|
if self.docstatus == 1:
|
||||||
# calculate transferred qty based on submitted stock entries
|
# calculate transferred qty based on submitted stock entries
|
||||||
self.update_transferred_qty_for_required_items()
|
self.update_transferred_qty_for_required_items()
|
||||||
|
self.update_returned_qty()
|
||||||
|
|
||||||
# update in bin
|
# update in bin
|
||||||
self.update_reserved_qty_for_production()
|
self.update_reserved_qty_for_production()
|
||||||
@ -930,23 +932,62 @@ class WorkOrder(Document):
|
|||||||
self.set_available_qty()
|
self.set_available_qty()
|
||||||
|
|
||||||
def update_transferred_qty_for_required_items(self):
|
def update_transferred_qty_for_required_items(self):
|
||||||
"""update transferred qty from submitted stock entries for that item against
|
ste = frappe.qb.DocType("Stock Entry")
|
||||||
the work order"""
|
ste_child = frappe.qb.DocType("Stock Entry Detail")
|
||||||
|
|
||||||
for d in self.required_items:
|
query = (
|
||||||
transferred_qty = frappe.db.sql(
|
frappe.qb.from_(ste)
|
||||||
"""select sum(qty)
|
.inner_join(ste_child)
|
||||||
from `tabStock Entry` entry, `tabStock Entry Detail` detail
|
.on((ste_child.parent == ste.name))
|
||||||
where
|
.select(
|
||||||
entry.work_order = %(name)s
|
ste_child.item_code,
|
||||||
and entry.purpose = 'Material Transfer for Manufacture'
|
ste_child.original_item,
|
||||||
and entry.docstatus = 1
|
fn.Sum(ste_child.qty).as_("qty"),
|
||||||
and detail.parent = entry.name
|
)
|
||||||
and (detail.item_code = %(item)s or detail.original_item = %(item)s)""",
|
.where(
|
||||||
{"name": self.name, "item": d.item_code},
|
(ste.docstatus == 1)
|
||||||
)[0][0]
|
& (ste.work_order == self.name)
|
||||||
|
& (ste.purpose == "Material Transfer for Manufacture")
|
||||||
|
& (ste.is_return == 0)
|
||||||
|
)
|
||||||
|
.groupby(ste_child.item_code)
|
||||||
|
)
|
||||||
|
|
||||||
d.db_set("transferred_qty", flt(transferred_qty), update_modified=False)
|
data = query.run(as_dict=1) or []
|
||||||
|
transferred_items = frappe._dict({d.original_item or d.item_code: d.qty for d in data})
|
||||||
|
|
||||||
|
for row in self.required_items:
|
||||||
|
row.db_set(
|
||||||
|
"transferred_qty", (transferred_items.get(row.item_code) or 0.0), update_modified=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_returned_qty(self):
|
||||||
|
ste = frappe.qb.DocType("Stock Entry")
|
||||||
|
ste_child = frappe.qb.DocType("Stock Entry Detail")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(ste)
|
||||||
|
.inner_join(ste_child)
|
||||||
|
.on((ste_child.parent == ste.name))
|
||||||
|
.select(
|
||||||
|
ste_child.item_code,
|
||||||
|
ste_child.original_item,
|
||||||
|
fn.Sum(ste_child.qty).as_("qty"),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(ste.docstatus == 1)
|
||||||
|
& (ste.work_order == self.name)
|
||||||
|
& (ste.purpose == "Material Transfer for Manufacture")
|
||||||
|
& (ste.is_return == 1)
|
||||||
|
)
|
||||||
|
.groupby(ste_child.item_code)
|
||||||
|
)
|
||||||
|
|
||||||
|
data = query.run(as_dict=1) or []
|
||||||
|
returned_dict = frappe._dict({d.original_item or d.item_code: d.qty for d in data})
|
||||||
|
|
||||||
|
for row in self.required_items:
|
||||||
|
row.db_set("returned_qty", (returned_dict.get(row.item_code) or 0.0), update_modified=False)
|
||||||
|
|
||||||
def update_consumed_qty_for_required_items(self):
|
def update_consumed_qty_for_required_items(self):
|
||||||
"""
|
"""
|
||||||
@ -1470,3 +1511,25 @@ def get_reserved_qty_for_production(item_code: str, warehouse: str) -> float:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
).run()[0][0] or 0.0
|
).run()[0][0] or 0.0
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_stock_return_entry(work_order):
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry import get_available_materials
|
||||||
|
|
||||||
|
non_consumed_items = get_available_materials(work_order)
|
||||||
|
if not non_consumed_items:
|
||||||
|
return
|
||||||
|
|
||||||
|
wo_doc = frappe.get_cached_doc("Work Order", work_order)
|
||||||
|
|
||||||
|
stock_entry = frappe.new_doc("Stock Entry")
|
||||||
|
stock_entry.from_bom = 1
|
||||||
|
stock_entry.is_return = 1
|
||||||
|
stock_entry.work_order = work_order
|
||||||
|
stock_entry.purpose = "Material Transfer for Manufacture"
|
||||||
|
stock_entry.bom_no = wo_doc.bom_no
|
||||||
|
stock_entry.add_transfered_raw_materials_in_items()
|
||||||
|
stock_entry.set_stock_entry_type()
|
||||||
|
|
||||||
|
return stock_entry
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"column_break_11",
|
"column_break_11",
|
||||||
"transferred_qty",
|
"transferred_qty",
|
||||||
"consumed_qty",
|
"consumed_qty",
|
||||||
|
"returned_qty",
|
||||||
"available_qty_at_source_warehouse",
|
"available_qty_at_source_warehouse",
|
||||||
"available_qty_at_wip_warehouse"
|
"available_qty_at_wip_warehouse"
|
||||||
],
|
],
|
||||||
@ -97,6 +98,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 1,
|
||||||
"depends_on": "eval:!parent.skip_transfer",
|
"depends_on": "eval:!parent.skip_transfer",
|
||||||
"fieldname": "consumed_qty",
|
"fieldname": "consumed_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
@ -127,11 +129,19 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 1,
|
||||||
|
"fieldname": "returned_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Returned Qty ",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-13 18:46:32.966416",
|
"modified": "2022-09-28 10:50:43.512562",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Work Order Item",
|
"name": "Work Order Item",
|
||||||
@ -140,5 +150,6 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
@ -50,7 +50,7 @@ frappe.query_reports["Work Order Consumed Materials"] = {
|
|||||||
label: __("Status"),
|
label: __("Status"),
|
||||||
fieldname: "status",
|
fieldname: "status",
|
||||||
fieldtype: "Select",
|
fieldtype: "Select",
|
||||||
options: ["In Process", "Completed", "Stopped"]
|
options: ["", "In Process", "Completed", "Stopped"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: __("Excess Materials Consumed"),
|
label: __("Excess Materials Consumed"),
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
@ -18,7 +20,11 @@ def get_data(report_filters):
|
|||||||
filters = get_filter_condition(report_filters)
|
filters = get_filter_condition(report_filters)
|
||||||
|
|
||||||
wo_items = {}
|
wo_items = {}
|
||||||
for d in frappe.get_all("Work Order", filters=filters, fields=fields):
|
|
||||||
|
work_orders = frappe.get_all("Work Order", filters=filters, fields=fields)
|
||||||
|
returned_materials = get_returned_materials(work_orders)
|
||||||
|
|
||||||
|
for d in work_orders:
|
||||||
d.extra_consumed_qty = 0.0
|
d.extra_consumed_qty = 0.0
|
||||||
if d.consumed_qty and d.consumed_qty > d.required_qty:
|
if d.consumed_qty and d.consumed_qty > d.required_qty:
|
||||||
d.extra_consumed_qty = d.consumed_qty - d.required_qty
|
d.extra_consumed_qty = d.consumed_qty - d.required_qty
|
||||||
@ -39,6 +45,28 @@ def get_data(report_filters):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_returned_materials(work_orders):
|
||||||
|
raw_materials_qty = defaultdict(float)
|
||||||
|
|
||||||
|
raw_materials = frappe.get_all(
|
||||||
|
"Stock Entry",
|
||||||
|
fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"],
|
||||||
|
filters=[
|
||||||
|
["Stock Entry", "is_return", "=", 1],
|
||||||
|
["Stock Entry Detail", "docstatus", "=", 1],
|
||||||
|
["Stock Entry", "work_order", "in", [d.name for d in work_orders]],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
for d in raw_materials:
|
||||||
|
raw_materials_qty[d.item_code] += d.qty
|
||||||
|
|
||||||
|
for row in work_orders:
|
||||||
|
row.returned_qty = 0.0
|
||||||
|
if raw_materials_qty.get(row.raw_material_item_code):
|
||||||
|
row.returned_qty = raw_materials_qty.get(row.raw_material_item_code)
|
||||||
|
|
||||||
|
|
||||||
def get_fields():
|
def get_fields():
|
||||||
return [
|
return [
|
||||||
"`tabWork Order Item`.`parent`",
|
"`tabWork Order Item`.`parent`",
|
||||||
@ -65,7 +93,7 @@ def get_filter_condition(report_filters):
|
|||||||
for field in ["name", "production_item", "company", "status"]:
|
for field in ["name", "production_item", "company", "status"]:
|
||||||
value = report_filters.get(field)
|
value = report_filters.get(field)
|
||||||
if value:
|
if value:
|
||||||
key = f"`{field}`"
|
key = f"{field}"
|
||||||
filters.update({key: value})
|
filters.update({key: value})
|
||||||
|
|
||||||
return filters
|
return filters
|
||||||
@ -112,4 +140,10 @@ def get_columns():
|
|||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"width": 100,
|
"width": 100,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": _("Returned Qty"),
|
||||||
|
"fieldname": "returned_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 100,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
@ -148,19 +148,19 @@
|
|||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
|
"depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
|
||||||
"fieldname": "purchase_order",
|
"fieldname": "purchase_order",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Purchase Order",
|
"label": "Purchase Order",
|
||||||
"options": "Purchase Order"
|
"options": "Purchase Order"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
|
"depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
|
||||||
"fieldname": "subcontracting_order",
|
"fieldname": "subcontracting_order",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Subcontracting Order",
|
"label": "Subcontracting Order",
|
||||||
"options": "Subcontracting Order"
|
"options": "Subcontracting Order"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.purpose==\"Sales Return\"",
|
"depends_on": "eval:doc.purpose==\"Sales Return\"",
|
||||||
"fieldname": "delivery_note_no",
|
"fieldname": "delivery_note_no",
|
||||||
@ -616,6 +616,7 @@
|
|||||||
"fieldname": "is_return",
|
"fieldname": "is_return",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Is Return",
|
"label": "Is Return",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -627,7 +628,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-05-02 05:21:39.060501",
|
"modified": "2022-10-07 14:39:51.943770",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Entry",
|
"name": "Stock Entry",
|
||||||
|
@ -1212,13 +1212,19 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
def update_work_order(self):
|
def update_work_order(self):
|
||||||
def _validate_work_order(pro_doc):
|
def _validate_work_order(pro_doc):
|
||||||
|
msg, title = "", ""
|
||||||
if flt(pro_doc.docstatus) != 1:
|
if flt(pro_doc.docstatus) != 1:
|
||||||
frappe.throw(_("Work Order {0} must be submitted").format(self.work_order))
|
msg = f"Work Order {self.work_order} must be submitted"
|
||||||
|
|
||||||
if pro_doc.status == "Stopped":
|
if pro_doc.status == "Stopped":
|
||||||
frappe.throw(
|
msg = f"Transaction not allowed against stopped Work Order {self.work_order}"
|
||||||
_("Transaction not allowed against stopped Work Order {0}").format(self.work_order)
|
|
||||||
)
|
if self.is_return and pro_doc.status not in ["Completed", "Closed"]:
|
||||||
|
title = _("Stock Return")
|
||||||
|
msg = f"Work Order {self.work_order} must be completed or closed"
|
||||||
|
|
||||||
|
if msg:
|
||||||
|
frappe.throw(_(msg), title=title)
|
||||||
|
|
||||||
if self.job_card:
|
if self.job_card:
|
||||||
job_doc = frappe.get_doc("Job Card", self.job_card)
|
job_doc = frappe.get_doc("Job Card", self.job_card)
|
||||||
@ -1754,10 +1760,12 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
for key, row in available_materials.items():
|
for key, row in available_materials.items():
|
||||||
remaining_qty_to_produce = flt(wo_data.trans_qty) - flt(wo_data.produced_qty)
|
remaining_qty_to_produce = flt(wo_data.trans_qty) - flt(wo_data.produced_qty)
|
||||||
if remaining_qty_to_produce <= 0:
|
if remaining_qty_to_produce <= 0 and not self.is_return:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce
|
qty = flt(row.qty)
|
||||||
|
if not self.is_return:
|
||||||
|
qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce
|
||||||
|
|
||||||
item = row.item_details
|
item = row.item_details
|
||||||
if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
|
if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
|
||||||
@ -1781,6 +1789,9 @@ class StockEntry(StockController):
|
|||||||
self.update_item_in_stock_entry_detail(row, item, qty)
|
self.update_item_in_stock_entry_detail(row, item, qty)
|
||||||
|
|
||||||
def update_item_in_stock_entry_detail(self, row, item, qty) -> None:
|
def update_item_in_stock_entry_detail(self, row, item, qty) -> None:
|
||||||
|
if not qty:
|
||||||
|
return
|
||||||
|
|
||||||
ste_item_details = {
|
ste_item_details = {
|
||||||
"from_warehouse": item.warehouse,
|
"from_warehouse": item.warehouse,
|
||||||
"to_warehouse": "",
|
"to_warehouse": "",
|
||||||
@ -1794,6 +1805,9 @@ class StockEntry(StockController):
|
|||||||
"original_item": item.original_item,
|
"original_item": item.original_item,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.is_return:
|
||||||
|
ste_item_details["to_warehouse"] = item.s_warehouse
|
||||||
|
|
||||||
if row.serial_nos:
|
if row.serial_nos:
|
||||||
serial_nos = row.serial_nos
|
serial_nos = row.serial_nos
|
||||||
if item.batch_no:
|
if item.batch_no:
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
frappe.listview_settings['Stock Entry'] = {
|
frappe.listview_settings['Stock Entry'] = {
|
||||||
add_fields: ["`tabStock Entry`.`from_warehouse`", "`tabStock Entry`.`to_warehouse`",
|
add_fields: ["`tabStock Entry`.`from_warehouse`", "`tabStock Entry`.`to_warehouse`",
|
||||||
"`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`"],
|
"`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`",
|
||||||
|
"`tabStock Entry`.`is_return`"],
|
||||||
get_indicator: function (doc) {
|
get_indicator: function (doc) {
|
||||||
if (doc.docstatus === 0) {
|
debugger
|
||||||
|
if(doc.is_return===1 && doc.purpose === "Material Transfer for Manufacture") {
|
||||||
|
return [__("Material Returned from WIP"), "orange",
|
||||||
|
"is_return,=,1|purpose,=,Material Transfer for Manufacture|docstatus,<,2"];
|
||||||
|
} else if (doc.docstatus === 0) {
|
||||||
return [__("Draft"), "red", "docstatus,=,0"];
|
return [__("Draft"), "red", "docstatus,=,0"];
|
||||||
|
|
||||||
} else if (doc.purpose === 'Send to Warehouse' && doc.per_transferred < 100) {
|
} else if (doc.purpose === 'Send to Warehouse' && doc.per_transferred < 100) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user