Merge pull request #35629 from rohitwaghchaure/fixed-process-loss-in-job-card
fix: added process loss in job card
This commit is contained in:
commit
4ee08b92ae
@ -83,7 +83,7 @@ frappe.ui.form.on('Job Card', {
|
||||
// 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 @@ frappe.ui.form.on('Job Card', {
|
||||
}
|
||||
});
|
||||
|
||||
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");
|
||||
}
|
||||
});
|
||||
|
@ -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",
|
||||
|
@ -161,7 +161,7 @@ class JobCard(Document):
|
||||
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 @@ class JobCard(Document):
|
||||
},
|
||||
)
|
||||
|
||||
def before_save(self):
|
||||
self.set_process_loss()
|
||||
|
||||
def on_submit(self):
|
||||
self.validate_transfer_qty()
|
||||
self.validate_job_card()
|
||||
@ -487,19 +490,35 @@ class JobCard(Document):
|
||||
)
|
||||
)
|
||||
|
||||
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 @@ class JobCard(Document):
|
||||
):
|
||||
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 @@ class JobCard(Document):
|
||||
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 @@ class JobCard(Document):
|
||||
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 @@ class JobCard(Document):
|
||||
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 @@ class JobCard(Document):
|
||||
_("{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 @@ class JobCard(Document):
|
||||
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 @@ class JobCard(Document):
|
||||
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 @@ class JobCard(Document):
|
||||
|
||||
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 @@ class JobCard(Document):
|
||||
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."))
|
||||
|
@ -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 @@ class TestJobCard(FrappeTestCase):
|
||||
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"
|
||||
|
@ -141,6 +141,7 @@ def setup_bom(**args):
|
||||
routing=args.routing,
|
||||
with_operations=1,
|
||||
currency=args.currency,
|
||||
source_warehouse=args.source_warehouse,
|
||||
)
|
||||
else:
|
||||
bom_doc = frappe.get_doc("BOM", name)
|
||||
|
@ -903,7 +903,7 @@ class TestWorkOrder(FrappeTestCase):
|
||||
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):
|
||||
|
@ -139,7 +139,7 @@ frappe.ui.form.on("Work Order", {
|
||||
}
|
||||
|
||||
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 @@ frappe.ui.form.on("Work Order", {
|
||||
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 @@ frappe.ui.form.on("Work Order", {
|
||||
|
||||
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 @@ frappe.ui.form.on("Work Order", {
|
||||
'workstation': data.workstation,
|
||||
'batch_size': data.batch_size,
|
||||
'qty': pending_qty,
|
||||
'pending_qty': pending_qty
|
||||
'pending_qty': pending_qty,
|
||||
'sequence_id': data.sequence_id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -245,7 +245,9 @@ class WorkOrder(Document):
|
||||
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 @@ class WorkOrder(Document):
|
||||
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'"))
|
||||
|
@ -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",
|
||||
|
@ -656,6 +656,21 @@ frappe.ui.form.on('Stock Entry', {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
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', {
|
||||
|
@ -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",
|
||||
|
@ -442,13 +442,16 @@ class StockEntry(StockController):
|
||||
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 @@ class StockEntry(StockController):
|
||||
|
||||
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 @@ class StockEntry(StockController):
|
||||
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):
|
||||
|
Loading…
Reference in New Issue
Block a user