修复:收紧托盘业务包数校验
This commit is contained in:
@@ -309,7 +309,7 @@ class PalletModel(BaseModel):
|
|||||||
pallet_id: int = Field(gt=0, description='托盘ID')
|
pallet_id: int = Field(gt=0, description='托盘ID')
|
||||||
pallet_code: str = Field(min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
|
pallet_code: str = Field(min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
|
||||||
qr_token: str = Field(min_length=1, max_length=128, pattern=r'\S', description='二维码固定标识')
|
qr_token: str = Field(min_length=1, max_length=128, pattern=r'\S', description='二维码固定标识')
|
||||||
capacity_packages: int = Field(gt=0, description='容量包数')
|
capacity_packages: int = Field(strict=True, gt=0, description='容量包数')
|
||||||
asset_status: Literal['0', '1'] = Field(description='资产状态(0正常 1停用)')
|
asset_status: Literal['0', '1'] = Field(description='资产状态(0正常 1停用)')
|
||||||
remark: str | None = Field(default=None, max_length=500, description='备注')
|
remark: str | None = Field(default=None, max_length=500, description='备注')
|
||||||
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ class FillingModel(BaseModel):
|
|||||||
strain_name_snapshot: str = Field(min_length=1, max_length=100, pattern=r'\S', description='菌种名称快照')
|
strain_name_snapshot: str = Field(min_length=1, max_length=100, pattern=r'\S', description='菌种名称快照')
|
||||||
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
||||||
package_spec: str = Field(min_length=1, max_length=64, pattern=r'\S', description='包装规格')
|
package_spec: str = Field(min_length=1, max_length=64, pattern=r'\S', description='包装规格')
|
||||||
initial_packages: int = Field(ge=0, description='初始包数')
|
initial_packages: int = Field(strict=True, gt=0, description='初始包数')
|
||||||
active_slot: Literal['ACTIVE'] | None = Field(default=None, description='活动装填槽')
|
active_slot: Literal['ACTIVE'] | None = Field(default=None, description='活动装填槽')
|
||||||
closed_at: datetime | None = Field(default=None, description='装填周期关闭时间')
|
closed_at: datetime | None = Field(default=None, description='装填周期关闭时间')
|
||||||
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
|
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
|
||||||
@@ -236,7 +236,7 @@ class ShipmentItemModel(BaseModel):
|
|||||||
shipment_id: int = Field(gt=0, description='发货单ID')
|
shipment_id: int = Field(gt=0, description='发货单ID')
|
||||||
pallet_id: int = Field(gt=0, description='托盘ID')
|
pallet_id: int = Field(gt=0, description='托盘ID')
|
||||||
active_filling_id: int | None = Field(default=None, gt=0, description='确认时活动装填记录ID')
|
active_filling_id: int | None = Field(default=None, gt=0, description='确认时活动装填记录ID')
|
||||||
package_qty: int | None = Field(default=None, ge=0, description='确认时包数快照')
|
package_qty: int | None = Field(default=None, strict=True, gt=0, description='确认时包数快照')
|
||||||
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
||||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||||
|
|
||||||
@@ -274,8 +274,8 @@ class UsageModel(BaseModel):
|
|||||||
active_filling_id: int = Field(gt=0, description='活动装填记录ID')
|
active_filling_id: int = Field(gt=0, description='活动装填记录ID')
|
||||||
site_id: int = Field(gt=0, description='现场ID')
|
site_id: int = Field(gt=0, description='现场ID')
|
||||||
line_id: int = Field(gt=0, description='产线ID')
|
line_id: int = Field(gt=0, description='产线ID')
|
||||||
used_packages: int = Field(ge=0, description='本次使用包数')
|
used_packages: int = Field(strict=True, gt=0, description='本次使用包数')
|
||||||
remaining_packages_after: int = Field(ge=0, description='使用后剩余包数')
|
remaining_packages_after: int = Field(strict=True, ge=0, description='使用后剩余包数')
|
||||||
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
|
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
|
||||||
request_hash: str = Field(min_length=1, max_length=64, pattern=r'\S', description='请求摘要')
|
request_hash: str = Field(min_length=1, max_length=64, pattern=r'\S', description='请求摘要')
|
||||||
operator_id: int = Field(gt=0, description='操作人ID')
|
operator_id: int = Field(gt=0, description='操作人ID')
|
||||||
@@ -291,7 +291,7 @@ class ReturnItemModel(BaseModel):
|
|||||||
return_id: int = Field(gt=0, description='退回单ID')
|
return_id: int = Field(gt=0, description='退回单ID')
|
||||||
pallet_id: int = Field(gt=0, description='托盘ID')
|
pallet_id: int = Field(gt=0, description='托盘ID')
|
||||||
active_filling_id_snapshot: int | None = Field(default=None, gt=0, description='申请时活动装填记录快照')
|
active_filling_id_snapshot: int | None = Field(default=None, gt=0, description='申请时活动装填记录快照')
|
||||||
remaining_packages_snapshot: int = Field(ge=0, description='申请时剩余包数快照')
|
remaining_packages_snapshot: int = Field(strict=True, ge=0, description='申请时剩余包数快照')
|
||||||
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
||||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||||
|
|
||||||
@@ -458,6 +458,6 @@ class PalletScanResponseModel(BaseModel):
|
|||||||
strain_name: str | None = Field(default=None, max_length=100, description='菌种名称')
|
strain_name: str | None = Field(default=None, max_length=100, description='菌种名称')
|
||||||
batch_no: str | None = Field(default=None, max_length=64, description='生产批次')
|
batch_no: str | None = Field(default=None, max_length=64, description='生产批次')
|
||||||
package_spec: str | None = Field(default=None, max_length=64, description='包装规格')
|
package_spec: str | None = Field(default=None, max_length=64, description='包装规格')
|
||||||
capacity_packages: int = Field(gt=0, description='容量包数')
|
capacity_packages: int = Field(strict=True, gt=0, description='容量包数')
|
||||||
remaining_packages: int = Field(ge=0, description='当前剩余包数')
|
remaining_packages: int = Field(strict=True, ge=0, description='当前剩余包数')
|
||||||
allowed_actions: list[NonBlankAction] = Field(description='当前接口允许的业务动作')
|
allowed_actions: list[NonBlankAction] = Field(description='当前接口允许的业务动作')
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ class PalletInventoryReportModel(BaseModel):
|
|||||||
strain_code: str | None = Field(default=None, max_length=64, description='当前菌种编码快照')
|
strain_code: str | None = Field(default=None, max_length=64, description='当前菌种编码快照')
|
||||||
strain_name: str | None = Field(default=None, max_length=100, description='当前菌种名称快照')
|
strain_name: str | None = Field(default=None, max_length=100, description='当前菌种名称快照')
|
||||||
batch_no: str | None = Field(default=None, max_length=64, description='当前生产批次')
|
batch_no: str | None = Field(default=None, max_length=64, description='当前生产批次')
|
||||||
initial_packages: int | None = Field(default=None, ge=0, description='初始包数')
|
initial_packages: int | None = Field(default=None, strict=True, gt=0, description='初始包数')
|
||||||
remaining_packages: int = Field(ge=0, description='当前剩余包数')
|
remaining_packages: int = Field(strict=True, ge=0, description='当前剩余包数')
|
||||||
updated_at: datetime = Field(description='库存更新时间')
|
updated_at: datetime = Field(description='库存更新时间')
|
||||||
|
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class FillingReportModel(BaseModel):
|
|||||||
strain_name_snapshot: str = Field(min_length=1, max_length=100, pattern=r'\S', description='菌种名称快照')
|
strain_name_snapshot: str = Field(min_length=1, max_length=100, pattern=r'\S', description='菌种名称快照')
|
||||||
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
||||||
package_spec: str = Field(min_length=1, max_length=64, pattern=r'\S', description='包装规格')
|
package_spec: str = Field(min_length=1, max_length=64, pattern=r'\S', description='包装规格')
|
||||||
initial_packages: int = Field(ge=0, description='初始包数')
|
initial_packages: int = Field(strict=True, gt=0, description='初始包数')
|
||||||
operator_id: int = Field(gt=0, description='操作人ID')
|
operator_id: int = Field(gt=0, description='操作人ID')
|
||||||
operator_name: str | None = Field(default=None, max_length=100, description='操作人名称')
|
operator_name: str | None = Field(default=None, max_length=100, description='操作人名称')
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ class ShipmentReportModel(BaseModel):
|
|||||||
pallet_id: int = Field(gt=0, description='托盘ID')
|
pallet_id: int = Field(gt=0, description='托盘ID')
|
||||||
pallet_code: str = Field(min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
|
pallet_code: str = Field(min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
|
||||||
active_filling_id: int | None = Field(default=None, gt=0, description='发货时活动装填记录ID')
|
active_filling_id: int | None = Field(default=None, gt=0, description='发货时活动装填记录ID')
|
||||||
package_qty: int = Field(ge=0, description='发货时包数快照')
|
package_qty: int = Field(strict=True, gt=0, description='发货时包数快照')
|
||||||
|
|
||||||
|
|
||||||
class ConsumptionReportQueryModel(BaseModel):
|
class ConsumptionReportQueryModel(BaseModel):
|
||||||
@@ -157,8 +157,8 @@ class ConsumptionReportModel(BaseModel):
|
|||||||
strain_code: str | None = Field(default=None, max_length=64, description='菌种编码快照')
|
strain_code: str | None = Field(default=None, max_length=64, description='菌种编码快照')
|
||||||
strain_name: str | None = Field(default=None, max_length=100, description='菌种名称快照')
|
strain_name: str | None = Field(default=None, max_length=100, description='菌种名称快照')
|
||||||
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
||||||
used_packages: int = Field(ge=0, description='本次使用包数')
|
used_packages: int = Field(strict=True, gt=0, description='本次使用包数')
|
||||||
remaining_packages_after: int = Field(ge=0, description='使用后剩余包数')
|
remaining_packages_after: int = Field(strict=True, ge=0, description='使用后剩余包数')
|
||||||
operator_id: int = Field(gt=0, description='操作人ID')
|
operator_id: int = Field(gt=0, description='操作人ID')
|
||||||
operator_name: str | None = Field(default=None, max_length=100, description='操作人名称')
|
operator_name: str | None = Field(default=None, max_length=100, description='操作人名称')
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ class ReturnReportModel(BaseModel):
|
|||||||
pallet_id: int = Field(gt=0, description='托盘ID')
|
pallet_id: int = Field(gt=0, description='托盘ID')
|
||||||
pallet_code: str = Field(min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
|
pallet_code: str = Field(min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
|
||||||
active_filling_id_snapshot: int | None = Field(default=None, gt=0, description='活动装填记录快照')
|
active_filling_id_snapshot: int | None = Field(default=None, gt=0, description='活动装填记录快照')
|
||||||
remaining_packages_snapshot: int = Field(ge=0, description='剩余包数快照')
|
remaining_packages_snapshot: int = Field(strict=True, ge=0, description='剩余包数快照')
|
||||||
|
|
||||||
|
|
||||||
class PalletTraceReportQueryModel(BaseModel):
|
class PalletTraceReportQueryModel(BaseModel):
|
||||||
@@ -238,8 +238,8 @@ class PalletTraceReportModel(BaseModel):
|
|||||||
after_site_name: str | None = Field(default=None, max_length=100, description='变更后现场名称')
|
after_site_name: str | None = Field(default=None, max_length=100, description='变更后现场名称')
|
||||||
after_line_id: int | None = Field(default=None, gt=0, description='变更后产线ID')
|
after_line_id: int | None = Field(default=None, gt=0, description='变更后产线ID')
|
||||||
after_line_name: str | None = Field(default=None, max_length=100, description='变更后产线名称')
|
after_line_name: str | None = Field(default=None, max_length=100, description='变更后产线名称')
|
||||||
before_remaining_packages: int = Field(ge=0, description='变更前剩余包数')
|
before_remaining_packages: int = Field(strict=True, ge=0, description='变更前剩余包数')
|
||||||
after_remaining_packages: int = Field(ge=0, description='变更后剩余包数')
|
after_remaining_packages: int = Field(strict=True, ge=0, description='变更后剩余包数')
|
||||||
operator_id: int = Field(gt=0, description='操作人ID')
|
operator_id: int = Field(gt=0, description='操作人ID')
|
||||||
operator_name: str | None = Field(default=None, max_length=100, description='操作人名称')
|
operator_name: str | None = Field(default=None, max_length=100, description='操作人名称')
|
||||||
occurred_at: datetime = Field(description='发生时间')
|
occurred_at: datetime = Field(description='发生时间')
|
||||||
|
|||||||
@@ -589,6 +589,163 @@ PAGE_QUERY_MODELS = (
|
|||||||
PalletTraceReportQueryModel,
|
PalletTraceReportQueryModel,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STRICT_POSITIVE_PACKAGE_FIELDS = {
|
||||||
|
PalletModel: {'capacity_packages'},
|
||||||
|
FillingModel: {'initial_packages'},
|
||||||
|
ShipmentItemModel: {'package_qty'},
|
||||||
|
UsageModel: {'used_packages'},
|
||||||
|
PalletScanResponseModel: {'capacity_packages'},
|
||||||
|
PalletInventoryReportModel: {'initial_packages'},
|
||||||
|
FillingReportModel: {'initial_packages'},
|
||||||
|
ShipmentReportModel: {'package_qty'},
|
||||||
|
ConsumptionReportModel: {'used_packages'},
|
||||||
|
}
|
||||||
|
|
||||||
|
STRICT_NON_NEGATIVE_PACKAGE_FIELDS = {
|
||||||
|
UsageModel: {'remaining_packages_after'},
|
||||||
|
ReturnItemModel: {'remaining_packages_snapshot'},
|
||||||
|
PalletScanResponseModel: {'remaining_packages'},
|
||||||
|
PalletInventoryReportModel: {'remaining_packages'},
|
||||||
|
ConsumptionReportModel: {'remaining_packages_after'},
|
||||||
|
ReturnReportModel: {'remaining_packages_snapshot'},
|
||||||
|
PalletTraceReportModel: {'before_remaining_packages', 'after_remaining_packages'},
|
||||||
|
}
|
||||||
|
|
||||||
|
PACKAGE_MODEL_PAYLOADS = {
|
||||||
|
PalletModel: {
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'qr_token': 'qr-12345678',
|
||||||
|
'capacity_packages': 20,
|
||||||
|
'asset_status': '0',
|
||||||
|
},
|
||||||
|
FillingModel: {
|
||||||
|
'filling_id': 1,
|
||||||
|
'filling_no': 'F202607240001',
|
||||||
|
'pallet_id': 1,
|
||||||
|
'warehouse_id': 1,
|
||||||
|
'strain_id': 1,
|
||||||
|
'strain_code_snapshot': 'STRAIN-01',
|
||||||
|
'strain_name_snapshot': '菌种一号',
|
||||||
|
'batch_no': 'B20260724',
|
||||||
|
'package_spec': '20kg/包',
|
||||||
|
'initial_packages': 20,
|
||||||
|
'idempotency_key': 'fill-key-1',
|
||||||
|
'request_hash': 'fill-hash-1',
|
||||||
|
'operator_id': 1,
|
||||||
|
'operated_at': '2026-07-24T10:00:00',
|
||||||
|
},
|
||||||
|
ShipmentItemModel: {
|
||||||
|
'shipment_item_id': 1,
|
||||||
|
'shipment_id': 1,
|
||||||
|
'pallet_id': 1,
|
||||||
|
'package_qty': 20,
|
||||||
|
},
|
||||||
|
UsageModel: {
|
||||||
|
'usage_id': 1,
|
||||||
|
'usage_no': 'U202607240001',
|
||||||
|
'pallet_id': 1,
|
||||||
|
'active_filling_id': 1,
|
||||||
|
'site_id': 1,
|
||||||
|
'line_id': 1,
|
||||||
|
'used_packages': 1,
|
||||||
|
'remaining_packages_after': 19,
|
||||||
|
'idempotency_key': 'usage-key-1',
|
||||||
|
'request_hash': 'usage-hash-1',
|
||||||
|
'operator_id': 1,
|
||||||
|
'occurred_at': '2026-07-24T11:00:00',
|
||||||
|
},
|
||||||
|
ReturnItemModel: {
|
||||||
|
'return_item_id': 1,
|
||||||
|
'return_id': 1,
|
||||||
|
'pallet_id': 1,
|
||||||
|
'remaining_packages_snapshot': 0,
|
||||||
|
},
|
||||||
|
PalletScanResponseModel: {
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'flow_state': 'AT_SITE_PARTIAL',
|
||||||
|
'capacity_packages': 20,
|
||||||
|
'remaining_packages': 10,
|
||||||
|
'allowed_actions': [],
|
||||||
|
},
|
||||||
|
PalletInventoryReportModel: {
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'flow_state': 'FILLED_IN_WAREHOUSE',
|
||||||
|
'initial_packages': 20,
|
||||||
|
'remaining_packages': 20,
|
||||||
|
'updated_at': '2026-07-24T10:00:00',
|
||||||
|
},
|
||||||
|
FillingReportModel: {
|
||||||
|
'filling_id': 1,
|
||||||
|
'filling_no': 'F202607240001',
|
||||||
|
'operated_at': '2026-07-24T10:00:00',
|
||||||
|
'warehouse_id': 1,
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'strain_id': 1,
|
||||||
|
'strain_code_snapshot': 'STRAIN-01',
|
||||||
|
'strain_name_snapshot': '菌种一号',
|
||||||
|
'batch_no': 'B20260724',
|
||||||
|
'package_spec': '20kg/包',
|
||||||
|
'initial_packages': 20,
|
||||||
|
'operator_id': 1,
|
||||||
|
},
|
||||||
|
ShipmentReportModel: {
|
||||||
|
'shipment_id': 1,
|
||||||
|
'shipment_no': 'S202607240001',
|
||||||
|
'confirmed_at': '2026-07-24T12:00:00',
|
||||||
|
'source_warehouse_id': 1,
|
||||||
|
'target_site_id': 1,
|
||||||
|
'customer_id': 1,
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'package_qty': 20,
|
||||||
|
},
|
||||||
|
ConsumptionReportModel: {
|
||||||
|
'usage_id': 1,
|
||||||
|
'usage_no': 'U202607240001',
|
||||||
|
'occurred_at': '2026-07-24T13:00:00',
|
||||||
|
'customer_id': 1,
|
||||||
|
'site_id': 1,
|
||||||
|
'line_id': 1,
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'strain_id': 1,
|
||||||
|
'batch_no': 'B20260724',
|
||||||
|
'used_packages': 1,
|
||||||
|
'remaining_packages_after': 19,
|
||||||
|
'operator_id': 1,
|
||||||
|
},
|
||||||
|
ReturnReportModel: {
|
||||||
|
'return_id': 1,
|
||||||
|
'return_no': 'R202607240001',
|
||||||
|
'status': 'REQUESTED',
|
||||||
|
'requested_at': '2026-07-24T14:00:00',
|
||||||
|
'source_site_id': 1,
|
||||||
|
'target_warehouse_id': 1,
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'remaining_packages_snapshot': 0,
|
||||||
|
},
|
||||||
|
PalletTraceReportModel: {
|
||||||
|
'event_id': 1,
|
||||||
|
'event_seq': 1,
|
||||||
|
'event_type': 'INBOUND_CONFIRMED',
|
||||||
|
'source_type': 'INBOUND',
|
||||||
|
'source_id': 1,
|
||||||
|
'pallet_id': 1,
|
||||||
|
'pallet_code': 'PLT-001',
|
||||||
|
'before_state': 'UNSTOCKED',
|
||||||
|
'after_state': 'EMPTY_IN_WAREHOUSE',
|
||||||
|
'before_remaining_packages': 0,
|
||||||
|
'after_remaining_packages': 0,
|
||||||
|
'operator_id': 1,
|
||||||
|
'occurred_at': '2026-07-24T15:00:00',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class BusinessVoContractTest(unittest.TestCase):
|
class BusinessVoContractTest(unittest.TestCase):
|
||||||
def test_all_models_have_exact_fields_and_camel_case_aliases(self) -> None:
|
def test_all_models_have_exact_fields_and_camel_case_aliases(self) -> None:
|
||||||
@@ -684,6 +841,49 @@ class BusinessVoContractTest(unittest.TestCase):
|
|||||||
with self.subTest(model=model_class.__name__, value=invalid_value), self.assertRaises(ValidationError):
|
with self.subTest(model=model_class.__name__, value=invalid_value), self.assertRaises(ValidationError):
|
||||||
model_class.model_validate(payload | {field_name: invalid_value})
|
model_class.model_validate(payload | {field_name: invalid_value})
|
||||||
|
|
||||||
|
def test_output_occurrence_and_capacity_fields_are_strict_positive_integers(self) -> None:
|
||||||
|
accepted_invalid_values = []
|
||||||
|
|
||||||
|
for model_class, field_names in STRICT_POSITIVE_PACKAGE_FIELDS.items():
|
||||||
|
payload = PACKAGE_MODEL_PAYLOADS[model_class]
|
||||||
|
model_class.model_validate(payload)
|
||||||
|
for field_name in field_names:
|
||||||
|
for invalid_value in (0, '1', 1.5, True):
|
||||||
|
try:
|
||||||
|
model_class.model_validate(payload | {field_name: invalid_value})
|
||||||
|
except ValidationError:
|
||||||
|
continue
|
||||||
|
accepted_invalid_values.append((model_class.__name__, field_name, repr(invalid_value)))
|
||||||
|
|
||||||
|
self.assertEqual([], accepted_invalid_values)
|
||||||
|
self.assertIsNone(
|
||||||
|
ShipmentItemModel.model_validate(
|
||||||
|
PACKAGE_MODEL_PAYLOADS[ShipmentItemModel] | {'package_qty': None}
|
||||||
|
).package_qty
|
||||||
|
)
|
||||||
|
self.assertIsNone(
|
||||||
|
PalletInventoryReportModel.model_validate(
|
||||||
|
PACKAGE_MODEL_PAYLOADS[PalletInventoryReportModel] | {'initial_packages': None}
|
||||||
|
).initial_packages
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_output_snapshot_and_remaining_fields_are_strict_non_negative_integers(self) -> None:
|
||||||
|
accepted_invalid_values = []
|
||||||
|
|
||||||
|
for model_class, field_names in STRICT_NON_NEGATIVE_PACKAGE_FIELDS.items():
|
||||||
|
payload = PACKAGE_MODEL_PAYLOADS[model_class]
|
||||||
|
for field_name in field_names:
|
||||||
|
zero_model = model_class.model_validate(payload | {field_name: 0})
|
||||||
|
self.assertEqual(0, getattr(zero_model, field_name))
|
||||||
|
for invalid_value in (-1, '1', 1.5, True):
|
||||||
|
try:
|
||||||
|
model_class.model_validate(payload | {field_name: invalid_value})
|
||||||
|
except ValidationError:
|
||||||
|
continue
|
||||||
|
accepted_invalid_values.append((model_class.__name__, field_name, repr(invalid_value)))
|
||||||
|
|
||||||
|
self.assertEqual([], accepted_invalid_values)
|
||||||
|
|
||||||
def test_id_lists_reject_empty_non_positive_and_duplicate_values(self) -> None:
|
def test_id_lists_reject_empty_non_positive_and_duplicate_values(self) -> None:
|
||||||
cases = (
|
cases = (
|
||||||
(DeleteStrainModel, {}, 'strainIds'),
|
(DeleteStrainModel, {}, 'strainIds'),
|
||||||
|
|||||||
Reference in New Issue
Block a user