统一作业列表与待办统计并更新废弃扫码
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
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' }
|
||||
}
|
||||
Reference in New Issue
Block a user