CREATE TABLE IF NOT EXISTS users ( id BIGSERIAL PRIMARY KEY, first_name VARCHAR(255) NOT NULL, last_name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE, phone_number VARCHAR(20) UNIQUE, role VARCHAR(50) NOT NULL, password BYTEA NOT NULL, email_verified BOOLEAN NOT NULL DEFAULT FALSE, phone_verified BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ, company_id BIGINT, suspended_at TIMESTAMPTZ NULL, -- this can be NULL if the user is not suspended suspended BOOLEAN NOT NULL DEFAULT FALSE, CHECK ( email IS NOT NULL OR phone_number IS NOT NULL ), UNIQUE (email, company_id), UNIQUE (phone_number, company_id) ); CREATE TABLE IF NOT EXISTS virtual_game_providers ( id BIGSERIAL PRIMARY KEY, provider_id VARCHAR(100) UNIQUE NOT NULL, -- providerId from Veli Games provider_name VARCHAR(255) NOT NULL, -- providerName logo_dark TEXT, -- logoForDark (URL) logo_light TEXT, -- logoForLight (URL) enabled BOOLEAN NOT NULL DEFAULT TRUE, -- allow enabling/disabling providers created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ ); CREATE TABLE IF NOT EXISTS virtual_games ( id BIGSERIAL PRIMARY KEY, game_id VARCHAR(150) NOT NULL, provider_id VARCHAR(100) NOT NULL REFERENCES virtual_game_providers(provider_id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, category VARCHAR(100), device_type VARCHAR(100), volatility VARCHAR(50), rtp NUMERIC(5, 2), has_demo BOOLEAN DEFAULT FALSE, has_free_bets BOOLEAN DEFAULT FALSE, bets NUMERIC [] DEFAULT '{}', thumbnail TEXT, status INT, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS ux_virtual_games_provider_game ON virtual_games (provider_id, game_id); CREATE TABLE IF NOT EXISTS wallets ( id BIGSERIAL PRIMARY KEY, balance BIGINT NOT NULL DEFAULT 0, currency VARCHAR(3) NOT NULL DEFAULT 'ETB', is_withdraw BOOLEAN NOT NULL, is_bettable BOOLEAN NOT NULL, is_transferable BOOLEAN NOT NULL, user_id BIGINT NOT NULL, type TEXT NOT NULL CHECK ( type IN ( 'regular_wallet', 'static_wallet', 'branch_wallet', 'company_wallet' ) ), is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, type) ); CREATE TABLE refresh_tokens ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL, token TEXT NOT NULL UNIQUE, expires_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, revoked BOOLEAN DEFAULT FALSE NOT NULL, CONSTRAINT unique_token UNIQUE (token) ); ----- CREATE TABLE otps ( id BIGSERIAL PRIMARY KEY, sent_to VARCHAR(255) NOT NULL, medium VARCHAR(50) NOT NULL, otp_for VARCHAR(50) NOT NULL, otp VARCHAR(10) NOT NULL, used BOOLEAN NOT NULL DEFAULT FALSE, used_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMPTZ NOT NULL ); CREATE TABLE IF NOT EXISTS bets ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, amount BIGINT NOT NULL, total_odds REAL NOT NULL, status INT NOT NULL, user_id BIGINT NOT NULL, is_shop_bet BOOLEAN NOT NULL, cashed_out BOOLEAN NOT NULL DEFAULT false, outcomes_hash TEXT NOT NULL, fast_code VARCHAR(10) NOT NULL, processed BOOLEAN DEFAULT FALSE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS tickets ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, amount BIGINT NOT NULL, total_odds REAL NOT NULL, IP VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE exchange_rates ( id SERIAL PRIMARY KEY, from_currency VARCHAR(3) NOT NULL, to_currency VARCHAR(3) NOT NULL, rate DECIMAL(19, 6) NOT NULL, valid_until TIMESTAMP NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW (), UNIQUE (from_currency, to_currency) ); CREATE TABLE IF NOT EXISTS bet_outcomes ( id BIGSERIAL PRIMARY KEY, bet_id BIGINT NOT NULL, sport_id BIGINT NOT NULL, event_id BIGINT NOT null, odd_id BIGINT NOT NULL, home_team_name TEXT NOT NULL, away_team_name TEXT NOT NULL, market_id BIGINT NOT NULL, market_name TEXT NOT NULL, odd REAL NOT NULL, odd_name TEXT NOT NULL, odd_header TEXT NOT NULL, odd_handicap TEXT NOT NULL, status INT NOT NULL DEFAULT 0, expires TIMESTAMP NOT NULL ); CREATE TABLE IF NOT EXISTS ticket_outcomes ( id BIGSERIAL PRIMARY KEY, ticket_id BIGINT NOT NULL, event_id BIGINT NOT null, odd_id BIGINT NOT NULL, home_team_name TEXT NOT NULL, away_team_name TEXT NOT NULL, market_id BIGINT NOT NULL, market_name TEXT NOT NULL, odd REAL NOT NULL, odd_name TEXT NOT NULL, odd_header TEXT NOT NULL, odd_handicap TEXT NOT NULL, status INT NOT NULL DEFAULT 0, expires TIMESTAMP NOT NULL ); CREATE TABLE IF NOT EXISTS banks ( id BIGSERIAL PRIMARY KEY, slug VARCHAR(255) NOT NULL UNIQUE, swift VARCHAR(20) NOT NULL, name VARCHAR(255) NOT NULL, acct_length INT NOT NULL, country_id INT NOT NULL, is_mobilemoney INT, -- nullable integer (0 or 1) is_active INT NOT NULL, -- 0 or 1 is_rtgs INT NOT NULL, -- 0 or 1 active INT NOT NULL, -- 0 or 1 is_24hrs INT, -- nullable integer (0 or 1) created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, currency VARCHAR(10) NOT NULL, bank_logo TEXT -- URL or base64 string ); CREATE TABLE IF NOT EXISTS wallets ( id BIGSERIAL PRIMARY KEY, balance BIGINT NOT NULL DEFAULT 0, is_withdraw BOOLEAN NOT NULL, is_bettable BOOLEAN NOT NULL, is_transferable BOOLEAN NOT NULL, user_id BIGINT NOT NULL, type VARCHAR(255) NOT NULL, is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT balance_positve CHECK (balance >= 0) ); CREATE TABLE IF NOT EXISTS customer_wallets ( id BIGSERIAL PRIMARY KEY, customer_id BIGINT NOT NULL, regular_wallet_id BIGINT NOT NULL, static_wallet_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS wallet_transfer ( id BIGSERIAL PRIMARY KEY, amount BIGINT, message TEXT NOT NULL, type VARCHAR(255), receiver_wallet_id BIGINT, sender_wallet_id BIGINT, cashier_id BIGINT, verified BOOLEAN DEFAULT false, reference_number VARCHAR(255) NOT NULL, session_id VARCHAR(255), status VARCHAR(255), payment_method VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS shop_transactions ( id BIGSERIAL PRIMARY KEY, amount BIGINT NOT NULL, branch_id BIGINT NOT NULL, company_id BIGINT NOT NULL, user_id BIGINT NOT NULL, type BIGINT NOT NULL, full_name VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, payment_option BIGINT NOT NULL, bank_code VARCHAR(255), beneficiary_name VARCHAR(255), account_name VARCHAR(255), account_number VARCHAR(255), reference_number VARCHAR(255), approved_by BIGINT, verified BOOLEAN DEFAULT false NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS shop_bets ( id BIGSERIAL PRIMARY KEY, shop_transaction_id BIGINT NOT NULL, cashout_id VARCHAR(255) NOT NULL, cashed_out_by BIGINT, bet_id BIGINT NOT NULL, number_of_outcomes BIGINT NOT NULL, cashed_out BOOLEAN DEFAULT FALSE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (shop_transaction_id), UNIQUE (bet_id), UNIQUE (cashout_id) ); CREATE TABLE IF NOT EXISTS shop_deposits ( id BIGSERIAL PRIMARY KEY, shop_transaction_id BIGINT NOT NULL, customer_id BIGINT NOT NULL, wallet_transfer_id BIGINT, branch_wallet_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (shop_transaction_id) ); CREATE TABLE IF NOT EXISTS branches ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, location TEXT NOT NULL, profit_percent REAL NOT NULL, is_active BOOLEAN NOT NULL DEFAULT false, wallet_id BIGINT NOT NULL, branch_manager_id BIGINT NOT NULL, company_id BIGINT NOT NULL, is_self_owned BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (wallet_id), CONSTRAINT profit_percentage_check CHECK ( profit_percent >= 0 AND profit_percent < 1 ) ); CREATE TABLE IF NOT EXISTS branch_operations ( id BIGSERIAL PRIMARY KEY, operation_id BIGINT NOT NULL, branch_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS branch_cashiers ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL, branch_id BIGINT NOT NULL, UNIQUE (user_id, branch_id) ); CREATE TABLE IF NOT EXISTS branch_locations (key TEXT PRIMARY KEY, value TEXT NOT NULL); CREATE TABLE events ( id BIGSERIAL PRIMARY KEY, source_event_id TEXT NOT NULL, sport_id INT NOT NULL, match_name TEXT NOT NULL, home_team TEXT NOT NULL, away_team TEXT NOT NULL, home_team_id BIGINT NOT NULL, away_team_id BIGINT NOT NULL, home_kit_image TEXT NOT NULL, away_kit_image TEXT NOT NULL, league_id BIGINT NOT NULL, league_name TEXT NOT NULL, start_time TIMESTAMP NOT NULL, score TEXT, match_minute INT, timer_status TEXT, added_time INT, match_period INT, is_live BOOLEAN NOT NULL DEFAULT false, status TEXT NOT NULL, fetched_at TIMESTAMP DEFAULT now (), source TEXT NOT NULL DEFAULT 'b365api' CHECK ( source IN ('b365api', 'bfair', '1xbet', 'bwin', 'enetpulse') ), default_is_active BOOLEAN NOT NULL DEFAULT true, default_is_featured BOOLEAN NOT NULL DEFAULT false, default_winning_upper_limit BIGINT NOT NULL, is_monitored BOOLEAN NOT NULL DEFAULT FALSE, UNIQUE(source_event_id, source) ); CREATE TABLE event_history ( id BIGSERIAL PRIMARY KEY, event_id BIGINT NOT NULL, status TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE company_event_settings ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, event_id BIGINT NOT NULL, is_active BOOLEAN, is_featured BOOLEAN, winning_upper_limit BIGINT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (company_id, event_id) ); CREATE TABLE odds_market ( id BIGSERIAL PRIMARY KEY, event_id BIGINT NOT NULL, market_type TEXT NOT NULL, market_name TEXT NOT NULL, market_category TEXT NOT NULL, market_id BIGINT NOT NULL, raw_odds JSONB NOT NULL, default_is_active BOOLEAN NOT NULL DEFAULT true, fetched_at TIMESTAMP DEFAULT now (), expires_at TIMESTAMP NOT NULL, UNIQUE (event_id, market_id) ); CREATE TABLE odd_history ( id BIGSERIAL PRIMARY KEY, odds_market_id BIGINT NOT NULL REFERENCES odds_market (id), raw_odd_id BIGINT NOT NULL, market_id BIGINT NOT NULL, event_id BIGINT NOT NULL, odd_value DOUBLE PRECISION NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE disabled_odd ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, odds_market_id BIGINT NOT NULL, raw_odd_id BIGINT NOT NULL, event_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE company_odd_settings ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, odds_market_id BIGINT NOT NULL, is_active BOOLEAN, custom_raw_odds JSONB, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (company_id, odds_market_id) ); CREATE TABLE result_log ( id BIGSERIAL PRIMARY KEY, status_not_finished_count INT NOT NULL, status_not_finished_bets INT NOT NULL, status_to_be_fixed_count INT NOT NULL, status_to_be_fixed_bets INT NOT NULL, status_postponed_count INT NOT NULL, status_postponed_bets INT NOT NULL, status_ended_count INT NOT NULL, status_ended_bets INT NOT NULL, status_removed_count INT NOT NULL, status_removed_bets INT NOT NULL, removed_count INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE companies ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, slug TEXT UNIQUE NOT NULL, admin_id BIGINT NOT NULL, wallet_id BIGINT NOT NULL, deducted_percentage REAL NOT NULL, is_active BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT deducted_percentage_check CHECK ( deducted_percentage >= 0 AND deducted_percentage < 1 ) ); CREATE TABLE leagues ( id BIGINT PRIMARY KEY, name TEXT NOT NULL, img_url TEXT, country_code TEXT, bet365_id INT, sport_id INT NOT NULL, default_is_active BOOLEAN NOT NULL DEFAULT true, default_is_featured BOOLEAN NOT NULL DEFAULT false ); CREATE TABLE company_league_settings ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, league_id BIGINT NOT NULL, is_active BOOLEAN, is_featured BOOLEAN, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (league_id, company_id) ); CREATE TABLE teams ( id BIGSERIAL PRIMARY KEY, team_name TEXT NOT NULL, country_code TEXT NOT NULL, bet365_id BIGINT, img_url TEXT ); CREATE TABLE IF NOT EXISTS global_settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Tenant/Company-specific overrides CREATE TABLE IF NOT EXISTS company_settings ( company_id BIGINT NOT NULL REFERENCES companies (id) ON DELETE CASCADE, key TEXT NOT NULL, value TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (company_id, key) ); CREATE TABLE user_bonuses ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, type TEXT NOT NULL, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, reward_amount BIGINT NOT NULL, is_claimed BOOLEAN NOT NULL DEFAULT false, expires_at TIMESTAMP NOT NULL, claimed_at TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE flags ( id BIGSERIAL PRIMARY KEY, bet_id BIGINT REFERENCES bets (id) ON DELETE CASCADE, odds_market_id BIGINT REFERENCES odds_market (id), reason TEXT, flagged_at TIMESTAMP DEFAULT NOW (), resolved BOOLEAN DEFAULT FALSE, -- either bet or odd is flagged (not at the same time) CHECK ( ( bet_id IS NOT NULL AND odds_market_id IS NULL ) OR ( bet_id IS NULL AND odds_market_id IS NOT NULL ) ) ); CREATE TABLE direct_deposits ( id BIGSERIAL PRIMARY KEY, customer_id BIGINT NOT NULL REFERENCES users (id), wallet_id BIGINT NOT NULL REFERENCES wallets (id), amount NUMERIC(15, 2) NOT NULL, bank_reference TEXT NOT NULL, sender_account TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('pending', 'completed', 'rejected')), created_at TIMESTAMP NOT NULL DEFAULT NOW (), verified_by BIGINT REFERENCES users (id), verification_notes TEXT, verified_at TIMESTAMP ); CREATE INDEX idx_direct_deposits_status ON direct_deposits (status); CREATE INDEX idx_direct_deposits_customer ON direct_deposits (customer_id); CREATE INDEX idx_direct_deposits_reference ON direct_deposits (bank_reference); CREATE TABLE IF NOT EXISTS raffles ( id SERIAL PRIMARY KEY, company_id INT NOT NULL, name VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), expires_at TIMESTAMP NOT NULL, -- -1 means there is no limit for the raffle ticket_limit INT NOT NULL DEFAULT -1, type VARCHAR(50) NOT NULL CHECK (type IN ('virtual', 'sport')), status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'completed')) ); CREATE TABLE IF NOT EXISTS raffle_tickets ( id SERIAL PRIMARY KEY, raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, user_id INT NOT NULL, is_active BOOL DEFAULT true ); CREATE TABLE IF NOT EXISTS raffle_winners ( id SERIAL PRIMARY KEY, raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, user_id INT NOT NULL, rank INT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS raffle_sport_filters ( id SERIAL PRIMARY KEY, raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, sport_id BIGINT NOT NULL, league_id BIGINT NOT NULL, CONSTRAINT unique_raffle_sport_league UNIQUE (raffle_id, sport_id, league_id) ); CREATE TABLE IF NOT EXISTS raffle_game_filters ( id SERIAL PRIMARY KEY, raffle_id INT NOT NULL REFERENCES raffles(id) ON DELETE CASCADE, game_id VARCHAR(150) NOT NULL, CONSTRAINT unique_raffle_game UNIQUE (raffle_id, game_id) ); CREATE TABLE IF NOT EXISTS accumulator ( outcome_count BIGINT PRIMARY KEY, default_multiplier REAL NOT NULL ); CREATE TABLE IF NOT EXISTS company_accumulator ( id SERIAL PRIMARY KEY, company_id BIGINT NOT NULL, outcome_count BIGINT NOT NULL, multiplier REAL NOT NULL ); ------ Views CREATE VIEW companies_details AS SELECT companies.*, wallets.balance, wallets.is_active as wallet_is_active, users.first_name AS admin_first_name, users.last_name AS admin_last_name, users.phone_number AS admin_phone_number FROM companies JOIN wallets ON wallets.id = companies.wallet_id JOIN users ON users.id = companies.admin_id; ; CREATE VIEW branch_details AS SELECT branches.*, CONCAT (users.first_name, ' ', users.last_name) AS manager_name, users.phone_number AS manager_phone_number, wallets.balance, wallets.is_active AS wallet_is_active FROM branches LEFT JOIN users ON branches.branch_manager_id = users.id LEFT JOIN wallets ON wallets.id = branches.wallet_id; CREATE TABLE IF NOT EXISTS supported_operations ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL ); CREATE VIEW bet_with_outcomes AS SELECT bets.*, CONCAT (users.first_name, ' ', users.last_name) AS full_name, users.phone_number, JSON_AGG (bet_outcomes.*) AS outcomes FROM bets LEFT JOIN bet_outcomes ON bets.id = bet_outcomes.bet_id LEFT JOIN users ON bets.user_id = users.id GROUP BY bets.id, users.first_name, users.last_name, users.phone_number; CREATE VIEW ticket_with_outcomes AS SELECT tickets.*, JSON_AGG (ticket_outcomes.*) AS outcomes FROM tickets LEFT JOIN ticket_outcomes ON tickets.id = ticket_outcomes.ticket_id GROUP BY tickets.id; CREATE VIEW customer_wallet_details AS SELECT cw.id, cw.customer_id, rw.id AS regular_id, rw.balance AS regular_balance, sw.id AS static_id, sw.balance AS static_balance, rw.is_active as regular_is_active, sw.is_active as static_is_active, rw.updated_at as regular_updated_at, sw.updated_at as static_updated_at, cw.created_at, users.first_name, users.last_name, users.phone_number FROM customer_wallets cw JOIN wallets rw ON cw.regular_wallet_id = rw.id JOIN wallets sw ON cw.static_wallet_id = sw.id JOIN users ON users.id = cw.customer_id; CREATE VIEW wallet_transfer_details AS SELECT wt.*, users.first_name, users.last_name, users.phone_number FROM wallet_transfer wt LEFT JOIN users ON users.id = wt.cashier_id; CREATE VIEW shop_transaction_detail AS SELECT st.*, cr.first_name AS creator_first_name, cr.last_name AS creator_last_name, cr.phone_number AS creator_phone_number, ap.first_name AS approver_first_name, ap.last_name AS approver_last_name, ap.phone_number AS approver_phone_number, branches.name AS branch_name, branches.location AS branch_location FROM shop_transactions st LEFT JOIN users cr ON cr.id = st.user_id LEFT JOIN users ap ON ap.id = st.approved_by LEFT JOIN branches ON branches.id = st.branch_id; CREATE VIEW shop_bet_detail AS SELECT sb.*, st.full_name AS customer_full_name, st.phone_number AS customer_phone_number, st.branch_id, st.company_id, st.amount, st.verified AS transaction_verified, bets.status, bets.total_odds, JSON_AGG (bet_outcomes.*) AS outcomes FROM shop_bets AS sb JOIN shop_transactions st ON st.id = sb.shop_transaction_id JOIN bets ON bets.id = sb.bet_id LEFT JOIN bet_outcomes ON bet_outcomes.bet_id = sb.bet_id GROUP BY sb.id, st.full_name, st.phone_number, st.branch_id, st.company_id, st.amount, st.verified, bets.status, bets.total_odds; CREATE VIEW shop_deposit_detail AS SELECT sd.*, st.full_name, st.phone_number, st.branch_id, st.company_id, st.amount, st.verified AS transaction_verified FROM shop_deposits AS sd JOIN shop_transactions st ON st.id = sd.shop_transaction_id; CREATE VIEW league_with_settings AS SELECT l.*, cls.company_id, COALESCE(cls.is_active, l.default_is_active) AS is_active, COALESCE(cls.is_featured, l.default_is_featured) AS is_featured, cls.updated_at FROM leagues l LEFT JOIN company_league_settings cls ON l.id = cls.league_id; CREATE VIEW event_with_settings AS SELECT e.*, ces.company_id, COALESCE(ces.is_active, e.default_is_active) AS is_active, COALESCE(ces.is_featured, e.default_is_featured) AS is_featured, COALESCE( ces.winning_upper_limit, e.default_winning_upper_limit ) AS winning_upper_limit, ces.updated_at, l.country_code as league_cc FROM events e LEFT JOIN company_event_settings ces ON e.id = ces.event_id JOIN leagues l ON l.id = e.league_id; CREATE VIEW event_with_country AS SELECT events.*, leagues.country_code as league_cc FROM events LEFT JOIN leagues ON leagues.id = events.league_id; CREATE VIEW odds_market_with_settings AS SELECT o.id, o.event_id, o.market_type, o.market_name, o.market_category, o.market_id, o.default_is_active, o.fetched_at, o.expires_at, cos.company_id, COALESCE(cos.is_active, o.default_is_active) AS is_active, COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds, cos.updated_at FROM odds_market o LEFT JOIN company_odd_settings cos ON o.id = cos.odds_market_id; CREATE VIEW odds_market_with_event AS SELECT o.*, e.is_monitored, e.is_live, e.status, e.source FROM odds_market o JOIN events e ON o.event_id = e.id; -- Foreign Keys ALTER TABLE refresh_tokens ADD CONSTRAINT fk_refresh_tokens_users FOREIGN KEY (user_id) REFERENCES users (id); ALTER TABLE bets ADD CONSTRAINT fk_bets_users FOREIGN KEY (user_id) REFERENCES users (id); ALTER TABLE wallets ADD CONSTRAINT fk_wallets_users FOREIGN KEY (user_id) REFERENCES users (id); ALTER TABLE customer_wallets ADD CONSTRAINT fk_customer_wallets_customers FOREIGN KEY (customer_id) REFERENCES users (id), ADD CONSTRAINT fk_customer_wallets_regular_wallet FOREIGN KEY (regular_wallet_id) REFERENCES wallets (id), ADD CONSTRAINT fk_customer_wallets_static_wallet FOREIGN KEY (static_wallet_id) REFERENCES wallets (id); ALTER TABLE wallet_transfer ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_id) REFERENCES wallets (id), ADD CONSTRAINT fk_wallet_transfer_sender_wallet FOREIGN KEY (sender_wallet_id) REFERENCES wallets (id), ADD CONSTRAINT fk_wallet_transfer_cashier FOREIGN KEY (cashier_id) REFERENCES users (id); ALTER TABLE shop_transactions ADD CONSTRAINT fk_shop_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches (id), ADD CONSTRAINT fk_shop_transactions_users FOREIGN KEY (user_id) REFERENCES users (id); ALTER TABLE shop_bets ADD CONSTRAINT fk_shop_bet_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions (id), ADD CONSTRAINT fk_shop_bet_bets FOREIGN KEY (bet_id) REFERENCES bets (id); ALTER TABLE shop_deposits ADD CONSTRAINT fk_shop_deposit_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions (id), ADD CONSTRAINT fk_shop_deposit_customers FOREIGN KEY (customer_id) REFERENCES users (id); ALTER TABLE branches ADD CONSTRAINT fk_branches_wallet FOREIGN KEY (wallet_id) REFERENCES wallets (id), ADD CONSTRAINT fk_branches_manager FOREIGN KEY (branch_manager_id) REFERENCES users (id), ADD CONSTRAINT fk_branches_location FOREIGN KEY (location) REFERENCES branch_locations (key); ALTER TABLE branch_operations ADD CONSTRAINT fk_branch_operations_operations FOREIGN KEY (operation_id) REFERENCES supported_operations (id) ON DELETE CASCADE, ADD CONSTRAINT fk_branch_operations_branches FOREIGN KEY (branch_id) REFERENCES branches (id) ON DELETE CASCADE; ALTER TABLE branch_cashiers ADD CONSTRAINT fk_branch_cashiers_users FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, ADD CONSTRAINT fk_branch_cashiers_branches FOREIGN KEY (branch_id) REFERENCES branches (id) ON DELETE CASCADE; ALTER TABLE companies ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users (id), ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets (id) ON DELETE CASCADE; ALTER TABLE company_league_settings ADD CONSTRAINT fk_league_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE, ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES leagues (id) ON DELETE CASCADE; ALTER TABLE company_event_settings ADD CONSTRAINT fk_event_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE, ADD CONSTRAINT fk_event_settings_event FOREIGN KEY (event_id) REFERENCES events (id) ON DELETE CASCADE; ALTER TABLE company_odd_settings ADD CONSTRAINT fk_odds_settings_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE, ADD CONSTRAINT fk_odds_settings_odds_market FOREIGN KEY (odds_market_id) REFERENCES odds_market (id) ON DELETE CASCADE;