You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
20 KiB
20 KiB
合同变更功能设计方案
一、需求理解(通俗说明)
- 谁能变更:只有「已激活」的合同可以发起变更。即:合同状态=可用(contract_status='3')且 激活标识=是(active_flag='1')。
- 做什么:
- 做一个「合同变更」列表页,风格和合同信息列表类似,能新增;点新增就跳到「合同变更申请」页。
- 在「合同变更申请」页里先选一个合同,选完后把该合同的信息、物料等查出来填到页面,用户可修改后提交(暂存或走审批)。
- 要能按合同查「合同变更历史」——某合同共发生过几次变更、每次变更单号、时间、状态、是否已回写等。
- 变更审批通过后,要把「变更后的内容」写回正式表:合同表(erp_contract_info)、合同物料表(erp_contract_material)。
下面按「表结构」和「业务逻辑」分开说,不写具体代码,只讲逻辑与 SQL 表结构。
二、表结构设计(MySQL)
思路:一次变更 = 一张变更单 + 变更后的合同信息快照 + 变更后的物料快照。
审批通过后,用快照表的数据去更新正式合同表、合同物料表,并标记「已回写」,便于查历史、防重复回写。
1. 合同变更主表:erp_contract_change
存「一次变更申请」的公共信息,关联原合同。
| 字段名 | 类型 | 说明 |
|---|---|---|
| contract_change_id | bigint, PK, 自增 | 合同变更ID |
| tenant_id | varchar(20) | 租户编号 |
| contract_id | bigint | 原合同ID(关联 erp_contract_info.contract_id) |
| contract_code | varchar(64) | 原合同编号(冗余,列表展示用) |
| contract_name | varchar(255) | 原合同名称(冗余,列表展示用) |
| change_code | varchar(64) | 变更单编号(如:HTBG-2025-001) |
| change_reason | varchar(500) | 变更原因 |
| change_status | varchar(32) | 变更状态:1暂存 2审批中 3可用 |
| flow_status | varchar(32) | 流程状态(与工作流一致) |
| apply_time | datetime | 申请时间 |
| write_back_flag | char(1) | 是否已回写:0否 1是 |
| write_back_time | datetime | 回写时间 |
| remark | varchar(255) | 备注 |
| active_flag | char(1) | 激活标识(1是 0否) |
| del_flag | char(1) | 删除标志(0存在 1删除) |
| create_dept | bigint | 创建部门 |
| create_by | bigint | 创建人 |
| create_time | datetime | 创建时间 |
| update_by | bigint | 更新人 |
| update_time | datetime | 更新时间 |
说明:
- 列表页、历史记录都查这张表;按 contract_id 查即该合同的变更历史。
- write_back_flag 防止重复回写;只有 change_status='3'(可用)且流程通过后才执行回写并置为 1。
2. 合同变更信息快照表:erp_contract_change_info
存「变更后的合同主信息」快照,字段与 erp_contract_info 对齐(去掉合同主键 contract_id,改为关联变更单)。
- 主键:change_info_id(bigint, 自增)。
- 关联:contract_change_id(bigint,关联 erp_contract_change.contract_change_id)。
- 其余字段:与 erp_contract_info 一致(如 contract_code、contract_name、total_price、contract_date、contract_manager_id 等所有业务字段),但这里表示的是「变更后」要写回合同表的值。
- 公共字段:tenant_id, remark, del_flag, create_dept, create_by, create_time, update_by, update_time。
约束:一个变更单对应一条快照(1:1)。回写时:用这条记录 UPDATE erp_contract_info SET ... WHERE contract_id = 主表中的 contract_id。
3. 合同变更物料快照表:erp_contract_change_material
存「变更后的合同物料」快照,字段与 erp_contract_material 对齐。
- 主键:change_material_id(bigint, 自增)。
- 关联:contract_change_id(bigint),不存 contract_id(回写时再填原 contract_id)。
- 其余字段:与 erp_contract_material 一致(如 material_flag, product_name, specification_description, material_id, amount, unit_id, before_price, tax_rate, including_price, subtotal, remark 等)。
- 公共字段:tenant_id, active_flag, del_flag, create_dept, create_by, create_time, update_by, update_time。
回写逻辑:
- 先按原 contract_id 删除 erp_contract_material 中该合同下所有物料;
- 再把本变更单下 erp_contract_change_material 的记录插入到 erp_contract_material,contract_id 填原合同ID。
4. 合同变更付款方式快照表:erp_contract_change_payment_method
存「变更后的合同付款方式」快照,字段与 erp_contract_payment_method 一一对齐。
- 主键:change_payment_id(bigint, 自增)。
- 关联:contract_change_id(bigint),关联 erp_contract_change.contract_change_id;不存 contract_id(回写时再填原 contract_id)。
- 业务字段(与 erp_contract_payment_method 一致):sort_order、payment_stage_id、payment_deadline、payment_percentage、invoice_percentage、payment_amount、payment_description、remark。
- 公共字段:tenant_id、active_flag、del_flag、create_dept、create_by、create_time、update_by、update_time。
回写逻辑:
- 先按原 contract_id 删除 erp_contract_payment_method 中该合同下所有付款方式;
- 再把本变更单下 erp_contract_change_payment_method 的记录插入到 erp_contract_payment_method,contract_id 填原合同ID,其他字段从快照表拷贝。
三、建表 SQL 示例
-- 合同变更主表
DROP TABLE IF EXISTS `erp_contract_change`;
CREATE TABLE `erp_contract_change` (
`contract_change_id` bigint NOT NULL AUTO_INCREMENT COMMENT '合同变更ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`contract_id` bigint NOT NULL COMMENT '原合同ID',
`contract_code` varchar(64) DEFAULT NULL COMMENT '原合同编号',
`contract_name` varchar(255) DEFAULT NULL COMMENT '原合同名称',
`change_code` varchar(64) DEFAULT NULL COMMENT '变更单编号',
`change_reason` varchar(500) DEFAULT NULL COMMENT '变更原因',
`change_status` varchar(32) DEFAULT NULL COMMENT '变更状态(1暂存 2审批中 3可用)',
`flow_status` varchar(32) DEFAULT NULL COMMENT '流程状态',
`apply_time` datetime DEFAULT NULL COMMENT '申请时间',
`write_back_flag` char(1) DEFAULT '0' COMMENT '是否已回写(0否 1是)',
`write_back_time` datetime DEFAULT NULL COMMENT '回写时间',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`active_flag` char(1) DEFAULT '1' COMMENT '激活标识(1是 0否)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
`create_by` bigint DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`contract_change_id`),
KEY `idx_contract_id` (`contract_id`),
KEY `idx_change_status` (`change_status`),
KEY `idx_change_code` (`change_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='合同变更主表';
-- 合同变更信息快照表(字段与 erp_contract_info 对齐,此处仅列关键字段,其余按 erp_contract_info 补全)
DROP TABLE IF EXISTS `erp_contract_change_info`;
CREATE TABLE `erp_contract_change_info` (
`change_info_id` bigint NOT NULL AUTO_INCREMENT COMMENT '变更信息快照ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`contract_change_id` bigint NOT NULL COMMENT '合同变更ID',
`contract_flag` char(1) DEFAULT NULL COMMENT '有无合同(1有 2无)',
`customer_contract_code` varchar(128) DEFAULT NULL COMMENT '客户合同编号',
`contract_code` varchar(64) DEFAULT NULL COMMENT '合同编号',
`contract_name` varchar(255) DEFAULT NULL COMMENT '合同名称',
`contract_category` varchar(32) DEFAULT NULL COMMENT '合同大类',
`contract_type` varchar(32) DEFAULT NULL COMMENT '合同类型',
`business_direction` varchar(16) DEFAULT NULL COMMENT '业务方向',
`contract_dept_id` bigint DEFAULT NULL COMMENT '部门',
`contract_date` datetime DEFAULT NULL COMMENT '合同签订日期',
`total_price` decimal(16,2) DEFAULT NULL COMMENT '合同总价',
`one_customer_id` bigint DEFAULT NULL COMMENT '甲方公司',
`one_represent` varchar(128) DEFAULT NULL COMMENT '甲方授权代表',
`one_date` datetime DEFAULT NULL COMMENT '甲方签字日期',
`two_customer_id` bigint DEFAULT NULL COMMENT '乙方公司',
`two_represent` varchar(128) DEFAULT NULL COMMENT '乙方授权代表',
`two_date` datetime DEFAULT NULL COMMENT '乙方签字日期',
`contract_manager_id` bigint DEFAULT NULL COMMENT '合同负责人',
`template_id` bigint DEFAULT NULL COMMENT '合同模板ID',
`oss_id` varchar(512) DEFAULT NULL COMMENT '附件ID',
`payment_account_id` bigint DEFAULT NULL COMMENT '付款账户ID',
`payment_method` varchar(128) DEFAULT NULL COMMENT '付款方式',
`signature_appendix` bigint DEFAULT NULL COMMENT '签字合同附件',
`warranty_period` int DEFAULT NULL COMMENT '质保期(天)',
`internal_contract_code` varchar(128) DEFAULT NULL COMMENT '内部合同号',
`external_contract_code` varchar(128) DEFAULT NULL COMMENT '外部合同号',
`order_contract_code` varchar(128) DEFAULT NULL COMMENT '订单号',
`project_contract_code` varchar(128) DEFAULT NULL COMMENT '项目号',
`delivery_start` int DEFAULT NULL COMMENT '交付启动期限',
`warranty_period_description` varchar(128) DEFAULT '' COMMENT '质保期描述',
`delivery_location` varchar(255) DEFAULT '' COMMENT '交货地点',
`ship_method` varchar(128) DEFAULT '' COMMENT '运输方式',
`tax_rate` decimal(16,2) DEFAULT NULL COMMENT '合同税率',
`signing_place` varchar(200) DEFAULT '' COMMENT '签订地点',
`material_remark` varchar(500) DEFAULT '' COMMENT '合同物料备注',
`contract_template_flag` char(1) DEFAULT NULL COMMENT '合同模板标识',
`capitalized_amount` varchar(128) DEFAULT NULL COMMENT '合同大写金额',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志',
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
`create_by` bigint DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`change_info_id`),
UNIQUE KEY `uk_contract_change_id` (`contract_change_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='合同变更信息快照';
-- 合同变更物料快照表(字段与 erp_contract_material 对齐)
DROP TABLE IF EXISTS `erp_contract_change_material`;
CREATE TABLE `erp_contract_change_material` (
`change_material_id` bigint NOT NULL AUTO_INCREMENT COMMENT '变更物料快照ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`contract_change_id` bigint NOT NULL COMMENT '合同变更ID',
`material_flag` char(1) DEFAULT NULL COMMENT '标准物料标识',
`product_name` varchar(255) DEFAULT NULL COMMENT '产品名称',
`specification_description` varchar(255) DEFAULT NULL COMMENT '规格描述',
`material_id` bigint DEFAULT NULL COMMENT '物料ID',
`relation_material_id` bigint DEFAULT NULL COMMENT '销售物料ID',
`amount` decimal(16,2) DEFAULT NULL COMMENT '数量',
`unit_id` bigint DEFAULT NULL COMMENT '单位ID',
`before_price` decimal(16,2) DEFAULT NULL COMMENT '未税单价',
`tax_rate` decimal(16,2) DEFAULT NULL COMMENT '税率',
`including_price` decimal(16,2) DEFAULT NULL COMMENT '含税单价',
`subtotal` decimal(16,2) DEFAULT NULL COMMENT '小计',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`sort_order` int DEFAULT 0 COMMENT '排序号',
`active_flag` char(1) DEFAULT '1' COMMENT '激活标识',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志',
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
`create_by` bigint DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`change_material_id`),
KEY `idx_contract_change_id` (`contract_change_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='合同变更物料快照';
-- 合同变更付款方式快照表(字段与 erp_contract_payment_method 对齐)
DROP TABLE IF EXISTS `erp_contract_change_payment_method`;
CREATE TABLE `erp_contract_change_payment_method` (
`change_payment_id` bigint NOT NULL AUTO_INCREMENT COMMENT '变更付款方式快照ID',
`tenant_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '000000' COMMENT '租户编号',
`contract_change_id` bigint NOT NULL COMMENT '合同变更ID',
`sort_order` int NULL DEFAULT NULL COMMENT '排序号',
`payment_stage_id` bigint NULL DEFAULT NULL COMMENT '付款节点ID',
`payment_deadline` decimal(8, 0) NULL DEFAULT NULL COMMENT '支付期限',
`payment_percentage` decimal(8, 0) NULL DEFAULT NULL COMMENT '支付比例',
`invoice_percentage` decimal(8, 0) NULL DEFAULT NULL COMMENT '发票比例',
`payment_amount` decimal(16, 2) NULL DEFAULT NULL COMMENT '支付金额',
`payment_description` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '付款条款',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
`active_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '激活标识(1是 0否)',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
`create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门',
`create_by` bigint NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` bigint NULL DEFAULT NULL COMMENT '更新人',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`change_payment_id`) USING BTREE,
KEY `idx_contract_change_id` (`contract_change_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='合同变更付款方式快照' ROW_FORMAT=DYNAMIC;
说明:erp_contract_change_info 若与现有 erp_contract_info 字段有新增,需在快照表里同步增加一列,保证回写时能完整覆盖。erp_contract_change_payment_method 与 erp_contract_payment_method 字段保持一致,便于回写时逐字段拷贝。
四、业务逻辑(通俗描述)
1. 合同变更列表页(类似合同信息)
- 数据来源:查表 erp_contract_change(可加 del_flag=0、按租户等条件)。
- 能力:列表展示变更单号、关联合同、变更状态、申请时间、是否已回写等;支持按合同、状态、时间筛选。
- 新增:点「新增」跳转到「合同变更申请」页(不传变更单 id,即新建)。
2. 合同变更申请页
- 选合同:
- 下拉/选择器只查「可变更」的合同:
erp_contract_info中contract_status = '3'且active_flag = '1'(已激活)。 - 若已有合同变更单 id(编辑/查看),则直接带出已选的 contract_id。
- 下拉/选择器只查「可变更」的合同:
- 带出合同信息:
- 选好合同后,后端根据 contract_id 查 erp_contract_info、erp_contract_material、erp_contract_payment_method,返回前端。
- 前端把合同主信息、物料列表、付款方式列表展示在表单中,允许编辑。
- 保存/提交:
- 暂存:插入或更新 erp_contract_change(change_status=1),并插入/更新 erp_contract_change_info 一条、erp_contract_change_material 多条、erp_contract_change_payment_method 多条。
- 提交审批:同上,但 change_status=2,并走工作流(flow_status 由流程更新)。
3. 合同变更历史
- 入口:可在合同变更列表页增加「按合同查历史」;或在合同信息详情/列表中提供「变更历史」入口。
- 查询:按 contract_id 查 erp_contract_change,按申请时间倒序,得到该合同下全部变更单。
- 展示:变更单号、申请时间、变更状态、是否已回写、变更原因等;点某条可进详情,详情从 erp_contract_change_info、erp_contract_change_material、erp_contract_change_payment_method 取数据展示(只读即可)。
4. 变更通过后回写合同表、合同物料表与合同付款方式表
- 触发时机:流程审批通过且 change_status 更新为 '3'(可用)时(可在流程监听/回调里做)。
- 前置条件:write_back_flag = '0',避免重复回写。
- 回写步骤:
- 用 erp_contract_change 的 contract_id 找到原合同。
- 合同主信息:用 erp_contract_change_info 中该 contract_change_id 的唯一一条记录,按字段更新 erp_contract_info(WHERE contract_id = 原 contract_id)。注意:不更新 contract_id、合同状态等由系统控制的字段,只更新业务可变更字段。
- 合同物料:
- 删除:DELETE FROM erp_contract_material WHERE contract_id = 原 contract_id;
- 插入:把 erp_contract_change_material 中该 contract_change_id 下的记录逐条 INSERT 到 erp_contract_material,contract_id 填原 contract_id,其他字段从快照表拷贝。
- 合同付款方式:
- 删除:DELETE FROM erp_contract_payment_method WHERE contract_id = 原 contract_id;
- 插入:把 erp_contract_change_payment_method 中该 contract_change_id 下的记录逐条 INSERT 到 erp_contract_payment_method,contract_id 填原 contract_id,其他字段从快照表拷贝。
- 更新 erp_contract_change:write_back_flag='1', write_back_time=当前时间。
五、小结
| 项目 | 说明 |
|---|---|
| 可变更范围 | 仅合同状态=可用 且 激活标识=是 的合同 |
| 列表与申请 | 列表查 erp_contract_change;申请页选合同后查 erp_contract_info + erp_contract_material + erp_contract_payment_method 带出并编辑,保存到变更主表+三张快照表 |
| 历史 | 按 contract_id 查 erp_contract_change,详情用 change_info、change_material、change_payment_method |
| 回写 | 审批通过后,用 change_info 更新 contract_info,用 change_material 全量替换该 contract_id 下物料,用 change_payment_method 全量替换该 contract_id 下付款方式,并标记已回写 |
按上述表结构与逻辑实现即可满足「只有合同激活后可变更、可查变更历史、变更后内容回写合同表、合同物料表与合同付款方式表」的需求。