Merge pull request #24034 from rohitwaghchaure/fixed-job-card-cacncel-flow
fix: don't cancel job card if manufacturing entry has made
This commit is contained in:
@@ -10,6 +10,7 @@ from frappe.model.mapper import get_mapped_doc
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class OperationMismatchError(frappe.ValidationError): pass
|
class OperationMismatchError(frappe.ValidationError): pass
|
||||||
|
class JobCardCancelError(frappe.ValidationError): pass
|
||||||
|
|
||||||
class JobCard(Document):
|
class JobCard(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@@ -110,29 +111,44 @@ class JobCard(Document):
|
|||||||
for_quantity, time_in_mins = 0, 0
|
for_quantity, time_in_mins = 0, 0
|
||||||
from_time_list, to_time_list = [], []
|
from_time_list, to_time_list = [], []
|
||||||
|
|
||||||
field = "operation_id"
|
|
||||||
data = frappe.get_all('Job Card',
|
data = 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"],
|
||||||
filters = {"docstatus": 1, "work_order": self.work_order, field: self.get(field)})
|
filters = {"docstatus": 1, "work_order": self.work_order, "operation_id": self.operation_id})
|
||||||
|
|
||||||
if data and len(data) > 0:
|
if data and len(data) > 0:
|
||||||
for_quantity = data[0].completed_qty
|
for_quantity = flt(data[0].completed_qty)
|
||||||
time_in_mins = data[0].time_in_mins
|
time_in_mins = flt(data[0].time_in_mins)
|
||||||
|
|
||||||
if self.get(field):
|
wo = frappe.get_doc('Work Order', self.work_order)
|
||||||
|
if self.operation_id:
|
||||||
|
self.validate_produced_quantity(for_quantity, wo)
|
||||||
|
self.update_work_order_data(for_quantity, time_in_mins, wo)
|
||||||
|
|
||||||
|
def validate_produced_quantity(self, for_quantity, wo):
|
||||||
|
if self.docstatus < 2: return
|
||||||
|
|
||||||
|
if wo.produced_qty > for_quantity:
|
||||||
|
first_part_msg = (_("The {0} {1} is used to calculate the valuation cost for the finished good {2}.")
|
||||||
|
.format(frappe.bold(_("Job Card")), frappe.bold(self.name), frappe.bold(self.production_item)))
|
||||||
|
|
||||||
|
second_part_msg = (_("Kindly cancel the Manufacturing Entries first against the work order {0}.")
|
||||||
|
.format(frappe.bold(get_link_to_form("Work Order", self.work_order))))
|
||||||
|
|
||||||
|
frappe.throw(_("{0} {1}").format(first_part_msg, second_part_msg),
|
||||||
|
JobCardCancelError, title = _("Error"))
|
||||||
|
|
||||||
|
def update_work_order_data(self, for_quantity, time_in_mins, wo):
|
||||||
time_data = frappe.db.sql("""
|
time_data = frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
min(from_time) as start_time, max(to_time) as end_time
|
min(from_time) as start_time, max(to_time) as end_time
|
||||||
FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
|
FROM `tabJob Card` jc, `tabJob Card Time Log` jctl
|
||||||
WHERE
|
WHERE
|
||||||
jctl.parent = jc.name and jc.work_order = %s
|
jctl.parent = jc.name and jc.work_order = %s
|
||||||
and jc.{0} = %s and jc.docstatus = 1
|
and jc.operation_id = %s and jc.docstatus = 1
|
||||||
""".format(field), (self.work_order, self.get(field)), as_dict=1)
|
""", (self.work_order, self.operation_id), as_dict=1)
|
||||||
|
|
||||||
wo = frappe.get_doc('Work Order', self.work_order)
|
|
||||||
|
|
||||||
for data in wo.operations:
|
for data in wo.operations:
|
||||||
if data.get("name") == self.get(field):
|
if data.get("name") == self.operation_id:
|
||||||
data.completed_qty = for_quantity
|
data.completed_qty = for_quantity
|
||||||
data.actual_operation_time = time_in_mins
|
data.actual_operation_time = time_in_mins
|
||||||
data.actual_start_time = time_data[0].start_time if time_data else None
|
data.actual_start_time = time_data[0].start_time if time_data else None
|
||||||
|
|||||||
@@ -5,16 +5,17 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import unittest
|
import unittest
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt, time_diff_in_hours, now, add_days, cint
|
from frappe.utils import flt, now, cint, add_to_date
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||||
from erpnext.manufacturing.doctype.work_order.work_order \
|
|
||||||
import make_stock_entry, ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError
|
|
||||||
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
||||||
from erpnext.stock.utils import get_bin
|
from erpnext.stock.utils import get_bin
|
||||||
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
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
|
from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry,
|
||||||
|
ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError)
|
||||||
|
from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError
|
||||||
|
|
||||||
class TestWorkOrder(unittest.TestCase):
|
class TestWorkOrder(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -397,15 +398,42 @@ class TestWorkOrder(unittest.TestCase):
|
|||||||
data = frappe.get_cached_value('BOM',
|
data = frappe.get_cached_value('BOM',
|
||||||
{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
|
{'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item'])
|
||||||
|
|
||||||
if data:
|
|
||||||
bom, bom_item = data
|
bom, bom_item = data
|
||||||
|
|
||||||
bom_doc = frappe.get_doc('BOM', bom)
|
bom_doc = frappe.get_doc('BOM', bom)
|
||||||
work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom)
|
work_order = make_wo_order_test_record(item=bom_item, qty=1,
|
||||||
|
bom_no=bom, source_warehouse="_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
for row in work_order.required_items:
|
||||||
|
test_stock_entry.make_stock_entry(item_code=row.item_code,
|
||||||
|
target="_Test Warehouse - _TC", qty=row.required_qty, basic_rate=100)
|
||||||
|
|
||||||
|
ste = frappe.get_doc(make_stock_entry(work_order.name, "Material Transfer for Manufacture", 1))
|
||||||
|
ste.submit()
|
||||||
|
|
||||||
job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
|
job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name})
|
||||||
self.assertEqual(len(job_cards), len(bom_doc.operations))
|
self.assertEqual(len(job_cards), len(bom_doc.operations))
|
||||||
|
|
||||||
|
for i, job_card in enumerate(job_cards):
|
||||||
|
doc = frappe.get_doc("Job Card", job_card)
|
||||||
|
doc.append("time_logs", {
|
||||||
|
"from_time": now(),
|
||||||
|
"hours": i,
|
||||||
|
"to_time": add_to_date(now(), i),
|
||||||
|
"completed_qty": doc.for_quantity
|
||||||
|
})
|
||||||
|
doc.submit()
|
||||||
|
|
||||||
|
ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1))
|
||||||
|
ste1.submit()
|
||||||
|
|
||||||
|
for job_card in job_cards:
|
||||||
|
doc = frappe.get_doc("Job Card", job_card)
|
||||||
|
self.assertRaises(JobCardCancelError, doc.cancel)
|
||||||
|
|
||||||
|
ste1.cancel()
|
||||||
|
ste.cancel()
|
||||||
|
|
||||||
def test_work_order_with_non_transfer_item(self):
|
def test_work_order_with_non_transfer_item(self):
|
||||||
items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0}
|
items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0}
|
||||||
for item, allow_transfer in items.items():
|
for item, allow_transfer in items.items():
|
||||||
|
|||||||
Reference in New Issue
Block a user