feat(cost-center): 成本项编码改系统自动生成,补自动发货价格导入脚本

- 新建成本项目不再手填编码:后端按成本类型生成基础编码(auto_purchase /
  work_order_reward / manual_service / platform_fee / payment_fee /
  after_sales_loss / shipping_other / other_cost),重复时追加 _2、_3;
  接口显式传 code 时仍优先使用,兼容脚本导入
- 前端移除编码输入框,改为说明提示,创建成功提示展示生成的编码
- 新增 scripts/sync-affiliate-dash-prices.ts + scripts/data/affiliate-dash-prices.json:
  按上游 38 条自动发货价格补建 aff:<sku> 商品,并按 cost_amount(分)生成
  scope=product 的按件成本版本;价格变化时停用旧版本再插新版本
- 自动发货商品同步与价格导入的 attributes_json 改为合并,避免互相覆盖
This commit is contained in:
yml2213
2026-10-22 15:07:29 +08:00
parent 0f7d9d4fad
commit 78580c6165
5 changed files with 297 additions and 22 deletions
@@ -25,6 +25,17 @@ const COST_TYPES = new Set([
'shipping_other',
'other',
])
/** 成本项自动编码的基础名:按成本类型生成,重复时追加 _2、_3。 */
const COST_ITEM_CODE_BASE: Record<string, string> = {
auto_purchase: 'auto_purchase',
work_order_reward: 'work_order_reward',
manual_service: 'manual_service',
platform_fee: 'platform_fee',
payment_fee: 'payment_fee',
after_sales_loss: 'after_sales_loss',
shipping_other: 'shipping_other',
other: 'other_cost',
}
const CARRIER_TYPES = new Set(['affiliate_dash', 'work_order', 'manual', 'shared'])
const PRICING_MODES = new Set([
'per_item',
@@ -1498,7 +1509,7 @@ export async function syncAdminAffiliateDashProducts() {
SET carrier_type = 'affiliate_dash',
canonical_name = EXCLUDED.canonical_name,
unit_name = EXCLUDED.unit_name,
attributes_json = EXCLUDED.attributes_json,
attributes_json = product_catalog.attributes_json || EXCLUDED.attributes_json,
updated_at = $1
RETURNING (xmax = 0) AS inserted
`,
@@ -2161,11 +2172,12 @@ export async function listAdminCostItems(payload: JsonObject = {}) {
}
export async function createAdminCostItem(payload: JsonObject, _session: AdminSession) {
const code = normalizeRequiredText(payload.code, '成本项编码', 64)
const name = normalizeRequiredText(payload.name, '成本项名称', 120)
const costType = normalizeStatus(payload.costType, COST_TYPES, null, '成本类型无效')
const carrierType = normalizeStatus(payload.carrierType, CARRIER_TYPES, null, '成本载体无效')
const pricingMode = normalizeStatus(payload.pricingMode, PRICING_MODES, null, '计价方式无效')
// 编码由系统生成(按成本类型 + 冲突序号);显式传入时仍优先使用,兼容脚本导入。
const code = normalizeOptionalText(payload.code, 64) || (await generateCostItemCode(costType))
const unitName = normalizeOptionalText(payload.unitName, 16) || '件'
const currency = normalizeOptionalText(payload.currency, 8) || 'CNY'
const status = normalizeStatus(payload.status, PRODUCT_STATUSES, 'active', '成本项状态无效')
@@ -2193,6 +2205,22 @@ export async function createAdminCostItem(payload: JsonObject, _session: AdminSe
return { costItem: mapCostItem(inserted) }
}
async function generateCostItemCode(costType: string) {
const base = COST_ITEM_CODE_BASE[costType] || 'cost_item'
let candidate = base
let index = 2
for (let attempt = 0; attempt < 100; attempt += 1) {
const existing = await query<{ id: number }>(
'SELECT id FROM cost_items WHERE code = $1 LIMIT 1',
[candidate],
)
if (!existing.rows[0]) return candidate
candidate = `${base}_${index}`
index += 1
}
return `${base}_${Date.now()}`
}
export async function updateAdminCostItem(costItemId: number | string, payload: JsonObject) {
const id = normalizeId(costItemId, '成本项 ID')
const current = await getCostItemRow(id)