-- Migration: 013_CreditNotes.sql -- Description: Credit notes for reversing/correcting invoices -- Date: 2026-01-18 -- Credit note read models CREATE TABLE IF NOT EXISTS credit_note_read_models ( aggregate_id VARCHAR(255) PRIMARY KEY, company_id VARCHAR(255) NOT NULL, fiscal_year_id VARCHAR(255), customer_id VARCHAR(255) NOT NULL, customer_name VARCHAR(255) NOT NULL, credit_note_number VARCHAR(50) NOT NULL, original_invoice_id VARCHAR(255), original_invoice_number VARCHAR(50), credit_note_date DATE NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'Draft', -- Amounts amount_ex_vat DECIMAL(18, 2) NOT NULL DEFAULT 0, amount_vat DECIMAL(18, 2) NOT NULL DEFAULT 0, amount_total DECIMAL(18, 2) NOT NULL DEFAULT 0, amount_applied DECIMAL(18, 2) NOT NULL DEFAULT 0, amount_remaining DECIMAL(18, 2) NOT NULL DEFAULT 0, -- Lines stored as JSONB for flexibility lines JSONB NOT NULL DEFAULT '[]'::jsonb, -- Ledger integration ledger_transaction_id VARCHAR(255), -- Audit fields reason TEXT, issued_at TIMESTAMP WITH TIME ZONE, voided_at TIMESTAMP WITH TIME ZONE, voided_reason TEXT, -- EventFlow fields create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), update_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), last_aggregate_sequence_number BIGINT NOT NULL DEFAULT 0 ); -- Indexes for common queries CREATE INDEX IF NOT EXISTS idx_credit_note_read_models_company_id ON credit_note_read_models(company_id); CREATE INDEX IF NOT EXISTS idx_credit_note_read_models_customer_id ON credit_note_read_models(customer_id); CREATE INDEX IF NOT EXISTS idx_credit_note_read_models_status ON credit_note_read_models(status); CREATE INDEX IF NOT EXISTS idx_credit_note_read_models_original_invoice ON credit_note_read_models(original_invoice_id); CREATE INDEX IF NOT EXISTS idx_credit_note_read_models_date ON credit_note_read_models(credit_note_date); -- Unique constraint: credit note number must be unique per company CREATE UNIQUE INDEX IF NOT EXISTS idx_credit_note_read_models_number_unique ON credit_note_read_models(company_id, credit_note_number); -- Credit note applications (when credit is applied to invoices) CREATE TABLE IF NOT EXISTS credit_note_applications ( id TEXT PRIMARY KEY, company_id VARCHAR(255) NOT NULL, credit_note_id VARCHAR(255) NOT NULL, invoice_id VARCHAR(255) NOT NULL, amount DECIMAL(18, 2) NOT NULL, applied_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), applied_by VARCHAR(255) NOT NULL, ledger_transaction_id VARCHAR(255), CONSTRAINT fk_credit_note_applications_credit_note FOREIGN KEY (credit_note_id) REFERENCES credit_note_read_models(aggregate_id), CONSTRAINT fk_credit_note_applications_invoice FOREIGN KEY (invoice_id) REFERENCES invoice_read_models(aggregate_id) ); CREATE INDEX IF NOT EXISTS idx_credit_note_applications_credit_note ON credit_note_applications(credit_note_id); CREATE INDEX IF NOT EXISTS idx_credit_note_applications_invoice ON credit_note_applications(invoice_id);