商品成本价与调价单开发计划.md 11.6 KB

商品成本价体系与调价单开发计划

现有基础:前端 wtPriceAdjust/index.vue + Form.vue(骨架页面),后端和数据库均不存在

一、背景与目标

现状问题

  1. 没有统一的成本价存储:当前成本价只能从最近一次采购入库单的单价推算,不准确
  2. 同一商品不同仓库成本不同:如"卡带 开心镇"在莱迪仓平均成本 42.92,景枫仓 111.00
  3. 出库时不记录成本价wt_xsckd_mx 表只有售价(dj),无法计算毛利
  4. 拆装单成本价精度丢失wt_czdmx.cbdj 字段是 decimal(50,0),没有小数位
  5. 缺少调价单功能:无法对在库商品进行成本调整

目标

建立完整的加权平均成本价体系,支持:采购入库自动算成本 → 调价单手动调整 → 出库时记录快照 → 拆装单/调拨单联动


二、数据库变更

2.1 新建表:wt_sp_cost(商品仓库成本表)

核心表,按「商品+仓库」维度存储当前加权平均成本价

CREATE TABLE wt_sp_cost (
    F_Id         VARCHAR(50)    NOT NULL COMMENT '主键',
    spbh         VARCHAR(50)    NOT NULL COMMENT '商品编号',
    ck           VARCHAR(50)    NOT NULL COMMENT '仓库ID',
    cbj          DECIMAL(18,2)  DEFAULT 0.00 COMMENT '当前加权平均成本价',
    sl           INT            DEFAULT 0 COMMENT '当前在库数量',
    update_time  DATETIME       DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
    PRIMARY KEY (F_Id),
    UNIQUE KEY uk_sp_ck (spbh, ck)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品仓库成本表';

2.2 新建表:wt_tjd(调价单主表)

CREATE TABLE wt_tjd (
    F_Id         VARCHAR(50)    NOT NULL COMMENT '单据编号(TJD+日期+序号)',
    djrq         DATETIME       COMMENT '单据日期',
    ck           VARCHAR(50)    COMMENT '仓库(选仓库加载全部商品)',
    jsr          VARCHAR(50)    COMMENT '经手人',
    zdr          VARCHAR(50)    COMMENT '制单人',
    djzt         VARCHAR(50)    DEFAULT '草稿' COMMENT '单据状态(草稿/已审核)',
    bz           TEXT           COMMENT '备注',
    PRIMARY KEY (F_Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品调价单';

2.3 新建表:wt_tjd_mx(调价单明细)

CREATE TABLE wt_tjd_mx (
    F_Id         VARCHAR(50)    NOT NULL COMMENT '主键',
    djbh         VARCHAR(50)    NOT NULL COMMENT '单据编号(FK→wt_tjd)',
    ck           VARCHAR(50)    COMMENT '仓库',
    spbh         VARCHAR(50)    COMMENT '商品编号',
    spmc         VARCHAR(200)   COMMENT '商品名称',
    dw           VARCHAR(50)    COMMENT '单位',
    sl           INT            DEFAULT 0 COMMENT '数量(在库数量,自动带出)',
    tqdj         DECIMAL(18,2)  DEFAULT 0.00 COMMENT '调前单价(自动带出)',
    tjbl         DECIMAL(8,4)   DEFAULT 0.00 COMMENT '调价比率%',
    thdj         DECIMAL(18,2)  DEFAULT 0.00 COMMENT '调后单价',
    tqje         DECIMAL(18,2)  DEFAULT 0.00 COMMENT '调前金额(sl×tqdj)',
    thje         DECIMAL(18,2)  DEFAULT 0.00 COMMENT '调后金额(sl×thdj)',
    bz           VARCHAR(500)   COMMENT '备注',
    PRIMARY KEY (F_Id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品调价单明细';

2.4 修改表:wt_xsckd_mx(出库单明细加成本价字段)

出库时保存成本价快照,用于后续毛利计算

ALTER TABLE wt_xsckd_mx 
    ADD COLUMN cbdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本单价(出库时快照)',
    ADD COLUMN cbje DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本金额(出库时快照)';

2.5 修改表:wt_czdmx + wt_czdmx_2(修复精度)

当前 cbdj/cbje 是 decimal(50,0) 没有小数位,需要修复

ALTER TABLE wt_czdmx MODIFY COLUMN cbdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本单价';
ALTER TABLE wt_czdmx MODIFY COLUMN cbje DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本金额';
ALTER TABLE wt_czdmx_2 MODIFY COLUMN cbdj DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本单价';
ALTER TABLE wt_czdmx_2 MODIFY COLUMN cbje DECIMAL(18,2) DEFAULT 0.00 COMMENT '成本金额';

2.6 初始化 wt_sp_cost 数据

根据当前在库序列号的批次入库价计算加权平均成本

INSERT INTO wt_sp_cost (F_Id, spbh, ck, cbj, sl, update_time)
SELECT 
    CONCAT(sub.spbh, '_', sub.in_warehouse) AS F_Id,
    sub.spbh,
    sub.in_warehouse AS ck,
    ROUND(SUM(IFNULL(mx.dj, 0) * sub.qty) / NULLIF(SUM(sub.qty), 0), 2) AS cbj,
    SUM(sub.qty) AS sl,
    NOW() AS update_time
FROM (
    SELECT sn.spbh, sn.in_warehouse, sn.in_djbh, COUNT(*) AS qty
    FROM wt_serial_number sn
    WHERE sn.status = 0
    GROUP BY sn.spbh, sn.in_warehouse, sn.in_djbh
) sub
LEFT JOIN wt_xsckd_mx mx ON sub.in_djbh = mx.djbh AND sub.spbh = mx.spbh
GROUP BY sub.spbh, sub.in_warehouse;

三、业务逻辑变更

3.1 成本价读取(统一入口)

所有需要取成本价的地方,统一从 wt_sp_cost 读取:

GetProductCost(商品ID, 仓库ID)
  → SELECT cbj FROM wt_sp_cost WHERE spbh = 商品ID AND ck = 仓库ID
  → 如果没记录,兜底取最近采购入库单价 → 取零售价 → 返回 0

影响范围:拆装单(WtCzdService)、出库单(WtXsckdService)、调价单(新)

3.2 采购入库时更新成本(WtXsckdService.Create)

当 djlx = "采购入库单" 时,保存后自动更新 wt_sp_cost

对每个明细行:
  旧记录 = SELECT cbj, sl FROM wt_sp_cost WHERE spbh AND ck
  如果存在:
    新成本 = (旧sl × 旧cbj + 入库数 × 入库单价) ÷ (旧sl + 入库数)
    UPDATE wt_sp_cost SET cbj = 新成本, sl = 旧sl + 入库数
  如果不存在:
    INSERT wt_sp_cost (spbh, ck, cbj, sl) VALUES (商品, 仓库, 入库单价, 入库数)

3.3 销售出库时记录成本快照(WtXsckdService.Create)

当 djlx = "销售出库单" 时

对每个明细行:
  cbdj = SELECT cbj FROM wt_sp_cost WHERE spbh AND ck
  cbje = cbdj × 数量
  保存到 wt_xsckd_mx.cbdj, wt_xsckd_mx.cbje
  UPDATE wt_sp_cost SET sl = sl - 出库数 WHERE spbh AND ck

3.4 调价单审核时更新成本(WtTjdService,新建)

对每个明细行:
  UPDATE wt_sp_cost SET cbj = 调后单价 WHERE spbh AND ck

3.5 拆装单保存时联动(WtCzdService.Create)

出货明细:
  cbdj 从 wt_sp_cost 取出货仓库的成本价
  UPDATE wt_sp_cost SET sl = sl - 出库数 WHERE 出货仓库

入货明细:
  cbdj = 出货总成本金额 ÷ 入货总数量(分摊成本)
  UPSERT wt_sp_cost SET cbj = 加权后新成本, sl = sl + 入库数 WHERE 入货仓库

3.6 调拨单联动

出仓成本不变(从 wt_sp_cost 取)
入仓按出仓成本入库(加权平均计入)

3.7 采购退货 / 销售退货

退货入库:按原出库成本价回入(不改变成本单价,只增加数量和总成本)
退货出库:减少数量,成本单价不变

四、后端开发任务

4.1 数据库与 Entity

任务 说明
执行 SQL 建表/改表 2.1~2.6 的 SQL 语句
WtSpCostEntity 映射 wt_sp_cost
WtTjdEntity 映射 wt_tjd
WtTjdMxEntity 映射 wt_tjd_mx
WtXsckdMxEntity 加字段 加 Cbdj、Cbje 属性
WtCzdmxEntity 修复精度 cbdj/cbje 改为 decimal

4.2 调价单 Service(WtTjdService,新建)

接口 方法 说明
GET /api/Extend/WtTjd/{id} GetInfo 获取调价单详情
GET /api/Extend/WtTjd GetList 列表查询(分页)
POST /api/Extend/WtTjd Create 新建调价单
PUT /api/Extend/WtTjd/{id} Update 修改调价单
DELETE /api/Extend/WtTjd/{id} Delete 删除调价单
POST /api/Extend/WtTjd/Actions/Audit/{id} Audit 审核(更新 wt_sp_cost)
GET /api/Extend/WtTjd/Actions/LoadProducts LoadProducts 按仓库加载商品+成本价+库存

4.3 成本价 Service(WtSpCostService,新建或合并)

接口 说明
GET /api/Extend/WtSpCost/GetCost?spbh=&ck= 查询单个商品在指定仓库的成本价
POST /api/Extend/WtSpCost/RecalcAll 重算所有成本(基于当前库存数据)

4.4 改造现有 Service

Service 改造内容
WtXsckdService.Create 采购入库:更新 wt_sp_cost 加权平均
销售出库:快照 cbdj/cbje 到明细
WtCzdService.Create 出货:快照 cbdj 从 wt_sp_cost 取
入货:按出货总成本分摊
WtCzdService.GetProductCost 改为从 wt_sp_cost 读取

五、前端开发任务

5.1 调价单页面(新建)

列表页 views/wtTjd/index.vue

  • 搜索条件:单据编号、仓库、日期范围、状态
  • 列表字段:单据编号、单据日期、仓库、经手人、状态、操作(编辑/审核/删除)

表单页 views/wtTjd/Form.vue

  • 主表:单据编号(自动生成 TJD+日期+序号)、单据日期、仓库(下拉选择)、经手人、备注
  • 明细表(el-table 内嵌编辑):
说明 交互
仓库 自动带出(跟主表仓库) 只读
商品编码 选择或仓库加载 可选/只读
商品名称 自动带出 只读
单位 自动带出 只读
数量 在库数量 只读
调前单价 自动从 wt_sp_cost 带出 只读
调价比率% 用户输入 可编辑,变化 → 自动算调后单价
调后单价 用户输入或自动算 可编辑,变化 → 自动算比率
调前金额 数量×调前单价 自动算,只读
调后金额 数量×调后单价 自动算,只读
备注 可编辑

交互逻辑

  1. 选择仓库 → 调用 LoadProducts → 加载该仓库所有有库存商品到明细表
  2. 也可手动添加行选择商品
  3. 输入调价比率 → 调后单价 = 调前单价 × (1 + 比率/100)
  4. 输入调后单价 → 调价比率 = (调后 - 调前) / 调前 × 100
  5. 审核按钮 → 确认后批量更新成本价

5.2 改造拆装单

  • GetProductCost 接口改为从 wt_sp_cost 取成本价(后端改,前端无需变)

5.3 改造出库单

  • 保存时后端自动写入 cbdj/cbje(后端改,前端可选显示)

六、开发顺序(建议)

阶段 任务 依赖
P1 执行 SQL:建 wt_sp_cost 表 + 初始化数据
P1 执行 SQL:修复 wt_czdmx 精度
P1 执行 SQL:wt_xsckd_mx 加 cbdj/cbje
P2 后端:WtSpCostEntity + GetCost 接口 P1
P2 后端:改造 GetProductCost 从 wt_sp_cost 读 P2
P3 执行 SQL:建 wt_tjd + wt_tjd_mx 表
P3 后端:WtTjdEntity/DTO/Service 全套 CRUD P3
P3 后端:LoadProducts + Audit 接口 P2+P3
P3 前端:调价单列表页 + 表单页 P3 后端
P4 后端:改造 WtXsckdService 采购入库更新成本 P2
P4 后端:改造 WtXsckdService 出库快照成本 P2
P4 后端:改造 WtCzdService 拆装单成本联动 P2
P5 测试:全流程验证 全部

七、验收标准

  1. wt_sp_cost 初始化后,每个商品+仓库组合有正确的加权平均成本价
  2. ✅ 拆装单选商品后自动带出成本单价(从 wt_sp_cost)
  3. ✅ 调价单:选仓库加载所有商品,调价比率和调后单价双向联动
  4. ✅ 调价单审核后,wt_sp_cost.cbj 更新为调后单价
  5. ✅ 采购入库后,wt_sp_cost 自动重算加权平均成本
  6. ✅ 销售出库时,wt_xsckd_mx.cbdj/cbje 记录出库时刻的成本价
  7. ✅ 成本金额精度为 2 位小数