1
0
forked from erp-dev/erp

feat: batch add parameters

This commit is contained in:
2026-01-07 16:33:33 +08:00
parent 5b07332015
commit beaa93c3cb
9 changed files with 722 additions and 15 deletions

View File

@@ -421,7 +421,7 @@ class PrintingJobCreateUpdateSerializer(serializers.ModelSerializer):
class PrintingJobBatchAdvanceRecordSerializer(serializers.ModelSerializer): class PrintingJobBatchAdvanceRecordSerializer(serializers.ModelSerializer):
"""印染任务批量推进记录(用于嵌入 PrintingJob 的序列化结果)""" """印染任务批量操作记录(用于嵌入 PrintingJob 的序列化结果)"""
state_id = serializers.IntegerField(source='state.id', read_only=True) state_id = serializers.IntegerField(source='state.id', read_only=True)
state_name = serializers.CharField(source='state.name', read_only=True) state_name = serializers.CharField(source='state.name', read_only=True)
@@ -440,6 +440,7 @@ class PrintingJobBatchAdvanceRecordSerializer(serializers.ModelSerializer):
'created_by_username', 'created_by_username',
'created_by_name', 'created_by_name',
'parameters', 'parameters',
'only_parameters',
'created_at', 'created_at',
] ]
read_only_fields = fields read_only_fields = fields

View File

