Merge pull request #35629 from rohitwaghchaure/fixed-process-loss-in-job-card
fix: added process loss in job card
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index 5305db3..4a46d57 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -83,7 +83,7 @@
// and if stock mvt for WIP is required
if (frm.doc.work_order) {
frappe.db.get_value('Work Order', frm.doc.work_order, ['skip_transfer', 'status'], (result) => {
- if (result.skip_transfer === 1 || result.status == 'In Process' || frm.doc.transferred_qty > 0) {
+ if (result.skip_transfer === 1 || result.status == 'In Process' || frm.doc.transferred_qty > 0 || !frm.doc.items.length) {
frm.trigger("prepare_timer_buttons");
}
});
@@ -411,6 +411,16 @@
}
});
+ if (frm.doc.total_completed_qty && frm.doc.for_quantity > frm.doc.total_completed_qty) {
+ let flt_precision = precision('for_quantity', frm.doc);
+ let process_loss_qty = (
+ flt(frm.doc.for_quantity, flt_precision)
+ - flt(frm.doc.total_completed_qty, flt_precision)
+ );
+
+ frm.set_value('process_loss_qty', process_loss_qty);
+ }
+
refresh_field("total_completed_qty");
}
});
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index f49f018..5d912fa 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -39,6 +39,7 @@
"time_logs",
"section_break_13",
"total_completed_qty",
+ "process_loss_qty",
"column_break_15",
"total_time_in_mins",
"section_break_8",
@@ -448,11 +449,17 @@
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
+ },
+ {
+ "fieldname": "process_loss_qty",
+ "fieldtype": "Float",
+ "label": "Process Loss Qty",
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2023-05-23 09:56:43.826602",
+ "modified": "2023-06-09 12:04:55.534264",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index fcaa3fd..496cbfd 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -161,7 +161,7 @@
self.total_completed_qty = flt(self.total_completed_qty, self.precision("total_completed_qty"))
for row in self.sub_operations:
- self.total_completed_qty += row.completed_qty
+ self.c += row.completed_qty
def get_overlap_for(self, args, check_next_available_slot=False):
production_capacity = 1
@@ -451,6 +451,9 @@
},
)
+ def before_save(self):
+ self.set_process_loss()
+
def on_submit(self):
self.validate_transfer_qty()
self.validate_job_card()
@@ -487,19 +490,35 @@
)
)
- if self.for_quantity and self.total_completed_qty != self.for_quantity:
+ precision = self.precision("total_completed_qty")
+ total_completed_qty = flt(
+ flt(self.total_completed_qty, precision) + flt(self.process_loss_qty, precision)
+ )
+
+ if self.for_quantity and flt(total_completed_qty, precision) != flt(
+ self.for_quantity, precision
+ ):
total_completed_qty = bold(_("Total Completed Qty"))
qty_to_manufacture = bold(_("Qty to Manufacture"))
frappe.throw(
_("The {0} ({1}) must be equal to {2} ({3})").format(
total_completed_qty,
- bold(self.total_completed_qty),
+ bold(flt(total_completed_qty, precision)),
qty_to_manufacture,
bold(self.for_quantity),
)
)
+ def set_process_loss(self):
+ precision = self.precision("total_completed_qty")
+
+ self.process_loss_qty = 0.0
+ if self.total_completed_qty and self.for_quantity > self.total_completed_qty:
+ self.process_loss_qty = flt(self.for_quantity, precision) - flt(
+ self.total_completed_qty, precision
+ )
+
def update_work_order(self):
if not self.work_order:
return
@@ -511,7 +530,7 @@
):
return
- for_quantity, time_in_mins = 0, 0
+ for_quantity, time_in_mins, process_loss_qty = 0, 0, 0
from_time_list, to_time_list = [], []
field = "operation_id"
@@ -519,6 +538,7 @@
if data and len(data) > 0:
for_quantity = flt(data[0].completed_qty)
time_in_mins = flt(data[0].time_in_mins)
+ process_loss_qty = flt(data[0].process_loss_qty)
wo = frappe.get_doc("Work Order", self.work_order)
@@ -526,8 +546,8 @@
self.update_corrective_in_work_order(wo)
elif self.operation_id:
- self.validate_produced_quantity(for_quantity, wo)
- self.update_work_order_data(for_quantity, time_in_mins, wo)
+ self.validate_produced_quantity(for_quantity, process_loss_qty, wo)
+ self.update_work_order_data(for_quantity, process_loss_qty, time_in_mins, wo)
def update_corrective_in_work_order(self, wo):
wo.corrective_operation_cost = 0.0
@@ -542,11 +562,11 @@
wo.flags.ignore_validate_update_after_submit = True
wo.save()
- def validate_produced_quantity(self, for_quantity, wo):
+ def validate_produced_quantity(self, for_quantity, process_loss_qty, wo):
if self.docstatus < 2:
return
- if wo.produced_qty > for_quantity:
+ if wo.produced_qty > for_quantity + process_loss_qty:
first_part_msg = _(
"The {0} {1} is used to calculate the valuation cost for the finished good {2}."
).format(
@@ -561,7 +581,7 @@
_("{0} {1}").format(first_part_msg, second_part_msg), JobCardCancelError, title=_("Error")
)
- def update_work_order_data(self, for_quantity, time_in_mins, wo):
+ def update_work_order_data(self, for_quantity, process_loss_qty, time_in_mins, wo):
workstation_hour_rate = frappe.get_value("Workstation", self.workstation, "hour_rate")
jc = frappe.qb.DocType("Job Card")
jctl = frappe.qb.DocType("Job Card Time Log")
@@ -582,6 +602,7 @@
for data in wo.operations:
if data.get("name") == self.operation_id:
data.completed_qty = for_quantity
+ data.process_loss_qty = process_loss_qty
data.actual_operation_time = time_in_mins
data.actual_start_time = time_data[0].start_time if time_data else None
data.actual_end_time = time_data[0].end_time if time_data else None
@@ -599,7 +620,11 @@
def get_current_operation_data(self):
return frappe.get_all(
"Job Card",
- fields=["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
+ fields=[
+ "sum(total_time_in_mins) as time_in_mins",
+ "sum(total_completed_qty) as completed_qty",
+ "sum(process_loss_qty) as process_loss_qty",
+ ],
filters={
"docstatus": 1,
"work_order": self.work_order,
@@ -777,7 +802,7 @@
data = frappe.get_all(
"Work Order Operation",
- fields=["operation", "status", "completed_qty"],
+ fields=["operation", "status", "completed_qty", "sequence_id"],
filters={"docstatus": 1, "parent": self.work_order, "sequence_id": ("<", self.sequence_id)},
order_by="sequence_id, idx",
)
@@ -795,6 +820,16 @@
OperationSequenceError,
)
+ if row.completed_qty < current_operation_qty:
+ msg = f"""The completed quantity {bold(current_operation_qty)}
+ of an operation {bold(self.operation)} cannot be greater
+ than the completed quantity {bold(row.completed_qty)}
+ of a previous operation
+ {bold(row.operation)}.
+ """
+
+ frappe.throw(_(msg))
+
def validate_work_order(self):
if self.is_work_order_closed():
frappe.throw(_("You can't make any changes to Job Card since Work Order is closed."))
diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py
index a7f0648..e7fbcda 100644
--- a/erpnext/manufacturing/doctype/job_card/test_job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py
@@ -5,6 +5,7 @@
from typing import Literal
import frappe
+from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import random_string
from frappe.utils.data import add_to_date, now, today
@@ -469,6 +470,119 @@
self.assertEqual(ste.from_bom, 1.0)
self.assertEqual(ste.bom_no, work_order.bom_no)
+ def test_job_card_proccess_qty_and_completed_qty(self):
+ from erpnext.manufacturing.doctype.routing.test_routing import (
+ create_routing,
+ setup_bom,
+ setup_operations,
+ )
+ from erpnext.manufacturing.doctype.work_order.work_order import (
+ make_stock_entry as make_stock_entry_for_wo,
+ )
+ from erpnext.stock.doctype.item.test_item import make_item
+ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+ operations = [
+ {"operation": "Test Operation A1", "workstation": "Test Workstation A", "time_in_mins": 30},
+ {"operation": "Test Operation B1", "workstation": "Test Workstation A", "time_in_mins": 20},
+ ]
+
+ make_test_records("UOM")
+
+ warehouse = create_warehouse("Test Warehouse 123 for Job Card")
+
+ setup_operations(operations)
+
+ item_code = "Test Job Card Process Qty Item"
+ for item in [item_code, item_code + "RM 1", item_code + "RM 2"]:
+ if not frappe.db.exists("Item", item):
+ make_item(
+ item,
+ {
+ "item_name": item,
+ "stock_uom": "Nos",
+ "is_stock_item": 1,
+ },
+ )
+
+ routing_doc = create_routing(routing_name="Testing Route", operations=operations)
+ bom_doc = setup_bom(
+ item_code=item_code,
+ routing=routing_doc.name,
+ raw_materials=[item_code + "RM 1", item_code + "RM 2"],
+ source_warehouse=warehouse,
+ )
+
+ for row in bom_doc.items:
+ make_stock_entry(
+ item_code=row.item_code,
+ target=row.source_warehouse,
+ qty=10,
+ basic_rate=100,
+ )
+
+ wo_doc = make_wo_order_test_record(
+ production_item=item_code,
+ bom_no=bom_doc.name,
+ skip_transfer=1,
+ wip_warehouse=warehouse,
+ source_warehouse=warehouse,
+ )
+
+ for row in routing_doc.operations:
+ self.assertEqual(row.sequence_id, row.idx)
+
+ first_job_card = frappe.get_all(
+ "Job Card",
+ filters={"work_order": wo_doc.name, "sequence_id": 1},
+ fields=["name"],
+ order_by="sequence_id",
+ limit=1,
+ )[0].name
+
+ jc = frappe.get_doc("Job Card", first_job_card)
+ jc.time_logs[0].completed_qty = 8
+ jc.save()
+ jc.submit()
+
+ self.assertEqual(jc.process_loss_qty, 2)
+ self.assertEqual(jc.for_quantity, 10)
+
+ second_job_card = frappe.get_all(
+ "Job Card",
+ filters={"work_order": wo_doc.name, "sequence_id": 2},
+ fields=["name"],
+ order_by="sequence_id",
+ limit=1,
+ )[0].name
+
+ jc2 = frappe.get_doc("Job Card", second_job_card)
+ jc2.time_logs[0].completed_qty = 10
+
+ self.assertRaises(frappe.ValidationError, jc2.save)
+
+ jc2.load_from_db()
+ jc2.time_logs[0].completed_qty = 8
+ jc2.save()
+ jc2.submit()
+
+ self.assertEqual(jc2.for_quantity, 10)
+ self.assertEqual(jc2.process_loss_qty, 2)
+
+ s = frappe.get_doc(make_stock_entry_for_wo(wo_doc.name, "Manufacture", 10))
+ s.submit()
+
+ self.assertEqual(s.process_loss_qty, 2)
+
+ wo_doc.reload()
+ for row in wo_doc.operations:
+ self.assertEqual(row.completed_qty, 8)
+ self.assertEqual(row.process_loss_qty, 2)
+
+ self.assertEqual(wo_doc.produced_qty, 8)
+ self.assertEqual(wo_doc.process_loss_qty, 2)
+ self.assertEqual(wo_doc.status, "Completed")
+
def create_bom_with_multiple_operations():
"Create a BOM with multiple operations and Material Transfer against Job Card"
diff --git a/erpnext/manufacturing/doctype/routing/test_routing.py b/erpnext/manufacturing/doctype/routing/test_routing.py
index 48f1851..a37ff28 100644
--- a/erpnext/manufacturing/doctype/routing/test_routing.py
+++ b/erpnext/manufacturing/doctype/routing/test_routing.py
@@ -141,6 +141,7 @@
routing=args.routing,
with_operations=1,
currency=args.currency,
+ source_warehouse=args.source_warehouse,
)
else:
bom_doc = frappe.get_doc("BOM", name)
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 3c7c787..9f8390c 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -903,7 +903,7 @@
self.assertEqual(se.process_loss_qty, 1)
wo.load_from_db()
- self.assertEqual(wo.status, "In Process")
+ self.assertEqual(wo.status, "Completed")
@timeout(seconds=60)
def test_job_card_scrap_item(self):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index d0c9966..c1a078d 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -139,7 +139,7 @@
}
if (frm.doc.status != "Closed") {
- if (frm.doc.docstatus === 1
+ if (frm.doc.docstatus === 1 && frm.doc.status !== "Completed"
&& frm.doc.operations && frm.doc.operations.length) {
const not_completed = frm.doc.operations.filter(d => {
@@ -256,6 +256,12 @@
label: __('Batch Size'),
read_only: 1
},
+ {
+ fieldtype: 'Int',
+ fieldname: 'sequence_id',
+ label: __('Sequence Id'),
+ read_only: 1
+ },
],
data: operations_data,
in_place_edit: true,
@@ -280,8 +286,8 @@
var pending_qty = 0;
frm.doc.operations.forEach(data => {
- if(data.completed_qty != frm.doc.qty) {
- pending_qty = frm.doc.qty - flt(data.completed_qty);
+ if(data.completed_qty + data.process_loss_qty != frm.doc.qty) {
+ pending_qty = frm.doc.qty - flt(data.completed_qty) - flt(data.process_loss_qty);
if (pending_qty) {
dialog.fields_dict.operations.df.data.push({
@@ -290,7 +296,8 @@
'workstation': data.workstation,
'batch_size': data.batch_size,
'qty': pending_qty,
- 'pending_qty': pending_qty
+ 'pending_qty': pending_qty,
+ 'sequence_id': data.sequence_id
});
}
}
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index aecace6..a236f2a 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -46,8 +46,8 @@
"required_items_section",
"materials_and_operations_tab",
"operations_section",
- "operations",
"transfer_material_against",
+ "operations",
"time",
"planned_start_date",
"planned_end_date",
@@ -330,7 +330,6 @@
"label": "Expected Delivery Date"
},
{
- "collapsible": 1,
"fieldname": "operations_section",
"fieldtype": "Section Break",
"label": "Operations",
@@ -591,7 +590,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
- "modified": "2023-04-06 12:35:12.149827",
+ "modified": "2023-06-09 13:20:09.154362",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 3265b8f..bfdcf61 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -245,7 +245,9 @@
status = "Not Started"
if flt(self.material_transferred_for_manufacturing) > 0:
status = "In Process"
- if flt(self.produced_qty) >= flt(self.qty):
+
+ total_qty = flt(self.produced_qty) + flt(self.process_loss_qty)
+ if flt(total_qty) >= flt(self.qty):
status = "Completed"
else:
status = "Cancelled"
@@ -761,13 +763,15 @@
max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage / 100 * flt(self.qty))
for d in self.get("operations"):
- if not d.completed_qty:
+ precision = d.precision("completed_qty")
+ qty = flt(d.completed_qty, precision) + flt(d.process_loss_qty, precision)
+ if not qty:
d.status = "Pending"
- elif flt(d.completed_qty) < flt(self.qty):
+ elif flt(qty) < flt(self.qty):
d.status = "Work in Progress"
- elif flt(d.completed_qty) == flt(self.qty):
+ elif flt(qty) == flt(self.qty):
d.status = "Completed"
- elif flt(d.completed_qty) <= max_allowed_qty_for_wo:
+ elif flt(qty) <= max_allowed_qty_for_wo:
d.status = "Completed"
else:
frappe.throw(_("Completed Qty cannot be greater than 'Qty to Manufacture'"))
diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
index 31b9201..de1f67f 100644
--- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
+++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
@@ -2,12 +2,14 @@
"actions": [],
"creation": "2014-10-16 14:35:41.950175",
"doctype": "DocType",
+ "editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"details",
"operation",
"status",
"completed_qty",
+ "process_loss_qty",
"column_break_4",
"bom",
"workstation_type",
@@ -36,6 +38,7 @@
"fieldtype": "Section Break"
},
{
+ "columns": 2,
"fieldname": "operation",
"fieldtype": "Link",
"in_list_view": 1,
@@ -46,6 +49,7 @@
"reqd": 1
},
{
+ "columns": 2,
"fieldname": "bom",
"fieldtype": "Link",
"in_list_view": 1,
@@ -62,7 +66,7 @@
"oldfieldtype": "Text"
},
{
- "columns": 1,
+ "columns": 2,
"description": "Operation completed for how many finished goods?",
"fieldname": "completed_qty",
"fieldtype": "Float",
@@ -80,6 +84,7 @@
"options": "Pending\nWork in Progress\nCompleted"
},
{
+ "columns": 1,
"fieldname": "workstation",
"fieldtype": "Link",
"in_list_view": 1,
@@ -115,7 +120,7 @@
"fieldname": "time_in_mins",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Operation Time",
+ "label": "Time",
"oldfieldname": "time_in_mins",
"oldfieldtype": "Currency",
"reqd": 1
@@ -203,12 +208,21 @@
"fieldtype": "Link",
"label": "Workstation Type",
"options": "Workstation Type"
+ },
+ {
+ "columns": 2,
+ "fieldname": "process_loss_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Process Loss Qty",
+ "no_copy": 1,
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2022-11-09 01:37:56.563068",
+ "modified": "2023-06-09 14:03:01.612909",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Operation",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 2c8e7a7..00b4471 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -656,6 +656,21 @@
});
}
},
+
+ process_loss_qty(frm) {
+ if (frm.doc.process_loss_qty) {
+ frm.doc.process_loss_percentage = flt(frm.doc.process_loss_qty / frm.doc.fg_completed_qty * 100, precision("process_loss_qty", frm.doc));
+ refresh_field("process_loss_percentage");
+ }
+ },
+
+ process_loss_percentage(frm) {
+ debugger
+ if (frm.doc.process_loss_percentage) {
+ frm.doc.process_loss_qty = flt((frm.doc.fg_completed_qty * frm.doc.process_loss_percentage) / 100 , precision("process_loss_qty", frm.doc));
+ refresh_field("process_loss_qty");
+ }
+ }
});
frappe.ui.form.on('Stock Entry Detail', {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index bc5533f..9bf679b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -24,6 +24,7 @@
"company",
"posting_date",
"posting_time",
+ "column_break_eaoa",
"set_posting_time",
"inspection_required",
"apply_putaway_rule",
@@ -640,16 +641,16 @@
},
{
"collapsible": 1,
+ "depends_on": "eval: doc.fg_completed_qty > 0 && in_list([\"Manufacture\", \"Repack\"], doc.purpose)",
"fieldname": "section_break_7qsm",
"fieldtype": "Section Break",
"label": "Process Loss"
},
{
- "depends_on": "process_loss_percentage",
+ "depends_on": "eval: doc.fg_completed_qty > 0 && in_list([\"Manufacture\", \"Repack\"], doc.purpose)",
"fieldname": "process_loss_qty",
"fieldtype": "Float",
- "label": "Process Loss Qty",
- "read_only": 1
+ "label": "Process Loss Qty"
},
{
"fieldname": "column_break_e92r",
@@ -657,8 +658,6 @@
},
{
"depends_on": "eval:doc.from_bom && doc.fg_completed_qty",
- "fetch_from": "bom_no.process_loss_percentage",
- "fetch_if_empty": 1,
"fieldname": "process_loss_percentage",
"fieldtype": "Percent",
"label": "% Process Loss"
@@ -667,6 +666,10 @@
"fieldname": "items_section",
"fieldtype": "Section Break",
"label": "Items"
+ },
+ {
+ "fieldname": "column_break_eaoa",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-file-text",
@@ -674,7 +677,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-04-06 12:42:56.673180",
+ "modified": "2023-06-09 15:46:28.418339",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index f19df83..517fea5 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -442,13 +442,16 @@
if self.purpose == "Manufacture" and self.work_order:
for d in self.items:
if d.is_finished_item:
+ if self.process_loss_qty:
+ d.qty = self.fg_completed_qty - self.process_loss_qty
+
item_wise_qty.setdefault(d.item_code, []).append(d.qty)
precision = frappe.get_precision("Stock Entry Detail", "qty")
for item_code, qty_list in item_wise_qty.items():
total = flt(sum(qty_list), precision)
- if (self.fg_completed_qty - total) > 0:
+ if (self.fg_completed_qty - total) > 0 and not self.process_loss_qty:
self.process_loss_qty = flt(self.fg_completed_qty - total, precision)
self.process_loss_percentage = flt(self.process_loss_qty * 100 / self.fg_completed_qty)
@@ -578,7 +581,9 @@
for d in prod_order.get("operations"):
total_completed_qty = flt(self.fg_completed_qty) + flt(prod_order.produced_qty)
- completed_qty = d.completed_qty + (allowance_percentage / 100 * d.completed_qty)
+ completed_qty = (
+ d.completed_qty + d.process_loss_qty + (allowance_percentage / 100 * d.completed_qty)
+ )
if total_completed_qty > flt(completed_qty):
job_card = frappe.db.get_value("Job Card", {"operation_id": d.name}, "name")
if not job_card:
@@ -1640,16 +1645,36 @@
if self.purpose not in ("Manufacture", "Repack"):
return
- self.process_loss_qty = 0.0
- if not self.process_loss_percentage:
+ precision = self.precision("process_loss_qty")
+ if self.work_order:
+ data = frappe.get_all(
+ "Work Order Operation",
+ filters={"parent": self.work_order},
+ fields=["max(process_loss_qty) as process_loss_qty"],
+ )
+
+ if data and data[0].process_loss_qty is not None:
+ process_loss_qty = data[0].process_loss_qty
+ if flt(self.process_loss_qty, precision) != flt(process_loss_qty, precision):
+ self.process_loss_qty = flt(process_loss_qty, precision)
+
+ frappe.msgprint(
+ _("The Process Loss Qty has reset as per job cards Process Loss Qty"), alert=True
+ )
+
+ if not self.process_loss_percentage and not self.process_loss_qty:
self.process_loss_percentage = frappe.get_cached_value(
"BOM", self.bom_no, "process_loss_percentage"
)
- if self.process_loss_percentage:
+ if self.process_loss_percentage and not self.process_loss_qty:
self.process_loss_qty = flt(
(flt(self.fg_completed_qty) * flt(self.process_loss_percentage)) / 100
)
+ elif self.process_loss_qty and not self.process_loss_percentage:
+ self.process_loss_percentage = flt(
+ (flt(self.process_loss_qty) / flt(self.fg_completed_qty)) * 100
+ )
def set_work_order_details(self):
if not getattr(self, "pro_doc", None):