This commit is contained in:
Tomas Dvorak
2026-01-26 08:13:18 +01:00
parent aa036b6550
commit dfc079288f
505 changed files with 95755 additions and 5712 deletions
@@ -0,0 +1,406 @@
-- Create invoice management tables
-- Migration: 20250110000002_create_invoice_tables
-- Invoice settings table
CREATE TABLE invoice_settings (
id SERIAL PRIMARY KEY,
company_name VARCHAR(255) NOT NULL,
company_ico VARCHAR(20) NOT NULL,
company_dic VARCHAR(20),
company_address TEXT,
company_city VARCHAR(100),
company_zip VARCHAR(10),
company_country VARCHAR(100) DEFAULT 'Česká republika',
bank_name VARCHAR(255),
bank_account VARCHAR(50),
bank_iban VARCHAR(50),
bank_swift VARCHAR(20),
invoice_number_format VARCHAR(100) DEFAULT 'F{year}{seq:6}',
next_invoice_number INTEGER DEFAULT 1,
current_year INTEGER,
default_payment_term INTEGER DEFAULT 14,
default_vat_rate DECIMAL(5,2) DEFAULT 21.0,
default_currency VARCHAR(3) DEFAULT 'CZK',
email_from VARCHAR(255),
email_subject VARCHAR(255) DEFAULT 'Faktura č. {invoice_number}',
email_body TEXT,
pdf_logo_path VARCHAR(500),
pdf_footer TEXT,
registration_number VARCHAR(50),
tax_registration_number VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_by INTEGER
);
-- Invoice customers table
CREATE TABLE invoice_customers (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
ico VARCHAR(20) UNIQUE,
dic VARCHAR(20),
address TEXT,
city VARCHAR(100),
zip VARCHAR(10),
country VARCHAR(100) DEFAULT 'Česká republika',
email VARCHAR(255),
phone VARCHAR(50),
website VARCHAR(255),
business_type VARCHAR(100),
vat_payer BOOLEAN DEFAULT true,
notes TEXT,
active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by INTEGER,
updated_by INTEGER
);
CREATE INDEX idx_invoice_customers_ico ON invoice_customers(ico);
CREATE INDEX idx_invoice_customers_active ON invoice_customers(active);
CREATE INDEX idx_invoice_customers_name ON invoice_customers(name);
-- Invoice sequences table
CREATE TABLE invoice_sequences (
id SERIAL PRIMARY KEY,
type VARCHAR(50) NOT NULL,
year INTEGER NOT NULL,
current_number INTEGER DEFAULT 1,
prefix VARCHAR(20),
suffix VARCHAR(20),
padding INTEGER DEFAULT 6,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(type, year)
);
-- Insert default sequences
INSERT INTO invoice_sequences (type, year, current_number, prefix) VALUES
('faktura', EXTRACT(YEAR FROM CURRENT_DATE), 1, 'F'),
('zalohova_faktura', EXTRACT(YEAR FROM CURRENT_DATE), 1, 'ZF'),
('proforma_faktura', EXTRACT(YEAR FROM CURRENT_DATE), 1, 'PF'),
('dobropis', EXTRACT(YEAR FROM CURRENT_DATE), 1, 'D');
-- Invoice templates table
CREATE TABLE invoice_templates (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL,
description TEXT,
header_html TEXT,
body_html TEXT,
footer_html TEXT,
css TEXT,
default_vat_rate DECIMAL(5,2) DEFAULT 21.0,
default_payment_term INTEGER DEFAULT 14,
active BOOLEAN DEFAULT true,
default BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by INTEGER,
updated_by INTEGER
);
-- Invoices table
CREATE TABLE invoices (
id SERIAL PRIMARY KEY,
invoice_number VARCHAR(50) NOT NULL UNIQUE,
invoice_type VARCHAR(20) DEFAULT 'faktura',
variable_symbol VARCHAR(20),
constant_symbol VARCHAR(20),
specific_symbol VARCHAR(20),
issue_date TIMESTAMP NOT NULL,
due_date TIMESTAMP NOT NULL,
taxable_supply_date TIMESTAMP,
-- Supplier information (auto-filled)
supplier_name VARCHAR(255) NOT NULL,
supplier_ico VARCHAR(20),
supplier_dic VARCHAR(20),
supplier_address TEXT,
supplier_city VARCHAR(100),
supplier_zip VARCHAR(10),
supplier_country VARCHAR(100) DEFAULT 'Česká republika',
-- Supplier bank information
bank_name VARCHAR(255),
bank_account VARCHAR(50),
bank_iban VARCHAR(50),
bank_swift VARCHAR(20),
-- Customer information
customer_id INTEGER REFERENCES invoice_customers(id),
customer_name VARCHAR(255) NOT NULL,
customer_ico VARCHAR(20),
customer_dic VARCHAR(20),
customer_address TEXT,
customer_city VARCHAR(100),
customer_zip VARCHAR(10),
customer_country VARCHAR(100) DEFAULT 'Česká republika',
customer_email VARCHAR(255),
customer_phone VARCHAR(50),
-- Financial summary
total_amount DECIMAL(15,2) NOT NULL,
total_vat DECIMAL(15,2) NOT NULL,
total_amount_vat DECIMAL(15,2) NOT NULL,
total_amount_without_vat DECIMAL(15,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'CZK',
-- Status and workflow
status VARCHAR(20) DEFAULT 'draft',
payment_status VARCHAR(20) DEFAULT 'unpaid',
payment_date TIMESTAMP,
paid_amount DECIMAL(15,2) DEFAULT 0,
-- Additional information
note TEXT,
payment_note TEXT,
internal_note TEXT,
-- PDF and sending
pdf_path VARCHAR(500),
pdf_generated_at TIMESTAMP,
sent_at TIMESTAMP,
sent_to TEXT,
-- Metadata
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by INTEGER,
updated_by INTEGER
);
CREATE INDEX idx_invoices_invoice_number ON invoices(invoice_number);
CREATE INDEX idx_invoices_status ON invoices(status);
CREATE INDEX idx_invoices_payment_status ON invoices(payment_status);
CREATE INDEX idx_invoices_customer_id ON invoices(customer_id);
CREATE INDEX idx_invoices_issue_date ON invoices(issue_date);
CREATE INDEX idx_invoices_due_date ON invoices(due_date);
CREATE INDEX idx_invoices_customer_ico ON invoices(customer_ico);
-- Invoice items table
CREATE TABLE invoice_items (
id SERIAL PRIMARY KEY,
invoice_id INTEGER NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
description TEXT NOT NULL,
quantity DECIMAL(12,3) NOT NULL,
unit VARCHAR(20) DEFAULT 'ks',
unit_price DECIMAL(15,2) NOT NULL,
total_price DECIMAL(15,2) NOT NULL,
vat_rate DECIMAL(5,2) NOT NULL,
vat_amount DECIMAL(15,2) NOT NULL,
total_with_vat DECIMAL(15,2) NOT NULL,
code VARCHAR(100),
note TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_invoice_items_invoice_id ON invoice_items(invoice_id);
-- Invoice payments table
CREATE TABLE invoice_payments (
id SERIAL PRIMARY KEY,
invoice_id INTEGER NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
amount DECIMAL(15,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'CZK',
payment_date TIMESTAMP NOT NULL,
payment_method VARCHAR(50),
variable_symbol VARCHAR(20),
constant_symbol VARCHAR(20),
specific_symbol VARCHAR(20),
bank_account VARCHAR(50),
note TEXT,
reference_number VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by INTEGER
);
CREATE INDEX idx_invoice_payments_invoice_id ON invoice_payments(invoice_id);
CREATE INDEX idx_invoice_payments_payment_date ON invoice_payments(payment_date);
-- Insert default invoice settings
INSERT INTO invoice_settings (
company_name,
company_ico,
company_dic,
company_address,
company_city,
company_zip,
company_country,
bank_name,
bank_account,
bank_iban,
bank_swift,
invoice_number_format,
next_invoice_number,
current_year,
default_payment_term,
default_vat_rate,
default_currency,
email_subject,
registration_number,
tax_registration_number
) VALUES (
'Fotbalový klub',
'12345678',
'CZ12345678',
'Sportovní 1',
'Praha',
'11000',
'Česká republika',
'Česká spořitelna',
'123456789/0800',
'CZ650800000000123456789',
'GIBACZPX',
'F{year}{seq:6}',
1,
EXTRACT(YEAR FROM CURRENT_DATE)::INTEGER,
14,
21.0,
'CZK',
'Faktura č. {invoice_number}',
'Spisová značka: 12345',
'DIČ: CZ12345678'
);
-- Insert default invoice template
INSERT INTO invoice_templates (
name,
type,
description,
header_html,
body_html,
footer_html,
css,
default_vat_rate,
default_payment_term,
active,
default
) VALUES (
'Standardní faktura',
'standard',
'Standardní šablona pro faktury',
'<div class="invoice-header">
<div class="logo">
<img src="{logo_url}" alt="Logo" />
</div>
<div class="supplier-info">
<h2>{supplier_name}</h2>
<p>{supplier_address}</p>
<p>{supplier_zip} {supplier_city}</p>
<p>IČO: {supplier_ico}</p>
{supplier_dic ? `<p>DIČ: {supplier_dic}</p>` : ''}
</div>
<div class="invoice-info">
<h1>Faktura</h1>
<p>Číslo: {invoice_number}</p>
<p>Datum vydání: {issue_date}</p>
<p>Datum splatnosti: {due_date}</p>
<p> Datum zdanitelného plnění: {taxable_supply_date}</p>
</div>
</div>',
'<div class="invoice-body">
<div class="customer-info">
<h3>Odběratel</h3>
<p><strong>{customer_name}</strong></p>
{customer_ico ? `<p>IČO: {customer_ico}</p>` : ''}
{customer_dic ? `<p>DIČ: {customer_dic}</p>` : ''}
<p>{customer_address}</p>
<p>{customer_zip} {customer_city}</p>
{customer_country !== 'Česká republika' ? `<p>{customer_country}</p>` : ''}
</div>
<div class="invoice-items">
<table>
<thead>
<tr>
<th>Položka</th>
<th>Množství</th>
<th>Jedn. cena</th>
<th>DPH %</th>
<th>Celkem</th>
</tr>
</thead>
<tbody>
{items}
</tbody>
</table>
</div>
<div class="invoice-summary">
<table>
<tr>
<td>Celkem bez DPH:</td>
<td class="amount">{total_amount_without_vat}</td>
</tr>
<tr>
<td>DPH {total_vat_rate}%:</td>
<td class="amount">{total_vat}</td>
</tr>
<tr class="total">
<td>Celkem k úhradě:</td>
<td class="amount">{total_amount_vat}</td>
</tr>
</table>
</div>
</div>',
'<div class="invoice-footer">
<div class="payment-info">
<h3>Platební údaje</h3>
<p><strong>Banka:</strong> {bank_name}</p>
<p><strong>Číslo účtu:</strong> {bank_account}</p>
<p><strong>IBAN:</strong> {bank_iban}</p>
<p><strong>SWIFT:</strong> {bank_swift}</p>
{variable_symbol ? `<p><strong>Var. symbol:</strong> {variable_symbol}</p>` : ''}
{constant_symbol ? `<p><strong>Konst. symbol:</strong> {constant_symbol}</p>` : ''}
</div>
<div class="notes">
{payment_note ? `<p><strong>Poznámka:</strong> {payment_note}</p>` : ''}
{note ? `<p>{note}</p>` : ''}
</div>
<div class="signature">
<p>Vystavil:</p>
<div class="signature-line"></div>
</div>
</div>',
'body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.invoice-header { display: flex; justify-content: space-between; margin-bottom: 40px; }
.logo img { max-height: 80px; }
.invoice-info { text-align: right; }
.invoice-info h1 { margin: 0; font-size: 24px; }
.invoice-body { margin-bottom: 40px; }
.customer-info { margin-bottom: 30px; }
.invoice-items table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
.invoice-items th, .invoice-items td { border: 1px solid #ddd; padding: 8px; text-align: left; }
.invoice-items th { background-color: #f5f5f5; }
.invoice-items .amount { text-align: right; }
.invoice-summary table { width: 300px; margin-left: auto; }
.invoice-summary td { padding: 5px; }
.invoice-summary .total { font-weight: bold; border-top: 2px solid #333; }
.invoice-footer { margin-top: 40px; }
.payment-info { margin-bottom: 20px; }
.signature-line { border-bottom: 1px solid #333; width: 200px; margin-top: 40px; }',
21.0,
14,
true,
true
);
-- Create trigger to update updated_at column
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
-- Apply the trigger to all invoice tables
CREATE TRIGGER update_invoice_settings_updated_at BEFORE UPDATE ON invoice_settings FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_invoice_customers_updated_at BEFORE UPDATE ON invoice_customers FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_invoice_sequences_updated_at BEFORE UPDATE ON invoice_sequences FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_invoice_templates_updated_at BEFORE UPDATE ON invoice_templates FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_invoices_updated_at BEFORE UPDATE ON invoices FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_invoice_items_updated_at BEFORE UPDATE ON invoice_items FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_invoice_payments_updated_at BEFORE UPDATE ON invoice_payments FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();