| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- import type { PlanFormProductRow } from '@/composables/usePlContractToPlan'
- import type { PlanStopProductFormRow } from '@/utils/pl-plan-stop-form'
- export interface ArchiveBrandViewRow {
- brandId: string
- brandName: string
- qty: number
- price?: number
- amount?: number
- }
- export interface ArchiveBrandViewItem {
- key: string
- productName: string
- unit?: string
- planQty?: number
- segmentQty: number
- brandMode?: string
- rows: ArchiveBrandViewRow[]
- }
- function getSegmentQty(
- row: PlanStopProductFormRow,
- mode: 'inbound' | 'outbound',
- planRow?: PlanFormProductRow
- ): number {
- const primary = mode === 'inbound' ? row.inboundQty : row.outboundQty
- if (primary != null && Number.isFinite(Number(primary))) return Number(primary)
- const fallback = mode === 'inbound' ? planRow?.inboundQty : planRow?.outboundQty
- if (fallback != null && Number.isFinite(Number(fallback))) return Number(fallback)
- return 0
- }
- function getSegmentPrice(
- row: PlanStopProductFormRow,
- mode: 'inbound' | 'outbound',
- planRow?: PlanFormProductRow
- ): number | undefined {
- const primary = mode === 'inbound' ? row.inboundPrice : row.outboundPrice
- if (primary != null && Number.isFinite(Number(primary))) return Number(primary)
- const fallback = mode === 'inbound' ? planRow?.inboundPrice : planRow?.outboundPrice
- if (fallback != null && Number.isFinite(Number(fallback))) return Number(fallback)
- if (planRow?.taxPrice != null && Number.isFinite(Number(planRow.taxPrice))) {
- return Number(planRow.taxPrice)
- }
- return undefined
- }
- function getPlanBrandRows(planRow?: PlanFormProductRow) {
- return (planRow?.brandQtyRows || []).filter((row) => Number(row.qty) > 0)
- }
- function getStopBrandRows(stopRow: PlanStopProductFormRow) {
- return (stopRow.brandQtys || []).filter((row) => Number(row.qty) > 0)
- }
- function getAllocationMap(
- allocations: Map<string, Map<string, number>>,
- planProductId: string
- ): Map<string, number> {
- let map = allocations.get(planProductId)
- if (!map) {
- map = new Map()
- allocations.set(planProductId, map)
- }
- return map
- }
- /** 按段顺序贪心分配计划级品牌量(旧数据无段级品牌明细时回退) */
- function allocateBrandRows(
- brandRows: Array<{ brandId: string; brandName: string; qty: number }>,
- segmentQty: number,
- price: number | undefined,
- allocated: Map<string, number>
- ): ArchiveBrandViewRow[] {
- if (segmentQty <= 0 || !brandRows.length) return []
- let remaining = segmentQty
- const result: ArchiveBrandViewRow[] = []
- for (const brand of brandRows) {
- const total = Number(brand.qty || 0)
- if (total <= 0) continue
- const used = allocated.get(brand.brandId) || 0
- const available = total - used
- if (available <= 0.0005) continue
- const take = Math.min(available, remaining)
- if (take <= 0.0005) continue
- allocated.set(brand.brandId, used + take)
- remaining -= take
- result.push({
- brandId: brand.brandId,
- brandName: brand.brandName,
- qty: take,
- price,
- amount: price != null ? take * price : undefined
- })
- if (remaining <= 0.0005) break
- }
- return result
- }
- function buildRowsFromStopBrandQtys(
- stopRow: PlanStopProductFormRow,
- fallbackPrice: number | undefined
- ): ArchiveBrandViewRow[] {
- return getStopBrandRows(stopRow).map((brand) => {
- const qty = Number(brand.qty || 0)
- const price =
- brand.unitPrice != null && Number.isFinite(Number(brand.unitPrice))
- ? Number(brand.unitPrice)
- : fallbackPrice
- return {
- brandId: String(brand.brandId || ''),
- brandName: String(brand.brandName || ''),
- qty,
- price,
- amount: price != null ? qty * price : undefined
- }
- })
- }
- export function buildArchiveBrandViewItems(
- stopProducts: PlanStopProductFormRow[],
- planProducts: PlanFormProductRow[],
- mode: 'inbound' | 'outbound',
- allocations: Map<string, Map<string, number>>
- ): ArchiveBrandViewItem[] {
- const planById = new Map(planProducts.filter((row) => row.id).map((row) => [String(row.id), row]))
- const planByProductId = new Map(planProducts.map((row) => [String(row.productId), row]))
- const items: ArchiveBrandViewItem[] = []
- for (const stopRow of stopProducts) {
- const planRow =
- (stopRow.planProductId ? planById.get(String(stopRow.planProductId)) : undefined) ||
- planByProductId.get(String(stopRow.productId))
- const brandMode = stopRow.brandMode || planRow?.brandMode
- if (brandMode !== 'any' && brandMode !== 'required') continue
- const planProductId = String(stopRow.planProductId || planRow?.id || '').trim()
- if (!planProductId) continue
- const segmentQty = getSegmentQty(stopRow, mode, planRow)
- if (segmentQty <= 0) continue
- const price = getSegmentPrice(stopRow, mode, planRow)
- // 优先使用段级归档时填写的品牌数量
- let rows = buildRowsFromStopBrandQtys(stopRow, price)
- if (!rows.length) {
- const brandRows = getPlanBrandRows(planRow)
- if (!brandRows.length) continue
- const allocated = getAllocationMap(allocations, planProductId)
- rows = allocateBrandRows(brandRows, segmentQty, price, allocated)
- }
- if (!rows.length) continue
- items.push({
- key: `${planProductId}-${stopRow.id || stopRow.productId}`,
- productName:
- stopRow.standardProductName ||
- stopRow.productName ||
- planRow?.standardProductName ||
- planRow?.productName ||
- '—',
- unit: stopRow.unit || planRow?.unit,
- planQty: stopRow.planQty ?? planRow?.quantity,
- segmentQty,
- brandMode,
- rows
- })
- }
- return items
- }
- export function buildArchiveBrandViewsBySegment(input: {
- loadProducts: PlanStopProductFormRow[]
- deliveries: Array<{ key: string; products: PlanStopProductFormRow[] }>
- planProducts: PlanFormProductRow[]
- }): Map<string, ArchiveBrandViewItem[]> {
- const result = new Map<string, ArchiveBrandViewItem[]>()
- // 装货(入库)与送货(出库)各自独立分配池,互不占位(仅旧数据回退贪心时使用)
- const inboundAllocations = new Map<string, Map<string, number>>()
- const outboundAllocations = new Map<string, Map<string, number>>()
- result.set(
- 'load',
- buildArchiveBrandViewItems(
- input.loadProducts,
- input.planProducts,
- 'inbound',
- inboundAllocations
- )
- )
- for (const delivery of input.deliveries) {
- result.set(
- delivery.key,
- buildArchiveBrandViewItems(
- delivery.products,
- input.planProducts,
- 'outbound',
- outboundAllocations
- )
- )
- }
- return result
- }
- export function formatArchiveBrandAmount(qty?: number, price?: number): string {
- if (qty == null || price == null) return '—'
- const amount = Number(qty) * Number(price)
- return Number.isFinite(amount) ? amount.toFixed(2) : '—'
- }
|