Files
backend/database/migrations/038_invoice_templates.sql
T
Alireza HassaniandCursor 3faeb9bc0d Expand invoices with full templates, public viewer API, and account holder.
Add invoice template CRUD, key points/accounts, public GET endpoint, and migration 039 for account_holder_name.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 11:04:26 +03:30

191 lines
7.3 KiB
SQL

-- Meshkee CMS — full invoice templates + invoice key points / accounts
-- ---------------------------------------------------------------------------
-- invoice templates (full blueprint: name, top text, items, key points, accounts)
-- ---------------------------------------------------------------------------
CREATE TABLE invoice_templates (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
owner_scope invoice_owner_scope NOT NULL,
business_id BIGINT,
name VARCHAR(255) NOT NULL,
top_text TEXT,
sort_order INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT invoice_templates_business_id_fkey
FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE,
CONSTRAINT invoice_templates_name_nonempty
CHECK (char_length(trim(name)) > 0),
CONSTRAINT invoice_templates_scope_business_check
CHECK (
(owner_scope = 'platform' AND business_id IS NULL)
OR (owner_scope = 'business' AND business_id IS NOT NULL)
)
);
CREATE INDEX idx_invoice_templates_owner_scope
ON invoice_templates (owner_scope, sort_order);
CREATE INDEX idx_invoice_templates_business_id
ON invoice_templates (business_id, sort_order)
WHERE business_id IS NOT NULL;
CREATE TRIGGER invoice_templates_set_updated_at
BEFORE UPDATE ON invoice_templates
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
-- ---------------------------------------------------------------------------
-- template line items
-- ---------------------------------------------------------------------------
CREATE TABLE invoice_template_items (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
template_id BIGINT NOT NULL,
item_template_id BIGINT,
title VARCHAR(255) NOT NULL,
duration VARCHAR(100),
worktime VARCHAR(100),
description TEXT,
price NUMERIC(12, 2) NOT NULL DEFAULT 0,
discounted_price NUMERIC(12, 2),
sort_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT invoice_template_items_template_id_fkey
FOREIGN KEY (template_id) REFERENCES invoice_templates (id) ON DELETE CASCADE,
CONSTRAINT invoice_template_items_item_template_id_fkey
FOREIGN KEY (item_template_id) REFERENCES invoice_item_templates (id) ON DELETE SET NULL,
CONSTRAINT invoice_template_items_title_nonempty
CHECK (char_length(trim(title)) > 0),
CONSTRAINT invoice_template_items_price_non_negative
CHECK (price >= 0),
CONSTRAINT invoice_template_items_discounted_non_negative
CHECK (discounted_price IS NULL OR discounted_price >= 0)
);
CREATE INDEX idx_invoice_template_items_template_id
ON invoice_template_items (template_id, sort_order);
CREATE TRIGGER invoice_template_items_set_updated_at
BEFORE UPDATE ON invoice_template_items
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
-- ---------------------------------------------------------------------------
-- template key points (duplicatable)
-- ---------------------------------------------------------------------------
CREATE TABLE invoice_template_key_points (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
template_id BIGINT NOT NULL,
text TEXT NOT NULL,
sort_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT invoice_template_key_points_template_id_fkey
FOREIGN KEY (template_id) REFERENCES invoice_templates (id) ON DELETE CASCADE,
CONSTRAINT invoice_template_key_points_text_nonempty
CHECK (char_length(trim(text)) > 0)
);
CREATE INDEX idx_invoice_template_key_points_template_id
ON invoice_template_key_points (template_id, sort_order);
CREATE TRIGGER invoice_template_key_points_set_updated_at
BEFORE UPDATE ON invoice_template_key_points
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
-- ---------------------------------------------------------------------------
-- template bank accounts (duplicatable)
-- ---------------------------------------------------------------------------
CREATE TABLE invoice_template_accounts (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
template_id BIGINT NOT NULL,
bank_name VARCHAR(255) NOT NULL,
card_number VARCHAR(64),
iban VARCHAR(64),
sort_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT invoice_template_accounts_template_id_fkey
FOREIGN KEY (template_id) REFERENCES invoice_templates (id) ON DELETE CASCADE,
CONSTRAINT invoice_template_accounts_bank_name_nonempty
CHECK (char_length(trim(bank_name)) > 0)
);
CREATE INDEX idx_invoice_template_accounts_template_id
ON invoice_template_accounts (template_id, sort_order);
CREATE TRIGGER invoice_template_accounts_set_updated_at
BEFORE UPDATE ON invoice_template_accounts
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
-- ---------------------------------------------------------------------------
-- extend issued invoices
-- ---------------------------------------------------------------------------
ALTER TABLE invoices
ADD COLUMN IF NOT EXISTS top_text TEXT,
ADD COLUMN IF NOT EXISTS invoice_template_id BIGINT;
ALTER TABLE invoices
DROP CONSTRAINT IF EXISTS invoices_invoice_template_id_fkey;
ALTER TABLE invoices
ADD CONSTRAINT invoices_invoice_template_id_fkey
FOREIGN KEY (invoice_template_id) REFERENCES invoice_templates (id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_invoices_invoice_template_id
ON invoices (invoice_template_id)
WHERE invoice_template_id IS NOT NULL;
CREATE TABLE invoice_key_points (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
invoice_id BIGINT NOT NULL,
text TEXT NOT NULL,
sort_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT invoice_key_points_invoice_id_fkey
FOREIGN KEY (invoice_id) REFERENCES invoices (id) ON DELETE CASCADE,
CONSTRAINT invoice_key_points_text_nonempty
CHECK (char_length(trim(text)) > 0)
);
CREATE INDEX idx_invoice_key_points_invoice_id
ON invoice_key_points (invoice_id, sort_order);
CREATE TRIGGER invoice_key_points_set_updated_at
BEFORE UPDATE ON invoice_key_points
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
CREATE TABLE invoice_accounts (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
invoice_id BIGINT NOT NULL,
bank_name VARCHAR(255) NOT NULL,
card_number VARCHAR(64),
iban VARCHAR(64),
sort_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT invoice_accounts_invoice_id_fkey
FOREIGN KEY (invoice_id) REFERENCES invoices (id) ON DELETE CASCADE,
CONSTRAINT invoice_accounts_bank_name_nonempty
CHECK (char_length(trim(bank_name)) > 0)
);
CREATE INDEX idx_invoice_accounts_invoice_id
ON invoice_accounts (invoice_id, sort_order);
CREATE TRIGGER invoice_accounts_set_updated_at
BEFORE UPDATE ON invoice_accounts
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();