# 商品成本价体系与调价单开发计划 > **现有基础**:前端 `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`(商品仓库成本表) > 核心表,按「商品+仓库」维度存储当前加权平均成本价 ```sql 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`(调价单主表) ```sql 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`(调价单明细) ```sql 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`(出库单明细加成本价字段) > 出库时保存成本价快照,用于后续毛利计算 ```sql 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) 没有小数位,需要修复 ```sql 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` 数据 > 根据当前在库序列号的批次入库价计算加权平均成本 ```sql 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 位小数