@@ -1654,3 +1654,226 @@ class PrintingOrderBatchAdvanceRecordsAPITest(TestCase):
self.client.logout() self.client.logout()
response = self.client.get(f'/api/v2/printing-orders/{self.printing_order.id}/batch-advance-records/') response = self.client.get(f'/api/v2/printing-orders/{self.printing_order.id}/batch-advance-records/')
self.assertEqual(response.status_code, 401) self.assertEqual(response.status_code, 401)
class PrintingJobBatchAddParametersAPITest(TestCase):
"""测试批量补充工艺参数 API"""
def setUp(self):
self.client = APIClient()
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,
name='工厂员工',
sys_user=self.user
)
self.client.force_authenticate(user=self.user)
# 创建客户
self.customer = basic_models.Customer.objects.create(
merchant=self.merchant,
name='测试客户'
)
# 创建产品类别和产品
self.category = basic_models.ProductCategory.objects.create(
merchant=self.merchant,
name='测试类别'
)
self.product = basic_models.Product.objects.create(
merchant=self.merchant,
name='测试产品',
category=self.category,
unit=basic_models.ProductUnitEnum.METER
)
# 创建流程状态
self.state1 = stateflow_models.State.objects.create(name='染色', description='染色工序')
self.state2 = stateflow_models.State.objects.create(name='整理', description='整理工序')
# 创建流程
self.process = stateflow_models.Process.objects.create(name='印染流程')
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state1, order=1)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state2, order=2)
# 创建印染订单
self.printing_order = printing_models.PrintingOrder.objects.create(
customer=self.customer,
fabric='测试面料',
width='150cm',
process=self.process
)
# 创建印染明细并绑定流程实例
self.job1 = printing_models.PrintingJob.objects.create(
printing_order=self.printing_order,
product=self.product,
quantity=100,
unit=''
)
self.job2 = printing_models.PrintingJob.objects.create(
printing_order=self.printing_order,
product=self.product,
quantity=200,
unit=''
)
# 创建业务对象并推进到 state1
from stateflow import services as stateflow_services
from django.contrib.contenttypes.models import ContentType
ct = ContentType.objects.get_for_model(printing_models.PrintingJob)
self.bo1 = stateflow_models.BusinessObject.objects.create(
name=f'PrintingJob-{self.job1.id}',
process=self.process,
content_type=ct,
object_id=self.job1.id
)
self.job1.business_object = self.bo1
self.job1.save()
self.bo2 = stateflow_models.BusinessObject.objects.create(
name=f'PrintingJob-{self.job2.id}',
process=self.process,
content_type=ct,
object_id=self.job2.id
)
self.job2.business_object = self.bo2
self.job2.save()
# 推进到 state1创建 state_log
stateflow_services.advance_to_next_state(self.bo1, self.user, temperature='25.0')
stateflow_services.advance_to_next_state(self.bo2, self.user, temperature='25.0')
def test_batch_add_parameters_success(self):
"""测试成功批量补充参数"""
response = self.client.post(
'/api/v2/printing-jobs/batch-add-parameters/',
{
'printing_job_ids': [self.job1.id, self.job2.id],
'state_id': self.state1.id,
'parameters': {'temperature': '26.0', 'operator': '李四'},
'remark': '补测数据'
},
format='json'
)
self.assertEqual(response.status_code, 200)
data = response.data
self.assertEqual(data['detail'], '批量补充参数成功')
self.assertEqual(data['printing_order_id'], self.printing_order.id)
self.assertEqual(set(data['printing_job_ids']), {self.job1.id, self.job2.id})
self.assertEqual(data['state_id'], self.state1.id)
self.assertEqual(data['state_name'], '染色')
self.assertEqual(data['affected_count'], 2)
# 验证创建了批量操作记录
record = printing_models.PrintingJobBatchAdvanceRecord.objects.get(id=data['batch_record_id'])
self.assertTrue(record.only_parameters)
self.assertEqual(record.parameters, {'temperature': '26.0', 'operator': '李四'})
self.assertEqual(record.printing_jobs.count(), 2)
def test_batch_add_parameters_empty_parameters(self):
"""测试参数为空时返回错误"""
response = self.client.post(
'/api/v2/printing-jobs/batch-add-parameters/',
{
'printing_job_ids': [self.job1.id],
'state_id': self.state1.id,
'parameters': {},
},
format='json'
)
self.assertEqual(response.status_code, 400)
def test_batch_add_parameters_job_not_found(self):
"""测试 job 不存在时返回错误"""
response = self.client.post(
'/api/v2/printing-jobs/batch-add-parameters/',
{
'printing_job_ids': [99999],
'state_id': self.state1.id,
'parameters': {'temperature': '26.0'},
},
format='json'
)
self.assertEqual(response.status_code, 400)
self.assertIn('missing_jobs', response.data)
def test_batch_add_parameters_state_not_found(self):
"""测试 state 不存在时返回错误"""
response = self.client.post(
'/api/v2/printing-jobs/batch-add-parameters/',
{
'printing_job_ids': [self.job1.id],
'state_id': 99999,
'parameters': {'temperature': '26.0'},
},
format='json'
)
self.assertEqual(response.status_code, 400)
def test_batch_add_parameters_missing_state_log(self):
"""测试 job 没有对应 state_log 时返回错误"""
# state2 的 state_log 尚未创建(还没推进到那一步)
response = self.client.post(
'/api/v2/printing-jobs/batch-add-parameters/',
{
'printing_job_ids': [self.job1.id],
'state_id': self.state2.id, # 还没推进到这个状态
'parameters': {'temperature': '26.0'},
},
format='json'
)
self.assertEqual(response.status_code, 400)
self.assertIn('missing_state_log', response.data)
def test_batch_add_parameters_different_orders(self):
"""测试 jobs 属于不同订单时返回错误"""
# 创建另一个订单和 job
another_order = printing_models.PrintingOrder.objects.create(
customer=self.customer,
fabric='另一面料',
width='160cm'
)
another_job = printing_models.PrintingJob.objects.create(
printing_order=another_order,
product=self.product,
quantity=300,
unit=''
)
response = self.client.post(
'/api/v2/printing-jobs/batch-add-parameters/',
{
'printing_job_ids': [self.job1.id, another_job.id],
'state_id': self.state1.id,
'parameters': {'temperature': '26.0'},
},
format='json'
)
self.assertEqual(response.status_code, 400)
def test_batch_add_parameters_unauthenticated(self):
"""测试未登录时返回 401"""
self.client.logout()
response = self.client.post(
'/api/v2/printing-jobs/batch-add-parameters/',
{
'printing_job_ids': [self.job1.id],
'state_id': self.state1.id,
'parameters': {'temperature': '26.0'},
},
format='json'
)
self.assertEqual(response.status_code, 401)

View File

