mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
hot fix #1
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
-- Remove unwanted admin navigation sections
|
||||
-- This SQL removes navigation items with the specified labels from the admin navigation
|
||||
|
||||
DELETE FROM navigation_items
|
||||
WHERE label IN ('Zařízení', 'Správa zařízení', 'Vybavení', 'Údržba')
|
||||
AND requires_admin = true;
|
||||
|
||||
-- Also remove any child items that might be orphaned
|
||||
DELETE FROM navigation_items
|
||||
WHERE parent_id IN (
|
||||
SELECT id FROM (
|
||||
SELECT id FROM navigation_items
|
||||
WHERE label IN ('Zařízení', 'Správa zařízení', 'Vybavení', 'Údržba')
|
||||
AND requires_admin = true
|
||||
) AS parent_ids
|
||||
);
|
||||
|
||||
-- Verify removal
|
||||
SELECT label, page_type, requires_admin FROM navigation_items
|
||||
WHERE label IN ('Zařízení', 'Správa zařízení', 'Vybavení', 'Údržba');
|
||||
@@ -38,6 +38,13 @@ func MigrateDB(db *gorm.DB) error {
|
||||
&models.ContactCategory{},
|
||||
&models.Contact{},
|
||||
&models.PageElementConfig{},
|
||||
&models.Invoice{},
|
||||
&models.InvoiceItem{},
|
||||
&models.InvoicePayment{},
|
||||
&models.InvoiceCustomer{},
|
||||
&models.InvoiceSettings{},
|
||||
&models.InvoiceSequence{},
|
||||
&models.InvoiceTemplate{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -61,6 +68,13 @@ func MigrateDB(db *gorm.DB) error {
|
||||
&models.ContactCategory{},
|
||||
&models.Contact{},
|
||||
&models.PageElementConfig{},
|
||||
&models.Invoice{},
|
||||
&models.InvoiceItem{},
|
||||
&models.InvoicePayment{},
|
||||
&models.InvoiceCustomer{},
|
||||
&models.InvoiceSettings{},
|
||||
&models.InvoiceSequence{},
|
||||
&models.InvoiceTemplate{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
-- Create languages table
|
||||
CREATE TABLE IF NOT EXISTS languages (
|
||||
id VARCHAR(5) PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
native_name VARCHAR(100) NOT NULL,
|
||||
code VARCHAR(10) NOT NULL UNIQUE,
|
||||
is_default BOOLEAN DEFAULT FALSE,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Create translations table
|
||||
CREATE TABLE IF NOT EXISTS translations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
key VARCHAR(200) NOT NULL,
|
||||
language_code VARCHAR(10) NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
context VARCHAR(100),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (language_code) REFERENCES languages(code) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create content_translations table
|
||||
CREATE TABLE IF NOT EXISTS content_translations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
content_type VARCHAR(50) NOT NULL,
|
||||
content_id INTEGER NOT NULL,
|
||||
language_code VARCHAR(10) NOT NULL,
|
||||
title VARCHAR(500),
|
||||
content TEXT,
|
||||
excerpt TEXT,
|
||||
meta_title VARCHAR(200),
|
||||
meta_description VARCHAR(500),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (language_code) REFERENCES languages(code) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create user_language_preferences table
|
||||
CREATE TABLE IF NOT EXISTS user_language_preferences (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL UNIQUE,
|
||||
language_code VARCHAR(10) NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (language_code) REFERENCES languages(code) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_translations_key ON translations(key);
|
||||
CREATE INDEX IF NOT EXISTS idx_translations_language_key ON translations(language_code, key);
|
||||
CREATE INDEX IF NOT EXISTS idx_translations_context ON translations(context);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_translations_content ON content_translations(content_type, content_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_translations_language ON content_translations(language_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_languages_active ON languages(is_active) WHERE is_active = TRUE;
|
||||
CREATE INDEX IF NOT EXISTS idx_languages_default ON languages(is_default) WHERE is_default = TRUE;
|
||||
|
||||
-- Insert default languages
|
||||
INSERT INTO languages (id, name, native_name, code, is_default, is_active, sort_order) VALUES
|
||||
('cs', 'Czech', 'Čeština', 'cs', true, true, 1),
|
||||
('en', 'English', 'English', 'en', false, true, 2)
|
||||
ON CONFLICT (code) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
native_name = EXCLUDED.native_name,
|
||||
is_default = EXCLUDED.is_default,
|
||||
is_active = EXCLUDED.is_active,
|
||||
sort_order = EXCLUDED.sort_order;
|
||||
|
||||
-- Insert basic Czech translations
|
||||
INSERT INTO translations (key, language_code, value, context) VALUES
|
||||
-- Common
|
||||
('common.welcome_message', 'cs', 'Vítejte v našem fotbalovém klubu', 'common'),
|
||||
('common.welcome_subtitle', 'cs', 'Oficiální stránky fotbalového klubu', 'common'),
|
||||
|
||||
-- Navigation
|
||||
('nav.home', 'cs', 'Domů', 'navbar'),
|
||||
('nav.news', 'cs', 'Aktuality', 'navbar'),
|
||||
('nav.matches', 'cs', 'Zápasy', 'navbar'),
|
||||
('nav.players', 'cs', 'Hráči', 'navbar'),
|
||||
('nav.gallery', 'cs', 'Galerie', 'navbar'),
|
||||
('nav.videos', 'cs', 'Videa', 'navbar'),
|
||||
('nav.contact', 'cs', 'Kontakt', 'navbar'),
|
||||
('nav.about', 'cs', 'O klubu', 'navbar'),
|
||||
('nav.activities', 'cs', 'Aktivity', 'navbar'),
|
||||
('nav.sponsors', 'cs', 'Sponzoři', 'navbar'),
|
||||
('nav.articles', 'cs', 'Články', 'navbar'),
|
||||
('nav.tables', 'cs', 'Tabulky', 'navbar'),
|
||||
('nav.calendar', 'cs', 'Kalendář', 'navbar'),
|
||||
('nav.shop', 'cs', 'Obchod', 'navbar'),
|
||||
|
||||
-- Common actions
|
||||
('action.save', 'cs', 'Uložit', 'common'),
|
||||
('action.cancel', 'cs', 'Zrušit', 'common'),
|
||||
('action.edit', 'cs', 'Upravit', 'common'),
|
||||
('action.delete', 'cs', 'Smazat', 'common'),
|
||||
('action.create', 'cs', 'Vytvořit', 'common'),
|
||||
('action.update', 'cs', 'Aktualizovat', 'common'),
|
||||
('action.publish', 'cs', 'Publikovat', 'common'),
|
||||
('action.unpublish', 'cs', 'Skrýt', 'common'),
|
||||
('action.search', 'cs', 'Hledat', 'common'),
|
||||
('action.more', 'cs', 'Více', 'common'),
|
||||
|
||||
-- Forms
|
||||
('form.required', 'cs', 'Toto pole je povinné', 'form'),
|
||||
('form.email', 'cs', 'E-mail', 'form'),
|
||||
('form.password', 'cs', 'Heslo', 'form'),
|
||||
('form.name', 'cs', 'Jméno', 'form'),
|
||||
('form.message', 'cs', 'Zpráva', 'form'),
|
||||
('form.submit', 'cs', 'Odeslat', 'form'),
|
||||
|
||||
-- Messages
|
||||
('msg.success', 'cs', 'Operace proběhla úspěšně', 'message'),
|
||||
('msg.error', 'cs', 'Došlo k chybě', 'message'),
|
||||
('msg.loading', 'cs', 'Načítám...', 'message'),
|
||||
('msg.no_data', 'cs', 'Žádná data', 'message'),
|
||||
|
||||
-- Admin
|
||||
('admin.dashboard', 'cs', 'Nástěnka', 'admin'),
|
||||
('admin.articles', 'cs', 'Články', 'admin'),
|
||||
('admin.matches', 'cs', 'Zápasy', 'admin'),
|
||||
('admin.players', 'cs', 'Hráči', 'admin'),
|
||||
('admin.settings', 'cs', 'Nastavení', 'admin'),
|
||||
('admin.users', 'cs', 'Uživatelé', 'admin'),
|
||||
|
||||
-- Content
|
||||
('content.read_more', 'cs', 'Číst více', 'content'),
|
||||
('content.published_at', 'cs', 'Publikováno', 'content'),
|
||||
('content.updated_at', 'cs', 'Aktualizováno', 'content'),
|
||||
('content.author', 'cs', 'Autor', 'content'),
|
||||
|
||||
-- Matches
|
||||
('match.date', 'cs', 'Datum', 'match'),
|
||||
('match.time', 'cs', 'Čas', 'match'),
|
||||
('match.place', 'cs', 'Místo', 'match'),
|
||||
('match.result', 'cs', 'Výsledek', 'match'),
|
||||
('match.score', 'cs', 'Skóre', 'match'),
|
||||
('match.team_home', 'cs', 'Domácí', 'match'),
|
||||
('match.team_away', 'cs', 'Hosté', 'match'),
|
||||
|
||||
-- Teams
|
||||
('team.name', 'cs', 'Název týmu', 'team'),
|
||||
('team.players', 'cs', 'Hráči', 'team'),
|
||||
('team.coach', 'cs', 'Trenér', 'team'),
|
||||
('team.category', 'cs', 'Kategorie', 'team'),
|
||||
|
||||
-- Gallery
|
||||
('homepage.gallery', 'cs', 'Galerie', 'gallery'),
|
||||
('gallery.albums', 'cs', 'Alba', 'gallery'),
|
||||
('gallery.photos', 'cs', 'Fotky', 'gallery'),
|
||||
('gallery.view_all', 'cs', 'Zobrazit vše', 'gallery'),
|
||||
|
||||
-- Search
|
||||
('search.placeholder', 'cs', 'Hledat...', 'search'),
|
||||
('search.results', 'cs', 'Výsledky hledání', 'search'),
|
||||
('search.no_results', 'cs', 'Nebyly nalezeny žádné výsledky', 'search'),
|
||||
|
||||
-- Pagination
|
||||
('pagination.previous', 'cs', 'Předchozí', 'pagination'),
|
||||
('pagination.next', 'cs', 'Další', 'pagination'),
|
||||
('pagination.page', 'cs', 'Strana', 'pagination'),
|
||||
('pagination.of', 'cs', 'z', 'pagination'),
|
||||
|
||||
-- Date/Time
|
||||
('date.today', 'cs', 'Dnes', 'date'),
|
||||
('date.yesterday', 'cs', 'Včera', 'date'),
|
||||
('date.tomorrow', 'cs', 'Zítra', 'date'),
|
||||
('date.format', 'cs', 'DD.MM.YYYY', 'date'),
|
||||
('time.format', 'cs', 'HH:mm', 'date')
|
||||
ON CONFLICT (key, language_code) DO NOTHING;
|
||||
|
||||
-- Insert basic English translations
|
||||
INSERT INTO translations (key, language_code, value, context) VALUES
|
||||
-- Common
|
||||
('common.welcome_message', 'en', 'Welcome to our football club', 'common'),
|
||||
('common.welcome_subtitle', 'en', 'Official website of the football club', 'common'),
|
||||
|
||||
-- Navigation
|
||||
('nav.home', 'en', 'Home', 'navbar'),
|
||||
('nav.news', 'en', 'News', 'navbar'),
|
||||
('nav.matches', 'en', 'Matches', 'navbar'),
|
||||
('nav.players', 'en', 'Players', 'navbar'),
|
||||
('nav.gallery', 'en', 'Gallery', 'navbar'),
|
||||
('nav.videos', 'en', 'Videos', 'navbar'),
|
||||
('nav.contact', 'en', 'Contact', 'navbar'),
|
||||
('nav.about', 'en', 'About', 'navbar'),
|
||||
('nav.activities', 'en', 'Activities', 'navbar'),
|
||||
('nav.sponsors', 'en', 'Sponsors', 'navbar'),
|
||||
('nav.articles', 'en', 'Articles', 'navbar'),
|
||||
('nav.tables', 'en', 'Tables', 'navbar'),
|
||||
('nav.calendar', 'en', 'Calendar', 'navbar'),
|
||||
('nav.shop', 'en', 'Shop', 'navbar'),
|
||||
|
||||
-- Common actions
|
||||
('action.save', 'en', 'Save', 'common'),
|
||||
('action.cancel', 'en', 'Cancel', 'common'),
|
||||
('action.edit', 'en', 'Edit', 'common'),
|
||||
('action.delete', 'en', 'Delete', 'common'),
|
||||
('action.create', 'en', 'Create', 'common'),
|
||||
('action.update', 'en', 'Update', 'common'),
|
||||
('action.publish', 'en', 'Publish', 'common'),
|
||||
('action.unpublish', 'en', 'Unpublish', 'common'),
|
||||
('action.search', 'en', 'Search', 'common'),
|
||||
('action.more', 'en', 'More', 'common'),
|
||||
|
||||
-- Forms
|
||||
('form.required', 'en', 'This field is required', 'form'),
|
||||
('form.email', 'en', 'Email', 'form'),
|
||||
('form.password', 'en', 'Password', 'form'),
|
||||
('form.name', 'en', 'Name', 'form'),
|
||||
('form.message', 'en', 'Message', 'form'),
|
||||
('form.submit', 'en', 'Submit', 'form'),
|
||||
|
||||
-- Messages
|
||||
('msg.success', 'en', 'Operation successful', 'message'),
|
||||
('msg.error', 'en', 'An error occurred', 'message'),
|
||||
('msg.loading', 'en', 'Loading...', 'message'),
|
||||
('msg.no_data', 'en', 'No data', 'message'),
|
||||
|
||||
-- Admin
|
||||
('admin.dashboard', 'en', 'Dashboard', 'admin'),
|
||||
('admin.articles', 'en', 'Articles', 'admin'),
|
||||
('admin.matches', 'en', 'Matches', 'admin'),
|
||||
('admin.players', 'en', 'Players', 'admin'),
|
||||
('admin.settings', 'en', 'Settings', 'admin'),
|
||||
('admin.users', 'en', 'Users', 'admin'),
|
||||
|
||||
-- Content
|
||||
('content.read_more', 'en', 'Read more', 'content'),
|
||||
('content.published_at', 'en', 'Published', 'content'),
|
||||
('content.updated_at', 'en', 'Updated', 'content'),
|
||||
('content.author', 'en', 'Author', 'content'),
|
||||
|
||||
-- Matches
|
||||
('match.date', 'en', 'Date', 'match'),
|
||||
('match.time', 'en', 'Time', 'match'),
|
||||
('match.place', 'en', 'Place', 'match'),
|
||||
('match.result', 'en', 'Result', 'match'),
|
||||
('match.score', 'en', 'Score', 'match'),
|
||||
('match.team_home', 'en', 'Home', 'match'),
|
||||
('match.team_away', 'en', 'Away', 'match'),
|
||||
|
||||
-- Teams
|
||||
('team.name', 'en', 'Team name', 'team'),
|
||||
('team.players', 'en', 'Players', 'team'),
|
||||
('team.coach', 'en', 'Coach', 'team'),
|
||||
('team.category', 'en', 'Category', 'team'),
|
||||
|
||||
-- Gallery
|
||||
('homepage.gallery', 'en', 'Gallery', 'gallery'),
|
||||
('gallery.albums', 'en', 'Albums', 'gallery'),
|
||||
('gallery.photos', 'en', 'Photos', 'gallery'),
|
||||
('gallery.view_all', 'en', 'View all', 'gallery'),
|
||||
|
||||
-- Search
|
||||
('search.placeholder', 'en', 'Search...', 'search'),
|
||||
('search.results', 'en', 'Search results', 'search'),
|
||||
('search.no_results', 'en', 'No results found', 'search'),
|
||||
|
||||
-- Pagination
|
||||
('pagination.previous', 'en', 'Previous', 'pagination'),
|
||||
('pagination.next', 'en', 'Next', 'pagination'),
|
||||
('pagination.page', 'en', 'Page', 'pagination'),
|
||||
('pagination.of', 'en', 'of', 'pagination'),
|
||||
|
||||
-- Date/Time
|
||||
('date.today', 'en', 'Today', 'date'),
|
||||
('date.yesterday', 'en', 'Yesterday', 'date'),
|
||||
('date.tomorrow', 'en', 'Tomorrow', 'date'),
|
||||
('date.format', 'en', 'MM/DD/YYYY', 'date'),
|
||||
('time.format', 'en', 'HH:mm', 'date')
|
||||
ON CONFLICT (key, language_code) DO NOTHING;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- Ticket system migration rollback
|
||||
-- Migration: 20250109000001_add_ticket_system.down.sql
|
||||
|
||||
-- Drop views
|
||||
DROP VIEW IF EXISTS available_tickets_view;
|
||||
|
||||
-- Remove ticket-related fields from eshop_orders
|
||||
ALTER TABLE eshop_orders
|
||||
DROP COLUMN IF EXISTS ticket_order,
|
||||
DROP COLUMN IF EXISTS ticket_campaign_id;
|
||||
|
||||
-- Drop tables in reverse order of creation
|
||||
DROP TABLE IF EXISTS ticket_availability;
|
||||
DROP TABLE IF EXISTS tickets;
|
||||
DROP TABLE IF EXISTS campaign_ticket_types;
|
||||
DROP TABLE IF EXISTS ticket_campaigns;
|
||||
DROP TABLE IF EXISTS ticket_types;
|
||||
|
||||
-- Disable UUID extension (optional, usually kept)
|
||||
-- DROP EXTENSION IF EXISTS "uuid-ossp";
|
||||
@@ -0,0 +1,190 @@
|
||||
-- Ticket system migration for match ticketing
|
||||
-- Migration: 20250109000001_add_ticket_system.up.sql
|
||||
|
||||
-- Enable UUID extension if not exists
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- Ticket types for different pricing tiers
|
||||
CREATE TABLE ticket_types (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
price_cents BIGINT NOT NULL,
|
||||
currency VARCHAR(10) DEFAULT 'CZK',
|
||||
color VARCHAR(20) DEFAULT 'primary', -- primary, secondary, success, warning, danger
|
||||
display_order INTEGER DEFAULT 0,
|
||||
active BOOLEAN DEFAULT true,
|
||||
|
||||
-- Constraints and limits
|
||||
max_tickets_per_order INTEGER DEFAULT 10,
|
||||
sale_start_time TIMESTAMP WITH TIME ZONE,
|
||||
sale_end_time TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
-- Access control (optional)
|
||||
requires_membership BOOLEAN DEFAULT false,
|
||||
min_age INTEGER,
|
||||
|
||||
INDEX idx_ticket_types_active (active),
|
||||
INDEX idx_ticket_types_order (display_order)
|
||||
);
|
||||
|
||||
-- Ticket campaigns for specific matches or events
|
||||
CREATE TABLE ticket_campaigns (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Link to match (can be null for non-match events)
|
||||
external_match_id VARCHAR(255), -- FACR match ID
|
||||
competition_code VARCHAR(50),
|
||||
match_date_time TIMESTAMP WITH TIME ZONE,
|
||||
home_team VARCHAR(255),
|
||||
away_team VARCHAR(255),
|
||||
venue VARCHAR(255),
|
||||
|
||||
-- Campaign settings
|
||||
active BOOLEAN DEFAULT true,
|
||||
sale_start_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
sale_end_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
max_total_tickets INTEGER, -- Overall capacity limit
|
||||
|
||||
INDEX idx_ticket_campaigns_active (active),
|
||||
INDEX idx_ticket_campaigns_match (external_match_id),
|
||||
INDEX idx_ticket_campaigns_dates (sale_start_time, sale_end_time)
|
||||
);
|
||||
|
||||
-- Link ticket types to campaigns (many-to-many with campaign-specific overrides)
|
||||
CREATE TABLE campaign_ticket_types (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
campaign_id UUID NOT NULL REFERENCES ticket_campaigns(id) ON DELETE CASCADE,
|
||||
ticket_type_id UUID NOT NULL REFERENCES ticket_types(id) ON DELETE CASCADE,
|
||||
|
||||
-- Campaign-specific overrides
|
||||
price_cents BIGINT, -- Override default price if set
|
||||
max_quantity INTEGER, -- Campaign-specific quantity limit
|
||||
|
||||
UNIQUE(campaign_id, ticket_type_id),
|
||||
INDEX idx_campaign_ticket_types_campaign (campaign_id),
|
||||
INDEX idx_campaign_ticket_types_type (ticket_type_id)
|
||||
);
|
||||
|
||||
-- Ticket instances (actual sold tickets)
|
||||
CREATE TABLE tickets (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
order_id UUID, -- Link to e-shop order when purchased
|
||||
campaign_id UUID NOT NULL REFERENCES ticket_campaigns(id),
|
||||
ticket_type_id UUID NOT NULL REFERENCES ticket_types(id),
|
||||
|
||||
-- Ticket holder information
|
||||
holder_name VARCHAR(255) NOT NULL,
|
||||
holder_email VARCHAR(255) NOT NULL,
|
||||
holder_phone VARCHAR(50),
|
||||
|
||||
-- Ticket details
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
unit_price_cents BIGINT NOT NULL,
|
||||
total_price_cents BIGINT NOT NULL,
|
||||
currency VARCHAR(10) DEFAULT 'CZK',
|
||||
|
||||
-- Status tracking
|
||||
status VARCHAR(20) DEFAULT 'reserved', -- reserved, paid, cancelled, used
|
||||
barcode VARCHAR(255) UNIQUE, -- Unique barcode/QR code
|
||||
qr_code_data TEXT, -- JSON with QR code data
|
||||
|
||||
-- Usage tracking
|
||||
used_at TIMESTAMP WITH TIME ZONE,
|
||||
used_by VARCHAR(255), -- Who validated the ticket
|
||||
|
||||
INDEX idx_tickets_order (order_id),
|
||||
INDEX idx_tickets_campaign (campaign_id),
|
||||
INDEX idx_tickets_status (status),
|
||||
INDEX idx_tickets_barcode (barcode),
|
||||
INDEX idx_tickets_holder_email (holder_email)
|
||||
);
|
||||
|
||||
-- Ticket availability tracking per campaign/type
|
||||
CREATE TABLE ticket_availability (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
campaign_id UUID NOT NULL REFERENCES ticket_campaigns(id) ON DELETE CASCADE,
|
||||
ticket_type_id UUID NOT NULL REFERENCES ticket_types(id) ON DELETE CASCADE,
|
||||
|
||||
total_capacity INTEGER NOT NULL DEFAULT 0,
|
||||
sold_quantity INTEGER NOT NULL DEFAULT 0,
|
||||
reserved_quantity INTEGER NOT NULL DEFAULT 0,
|
||||
available_quantity INTEGER GENERATED ALWAYS AS (total_capacity - sold_quantity - reserved_quantity) STORED,
|
||||
|
||||
UNIQUE(campaign_id, ticket_type_id),
|
||||
INDEX idx_ticket_availability_campaign (campaign_id),
|
||||
INDEX idx_ticket_availability_type (ticket_type_id)
|
||||
);
|
||||
|
||||
-- Add ticket-related fields to existing e-shop orders
|
||||
ALTER TABLE eshop_orders
|
||||
ADD COLUMN ticket_order BOOLEAN DEFAULT false,
|
||||
ADD COLUMN ticket_campaign_id UUID REFERENCES ticket_campaigns(id);
|
||||
|
||||
-- Create indexes for new order fields
|
||||
CREATE INDEX idx_eshop_orders_ticket_order ON eshop_orders(ticket_order);
|
||||
CREATE INDEX idx_eshop_orders_ticket_campaign ON eshop_orders(ticket_campaign_id);
|
||||
|
||||
-- Insert default ticket types
|
||||
INSERT INTO ticket_types (name, description, price_cents, color, display_order) VALUES
|
||||
('Dospělý', 'Vstupenka pro dospělého', 15000, 'primary', 1),
|
||||
('Dítě do 15 let', 'Vstupenka pro děti do 15 let', 5000, 'success', 2),
|
||||
('Student', 'Vstupenka pro studenty (platí ISIC)', 8000, 'warning', 3),
|
||||
('Senior', 'Vstupenka pro seniory nad 65 let', 8000, 'secondary', 4),
|
||||
('VIP', 'VIP vstupenka s občerstvením', 50000, 'danger', 5);
|
||||
|
||||
-- Create view for available tickets with campaign info
|
||||
CREATE VIEW available_tickets_view AS
|
||||
SELECT
|
||||
c.id as campaign_id,
|
||||
c.title as campaign_title,
|
||||
c.description as campaign_description,
|
||||
c.external_match_id,
|
||||
c.competition_code,
|
||||
c.match_date_time,
|
||||
c.home_team,
|
||||
c.away_team,
|
||||
c.venue,
|
||||
c.sale_start_time,
|
||||
c.sale_end_time,
|
||||
tt.id as ticket_type_id,
|
||||
tt.name as ticket_type_name,
|
||||
tt.description as ticket_type_description,
|
||||
COALESCE(ctt.price_cents, tt.price_cents) as price_cents,
|
||||
COALESCE(ctt.max_quantity, tt.max_tickets_per_order) as max_per_order,
|
||||
tt.color,
|
||||
ta.available_quantity,
|
||||
ta.total_capacity,
|
||||
CASE
|
||||
WHEN NOW() < c.sale_start_time THEN 'upcoming'
|
||||
WHEN NOW() > c.sale_end_time THEN 'ended'
|
||||
WHEN ta.available_quantity <= 0 THEN 'sold_out'
|
||||
ELSE 'available'
|
||||
END as sale_status
|
||||
FROM ticket_campaigns c
|
||||
JOIN campaign_ticket_types ctt ON c.id = ctt.campaign_id
|
||||
JOIN ticket_types tt ON ctt.ticket_type_id = tt.id
|
||||
LEFT JOIN ticket_availability ta ON c.id = ta.campaign_id AND tt.id = ta.ticket_type_id
|
||||
WHERE c.active = true
|
||||
AND tt.active = true
|
||||
AND c.deleted_at IS NULL
|
||||
AND tt.deleted_at IS NULL;
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Drop financial management tables
|
||||
-- Migration: 20250110000001_create_financial_tables
|
||||
|
||||
DROP TRIGGER IF EXISTS update_financial_settings_updated_at ON financial_settings;
|
||||
DROP TRIGGER IF EXISTS update_financial_reports_updated_at ON financial_reports;
|
||||
DROP TRIGGER IF EXISTS update_expense_documents_updated_at ON expense_documents;
|
||||
DROP TRIGGER IF EXISTS update_expenses_updated_at ON expenses;
|
||||
DROP TRIGGER IF EXISTS update_sponsorship_documents_updated_at ON sponsorship_documents;
|
||||
DROP TRIGGER IF EXISTS update_sponsorship_payments_updated_at ON sponsorship_payments;
|
||||
DROP TRIGGER IF EXISTS update_sponsorships_updated_at ON sponsorships;
|
||||
DROP TRIGGER IF EXISTS update_budgets_updated_at ON budgets;
|
||||
|
||||
DROP FUNCTION IF EXISTS update_updated_at_column();
|
||||
|
||||
DROP TABLE IF EXISTS financial_settings;
|
||||
DROP TABLE IF EXISTS financial_reports;
|
||||
DROP TABLE IF EXISTS expense_documents;
|
||||
DROP TABLE IF EXISTS expenses;
|
||||
DROP TABLE IF EXISTS sponsorship_documents;
|
||||
DROP TABLE IF EXISTS sponsorship_payments;
|
||||
DROP TABLE IF EXISTS sponsorships;
|
||||
DROP TABLE IF EXISTS budgets;
|
||||
@@ -0,0 +1,259 @@
|
||||
-- Create financial management tables
|
||||
-- Migration: 20250110000001_create_financial_tables
|
||||
|
||||
-- Budgets table
|
||||
CREATE TABLE budgets (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(100),
|
||||
yearly_limit DECIMAL(12,2),
|
||||
monthly_limit DECIMAL(12,2),
|
||||
current_spend DECIMAL(12,2) DEFAULT 0,
|
||||
fiscal_year INTEGER,
|
||||
start_date TIMESTAMP,
|
||||
end_date TIMESTAMP,
|
||||
active BOOLEAN DEFAULT true,
|
||||
alert_threshold DECIMAL(5,2) DEFAULT 80,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_budgets_category ON budgets(category);
|
||||
CREATE INDEX idx_budgets_fiscal_year ON budgets(fiscal_year);
|
||||
CREATE INDEX idx_budgets_active ON budgets(active);
|
||||
|
||||
-- Sponsorships table
|
||||
CREATE TABLE sponsorships (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sponsor_name VARCHAR(255) NOT NULL,
|
||||
sponsor_logo VARCHAR(500),
|
||||
contact_person VARCHAR(255),
|
||||
contact_email VARCHAR(255),
|
||||
contact_phone VARCHAR(50),
|
||||
contract_number VARCHAR(100) UNIQUE,
|
||||
contract_type VARCHAR(100),
|
||||
total_value DECIMAL(12,2),
|
||||
payment_schedule VARCHAR(100),
|
||||
currency VARCHAR(3) DEFAULT 'CZK',
|
||||
start_date TIMESTAMP,
|
||||
end_date TIMESTAMP,
|
||||
auto_renewal BOOLEAN DEFAULT false,
|
||||
renewal_notice INTEGER DEFAULT 90,
|
||||
benefits TEXT,
|
||||
obligations TEXT,
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
last_payment_date TIMESTAMP,
|
||||
next_payment_date TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sponsorships_status ON sponsorships(status);
|
||||
CREATE INDEX idx_sponsorships_contract_number ON sponsorships(contract_number);
|
||||
CREATE INDEX idx_sponsorships_end_date ON sponsorships(end_date);
|
||||
|
||||
-- Sponsorship payments table
|
||||
CREATE TABLE sponsorship_payments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sponsorship_id INTEGER NOT NULL REFERENCES sponsorships(id) ON DELETE CASCADE,
|
||||
amount DECIMAL(12,2),
|
||||
currency VARCHAR(3) DEFAULT 'CZK',
|
||||
payment_date TIMESTAMP,
|
||||
payment_method VARCHAR(100),
|
||||
reference_number VARCHAR(255),
|
||||
status VARCHAR(50) DEFAULT 'received',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sponsorship_payments_sponsorship_id ON sponsorship_payments(sponsorship_id);
|
||||
CREATE INDEX idx_sponsorship_payments_payment_date ON sponsorship_payments(payment_date);
|
||||
CREATE INDEX idx_sponsorship_payments_status ON sponsorship_payments(status);
|
||||
|
||||
-- Sponsorship documents table
|
||||
CREATE TABLE sponsorship_documents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sponsorship_id INTEGER NOT NULL REFERENCES sponsorships(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(100),
|
||||
file_name VARCHAR(500),
|
||||
file_path VARCHAR(500),
|
||||
file_size BIGINT,
|
||||
mime_type VARCHAR(100),
|
||||
description TEXT,
|
||||
version VARCHAR(20) DEFAULT '1.0',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sponsorship_documents_sponsorship_id ON sponsorship_documents(sponsorship_id);
|
||||
CREATE INDEX idx_sponsorship_documents_type ON sponsorship_documents(type);
|
||||
|
||||
-- Expenses table
|
||||
CREATE TABLE expenses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(100),
|
||||
subcategory VARCHAR(100),
|
||||
amount DECIMAL(12,2),
|
||||
currency VARCHAR(3) DEFAULT 'CZK',
|
||||
vat_rate DECIMAL(5,2) DEFAULT 21,
|
||||
vat_amount DECIMAL(12,2),
|
||||
total_amount DECIMAL(12,2),
|
||||
expense_date TIMESTAMP,
|
||||
payment_method VARCHAR(100),
|
||||
has_receipt BOOLEAN DEFAULT false,
|
||||
receipt_data TEXT,
|
||||
receipt_image VARCHAR(500),
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
approved_by INTEGER,
|
||||
approved_at TIMESTAMP,
|
||||
rejection_reason TEXT,
|
||||
budget_id INTEGER REFERENCES budgets(id),
|
||||
team_id INTEGER,
|
||||
project_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_expenses_category ON expenses(category);
|
||||
CREATE INDEX idx_expenses_status ON expenses(status);
|
||||
CREATE INDEX idx_expenses_expense_date ON expenses(expense_date);
|
||||
CREATE INDEX idx_expenses_budget_id ON expenses(budget_id);
|
||||
CREATE INDEX idx_expenses_team_id ON expenses(team_id);
|
||||
CREATE INDEX idx_expenses_created_by ON expenses(created_by);
|
||||
|
||||
-- Expense documents table
|
||||
CREATE TABLE expense_documents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
expense_id INTEGER NOT NULL REFERENCES expenses(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(100),
|
||||
file_name VARCHAR(500),
|
||||
file_path VARCHAR(500),
|
||||
file_size BIGINT,
|
||||
mime_type VARCHAR(100),
|
||||
ocr_data TEXT,
|
||||
ocr_accuracy DECIMAL(5,2),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_expense_documents_expense_id ON expense_documents(expense_id);
|
||||
CREATE INDEX idx_expense_documents_type ON expense_documents(type);
|
||||
|
||||
-- Financial reports table
|
||||
CREATE TABLE financial_reports (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(100),
|
||||
period VARCHAR(50),
|
||||
report_data TEXT,
|
||||
summary TEXT,
|
||||
file_path VARCHAR(500),
|
||||
generated_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
CREATE INDEX idx_financial_reports_type ON financial_reports(type);
|
||||
CREATE INDEX idx_financial_reports_period ON financial_reports(period);
|
||||
CREATE INDEX idx_financial_reports_generated_at ON financial_reports(generated_at);
|
||||
|
||||
-- Financial settings table
|
||||
CREATE TABLE financial_settings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
default_currency VARCHAR(3) DEFAULT 'CZK',
|
||||
default_vat_rate DECIMAL(5,2) DEFAULT 21,
|
||||
fiscal_year_start VARCHAR(10) DEFAULT '01-01',
|
||||
expense_approval_required BOOLEAN DEFAULT true,
|
||||
max_expense_auto_approve DECIMAL(12,2) DEFAULT 1000,
|
||||
budget_alert_enabled BOOLEAN DEFAULT true,
|
||||
budget_alert_threshold DECIMAL(5,2) DEFAULT 80,
|
||||
sponsorship_alert_enabled BOOLEAN DEFAULT true,
|
||||
ocr_service_enabled BOOLEAN DEFAULT true,
|
||||
ocr_provider VARCHAR(50) DEFAULT 'tesseract',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Insert default financial settings
|
||||
INSERT INTO financial_settings (
|
||||
default_currency,
|
||||
default_vat_rate,
|
||||
fiscal_year_start,
|
||||
expense_approval_required,
|
||||
max_expense_auto_approve,
|
||||
budget_alert_enabled,
|
||||
budget_alert_threshold,
|
||||
sponsorship_alert_enabled,
|
||||
ocr_service_enabled,
|
||||
ocr_provider
|
||||
) VALUES (
|
||||
'CZK',
|
||||
21.0,
|
||||
'01-01',
|
||||
true,
|
||||
1000.00,
|
||||
true,
|
||||
80.0,
|
||||
true,
|
||||
true,
|
||||
'tesseract'
|
||||
);
|
||||
|
||||
-- Create default budget categories for a typical football club
|
||||
INSERT INTO budgets (
|
||||
name,
|
||||
description,
|
||||
category,
|
||||
yearly_limit,
|
||||
monthly_limit,
|
||||
fiscal_year,
|
||||
start_date,
|
||||
end_date,
|
||||
active
|
||||
) VALUES
|
||||
('Týmové provoz', 'Mzdy hráčů, trenérů a realizačního týmu', 'Týmové provoz', 500000.00, 41667.00, 2025, '2025-01-01 00:00:00', '2025-12-31 23:59:59', true),
|
||||
('Stadion a zařízení', 'Údržba stadionu, energie, voda, odpady', 'Stadion', 200000.00, 16667.00, 2025, '2025-01-01 00:00:00', '2025-12-31 23:59:59', true),
|
||||
('Cestování', 'Ubytování, doprava na zápasy a soustředění', 'Cestování', 150000.00, 12500.00, 2025, '2025-01-01 00:00:00', '2025-12-31 23:59:59', true),
|
||||
('Materiál a vybavení', 'Sportovní vybavení, dresy, míče', 'Materiál', 100000.00, 8333.00, 2025, '2025-01-01 00:00:00', '2025-12-31 23:59:59', true),
|
||||
('Marketing a reklama', 'Propagace, reklama, sociální sítě', 'Marketing', 80000.00, 6667.00, 2025, '2025-01-01 00:00:00', '2025-12-31 23:59:59', true),
|
||||
('Administrativa', 'Kancelářské potřeby, software, právní služby', 'Administrativa', 50000.00, 4167.00, 2025, '2025-01-01 00:00:00', '2025-12-31 23:59:59', 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 financial tables
|
||||
CREATE TRIGGER update_budgets_updated_at BEFORE UPDATE ON budgets FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_sponsorships_updated_at BEFORE UPDATE ON sponsorships FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_sponsorship_payments_updated_at BEFORE UPDATE ON sponsorship_payments FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_sponsorship_documents_updated_at BEFORE UPDATE ON sponsorship_documents FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_expenses_updated_at BEFORE UPDATE ON expenses FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_expense_documents_updated_at BEFORE UPDATE ON expense_documents FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_financial_reports_updated_at BEFORE UPDATE ON financial_reports FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_financial_settings_updated_at BEFORE UPDATE ON financial_settings FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
@@ -0,0 +1,20 @@
|
||||
-- Drop invoice management tables
|
||||
-- Migration: 20250110000002_create_invoice_tables
|
||||
|
||||
DROP TRIGGER IF EXISTS update_invoice_payments_updated_at ON invoice_payments;
|
||||
DROP TRIGGER IF EXISTS update_invoice_items_updated_at ON invoice_items;
|
||||
DROP TRIGGER IF EXISTS update_invoices_updated_at ON invoices;
|
||||
DROP TRIGGER IF EXISTS update_invoice_templates_updated_at ON invoice_templates;
|
||||
DROP TRIGGER IF EXISTS update_invoice_sequences_updated_at ON invoice_sequences;
|
||||
DROP TRIGGER IF EXISTS update_invoice_customers_updated_at ON invoice_customers;
|
||||
DROP TRIGGER IF EXISTS update_invoice_settings_updated_at ON invoice_settings;
|
||||
|
||||
DROP FUNCTION IF EXISTS update_updated_at_column();
|
||||
|
||||
DROP TABLE IF EXISTS invoice_payments;
|
||||
DROP TABLE IF EXISTS invoice_items;
|
||||
DROP TABLE IF EXISTS invoices;
|
||||
DROP TABLE IF EXISTS invoice_templates;
|
||||
DROP TABLE IF EXISTS invoice_sequences;
|
||||
DROP TABLE IF EXISTS invoice_customers;
|
||||
DROP TABLE IF EXISTS invoice_settings;
|
||||
@@ -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();
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE qr_codes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
target_url VARCHAR(500) NOT NULL,
|
||||
qr_code_url VARCHAR(500),
|
||||
scan_count INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX idx_qr_codes_created_at ON qr_codes(created_at);
|
||||
CREATE INDEX idx_qr_codes_is_active ON qr_codes(is_active);
|
||||
CREATE INDEX idx_qr_codes_target_url ON qr_codes(target_url);
|
||||
@@ -0,0 +1,147 @@
|
||||
-- Financial management tables
|
||||
|
||||
-- Budgets table
|
||||
CREATE TABLE budgets (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(100) NOT NULL,
|
||||
yearly_limit DECIMAL(12,2),
|
||||
monthly_limit DECIMAL(12,2),
|
||||
current_spend DECIMAL(12,2) DEFAULT 0,
|
||||
fiscal_year INTEGER NOT NULL,
|
||||
start_date TIMESTAMP NOT NULL,
|
||||
end_date TIMESTAMP NOT NULL,
|
||||
active BOOLEAN DEFAULT true,
|
||||
alert_threshold DECIMAL(5,2) DEFAULT 80.00,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Sponsorships table
|
||||
CREATE TABLE sponsorships (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sponsor_name VARCHAR(255) NOT NULL,
|
||||
sponsor_logo VARCHAR(500),
|
||||
contact_person VARCHAR(255),
|
||||
contact_email VARCHAR(255),
|
||||
contact_phone VARCHAR(50),
|
||||
contract_number VARCHAR(100) UNIQUE,
|
||||
contract_type VARCHAR(100),
|
||||
total_value DECIMAL(12,2),
|
||||
payment_schedule VARCHAR(100) DEFAULT 'Měsíčně',
|
||||
currency VARCHAR(3) DEFAULT 'CZK',
|
||||
start_date TIMESTAMP NOT NULL,
|
||||
end_date TIMESTAMP NOT NULL,
|
||||
auto_renewal BOOLEAN DEFAULT false,
|
||||
renewal_notice INTEGER DEFAULT 90,
|
||||
benefits TEXT,
|
||||
obligations TEXT,
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
last_payment_date TIMESTAMP,
|
||||
next_payment_date TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Sponsorship payments table
|
||||
CREATE TABLE sponsorship_payments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sponsorship_id INTEGER NOT NULL REFERENCES sponsorships(id) ON DELETE CASCADE,
|
||||
amount DECIMAL(12,2) NOT NULL,
|
||||
currency VARCHAR(3) DEFAULT 'CZK',
|
||||
payment_date TIMESTAMP NOT NULL,
|
||||
payment_method VARCHAR(100),
|
||||
reference_number VARCHAR(255),
|
||||
status VARCHAR(50) DEFAULT 'received',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Sponsorship documents table
|
||||
CREATE TABLE sponsorship_documents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sponsorship_id INTEGER NOT NULL REFERENCES sponsorships(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(100),
|
||||
file_name VARCHAR(500),
|
||||
file_path VARCHAR(500),
|
||||
file_size BIGINT,
|
||||
mime_type VARCHAR(100),
|
||||
description TEXT,
|
||||
version VARCHAR(20) DEFAULT '1.0',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Expenses table
|
||||
CREATE TABLE expenses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(100) NOT NULL,
|
||||
subcategory VARCHAR(100),
|
||||
amount DECIMAL(12,2) NOT NULL,
|
||||
currency VARCHAR(3) DEFAULT 'CZK',
|
||||
vat_rate DECIMAL(5,2) DEFAULT 21.00,
|
||||
vat_amount DECIMAL(12,2),
|
||||
total_amount DECIMAL(12,2),
|
||||
expense_date TIMESTAMP NOT NULL,
|
||||
payment_method VARCHAR(100),
|
||||
has_receipt BOOLEAN DEFAULT false,
|
||||
receipt_data TEXT,
|
||||
receipt_image VARCHAR(500),
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
approved_by INTEGER,
|
||||
approved_at TIMESTAMP,
|
||||
rejection_reason TEXT,
|
||||
budget_id INTEGER REFERENCES budgets(id),
|
||||
team_id INTEGER,
|
||||
project_id INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER NOT NULL,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Expense documents table
|
||||
CREATE TABLE expense_documents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
expense_id INTEGER NOT NULL REFERENCES expenses(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(100),
|
||||
file_name VARCHAR(500),
|
||||
file_path VARCHAR(500),
|
||||
file_size BIGINT,
|
||||
mime_type VARCHAR(100),
|
||||
ocr_data TEXT,
|
||||
ocr_accuracy DECIMAL(5,2),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX idx_budgets_category ON budgets(category);
|
||||
CREATE INDEX idx_budgets_fiscal_year ON budgets(fiscal_year);
|
||||
CREATE INDEX idx_budgets_active ON budgets(active);
|
||||
CREATE INDEX idx_expenses_category ON expenses(category);
|
||||
CREATE INDEX idx_expenses_status ON expenses(status);
|
||||
CREATE INDEX idx_expenses_expense_date ON expenses(expense_date);
|
||||
CREATE INDEX idx_expenses_budget_id ON expenses(budget_id);
|
||||
CREATE INDEX idx_expenses_created_by ON expenses(created_by);
|
||||
CREATE INDEX idx_sponsorships_status ON sponsorships(status);
|
||||
CREATE INDEX idx_sponsorships_contract_number ON sponsorships(contract_number);
|
||||
CREATE INDEX idx_sponsorship_payments_sponsorship_id ON sponsorship_payments(sponsorship_id);
|
||||
CREATE INDEX idx_sponsorship_documents_sponsorship_id ON sponsorship_documents(sponsorship_id);
|
||||
CREATE INDEX idx_expense_documents_expense_id ON expense_documents(expense_id);
|
||||
@@ -0,0 +1,200 @@
|
||||
-- Invoice management tables
|
||||
|
||||
-- Customers table
|
||||
CREATE TABLE customers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
ico VARCHAR(20),
|
||||
dic VARCHAR(20),
|
||||
address TEXT,
|
||||
city VARCHAR(100),
|
||||
zip VARCHAR(10),
|
||||
country VARCHAR(100) DEFAULT 'Česká republika',
|
||||
email VARCHAR(255),
|
||||
phone VARCHAR(50),
|
||||
notes TEXT,
|
||||
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 from club settings)
|
||||
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 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
|
||||
subtotal_ex_vat DECIMAL(12,2) DEFAULT 0,
|
||||
vat_amount DECIMAL(12,2) DEFAULT 0,
|
||||
total_amount DECIMAL(12,2) NOT NULL,
|
||||
paid_amount DECIMAL(12,2) DEFAULT 0,
|
||||
|
||||
-- Status and dates
|
||||
status VARCHAR(50) DEFAULT 'draft',
|
||||
payment_status VARCHAR(50) DEFAULT 'unpaid',
|
||||
sent_at TIMESTAMP,
|
||||
paid_at TIMESTAMP,
|
||||
|
||||
-- Notes and metadata
|
||||
notes TEXT,
|
||||
internal_notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER NOT NULL,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Invoice items table
|
||||
CREATE TABLE invoice_items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
invoice_id INTEGER NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
quantity DECIMAL(10,2) NOT NULL DEFAULT 1,
|
||||
unit VARCHAR(50) DEFAULT 'ks',
|
||||
unit_price DECIMAL(12,2) NOT NULL,
|
||||
vat_rate DECIMAL(5,2) DEFAULT 21.00,
|
||||
vat_amount DECIMAL(12,2),
|
||||
total_price_ex_vat DECIMAL(12,2),
|
||||
total_price_with_vat DECIMAL(12,2),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Invoice payments table
|
||||
CREATE TABLE invoice_payments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
invoice_id INTEGER NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
|
||||
amount DECIMAL(12,2) NOT NULL,
|
||||
payment_date TIMESTAMP NOT NULL,
|
||||
payment_method VARCHAR(100),
|
||||
reference_number VARCHAR(255),
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Invoice documents table
|
||||
CREATE TABLE invoice_documents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
invoice_id INTEGER NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(100),
|
||||
file_name VARCHAR(500),
|
||||
file_path VARCHAR(500),
|
||||
file_size BIGINT,
|
||||
mime_type VARCHAR(100),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Invoice settings table (singleton)
|
||||
CREATE TABLE invoice_settings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
-- Default supplier information
|
||||
default_supplier_name VARCHAR(255),
|
||||
default_supplier_ico VARCHAR(20),
|
||||
default_supplier_dic VARCHAR(20),
|
||||
default_supplier_address TEXT,
|
||||
default_supplier_city VARCHAR(100),
|
||||
default_supplier_zip VARCHAR(10),
|
||||
default_supplier_country VARCHAR(100) DEFAULT 'Česká republika',
|
||||
|
||||
-- Default bank information
|
||||
default_bank_name VARCHAR(255),
|
||||
default_bank_account VARCHAR(50),
|
||||
default_bank_iban VARCHAR(50),
|
||||
default_bank_swift VARCHAR(20),
|
||||
|
||||
-- Invoice defaults
|
||||
default_due_days INTEGER DEFAULT 14,
|
||||
default_vat_rate DECIMAL(5,2) DEFAULT 21.00,
|
||||
default_payment_method VARCHAR(100),
|
||||
invoice_number_prefix VARCHAR(20) DEFAULT 'FV',
|
||||
next_invoice_number INTEGER DEFAULT 1,
|
||||
|
||||
-- Email settings
|
||||
email_subject VARCHAR(255),
|
||||
email_body TEXT,
|
||||
send_emails BOOLEAN DEFAULT false,
|
||||
|
||||
-- Other settings
|
||||
currency VARCHAR(3) DEFAULT 'CZK',
|
||||
language VARCHAR(10) DEFAULT 'cs',
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX idx_customers_ico ON customers(ico);
|
||||
CREATE INDEX idx_customers_email ON customers(email);
|
||||
CREATE INDEX idx_invoices_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_invoice_items_invoice_id ON invoice_items(invoice_id);
|
||||
CREATE INDEX idx_invoice_payments_invoice_id ON invoice_payments(invoice_id);
|
||||
CREATE INDEX idx_invoice_documents_invoice_id ON invoice_documents(invoice_id);
|
||||
|
||||
-- Insert default invoice settings
|
||||
INSERT INTO invoice_settings (
|
||||
default_supplier_name,
|
||||
default_due_days,
|
||||
default_vat_rate,
|
||||
invoice_number_prefix,
|
||||
next_invoice_number,
|
||||
currency,
|
||||
language
|
||||
) VALUES (
|
||||
'Fotbalový klub',
|
||||
14,
|
||||
21.00,
|
||||
'FV',
|
||||
1,
|
||||
'CZK',
|
||||
'cs'
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE comment_reactions DROP CONSTRAINT IF EXISTS unique_comment_user_reaction;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- Add unique constraint for comment reactions if it doesn't exist
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_name = 'unique_comment_user_reaction'
|
||||
AND table_name = 'comment_reactions'
|
||||
) THEN
|
||||
ALTER TABLE comment_reactions ADD CONSTRAINT unique_comment_user_reaction UNIQUE (comment_id, user_id);
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -89,5 +89,4 @@ CREATE TABLE IF NOT EXISTS comment_reactions (
|
||||
);
|
||||
|
||||
CREATE INDEX idx_comment_reactions_comment ON comment_reactions(comment_id);
|
||||
CREATE INDEX idx_comment_reactions_user ON comment_reactions(user_id);
|
||||
CREATE INDEX idx_comment_reactions_type ON comment_reactions(type);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-- Drop e-shop tables
|
||||
DROP TABLE IF EXISTS eshop_shipping_labels CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_payments CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_order_items CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_orders CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_cart_items CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_carts CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_product_variants CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_products CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_product_categories CASCADE;
|
||||
DROP TABLE IF EXISTS eshop_settings CASCADE;
|
||||
@@ -0,0 +1,193 @@
|
||||
-- E-shop tables for MyClub
|
||||
-- These tables support product catalog, shopping cart, orders, payments, and shipping
|
||||
|
||||
-- Product categories
|
||||
CREATE TABLE IF NOT EXISTS eshop_product_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
slug VARCHAR(190) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
parent_id INTEGER REFERENCES eshop_product_categories(id),
|
||||
display_order INTEGER DEFAULT 0,
|
||||
active BOOLEAN DEFAULT true
|
||||
);
|
||||
|
||||
-- Products
|
||||
CREATE TABLE IF NOT EXISTS eshop_products (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
slug VARCHAR(190) UNIQUE NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
short_description TEXT,
|
||||
description_html TEXT,
|
||||
price_cents BIGINT NOT NULL,
|
||||
currency VARCHAR(10) DEFAULT 'CZK',
|
||||
vat_rate DECIMAL(5,4),
|
||||
active BOOLEAN DEFAULT true,
|
||||
stock_mode VARCHAR(20) DEFAULT 'finite',
|
||||
default_image_url VARCHAR(500),
|
||||
gallery_json TEXT,
|
||||
tags TEXT,
|
||||
metadata_json TEXT,
|
||||
category_id INTEGER REFERENCES eshop_product_categories(id)
|
||||
);
|
||||
|
||||
-- Product variants
|
||||
CREATE TABLE IF NOT EXISTS eshop_product_variants (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
product_id INTEGER NOT NULL REFERENCES eshop_products(id) ON DELETE CASCADE,
|
||||
sku VARCHAR(64),
|
||||
name VARCHAR(255),
|
||||
attributes_json TEXT,
|
||||
stock_qty INTEGER DEFAULT 0,
|
||||
barcode VARCHAR(128),
|
||||
image_url VARCHAR(500)
|
||||
);
|
||||
|
||||
-- Shopping carts
|
||||
CREATE TABLE IF NOT EXISTS eshop_carts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
session_token VARCHAR(64),
|
||||
currency VARCHAR(10),
|
||||
completed BOOLEAN DEFAULT false
|
||||
);
|
||||
|
||||
-- Cart items
|
||||
CREATE TABLE IF NOT EXISTS eshop_cart_items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
cart_id INTEGER NOT NULL REFERENCES eshop_carts(id) ON DELETE CASCADE,
|
||||
product_id INTEGER NOT NULL REFERENCES eshop_products(id),
|
||||
variant_id INTEGER REFERENCES eshop_product_variants(id),
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
unit_price_cents BIGINT NOT NULL,
|
||||
currency VARCHAR(10)
|
||||
);
|
||||
|
||||
-- Orders
|
||||
CREATE TABLE IF NOT EXISTS eshop_orders (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
order_number VARCHAR(32) UNIQUE NOT NULL,
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
session_token VARCHAR(64),
|
||||
email VARCHAR(255),
|
||||
first_name VARCHAR(100),
|
||||
last_name VARCHAR(100),
|
||||
billing_address_json TEXT,
|
||||
shipping_address_json TEXT,
|
||||
status VARCHAR(32) DEFAULT 'new',
|
||||
total_amount_cents BIGINT NOT NULL,
|
||||
currency VARCHAR(10),
|
||||
shipping_method VARCHAR(32),
|
||||
shipping_price_cents BIGINT DEFAULT 0,
|
||||
shipping_data_json TEXT,
|
||||
metadata_json TEXT
|
||||
);
|
||||
|
||||
-- Order items
|
||||
CREATE TABLE IF NOT EXISTS eshop_order_items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
order_id INTEGER NOT NULL REFERENCES eshop_orders(id) ON DELETE CASCADE,
|
||||
product_id INTEGER NOT NULL,
|
||||
variant_id INTEGER REFERENCES eshop_product_variants(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
sku VARCHAR(64),
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
unit_price_cents BIGINT NOT NULL,
|
||||
currency VARCHAR(10),
|
||||
vat_rate DECIMAL(5,4)
|
||||
);
|
||||
|
||||
-- Payments
|
||||
CREATE TABLE IF NOT EXISTS eshop_payments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
order_id INTEGER NOT NULL REFERENCES eshop_orders(id) ON DELETE CASCADE,
|
||||
provider VARCHAR(32),
|
||||
provider_payment_id VARCHAR(128),
|
||||
status VARCHAR(32) DEFAULT 'pending',
|
||||
amount_cents BIGINT NOT NULL,
|
||||
currency VARCHAR(10),
|
||||
raw_payload_json TEXT
|
||||
);
|
||||
|
||||
-- Shipping labels
|
||||
CREATE TABLE IF NOT EXISTS eshop_shipping_labels (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
order_id INTEGER NOT NULL REFERENCES eshop_orders(id) ON DELETE CASCADE,
|
||||
carrier VARCHAR(32),
|
||||
packeta_packet_id VARCHAR(64),
|
||||
tracking_number VARCHAR(64),
|
||||
label_url VARCHAR(500),
|
||||
status VARCHAR(64),
|
||||
history_json TEXT
|
||||
);
|
||||
|
||||
-- E-shop settings
|
||||
CREATE TABLE IF NOT EXISTS eshop_settings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
default_currency VARCHAR(10) DEFAULT 'CZK',
|
||||
supported_currencies TEXT,
|
||||
default_country VARCHAR(2) DEFAULT 'CZ',
|
||||
shipping_options_json TEXT,
|
||||
terms_url VARCHAR(500),
|
||||
returns_policy_url VARCHAR(500),
|
||||
support_email VARCHAR(255),
|
||||
support_phone VARCHAR(64)
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_products_active ON eshop_products(active) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_products_slug ON eshop_products(slug) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_products_category ON eshop_products(category_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_variants_product ON eshop_product_variants(product_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_variants_sku ON eshop_product_variants(sku) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_carts_user ON eshop_carts(user_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_carts_session ON eshop_carts(session_token) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_carts_completed ON eshop_carts(completed) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_cart_items_cart ON eshop_cart_items(cart_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_orders_user ON eshop_orders(user_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_orders_session ON eshop_orders(session_token) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_orders_status ON eshop_orders(status) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_orders_number ON eshop_orders(order_number) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_payments_order ON eshop_payments(order_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_payments_provider ON eshop_payments(provider, provider_payment_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_shipping_order ON eshop_shipping_labels(order_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_eshop_shipping_packet ON eshop_shipping_labels(packeta_packet_id) WHERE deleted_at IS NULL;
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Down migration for facility management tables
|
||||
-- Migration: 20260109000001_create_facility_management_tables.down.sql
|
||||
|
||||
DROP TABLE IF EXISTS facility_booking_templates;
|
||||
DROP TABLE IF EXISTS weather_conditions;
|
||||
DROP TABLE IF EXISTS facility_maintenance;
|
||||
DROP TABLE IF EXISTS facility_equipment;
|
||||
DROP TABLE IF EXISTS facility_bookings;
|
||||
DROP TABLE IF EXISTS facility_availability_rules;
|
||||
DROP TABLE IF EXISTS facilities;
|
||||
@@ -0,0 +1,292 @@
|
||||
-- Create facility management tables
|
||||
-- Migration: 20260109000001_create_facility_management_tables.up.sql
|
||||
|
||||
-- Facilities table
|
||||
CREATE TABLE IF NOT EXISTS facilities (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('field', 'gym', 'locker', 'classroom', 'storage', 'other')),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'maintenance', 'closed')),
|
||||
capacity INTEGER,
|
||||
area DECIMAL(10,2),
|
||||
location VARCHAR(255),
|
||||
is_indoor BOOLEAN DEFAULT true,
|
||||
is_outdoor BOOLEAN DEFAULT false,
|
||||
image_url VARCHAR(500),
|
||||
|
||||
-- Booking settings
|
||||
requires_approval BOOLEAN DEFAULT false,
|
||||
min_booking_duration INTEGER DEFAULT 30,
|
||||
max_booking_duration INTEGER DEFAULT 240,
|
||||
booking_advance_days INTEGER DEFAULT 30,
|
||||
|
||||
-- Pricing
|
||||
price_per_hour DECIMAL(10,2) DEFAULT 0.00,
|
||||
|
||||
CONSTRAINT facilities_name_unique UNIQUE (name, deleted_at)
|
||||
);
|
||||
|
||||
-- Facility availability rules
|
||||
CREATE TABLE IF NOT EXISTS facility_availability_rules (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
|
||||
day_of_week INTEGER NOT NULL CHECK (day_of_week BETWEEN 0 AND 6),
|
||||
start_time VARCHAR(5) NOT NULL CHECK (start_time ~ '^[0-2][0-9]:[0-5][0-9]$'),
|
||||
end_time VARCHAR(5) NOT NULL CHECK (end_time ~ '^[0-2][0-9]:[0-5][0-9]$'),
|
||||
is_available BOOLEAN DEFAULT true,
|
||||
start_date DATE,
|
||||
end_date DATE
|
||||
);
|
||||
|
||||
-- Facility bookings
|
||||
CREATE TABLE IF NOT EXISTS facility_bookings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
start_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
end_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'cancelled', 'completed', 'noshow')),
|
||||
|
||||
-- Pricing
|
||||
total_price DECIMAL(10,2) DEFAULT 0.00,
|
||||
payment_status VARCHAR(20) DEFAULT 'pending',
|
||||
|
||||
-- Attendance tracking
|
||||
actual_start_time TIMESTAMP WITH TIME ZONE,
|
||||
actual_end_time TIMESTAMP WITH TIME ZONE,
|
||||
attendees_count INTEGER DEFAULT 0,
|
||||
|
||||
-- Notes
|
||||
internal_notes TEXT,
|
||||
public_notes TEXT,
|
||||
|
||||
-- Cancellation
|
||||
cancelled_at TIMESTAMP WITH TIME ZONE,
|
||||
cancelled_by INTEGER REFERENCES users(id),
|
||||
cancel_reason TEXT,
|
||||
|
||||
CONSTRAINT bookings_no_overlap EXCLUDE (facility_id WITH =, tsrange(start_time, end_time) WITH &&) WHERE (deleted_at IS NULL AND status NOT IN ('cancelled', 'noshow'))
|
||||
);
|
||||
|
||||
-- Facility equipment
|
||||
CREATE TABLE IF NOT EXISTS facility_equipment (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
|
||||
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(100),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'available' CHECK (status IN ('available', 'in_use', 'maintenance', 'damaged', 'lost', 'retired')),
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
available INTEGER NOT NULL DEFAULT 1,
|
||||
|
||||
-- Purchase info
|
||||
purchase_date DATE,
|
||||
purchase_price DECIMAL(10,2),
|
||||
supplier VARCHAR(255),
|
||||
serial_number VARCHAR(255),
|
||||
warranty_expiry DATE,
|
||||
|
||||
-- Maintenance
|
||||
last_maintenance_date DATE,
|
||||
next_maintenance_date DATE,
|
||||
|
||||
-- Location tracking
|
||||
current_location VARCHAR(255),
|
||||
|
||||
image_url VARCHAR(500),
|
||||
|
||||
-- Usage tracking
|
||||
usage_count INTEGER DEFAULT 0,
|
||||
last_used_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
CONSTRAINT equipment_name_facility_unique UNIQUE (name, facility_id, deleted_at)
|
||||
);
|
||||
|
||||
-- Facility maintenance
|
||||
CREATE TABLE IF NOT EXISTS facility_maintenance (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
|
||||
|
||||
type VARCHAR(20) NOT NULL CHECK (type IN ('routine', 'repair', 'inspection', 'upgrade')),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Scheduling
|
||||
scheduled_date TIMESTAMP WITH TIME ZONE,
|
||||
estimated_duration INTEGER, -- minutes
|
||||
actual_duration INTEGER, -- minutes
|
||||
|
||||
-- Status
|
||||
status VARCHAR(20) DEFAULT 'scheduled',
|
||||
started_at TIMESTAMP WITH TIME ZONE,
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
-- Cost
|
||||
estimated_cost DECIMAL(10,2),
|
||||
actual_cost DECIMAL(10,2),
|
||||
|
||||
-- Personnel
|
||||
assigned_to VARCHAR(255),
|
||||
performed_by VARCHAR(255),
|
||||
|
||||
-- Impact
|
||||
is_facility_unavailable BOOLEAN DEFAULT true,
|
||||
|
||||
-- Notes
|
||||
internal_notes TEXT,
|
||||
public_notes TEXT,
|
||||
|
||||
-- Related equipment (JSON array)
|
||||
equipment_affected TEXT
|
||||
);
|
||||
|
||||
-- Weather conditions for outdoor facilities
|
||||
CREATE TABLE IF NOT EXISTS weather_conditions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
|
||||
|
||||
date_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
temperature DECIMAL(5,2), -- Celsius
|
||||
humidity DECIMAL(5,2), -- Percentage
|
||||
precipitation DECIMAL(8,2), -- mm
|
||||
wind_speed DECIMAL(5,2), -- km/h
|
||||
wind_direction INTEGER, -- Degrees
|
||||
weather_code VARCHAR(10), -- OpenWeatherMap condition code
|
||||
description VARCHAR(255),
|
||||
is_suitable BOOLEAN DEFAULT false,
|
||||
recommendations TEXT,
|
||||
|
||||
CONSTRAINT weather_facility_datetime_unique UNIQUE (facility_id, date_time)
|
||||
);
|
||||
|
||||
-- Facility booking templates
|
||||
CREATE TABLE IF NOT EXISTS facility_booking_templates (
|
||||
id SERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
|
||||
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Default booking settings
|
||||
duration INTEGER, -- minutes
|
||||
price_per_hour DECIMAL(10,2),
|
||||
requires_approval BOOLEAN DEFAULT false,
|
||||
|
||||
-- Recurrence pattern
|
||||
is_recurring BOOLEAN DEFAULT false,
|
||||
recurrence_pattern TEXT, -- JSON
|
||||
|
||||
-- Default settings
|
||||
default_title VARCHAR(255),
|
||||
default_description TEXT,
|
||||
default_attendees INTEGER DEFAULT 1,
|
||||
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
|
||||
CONSTRAINT template_name_facility_unique UNIQUE (name, facility_id, deleted_at)
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_facilities_type ON facilities(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_facilities_status ON facilities(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_facilities_is_outdoor ON facilities(is_outdoor);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_bookings_facility_id ON facility_bookings(facility_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_bookings_user_id ON facility_bookings(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_bookings_start_time ON facility_bookings(start_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_bookings_end_time ON facility_bookings(end_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_bookings_status ON facility_bookings(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_bookings_time_range ON facility_bookings USING gist (tsrange(start_time, end_time));
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_equipment_facility_id ON facility_equipment(facility_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_equipment_status ON facility_equipment(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_equipment_category ON facility_equipment(category);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_maintenance_facility_id ON facility_maintenance(facility_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_maintenance_scheduled_date ON facility_maintenance(scheduled_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_maintenance_status ON facility_maintenance(status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_weather_conditions_facility_id ON weather_conditions(facility_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_weather_conditions_date_time ON weather_conditions(date_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_weather_conditions_is_suitable ON weather_conditions(is_suitable);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_booking_templates_facility_id ON facility_booking_templates(facility_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_facility_booking_templates_is_active ON facility_booking_templates(is_active);
|
||||
|
||||
-- Insert sample data
|
||||
INSERT INTO facilities (name, description, type, status, capacity, area, location, is_indoor, is_outdoor, price_per_hour, min_booking_duration, max_booking_duration, booking_advance_days) VALUES
|
||||
('Hlavní hřiště', 'Plnoformátové fotbalové hřiště s přírodním trávníkem', 'field', 'active', 22, 7200.00, 'Hlavní sportovní areál', false, true, 500.00, 60, 180, 14),
|
||||
('Tréninkové hřiště č. 1', 'Menší tréninkové hřiště s umělým trávníkem', 'field', 'active', 16, 4800.00, 'Tréninkový areál - sever', false, true, 300.00, 30, 120, 7),
|
||||
('Posilovna', 'Plně vybavená posilovna pro hráče', 'gym', 'active', 20, 150.00, 'Hlavní budova - 1. patro', true, false, 100.00, 30, 90, 3),
|
||||
('Šatna A', 'Šatna pro domácí tým', 'locker', 'active', 25, 80.00, 'Hlavní budova - přízemí', true, false, 0.00, 15, 60, 1),
|
||||
('Zasedací místnost', 'Místnost pro týmové porady a prezentace', 'classroom', 'active', 30, 60.00, 'Hlavní budova - 2. patro', true, false, 50.00, 30, 120, 7),
|
||||
('Sklad vybavení', 'Sklad pro tréninkové vybavení', 'storage', 'active', 5, 40.00, 'Hlavní budova - suterén', true, false, 0.00, 15, 30, 1);
|
||||
|
||||
-- Insert availability rules for main field (available Mon-Fri 16:00-22:00, Sat-Sun 08:00-22:00)
|
||||
INSERT INTO facility_availability_rules (facility_id, day_of_week, start_time, end_time, is_available)
|
||||
SELECT f.id, d.day_of_week, d.start_time, d.end_time, true
|
||||
FROM facilities f,
|
||||
(VALUES
|
||||
(1, 1, '16:00', '22:00'), -- Monday
|
||||
(1, 2, '16:00', '22:00'), -- Tuesday
|
||||
(1, 3, '16:00', '22:00'), -- Wednesday
|
||||
(1, 4, '16:00', '22:00'), -- Thursday
|
||||
(1, 5, '16:00', '22:00'), -- Friday
|
||||
(1, 6, '08:00', '22:00'), -- Saturday
|
||||
(1, 0, '08:00', '22:00') -- Sunday
|
||||
) AS d(day_of_week, start_time, end_time)
|
||||
WHERE f.name = 'Hlavní hřiště';
|
||||
|
||||
-- Insert sample equipment
|
||||
INSERT INTO facility_equipment (facility_id, name, description, category, quantity, available, purchase_date, purchase_price)
|
||||
SELECT f.id, e.name, e.description, e.category, e.quantity, e.quantity, e.purchase_date, e.purchase_price
|
||||
FROM facilities f,
|
||||
(VALUES
|
||||
(1, 'Míče velikosti 5', 'Plnoformátové fotbalové míče', 'balls', 20, 20, '2024-01-15', 800.00),
|
||||
(1, 'Tréninkové kužely', 'Plastové kužele pro cvičení', 'cones', 50, 50, '2024-01-15', 500.00),
|
||||
(1, 'Branky', 'Přenosné branky pro trénink', 'goals', 4, 4, '2024-02-01', 2000.00),
|
||||
(3, 'Činky', 'Sada činek různých vah', 'weights', 10, 10, '2024-01-20', 5000.00),
|
||||
(3, 'Posilovací lavice', 'Profesionální lavice na bench press', 'benches', 3, 3, '2024-01-20', 3000.00)
|
||||
) AS e(name, description, category, quantity, available, purchase_date, purchase_price)
|
||||
WHERE f.name = 'Hlavní hřiště' AND e.category IN ('balls', 'cones', 'goals')
|
||||
UNION ALL
|
||||
SELECT f.id, e.name, e.description, e.category, e.quantity, e.quantity, e.purchase_date, e.purchase_price
|
||||
FROM facilities f,
|
||||
(VALUES
|
||||
('Činky', 'Sada činek různých vah', 'weights', 10, 10, '2024-01-20', 5000.00),
|
||||
('Posilovací lavice', 'Profesionální lavice na bench press', 'benches', 3, 3, '2024-01-20', 3000.00)
|
||||
) AS e(name, description, category, quantity, available, purchase_date, purchase_price)
|
||||
WHERE f.name = 'Posilovna' AND e.category IN ('weights', 'benches');
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Down migration for reaction constraint fix
|
||||
ALTER TABLE comment_reactions DROP CONSTRAINT IF EXISTS unique_comment_user_reaction;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- Handle existing unique constraint gracefully
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Check if constraint already exists
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_name = 'unique_comment_user_reaction'
|
||||
AND table_name = 'comment_reactions'
|
||||
) THEN
|
||||
-- Try to add constraint, ignore if it exists
|
||||
ALTER TABLE comment_reactions ADD CONSTRAINT unique_comment_user_reaction UNIQUE (comment_id, user_id);
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Remove indexes for comment_reactions table
|
||||
DROP INDEX IF EXISTS idx_comment_reactions_comment;
|
||||
DROP INDEX IF EXISTS idx_comment_reactions_user;
|
||||
DROP INDEX IF EXISTS idx_comment_reactions_type;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add indexes for comment_reactions table (separate from constraints)
|
||||
CREATE INDEX idx_comment_reactions_comment ON comment_reactions(comment_id);
|
||||
CREATE INDEX idx_comment_reactions_user ON comment_reactions(user_id);
|
||||
CREATE INDEX idx_comment_reactions_type ON comment_reactions(type);
|
||||
Reference in New Issue
Block a user