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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user