@@ -7,6 +7,7 @@ from api_v2.views import (
PrintingJobByCustomerView, PrintingJobByCustomerView,
PrintingJobBatchAdvancePreviewView, PrintingJobBatchAdvancePreviewView,
PrintingJobBatchAdvanceSubmitView, PrintingJobBatchAdvanceSubmitView,
PrintingJobBatchAddParametersView,
PlateOrderByProcessNodeView, PlateOrderByProcessNodeView,
PlateOrderByProcessView, PlateOrderByProcessView,
PlateOrderByStateStatusView, PlateOrderByStateStatusView,
@@ -25,6 +26,7 @@ urlpatterns = [
path('printing-jobs/by-customer/', PrintingJobByCustomerView.as_view(), name='api_v2_printing_job_by_customer'), path('printing-jobs/by-customer/', PrintingJobByCustomerView.as_view(), name='api_v2_printing_job_by_customer'),
path('printing-jobs/batch-advance/preview/', PrintingJobBatchAdvancePreviewView.as_view(), name='api_v2_printing_job_batch_advance_preview'), path('printing-jobs/batch-advance/preview/', PrintingJobBatchAdvancePreviewView.as_view(), name='api_v2_printing_job_batch_advance_preview'),
path('printing-jobs/batch-advance/', PrintingJobBatchAdvanceSubmitView.as_view(), name='api_v2_printing_job_batch_advance_submit'), path('printing-jobs/batch-advance/', PrintingJobBatchAdvanceSubmitView.as_view(), name='api_v2_printing_job_batch_advance_submit'),
path('printing-jobs/batch-add-parameters/', PrintingJobBatchAddParametersView.as_view(), name='api_v2_printing_job_batch_add_parameters'),
path('printing-orders/<int:printing_order_id>/batch-advance-records/', PrintingOrderBatchAdvanceRecordsView.as_view(), name='api_v2_printing_order_batch_advance_records'), path('printing-orders/<int:printing_order_id>/batch-advance-records/', PrintingOrderBatchAdvanceRecordsView.as_view(), name='api_v2_printing_order_batch_advance_records'),
path('plate-orders/by-process-node/', PlateOrderByProcessNodeView.as_view(), name='api_v2_plate_order_by_process_node'), path('plate-orders/by-process-node/', PlateOrderByProcessNodeView.as_view(), name='api_v2_plate_order_by_process_node'),
path('plate-orders/by-process/', PlateOrderByProcessView.as_view(), name='api_v2_plate_order_by_process'), path('plate-orders/by-process/', PlateOrderByProcessView.as_view(), name='api_v2_plate_order_by_process'),

View File

@@ -9,6 +9,7 @@ from .printing import (
PrintingJobV2Serializer, PrintingJobV2Serializer,
PrintingJobBatchAdvancePreviewView, PrintingJobBatchAdvancePreviewView,
PrintingJobBatchAdvanceSubmitView, PrintingJobBatchAdvanceSubmitView,
PrintingJobBatchAddParametersView,
PlateOrderByProcessNodeView, PlateOrderByProcessNodeView,
PlateOrderByProcessView, PlateOrderByProcessView,
PlateOrderByStateStatusView, PlateOrderByStateStatusView,
@@ -25,6 +26,7 @@ __all__ = [
'PrintingJobV2Serializer', 'PrintingJobV2Serializer',
'PrintingJobBatchAdvancePreviewView', 'PrintingJobBatchAdvancePreviewView',
'PrintingJobBatchAdvanceSubmitView', 'PrintingJobBatchAdvanceSubmitView',
'PrintingJobBatchAddParametersView',
'PlateOrderByProcessNodeView', 'PlateOrderByProcessNodeView',
'PlateOrderByProcessView', 'PlateOrderByProcessView',
'PlateOrderByStateStatusView', 'PlateOrderByStateStatusView',

View File

@@ -1348,7 +1348,7 @@ class PlateOrderBatchUpdateView(APIView):
class BatchAdvanceRecordSerializer(serializers.ModelSerializer): class BatchAdvanceRecordSerializer(serializers.ModelSerializer):
"""批量推进记录序列化器""" """批量操作记录序列化器"""
state_id = serializers.IntegerField(source='state.id', read_only=True) state_id = serializers.IntegerField(source='state.id', read_only=True)
state_name = serializers.CharField(source='state.name', read_only=True) state_name = serializers.CharField(source='state.name', read_only=True)
@@ -1369,6 +1369,7 @@ class BatchAdvanceRecordSerializer(serializers.ModelSerializer):
'created_by_username', 'created_by_username',
'created_by_name', 'created_by_name',
'parameters', 'parameters',
'only_parameters',
'printing_job_ids', 'printing_job_ids',
'printing_job_count', 'printing_job_count',
'created_at', 'created_at',
@@ -1458,3 +1459,200 @@ class PrintingOrderBatchAdvanceRecordsView(APIView):
'records': serializer.data, 'records': serializer.data,
'total_count': records.count(), 'total_count': records.count(),
}) })
class PrintingJobBatchAddParametersRequestSerializer(serializers.Serializer):
"""批量补充参数请求序列化器"""
printing_job_ids = serializers.ListField(
child=serializers.IntegerField(),
min_length=1,
help_text='印染明细ID列表'
)
state_id = serializers.IntegerField(help_text='目标流程节点ID')
parameters = serializers.DictField(
child=serializers.CharField(allow_blank=True),
help_text='要补充的工艺参数'
)
remark = serializers.CharField(required=False, default='', allow_blank=True, help_text='备注说明')
def _validate_jobs_for_batch_add_parameters(printing_job_ids: list[int], state_id: int) -> dict:
"""
验证批量补充参数的 jobs 并返回相关数据
验证规则:
1. 所有 job 必须存在
2. 所有 job 必须属于同一个 printing_order
3. 所有 job 必须有 business_object
4. 所有 job 的 business_object 必须有对应 state_id 的 state_log未撤销
返回:
{
'jobs': QuerySet[PrintingJob],
'printing_order': PrintingOrder,
'printing_order_id': int,
'state': State,
'state_logs': dict[int, StateFlowRecord], # job_id -> state_log
}
"""
from stateflow import models as stateflow_models
jobs = (
printing_models.PrintingJob.objects
.select_related('printing_order', 'business_object')
.filter(id__in=printing_job_ids)
)
found_ids = set(jobs.values_list('id', flat=True))
missing_ids = [i for i in printing_job_ids if i not in found_ids]
if missing_ids:
raise serializers.ValidationError({
'detail': f'以下 PrintingJob 不存在: {missing_ids}',
'missing_jobs': missing_ids,
})
# 验证同一订单
order_ids = set(jobs.values_list('printing_order_id', flat=True))
if len(order_ids) != 1:
raise serializers.ValidationError({
'detail': '所有 PrintingJob 必须属于同一个 PrintingOrder',
})
printing_order_id = order_ids.pop()
printing_order = printing_models.PrintingOrder.objects.get(id=printing_order_id)
# 验证 state 存在
try:
state = stateflow_models.State.objects.get(id=state_id)
except stateflow_models.State.DoesNotExist:
raise serializers.ValidationError({
'detail': f'State ID {state_id} 不存在',
})
# 验证所有 job 有 business_object 和对应的 state_log
jobs_without_bo = []
jobs_without_state_log = []
state_logs = {} # job_id -> state_log
for job in jobs:
if not job.business_object:
jobs_without_bo.append(job.id)
continue
# 查找对应 state_id 的 state_log未撤销
state_log = job.business_object.state_logs.filter(
state_id=state_id,
is_cancelled=False
).first()
if not state_log:
jobs_without_state_log.append(job.id)
else:
state_logs[job.id] = state_log
if jobs_without_bo:
raise serializers.ValidationError({
'detail': f'以下 PrintingJob 没有流程实例 (business_object): {jobs_without_bo}',
'missing_business_object': jobs_without_bo,
})
if jobs_without_state_log:
raise serializers.ValidationError({
'detail': f'以下 PrintingJob 没有对应 state_id={state_id} 的状态流转记录(或已撤销): {jobs_without_state_log}',
'missing_state_log': jobs_without_state_log,
})
return {
'jobs': jobs,
'printing_order': printing_order,
'printing_order_id': printing_order_id,
'state': state,
'state_logs': state_logs,
}
class PrintingJobBatchAddParametersView(APIView):
"""
批量补充工艺参数
为多个 PrintingJob 的已完成状态流转记录批量补充工艺参数。
POST /api/v2/printing-jobs/batch-add-parameters/
请求体:
{
"printing_job_ids": [101, 102, 103],
"state_id": 7,
"parameters": {
"temperature": "26.0",
"operator": "李四"
},
"remark": "批量补充参数备注"
}
响应:
{
"detail": "批量补充参数成功",
"batch_record_id": 15,
"printing_order_id": 55,
"printing_job_ids": [101, 102, 103],
"state_id": 7,
"state_name": "染色",
"parameters": {...},
"affected_count": 3
}
"""
permission_classes = [permissions.IsAuthenticated, IsPrintingFactory]
def post(self, request):
srz = PrintingJobBatchAddParametersRequestSerializer(data=request.data)
srz.is_valid(raise_exception=True)
payload = srz.validated_data
printing_job_ids = payload['printing_job_ids']
state_id = payload['state_id']
parameters = payload['parameters']
remark = payload.get('remark', '')
if not parameters:
raise serializers.ValidationError({
'detail': '参数不能为空',
})
# 验证数据
data = _validate_jobs_for_batch_add_parameters(printing_job_ids, state_id)
from stateflow import services as stateflow_services
# 全成功/全失败
with transaction.atomic():
# 创建批量操作记录
record = printing_models.PrintingJobBatchAdvanceRecord.objects.create(
printing_order=data['printing_order'],
state=data['state'],
created_by=request.user,
parameters=parameters,
only_parameters=True, # 标记为仅补充参数
)
record.printing_jobs.set(data['jobs'])
# 逐个补充参数
for job in data['jobs']:
state_log = data['state_logs'][job.id]
stateflow_services.add_parameters_to_state_log(
state_log,
remark=remark,
**parameters
)
return Response({
'detail': '批量补充参数成功',
'batch_record_id': record.id,
'printing_order_id': data['printing_order_id'],
'printing_job_ids': [j.id for j in data['jobs']],
'state_id': state_id,
'state_name': data['state'].name,
'parameters': parameters,
'affected_count': len(data['jobs']),
})

