mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-26 05:15:20 +00:00
chore: resolve conflict
This commit is contained in:
@@ -1236,7 +1236,7 @@ class JobCard(Document):
|
|||||||
def set_status(self, update_status=False):
|
def set_status(self, update_status=False):
|
||||||
self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0]
|
self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0]
|
||||||
if self.finished_good and self.docstatus == 1:
|
if self.finished_good and self.docstatus == 1:
|
||||||
if (self.manufactured_qty + self.process_loss_qty) >= self.for_quantity:
|
if (self.manufactured_qty + self.process_loss_qty) >= self.get_qty_to_produce():
|
||||||
self.status = "Completed"
|
self.status = "Completed"
|
||||||
elif self.transferred_qty > 0 or self.skip_material_transfer:
|
elif self.transferred_qty > 0 or self.skip_material_transfer:
|
||||||
self.status = "Work In Progress"
|
self.status = "Work In Progress"
|
||||||
@@ -1267,7 +1267,8 @@ class JobCard(Document):
|
|||||||
self.status = "Work In Progress"
|
self.status = "Work In Progress"
|
||||||
|
|
||||||
if self.docstatus == 1 and (
|
if self.docstatus == 1 and (
|
||||||
self.for_quantity <= (self.total_completed_qty + self.process_loss_qty) or not self.items
|
self.get_qty_to_produce() <= (self.total_completed_qty + self.process_loss_qty)
|
||||||
|
or not self.items
|
||||||
):
|
):
|
||||||
self.status = "Completed"
|
self.status = "Completed"
|
||||||
|
|
||||||
@@ -1280,53 +1281,10 @@ class JobCard(Document):
|
|||||||
if self.workstation:
|
if self.workstation:
|
||||||
self.update_workstation_status()
|
self.update_workstation_status()
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
def get_qty_to_produce(self):
|
def get_qty_to_produce(self):
|
||||||
"""Qty this job card is expected to produce, the pending qty is left to another job card."""
|
"""Qty this job card is expected to produce, the pending qty is left to another job card."""
|
||||||
return flt(self.for_quantity) - flt(self.pending_qty)
|
return flt(self.for_quantity) - flt(self.pending_qty)
|
||||||
|
|
||||||
def set_finished_good_status(self):
|
|
||||||
# Only reached for a submitted job card (docstatus == 1) with a finished good, see set_status().
|
|
||||||
qty_to_produce = self.get_qty_to_produce()
|
|
||||||
|
|
||||||
if (self.manufactured_qty + self.process_loss_qty) >= qty_to_produce:
|
|
||||||
self.status = "Completed"
|
|
||||||
elif (self.total_completed_qty + self.process_loss_qty) >= qty_to_produce:
|
|
||||||
# Production is done and the card is submitted, but the finished goods have not been
|
|
||||||
# booked into stock yet (Manufacture Stock Entry pending) — distinct from active WIP.
|
|
||||||
self.status = "To Manufacture"
|
|
||||||
elif self.transferred_qty > 0 or self.skip_material_transfer:
|
|
||||||
self.status = "Work In Progress"
|
|
||||||
|
|
||||||
def set_non_semi_fg_status(self):
|
|
||||||
if self.items:
|
|
||||||
item_data = frappe.get_all(
|
|
||||||
"Job Card Item",
|
|
||||||
filters={"parent": self.name},
|
|
||||||
fields=["transferred_qty", "required_qty"],
|
|
||||||
)
|
|
||||||
all_transferred = item_data and all(
|
|
||||||
flt(d.transferred_qty) >= flt(d.required_qty) for d in item_data
|
|
||||||
)
|
|
||||||
any_transferred = any(flt(d.transferred_qty) > 0 for d in item_data)
|
|
||||||
|
|
||||||
if all_transferred:
|
|
||||||
self.status = "Material Transferred"
|
|
||||||
elif any_transferred:
|
|
||||||
self.status = "Partially Transferred"
|
|
||||||
elif flt(self.for_quantity) <= flt(self.transferred_qty):
|
|
||||||
self.status = "Material Transferred"
|
|
||||||
|
|
||||||
if self.time_logs:
|
|
||||||
self.status = "Work In Progress"
|
|
||||||
|
|
||||||
if self.docstatus == 1 and (
|
|
||||||
self.get_qty_to_produce() <= (self.total_completed_qty + self.process_loss_qty) or not self.items
|
|
||||||
):
|
|
||||||
self.status = "Completed"
|
|
||||||
|
|
||||||
>>>>>>> 970039d8ec (fix(job_card): leave the pending qty out of the job card's own output (#57686))
|
|
||||||
def set_wip_warehouse(self):
|
def set_wip_warehouse(self):
|
||||||
if not self.wip_warehouse:
|
if not self.wip_warehouse:
|
||||||
self.wip_warehouse = frappe.get_cached_value("Company", self.company, "default_wip_warehouse")
|
self.wip_warehouse = frappe.get_cached_value("Company", self.company, "default_wip_warehouse")
|
||||||
@@ -1625,13 +1583,8 @@ class JobCard(Document):
|
|||||||
|
|
||||||
ste = ManufactureEntry(
|
ste = ManufactureEntry(
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
"for_quantity": self.for_quantity - self.manufactured_qty,
|
|
||||||
"process_loss_qty": max(self.process_loss_qty - get_consumed_process_loss(), 0),
|
|
||||||
=======
|
|
||||||
"for_quantity": self.get_qty_to_produce() - self.manufactured_qty,
|
"for_quantity": self.get_qty_to_produce() - self.manufactured_qty,
|
||||||
"process_loss_qty": max(self.process_loss_qty - self.get_consumed_process_loss(), 0),
|
"process_loss_qty": max(self.process_loss_qty - get_consumed_process_loss(), 0),
|
||||||
>>>>>>> 970039d8ec (fix(job_card): leave the pending qty out of the job card's own output (#57686))
|
|
||||||
"job_card": self.name,
|
"job_card": self.name,
|
||||||
"skip_material_transfer": self.skip_material_transfer,
|
"skip_material_transfer": self.skip_material_transfer,
|
||||||
"backflush_from_wip_warehouse": self.backflush_from_wip_warehouse,
|
"backflush_from_wip_warehouse": self.backflush_from_wip_warehouse,
|
||||||
|
|||||||
@@ -1265,8 +1265,6 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
8,
|
8,
|
||||||
)
|
)
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
def test_semi_fg_pending_qty_is_left_to_another_job_card(self):
|
def test_semi_fg_pending_qty_is_left_to_another_job_card(self):
|
||||||
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
|
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
@@ -1319,7 +1317,16 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
|
|
||||||
make_stock_entry(item_code=rm, target=warehouse, qty=100, basic_rate=100)
|
make_stock_entry(item_code=rm, target=warehouse, qty=100, basic_rate=100)
|
||||||
|
|
||||||
job_card = self.get_first_job_card(work_order.name)
|
job_card = frappe.get_doc(
|
||||||
|
"Job Card",
|
||||||
|
frappe.get_all(
|
||||||
|
"Job Card",
|
||||||
|
filters={"work_order": work_order.name},
|
||||||
|
order_by="sequence_id, creation",
|
||||||
|
limit=1,
|
||||||
|
pluck="name",
|
||||||
|
)[0],
|
||||||
|
)
|
||||||
job_card.append("time_logs", {"from_time": "2024-04-01 08:00:00"})
|
job_card.append("time_logs", {"from_time": "2024-04-01 08:00:00"})
|
||||||
job_card.save()
|
job_card.save()
|
||||||
|
|
||||||
@@ -1337,7 +1344,7 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
self.assertEqual(flt(job_card.process_loss_qty), 0)
|
self.assertEqual(flt(job_card.process_loss_qty), 0)
|
||||||
|
|
||||||
job_card.submit()
|
job_card.submit()
|
||||||
self.assertEqual(job_card.status, "To Manufacture")
|
self.assertEqual(job_card.status, "Work In Progress")
|
||||||
|
|
||||||
manufacturing_entry = frappe.get_doc(job_card.make_stock_entry_for_semi_fg_item())
|
manufacturing_entry = frappe.get_doc(job_card.make_stock_entry_for_semi_fg_item())
|
||||||
finished_item = next(row for row in manufacturing_entry.items if row.is_finished_item)
|
finished_item = next(row for row in manufacturing_entry.items if row.is_finished_item)
|
||||||
@@ -1348,145 +1355,6 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
self.assertEqual(flt(job_card.manufactured_qty), 3)
|
self.assertEqual(flt(job_card.manufactured_qty), 3)
|
||||||
self.assertEqual(job_card.status, "Completed")
|
self.assertEqual(job_card.status, "Completed")
|
||||||
|
|
||||||
def test_semi_fg_sequence_needs_previous_operations_manufactured(self):
|
|
||||||
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
|
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
|
||||||
|
|
||||||
warehouse = "Stores - _TC"
|
|
||||||
rm1 = make_item("Sequence Check RM 1", {"is_stock_item": 1}).name
|
|
||||||
rm2 = make_item("Sequence Check RM 2", {"is_stock_item": 1}).name
|
|
||||||
sfg1 = make_item("Sequence Check SFG 1", {"is_stock_item": 1}).name
|
|
||||||
sfg2 = make_item("Sequence Check SFG 2", {"is_stock_item": 1}).name
|
|
||||||
fg = make_item("Sequence Check FG 1", {"is_stock_item": 1}).name
|
|
||||||
|
|
||||||
semi_fg_boms = {}
|
|
||||||
for semi_fg_item, raw_material in ((sfg1, rm1), (sfg2, rm2)):
|
|
||||||
bom = frappe.new_doc("BOM", company="_Test Company", item=semi_fg_item, quantity=1)
|
|
||||||
bom.append("items", {"item_code": raw_material, "qty": 1})
|
|
||||||
bom.insert()
|
|
||||||
bom.submit()
|
|
||||||
semi_fg_boms[semi_fg_item] = bom.name
|
|
||||||
|
|
||||||
fg_bom = frappe.new_doc(
|
|
||||||
"BOM",
|
|
||||||
company="_Test Company",
|
|
||||||
item=fg,
|
|
||||||
quantity=1,
|
|
||||||
with_operations=1,
|
|
||||||
track_semi_finished_goods=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
{
|
|
||||||
"operation": "Sequence Check Op A",
|
|
||||||
"finished_good": sfg1,
|
|
||||||
"bom_no": semi_fg_boms[sfg1],
|
|
||||||
"sequence_id": 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation": "Sequence Check Op B",
|
|
||||||
"finished_good": sfg2,
|
|
||||||
"bom_no": semi_fg_boms[sfg2],
|
|
||||||
"sequence_id": 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"operation": "Sequence Check Op C",
|
|
||||||
"finished_good": fg,
|
|
||||||
"is_final_finished_good": 1,
|
|
||||||
"sequence_id": 2,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
for row in operations:
|
|
||||||
row.update(
|
|
||||||
{
|
|
||||||
"workstation": "_Test Workstation A",
|
|
||||||
"finished_good_qty": 1,
|
|
||||||
"time_in_mins": 60,
|
|
||||||
"source_warehouse": warehouse,
|
|
||||||
"fg_warehouse": warehouse,
|
|
||||||
"skip_material_transfer": 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
make_workstation(row)
|
|
||||||
make_operation(row)
|
|
||||||
fg_bom.append("operations", row)
|
|
||||||
|
|
||||||
fg_bom.append("items", {"item_code": sfg1, "qty": 1, "operation_row_id": 3})
|
|
||||||
fg_bom.append("items", {"item_code": sfg2, "qty": 1, "operation_row_id": 3})
|
|
||||||
fg_bom.insert()
|
|
||||||
fg_bom.submit()
|
|
||||||
|
|
||||||
work_order = make_wo_order_test_record(
|
|
||||||
item=fg,
|
|
||||||
qty=5,
|
|
||||||
source_warehouse=warehouse,
|
|
||||||
fg_warehouse=warehouse,
|
|
||||||
bom_no=fg_bom.name,
|
|
||||||
skip_transfer=1,
|
|
||||||
do_not_save=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in work_order.operations:
|
|
||||||
row.time_in_mins = 60
|
|
||||||
|
|
||||||
work_order.save()
|
|
||||||
work_order.submit()
|
|
||||||
|
|
||||||
make_stock_entry(item_code=rm1, target=warehouse, qty=10, basic_rate=100)
|
|
||||||
make_stock_entry(item_code=rm2, target=warehouse, qty=10, basic_rate=100)
|
|
||||||
|
|
||||||
def get_job_card(operation):
|
|
||||||
return frappe.get_doc(
|
|
||||||
"Job Card",
|
|
||||||
frappe.db.get_value(
|
|
||||||
"Job Card",
|
|
||||||
{"work_order": work_order.name, "operation": operation, "docstatus": 0},
|
|
||||||
"name",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_time_log(job_card, day, qty):
|
|
||||||
job_card.append(
|
|
||||||
"time_logs",
|
|
||||||
{
|
|
||||||
"from_time": f"2024-01-{day} 08:00:00",
|
|
||||||
"to_time": f"2024-01-{day} 09:00:00",
|
|
||||||
"completed_qty": qty,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
jc_a = get_job_card("Sequence Check Op A")
|
|
||||||
jc_a.for_quantity = 3
|
|
||||||
add_time_log(jc_a, "01", 3)
|
|
||||||
jc_a.submit()
|
|
||||||
|
|
||||||
jc_b = get_job_card("Sequence Check Op B")
|
|
||||||
add_time_log(jc_b, "02", jc_b.for_quantity)
|
|
||||||
jc_b.submit()
|
|
||||||
frappe.get_doc(jc_b.make_stock_entry_for_semi_fg_item()).submit()
|
|
||||||
|
|
||||||
jc_c = get_job_card("Sequence Check Op C")
|
|
||||||
jc_c.for_quantity = 3
|
|
||||||
add_time_log(jc_c, "03", 3)
|
|
||||||
self.assertRaises(OperationSequenceError, jc_c.save)
|
|
||||||
|
|
||||||
frappe.get_doc(jc_a.make_stock_entry_for_semi_fg_item()).submit()
|
|
||||||
|
|
||||||
jc_c.reload()
|
|
||||||
jc_c.for_quantity = 4
|
|
||||||
add_time_log(jc_c, "03", 4)
|
|
||||||
self.assertRaises(OperationSequenceError, jc_c.save)
|
|
||||||
|
|
||||||
jc_c.reload()
|
|
||||||
jc_c.for_quantity = 3
|
|
||||||
add_time_log(jc_c, "03", 3)
|
|
||||||
jc_c.submit()
|
|
||||||
|
|
||||||
self.assertEqual(jc_c.docstatus, 1)
|
|
||||||
|
|
||||||
>>>>>>> 970039d8ec (fix(job_card): leave the pending qty out of the job card's own output (#57686))
|
|
||||||
def test_semi_fg_batch_auto_pull_on_manufacture(self):
|
def test_semi_fg_batch_auto_pull_on_manufacture(self):
|
||||||
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
|
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|||||||
Reference in New Issue
Block a user