feat(finance): 补齐自动发货与接单平台成本和对账

自动发货:价格导入支持 --effective-from,把首次基线版本回填到历史起点;41,676 个订单项全部归一并生成采购成本快照,缺失 0。\n\n接单平台:新增按工单 product_rule_id 回填商品的接口与按钮,长尾项按 provider+shop_id+sku_code+normalized_sku_name 补建 wo: 商品和 sku_code_name 渠道映射;118,053 个订单项全部归一。\n\n成本:已验收工单按 reward_amount 写 actual;未验收但已确定报酬的工单写 estimated,验收后升级。全量 95,870 actual + 21,217 estimated,actual 与 reward 0 差异。\n\n剩余 966 条取消/零报酬工单保留在成本异常队列,不自动记 0;人工块未改动。\n\n实测自动发货与接单平台未归一收入均为 0;整体未归一 79.67% 降至 57.54%,全部来自人工块。
This commit is contained in:
yml2213
2026-10-22 18:59:53 +08:00
parent 54d2eb2f59
commit 72e1141c8a
9 changed files with 566 additions and 57 deletions
@@ -43,6 +43,7 @@ import {
backfillAdminAffiliateDashSkus,
backfillAdminNormalizedQuantities,
backfillAdminProductMappings,
backfillAdminWorkOrderProductSkus,
createAdminChannelProductMapping,
createAdminCostItem,
createAdminCostSnapshot,
@@ -368,6 +369,7 @@ export default function AdminCostCenterPage() {
const [bootstrapApplying, setBootstrapApplying] = useState(false)
const [syncingAffiliate, setSyncingAffiliate] = useState(false)
const [backfillingAffiliateSkus, setBackfillingAffiliateSkus] = useState(false)
const [backfillingWorkOrderSkus, setBackfillingWorkOrderSkus] = useState(false)
const versionScopeType = Form.useWatch('scopeType', versionForm)
const versionCostItemId = Form.useWatch('costItemId', versionForm)
@@ -694,6 +696,33 @@ export default function AdminCostCenterPage() {
}
}
async function backfillWorkOrderProducts() {
setBackfillingWorkOrderSkus(true)
try {
const response = await backfillAdminWorkOrderProductSkus()
const result = response.data
const factsText = result.factsRebuild
? `;已重算 ${result.factsRebuild.fromDate} ~ ${result.factsRebuild.toDate} 的销售事实`
: ''
const remainingText =
result.remainingCount > 0
? `;仍有 ${result.remainingCount} 个订单项没有可用的工单模板`
: ''
const tailText =
result.tailUpdatedCount > 0
? `,其中长尾商品 ${result.tailUpdatedCount} 个(新建商品 ${result.tailProductCount}、映射 ${result.tailMappingCount})`
: ''
message.success(
`已按工单模板回填 ${result.updatedCount} 个订单项${tailText},${result.skippedCount} 个已一致${factsText}${remainingText}`,
)
refreshAll()
} catch (error) {
message.error(error instanceof Error ? error.message : '回填接单平台商品失败')
} finally {
setBackfillingWorkOrderSkus(false)
}
}
function openEditProduct(item: ProductCatalogItem) {
setEditingProduct(item)
productForm.setFieldsValue({
@@ -1731,13 +1760,22 @@ export default function AdminCostCenterPage() {
{canConfig ? (
<>
{activeCarrier === 'all' || activeCarrier === 'work_order' ? (
<Button
icon={<SyncOutlined />}
loading={bootstrapPreviewing || bootstrapApplying}
onClick={runTemplateBootstrap}
>
从接单模板初始化
</Button>
<>
<Button
icon={<SyncOutlined />}
loading={bootstrapPreviewing || bootstrapApplying}
onClick={runTemplateBootstrap}
>
从接单模板初始化
</Button>
<Button
icon={<SyncOutlined />}
loading={backfillingWorkOrderSkus}
onClick={() => void backfillWorkOrderProducts()}
>
按工单回填商品
</Button>
</>
) : null}
{activeCarrier === 'all' || activeCarrier === 'affiliate_dash' ? (
<Button
@@ -38,6 +38,27 @@ export function applyAdminTemplateProductBootstrap() {
)
}
export function backfillAdminWorkOrderProductSkus() {
return apiPost<{
updatedCount: number
skippedCount: number
remainingCount: number
workOrderItemCount: number
matchedCount: number
ruleMatchedCount: number
tailUpdatedCount: number
tailProductCount: number
tailMappingCount: number
factsRebuild: {
fromDate: string
toDate: string
deletedCount: number
insertedCount: number
batchCount: number
} | null
}>('/api/v1/admin/cost-center/work-order-products/backfill')
}
export function fetchAdminProductCatalog(params?: Record<string, unknown>) {
return apiGet<ProductCatalogListResult>('/api/v1/admin/cost-center/products', params)
}