Merge remote-tracking branch 'origin/codex/xuxiake-mainline' into codex/wuyongjie-bom-cost-dynamic-20260807
This commit is contained in:
commit
81b1453db5
@ -1,5 +1,6 @@
|
||||
import {
|
||||
calculateOaBoardOverallActualDays,
|
||||
calculateOaBoardPricingFlowActualDays,
|
||||
getRunningTasks,
|
||||
type OaBoardLifecycleFlowRecord,
|
||||
} from '../oa-board-sync.service'
|
||||
@ -71,3 +72,39 @@ describe('OA 看板当前任务选择', () => {
|
||||
expect(tasks[0]?.taskId).toBe('new-running-task')
|
||||
})
|
||||
})
|
||||
describe('OA 看板限价时效', () => {
|
||||
it('限价子流程尚未发起时,从首单和美术并行流程最后完成时间开始计算', () => {
|
||||
const actualDays = calculateOaBoardPricingFlowActualDays(
|
||||
[
|
||||
{
|
||||
flowType: 'review',
|
||||
status: 'COMPLETED',
|
||||
result: 'AGREE',
|
||||
fieldSnapshots: [
|
||||
{
|
||||
fieldName: '是否需要视觉出图',
|
||||
fieldValue: '视觉出图',
|
||||
fieldExtValue: null,
|
||||
normalizedValue: '视觉出图',
|
||||
metadata: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
flowType: 'order',
|
||||
status: 'COMPLETED',
|
||||
finishedAtSource: new Date('2026-08-01T05:00:00.000Z'),
|
||||
},
|
||||
{
|
||||
flowType: 'art',
|
||||
status: 'COMPLETED',
|
||||
finishedAtSource: new Date('2026-08-02T05:00:00.000Z'),
|
||||
},
|
||||
] as never,
|
||||
'限价-发起',
|
||||
new Date('2026-08-04T05:00:00.000Z'),
|
||||
)
|
||||
|
||||
expect(actualDays).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1984,6 +1984,39 @@ function pickFlowActualDays(records: NormalizedFlowRecord[], flowType: OaBoardFl
|
||||
return Number(Math.max(...values).toFixed(2))
|
||||
}
|
||||
|
||||
export function calculateOaBoardPricingFlowActualDays(
|
||||
records: NormalizedFlowRecord[],
|
||||
pendingStageName: string | null,
|
||||
now = new Date(),
|
||||
) {
|
||||
const pricingActual = pickFlowActualDays(records, 'pricing')
|
||||
if (pricingActual !== null) return pricingActual
|
||||
|
||||
// 限价子流程尚未发起时,待发起阶段已由前序流程完成触发。
|
||||
// 该阶段从最后一个必经的并行流程完成时开始计时,直到真正发起限价。
|
||||
if (pendingStageName !== '限价-发起') return null
|
||||
|
||||
const reviewRecord = records.find((record) => record.flowType === 'review') ?? null
|
||||
const orderRecord = records.find((record) => record.flowType === 'order') ?? null
|
||||
const artRecord = records.find((record) => record.flowType === 'art') ?? null
|
||||
const orderFinishedAt =
|
||||
getFlowLifecycleStatus(orderRecord) === 'COMPLETED' ? orderRecord?.finishedAtSource ?? null : null
|
||||
if (!orderFinishedAt) return null
|
||||
|
||||
const artFinishedAt = isVisualDesignRequired(reviewRecord)
|
||||
? getFlowLifecycleStatus(artRecord) === 'COMPLETED'
|
||||
? artRecord?.finishedAtSource ?? null
|
||||
: null
|
||||
: null
|
||||
if (isVisualDesignRequired(reviewRecord) && !artFinishedAt) return null
|
||||
|
||||
const startedAt = [orderFinishedAt, artFinishedAt]
|
||||
.filter((value): value is Date => Boolean(value))
|
||||
.sort((left, right) => right.getTime() - left.getTime())[0]
|
||||
|
||||
return startedAt ? calculateHours(startedAt, null, now) : null
|
||||
}
|
||||
|
||||
function buildTimelineMetrics(
|
||||
records: NormalizedFlowRecord[],
|
||||
productType: string | null,
|
||||
@ -2066,7 +2099,7 @@ function buildTimelineMetrics(
|
||||
pricingParallelActual,
|
||||
])
|
||||
const artActual = pickFlowActualDays(records, 'art')
|
||||
const pricingFlowActual = pickFlowActualDays(records, 'pricing')
|
||||
const pricingFlowActual = calculateOaBoardPricingFlowActualDays(records, pendingStageName)
|
||||
const listingActual = pickFlowActualDays(records, 'main')
|
||||
const definitions: TimelineMetricDefinition[] = [
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user