Update 2026-06-24 10:47:45

This commit is contained in:
yi
2026-06-24 10:47:45 +08:00
parent ffc7eef984
commit 94625d4f3f
29 changed files with 1417 additions and 158 deletions
+192 -50
View File
@@ -1,58 +1,200 @@
SET client_encoding = 'UTF8';
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE,
name VARCHAR(255),
email VARCHAR(255),
phone VARCHAR(255),
role VARCHAR(50),
password_hash VARCHAR(255),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- ============================================================
-- 云海财务系统 - 示例数据
-- ============================================================
CREATE TABLE IF NOT EXISTS customers (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
address VARCHAR(255),
remark TEXT,
contact_person VARCHAR(255),
phone VARCHAR(255),
email VARCHAR(255),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS projects (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
project_code VARCHAR(255),
customer_id INTEGER,
project_manager_id INTEGER,
contract_amount NUMERIC,
status VARCHAR(50),
location VARCHAR(255),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- 1. 插入用户数据
INSERT INTO users (username, name, email, phone, role, password_hash, created_at, updated_at) VALUES
('admin', '系统管理员', 'admin@yunhai.com', '13800000001', 'admin', '$2a$10$xyz', NOW(), NOW()),
('finance', '财务专员', 'finance@yunhai.com', '13800000002', 'finance', '$2a$10$xyz', NOW(), NOW()),
('manager', '项目经理', 'manager@yunhai.com', '13800000003', 'manager', '$2a$10$xyz', NOW(), NOW()),
('employee', '普通员工', 'employee@yunhai.com', '13800000004', 'employee', '$2a$10$xyz', NOW(), NOW())
-- 1. 用户
INSERT INTO users (username, name, email, phone, role, password_hash, created_at, updated_at) VALUES
('admin', '系统管理员', 'admin@xinmi.cloud', '13800000001', 'admin', '$2b$10$XYs6Rc6LVqeFsmY4cMZdmOy.bhSc4Ib3yFfwj5kt.G.oHNiuZSkZm', NOW(), NOW()),
('finance', '财务专员', 'finance@xinmi.cloud', '13800000002', 'finance', '$2b$10$XYs6Rc6LVqeFsmY4cMZdmOy.bhSc4Ib3yFfwj5kt.G.oHNiuZSkZm', NOW(), NOW()),
('manager', '项目经理', 'manager@xinmi.cloud', '13800000003', 'manager', '$2b$10$XYs6Rc6LVqeFsmY4cMZdmOy.bhSc4Ib3yFfwj5kt.G.oHNiuZSkZm', NOW(), NOW()),
('employee', '普通员工', 'employee@xinmi.cloud', '13800000004', 'employee', '$2b$10$XYs6Rc6LVqeFsmY4cMZdmOy.bhSc4Ib3yFfwj5kt.G.oHNiuZSkZm', NOW(), NOW())
ON CONFLICT (username) DO NOTHING;
-- 2. 插入客户数据
INSERT INTO customers (name, address, remark, contact_person, phone, email, created_at, updated_at) VALUES
-- 2. 客户
INSERT INTO customers (name, address, remark, contact_person, phone, email, created_at, updated_at)
SELECT * FROM (VALUES
('老挝国家电力公司', '万象市', '大客户', 'Somphong', '020-11111111', 'edl@laos.com', NOW(), NOW()),
('万象基建集团', '万象市', '长期合作', 'Champa', '020-22222222', 'infra@laos.com', NOW(), NOW()),
('琅勃拉邦电力局', '琅勃拉邦', '', 'Keo', '020-33333333', 'lpq@laos.com', NOW(), NOW());
('琅勃拉邦电力局', '琅勃拉邦', '', 'Keo', '020-33333333', 'lpq@laos.com', NOW(), NOW())
) AS v(name, address, remark, contact_person, phone, email, created_at, updated_at)
WHERE NOT EXISTS (SELECT 1 FROM customers WHERE name = v.name);
-- 3. 插入项目数据 (假设customer_id与上表对应)
INSERT INTO projects (name, project_code, customer_id, project_manager_id, contract_amount, status, location, created_at, updated_at) VALUES
('万象22kV线路工程', 'P2026-001', 1, 3, 1500000, 'in_progress', '万象市赛塔尼县', NOW(), NOW()),
('琅勃拉邦变电站改造', 'P2026-002', 3, 3, 800000, 'planning', '琅勃拉邦', NOW(), NOW()),
('南塔电力抢修', 'P2026-003', 1, 3, 300000, 'completed', '南塔省', NOW(), NOW());
-- 3. 项目
INSERT INTO projects (name, project_code, customer_id, project_manager_id, contract_amount, status, location, start_date, description, created_at, updated_at)
SELECT v.name, v.project_code, c.id, u.id, v.contract_amount, v.status, v.location, v.start_date::date, v.description, NOW(), NOW()
FROM (VALUES
('万象22kV线路工程', 'P2026-001', '老挝国家电力公司', 'manager', 1500000, 'in_progress', '万象市赛塔尼县', '2026-01-15', '22kV架空线路新建工程'),
('琅勃拉邦变电站改造', 'P2026-002', '琅勃拉邦电力局', 'manager', 800000, 'planning', '琅勃拉邦', '2026-03-01', '35kV变电站设备更新改造'),
('南塔电力抢修', 'P2026-003', '老挝国家电力公司', 'manager', 300000, 'completed', '南塔省', '2025-11-01', '台风后应急抢修工程')
) AS v(name, project_code, customer_name, manager_username, contract_amount, status, location, start_date, description)
JOIN customers c ON c.name = v.customer_name
JOIN users u ON u.username = v.manager_username
WHERE NOT EXISTS (SELECT 1 FROM projects WHERE project_code = v.project_code);
-- 4. 供应商
INSERT INTO suppliers (name, address, contact_person, phone, email, product_category, supplier_source, country, remark, created_at, updated_at) VALUES
('万象电力器材有限公司', '万象市工业区', 'Bounmy', '020-55551111', 'sales@pole.la', '电杆横担', '老挝', '老挝', '本地电杆供应商', NOW(), NOW()),
('昆明电缆集团', '云南省昆明市', 'Zhang Wei', '13888880001', 'export@km-cable.cn', '电缆电线', '中国', '中国', '高压电缆主力供应商', NOW(), NOW()),
('深圳开关设备厂', '广东省深圳市', 'Li Ming', '13900001111', 'sales@sz-switch.cn', '开关设备', '中国', '中国', '断路器与隔离开关', NOW(), NOW());
INSERT INTO contacts (entity_id, entity_type, name, position, phone, is_primary, created_at, updated_at) VALUES
(1, 'supplier', 'Bounmy', '销售经理', '020-55551111', 1, NOW(), NOW()),
(2, 'supplier', 'Zhang Wei', '出口经理', '13888880001', 1, NOW(), NOW());
INSERT INTO supplier_payment_infos (supplier_id, account_name, bank_account, bank_name, is_primary, created_at, updated_at) VALUES
(1, '万象电力器材有限公司', 'LA0012345678', 'BCEL Bank', 1, NOW(), NOW()),
(2, '昆明电缆集团', '6222021234567890', '中国工商银行昆明分行', 1, NOW(), NOW());
-- 5. 分包商
INSERT INTO subcontractors (name, address, contact_person, phone, email, specialty, country, score, remark, created_at, updated_at) VALUES
('老挝电力施工一队', '万象市', 'Kham', '020-66662222', 'team1@laos.com', '架空线路施工', '老挝', 95, '经验丰富', NOW(), NOW()),
('万象电气安装公司', '万象市', 'Phone', '020-77773333', 'install@laos.com', '变电站安装', '老挝', 100, '新合作方', NOW(), NOW());
-- 6. 商品分类与商品
INSERT INTO product_categories (name, parent_id) VALUES
('电杆横担', NULL),
('电缆电线', NULL),
('开关设备', NULL);
INSERT INTO product_categories (name, parent_id) VALUES
('混凝土电杆', 1),
('高压电缆', 2),
('断路器', 3);
INSERT INTO products (name, model, category_id, category_name, unit, cost_price, price, brand, specification, source, stock_quantity, status, created_at, updated_at) VALUES
('12米混凝土电杆', 'DP-12M', 4, '混凝土电杆', '', 850, 1200, '万象建材', 'φ190×12000mm', '老挝', 200, 'active', NOW(), NOW()),
('JKLYJ-1-10 架空绝缘导线', 'JKLYJ-10', 5, '高压电缆', '', 8.5, 12, '昆明电缆', '1×10mm²', '中国', 5000, 'active', NOW(), NOW()),
('ZW32-12 真空断路器', 'ZW32-12', 6, '断路器', '', 8500, 12000, '深圳开关', '12kV户外型', '中国', 15, 'active', NOW(), NOW());
-- 7. 汇率
INSERT INTO exchange_rates (currency_code, to_currency_code, rate, effective_date, created_at) VALUES
('CNY', 'LAK', 2900, CURRENT_DATE, NOW()),
('CNY', 'USD', 0.143, CURRENT_DATE, NOW()),
('CNY', 'THB', 4.8, CURRENT_DATE, NOW()),
('USD', 'LAK', 20300, CURRENT_DATE, NOW());
-- 8. 财务分类
INSERT INTO expense_categories (category_level1, category_level2, label, description, sort_order) VALUES
('income', 'contract_payment', '项目合同收款', '客户按合同打款到账', 1),
('project', 'material', '材料采购', '电杆、导线、电缆等', 1),
('project', 'subcontract', '施工分包', '外包施工队费用', 3),
('company', 'salary', '工资薪酬', '办公室管理人员工资', 1)
ON CONFLICT (category_level1, category_level2) DO NOTHING;
-- 9. 预支 / 报销 / 付款
INSERT INTO advances (applicant_id, project_id, amount, currency, amount_cny, reason, advance_date, advance_code, status, applicant, created_at, updated_at)
SELECT u.id, p.id, v.amount, v.currency, v.amount_cny, v.reason, v.advance_date::date, v.advance_code, v.status, v.applicant, NOW(), NOW()
FROM (VALUES
(4, 1, 5000, 'CNY', 5000, '万象项目现场出差备用金', '2026-02-10', 'ADV-20260210-001', 'approved', '普通员工'),
(4, 1, 2000000, 'LAK', 690, '现场零星采购备用金', '2026-03-05', 'ADV-20260305-001', 'pending', '普通员工')
) AS v(applicant_id, project_id, amount, currency, amount_cny, reason, advance_date, advance_code, status, applicant)
JOIN users u ON u.id = v.applicant_id
JOIN projects p ON p.id = v.project_id
ON CONFLICT (advance_code) DO NOTHING;
INSERT INTO reimbursements (user_id, project_id, amount, currency, amount_cny, reason, reimbursement_date, reimbursement_code, status, applicant, expense_type, created_at, updated_at)
SELECT u.id, p.id, v.amount, v.currency, v.amount_cny, v.reason, v.reimbursement_date::date, v.reimbursement_code, v.status, v.applicant, v.expense_type, NOW(), NOW()
FROM (VALUES
(4, 1, 3200, 'CNY', 3200, '万象项目出差住宿费及交通费', '2026-02-20', 'REIM-20260220-001', 'pending', '普通员工', 'travel')
) AS v(user_id, project_id, amount, currency, amount_cny, reason, reimbursement_date, reimbursement_code, status, applicant, expense_type)
JOIN users u ON u.id = v.user_id
JOIN projects p ON p.id = v.project_id
ON CONFLICT (reimbursement_code) DO NOTHING;
INSERT INTO payment_requests (request_code, applicant, payment_date, payee, bank_account, bank_name, amount, currency, reason, status, expense_type, expense_category, project_id, created_at, updated_at)
SELECT v.request_code, v.applicant, v.payment_date::date, v.payee, v.bank_account, v.bank_name, v.amount, v.currency, v.reason, v.status, v.expense_type, v.expense_category, p.id, NOW(), NOW()
FROM (VALUES
('PAY-20260301-001', '财务专员', '2026-03-01', '万象电力器材有限公司', 'LA0012345678', 'BCEL Bank', 120000, 'CNY', '万象项目电杆首付款', 'approved', 'project', 'material', 'P2026-001'),
('PAY-20260315-001', '财务专员', '2026-03-15', '办公室房东', '6222009988776655', '中国建设银行', 15000, 'CNY', '2026年3月办公室租金', 'pending', 'company', 'rent', NULL)
) AS v(request_code, applicant, payment_date, payee, bank_account, bank_name, amount, currency, reason, status, expense_type, expense_category, project_code)
LEFT JOIN projects p ON p.project_code = v.project_code
ON CONFLICT (request_code) DO NOTHING;
-- 10. 采购申请与订单
INSERT INTO purchase_requests (request_code, project_id, applicant, request_date, expense_category, purchase_type, total_amount, currency, status, brief_description, remark, created_at, updated_at)
SELECT v.request_code, p.id, v.applicant, v.request_date::date, v.expense_category, v.purchase_type, v.total_amount, v.currency, v.status, v.brief_description, v.remark, NOW(), NOW()
FROM (VALUES
('PUR-20260310-001', 'P2026-001', '项目经理', '2026-03-10', 'material', 'inventory', 144000, 'CNY', 'approved', '万象项目电杆及金具采购', '预计120根12米电杆'),
('PUR-20260318-001', 'P2026-002', '项目经理', '2026-03-18', 'equipment', 'inventory', 36000, 'CNY', 'pending', '琅勃拉邦变电站断路器采购', '待询价确认型号')
) AS v(request_code, project_code, applicant, request_date, expense_category, purchase_type, total_amount, currency, status, brief_description, remark)
JOIN projects p ON p.project_code = v.project_code
ON CONFLICT (request_code) DO NOTHING;
INSERT INTO purchase_orders (code, purchase_request_id, project_id, supplier_id, currency, status, estimated_amount, remark, created_by, created_at, updated_at)
SELECT 'PO202603120001', pr.id, p.id, s.id, 'CNY', 'confirmed', 144000, '电杆采购正式订单', 'manager', NOW(), NOW()
FROM purchase_requests pr
JOIN projects p ON p.project_code = 'P2026-001'
JOIN suppliers s ON s.name = '万象电力器材有限公司'
WHERE pr.request_code = 'PUR-20260310-001'
ON CONFLICT (code) DO NOTHING;
INSERT INTO purchase_order_items (order_id, product_id, product_name, unit, quantity, unit_price, total_price, created_at) VALUES
(1, 1, '12米混凝土电杆', '', 120, 1200, 144000, NOW());
INSERT INTO payment_plans (purchase_order_id, project_id, plan_name, stage, planned_date, planned_amount, planned_percentage, status, created_at, updated_at) VALUES
(1, 1, '电杆采购首付款', '首付30%', '2026-03-15', 43200, 30, 'pending', NOW(), NOW()),
(1, 1, '电杆采购尾款', '尾款70%', '2026-04-15', 100800, 70, 'pending', NOW(), NOW());
-- 11. 付款节点与记录
INSERT INTO payment_nodes (project_id, name, amount, due_date, status, created_at, updated_at) VALUES
(1, '合同签订预付款', 450000, '2026-02-01', 'completed', NOW(), NOW()),
(1, '材料到场进度款', 450000, '2026-04-01', 'pending', NOW(), NOW()),
(1, '竣工验收尾款', 600000, '2026-08-01', 'pending', NOW(), NOW());
INSERT INTO payment_records (node_id, amount, payment_date, method, status, created_at) VALUES
(1, 450000, '2026-02-05', '银行转账', 'completed', NOW());
-- 12. 物流公司
INSERT INTO logistics_companies (code, name, address, phone, status, remark, created_at, updated_at) VALUES
('LC-001', '中老跨境物流', '万象市物流园', '020-88889999', 'active', '中国至老挝跨境运输', NOW(), NOW()),
('LC-002', '万象本地运输', '万象市', '020-88887777', 'active', '老挝境内二次转运', NOW(), NOW());
-- 13. 施工日志
INSERT INTO construction_logs (project_id, log_date, weather, recorded_by, work_content, next_plan, issues, created_at) VALUES
(1, '2026-03-15', '', '项目经理', '完成A段电杆基坑开挖20基', '继续B段基坑开挖', '部分场地积水需排水', NOW()),
(1, '2026-03-16', '多云', '项目经理', '电杆到货验收120根,合格118根', '组织电杆组立', '2根电杆表面裂纹退回', NOW());
-- 14. 工程模板
INSERT INTO project_type_templates (name, description, phases, is_system, created_at, updated_at) VALUES
('架空线路工程', '架空线路项目标准阶段', '[
{"order":1,"name":"勘察设计","type":"serial","depends":[],"sub_items":["线路勘察","图纸设计"]},
{"order":2,"name":"物资采购","type":"parallel","depends":[1],"sub_items":["电杆采购","导线采购"]},
{"order":3,"name":"基础施工","type":"serial","depends":[1],"sub_items":["杆坑开挖","基础浇筑"]},
{"order":4,"name":"架线施工","type":"serial","depends":[2,3],"sub_items":["电杆组立","放线紧线"]},
{"order":5,"name":"竣工验收","type":"serial","depends":[4],"sub_items":[]}
]'::jsonb, true, NOW(), NOW());
INSERT INTO project_phases (project_id, phase_name, phase_order, phase_type, status, sub_items, created_at) VALUES
(1, '勘察设计', 1, 'serial', 'completed', '[{"name":"现场勘察","done":true},{"name":"图纸设计","done":true}]'::jsonb, NOW()),
(1, '物资采购', 2, 'parallel', 'in_progress', '[{"name":"电杆采购","done":true},{"name":"导线采购","done":false}]'::jsonb, NOW()),
(1, '基础施工', 3, 'serial', 'pending', '[{"name":"杆坑开挖","done":false}]'::jsonb, NOW());
-- 15. 预算项目
INSERT INTO budget_projects (budget_code, name, customer_id, business_manager_id, status, total_amount, currency, survey_notes, remark, created_by, created_at, updated_at)
SELECT 'BJ0001', '万象22kV线路工程预算', c.id, u.id, 'approved', 1450000, 'CNY', '初版预算报价', '含材料、施工、运输', u.id, NOW(), NOW()
FROM customers c, users u
WHERE c.name = '老挝国家电力公司' AND u.username = 'manager'
AND NOT EXISTS (SELECT 1 FROM budget_projects WHERE budget_code = 'BJ0001');
INSERT INTO budget_quotations (budget_project_id, version, quotation_date, amount, currency, notes, status, created_at)
SELECT bp.id, 1, '2026-01-10', 1450000, 'CNY', '含材料、施工、运输', 'approved', NOW()
FROM budget_projects bp
WHERE bp.budget_code = 'BJ0001'
AND NOT EXISTS (SELECT 1 FROM budget_quotations WHERE budget_project_id = bp.id);
-- 16. 财务账本记录
INSERT INTO financial_records (record_code, txn_type, category_level1, category_level2, project_id, user_name, amount_original, currency, exchange_rate, amount_cny, record_date, counterparty_name, description, status, created_at, updated_at)
SELECT v.record_code, v.txn_type, v.category_level1, v.category_level2, p.id, v.user_name, v.amount_original, v.currency, v.exchange_rate, v.amount_cny, v.record_date::date, v.counterparty_name, v.description, v.status, NOW(), NOW()
FROM (VALUES
('FR-INC-20260205-001', 'income', 'income', 'contract_payment', 'P2026-001', '财务专员', 450000, 'CNY', 1, 450000, '2026-02-05', '老挝国家电力公司', '万象项目合同预付款到账', 'confirmed'),
('FR-EXP-20260312-001', 'expense', 'project', 'material', 'P2026-001', '财务专员', 43200, 'CNY', 1, 43200, '2026-03-12', '万象电力器材有限公司', '电杆采购首付款', 'confirmed'),
('FR-EXP-20260315-001', 'expense', 'company', 'rent', NULL, '财务专员', 15000, 'CNY', 1, 15000, '2026-03-15', '办公室房东', '3月办公室租金', 'confirmed')
) AS v(record_code, txn_type, category_level1, category_level2, project_code, user_name, amount_original, currency, exchange_rate, amount_cny, record_date, counterparty_name, description, status)
LEFT JOIN projects p ON p.project_code = v.project_code
ON CONFLICT (record_code) DO NOTHING;
-- 17. 库存记录
INSERT INTO inventory_records (record_type, project_id, purchase_request_id, product_id, quantity, unit_price, total_amount, record_date, operator, remark, created_at) VALUES
('in', 1, 1, 1, 118, 1200, 141600, '2026-03-16', '项目经理', '电杆验收入库(2根退回)', NOW());
+639
View File
@@ -0,0 +1,639 @@
SET client_encoding = 'UTF8';
-- ============================================================
-- 云海财务系统 - PostgreSQL 完整表结构
-- ============================================================
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE,
name VARCHAR(255),
email VARCHAR(255),
phone VARCHAR(255),
role VARCHAR(50),
avatar TEXT,
passport TEXT,
driver_license TEXT,
password_hash VARCHAR(255),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS customers (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
address VARCHAR(255),
remark TEXT,
contact_person VARCHAR(255),
phone VARCHAR(255),
email VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS projects (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
project_code VARCHAR(255),
customer_id INTEGER REFERENCES customers(id),
project_manager_id INTEGER REFERENCES users(id),
contract_amount NUMERIC,
start_date DATE,
end_date DATE,
description TEXT,
status VARCHAR(50) DEFAULT 'planning',
location VARCHAR(255),
type_template_id INTEGER,
current_phase VARCHAR(50),
phase_progress NUMERIC(5,2) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS contacts (
id SERIAL PRIMARY KEY,
entity_id INTEGER NOT NULL,
entity_type VARCHAR(50) NOT NULL,
name VARCHAR(255),
position VARCHAR(255),
phone VARCHAR(255),
is_primary INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS suppliers (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
address TEXT,
contact_person VARCHAR(255),
phone VARCHAR(255),
email VARCHAR(255),
product_category VARCHAR(255),
supplier_source VARCHAR(100),
supply_category TEXT,
country TEXT,
remark TEXT,
status TEXT DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS supplier_payment_infos (
id SERIAL PRIMARY KEY,
supplier_id INTEGER REFERENCES suppliers(id) ON DELETE CASCADE,
account_name TEXT,
bank_account TEXT,
bank_name TEXT,
qr_code TEXT,
is_primary INTEGER DEFAULT 0,
is_default BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS subcontractors (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
address TEXT,
contact_person VARCHAR(255),
phone VARCHAR(255),
email VARCHAR(255),
specialty TEXT,
category TEXT,
country TEXT,
remark TEXT,
score INTEGER DEFAULT 100,
initial_score INTEGER DEFAULT 100,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS subcontractor_payment_infos (
id SERIAL PRIMARY KEY,
subcontractor_id INTEGER REFERENCES subcontractors(id) ON DELETE CASCADE,
account_name TEXT,
bank_name TEXT,
bank_account TEXT,
qr_code TEXT,
is_primary INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS customer_payment_infos (
id SERIAL PRIMARY KEY,
customer_id INTEGER REFERENCES customers(id) ON DELETE CASCADE,
account_name TEXT,
bank_name TEXT,
bank_account TEXT,
qr_code TEXT,
is_primary INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS product_categories (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
parent_id INTEGER REFERENCES product_categories(id)
);
CREATE TABLE IF NOT EXISTS products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
model TEXT,
category_id INTEGER REFERENCES product_categories(id),
category_name TEXT,
unit TEXT DEFAULT '',
cost_price REAL,
price REAL DEFAULT 0,
brand TEXT,
specification TEXT,
source TEXT DEFAULT '老挝',
remark TEXT,
stock_quantity REAL DEFAULT 0,
stock_warning REAL DEFAULT 0,
status TEXT DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS exchange_rates (
id SERIAL PRIMARY KEY,
currency_code TEXT NOT NULL,
to_currency_code TEXT NOT NULL,
rate REAL NOT NULL,
effective_date DATE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS advances (
id SERIAL PRIMARY KEY,
applicant_id INTEGER REFERENCES users(id),
project_id INTEGER REFERENCES projects(id),
amount REAL NOT NULL DEFAULT 0,
currency TEXT DEFAULT 'CNY',
amount_cny REAL DEFAULT 0,
reason TEXT,
advance_date DATE,
advance_code TEXT UNIQUE,
status TEXT DEFAULT 'pending',
applicant TEXT,
attachments TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS reimbursements (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
project_id INTEGER REFERENCES projects(id),
amount REAL NOT NULL DEFAULT 0,
currency TEXT DEFAULT 'CNY',
amount_cny REAL DEFAULT 0,
reason TEXT,
reimbursement_date DATE,
reimbursement_code TEXT UNIQUE,
status TEXT DEFAULT 'pending',
applicant TEXT,
expense_type TEXT,
detail_items TEXT,
attachments TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS payment_requests (
id SERIAL PRIMARY KEY,
request_code TEXT UNIQUE,
applicant TEXT,
payment_date DATE,
payee TEXT,
bank_account TEXT,
bank_name TEXT,
amount REAL DEFAULT 0,
currency TEXT DEFAULT 'CNY',
reason TEXT,
status TEXT DEFAULT 'pending',
detail_items TEXT,
attachments TEXT,
payee_type TEXT,
payee_id INTEGER,
expense_type TEXT DEFAULT 'company',
expense_category TEXT,
project_id INTEGER REFERENCES projects(id),
purchase_request_id INTEGER,
purchase_order_id INTEGER,
payment_type TEXT DEFAULT 'company',
logistics_company_id INTEGER,
logistics_document_url TEXT,
driver_phone TEXT,
cargo_weight REAL,
transport_distance REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS verifications (
id SERIAL PRIMARY KEY,
advance_id INTEGER REFERENCES advances(id),
amount REAL DEFAULT 0,
currency TEXT DEFAULT 'CNY',
reason TEXT,
verification_date DATE,
verification_code TEXT UNIQUE,
status TEXT DEFAULT 'pending',
applicant TEXT,
advance_code TEXT,
advance_amount REAL,
detail_items TEXT,
attachments TEXT,
expense_type TEXT,
project_id INTEGER REFERENCES projects(id),
settlement TEXT,
settlement_amount REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS executions (
id SERIAL PRIMARY KEY,
apply_id INTEGER,
apply_type TEXT,
action TEXT,
execute_method TEXT,
voucher_no TEXT,
remark TEXT,
reject_reason TEXT,
voucher_files TEXT,
operator TEXT,
operator_role TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS purchase_requests (
id SERIAL PRIMARY KEY,
request_code TEXT UNIQUE NOT NULL,
project_id INTEGER NOT NULL REFERENCES projects(id),
applicant TEXT,
request_date DATE,
supplier_id INTEGER REFERENCES suppliers(id),
supplier_name TEXT,
expense_category TEXT,
purchase_type TEXT DEFAULT 'inventory',
total_amount REAL NOT NULL DEFAULT 0,
currency TEXT DEFAULT 'CNY',
status TEXT DEFAULT 'pending',
remark TEXT,
remarks TEXT,
brief_description TEXT,
attachments TEXT,
expected_date DATE,
execute_date DATE,
execute_method TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS purchase_request_items (
id SERIAL PRIMARY KEY,
purchase_request_id INTEGER NOT NULL REFERENCES purchase_requests(id) ON DELETE CASCADE,
product_id INTEGER REFERENCES products(id),
product_name TEXT NOT NULL,
specification TEXT,
unit TEXT,
quantity REAL NOT NULL DEFAULT 0,
unit_price REAL NOT NULL DEFAULT 0,
total_price REAL NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS purchase_orders (
id SERIAL PRIMARY KEY,
code TEXT UNIQUE NOT NULL,
purchase_request_id INTEGER REFERENCES purchase_requests(id),
project_id INTEGER REFERENCES projects(id),
supplier_id INTEGER REFERENCES suppliers(id),
supplier_country TEXT DEFAULT 'Laos',
currency TEXT DEFAULT 'CNY',
status TEXT DEFAULT 'draft',
estimated_amount REAL DEFAULT 0,
paid_amount REAL DEFAULT 0,
contract_url TEXT,
quotation_url TEXT,
actual_delivery_date DATE,
expected_delivery_date DATE,
receiving_status VARCHAR(20) DEFAULT 'pending',
remark TEXT,
created_by TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS purchase_order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER NOT NULL REFERENCES purchase_orders(id) ON DELETE CASCADE,
product_id INTEGER REFERENCES products(id),
product_name TEXT,
specification TEXT,
unit TEXT,
quantity REAL DEFAULT 0,
unit_price REAL DEFAULT 0,
total_price REAL DEFAULT 0,
received_quantity REAL DEFAULT 0,
verified_quantity REAL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS payment_plans (
id SERIAL PRIMARY KEY,
purchase_order_id INTEGER REFERENCES purchase_orders(id),
project_id INTEGER REFERENCES projects(id),
plan_name TEXT,
stage TEXT,
planned_date DATE,
planned_amount REAL,
planned_percentage REAL,
actual_amount REAL DEFAULT 0,
actual_date DATE,
payment_request_id INTEGER,
reminder_days INTEGER DEFAULT 3,
amount REAL DEFAULT 0,
due_date DATE,
status TEXT DEFAULT 'pending',
remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS payment_nodes (
id SERIAL PRIMARY KEY,
project_id INTEGER REFERENCES projects(id),
name TEXT,
amount REAL DEFAULT 0,
due_date DATE,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS payment_records (
id SERIAL PRIMARY KEY,
node_id INTEGER REFERENCES payment_nodes(id),
amount REAL DEFAULT 0,
payment_date DATE,
method TEXT,
status TEXT DEFAULT 'completed',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS inventory_records (
id SERIAL PRIMARY KEY,
record_type TEXT NOT NULL,
project_id INTEGER REFERENCES projects(id),
purchase_request_id INTEGER REFERENCES purchase_requests(id),
product_id INTEGER NOT NULL REFERENCES products(id),
quantity REAL NOT NULL DEFAULT 0,
unit_price REAL,
total_amount REAL,
record_date DATE NOT NULL,
operator TEXT,
remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS logistics_companies (
id SERIAL PRIMARY KEY,
code TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
address TEXT,
phone TEXT,
email TEXT,
quotation_description TEXT,
status TEXT DEFAULT 'active',
remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS logistics_company_contacts (
id SERIAL PRIMARY KEY,
logistics_company_id INTEGER REFERENCES logistics_companies(id) ON DELETE CASCADE,
name TEXT,
phone TEXT,
position TEXT,
is_primary INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS logistics_company_bank_accounts (
id SERIAL PRIMARY KEY,
logistics_company_id INTEGER REFERENCES logistics_companies(id) ON DELETE CASCADE,
account_name TEXT,
bank_name TEXT,
account_number TEXT,
is_default INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS construction_logs (
id SERIAL PRIMARY KEY,
project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
log_date DATE NOT NULL,
weather TEXT,
recorded_by TEXT,
work_content TEXT,
next_plan TEXT,
issues TEXT,
photos TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS project_type_templates (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
phases JSONB NOT NULL DEFAULT '[]',
is_system BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS project_phases (
id SERIAL PRIMARY KEY,
project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
template_key VARCHAR(50),
phase_name VARCHAR(100) NOT NULL,
phase_order INTEGER NOT NULL,
phase_type VARCHAR(20) DEFAULT 'serial',
depends_on INTEGER[],
status VARCHAR(20) DEFAULT 'pending',
started_at TIMESTAMP,
completed_at TIMESTAMP,
completed_by INTEGER REFERENCES users(id),
remark TEXT,
photos TEXT[],
sub_items JSONB DEFAULT '[]',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS budget_projects (
id SERIAL PRIMARY KEY,
budget_code TEXT,
name TEXT NOT NULL,
customer_id INTEGER REFERENCES customers(id),
business_manager_id INTEGER REFERENCES users(id),
location TEXT,
survey_date DATE,
intermediary_name TEXT,
intermediary_fee_type TEXT DEFAULT 'fixed',
intermediary_fee REAL DEFAULT 0,
customer_requirements TEXT,
survey_notes TEXT,
status TEXT DEFAULT 'draft',
total_amount REAL DEFAULT 0,
currency TEXT DEFAULT 'CNY',
remark TEXT,
created_by INTEGER REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS budget_quotations (
id SERIAL PRIMARY KEY,
budget_project_id INTEGER REFERENCES budget_projects(id) ON DELETE CASCADE,
version INTEGER DEFAULT 1,
quotation_date DATE,
amount REAL DEFAULT 0,
currency TEXT DEFAULT 'CNY',
file_url TEXT,
notes TEXT,
status TEXT DEFAULT 'draft',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS budget_attachments (
id SERIAL PRIMARY KEY,
budget_project_id INTEGER REFERENCES budget_projects(id) ON DELETE CASCADE,
file_url TEXT,
file_type TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS project_contracts (
id SERIAL PRIMARY KEY,
project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
contract_no TEXT,
contract_amount REAL,
sign_date DATE,
file_url TEXT,
remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS project_milestones (
id SERIAL PRIMARY KEY,
project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
milestone_name TEXT,
amount REAL DEFAULT 0,
due_date DATE,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS project_receipts (
id SERIAL PRIMARY KEY,
project_id INTEGER REFERENCES projects(id),
milestone_id INTEGER REFERENCES project_milestones(id),
amount REAL DEFAULT 0,
receipt_date DATE,
financial_record_id INTEGER,
remark TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS subcontractor_events (
id SERIAL PRIMARY KEY,
subcontractor_id INTEGER REFERENCES subcontractors(id),
project_id INTEGER REFERENCES projects(id),
event_type VARCHAR(10),
score_change INTEGER,
event_date DATE,
title VARCHAR(200),
description TEXT,
created_by INTEGER REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 财务分类与账本(与 migrations 006/007 一致)
CREATE TABLE IF NOT EXISTS expense_categories (
id SERIAL PRIMARY KEY,
category_level1 TEXT NOT NULL,
category_level2 TEXT NOT NULL,
label TEXT NOT NULL,
description TEXT,
sort_order INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(category_level1, category_level2)
);
CREATE TABLE IF NOT EXISTS financial_records (
id SERIAL PRIMARY KEY,
record_code TEXT UNIQUE NOT NULL,
txn_type TEXT NOT NULL CHECK (txn_type IN ('income', 'expense')),
category_level1 TEXT NOT NULL,
category_level2 TEXT NOT NULL,
project_id INTEGER REFERENCES projects(id),
user_id INTEGER REFERENCES users(id),
user_name TEXT,
amount_original REAL NOT NULL DEFAULT 0,
currency TEXT NOT NULL DEFAULT 'CNY',
exchange_rate REAL NOT NULL DEFAULT 1,
amount_cny REAL NOT NULL DEFAULT 0,
record_date DATE NOT NULL,
counterparty_name TEXT,
counterparty_type TEXT,
counterparty_id INTEGER,
source TEXT NOT NULL DEFAULT 'manual',
source_id INTEGER,
source_code TEXT,
description TEXT,
voucher_no TEXT,
attachments TEXT,
status TEXT NOT NULL DEFAULT 'confirmed' CHECK (status IN ('confirmed', 'pending', 'voided')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_products_category ON products(category_id);
CREATE INDEX IF NOT EXISTS idx_purchase_requests_project ON purchase_requests(project_id);
CREATE INDEX IF NOT EXISTS idx_financial_records_project ON financial_records(project_id);
CREATE INDEX IF NOT EXISTS idx_expense_categories_level1 ON expense_categories(category_level1);
-- 兼容已存在的旧 projects 表,补充缺失字段
ALTER TABLE projects ADD COLUMN IF NOT EXISTS start_date DATE;
ALTER TABLE projects ADD COLUMN IF NOT EXISTS end_date DATE;
ALTER TABLE projects ADD COLUMN IF NOT EXISTS description TEXT;
ALTER TABLE projects ADD COLUMN IF NOT EXISTS type_template_id INTEGER;
ALTER TABLE projects ADD COLUMN IF NOT EXISTS current_phase VARCHAR(50);
ALTER TABLE projects ADD COLUMN IF NOT EXISTS phase_progress NUMERIC(5,2) DEFAULT 0;
-- 兼容已存在库:补充路由依赖字段
ALTER TABLE users ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT true;
UPDATE users SET is_active = true WHERE is_active IS NULL;
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS budget_code TEXT;
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS business_manager_id INTEGER REFERENCES users(id);
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS location TEXT;
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS survey_date DATE;
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS intermediary_name TEXT;
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS intermediary_fee_type TEXT DEFAULT 'fixed';
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS intermediary_fee REAL DEFAULT 0;
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS customer_requirements TEXT;
ALTER TABLE budget_projects ADD COLUMN IF NOT EXISTS survey_notes TEXT;
ALTER TABLE reimbursements ADD COLUMN IF NOT EXISTS approval_remark TEXT;