1
0
forked from erp-dev/erp

feat: added plate order clone

This commit is contained in:
2025-12-16 08:42:53 +08:00
parent 4c9b8e6a42
commit 0638dd1ad3
25 changed files with 46174 additions and 8 deletions

View File

@@ -10,6 +10,8 @@ from basic_info import models as basic_models
from printing import models as printing_models
from business import models as business_models
from api_v2.views.printing import PrintingJobByCustomerView
from django.contrib.contenttypes.models import ContentType
from stateflow import models as stateflow_models
class QuickCreateEmployeeUserAPITest(TestCase):
@@ -205,5 +207,275 @@ class PrintingJobByCustomerAPITest(TestCase):
})
self.assertEqual(len(resp.data), 1)
data = resp.data[0]
self.assertEqual(data['product'], self.product.id)
self.assertEqual(data['product']['id'], self.product.id)
self.assertEqual(data['billed_quantity'], '20.00')
class PrintingJobBatchAdvanceV2APITest(TestCase):
def setUp(self):
self.client = APIClient()
# 工厂用户(满足 IsPrintingFactory
self.merchant = basic_models.Merchant.objects.create(
name='印染工厂',
type=basic_models.MerchantTypeEnum.FACTORY,
)
self.user = get_user_model().objects.create_user(username='factory_user', password='pass12345')
self.employee = basic_models.Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='工厂员工',
)
self.client.force_authenticate(user=self.user)
# 基础客户/产品
self.customer = basic_models.Customer.objects.create(
merchant=self.merchant,
name='客户A',
created_by=None,
)
category = basic_models.ProductCategory.objects.create(
merchant=self.merchant,
name='品类',
product_prefix='FAB',
)
self.product = basic_models.Product.objects.create(
merchant=self.merchant,
category=category,
name='产品A',
human_id='FAB-100',
width_size=Decimal('150.00'),
color='红色',
unit=basic_models.ProductUnitEnum.METER,
)
# stateflow流程 + 3个节点 + 节点参数(第一步必填)
self.state1 = stateflow_models.State.objects.create(name='待处理1', description='第一个节点')
self.state2 = stateflow_models.State.objects.create(name='待处理2', description='第二个节点')
self.state3 = stateflow_models.State.objects.create(name='待处理3', description='第三个节点')
self.param_required = stateflow_models.StateParameter.objects.create(
key='temperature',
value='',
is_required=True,
description='温度(必填)',
)
self.state1.parameters.add(self.param_required)
self.process = stateflow_models.Process.objects.create(name='印染流程')
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
# printing订单 + 两条明细(每条明细都有 business_object
self.printing_order = printing_models.PrintingOrder.objects.create(
customer=self.customer,
fabric='',
width='150cm',
process=self.process,
)
bo1 = stateflow_models.BusinessObject.objects.create(
name='BO-1',
process=self.process,
)
bo2 = stateflow_models.BusinessObject.objects.create(
name='BO-2',
process=self.process,
)
self.job1 = printing_models.PrintingJob.objects.create(
printing_order=self.printing_order,
product=self.product,
quantity=10,
unit='',
business_object=bo1,
)
self.job2 = printing_models.PrintingJob.objects.create(
printing_order=self.printing_order,
product=self.product,
quantity=20,
unit='',
business_object=bo2,
)
def test_preview_success_returns_next_state_and_parameters(self):
resp = self.client.post(
'/api/v2/printing-jobs/batch-advance/preview/',
{'printing_job_ids': [self.job1.id, self.job2.id]},
format='json'
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data['printing_order_id'], self.printing_order.id)
self.assertEqual(set(resp.data['printing_job_ids']), {self.job1.id, self.job2.id})
next_state = resp.data['next_state']
self.assertEqual(next_state['id'], self.state1.id)
self.assertEqual(next_state['order'], 0)
self.assertTrue(any(p['key'] == 'temperature' and p['is_required'] for p in next_state['parameters']))
def test_preview_fails_when_jobs_not_same_printing_order(self):
other_order = printing_models.PrintingOrder.objects.create(
customer=self.customer,
fabric='',
width='160cm',
process=self.process,
)
bo3 = stateflow_models.BusinessObject.objects.create(name='BO-3', process=self.process)
job3 = printing_models.PrintingJob.objects.create(
printing_order=other_order,
product=self.product,
quantity=5,
unit='',
business_object=bo3,
)
resp = self.client.post(
'/api/v2/printing-jobs/batch-advance/preview/',
{'printing_job_ids': [self.job1.id, job3.id]},
format='json'
)
self.assertEqual(resp.status_code, 400)
self.assertIn('printing_order', resp.data.get('detail', ''))
def test_submit_success_advances_all_and_creates_batch_record(self):
resp = self.client.post(
'/api/v2/printing-jobs/batch-advance/',
{
'printing_job_ids': [self.job1.id, self.job2.id],
'parameters': {'temperature': '25.5'}
},
format='json'
)
self.assertEqual(resp.status_code, 200)
self.assertIn('batch_id', resp.data)
# stateflow两条 business_object 都应生成 1 条完成日志,且完成的是 state1
self.job1.refresh_from_db()
self.job2.refresh_from_db()
self.assertEqual(self.job1.business_object.state_logs.filter(is_cancelled=False).count(), 1)
self.assertEqual(self.job2.business_object.state_logs.filter(is_cancelled=False).count(), 1)
self.assertEqual(self.job1.business_object.state_logs.first().state_id, self.state1.id)
self.assertEqual(self.job2.business_object.state_logs.first().state_id, self.state1.id)
# printing批量记录应存在且关联 jobs
record = printing_models.PrintingJobBatchAdvanceRecord.objects.get(id=resp.data['batch_id'])
self.assertEqual(record.created_by_id, self.user.id)
self.assertEqual(record.printing_order_id, self.printing_order.id)
self.assertEqual(record.state_id, self.state1.id)
self.assertEqual(record.parameters.get('temperature'), '25.5')
self.assertEqual(set(record.printing_jobs.values_list('id', flat=True)), {self.job1.id, self.job2.id})
def test_submit_fails_when_missing_required_parameter(self):
resp = self.client.post(
'/api/v2/printing-jobs/batch-advance/',
{
'printing_job_ids': [self.job1.id, self.job2.id],
'parameters': {} # 缺少 temperature
},
format='json'
)
self.assertEqual(resp.status_code, 400)
self.assertIn('缺失必填参数', resp.data.get('detail', ''))
# 未产生任何日志/批量记录
self.assertEqual(self.job1.business_object.state_logs.count(), 0)
self.assertEqual(self.job2.business_object.state_logs.count(), 0)
self.assertEqual(printing_models.PrintingJobBatchAdvanceRecord.objects.count(), 0)
class BusinessObjectCloneV2APITest(TestCase):
def setUp(self):
self.client = APIClient()
self.user = get_user_model().objects.create_user(username='u1', password='pass12345')
self.client.force_authenticate(user=self.user)
self.state1 = stateflow_models.State.objects.create(name='S1')
self.state2 = stateflow_models.State.objects.create(name='S2')
self.process = stateflow_models.Process.objects.create(name='P1')
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
ct = ContentType.objects.get_for_model(stateflow_models.Process)
self.bo = stateflow_models.BusinessObject.objects.create(
name='BO',
process=self.process,
content_type=ct,
object_id=111,
)
# 创建一条日志 + 参数记录,确保“克隆包含工艺参数”
log = stateflow_models.StateFlowRecord.objects.create(
business_object=self.bo,
state=self.state1,
completed_by=self.user,
)
stateflow_models.StateLogParameterRecord.objects.create(
state_log=log,
parameters={'temperature': '25.5'},
remark='r1',
)
def test_clone_business_object_api_returns_new_id(self):
new_object_id = 222
resp = self.client.post(
'/api/v2/stateflow/business-objects/clone/',
{
'business_object_id': self.bo.id,
'content_type': self.bo.content_type_id,
'object_id': new_object_id,
},
format='json'
)
self.assertEqual(resp.status_code, 200)
self.assertIn('business_object_id', resp.data)
new_id = resp.data['business_object_id']
self.assertNotEqual(new_id, self.bo.id)
cloned = stateflow_models.BusinessObject.objects.get(id=new_id)
self.assertEqual(cloned.process_id, self.bo.process_id)
self.assertEqual(cloned.state_logs.count(), self.bo.state_logs.count())
self.assertEqual(cloned.content_type_id, self.bo.content_type_id)
self.assertEqual(cloned.object_id, new_object_id)
def test_clone_business_object_api_404(self):
resp = self.client.post(
'/api/v2/stateflow/business-objects/clone/',
{
'business_object_id': 999999,
'content_type': self.bo.content_type_id,
'object_id': 222,
},
format='json'
)
self.assertEqual(resp.status_code, 404)
def test_clone_business_object_api_rejects_mismatched_content_type(self):
# 用另一个 ContentType比如 State
ct_state = ContentType.objects.get_for_model(stateflow_models.State)
self.assertNotEqual(ct_state.id, self.bo.content_type_id)
resp = self.client.post(
'/api/v2/stateflow/business-objects/clone/',
{
'business_object_id': self.bo.id,
'content_type': ct_state.id,
'object_id': 222,
},
format='json'
)
self.assertEqual(resp.status_code, 400)
self.assertIn('content_type', resp.data.get('detail', ''))
def test_clone_business_object_api_unauthorized(self):
client = APIClient()
resp = client.post(
'/api/v2/stateflow/business-objects/clone/',
{
'business_object_id': self.bo.id,
'content_type': self.bo.content_type_id,
'object_id': 222,
},
format='json'
)
self.assertEqual(resp.status_code, 401)