-- Company execution budget analysis schema -- Target database: PostgreSQL 14+ create extension if not exists pgcrypto; create schema if not exists budget_app; set search_path = budget_app, public; create table companies ( id uuid primary key default gen_random_uuid(), code varchar(30) not null unique, name varchar(200) not null, business_number varchar(30), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table fiscal_years ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), fiscal_year integer not null, start_date date not null, end_date date not null, is_closed boolean not null default false, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_fiscal_year unique (company_id, fiscal_year), constraint chk_fiscal_year_dates check (start_date <= end_date) ); create table departments ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), parent_department_id uuid references departments(id), code varchar(30) not null, name varchar(100) not null, manager_name varchar(100), is_active boolean not null default true, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_department_code unique (company_id, code) ); create table employees ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), department_id uuid references departments(id), employee_no varchar(30) not null, name varchar(100) not null, title varchar(100), email varchar(200), is_active boolean not null default true, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_employee_no unique (company_id, employee_no) ); create table business_units ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), code varchar(30) not null, name varchar(100) not null, is_active boolean not null default true, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_business_unit_code unique (company_id, code) ); create table vendors ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), code varchar(30) not null, name varchar(200) not null, business_number varchar(30), contact_name varchar(100), phone varchar(50), email varchar(200), is_active boolean not null default true, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_vendor_code unique (company_id, code) ); create table clients ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), code varchar(30) not null, name varchar(200) not null, business_number varchar(30), contact_name varchar(100), phone varchar(50), email varchar(200), is_active boolean not null default true, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_client_code unique (company_id, code) ); create table projects ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), business_unit_id uuid references business_units(id), department_id uuid references departments(id), client_id uuid references clients(id), project_code varchar(40) not null, project_name varchar(200) not null, project_type varchar(50), status varchar(30) not null default 'planning', contract_amount numeric(18, 2) not null default 0, start_date date, end_date date, manager_employee_id uuid references employees(id), description text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_project_code unique (company_id, project_code), constraint chk_project_status check (status in ('planning', 'active', 'on_hold', 'closed', 'cancelled')) ); create table account_categories ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), code varchar(30) not null, name varchar(100) not null, category_type varchar(20) not null, sort_order integer not null default 0, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_account_category_code unique (company_id, code), constraint chk_account_category_type check (category_type in ('revenue', 'cost', 'expense')) ); create table accounts ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), category_id uuid not null references account_categories(id), code varchar(30) not null, name varchar(100) not null, account_type varchar(20) not null, is_active boolean not null default true, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_account_code unique (company_id, code), constraint chk_account_type check (account_type in ('revenue', 'cost', 'expense')) ); create table budget_versions ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), fiscal_year_id uuid not null references fiscal_years(id), version_name varchar(100) not null, version_no integer not null, status varchar(20) not null default 'draft', created_by uuid references employees(id), approved_by uuid references employees(id), approved_at timestamptz, notes text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_budget_version unique (company_id, fiscal_year_id, version_no), constraint chk_budget_version_status check (status in ('draft', 'submitted', 'approved', 'archived')) ); create table budget_items ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), budget_version_id uuid not null references budget_versions(id), project_id uuid references projects(id), department_id uuid references departments(id), account_id uuid not null references accounts(id), month_no integer not null, planned_amount numeric(18, 2) not null default 0, memo text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_budget_item unique (budget_version_id, project_id, department_id, account_id, month_no), constraint chk_budget_month check (month_no between 1 and 12) ); create table purchase_requests ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), project_id uuid references projects(id), department_id uuid references departments(id), vendor_id uuid references vendors(id), requester_employee_id uuid references employees(id), request_no varchar(40) not null, request_date date not null, status varchar(20) not null default 'requested', description text, total_amount numeric(18, 2) not null default 0, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_purchase_request_no unique (company_id, request_no), constraint chk_purchase_request_status check (status in ('requested', 'approved', 'rejected', 'ordered', 'cancelled')) ); create table purchase_request_items ( id uuid primary key default gen_random_uuid(), purchase_request_id uuid not null references purchase_requests(id) on delete cascade, account_id uuid not null references accounts(id), item_name varchar(200) not null, quantity numeric(18, 3) not null default 1, unit_price numeric(18, 2) not null default 0, amount numeric(18, 2) not null default 0, needed_date date, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table purchase_orders ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), purchase_request_id uuid references purchase_requests(id), project_id uuid references projects(id), department_id uuid references departments(id), vendor_id uuid references vendors(id), order_no varchar(40) not null, order_date date not null, status varchar(20) not null default 'issued', total_amount numeric(18, 2) not null default 0, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_purchase_order_no unique (company_id, order_no), constraint chk_purchase_order_status check (status in ('issued', 'partial_received', 'received', 'cancelled')) ); create table purchase_order_items ( id uuid primary key default gen_random_uuid(), purchase_order_id uuid not null references purchase_orders(id) on delete cascade, account_id uuid not null references accounts(id), item_name varchar(200) not null, quantity numeric(18, 3) not null default 1, unit_price numeric(18, 2) not null default 0, amount numeric(18, 2) not null default 0, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table invoices ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), project_id uuid references projects(id), department_id uuid references departments(id), vendor_id uuid references vendors(id), client_id uuid references clients(id), invoice_no varchar(40) not null, invoice_type varchar(20) not null, issue_date date not null, due_date date, supply_amount numeric(18, 2) not null default 0, tax_amount numeric(18, 2) not null default 0, total_amount numeric(18, 2) not null default 0, status varchar(20) not null default 'issued', created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_invoice_no unique (company_id, invoice_no), constraint chk_invoice_type check (invoice_type in ('sales', 'purchase')), constraint chk_invoice_status check (status in ('issued', 'paid', 'cancelled')) ); create table actual_transactions ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), fiscal_year_id uuid references fiscal_years(id), project_id uuid references projects(id), department_id uuid references departments(id), account_id uuid not null references accounts(id), vendor_id uuid references vendors(id), client_id uuid references clients(id), employee_id uuid references employees(id), source_type varchar(30) not null, source_id uuid, transaction_date date not null, month_no integer not null, transaction_type varchar(20) not null, amount numeric(18, 2) not null, description text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint chk_actual_month check (month_no between 1 and 12), constraint chk_transaction_type check (transaction_type in ('revenue', 'cost', 'expense')), constraint chk_source_type check (source_type in ('manual', 'invoice', 'purchase_order', 'erp', 'excel_upload')) ); create table cashflow_transactions ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), project_id uuid references projects(id), department_id uuid references departments(id), transaction_date date not null, cashflow_type varchar(20) not null, amount numeric(18, 2) not null, description text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint chk_cashflow_type check (cashflow_type in ('inflow', 'outflow')) ); create table file_import_logs ( id uuid primary key default gen_random_uuid(), company_id uuid not null references companies(id), import_type varchar(30) not null, file_name varchar(255) not null, row_count integer not null default 0, success_count integer not null default 0, failure_count integer not null default 0, imported_by uuid references employees(id), imported_at timestamptz not null default now(), notes text, constraint chk_import_type check (import_type in ('budget', 'actual', 'project', 'vendor', 'client', 'account')) ); create table staging_ptc_transactions ( id uuid primary key default gen_random_uuid(), import_batch varchar(50) not null, source_file_name varchar(255) not null, source_sheet_name varchar(100) not null default 'Sheet1', source_row_no integer not null, transaction_date_raw varchar(50), transaction_date date, in_out varchar(20), account_code_raw varchar(30), account_name_raw varchar(100), department_name_raw varchar(100), vendor_name_raw varchar(200), project_code_raw varchar(50), project_type_raw varchar(50), project_name_raw varchar(200), description_raw text, supply_amount_raw varchar(50), vat_amount_raw varchar(50), total_amount_raw varchar(50), remarks_raw text, supply_amount numeric(18, 2), vat_amount numeric(18, 2), total_amount numeric(18, 2), normalized_transaction_type varchar(20), load_status varchar(20) not null default 'loaded', load_error text, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint uq_staging_ptc_row unique (import_batch, source_row_no), constraint chk_staging_load_status check (load_status in ('loaded', 'mapped', 'error')) ); create index idx_projects_company_status on projects(company_id, status); create index idx_projects_department on projects(department_id); create index idx_budget_items_project on budget_items(project_id, month_no); create index idx_budget_items_department on budget_items(department_id, month_no); create index idx_actual_transactions_project on actual_transactions(project_id, transaction_date); create index idx_actual_transactions_department on actual_transactions(department_id, transaction_date); create index idx_actual_transactions_account on actual_transactions(account_id, transaction_date); create index idx_purchase_orders_project on purchase_orders(project_id, order_date); create index idx_invoices_project on invoices(project_id, issue_date); create index idx_staging_ptc_batch on staging_ptc_transactions(import_batch, source_row_no); create index idx_staging_ptc_project on staging_ptc_transactions(project_code_raw); create index idx_staging_ptc_account on staging_ptc_transactions(account_code_raw); create index idx_staging_ptc_department on staging_ptc_transactions(department_name_raw); create or replace view vw_budget_vs_actual_monthly as select bv.company_id, fy.fiscal_year, bi.project_id, bi.department_id, bi.account_id, bi.month_no, sum(bi.planned_amount) as budget_amount, coalesce(sum(at.amount), 0) as actual_amount, sum(bi.planned_amount) - coalesce(sum(at.amount), 0) as variance_amount, case when sum(bi.planned_amount) = 0 then 0 else round((coalesce(sum(at.amount), 0) / sum(bi.planned_amount)) * 100, 2) end as execution_rate from budget_items bi join budget_versions bv on bv.id = bi.budget_version_id join fiscal_years fy on fy.id = bv.fiscal_year_id left join actual_transactions at on at.company_id = bv.company_id and coalesce(at.project_id, '00000000-0000-0000-0000-000000000000'::uuid) = coalesce(bi.project_id, '00000000-0000-0000-0000-000000000000'::uuid) and coalesce(at.department_id, '00000000-0000-0000-0000-000000000000'::uuid) = coalesce(bi.department_id, '00000000-0000-0000-0000-000000000000'::uuid) and at.account_id = bi.account_id and at.month_no = bi.month_no group by bv.company_id, fy.fiscal_year, bi.project_id, bi.department_id, bi.account_id, bi.month_no; create or replace view vw_project_profit_summary as select p.id as project_id, p.project_code, p.project_name, p.status, p.contract_amount, coalesce(sum(case when a.account_type = 'revenue' then at.amount else 0 end), 0) as revenue_amount, coalesce(sum(case when a.account_type in ('cost', 'expense') then at.amount else 0 end), 0) as cost_amount, coalesce(sum(case when a.account_type = 'revenue' then at.amount else 0 end), 0) - coalesce(sum(case when a.account_type in ('cost', 'expense') then at.amount else 0 end), 0) as profit_amount, case when coalesce(sum(case when a.account_type = 'revenue' then at.amount else 0 end), 0) = 0 then 0 else round( ( ( coalesce(sum(case when a.account_type = 'revenue' then at.amount else 0 end), 0) - coalesce(sum(case when a.account_type in ('cost', 'expense') then at.amount else 0 end), 0) ) / coalesce(sum(case when a.account_type = 'revenue' then at.amount else 0 end), 0) ) * 100, 2 ) end as profit_rate from projects p left join actual_transactions at on at.project_id = p.id left join accounts a on a.id = at.account_id group by p.id, p.project_code, p.project_name, p.status, p.contract_amount;