Files
boc/quixzoom-token-system/schema.sql
T
Bernt 58ca4e68db feat(boc): Complete Business Operations Center v1.0
- Go backend API with full CRUD for all modules (CRM, Sales, Finance, HR, Legal, Marketing, Support, Purchase, Inventory, Projects, Automation, Analytics)
- Rust analytics service with parallel report generation
- C runtime with POSIX shared memory IPC
- PostgreSQL schema with 30+ tables, full migrations
- Redis cache, sessions, pub/sub
- Kafka event streaming with Zookeeper
- WebSocket hub for real-time updates
- Automation engine with cron jobs, workflows, event triggers
- JWT authentication, multi-tenant from start
- Docker Compose with all services
- Nginx reverse proxy with rate limiting
- Integration tests passing
- Feature gap analysis against Fortnox/Odoo/Visma

Refs: BOC-001
2026-07-12 12:41:35 +00:00

331 lines
12 KiB
PL/PgSQL

-- ============================================================
-- QZ TOKEN SYSTEM — quiXzoom Token & Payment Schema
-- ============================================================
-- Komplett schema inklusive zoomers, missions, observations
-- ============================================================
-- ZOOMERS
-- ============================================================
CREATE TABLE zoomers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE,
phone VARCHAR(50),
status VARCHAR(50) DEFAULT 'active',
capabilities JSONB DEFAULT '[]',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- ============================================================
-- AREAS
-- ============================================================
CREATE TABLE areas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
country VARCHAR(100),
city VARCHAR(100),
priority FLOAT DEFAULT 0.5,
status VARCHAR(50) DEFAULT 'active',
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- ============================================================
-- OBSERVATIONS
-- ============================================================
CREATE TABLE observations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
observation_id VARCHAR(255) UNIQUE NOT NULL,
source_type VARCHAR(50) NOT NULL,
source_id VARCHAR(255),
zoomer_id UUID REFERENCES zoomers(id),
media_type VARCHAR(50),
media_url TEXT,
status VARCHAR(50) DEFAULT 'pending',
validated_by VARCHAR(255),
validated_at TIMESTAMP WITH TIME ZONE,
ai_analysis JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- ============================================================
-- OBJECTS
-- ============================================================
CREATE TABLE objects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
object_id VARCHAR(255) UNIQUE NOT NULL,
type VARCHAR(100) NOT NULL,
subtype VARCHAR(100),
status VARCHAR(50) DEFAULT 'active',
verification_status VARCHAR(50) DEFAULT 'unverified',
confidence FLOAT DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- ============================================================
-- MISSIONS
-- ============================================================
CREATE TABLE missions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
mission_id VARCHAR(255) UNIQUE NOT NULL,
type VARCHAR(50) NOT NULL,
area_id UUID REFERENCES areas(id),
assigned_to UUID REFERENCES zoomers(id),
assigned_at TIMESTAMP WITH TIME ZONE,
status VARCHAR(50) DEFAULT 'pending',
result JSONB DEFAULT '{}',
completed_at TIMESTAMP WITH TIME ZONE,
priority FLOAT DEFAULT 0.5,
instructions JSONB DEFAULT '[]',
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- ============================================================
-- ZOOMER WALLETS
-- ============================================================
CREATE TABLE zoomer_wallets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
zoomer_id UUID NOT NULL REFERENCES zoomers(id) ON DELETE CASCADE,
balance_qz_token DECIMAL(18, 8) DEFAULT 0.0,
balance_pending_qz_token DECIMAL(18, 8) DEFAULT 0.0,
stripe_connect_account_id VARCHAR(255),
stripe_connect_status VARCHAR(50) DEFAULT 'pending',
payout_schedule VARCHAR(50) DEFAULT 'weekly_monday',
payout_minimum_qz_token DECIMAL(18, 8) DEFAULT 10.0,
status VARCHAR(50) DEFAULT 'active',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(zoomer_id)
);
CREATE INDEX idx_zoomer_wallets_zoomer ON zoomer_wallets(zoomer_id);
CREATE INDEX idx_zoomer_wallets_stripe ON zoomer_wallets(stripe_connect_account_id);
-- ============================================================
-- TOKEN TRANSACTIONS (Immutable Ledger)
-- ============================================================
CREATE TABLE token_transactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tx_hash VARCHAR(255) UNIQUE NOT NULL,
tx_type VARCHAR(50) NOT NULL,
from_wallet_id UUID REFERENCES zoomer_wallets(id),
to_wallet_id UUID REFERENCES zoomer_wallets(id),
amount_qz_token DECIMAL(18, 8) NOT NULL,
amount_usd DECIMAL(18, 8) NOT NULL,
exchange_rate DECIMAL(18, 8) NOT NULL,
mission_id UUID REFERENCES missions(id),
observation_id UUID REFERENCES observations(id),
stripe_payment_intent_id VARCHAR(255),
stripe_payout_id VARCHAR(255),
description TEXT,
metadata JSONB DEFAULT '{}',
signature VARCHAR(512),
signed_by VARCHAR(255),
status VARCHAR(50) DEFAULT 'confirmed',
confirmed_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT no_negative_amount CHECK (amount_qz_token >= 0)
);
CREATE INDEX idx_token_tx_hash ON token_transactions(tx_hash);
CREATE INDEX idx_token_tx_from ON token_transactions(from_wallet_id);
CREATE INDEX idx_token_tx_to ON token_transactions(to_wallet_id);
CREATE INDEX idx_token_tx_type ON token_transactions(tx_type);
CREATE INDEX idx_token_tx_mission ON token_transactions(mission_id);
CREATE INDEX idx_token_tx_created ON token_transactions(created_at);
-- ============================================================
-- MISSION PRICING
-- ============================================================
CREATE TABLE mission_pricing (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
mission_type VARCHAR(50) NOT NULL,
base_price_qz_token DECIMAL(18, 8) NOT NULL,
base_price_usd DECIMAL(18, 8) NOT NULL,
complexity_multiplier DECIMAL(5, 2) DEFAULT 1.0,
distance_bonus_qz_token DECIMAL(18, 8) DEFAULT 0.0,
time_bonus_qz_token DECIMAL(18, 8) DEFAULT 0.0,
quality_bonus_qz_token DECIMAL(18, 8) DEFAULT 0.0,
min_price_qz_token DECIMAL(18, 8) DEFAULT 0.5,
max_price_qz_token DECIMAL(18, 8) DEFAULT 100.0,
country_code VARCHAR(10),
city VARCHAR(100),
status VARCHAR(50) DEFAULT 'active',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(mission_type, country_code, city)
);
CREATE INDEX idx_mission_pricing_type ON mission_pricing(mission_type);
CREATE INDEX idx_mission_pricing_geo ON mission_pricing(country_code, city);
-- ============================================================
-- PAYOUT SCHEDULES
-- ============================================================
CREATE TABLE payout_schedules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
zoomer_id UUID NOT NULL REFERENCES zoomers(id),
wallet_id UUID NOT NULL REFERENCES zoomer_wallets(id),
day_of_week INTEGER NOT NULL DEFAULT 1,
time_of_day TIME NOT NULL DEFAULT '09:00:00',
timezone VARCHAR(50) DEFAULT 'UTC',
minimum_qz_token DECIMAL(18, 8) DEFAULT 10.0,
status VARCHAR(50) DEFAULT 'active',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(zoomer_id)
);
CREATE INDEX idx_payout_schedules_zoomer ON payout_schedules(zoomer_id);
CREATE INDEX idx_payout_schedules_day ON payout_schedules(day_of_week);
-- ============================================================
-- PAYOUT EXECUTIONS
-- ============================================================
CREATE TABLE payout_executions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
zoomer_id UUID NOT NULL REFERENCES zoomers(id),
wallet_id UUID NOT NULL REFERENCES zoomer_wallets(id),
amount_qz_token DECIMAL(18, 8) NOT NULL,
amount_usd DECIMAL(18, 8) NOT NULL,
exchange_rate DECIMAL(18, 8) NOT NULL,
stripe_payout_id VARCHAR(255),
stripe_transfer_id VARCHAR(255),
status VARCHAR(50) DEFAULT 'pending',
error_message TEXT,
retry_count INTEGER DEFAULT 0,
processed_at TIMESTAMP WITH TIME ZONE,
completed_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT no_negative_payout CHECK (amount_qz_token > 0)
);
CREATE INDEX idx_payout_exec_zoomer ON payout_executions(zoomer_id);
CREATE INDEX idx_payout_exec_status ON payout_executions(status);
CREATE INDEX idx_payout_exec_stripe ON payout_executions(stripe_payout_id);
-- ============================================================
-- CUSTOMER TOP-UPS
-- ============================================================
CREATE TABLE customer_topups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id VARCHAR(255) NOT NULL,
amount_qz_token DECIMAL(18, 8) NOT NULL,
amount_usd DECIMAL(18, 8) NOT NULL,
exchange_rate DECIMAL(18, 8) NOT NULL,
stripe_payment_intent_id VARCHAR(255),
stripe_charge_id VARCHAR(255),
status VARCHAR(50) DEFAULT 'pending',
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
completed_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_customer_topups_customer ON customer_topups(customer_id);
CREATE INDEX idx_customer_topups_stripe ON customer_topups(stripe_payment_intent_id);
-- ============================================================
-- VIEWS
-- ============================================================
CREATE VIEW zoomer_balance AS
SELECT
z.id as zoomer_id,
z.name as zoomer_name,
zw.id as wallet_id,
zw.balance_qz_token,
zw.balance_pending_qz_token,
zw.balance_qz_token + zw.balance_pending_qz_token as total_balance,
zw.stripe_connect_status,
zw.payout_schedule
FROM zoomers z
JOIN zoomer_wallets zw ON zw.zoomer_id = z.id;
CREATE VIEW token_flow_summary AS
SELECT
DATE_TRUNC('day', created_at) as date,
tx_type,
COUNT(*) as transaction_count,
SUM(amount_qz_token) as total_qz_token,
SUM(amount_usd) as total_usd
FROM token_transactions
WHERE status = 'confirmed'
GROUP BY DATE_TRUNC('day', created_at), tx_type
ORDER BY date DESC, tx_type;
-- ============================================================
-- TRIGGERS
-- ============================================================
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_zoomers_updated_at BEFORE UPDATE ON zoomers
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_areas_updated_at BEFORE UPDATE ON areas
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON objects
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_missions_updated_at BEFORE UPDATE ON missions
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_zoomer_wallets_updated_at BEFORE UPDATE ON zoomer_wallets
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_mission_pricing_updated_at BEFORE UPDATE ON mission_pricing
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_payout_schedules_updated_at BEFORE UPDATE ON payout_schedules
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================
-- LEDGER INTEGRITY
-- ============================================================
CREATE OR REPLACE FUNCTION prevent_tx_update()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.status = 'confirmed' THEN
RAISE EXCEPTION 'Cannot modify confirmed transaction: %', OLD.tx_hash;
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER prevent_confirmed_tx_update BEFORE UPDATE ON token_transactions
FOR EACH ROW EXECUTE FUNCTION prevent_tx_update();
CREATE OR REPLACE FUNCTION prevent_tx_delete()
RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'Cannot delete transactions from immutable ledger';
END;
$$ language 'plpgsql';
CREATE TRIGGER prevent_tx_deletion BEFORE DELETE ON token_transactions
FOR EACH ROW EXECUTE FUNCTION prevent_tx_delete();