import { listReceivingCandidates } from './inbound.js' import { assertReceivingWarehouse } from './receiving-adapter.js' import { listWarehouseTransferCandidates } from './warehouse-transfers.js' import { listReturnCountTasks } from './return-counting.js' import { listPackingPlans } from './return-packing.js' import { listDispatchTasks, listOutboundTasks, listCargoDiscardTasks } from './fulfillment.js' import { listRelabelCandidates } from './relabel.js' export const WORKBENCH_COUNT_KEYS = ['receiving', 'warehouseTransferList', 'warehouseTransferWork', 'returnCounting', 'returnPacking', 'dispatchPicking', 'outboundPicking', 'relabelReceive', 'relabelExecute', 'relabelReview'] const ready = (count, unit = '单') => ({ status: 'ready', count, unit }) const distinctCount = (rows, key) => { if (!Array.isArray(rows)) throw new Error('待办列表返回结构无效') const codes = rows.map((row) => String(row?.[key] || '').trim()) if (codes.some((code) => !code)) throw new Error('待办列表缺少业务编号') return new Set(codes).size } export async function loadWorkbenchCounts({ profile, activeTransferNo = '' }) { const sources = [ { keys: ['receiving'], load: async () => { const rows = await listReceivingCandidates() rows.forEach((row) => assertReceivingWarehouse(row, profile, '待收货柜')) if (rows.some((row) => !row.sourceContainerBookingId || !row.containerNo)) throw new Error('待收货柜缺少业务定位信息') const keys = rows.map((row) => ({ key: `${row.sourceContainerBookingId}:${row.containerNo}` })) return { receiving: ready(distinctCount(keys, 'key'), '柜') } } }, { keys: ['warehouseTransferList', 'warehouseTransferWork'], load: async () => { const rows = (await listWarehouseTransferCandidates()).filter((row) => ['WAIT_PDA_RECEIVING', 'PARTIALLY_RECEIVED'].includes(row.status)) const active = rows.filter((row) => String(row.transferNo).trim() === String(activeTransferNo).trim()) return { warehouseTransferList: ready(distinctCount(rows, 'transferNo')), warehouseTransferWork: ready(distinctCount(active, 'transferNo'), '单进行中') } } }, { keys: ['returnCounting'], load: async () => { const value = await listReturnCountTasks({ page: 1, size: 1 }) const total = value?.total if ((typeof total !== 'number' && !(typeof total === 'string' && /^\d+$/.test(total))) || !Number.isSafeInteger(Number(total)) || Number(total) < 0) throw new Error('退货清点待办总数无效') return { returnCounting: ready(Number(total), '托') } } }, { keys: ['returnPacking'], load: async () => ({ returnPacking: ready(distinctCount((await listPackingPlans()).filter((row) => row.status === 'READY'), 'planNo'), '单计划') }) }, { keys: ['dispatchPicking'], load: async () => { const rows = (await listDispatchTasks()).filter((row) => row.status !== 'CANCELLED' && ['BEFORE_LOADING', 'LOADING', 'AFTER_LOADING'].includes(row.stage)) return { dispatchPicking: ready(distinctCount(rows, 'dispatchNo'), '单未发车') } } }, { keys: ['outboundPicking'], load: async () => { const [outbounds, discards] = await Promise.all([listOutboundTasks('PENDING'), listCargoDiscardTasks()]) return { outboundPicking: { ...ready(distinctCount(outbounds, 'taskNo')), discardCount: distinctCount(discards.filter((row) => row.status !== 'COMPLETED'), 'discardNo') } } } }, { keys: ['relabelReceive', 'relabelExecute', 'relabelReview'], load: async () => { const rows = await listRelabelCandidates() return { relabelReceive: ready(distinctCount(rows.filter((row) => !row.taskStatus || row.taskStatus === 'AVAILABLE'), 'relabelNo'), '单待接'), relabelExecute: ready(distinctCount(rows.filter((row) => row.taskStatus === 'PROCESSING' && row.claimedByCurrentOperator === true), 'relabelNo'), '单进行中'), relabelReview: ready(distinctCount(rows.filter((row) => row.taskStatus === 'WAIT_REVIEW'), 'relabelNo'), '单待复核') } } } ] const results = await Promise.allSettled(sources.map((source) => source.load())) return Object.assign({}, ...results.map((result, index) => result.status === 'fulfilled' ? result.value : Object.fromEntries(sources[index].keys.map((key) => [key, { status: 'error', error: result.reason }])))) } export function workbenchBadge(key, value) { if (!WORKBENCH_COUNT_KEYS.includes(key)) return { label: key.endsWith('Query') ? '扫码查询' : key === 'returnReceipt' ? '扫码签收' : '扫码作业', tone: 'is-static' } if (!value) return { label: '加载中…', tone: 'is-loading' } if (value.status === 'error') return { label: '获取失败', tone: 'is-error' } const hasWork = value.count > 0 || value.discardCount > 0 let label = `${value.count} ${value.unit}` if (key === 'warehouseTransferWork' && value.count === 0) label = '未开始' if (key === 'outboundPicking' && value.discardCount > 0) label = `${value.count} 出库 · ${value.discardCount} 废弃` return { label, tone: hasWork ? 'has-work' : 'is-empty' } }