Feat: 后端新增基础资料与业务管理只读汇总接口
为 Web 端改版提供名称、统计和看板数据。只新增只读查询、扩展列表与详情 返回字段;写接口、托盘状态机和幂等键规则均未改动,未新增权限标识。 - 基础资料:列表补充名称与汇总,客户详情返回现场树,托盘详情返回最近流转, 托盘列表支持按流转阶段筛选和二维码精确匹配;新增现场、产线、隧道的上级 资料选项接口;Excel 导入支持 preview 只校验不写入 - 发货出库:列表支持 keyword、receiptState 筛选并补充名称、托盘数、包数和 菌种构成;详情明细补充托盘号、菌种、批次 - 首次入库:列表与详情补充名称、托盘数、托盘当前状态;新增 /summary - 托盘退回:列表支持 keyword 并补充名称与托盘汇总,详情补充托盘号和隧道; 新增 /summary、/site-options - 工厂装填:列表支持 keyword 并补充托盘号、仓库、操作人;新增 /summary、 /batches、/batches/detail - 客户现场使用:列表支持 keyword、fifoSkipped 并补充名称和被跳过的先到托盘号; 新增 /site-options、/site-board 以及 usage-fifo 的 /queue - 先到先用 DAO 抽出公共候选条件与排序,建议查询生成的 SQL 与改动前一致 - 新增各汇总 DAO 的单元测试,同步更新路由权限与模型字段契约测试 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -250,6 +250,8 @@ class InboundItemModel(BaseModel):
|
||||
inbound_item_id: int = Field(gt=0, description='入库明细ID')
|
||||
inbound_id: int = Field(gt=0, description='入库单ID')
|
||||
pallet_id: int = Field(gt=0, description='托盘ID')
|
||||
pallet_code: str | None = Field(default=None, max_length=64, description='托盘编号(只读展示)')
|
||||
flow_state: PalletFlowState | None = Field(default=None, description='托盘当前流转状态(只读展示)')
|
||||
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
|
||||
@@ -275,6 +277,44 @@ class InboundModel(BaseModel):
|
||||
items: list[InboundItemModel] = Field(default_factory=list, description='入库托盘明细')
|
||||
|
||||
|
||||
class InboundListItemModel(InboundModel):
|
||||
"""首次入库单列表行:在单据字段外附带只读的名称与托盘数。"""
|
||||
|
||||
warehouse_name: str | None = Field(default=None, max_length=100, description='入库仓库名称')
|
||||
creator_name: str | None = Field(default=None, max_length=30, description='创建人昵称')
|
||||
confirmer_name: str | None = Field(default=None, max_length=30, description='确认人昵称')
|
||||
pallet_count: int = Field(default=0, strict=True, ge=0, description='托盘数')
|
||||
|
||||
|
||||
class InboundWarehouseCountModel(BaseModel):
|
||||
"""按仓库统计的托盘数。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
warehouse_id: int = Field(gt=0, description='仓库ID')
|
||||
warehouse_name: str | None = Field(default=None, max_length=100, description='仓库名称')
|
||||
pallet_count: int = Field(strict=True, ge=0, description='托盘数')
|
||||
|
||||
|
||||
class InboundOverviewModel(BaseModel):
|
||||
"""首次入库页面概况(只读)。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
draft_count: int = Field(strict=True, ge=0, description='待确认草稿单数')
|
||||
draft_pallet_count: int = Field(strict=True, ge=0, description='待确认草稿托盘数')
|
||||
oldest_draft_at: datetime | None = Field(default=None, description='最早草稿创建时间')
|
||||
month_start: date = Field(description='本月统计起始日期')
|
||||
month_confirmed_count: int = Field(strict=True, ge=0, description='本月已确认单数')
|
||||
month_confirmed_pallet_count: int = Field(strict=True, ge=0, description='本月已确认入库托盘数')
|
||||
unstocked_pallet_count: int = Field(strict=True, ge=0, description='待首次入库托盘数(资产正常且未入库)')
|
||||
empty_pallet_count: int = Field(strict=True, ge=0, description='授权仓库空托盘总数')
|
||||
empty_by_warehouse: list[InboundWarehouseCountModel] = Field(
|
||||
default_factory=list,
|
||||
description='授权仓库空托盘按仓库分布,托盘数从多到少',
|
||||
)
|
||||
|
||||
|
||||
class FillingModel(BaseModel):
|
||||
"""托盘装填记录展示模型。"""
|
||||
|
||||
@@ -305,6 +345,68 @@ class FillingModel(BaseModel):
|
||||
remark: str | None = Field(default=None, max_length=500, description='备注')
|
||||
|
||||
|
||||
class FillingListItemModel(FillingModel):
|
||||
"""托盘装填记录列表行:在记录字段外附带只读的托盘编号、仓库与操作人名称。"""
|
||||
|
||||
pallet_code: str | None = Field(default=None, max_length=64, description='托盘编号')
|
||||
warehouse_name: str | None = Field(default=None, max_length=100, description='仓库名称')
|
||||
operator_name: str | None = Field(default=None, max_length=30, description='操作人昵称')
|
||||
|
||||
|
||||
class FillingBatchModel(BaseModel):
|
||||
"""按菌种 + 生产批次聚合的装填汇总(只读)。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
strain_id: int = Field(gt=0, description='菌种ID')
|
||||
strain_code: str | None = Field(default=None, max_length=64, description='菌种编码快照')
|
||||
strain_name: str | None = Field(default=None, max_length=100, description='菌种名称快照')
|
||||
batch_no: str = Field(min_length=1, max_length=64, description='生产批次')
|
||||
production_date: date | None = Field(default=None, description='生产日期(批次内最早)')
|
||||
expiry_date: date | None = Field(default=None, description='有效期至(批次内最早)')
|
||||
package_spec: str | None = Field(default=None, max_length=64, description='最近一次装填的包装规格')
|
||||
pallet_count: int = Field(strict=True, ge=0, description='已装填托盘数')
|
||||
package_sum: int = Field(strict=True, ge=0, description='已装填包数')
|
||||
last_operated_at: datetime | None = Field(default=None, description='最近装填时间')
|
||||
last_operator_name: str | None = Field(default=None, max_length=30, description='最近装填操作人昵称')
|
||||
|
||||
|
||||
class FillingBatchStateCountModel(BaseModel):
|
||||
"""批次托盘按当前去向统计。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
state: str = Field(
|
||||
min_length=1,
|
||||
max_length=32,
|
||||
description='托盘当前流转状态;装填周期已结束(物料用完)时为 CONSUMED',
|
||||
)
|
||||
pallet_count: int = Field(strict=True, ge=0, description='托盘数')
|
||||
|
||||
|
||||
class FillingBatchRecordModel(BaseModel):
|
||||
"""批次内的一条装填记录(只读展示)。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
filling_id: int = Field(gt=0, description='装填记录ID')
|
||||
filling_no: str = Field(min_length=1, max_length=64, description='装填单号')
|
||||
pallet_id: int = Field(gt=0, description='托盘ID')
|
||||
pallet_code: str | None = Field(default=None, max_length=64, description='托盘编号')
|
||||
warehouse_name: str | None = Field(default=None, max_length=100, description='装填仓库名称')
|
||||
operator_name: str | None = Field(default=None, max_length=30, description='操作人昵称')
|
||||
operated_at: datetime = Field(description='装填时间')
|
||||
|
||||
|
||||
class FillingBatchDetailModel(FillingBatchModel):
|
||||
"""生产批次详情:批次汇总、托盘当前去向与最近装填记录。"""
|
||||
|
||||
distribution: list[FillingBatchStateCountModel] = Field(
|
||||
default_factory=list, description='托盘当前去向,托盘数从多到少'
|
||||
)
|
||||
recent: list[FillingBatchRecordModel] = Field(default_factory=list, description='最近 10 条装填记录')
|
||||
|
||||
|
||||
class ShipmentItemModel(BaseModel):
|
||||
"""整托发货明细模型。"""
|
||||
|
||||
@@ -321,6 +423,10 @@ class ShipmentItemModel(BaseModel):
|
||||
ge=0,
|
||||
description='当前库存剩余包数(只读,非确认快照)',
|
||||
)
|
||||
pallet_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='菌种名称快照(只读展示)')
|
||||
batch_no: 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='创建时间')
|
||||
|
||||
@@ -350,6 +456,61 @@ class ShipmentModel(BaseModel):
|
||||
items: list[ShipmentItemModel] = Field(default_factory=list, description='发货托盘明细')
|
||||
|
||||
|
||||
class ShipmentStrainSummaryModel(BaseModel):
|
||||
"""发货单内菌种构成。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
strain_name: str = Field(min_length=1, max_length=100, description='菌种名称或编码')
|
||||
pallet_count: int = Field(strict=True, gt=0, description='托盘数')
|
||||
|
||||
|
||||
class FillingOverviewModel(BaseModel):
|
||||
"""工厂装填页面概况(只读)。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
today_pallet_count: int = Field(strict=True, ge=0, description='今日装填托盘数')
|
||||
today_package_sum: int = Field(strict=True, ge=0, description='今日装填包数')
|
||||
today_batch_count: int = Field(strict=True, ge=0, description='今日涉及的生产批次数')
|
||||
month_start: date = Field(description='本月统计起始日期')
|
||||
month_pallet_count: int = Field(strict=True, ge=0, description='本月装填托盘数')
|
||||
month_batch_count: int = Field(strict=True, ge=0, description='本月涉及的生产批次数')
|
||||
month_top_strains: list[ShipmentStrainSummaryModel] = Field(
|
||||
default_factory=list,
|
||||
description='本月装填最多的菌种(最多 3 个)',
|
||||
)
|
||||
empty_pallet_count: int = Field(strict=True, ge=0, description='授权仓库可装填空托盘总数')
|
||||
empty_by_warehouse: list[InboundWarehouseCountModel] = Field(
|
||||
default_factory=list,
|
||||
description='授权仓库空托盘按仓库分布,托盘数从多到少',
|
||||
)
|
||||
near_expiry_days: int = Field(strict=True, gt=0, description='临期判定天数')
|
||||
near_expiry_pallet_count: int = Field(
|
||||
strict=True, ge=0, description='有效期在临期天数内(含已过期)且仍在仓的有料托盘数'
|
||||
)
|
||||
near_expiry_batch_count: int = Field(strict=True, ge=0, description='上述托盘涉及的生产批次数')
|
||||
nearest_expiry_batch_no: str | None = Field(default=None, max_length=64, description='最早到期的在仓批次')
|
||||
nearest_expiry_date: date | None = Field(default=None, description='最早到期的在仓批次有效期')
|
||||
|
||||
|
||||
class ShipmentListItemModel(ShipmentModel):
|
||||
"""整托发货单列表行:在单据字段外附带只读的名称与汇总。"""
|
||||
|
||||
source_warehouse_name: str | None = Field(default=None, max_length=100, description='来源仓库名称')
|
||||
target_site_name: str | None = Field(default=None, max_length=100, description='目标现场名称')
|
||||
customer_name: str | None = Field(default=None, max_length=100, description='客户名称')
|
||||
creator_name: str | None = Field(default=None, max_length=30, description='创建人昵称')
|
||||
pallet_count: int = Field(default=0, strict=True, ge=0, description='托盘数')
|
||||
package_qty: int | None = Field(
|
||||
default=None,
|
||||
strict=True,
|
||||
ge=0,
|
||||
description='包数:已确认为确认快照之和,草稿为当前可发余量之和',
|
||||
)
|
||||
strains: list[ShipmentStrainSummaryModel] = Field(default_factory=list, description='菌种构成')
|
||||
|
||||
|
||||
class UsageModel(BaseModel):
|
||||
"""客户现场使用记录展示模型。"""
|
||||
|
||||
@@ -373,6 +534,20 @@ class UsageModel(BaseModel):
|
||||
fifo_skip_reason: str | None = Field(default=None, max_length=500, description='跳过先到托盘原因')
|
||||
|
||||
|
||||
class UsageListItemModel(UsageModel):
|
||||
"""客户现场使用记录列表行:附带只读的托盘、现场、隧道、产线与操作人名称。"""
|
||||
|
||||
pallet_code: str | None = Field(default=None, max_length=64, description='托盘编号')
|
||||
site_name: str | None = Field(default=None, max_length=100, description='现场名称')
|
||||
tunnel_code: str | None = Field(default=None, max_length=64, description='隧道编号')
|
||||
tunnel_name: str | None = Field(default=None, max_length=100, description='隧道名称')
|
||||
line_name: str | None = Field(default=None, max_length=100, description='产线名称')
|
||||
operator_name: str | None = Field(default=None, max_length=30, description='操作人昵称')
|
||||
skipped_pallet_code: str | None = Field(
|
||||
default=None, max_length=64, description='被跳过的先到托盘编号(取自使用时的先到先用快照)'
|
||||
)
|
||||
|
||||
|
||||
class ReturnItemModel(BaseModel):
|
||||
"""托盘退回明细模型。"""
|
||||
|
||||
@@ -386,6 +561,8 @@ class ReturnItemModel(BaseModel):
|
||||
trace_no_snapshot: str | None = Field(default=None, max_length=64, description='申请时物料溯源号快照')
|
||||
batch_no_snapshot: str | None = Field(default=None, max_length=64, description='申请时生产批次快照')
|
||||
remaining_packages_snapshot: int = Field(strict=True, ge=0, description='申请时剩余包数快照')
|
||||
pallet_code: str | None = Field(default=None, max_length=64, description='托盘编号(只读展示)')
|
||||
tunnel_name: str | None = Field(default=None, max_length=100, description='申请时所在隧道名称(只读展示)')
|
||||
create_by: str | None = Field(default=None, max_length=64, description='创建者')
|
||||
create_time: datetime | None = Field(default=None, description='创建时间')
|
||||
|
||||
@@ -416,6 +593,37 @@ class ReturnModel(BaseModel):
|
||||
items: list[ReturnItemModel] = Field(default_factory=list, description='退回托盘明细')
|
||||
|
||||
|
||||
class ReturnListItemModel(ReturnModel):
|
||||
"""托盘退回单列表行:在单据字段外附带只读的名称与托盘汇总。"""
|
||||
|
||||
source_site_name: str | None = Field(default=None, max_length=100, description='来源现场名称')
|
||||
customer_name: str | None = Field(default=None, max_length=100, description='客户名称')
|
||||
target_warehouse_name: str | None = Field(default=None, max_length=100, description='目标仓库名称')
|
||||
requester_name: str | None = Field(default=None, max_length=30, description='申请人昵称')
|
||||
confirmer_name: str | None = Field(default=None, max_length=30, description='确认人昵称')
|
||||
pallet_count: int = Field(default=0, strict=True, ge=0, description='托盘数')
|
||||
package_sum: int = Field(default=0, strict=True, ge=0, description='申请时剩余包数快照之和')
|
||||
filled_count: int = Field(default=0, strict=True, ge=0, description='申请时仍有余量的托盘数')
|
||||
|
||||
|
||||
class ReturnOverviewModel(BaseModel):
|
||||
"""托盘退回页面概况(只读)。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
pending_count: int = Field(strict=True, ge=0, description='待到仓核对单数')
|
||||
pending_pallet_count: int = Field(strict=True, ge=0, description='待到仓核对托盘数(即退回中托盘)')
|
||||
pending_filled_count: int = Field(strict=True, ge=0, description='待核对托盘中仍有余量的托盘数')
|
||||
pending_package_sum: int = Field(strict=True, ge=0, description='待核对托盘剩余包数之和')
|
||||
oldest_pending_at: datetime | None = Field(default=None, description='最早一张待核对单的申请时间')
|
||||
month_start: date = Field(description='本月统计起始日期')
|
||||
month_confirmed_count: int = Field(strict=True, ge=0, description='本月已确认单数')
|
||||
month_confirmed_pallet_count: int = Field(strict=True, ge=0, description='本月已确认托盘数')
|
||||
month_filled_pallet_count: int = Field(strict=True, ge=0, description='本月确认后回到有料库存的托盘数')
|
||||
month_package_sum: int = Field(strict=True, ge=0, description='本月确认带回的剩余包数')
|
||||
month_avg_confirm_hours: float | None = Field(default=None, ge=0, description='本月申请到确认的平均小时数')
|
||||
|
||||
|
||||
class InboundPageQueryModel(BaseModel):
|
||||
"""首次入库分页查询模型。"""
|
||||
|
||||
@@ -440,6 +648,13 @@ class FillingPageQueryModel(BaseModel):
|
||||
page_num: int = Field(default=1, gt=0, description='当前页码')
|
||||
page_size: int = Field(default=10, gt=0, le=100, description='每页记录数')
|
||||
filling_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='装填单号')
|
||||
keyword: str | None = Field(
|
||||
default=None,
|
||||
min_length=1,
|
||||
max_length=64,
|
||||
pattern=r'\S',
|
||||
description='关键字:匹配装填单号、托盘编号或生产批次',
|
||||
)
|
||||
warehouse_id: int | None = Field(default=None, gt=0, description='仓库ID')
|
||||
strain_id: int | None = Field(default=None, gt=0, description='菌种ID')
|
||||
batch_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
||||
@@ -450,6 +665,36 @@ class FillingPageQueryModel(BaseModel):
|
||||
_validate_date_range = model_validator(mode='after')(_validate_page_query_date_range)
|
||||
|
||||
|
||||
class FillingBatchPageQueryModel(BaseModel):
|
||||
"""生产批次汇总分页查询模型。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
page_num: int = Field(default=1, gt=0, description='当前页码')
|
||||
page_size: int = Field(default=10, gt=0, le=100, description='每页记录数')
|
||||
keyword: str | None = Field(
|
||||
default=None,
|
||||
min_length=1,
|
||||
max_length=64,
|
||||
pattern=r'\S',
|
||||
description='关键字:匹配生产批次、菌种编码或名称',
|
||||
)
|
||||
strain_id: int | None = Field(default=None, gt=0, description='菌种ID')
|
||||
begin_time: str | None = Field(default=None, min_length=1, pattern=r'\S', description='装填开始日期')
|
||||
end_time: str | None = Field(default=None, min_length=1, pattern=r'\S', description='装填结束日期')
|
||||
|
||||
_validate_date_range = model_validator(mode='after')(_validate_page_query_date_range)
|
||||
|
||||
|
||||
class FillingBatchDetailQueryModel(BaseModel):
|
||||
"""生产批次详情查询模型。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
strain_id: int = Field(gt=0, description='菌种ID')
|
||||
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
|
||||
|
||||
|
||||
class ShipmentPageQueryModel(BaseModel):
|
||||
"""整托发货分页查询模型。"""
|
||||
|
||||
@@ -458,9 +703,17 @@ class ShipmentPageQueryModel(BaseModel):
|
||||
page_num: int = Field(default=1, gt=0, description='当前页码')
|
||||
page_size: int = Field(default=10, gt=0, le=100, description='每页记录数')
|
||||
shipment_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='发货单号')
|
||||
keyword: str | None = Field(
|
||||
default=None,
|
||||
min_length=1,
|
||||
max_length=64,
|
||||
pattern=r'\S',
|
||||
description='关键字:匹配发货单号、来源仓库、目标现场或客户名称',
|
||||
)
|
||||
source_warehouse_id: int | None = Field(default=None, gt=0, description='来源仓库ID')
|
||||
target_site_id: int | None = Field(default=None, gt=0, description='目标现场ID')
|
||||
status: DocumentStatus | None = Field(default=None, description='单据状态')
|
||||
receipt_state: Literal['PENDING', 'RECEIVED', 'LEGACY'] | None = Field(default=None, description='收货核对状态')
|
||||
begin_time: str | None = Field(default=None, min_length=1, pattern=r'\S', description='开始时间')
|
||||
end_time: str | None = Field(default=None, min_length=1, pattern=r'\S', description='结束时间')
|
||||
|
||||
@@ -475,6 +728,14 @@ class UsagePageQueryModel(BaseModel):
|
||||
page_num: int = Field(default=1, gt=0, description='当前页码')
|
||||
page_size: int = Field(default=10, gt=0, le=100, description='每页记录数')
|
||||
usage_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='使用记录号')
|
||||
keyword: str | None = Field(
|
||||
default=None,
|
||||
min_length=1,
|
||||
max_length=64,
|
||||
pattern=r'\S',
|
||||
description='关键字:匹配使用记录号或托盘编号',
|
||||
)
|
||||
fifo_skipped: bool | None = Field(default=None, description='为 true 时只看跳过先到托盘的记录')
|
||||
site_id: int | None = Field(default=None, gt=0, description='现场ID')
|
||||
tunnel_id: int | None = Field(default=None, gt=0, description='隧道ID')
|
||||
trace_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='物料溯源号')
|
||||
@@ -494,6 +755,13 @@ class ReturnPageQueryModel(BaseModel):
|
||||
page_num: int = Field(default=1, gt=0, description='当前页码')
|
||||
page_size: int = Field(default=10, gt=0, le=100, description='每页记录数')
|
||||
return_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='退回单号')
|
||||
keyword: str | None = Field(
|
||||
default=None,
|
||||
min_length=1,
|
||||
max_length=64,
|
||||
pattern=r'\S',
|
||||
description='关键字:匹配退回单号、托盘编号、来源现场、客户或目标仓库名称',
|
||||
)
|
||||
source_site_id: int | None = Field(default=None, gt=0, description='来源现场ID')
|
||||
target_warehouse_id: int | None = Field(default=None, gt=0, description='目标仓库ID')
|
||||
status: ReturnStatus | None = Field(default=None, description='退回状态')
|
||||
@@ -547,6 +815,71 @@ class BusinessOptionModel(BaseModel):
|
||||
name: str = Field(min_length=1, max_length=100, pattern=r'\S', description='业务名称')
|
||||
|
||||
|
||||
class ReturnSiteOptionModel(BusinessOptionModel):
|
||||
"""退回来源现场选项,附带所属客户名称。"""
|
||||
|
||||
customer_name: str | None = Field(default=None, max_length=100, description='客户名称')
|
||||
|
||||
|
||||
class UsageSiteOptionModel(BusinessOptionModel):
|
||||
"""现场使用页的现场选项,附带客户名称与启用隧道、产线数。"""
|
||||
|
||||
customer_name: str | None = Field(default=None, max_length=100, description='客户名称')
|
||||
tunnel_count: int = Field(default=0, strict=True, ge=0, description='启用隧道数')
|
||||
line_count: int = Field(default=0, strict=True, ge=0, description='启用产线数')
|
||||
|
||||
|
||||
class UsageBoardTunnelModel(BaseModel):
|
||||
"""隧道看板中的一条隧道(不含内部溯源号)。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
tunnel_id: int = Field(gt=0, description='隧道ID')
|
||||
tunnel_code: str = Field(min_length=1, max_length=64, description='隧道编号')
|
||||
tunnel_name: str = Field(min_length=1, max_length=100, description='隧道名称')
|
||||
in_use: bool = Field(description='是否有正在使用的物料批')
|
||||
pallet_count: int = Field(strict=True, ge=0, description='在隧道中仍有余量的托盘数')
|
||||
remaining_packages: int = Field(strict=True, ge=0, description='在隧道中托盘的剩余包数')
|
||||
started_at: datetime | None = Field(default=None, description='本轮物料批开始时间')
|
||||
last_used_at: datetime | None = Field(default=None, description='最近一次登记使用时间')
|
||||
last_line_name: str | None = Field(default=None, max_length=100, description='最近一次使用的产线名称')
|
||||
|
||||
|
||||
class UsageLinePackagesModel(BaseModel):
|
||||
"""按产线统计的用量。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
line_id: int | None = Field(default=None, gt=0, description='产线ID;未指定产线时为空')
|
||||
line_name: str | None = Field(default=None, max_length=100, description='产线名称')
|
||||
used_packages: int = Field(strict=True, ge=0, description='使用包数')
|
||||
|
||||
|
||||
class UsageSiteBoardModel(BaseModel):
|
||||
"""客户现场看板:库存、今日用量、隧道与先到先用概况(只读)。"""
|
||||
|
||||
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
|
||||
|
||||
site_id: int = Field(gt=0, description='现场ID')
|
||||
site_code: str | None = Field(default=None, max_length=64, description='现场编码')
|
||||
site_name: str | None = Field(default=None, max_length=100, description='现场名称')
|
||||
customer_name: str | None = Field(default=None, max_length=100, description='客户名称')
|
||||
pallet_count: int = Field(strict=True, ge=0, description='现场未使用与使用中的托盘数')
|
||||
package_sum: int = Field(strict=True, ge=0, description='上述托盘的剩余包数')
|
||||
unused_pallet_count: int = Field(strict=True, ge=0, description='现场未使用托盘数')
|
||||
partial_pallet_count: int = Field(strict=True, ge=0, description='现场使用中托盘数')
|
||||
empty_pallet_count: int = Field(strict=True, ge=0, description='现场已用完、待退回的托盘数')
|
||||
today_usage_count: int = Field(strict=True, ge=0, description='今日登记次数')
|
||||
today_used_packages: int = Field(strict=True, ge=0, description='今日使用包数')
|
||||
today_by_line: list[UsageLinePackagesModel] = Field(default_factory=list, description='今日用量按产线,从多到少')
|
||||
tunnel_count: int = Field(strict=True, ge=0, description='启用隧道数')
|
||||
active_tunnel_count: int = Field(strict=True, ge=0, description='正在使用的隧道数')
|
||||
today_fifo_skip_count: int = Field(strict=True, ge=0, description='今日跳过先到托盘的使用次数')
|
||||
week_fifo_skip_count: int = Field(strict=True, ge=0, description='近 7 天跳过先到托盘的使用次数')
|
||||
tunnels: list[UsageBoardTunnelModel] = Field(default_factory=list, description='启用隧道,按编号排序')
|
||||
lines: list[BusinessOptionModel] = Field(default_factory=list, description='启用产线,供筛选使用')
|
||||
|
||||
|
||||
class PalletScanResponseModel(BaseModel):
|
||||
"""业务扫码托盘响应模型。"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user