import type { FinanceCostEntryState } from '../../types'; /** * 文件职责:成本录入分类与明细本地编辑动作。 */ import type { FinanceCostCategoryCode, FinanceCostEntryDetailDto, } from '#/api/finance/cost'; import { roundAmount, sumAllCategoryTotal, sumCategoryItems } from './helpers'; interface EntryActionOptions { entry: FinanceCostEntryState; } /** 创建录入区编辑动作。 */ export function createEntryActions(options: EntryActionOptions) { function toggleCategoryExpanded(category: FinanceCostCategoryCode) { const target = options.entry.categories.find( (item) => item.category === category, ); if (!target) return; target.expanded = !target.expanded; } function setCategoryTotal(category: FinanceCostCategoryCode, value: number) { const target = options.entry.categories.find( (item) => item.category === category, ); if (!target) return; target.totalAmount = Math.max(0, roundAmount(value)); syncEntrySummary(); } function upsertDetailItem( category: FinanceCostCategoryCode, detail: FinanceCostEntryDetailDto, mode: 'create' | 'edit', ) { const target = options.entry.categories.find( (item) => item.category === category, ); if (!target) return; const nextItem: FinanceCostEntryDetailDto = { ...detail, itemId: detail.itemId || createTempItemId(), amount: resolveDetailAmount(category, detail), quantity: detail.quantity === undefined ? undefined : roundAmount(detail.quantity), unitPrice: detail.unitPrice === undefined ? undefined : roundAmount(detail.unitPrice), sortOrder: detail.sortOrder > 0 ? detail.sortOrder : 1, }; const index = target.items.findIndex( (item) => item.itemId === detail.itemId, ); if (mode === 'edit' && index !== -1) { target.items.splice(index, 1, nextItem); } else { target.items.push(nextItem); } target.items.sort((left, right) => left.sortOrder - right.sortOrder); target.totalAmount = sumCategoryItems(target); syncEntrySummary(); } function removeDetailItem( category: FinanceCostCategoryCode, itemId: string | undefined, ) { const target = options.entry.categories.find( (item) => item.category === category, ); if (!target || !itemId) return; target.items = target.items.filter((item) => item.itemId !== itemId); target.totalAmount = sumCategoryItems(target); syncEntrySummary(); } function syncEntrySummary() { const totalCost = sumAllCategoryTotal(options.entry.categories); options.entry.totalCost = totalCost; options.entry.costRate = options.entry.monthRevenue > 0 ? roundAmount((totalCost / options.entry.monthRevenue) * 100) : 0; } return { removeDetailItem, setCategoryTotal, syncEntrySummary, toggleCategoryExpanded, upsertDetailItem, }; } function resolveDetailAmount( category: FinanceCostCategoryCode, detail: FinanceCostEntryDetailDto, ) { if (category !== 'labor') { return Math.max(0, roundAmount(detail.amount)); } const quantity = roundAmount(detail.quantity ?? 0); const unitPrice = roundAmount(detail.unitPrice ?? 0); return Math.max(0, roundAmount(quantity * unitPrice)); } function createTempItemId() { return `tmp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; }