1
0
forked from erp-dev/erp

feat: new api for plate order (get_plate_order_by_state_status)

This commit is contained in:
2025-12-25 17:18:44 +08:00
parent 2c5174a1ec
commit ff1ea2482d
21 changed files with 936 additions and 58 deletions

View File

@@ -960,6 +960,239 @@ class PlateOrderByProcessV2APITest(TestCase):
self.assertEqual(resp.status_code, 400)
class PlateOrderByStateStatusV2APITest(TestCase):
def setUp(self):
self.client = APIClient()
self.merchant = basic_models.Merchant.objects.create(
name='开版工厂3',
type=basic_models.MerchantTypeEnum.FACTORY,
)
self.user = get_user_model().objects.create_user(username='state_status_user', password='pass12345')
basic_models.Employee.objects.create(
merchant=self.merchant,
sys_user=self.user,
name='开版员工3',
)
self.client.force_authenticate(user=self.user)
self.customer = basic_models.Customer.objects.create(
merchant=self.merchant,
name='客户SS',
created_by=None,
)
self.state_prepare = stateflow_models.State.objects.create(name='画图')
self.state_target = stateflow_models.State.objects.create(name='调色')
self.state_unlinked = stateflow_models.State.objects.create(name='未绑定')
self.param_temp = stateflow_models.StateParameter.objects.create(key='temperature', value='25')
self.state_target.parameters.add(self.param_temp)
self.process = stateflow_models.Process.objects.create(name='主流程')
self.node_prepare = stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state_prepare, order=0)
self.node_target = stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state_target, order=1)
self.other_process = stateflow_models.Process.objects.create(name='其他流程')
stateflow_models.ProcessNode.objects.create(process=self.other_process, state=self.state_target, order=0)
self.po_not_started = printing_models.PlateOrder.objects.create(
customer=self.customer,
process=self.process.id,
design_code='PO-NOT',
style_name='款式N',
)
self.po_no_logs = printing_models.PlateOrder.objects.create(
customer=self.customer,
process=self.process.id,
design_code='PO-ZERO',
style_name='款式Z',
)
self.po_completed_old = printing_models.PlateOrder.objects.create(
customer=self.customer,
process=self.process.id,
design_code='PO-C-OLD',
style_name='款式CO',
)
self.po_completed_new = printing_models.PlateOrder.objects.create(
customer=self.customer,
process=self.process.id,
design_code='PO-C-NEW',
style_name='款式CN',
)
self.po_cancelled = printing_models.PlateOrder.objects.create(
customer=self.customer,
process=self.process.id,
design_code='PO-CAN',
style_name='款式CA',
)
self.po_other_process = printing_models.PlateOrder.objects.create(
customer=self.customer,
process=self.other_process.id,
design_code='PO-OTHER',
style_name='款式OP',
)
self._complete_state(self.po_not_started, self.state_prepare)
self._complete_state(self.po_completed_old, self.state_prepare)
self._complete_state(self.po_completed_new, self.state_prepare)
self._complete_state(self.po_cancelled, self.state_prepare)
self._complete_state(self.po_completed_old, self.state_target, parameters={'temperature': '28'})
self._complete_state(self.po_completed_new, self.state_target, parameters={'temperature': '32'})
self._complete_state(self.po_cancelled, self.state_target, is_cancelled=True, parameters={'temperature': '99'})
self._complete_state(self.po_other_process, self.state_target, parameters={'temperature': '88'})
tz = timezone.get_default_timezone()
t_old = timezone.make_aware(datetime.datetime(2025, 12, 1, 9, 0, 0), tz)
t_new = timezone.make_aware(datetime.datetime(2025, 12, 3, 9, 0, 0), tz)
printing_models.PlateOrder.objects.filter(id=self.po_completed_old.id).update(created_at=t_old)
printing_models.PlateOrder.objects.filter(id=self.po_completed_new.id).update(created_at=t_new)
self.url = '/api/v2/plate-orders/by-state-status/'
def _complete_state(self, plate_order, state, *, is_cancelled=False, parameters=None):
log = stateflow_models.StateFlowRecord.objects.create(
business_object=plate_order.business_object,
state=state,
completed_by=self.user,
is_cancelled=is_cancelled,
)
if parameters is not None:
stateflow_models.StateLogParameterRecord.objects.create(
state_log=log,
parameters=parameters,
)
return log
def test_completed_status_returns_sorted_plate_orders(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'state_id': self.state_target.id,
'status': 'completed',
}
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data['state']['id'], self.state_target.id)
self.assertEqual(resp.data['status'], 'completed')
ids = [item['id'] for item in resp.data['results']]
self.assertEqual(ids, [self.po_completed_new.id, self.po_completed_old.id])
template_keys = [p['key'] for p in resp.data['state_parameters']]
self.assertEqual(template_keys, ['temperature'])
by_id = {item['id']: item for item in resp.data['results']}
self.assertEqual(by_id[self.po_completed_new.id]['state_parameters'][0]['value'], '32')
self.assertEqual(by_id[self.po_completed_old.id]['state_parameters'][0]['value'], '28')
self.assertIsNotNone(by_id[self.po_completed_new.id]['state_log'])
self.assertFalse(by_id[self.po_completed_new.id]['state_log']['is_cancelled'])
def test_default_status_is_completed(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'state_id': self.state_target.id,
}
)
self.assertEqual(resp.status_code, 200)
ids = [item['id'] for item in resp.data['results']]
self.assertEqual(set(ids), {self.po_completed_new.id, self.po_completed_old.id})
def test_state_id_with_not_started_status_is_invalid(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'state_id': self.state_target.id,
'status': 'not_started',
}
)
self.assertEqual(resp.status_code, 400)
def test_cancelled_status_returns_orders_with_cancelled_logs(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'state_id': self.state_target.id,
'status': 'cancelled',
}
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data['results'][0]['id'], self.po_cancelled.id)
self.assertTrue(resp.data['results'][0]['state_log']['is_cancelled'])
def test_invalid_process_state_pair_returns_400(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'state_id': self.state_unlinked.id,
'status': 'completed',
}
)
self.assertEqual(resp.status_code, 400)
self.assertIn('state 不属于该 process', resp.data['detail'])
def test_ordering_created_at_and_pagination(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'state_id': self.state_target.id,
'status': 'completed',
'ordering': 'created_at',
'limit': 1,
'offset': 1,
}
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data['count'], 2)
self.assertEqual(len(resp.data['results']), 1)
self.assertEqual(resp.data['results'][0]['id'], self.po_completed_new.id)
def test_other_process_records_are_excluded(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'state_id': self.state_target.id,
'status': 'completed',
}
)
ids = [item['id'] for item in resp.data['results']]
self.assertNotIn(self.po_other_process.id, ids)
def test_without_state_id_returns_orders_without_any_logs(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
}
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data['status'], 'not_started')
self.assertIsNone(resp.data['state'])
self.assertEqual(resp.data['state_parameters'], [])
ids = [item['id'] for item in resp.data['results']]
self.assertEqual(ids, [self.po_no_logs.id])
self.assertEqual(resp.data['results'][0]['state_parameters'], [])
self.assertIsNone(resp.data['results'][0]['state_log'])
def test_without_state_id_only_allows_not_started_status(self):
resp = self.client.get(
self.url,
{
'process_id': self.process.id,
'status': 'completed',
}
)
self.assertEqual(resp.status_code, 400)
self.assertIn('state_id 为空时仅支持 status=not_started', resp.data['detail'])
class PlateOrderBatchUpdateV2APITest(TestCase):
def setUp(self):
self.client = APIClient()