View File

@@ -0,0 +1,235 @@
# 批量补充工艺参数 API
## 概述
为多个 `PrintingJob` 的已完成状态流转记录批量补充工艺参数。
- **端点**: `POST /api/v2/printing-jobs/batch-add-parameters/`
- **认证**: 需要登录
- **权限**: 印染工厂用户
## 与批量推进的区别
| 维度 | batch-advance | batch-add-parameters |
|------|---------------|----------------------|
| **目的** | 推进流程状态 | 补充已完成状态的参数 |
| **前置条件** | jobs 处于同一待推进状态 | jobs 已完成指定状态 |
| **核心操作** | `advance_to_next_state()` | `add_parameters_to_state_log()` |
| **审计记录** | `only_parameters=False` | `only_parameters=True` |
## 请求
### 请求体
```json
{
"printing_job_ids": [101, 102, 103],
"state_id": 7,
"parameters": {
"temperature": "26.0",
"operator": "李四",
"remark_field": "补测数据"
},
"remark": "批量补充参数备注"
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `printing_job_ids` | array[int] | 是 | 印染明细ID列表 |
| `state_id` | int | 是 | 目标流程节点IDState.id |
| `parameters` | object | 是 | 要补充的工艺参数(不能为空) |
| `remark` | string | 否 | 备注说明 |
### 请求示例
```bash
curl -X POST "http://localhost:8000/api/v2/printing-jobs/batch-add-parameters/" \
-H "Authorization: Token <your-token>" \
-H "Content-Type: application/json" \
-d '{
"printing_job_ids": [101, 102, 103],
"state_id": 7,
"parameters": {"temperature": "26.0", "operator": "李四"},
"remark": "补测数据"
}'
```
## 响应
### 成功响应 (200 OK)
```json
{
"detail": "批量补充参数成功",
"batch_record_id": 15,
"printing_order_id": 55,
"printing_job_ids": [101, 102, 103],
"state_id": 7,
"state_name": "染色",
"parameters": {
"temperature": "26.0",
"operator": "李四"
},
"affected_count": 3
}
```
### 响应字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `detail` | string | 操作结果消息 |
| `batch_record_id` | int | 批量操作记录ID |
| `printing_order_id` | int | 印染订单ID |
| `printing_job_ids` | array[int] | 受影响的印染明细ID列表 |
| `state_id` | int | 目标流程节点ID |
| `state_name` | string | 流程节点名称 |
| `parameters` | object | 补充的工艺参数 |
| `affected_count` | int | 受影响的明细数量 |
### 错误响应
#### 参数为空 (400 Bad Request)
```json
{
"detail": "参数不能为空"
}
```
#### Job 不存在 (400 Bad Request)
```json
{
"detail": "以下 PrintingJob 不存在: [999]",
"missing_jobs": [999]
}
```
#### Jobs 属于不同订单 (400 Bad Request)
```json
{
"detail": "所有 PrintingJob 必须属于同一个 PrintingOrder"
}
```
#### Job 没有流程实例 (400 Bad Request)
```json
{
"detail": "以下 PrintingJob 没有流程实例 (business_object): [101]",
"missing_business_object": [101]
}
```
#### Job 没有对应状态记录 (400 Bad Request)
```json
{
"detail": "以下 PrintingJob 没有对应 state_id=7 的状态流转记录(或已撤销): [103]",
"missing_state_log": [103]
}
```
#### State 不存在 (400 Bad Request)
```json
{
"detail": "State ID 999 不存在"
}
```
#### 未认证 (401 Unauthorized)
```json
{
"detail": "Authentication credentials were not provided."
}
```
## 验证规则
| 规则 | 说明 |
|------|------|
| ① 所有 job 必须存在 | 返回 `missing_jobs` |
| ② 所有 job 必须属于同一个 `printing_order` | 与 batch_advance 一致 |
| ③ 所有 job 必须有 `business_object` | 返回 `missing_business_object` |
| ④ 所有 job 必须有对应 `state_id` 的未撤销状态记录 | 返回 `missing_state_log` |
| ⑤ `parameters` 不能为空 | 与单条 add-parameters 一致 |
## 事务策略
- **全成功/全失败**:任意一个 job 验证失败 → 整体失败,不写入任何数据
- 使用 `transaction.atomic()` 包裹
## 审计记录
操作成功后会创建 `PrintingJobBatchAdvanceRecord` 记录:
| 字段 | 值 |
|------|-----|
| `printing_order` | 印染订单 |
| `state` | 操作的流程节点 |
| `parameters` | 补充的参数 |
| `printing_jobs` | 涉及的明细 |
| `created_by` | 操作人 |
| `only_parameters` | **`True`** |
可通过 `GET /api/v2/printing-orders/{id}/batch-advance-records/` 查询所有批量操作记录。
## 前端使用示例
```javascript
// 批量补充参数
async function batchAddParameters(printingJobIds, stateId, parameters, remark = '') {
const response = await fetch('/api/v2/printing-jobs/batch-add-parameters/', {
method: 'POST',
headers: {
'Authorization': `Token ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
printing_job_ids: printingJobIds,
state_id: stateId,
parameters: parameters,
remark: remark
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail);
}
return await response.json();
}
// 使用示例
try {
const result = await batchAddParameters(
[101, 102, 103],
7,
{ temperature: '26.0', operator: '李四' },
'补测数据'
);
console.log(`成功补充 ${result.affected_count} 条明细的参数`);
} catch (error) {
console.error('补充参数失败:', error.message);
}
```
## 相关文档
- [批量推进 API](./api_v2_advance_printing_jobs_api.md)
- [批量操作记录查询](./api_v2_printing_order_batch_advance_records.md)
- [单条补充参数 API](./batch_submit_state_params.md)
## 相关模型
- `printing.PrintingJobBatchAdvanceRecord` - 批量操作记录(`only_parameters=True`
- `printing.PrintingJob` - 印染明细
- `stateflow.StateFlowRecord` - 状态流转记录
- `stateflow.StateLogParameterRecord` - 参数记录

View File

@@ -11,15 +11,18 @@
### 返回字段结构batch_advance_records ### 返回字段结构batch_advance_records
每条记录PrintingJobBatchAdvanceRecord包含 每条记录PrintingJobBatchAdvanceRecord包含
- `id`: 批量推进记录 ID - `id`: 批量操作记录 ID
- `printing_order`: 主订单 ID - `printing_order`: 主订单 ID
- `state`: 本次批量推进“完成”的流程节点 ID(即推进时的 next_pending_state - `state`: 本次批量操作的流程节点 ID
- `state_id`: 同 `state`(便于前端直接取用) - `state_id`: 同 `state`(便于前端直接取用)
- `state_name`: 节点名称 - `state_name`: 节点名称
- `created_by`: 操作人用户 ID可能为 null - `created_by`: 操作人用户 ID可能为 null
- `created_by_username`: 操作人用户名(可能为 null - `created_by_username`: 操作人用户名(可能为 null
- `created_by_name`: 操作人员工姓名(若用户有 employee则返回 employee.name否则 null - `created_by_name`: 操作人员工姓名(若用户有 employee则返回 employee.name否则 null
- `parameters`: 本次批量推进提交的参数 JSON(与推进接口 `parameters` 完全一致) - `parameters`: 本次批量操作提交的参数 JSON
- `only_parameters`: 操作类型标识
- `false`(默认):批量推进状态
- `true`:仅批量补充工艺参数(不推进状态)
- `created_at`: 记录创建时间ISO8601 - `created_at`: 记录创建时间ISO8601
### 示例(无记录) ### 示例(无记录)
@@ -56,6 +59,7 @@
"temperature": "25.5", "temperature": "25.5",
"operator": "张三" "operator": "张三"
}, },
"only_parameters": false,
"created_at": "2025-12-14T10:00:00+08:00" "created_at": "2025-12-14T10:00:00+08:00"
} }
] ]

View File

@@ -0,0 +1,34 @@
# Generated by Django 5.2.8 on 2026-01-07 08:20
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('printing', '0027_add_created_by_to_printingorder_and_printingjob'),
('stateflow', '0021_statelogparameterrecord'),
]
operations = [
migrations.AlterModelOptions(
name='printingjobbatchadvancerecord',
options={'verbose_name': '印染任务批量操作记录', 'verbose_name_plural': '印染任务批量操作记录'},
),
migrations.AddField(
model_name='printingjobbatchadvancerecord',
name='only_parameters',
field=models.BooleanField(default=False, help_text='True=仅补充参数不推进状态False=批量推进状态', verbose_name='仅参数补充'),
),
migrations.AlterField(
model_name='printingjobbatchadvancerecord',
name='parameters',
field=models.JSONField(blank=True, default=dict, help_text='批量操作时提交的参数', verbose_name='工艺参数'),
),
migrations.AlterField(
model_name='printingjobbatchadvancerecord',
name='state',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='printing_job_batch_advance_records', to='stateflow.state', verbose_name='操作的流程节点'),
),
]

View File

@@ -465,13 +465,16 @@ class PrintingJob(ModelBase):
class PrintingJobBatchAdvanceRecord(ModelBase): class PrintingJobBatchAdvanceRecord(ModelBase):
""" """
印染任务批量推进记录(审计) 印染任务批量操作记录(审计)
记录一次对多个 PrintingJob 用同一组参数推进同一步流程节点的行为: 记录一次"对多个 PrintingJob 用同一组参数操作同一步流程节点"的行为:
- created_by/created_at谁在何时做了批量推进 - created_by/created_at谁在何时做了批量操作
- parameters本次推进提交的工艺参数JSON - parameters本次操作提交的工艺参数JSON
- state本次被“完成”的流程节点(即推进时的 next_pending_state - state本次操作的流程节点
- printing_jobs本次批量推进涉及的所有明细 - printing_jobs本次批量操作涉及的所有明细
- only_parameters操作类型
- False默认批量推进状态
- True仅批量补充工艺参数不推进状态
""" """
printing_order = models.ForeignKey( printing_order = models.ForeignKey(
@@ -484,7 +487,7 @@ class PrintingJobBatchAdvanceRecord(ModelBase):
stateflow_models.State, stateflow_models.State,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='printing_job_batch_advance_records', related_name='printing_job_batch_advance_records',
verbose_name='完成的流程节点', verbose_name='操作的流程节点',
) )
created_by = models.ForeignKey( created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
@@ -498,7 +501,7 @@ class PrintingJobBatchAdvanceRecord(ModelBase):
default=dict, default=dict,
blank=True, blank=True,
verbose_name='工艺参数', verbose_name='工艺参数',
help_text='批量推进时提交的参数(与单条推进一致)', help_text='批量操作时提交的参数',
) )
printing_jobs = models.ManyToManyField( printing_jobs = models.ManyToManyField(
PrintingJob, PrintingJob,
@@ -506,7 +509,12 @@ class PrintingJobBatchAdvanceRecord(ModelBase):
verbose_name='印染明细', verbose_name='印染明细',
blank=False, blank=False,
) )
only_parameters = models.BooleanField(
default=False,
verbose_name='仅参数补充',
help_text='True=仅补充参数不推进状态False=批量推进状态',
)
class Meta: class Meta:
verbose_name = '印染任务批量推进记录' verbose_name = '印染任务批量操作记录'
verbose_name_plural = '印染任务批量推进记录' verbose_name_plural = '印染任务批量操作记录'