feat(backend): cascade complete tasks and steps when finishing work order
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
c6a9745c80
commit
01e0d99a03
2 files changed
+111
-8
No files matched your search
@@ -924,12 +924,51 @@ def complete_production_work_order(
|
||||
raise ValueError("工单不存在")
|
||||
if wo.work_order_status == "ywc":
|
||||
raise ValueError("工单已完成")
|
||||
if not data:
|
||||
raise ValueError("须选择生产结论(成功或失败)")
|
||||
concl = (getattr(data, "conclusion", None) or "").strip()
|
||||
if concl not in ("cg", "sb"):
|
||||
raise ValueError("须选择生产结论(成功或失败)")
|
||||
remark_val = None
|
||||
if getattr(data, "remark", None) is not None:
|
||||
remark_val = (data.remark or "").strip() or None
|
||||
|
||||
now = datetime.now()
|
||||
|
||||
incomplete_tasks = (
|
||||
db.query(BizProductionTask)
|
||||
.filter(
|
||||
BizProductionTask.work_order_id == wo.id,
|
||||
BizProductionTask.tenant_no == tenant_no,
|
||||
BizProductionTask.del_flag == 0,
|
||||
BizProductionTask.task_status != "ywc",
|
||||
)
|
||||
.all()
|
||||
)
|
||||
for task in incomplete_tasks:
|
||||
production_task_svc._force_complete_task_steps(
|
||||
db, task.id, tenant_no, user_id, now
|
||||
)
|
||||
task.task_status = "ywc"
|
||||
task.conclusion = concl
|
||||
if remark_val is not None:
|
||||
task.remark = remark_val
|
||||
if not task.completed_time:
|
||||
task.completed_time = now
|
||||
task.update_by = user_id
|
||||
task.update_time = now
|
||||
db.add(task)
|
||||
production_task_svc._consume_task_input_cell_products(
|
||||
db, task.id, user_id, now
|
||||
)
|
||||
|
||||
wo.work_order_status = "ywc"
|
||||
wo.update_by = user_id
|
||||
wo.update_time = datetime.now()
|
||||
if data and data.remark is not None:
|
||||
wo.remark = data.remark
|
||||
wo.update_time = now
|
||||
if remark_val is not None:
|
||||
wo.remark = remark_val
|
||||
db.add(wo)
|
||||
|
||||
from app.crud.pilot_outbound_status_sync import sync_schedule_from_production_work_order
|
||||
|
||||
sync_schedule_from_production_work_order(db, wo, user_id=user_id)
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
"""生产完成任务/工单状态级联。"""
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import ANY, MagicMock, patch
|
||||
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.schemas.production_work_order import ProductionWorkOrderComplete
|
||||
|
||||
|
||||
from app.crud import production_task as production_task_svc
|
||||
from app.crud import production_work_order as production_work_order_svc
|
||||
from app.schemas.production_work_order import ProductionWorkOrderComplete
|
||||
|
||||
|
||||
class TestProductionWorkOrderCompleteSchema(unittest.TestCase):
|
||||
@@ -82,5 +81,70 @@ class TestUpdateTaskStatusCascadeSteps(unittest.TestCase):
|
||||
self.assertIs(result, task)
|
||||
|
||||
|
||||
class TestCompleteProductionWorkOrderCascade(unittest.TestCase):
|
||||
@patch("app.crud.pilot_outbound_status_sync.sync_schedule_from_production_work_order")
|
||||
@patch("app.crud.production_task._consume_task_input_cell_products")
|
||||
@patch("app.crud.production_task._force_complete_task_steps")
|
||||
@patch("app.crud.production_work_order.get_production_work_order")
|
||||
def test_cascades_incomplete_tasks_and_steps(
|
||||
self, mock_get_wo, mock_force, mock_consume, mock_sync_sched
|
||||
):
|
||||
db = MagicMock()
|
||||
wo = MagicMock(id="wo-1", work_order_status="jxz", remark=None)
|
||||
mock_get_wo.return_value = wo
|
||||
|
||||
task_a = MagicMock(
|
||||
id="ta",
|
||||
task_status="dzx",
|
||||
completed_time=None,
|
||||
conclusion=None,
|
||||
remark=None,
|
||||
)
|
||||
q = MagicMock()
|
||||
db.query.return_value = q
|
||||
q.filter.return_value = q
|
||||
q.all.return_value = [task_a]
|
||||
|
||||
body = ProductionWorkOrderComplete(conclusion="sb", remark="batch fail")
|
||||
result = production_work_order_svc.complete_production_work_order(
|
||||
db, "wo-1", "t1", "user-1", body
|
||||
)
|
||||
|
||||
mock_force.assert_called_once_with(db, "ta", "t1", "user-1", ANY)
|
||||
self.assertEqual(task_a.task_status, "ywc")
|
||||
self.assertEqual(task_a.conclusion, "sb")
|
||||
self.assertEqual(task_a.remark, "batch fail")
|
||||
mock_consume.assert_called_once()
|
||||
self.assertEqual(wo.work_order_status, "ywc")
|
||||
self.assertEqual(wo.remark, "batch fail")
|
||||
mock_sync_sched.assert_called_once()
|
||||
db.commit.assert_called_once()
|
||||
self.assertIs(result, wo)
|
||||
|
||||
@patch("app.crud.production_work_order.get_production_work_order")
|
||||
def test_rejects_invalid_conclusion(self, mock_get_wo):
|
||||
db = MagicMock()
|
||||
mock_get_wo.return_value = MagicMock(work_order_status="jxz")
|
||||
bad = MagicMock(conclusion="xx", remark=None)
|
||||
with self.assertRaises(ValueError):
|
||||
production_work_order_svc.complete_production_work_order(
|
||||
db, "wo-1", "t1", "user-1", bad
|
||||
)
|
||||
|
||||
@patch("app.crud.production_work_order.get_production_work_order")
|
||||
def test_rejects_already_completed(self, mock_get_wo):
|
||||
db = MagicMock()
|
||||
mock_get_wo.return_value = MagicMock(work_order_status="ywc")
|
||||
with self.assertRaises(ValueError) as ctx:
|
||||
production_work_order_svc.complete_production_work_order(
|
||||
db,
|
||||
"wo-1",
|
||||
"t1",
|
||||
"user-1",
|
||||
ProductionWorkOrderComplete(conclusion="cg"),
|
||||
)
|
||||
self.assertIn("已完成", str(ctx.exception))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user