cmvr-wms/sql/wms_project_management_20260922.sql

363 lines
22 KiB
SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 项目经费、库存来源批次、跨项目使用及 AI 指令审计。
-- 用于从当前 WMS 结构升级。所有增量字段和索引均先检查再创建,允许在中断后重新执行。
-- 执行前必须显式确认 database() 是目标库,并完成数据库备份。
create table if not exists project_info (
id bigint(20) not null auto_increment comment '主键',
project_code varchar(64) not null comment '项目编码',
project_name varchar(200) not null comment '项目名称',
manager_id bigint(20) default null comment '项目负责人用户ID',
dept_id bigint(20) default null comment '归属部门ID',
start_date date default null comment '开始日期',
end_date date default null comment '结束日期',
project_status tinyint(1) not null default 1 comment '状态:0筹备 1进行中 2已完成 3已归档',
currency varchar(8) not null default 'CNY' comment '币种',
remark varchar(500) default null comment '备注',
create_by varchar(64) default null comment '创建人',
create_time datetime default null comment '创建时间',
update_by varchar(64) default null comment '更新人',
update_time datetime default null comment '更新时间',
primary key (id),
unique key uk_project_info_code (project_code),
key idx_project_info_name (project_name),
key idx_project_info_manager (manager_id),
key idx_project_info_status (project_status)
) engine=innodb default charset=utf8mb4 comment='项目主表';
create table if not exists project_member (
id bigint(20) not null auto_increment comment '主键',
project_id bigint(20) not null comment '项目ID',
user_id bigint(20) not null comment '用户ID',
member_role varchar(32) not null default 'MEMBER' comment '成员角色',
create_by varchar(64) default null comment '创建人',
create_time datetime default null comment '创建时间',
primary key (id),
unique key uk_project_member (project_id, user_id),
key idx_project_member_user (user_id)
) engine=innodb default charset=utf8mb4 comment='项目成员表';
create table if not exists expense_category (
id bigint(20) not null auto_increment comment '主键',
category_code varchar(64) not null comment '经费科目编码',
category_name varchar(100) not null comment '经费科目名称',
sort_order int not null default 0 comment '显示顺序',
status tinyint(1) not null default 0 comment '状态:0正常 1停用',
remark varchar(500) default null comment '备注',
create_by varchar(64) default null comment '创建人',
create_time datetime default null comment '创建时间',
update_by varchar(64) default null comment '更新人',
update_time datetime default null comment '更新时间',
primary key (id),
unique key uk_expense_category_code (category_code),
unique key uk_expense_category_name (category_name)
) engine=innodb default charset=utf8mb4 comment='项目经费科目';
create table if not exists project_budget (
id bigint(20) not null auto_increment comment '主键',
project_id bigint(20) not null comment '项目ID',
expense_category_id bigint(20) not null comment '经费科目ID',
budget_amount decimal(18,2) not null default 0.00 comment '预算金额',
committed_amount decimal(18,2) not null default 0.00 comment '已占用金额',
spent_amount decimal(18,2) not null default 0.00 comment '已支出金额',
alert_percent decimal(5,2) not null default 80.00 comment '预警百分比',
version int not null default 0 comment '乐观锁版本',
remark varchar(500) default null comment '备注',
create_by varchar(64) default null comment '创建人',
create_time datetime default null comment '创建时间',
update_by varchar(64) default null comment '更新人',
update_time datetime default null comment '更新时间',
primary key (id),
unique key uk_project_budget (project_id, expense_category_id),
key idx_project_budget_category (expense_category_id)
) engine=innodb default charset=utf8mb4 comment='项目分科目预算';
create table if not exists project_fund_ledger (
id bigint(20) not null auto_increment comment '主键',
ledger_no varchar(64) not null comment '流水号',
project_id bigint(20) not null comment '项目ID',
expense_category_id bigint(20) not null comment '经费科目ID',
biz_type varchar(32) not null comment '业务类型',
biz_id bigint(20) default null comment '业务主键',
biz_no varchar(64) default null comment '业务单号',
budget_delta decimal(18,2) not null default 0.00 comment '预算变化额',
committed_delta decimal(18,2) not null default 0.00 comment '占用变化额',
spent_delta decimal(18,2) not null default 0.00 comment '支出变化额',
balance_budget decimal(18,2) not null default 0.00 comment '变更后预算额',
balance_committed decimal(18,2) not null default 0.00 comment '变更后占用额',
balance_spent decimal(18,2) not null default 0.00 comment '变更后支出额',
idempotency_key varchar(128) default null comment '幂等键',
operator varchar(64) default null comment '操作人',
remark varchar(500) default null comment '说明',
create_time datetime not null default current_timestamp comment '创建时间',
primary key (id),
unique key uk_project_fund_ledger_no (ledger_no),
unique key uk_project_fund_idempotency (idempotency_key),
key idx_project_fund_project_category (project_id, expense_category_id, create_time),
key idx_project_fund_biz (biz_type, biz_id)
) engine=innodb default charset=utf8mb4 comment='项目经费不可变流水';
create table if not exists stock_lot (
id bigint(20) not null auto_increment comment '主键',
lot_no varchar(64) not null comment '库存批次号',
area_id bigint(20) not null comment '库区ID',
product_id bigint(20) not null comment '产品ID',
source_project_id bigint(20) not null comment '采购或来源项目ID',
expense_category_id bigint(20) default null comment '来源经费科目ID',
purchase_request_id bigint(20) default null comment '采购申请ID',
arrival_id bigint(20) default null comment '到货ID',
in_record_id bigint(20) default null comment '入库记录ID',
received_quantity decimal(16,3) not null comment '入库数量',
available_quantity decimal(16,3) not null comment '可用数量',
unit_cost decimal(18,2) not null default 0.00 comment '单位成本',
lot_status tinyint(1) not null default 0 comment '状态:0可用 1用尽 2冻结',
remark varchar(500) default null comment '备注',
create_by varchar(64) default null comment '创建人',
create_time datetime default null comment '创建时间',
update_by varchar(64) default null comment '更新人',
update_time datetime default null comment '更新时间',
primary key (id),
unique key uk_stock_lot_no (lot_no),
unique key uk_stock_lot_in_record (in_record_id),
key idx_stock_lot_available (area_id, product_id, lot_status, create_time),
key idx_stock_lot_source_project (source_project_id),
key idx_stock_lot_purchase (purchase_request_id)
) engine=innodb default charset=utf8mb4 comment='库存来源批次';
create table if not exists out_record_lot (
id bigint(20) not null auto_increment comment '主键',
out_record_id bigint(20) not null comment '出库记录ID',
stock_lot_id bigint(20) not null comment '库存批次ID',
source_project_id bigint(20) not null comment '来源项目ID',
usage_project_id bigint(20) not null comment '使用项目ID',
quantity decimal(16,3) not null comment '出库数量',
unit_cost decimal(18,2) not null default 0.00 comment '单位成本',
amount decimal(18,2) not null default 0.00 comment '使用成本',
create_time datetime not null default current_timestamp comment '创建时间',
primary key (id),
key idx_out_record_lot_out (out_record_id),
key idx_out_record_lot_stock (stock_lot_id),
key idx_out_record_lot_project_flow (source_project_id, usage_project_id)
) engine=innodb default charset=utf8mb4 comment='出库库存批次分摊';
create table if not exists ai_command (
id bigint(20) not null auto_increment comment '主键',
user_id bigint(20) not null comment '发起用户ID',
username varchar(64) not null comment '发起账号',
action varchar(64) not null comment '动作',
required_permission varchar(128) default null comment '所需权限',
request_text varchar(1000) default null comment '原始文本',
plan_json json not null comment '服务器确认的执行计划',
result_json json default null comment '执行结果',
command_status varchar(20) not null comment 'WAIT_CONFIRM/EXECUTING/SUCCEEDED/REJECTED/EXPIRED/FAILED',
expires_at datetime not null comment '过期时间',
confirmed_at datetime default null comment '人工确认时间',
executed_at datetime default null comment '执行完成时间',
error_message varchar(1000) default null comment '失败原因',
create_time datetime not null default current_timestamp comment '创建时间',
update_time datetime default null comment '更新时间',
primary key (id),
key idx_ai_command_user_status (user_id, command_status, create_time),
key idx_ai_command_expire (expires_at)
) engine=innodb default charset=utf8mb4 comment='AI 待确认业务指令';
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'purchase_request' and column_name = 'project_id');
set @ddl = if(@column_exists = 0,
'alter table purchase_request add column project_id bigint(20) default null comment ''采购项目ID'' after purchase_batch_id',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'purchase_request' and column_name = 'expense_category_id');
set @ddl = if(@column_exists = 0,
'alter table purchase_request add column expense_category_id bigint(20) default null comment ''经费科目ID'' after project_id',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'purchase_request' and column_name = 'estimated_unit_price');
set @ddl = if(@column_exists = 0,
'alter table purchase_request add column estimated_unit_price decimal(18,2) default null comment ''预计含税单价'' after quantity',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'purchase_request' and column_name = 'estimated_amount');
set @ddl = if(@column_exists = 0,
'alter table purchase_request add column estimated_amount decimal(18,2) default null comment ''预计总金额'' after estimated_unit_price',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @idx_exists = (select count(*) from information_schema.statistics
where table_schema = database() and table_name = 'purchase_request'
and index_name = 'idx_purchase_request_project');
set @ddl = if(@idx_exists = 0,
'alter table purchase_request add key idx_purchase_request_project (project_id, expense_category_id)',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'purchase_arrival' and column_name = 'actual_unit_price');
set @ddl = if(@column_exists = 0,
'alter table purchase_arrival add column actual_unit_price decimal(18,2) default null comment ''本批实际含税单价'' after arrival_quantity',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'purchase_arrival' and column_name = 'actual_amount');
set @ddl = if(@column_exists = 0,
'alter table purchase_arrival add column actual_amount decimal(18,2) default null comment ''本批实际金额'' after actual_unit_price',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'in_record' and column_name = 'source_project_id');
set @ddl = if(@column_exists = 0,
'alter table in_record add column source_project_id bigint(20) default null comment ''来源项目ID'' after arrival_id',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'in_record' and column_name = 'expense_category_id');
set @ddl = if(@column_exists = 0,
'alter table in_record add column expense_category_id bigint(20) default null comment ''来源经费科目ID'' after source_project_id',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @idx_exists = (select count(*) from information_schema.statistics
where table_schema = database() and table_name = 'in_record'
and index_name = 'idx_in_record_source_project');
set @ddl = if(@idx_exists = 0,
'alter table in_record add key idx_in_record_source_project (source_project_id)',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'out_record' and column_name = 'usage_project_id');
set @ddl = if(@column_exists = 0,
'alter table out_record add column usage_project_id bigint(20) default null comment ''使用项目ID'' after product_id',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'out_record' and column_name = 'cross_project_flag');
set @ddl = if(@column_exists = 0,
'alter table out_record add column cross_project_flag tinyint(1) not null default 0 comment ''是否跨项目使用'' after source_no',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @column_exists = (select count(*) from information_schema.columns
where table_schema = database() and table_name = 'out_record' and column_name = 'cross_project_reason');
set @ddl = if(@column_exists = 0,
'alter table out_record add column cross_project_reason varchar(500) default null comment ''跨项目使用原因'' after cross_project_flag',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
set @idx_exists = (select count(*) from information_schema.statistics
where table_schema = database() and table_name = 'out_record'
and index_name = 'idx_out_record_usage_project');
set @ddl = if(@idx_exists = 0,
'alter table out_record add key idx_out_record_usage_project (usage_project_id)',
'select 1');
prepare stmt from @ddl; execute stmt; deallocate prepare stmt;
insert into project_info(project_code, project_name, manager_id, project_status, currency, remark, create_by, create_time)
select 'LEGACY', '历史待归属项目', 1, 3, 'CNY', '升级前数据统一归入该项目,后续不得虚构原始归属', 'migration', sysdate()
where not exists (select 1 from project_info where project_code = 'LEGACY');
insert into expense_category(category_code, category_name, sort_order, status, remark, create_by, create_time)
select 'MATERIAL', '材料费', 10, 0, '材料及耗材', 'migration', sysdate()
where not exists (select 1 from expense_category where category_code = 'MATERIAL');
insert into expense_category(category_code, category_name, sort_order, status, remark, create_by, create_time)
select 'EQUIPMENT', '设备费', 20, 0, '仪器设备购置', 'migration', sysdate()
where not exists (select 1 from expense_category where category_code = 'EQUIPMENT');
insert into expense_category(category_code, category_name, sort_order, status, remark, create_by, create_time)
select 'TRIAL', '试制费', 30, 0, '试制及加工费用', 'migration', sysdate()
where not exists (select 1 from expense_category where category_code = 'TRIAL');
insert into expense_category(category_code, category_name, sort_order, status, remark, create_by, create_time)
select 'OTHER', '其他费用', 90, 0, '其他项目费用', 'migration', sysdate()
where not exists (select 1 from expense_category where category_code = 'OTHER');
insert into expense_category(category_code, category_name, sort_order, status, remark, create_by, create_time)
select 'LEGACY', '历史待归属', 99, 0, '升级前无法确认的经费科目', 'migration', sysdate()
where not exists (select 1 from expense_category where category_code = 'LEGACY');
set @legacy_project_id = (select id from project_info where project_code = 'LEGACY' limit 1);
set @legacy_category_id = (select id from expense_category where category_code = 'LEGACY' limit 1);
insert ignore into project_member(project_id, user_id, member_role, create_by, create_time)
values(@legacy_project_id, 1, 'MANAGER', 'migration', sysdate());
update purchase_request
set project_id = @legacy_project_id,
expense_category_id = @legacy_category_id,
estimated_unit_price = coalesce(estimated_unit_price, 0),
estimated_amount = coalesce(estimated_amount, 0)
where project_id is null;
update in_record
set source_project_id = @legacy_project_id,
expense_category_id = @legacy_category_id
where source_project_id is null;
update out_record
set usage_project_id = @legacy_project_id
where usage_project_id is null;
insert ignore into stock_lot(
lot_no, area_id, product_id, source_project_id, expense_category_id,
received_quantity, available_quantity, unit_cost, lot_status, remark, create_by, create_time
)
select concat('LEGACY-', si.id), si.area_id, si.product_id, @legacy_project_id, @legacy_category_id,
si.stock_num, si.stock_num, 0,
case when si.stock_num > 0 then 0 else 1 end,
'由升级时库存汇总生成,原采购来源未知', 'migration', coalesce(si.create_time, sysdate())
from stock_info si
where not exists (select 1 from stock_lot sl where sl.lot_no = concat('LEGACY-', si.id));
-- 项目管理菜单及权限。固定使用 2100-2113,当前项目菜单最大编号为 2042。
insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, route_name,
is_frame, is_cache, menu_type, visible, status, perms, icon,
create_by, create_time, update_by, update_time, remark)
select 2100, '项目管理', 0, 7, 'project', null, '', '', 1, 0, 'M', '0', '0', '', 'money',
'admin', sysdate(), '', null, '项目、经费和跨项目使用管理'
where not exists (select 1 from sys_menu where menu_id = 2100);
insert into sys_menu values
(2101, '项目与经费', 2100, 1, 'info', 'project/info/index', '', '', 1, 0, 'C', '0', '0', 'project:project:list', 'tree', 'admin', sysdate(), '', null, '项目及经费预算管理'),
(2102, '项目查询', 2101, 1, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:project:query', '#', 'admin', sysdate(), '', null, ''),
(2103, '项目新增', 2101, 2, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:project:add', '#', 'admin', sysdate(), '', null, ''),
(2104, '项目修改', 2101, 3, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:project:edit', '#', 'admin', sysdate(), '', null, ''),
(2105, '项目删除', 2101, 4, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:project:remove', '#', 'admin', sysdate(), '', null, ''),
(2106, '项目成员维护', 2101, 5, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:project:member', '#', 'admin', sysdate(), '', null, ''),
(2107, '项目经费查询', 2101, 6, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:budget:list', '#', 'admin', sysdate(), '', null, ''),
(2108, '项目经费维护', 2101, 7, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:budget:edit', '#', 'admin', sysdate(), '', null, ''),
(2109, '经费科目维护', 2101, 8, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:category:edit', '#', 'admin', sysdate(), '', null, ''),
(2110, '项目总览权限', 2101, 9, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:project:manage', '#', 'admin', sysdate(), '', null, ''),
(2111, '项目物资使用', 2101, 10, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:inventory:use', '#', 'admin', sysdate(), '', null, ''),
(2112, '跨项目使用', 2101, 11, '#', '', '', '', 1, 0, 'F', '0', '0', 'project:inventory:cross-use', '#', 'admin', sysdate(), '', null, ''),
(2113, 'AI助手使用', 2101, 12, '#', '', '', '', 1, 0, 'F', '1', '0', 'ai:assistant:use', '#', 'admin', sysdate(), '', null, '所有正常角色默认拥有,仅控制入口,具体动作仍校验业务权限')
on duplicate key update menu_id = values(menu_id);
-- 所有现有正常角色均可进入 AI 助手;业务查询和写入仍由原仓库权限控制。
insert ignore into sys_role_menu(role_id, menu_id)
select role_id, 2113 from sys_role where status = '0';
-- 延续现有业务角色能力:采购和库存角色可查看自己参与的项目及经费;原有出库角色获得项目物资使用权限。
insert ignore into sys_role_menu(role_id, menu_id)
select distinct role_id, target_menu_id
from (
select rm.role_id, targets.target_menu_id
from sys_role_menu rm
join sys_menu source_menu on source_menu.menu_id = rm.menu_id
join (
select 2100 target_menu_id union all select 2101 union all select 2102 union all select 2107
) targets
where source_menu.perms in ('warehouse:purchase:add', 'warehouse:purchase:manage', 'warehouse:stock:list')
) role_project_menu;
insert ignore into sys_role_menu(role_id, menu_id)
select distinct rm.role_id, 2111
from sys_role_menu rm
join sys_menu m on m.menu_id = rm.menu_id
where m.perms in ('warehouse:stock:out', 'warehouse:out:add');