feat(finance): 统一内部 SKU 驱动的履约与财务分流
- 新增 internal_sku_mappings / internal_sku_channels,平台字段统一映射到内部中文 SKU - 下单入口按内部 SKU 渠道决策修正默认人工路由,自动发货补全 dash sku - 财务 channel_assign 读取统一渠道表;卡券/人工能力兜底,自动发货/接单要求真实载体 - carrier reconciliation 自动补自动发货任务和接单工单 - pending 落明确原因:无内部 SKU / 无渠道能力 / 无履约载体 - 合并、重判、旧 sku_code 规则清理、部署后 preflight 核对脚本 - 待分类队列按内部 SKU 聚合并展示原因
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
-- 118_internal_sku_channels.sql
|
||||
--
|
||||
-- 内部中文 SKU 的渠道决策单一真源。
|
||||
-- 从商品渠道能力 + 现有 internal_sku 全局规则回填;后续 intake/finance 都读这张表。
|
||||
|
||||
CREATE TABLE IF NOT EXISTS internal_sku_channels (
|
||||
internal_sku TEXT NOT NULL,
|
||||
channel TEXT NOT NULL CHECK (channel IN ('work_order', 'affiliate_dash', 'third_party_voucher', 'manual')),
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
is_fallback BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
canonical_product_id BIGINT REFERENCES product_catalog(id) ON DELETE SET NULL,
|
||||
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
source TEXT NOT NULL DEFAULT 'backfill',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (internal_sku, channel)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_internal_sku_channels_priority
|
||||
ON internal_sku_channels (internal_sku, priority, channel)
|
||||
WHERE enabled = TRUE;
|
||||
|
||||
COMMENT ON TABLE internal_sku_channels IS
|
||||
'内部中文 SKU 渠道能力:priority 越小越优先,is_fallback 表示兜底渠道';
|
||||
|
||||
WITH capability_source AS (
|
||||
SELECT
|
||||
btrim(p.canonical_name) AS internal_sku,
|
||||
c.channel,
|
||||
MIN(c.priority) AS priority,
|
||||
BOOL_AND(c.is_fallback) AS is_fallback,
|
||||
MIN(p.id) AS canonical_product_id
|
||||
FROM product_catalog p
|
||||
JOIN product_channel_capabilities c ON c.product_id = p.id AND c.enabled = TRUE
|
||||
WHERE p.status = 'active' AND btrim(p.canonical_name) <> ''
|
||||
GROUP BY 1, 2
|
||||
)
|
||||
INSERT INTO internal_sku_channels (
|
||||
internal_sku, channel, priority, is_fallback, canonical_product_id, source
|
||||
)
|
||||
SELECT internal_sku, channel, priority, is_fallback, canonical_product_id, 'capability'
|
||||
FROM capability_source
|
||||
ON CONFLICT (internal_sku, channel) DO UPDATE SET
|
||||
priority = LEAST(internal_sku_channels.priority, EXCLUDED.priority),
|
||||
is_fallback = internal_sku_channels.is_fallback AND EXCLUDED.is_fallback,
|
||||
canonical_product_id = COALESCE(internal_sku_channels.canonical_product_id, EXCLUDED.canonical_product_id),
|
||||
updated_at = NOW();
|
||||
|
||||
WITH rule_source AS (
|
||||
SELECT btrim(match_key) AS internal_sku, channel, MIN(priority) AS priority
|
||||
FROM sku_channel_rules
|
||||
WHERE match_key_type = 'internal_sku'
|
||||
AND provider = '' AND shop_id = ''
|
||||
AND enabled = TRUE
|
||||
AND btrim(match_key) <> ''
|
||||
GROUP BY 1, 2
|
||||
)
|
||||
INSERT INTO internal_sku_channels (
|
||||
internal_sku, channel, priority, is_fallback, canonical_product_id, source
|
||||
)
|
||||
SELECT internal_sku, channel, priority, FALSE, NULL, 'rule'
|
||||
FROM rule_source
|
||||
ON CONFLICT (internal_sku, channel) DO UPDATE SET
|
||||
priority = LEAST(internal_sku_channels.priority, EXCLUDED.priority),
|
||||
is_fallback = FALSE,
|
||||
updated_at = NOW();
|
||||
@@ -0,0 +1,14 @@
|
||||
-- 119_fulfillment_pending_reason.sql
|
||||
--
|
||||
-- 待分类不再是一个模糊状态:落明确原因,供队列/审计区分。
|
||||
-- no_internal_sku 没有内部中文 SKU
|
||||
-- no_channel_capability 有内部 SKU,但没有渠道能力/规则
|
||||
-- no_carrier 有渠道能力,但缺任务/工单载体
|
||||
|
||||
ALTER TABLE order_items
|
||||
ADD COLUMN IF NOT EXISTS fulfillment_pending_reason TEXT,
|
||||
ADD COLUMN IF NOT EXISTS fulfillment_pending_detail JSONB;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_order_items_pending_reason
|
||||
ON order_items (fulfillment_pending_reason)
|
||||
WHERE fulfillment_status = 'pending';
|
||||
Reference in New Issue
Block a user