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:
@@ -2,12 +2,13 @@ from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import ColumnElement, Select, func, select, update
|
||||
from sqlalchemy import ColumnElement, Select, func, or_, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_admin.entity.do.user_do import SysUser
|
||||
from module_business.domain.enums import PalletFlowState
|
||||
from module_business.domain.test_batch_scope import TestBatchScope, batch_scope_condition
|
||||
from module_business.entity.do.master_do import (
|
||||
BizCustomer,
|
||||
@@ -39,6 +40,18 @@ from module_business.entity.vo.master_vo import (
|
||||
)
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
_PALLET_FLOW_GROUP_STATES = {
|
||||
'unstocked': (PalletFlowState.UNSTOCKED.value,),
|
||||
'warehouse': (PalletFlowState.EMPTY_IN_WAREHOUSE.value, PalletFlowState.FILLED_IN_WAREHOUSE.value),
|
||||
'transit': (PalletFlowState.IN_TRANSIT.value,),
|
||||
'site': (
|
||||
PalletFlowState.AT_SITE_UNUSED.value,
|
||||
PalletFlowState.AT_SITE_PARTIAL.value,
|
||||
PalletFlowState.AT_SITE_EMPTY.value,
|
||||
),
|
||||
'returning': (PalletFlowState.RETURNING.value,),
|
||||
}
|
||||
|
||||
|
||||
class MasterDao:
|
||||
"""菌种、客户、现场、产线、隧道和仓库主数据数据库操作层。"""
|
||||
@@ -494,15 +507,44 @@ class MasterDao:
|
||||
) -> PageModel | list[dict[str, Any]]:
|
||||
conditions = [batch_scope_condition(BizPallet.test_batch_id, scope)]
|
||||
if query_object.pallet_code:
|
||||
conditions.append(BizPallet.pallet_code.like(f'%{query_object.pallet_code}%'))
|
||||
# 扫码枪输入的是二维码固定标识,按编号模糊或标识精确匹配
|
||||
conditions.append(
|
||||
or_(
|
||||
BizPallet.pallet_code.like(f'%{query_object.pallet_code}%'),
|
||||
BizPallet.qr_token == query_object.pallet_code,
|
||||
)
|
||||
)
|
||||
if query_object.asset_status:
|
||||
conditions.append(BizPallet.asset_status == query_object.asset_status)
|
||||
if query_object.flow_group:
|
||||
conditions.append(cls._pallet_flow_group_condition(query_object.flow_group, scope))
|
||||
ids = cls._parse_id_list(getattr(query_object, 'ids', None))
|
||||
if ids:
|
||||
conditions.append(BizPallet.pallet_id.in_(ids))
|
||||
statement = select(BizPallet).where(*conditions).order_by(BizPallet.pallet_id)
|
||||
return await cls._paginate(db, statement, query_object, is_page)
|
||||
|
||||
@staticmethod
|
||||
def _pallet_flow_group_condition(flow_group: str, scope: TestBatchScope) -> ColumnElement[bool]:
|
||||
states = _PALLET_FLOW_GROUP_STATES[flow_group]
|
||||
in_states = BizPallet.pallet_id.in_(
|
||||
select(BizPalletInventory.pallet_id).where(
|
||||
BizPalletInventory.flow_state.in_(states),
|
||||
batch_scope_condition(BizPalletInventory.test_batch_id, scope),
|
||||
)
|
||||
)
|
||||
if flow_group != 'unstocked':
|
||||
return in_states
|
||||
# 历史托盘可能缺少库存行,同样视为未入库
|
||||
return or_(
|
||||
in_states,
|
||||
BizPallet.pallet_id.not_in(
|
||||
select(BizPalletInventory.pallet_id).where(
|
||||
batch_scope_condition(BizPalletInventory.test_batch_id, scope)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_pallet(
|
||||
cls,
|
||||
|
||||
Reference in New Issue
Block a user