forked from erp-dev/erp
feat: added remark field into shipment module
This commit is contained in:
@@ -92,6 +92,7 @@ class ExternalFinishedProductSerializer(serializers.ModelSerializer):
|
|||||||
'id',
|
'id',
|
||||||
'style_name',
|
'style_name',
|
||||||
'num_of_rolls',
|
'num_of_rolls',
|
||||||
|
'remark',
|
||||||
'created_at',
|
'created_at',
|
||||||
'created_by_id',
|
'created_by_id',
|
||||||
'created_by_name',
|
'created_by_name',
|
||||||
@@ -129,6 +130,7 @@ class ExternalFinishedProductInputSerializer(serializers.Serializer):
|
|||||||
"""外部成品表写入结构(external create 专用)"""
|
"""外部成品表写入结构(external create 专用)"""
|
||||||
style_name = serializers.CharField(max_length=200)
|
style_name = serializers.CharField(max_length=200)
|
||||||
num_of_rolls = serializers.IntegerField(min_value=0)
|
num_of_rolls = serializers.IntegerField(min_value=0)
|
||||||
|
remark = serializers.CharField(max_length=200, required=False, allow_blank=True, allow_null=True, default='')
|
||||||
|
|
||||||
|
|
||||||
class ShipmentCreateExternalSerializer(serializers.Serializer):
|
class ShipmentCreateExternalSerializer(serializers.Serializer):
|
||||||
|
|||||||
@@ -491,7 +491,7 @@ class ShipmentExternalCreateAPITestCase(TestCase):
|
|||||||
'remark': 'external 备注',
|
'remark': 'external 备注',
|
||||||
'external_id': 'EXT-ORDER-001',
|
'external_id': 'EXT-ORDER-001',
|
||||||
'external_finished_products': [
|
'external_finished_products': [
|
||||||
{'style_name': '款式A', 'num_of_rolls': 2},
|
{'style_name': '款式A', 'num_of_rolls': 2, 'remark': 'A备注'},
|
||||||
{'style_name': '款式B', 'num_of_rolls': 5},
|
{'style_name': '款式B', 'num_of_rolls': 5},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -512,6 +512,9 @@ class ShipmentExternalCreateAPITestCase(TestCase):
|
|||||||
shipment_models.ExternalFinishedProduct.objects.filter(shipment_id=shipment_id).count(),
|
shipment_models.ExternalFinishedProduct.objects.filter(shipment_id=shipment_id).count(),
|
||||||
2
|
2
|
||||||
)
|
)
|
||||||
|
efp_a = shipment_models.ExternalFinishedProduct.objects.filter(shipment_id=shipment_id, style_name='款式A').first()
|
||||||
|
self.assertIsNotNone(efp_a)
|
||||||
|
self.assertEqual(getattr(efp_a, 'remark', None) or '', 'A备注')
|
||||||
|
|
||||||
# 验证不会绑定任何销售品
|
# 验证不会绑定任何销售品
|
||||||
self.sales_item.refresh_from_db()
|
self.sales_item.refresh_from_db()
|
||||||
@@ -655,6 +658,29 @@ class ShipmentQueryAPITestCase(TestCase):
|
|||||||
self.assertIn('external_finished_products', result)
|
self.assertIn('external_finished_products', result)
|
||||||
self.assertIsInstance(result['external_finished_products'], list)
|
self.assertIsInstance(result['external_finished_products'], list)
|
||||||
|
|
||||||
|
def test_list_shipments_includes_external_finished_product_remark(self):
|
||||||
|
"""
|
||||||
|
Shipments list 需要附带 external_finished_products 明细数据(包含 remark 字段)。
|
||||||
|
"""
|
||||||
|
# 给 shipment1 挂一个外部成品表
|
||||||
|
shipment_models.ExternalFinishedProduct.objects.create(
|
||||||
|
shipment=self.shipment1,
|
||||||
|
style_name='款式X',
|
||||||
|
num_of_rolls=1,
|
||||||
|
remark='X备注',
|
||||||
|
created_by=self.user1,
|
||||||
|
)
|
||||||
|
|
||||||
|
resp = self.client.get('/api/v1/shipment/shipments/')
|
||||||
|
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
||||||
|
data = resp.json()
|
||||||
|
item = next(it for it in data['results'] if it['id'] == self.shipment1.id)
|
||||||
|
efps = item['external_finished_products']
|
||||||
|
self.assertIsInstance(efps, list)
|
||||||
|
target = next(p for p in efps if p['style_name'] == '款式X')
|
||||||
|
self.assertIn('remark', target)
|
||||||
|
self.assertEqual(target['remark'], 'X备注')
|
||||||
|
|
||||||
def test_retrieve_other_merchant_shipment_404(self):
|
def test_retrieve_other_merchant_shipment_404(self):
|
||||||
resp = self.client.get(f'/api/v1/shipment/shipments/{self.shipment2.id}/')
|
resp = self.client.get(f'/api/v1/shipment/shipments/{self.shipment2.id}/')
|
||||||
self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND)
|
self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND)
|
||||||
|
|||||||
@@ -41,5 +41,6 @@
|
|||||||
- `GET /api/v1/shipment/shipments/`:出货单列表(merchant 隔离 + LimitOffsetPagination)
|
- `GET /api/v1/shipment/shipments/`:出货单列表(merchant 隔离 + LimitOffsetPagination)
|
||||||
- `GET /api/v1/shipment/shipments/<id>/`:出货单详情(merchant 隔离)
|
- `GET /api/v1/shipment/shipments/<id>/`:出货单详情(merchant 隔离)
|
||||||
- 出货单列表序列化器增强:`sales_items` 与 `external_finished_products` 字段稳定输出(无数据也返回空数组)
|
- 出货单列表序列化器增强:`sales_items` 与 `external_finished_products` 字段稳定输出(无数据也返回空数组)
|
||||||
|
- ExternalFinishedProduct 增加 `remark` 字段(可空),并同步更新相关序列化器/文档/测试(包含 shipments list 附带数据)
|
||||||
- 更新 `docs/shipment_api.md`:补充“查询出货单(列表/详情)”说明
|
- 更新 `docs/shipment_api.md`:补充“查询出货单(列表/详情)”说明
|
||||||
- 新增 API 测试:覆盖列表/详情的商户隔离与未登录行为
|
- 新增 API 测试:覆盖列表/详情的商户隔离与未登录行为
|
||||||
|
|||||||
@@ -190,6 +190,7 @@
|
|||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| style_name | string | 是 | 款式名称 |
|
| style_name | string | 是 | 款式名称 |
|
||||||
| num_of_rolls | int | 是 | 卷数 |
|
| num_of_rolls | int | 是 | 卷数 |
|
||||||
|
| remark | string | 否 | 备注(可空) |
|
||||||
|
|
||||||
### 请求示例
|
### 请求示例
|
||||||
|
|
||||||
@@ -200,7 +201,7 @@
|
|||||||
"remark": "external 备注(可选)",
|
"remark": "external 备注(可选)",
|
||||||
"external_id": "EXT-ORDER-001",
|
"external_id": "EXT-ORDER-001",
|
||||||
"external_finished_products": [
|
"external_finished_products": [
|
||||||
{"style_name": "款式A", "num_of_rolls": 2},
|
{"style_name": "款式A", "num_of_rolls": 2, "remark": "A备注"},
|
||||||
{"style_name": "款式B", "num_of_rolls": 5}
|
{"style_name": "款式B", "num_of_rolls": 5}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.8 on 2026-01-15 12:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('shipment', '0006_add_merchant_to_shipment_and_salesitem'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='externalfinishedproduct',
|
||||||
|
name='remark',
|
||||||
|
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='备注'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -41,6 +41,12 @@ class ExternalFinishedProduct(ModelBase):
|
|||||||
num_of_rolls = models.PositiveIntegerField(
|
num_of_rolls = models.PositiveIntegerField(
|
||||||
verbose_name='卷数',
|
verbose_name='卷数',
|
||||||
)
|
)
|
||||||
|
remark = models.CharField(
|
||||||
|
max_length=200,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name='备注',
|
||||||
|
)
|
||||||
created_by = models.ForeignKey(
|
created_by = models.ForeignKey(
|
||||||
User,
|
User,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ def create_external_shipment(
|
|||||||
shipment=shipment,
|
shipment=shipment,
|
||||||
style_name=item.get('style_name', ''),
|
style_name=item.get('style_name', ''),
|
||||||
num_of_rolls=item.get('num_of_rolls', 0),
|
num_of_rolls=item.get('num_of_rolls', 0),
|
||||||
|
remark=item.get('remark') or '',
|
||||||
created_by=created_by,
|
created_by=created_by,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user