448 lines
16 KiB
PL/PgSQL
448 lines
16 KiB
PL/PgSQL
-- Company Finance Database Schema
|
|||
|
|
-- PostgreSQL 15+
|
||
|
|
-- Created: 2026-03-08
|
||
|
|
|
||
|
|
-- Drop existing database if exists and create new one
|
||
|
|
DROP DATABASE IF EXISTS company_finance_db;
|
||
|
|
CREATE DATABASE company_finance_db
|
||
|
|
WITH
|
||
|
|
OWNER = postgres
|
||
|
|
ENCODING = 'UTF8'
|
||
|
|
LC_COLLATE = 'en_US.UTF-8'
|
||
|
|
LC_CTYPE = 'en_US.UTF-8'
|
||
|
|
TABLESPACE = pg_default
|
||
|
|
CONNECTION LIMIT = -1
|
||
|
|
IS_TEMPLATE = False;
|
||
|
|
|
||
|
|
-- Connect to the new database
|
||
|
|
\c company_finance_db;
|
||
|
|
|
||
|
|
-- Enable UUID extension
|
||
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 1. Users Table (假设已存在,这里创建简化版本)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE IF NOT EXISTS users (
|
||
|
|
user_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
username VARCHAR(50) UNIQUE NOT NULL,
|
||
|
|
email VARCHAR(100) UNIQUE NOT NULL,
|
||
|
|
full_name_zh VARCHAR(100),
|
||
|
|
full_name_th VARCHAR(100),
|
||
|
|
full_name_en VARCHAR(100),
|
||
|
|
role VARCHAR(50) NOT NULL DEFAULT 'user',
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 2. Product Categories Table (商品分类表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE product_categories (
|
||
|
|
category_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
category_code VARCHAR(20) UNIQUE NOT NULL,
|
||
|
|
name_zh VARCHAR(100) NOT NULL,
|
||
|
|
name_th VARCHAR(100),
|
||
|
|
name_en VARCHAR(100),
|
||
|
|
description_zh TEXT,
|
||
|
|
description_th TEXT,
|
||
|
|
description_en TEXT,
|
||
|
|
parent_category_id UUID REFERENCES product_categories(category_id) ON DELETE SET NULL,
|
||
|
|
sort_order INTEGER DEFAULT 0,
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 3. Products Table (商品表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE products (
|
||
|
|
product_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
product_code VARCHAR(50) UNIQUE NOT NULL,
|
||
|
|
sku VARCHAR(50) UNIQUE,
|
||
|
|
name_zh VARCHAR(200) NOT NULL,
|
||
|
|
name_th VARCHAR(200),
|
||
|
|
name_en VARCHAR(200),
|
||
|
|
description_zh TEXT,
|
||
|
|
description_th TEXT,
|
||
|
|
description_en TEXT,
|
||
|
|
category_id UUID REFERENCES product_categories(category_id) ON DELETE SET NULL,
|
||
|
|
unit_price NUMERIC(15,2) NOT NULL DEFAULT 0.00,
|
||
|
|
currency VARCHAR(3) DEFAULT 'CNY',
|
||
|
|
unit_type VARCHAR(50) DEFAULT 'piece',
|
||
|
|
specifications JSONB,
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 4. Projects Table (项目表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE projects (
|
||
|
|
project_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
project_code VARCHAR(50) UNIQUE NOT NULL,
|
||
|
|
name_zh VARCHAR(200) NOT NULL,
|
||
|
|
name_th VARCHAR(200),
|
||
|
|
name_en VARCHAR(200),
|
||
|
|
description_zh TEXT,
|
||
|
|
description_th TEXT,
|
||
|
|
description_en TEXT,
|
||
|
|
client_name_zh VARCHAR(200),
|
||
|
|
client_name_th VARCHAR(200),
|
||
|
|
client_name_en VARCHAR(200),
|
||
|
|
contract_number VARCHAR(100),
|
||
|
|
contract_amount NUMERIC(15,2) NOT NULL DEFAULT 0.00,
|
||
|
|
currency VARCHAR(3) DEFAULT 'CNY',
|
||
|
|
start_date DATE,
|
||
|
|
end_date DATE,
|
||
|
|
status VARCHAR(50) DEFAULT 'planning' CHECK (status IN ('planning', 'in_progress', 'completed', 'cancelled', 'on_hold')),
|
||
|
|
project_manager_id UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 5. Payment Nodes Table (付款节点表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE payment_nodes (
|
||
|
|
node_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
project_id UUID REFERENCES projects(project_id) ON DELETE CASCADE,
|
||
|
|
node_code VARCHAR(50) NOT NULL,
|
||
|
|
name_zh VARCHAR(200) NOT NULL,
|
||
|
|
name_th VARCHAR(200),
|
||
|
|
name_en VARCHAR(200),
|
||
|
|
description_zh TEXT,
|
||
|
|
description_th TEXT,
|
||
|
|
description_en TEXT,
|
||
|
|
planned_date DATE NOT NULL,
|
||
|
|
actual_date DATE,
|
||
|
|
planned_amount NUMERIC(15,2) NOT NULL DEFAULT 0.00,
|
||
|
|
actual_amount NUMERIC(15,2),
|
||
|
|
currency VARCHAR(3) DEFAULT 'CNY',
|
||
|
|
node_type VARCHAR(50) DEFAULT 'payment' CHECK (node_type IN ('payment', 'receipt', 'milestone')),
|
||
|
|
status VARCHAR(50) DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'completed', 'cancelled', 'delayed')),
|
||
|
|
sort_order INTEGER DEFAULT 0,
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE(project_id, node_code)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 6. Payment Records Table (收付款记录表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE payment_records (
|
||
|
|
record_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
node_id UUID REFERENCES payment_nodes(node_id) ON DELETE CASCADE,
|
||
|
|
record_type VARCHAR(50) NOT NULL CHECK (record_type IN ('payment', 'receipt')),
|
||
|
|
amount NUMERIC(15,2) NOT NULL DEFAULT 0.00,
|
||
|
|
currency VARCHAR(3) DEFAULT 'CNY',
|
||
|
|
exchange_rate NUMERIC(10,6) DEFAULT 1.000000,
|
||
|
|
converted_amount NUMERIC(15,2),
|
||
|
|
payment_date DATE NOT NULL,
|
||
|
|
payment_method VARCHAR(50) DEFAULT 'bank_transfer' CHECK (payment_method IN ('bank_transfer', 'cash', 'check', 'credit_card', 'digital_wallet')),
|
||
|
|
reference_number VARCHAR(100),
|
||
|
|
bank_name VARCHAR(200),
|
||
|
|
account_number VARCHAR(100),
|
||
|
|
payer_name VARCHAR(200),
|
||
|
|
payee_name VARCHAR(200),
|
||
|
|
description TEXT,
|
||
|
|
status VARCHAR(50) DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'failed', 'cancelled')),
|
||
|
|
attachment_urls TEXT[],
|
||
|
|
verified_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
verified_at TIMESTAMP WITH TIME ZONE,
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 7. Exchange Rates Table (汇率表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE exchange_rates (
|
||
|
|
rate_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
base_currency VARCHAR(3) NOT NULL,
|
||
|
|
target_currency VARCHAR(3) NOT NULL,
|
||
|
|
exchange_rate NUMERIC(10,6) NOT NULL,
|
||
|
|
effective_date DATE NOT NULL,
|
||
|
|
source VARCHAR(100) DEFAULT 'manual',
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE(base_currency, target_currency, effective_date)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 8. Language Configs Table (多语言配置表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE language_configs (
|
||
|
|
config_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
config_key VARCHAR(100) NOT NULL,
|
||
|
|
module VARCHAR(50) NOT NULL,
|
||
|
|
value_zh TEXT NOT NULL,
|
||
|
|
value_th TEXT,
|
||
|
|
value_en TEXT,
|
||
|
|
description TEXT,
|
||
|
|
sort_order INTEGER DEFAULT 0,
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE(config_key, module)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- 9. Project Products Table (项目商品关联表)
|
||
|
|
-- ============================================
|
||
|
|
CREATE TABLE project_products (
|
||
|
|
project_product_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
project_id UUID REFERENCES projects(project_id) ON DELETE CASCADE,
|
||
|
|
product_id UUID REFERENCES products(product_id) ON DELETE CASCADE,
|
||
|
|
quantity INTEGER NOT NULL DEFAULT 1,
|
||
|
|
unit_price NUMERIC(15,2) NOT NULL DEFAULT 0.00,
|
||
|
|
currency VARCHAR(3) DEFAULT 'CNY',
|
||
|
|
total_amount NUMERIC(15,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
||
|
|
description TEXT,
|
||
|
|
is_active BOOLEAN DEFAULT TRUE,
|
||
|
|
created_by UUID REFERENCES users(user_id) ON DELETE SET NULL,
|
||
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||
|
|
UNIQUE(project_id, product_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- INDEXES
|
||
|
|
-- ============================================
|
||
|
|
|
||
|
|
-- Users indexes
|
||
|
|
CREATE INDEX idx_users_username ON users(username);
|
||
|
|
CREATE INDEX idx_users_email ON users(email);
|
||
|
|
CREATE INDEX idx_users_role ON users(role);
|
||
|
|
|
||
|
|
-- Product categories indexes
|
||
|
|
CREATE INDEX idx_product_categories_code ON product_categories(category_code);
|
||
|
|
CREATE INDEX idx_product_categories_parent ON product_categories(parent_category_id);
|
||
|
|
CREATE INDEX idx_product_categories_active ON product_categories(is_active);
|
||
|
|
|
||
|
|
-- Products indexes
|
||
|
|
CREATE INDEX idx_products_code ON products(product_code);
|
||
|
|
CREATE INDEX idx_products_sku ON products(sku);
|
||
|
|
CREATE INDEX idx_products_category ON products(category_id);
|
||
|
|
CREATE INDEX idx_products_active ON products(is_active);
|
||
|
|
CREATE INDEX idx_products_price ON products(unit_price);
|
||
|
|
|
||
|
|
-- Projects indexes
|
||
|
|
CREATE INDEX idx_projects_code ON projects(project_code);
|
||
|
|
CREATE INDEX idx_projects_status ON projects(status);
|
||
|
|
CREATE INDEX idx_projects_manager ON projects(project_manager_id);
|
||
|
|
CREATE INDEX idx_projects_dates ON projects(start_date, end_date);
|
||
|
|
CREATE INDEX idx_projects_active ON projects(is_active);
|
||
|
|
|
||
|
|
-- Payment nodes indexes
|
||
|
|
CREATE INDEX idx_payment_nodes_project ON payment_nodes(project_id);
|
||
|
|
CREATE INDEX idx_payment_nodes_code ON payment_nodes(node_code);
|
||
|
|
CREATE INDEX idx_payment_nodes_dates ON payment_nodes(planned_date, actual_date);
|
||
|
|
CREATE INDEX idx_payment_nodes_status ON payment_nodes(status);
|
||
|
|
CREATE INDEX idx_payment_nodes_type ON payment_nodes(node_type);
|
||
|
|
|
||
|
|
-- Payment records indexes
|
||
|
|
CREATE INDEX idx_payment_records_node ON payment_records(node_id);
|
||
|
|
CREATE INDEX idx_payment_records_type ON payment_records(record_type);
|
||
|
|
CREATE INDEX idx_payment_records_date ON payment_records(payment_date);
|
||
|
|
CREATE INDEX idx_payment_records_status ON payment_records(status);
|
||
|
|
CREATE INDEX idx_payment_records_method ON payment_records(payment_method);
|
||
|
|
|
||
|
|
-- Exchange rates indexes
|
||
|
|
CREATE INDEX idx_exchange_rates_currencies ON exchange_rates(base_currency, target_currency);
|
||
|
|
CREATE INDEX idx_exchange_rates_date ON exchange_rates(effective_date);
|
||
|
|
CREATE INDEX idx_exchange_rates_active ON exchange_rates(is_active);
|
||
|
|
|
||
|
|
-- Language configs indexes
|
||
|
|
CREATE INDEX idx_language_configs_key ON language_configs(config_key);
|
||
|
|
CREATE INDEX idx_language_configs_module ON language_configs(module);
|
||
|
|
CREATE INDEX idx_language_configs_active ON language_configs(is_active);
|
||
|
|
|
||
|
|
-- Project products indexes
|
||
|
|
CREATE INDEX idx_project_products_project ON project_products(project_id);
|
||
|
|
CREATE INDEX idx_project_products_product ON project_products(product_id);
|
||
|
|
CREATE INDEX idx_project_products_active ON project_products(is_active);
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- VIEWS
|
||
|
|
-- ============================================
|
||
|
|
|
||
|
|
-- View: 项目概览视图
|
||
|
|
CREATE OR REPLACE VIEW project_overview AS
|
||
|
|
SELECT
|
||
|
|
p.project_id,
|
||
|
|
p.project_code,
|
||
|
|
p.name_zh as project_name_zh,
|
||
|
|
p.name_en as project_name_en,
|
||
|
|
p.contract_amount,
|
||
|
|
p.currency,
|
||
|
|
p.start_date,
|
||
|
|
p.end_date,
|
||
|
|
p.status as project_status,
|
||
|
|
u.full_name_zh as project_manager_name,
|
||
|
|
COUNT(DISTINCT pp.product_id) as product_count,
|
||
|
|
COUNT(DISTINCT pn.node_id) as payment_node_count,
|
||
|
|
COALESCE(SUM(pr.amount), 0) as total_payments,
|
||
|
|
COALESCE(SUM(CASE WHEN pr.record_type = 'receipt' THEN pr.amount ELSE 0 END), 0) as total_receipts,
|
||
|
|
COALESCE(SUM(CASE WHEN pr.record_type = 'payment' THEN pr.amount ELSE 0 END), 0) as total_expenses
|
||
|
|
FROM projects p
|
||
|
|
LEFT JOIN users u ON p.project_manager_id = u.user_id
|
||
|
|
LEFT JOIN project_products pp ON p.project_id = pp.project_id
|
||
|
|
LEFT JOIN payment_nodes pn ON p.project_id = pn.project_id
|
||
|
|
LEFT JOIN payment_records pr ON pn.node_id = pr.node_id
|
||
|
|
WHERE p.is_active = TRUE
|
||
|
|
GROUP BY p.project_id, p.project_code, p.name_zh, p.name_en, p.contract_amount, p.currency,
|
||
|
|
p.start_date, p.end_date, p.status, u.full_name_zh;
|
||
|
|
|
||
|
|
-- View: 付款节点详情视图
|
||
|
|
CREATE OR REPLACE VIEW payment_node_details AS
|
||
|
|
SELECT
|
||
|
|
pn.node_id,
|
||
|
|
pn.project_id,
|
||
|
|
p.project_code,
|
||
|
|
p.name_zh as project_name_zh,
|
||
|
|
pn.node_code,
|
||
|
|
pn.name_zh as node_name_zh,
|
||
|
|
pn.planned_date,
|
||
|
|
pn.actual_date,
|
||
|
|
pn.planned_amount,
|
||
|
|
pn.actual_amount,
|
||
|
|
pn.currency,
|
||
|
|
pn.node_type,
|
||
|
|
pn.status as node_status,
|
||
|
|
COUNT(pr.record_id) as record_count,
|
||
|
|
COALESCE(SUM(pr.amount), 0) as total_recorded_amount,
|
||
|
|
CASE
|
||
|
|
WHEN pn.node_type = 'payment' THEN '支出'
|
||
|
|
WHEN pn.node_type = 'receipt' THEN '收入'
|
||
|
|
ELSE '里程碑'
|
||
|
|
END as node_type_cn
|
||
|
|
FROM payment_nodes pn
|
||
|
|
JOIN projects p ON pn.project_id = p.project_id
|
||
|
|
LEFT JOIN payment_records pr ON pn.node_id = pr.node_id AND pr.is_active = TRUE
|
||
|
|
WHERE pn.is_active = TRUE
|
||
|
|
GROUP BY pn.node_id, pn.project_id, p.project_code, p.name_zh, pn.node_code, pn.name_zh,
|
||
|
|
pn.planned_date, pn.actual_date, pn.planned_amount, pn.actual_amount, pn.currency,
|
||
|
|
pn.node_type, pn.status;
|
||
|
|
|
||
|
|
-- View: 商品分类树视图
|
||
|
|
CREATE OR REPLACE VIEW product_category_tree AS
|
||
|
|
WITH RECURSIVE category_tree AS (
|
||
|
|
SELECT
|
||
|
|
category_id,
|
||
|
|
category_code,
|
||
|
|
name_zh,
|
||
|
|
name_en,
|
||
|
|
parent_category_id,
|
||
|
|
1 as level,
|
||
|
|
ARRAY[category_code] as path_codes,
|
||
|
|
ARRAY[name_zh] as path_names
|
||
|
|
FROM product_categories
|
||
|
|
WHERE parent_category_id IS NULL AND is_active = TRUE
|
||
|
|
|
||
|
|
UNION ALL
|
||
|
|
|
||
|
|
SELECT
|
||
|
|
c.category_id,
|
||
|
|
c.category_code,
|
||
|
|
c.name_zh,
|
||
|
|
c.name_en,
|
||
|
|
c.parent_category_id,
|
||
|
|
ct.level + 1,
|
||
|
|
ct.path_codes || c.category_code,
|
||
|
|
ct.path_names || c.name_zh
|
||
|
|
FROM product_categories c
|
||
|
|
JOIN category_tree ct ON c.parent_category_id = ct.category_id
|
||
|
|
WHERE c.is_active = TRUE
|
||
|
|
)
|
||
|
|
SELECT
|
||
|
|
category_id,
|
||
|
|
category_code,
|
||
|
|
name_zh,
|
||
|
|
name_en,
|
||
|
|
parent_category_id,
|
||
|
|
level,
|
||
|
|
array_to_string(path_codes, ' > ') as full_path_code,
|
||
|
|
array_to_string(path_names, ' > ') as full_path_name
|
||
|
|
FROM category_tree
|
||
|
|
ORDER BY path_codes;
|
||
|
|
|
||
|
|
-- View: 汇率最新视图
|
||
|
|
CREATE OR REPLACE VIEW latest_exchange_rates AS
|
||
|
|
SELECT DISTINCT ON (base_currency, target_currency)
|
||
|
|
rate_id,
|
||
|
|
base_currency,
|
||
|
|
target_currency,
|
||
|
|
exchange_rate,
|
||
|
|
effective_date,
|
||
|
|
source,
|
||
|
|
created_at
|
||
|
|
FROM exchange_rates
|
||
|
|
WHERE is_active = TRUE
|
||
|
|
ORDER BY base_currency, target_currency, effective_date DESC;
|
||
|
|
|
||
|
|
-- ============================================
|
||
|
|
-- FUNCTIONS AND TRIGGERS
|
||
|
|
-- ============================================
|
||
|
|
|
||
|
|
-- Function to update updated_at timestamp
|
||
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
BEGIN
|
||
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
||
|
|
RETURN NEW;
|
||
|
|
END;
|
||
|
|
$$ language 'plpgsql';
|
||
|
|
|
||
|
|
-- Create triggers for all tables with updated_at column
|
||
|
|
DO $$
|
||
|
|
DECLARE
|
||
|
|
table_name text;
|
||
|
|
BEGIN
|
||
|
|
FOR table_name IN
|
||
|
|
SELECT tablename FROM pg_tables
|
||
|
|
WHERE schemaname = 'public'
|
||
|
|
AND tablename IN (
|
||
|
|
'users', 'product_categories', 'products', 'projects',
|
||
|
|
'payment_nodes', 'payment_records', 'exchange_rates',
|
||
|
|
'language_configs', 'project_products'
|
||
|
|
)
|
||
|
|
LOOP
|
||
|
|
EXECUTE format('
|
||
|
|
DROP TRIGGER IF EXISTS update_%s_updated_at ON %s;
|
||
|
|
CREATE TRIGGER update_%s_updated_at
|
||
|
|
BEFORE UPDATE ON %s
|
||
|
|
FOR EACH ROW
|
||
|
|
EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
', table_name, table_name, table_name, table_name);
|
||
|
|
END LOOP;
|
||
|
|
END $$;
|
||
|
|
|
||
|
|
-- Function to calculate project financial summary
|
||
|
|
CREATE OR REPLACE FUNCTION calculate_project_financial_summary(project_uuid UUID)
|
||
|
|
RETURNS TABLE(
|
||
|
|
total_contract_amount NUMERIC,
|
||
|
|
total_planned_payments NUMERIC,
|
||
|
|
total_actual_payments NUMERIC,
|
||
|
|
total_planned_receipts NUMERIC,
|
||
|
|
total_actual_receipts NUMERIC,
|
||
|
|
balance NUMERIC
|
||
|
|
) AS $$
|
||
|
|
BEGIN
|
||
|
|
RETURN QUERY
|
||
|
|
SELECT
|
||
|
|
COALESCE(p.contract_amount,
|