- 6 tables: structures, ranks, nodes, commissions, bonuses, rank_history - 5 enums: structure_type, node_status, commission_type, commission_status, bonus_type - LTREE extension for hierarchical path queries - 24 RLS policies for multi-tenancy - GIST index for LTREE path column Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
236 lines
9.3 KiB
SQL
236 lines
9.3 KiB
SQL
-- =============================================
|
|
-- Tables: mlm
|
|
-- Module: SAAS-021 MLM (Multi-Level Marketing)
|
|
-- =============================================
|
|
|
|
-- ─────────────────────────────────────────────
|
|
-- structures - MLM network structure configuration
|
|
-- ─────────────────────────────────────────────
|
|
CREATE TABLE mlm.structures (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
type mlm.structure_type NOT NULL,
|
|
|
|
-- Configuration per type (JSONB)
|
|
-- Unilevel: { max_width: null, max_depth: 10 }
|
|
-- Binary: { spillover: 'left_first' | 'weak_leg' | 'balanced' }
|
|
-- Matrix: { width: 3, depth: 7 }
|
|
config JSONB NOT NULL DEFAULT '{}',
|
|
|
|
-- Commission rates by level (JSONB array)
|
|
-- [{ level: 1, rate: 0.10 }, { level: 2, rate: 0.05 }, ...]
|
|
level_rates JSONB NOT NULL DEFAULT '[]',
|
|
|
|
-- Matching bonus rates (for matching commissions)
|
|
matching_rates JSONB DEFAULT '[]',
|
|
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by UUID REFERENCES users.users(id) ON DELETE SET NULL,
|
|
|
|
CONSTRAINT unique_structure_name_per_tenant UNIQUE (tenant_id, name)
|
|
);
|
|
|
|
COMMENT ON TABLE mlm.structures IS 'MLM network structure configurations';
|
|
COMMENT ON COLUMN mlm.structures.config IS 'Structure-specific configuration (max_depth, spillover, etc.)';
|
|
COMMENT ON COLUMN mlm.structures.level_rates IS 'Commission percentages by level depth';
|
|
|
|
-- Trigger for updated_at
|
|
CREATE TRIGGER set_structures_updated_at
|
|
BEFORE UPDATE ON mlm.structures
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.set_updated_at();
|
|
|
|
-- ─────────────────────────────────────────────
|
|
-- ranks - MLM qualification ranks
|
|
-- ─────────────────────────────────────────────
|
|
CREATE TABLE mlm.ranks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
structure_id UUID NOT NULL REFERENCES mlm.structures(id) ON DELETE CASCADE,
|
|
|
|
name VARCHAR(100) NOT NULL,
|
|
level INTEGER NOT NULL, -- 1=Entry, 2=Bronze, 3=Silver, etc.
|
|
badge_url VARCHAR(500),
|
|
color VARCHAR(7), -- Hex color for UI
|
|
|
|
-- Requirements to achieve rank (JSONB)
|
|
-- {
|
|
-- personal_volume: 1000,
|
|
-- group_volume: 10000,
|
|
-- direct_referrals: 3,
|
|
-- active_legs: 2,
|
|
-- rank_in_legs: { rank_level: 2, count: 1 }
|
|
-- }
|
|
requirements JSONB NOT NULL DEFAULT '{}',
|
|
|
|
-- Benefits for this rank
|
|
bonus_rate DECIMAL(10,4), -- Additional bonus percentage
|
|
benefits JSONB DEFAULT '{}', -- Other benefits (discounts, access, etc.)
|
|
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
CONSTRAINT unique_rank_level_per_structure UNIQUE (structure_id, level)
|
|
);
|
|
|
|
COMMENT ON TABLE mlm.ranks IS 'MLM qualification ranks with requirements and benefits';
|
|
COMMENT ON COLUMN mlm.ranks.requirements IS 'Conditions to achieve this rank';
|
|
|
|
-- Trigger for updated_at
|
|
CREATE TRIGGER set_ranks_updated_at
|
|
BEFORE UPDATE ON mlm.ranks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.set_updated_at();
|
|
|
|
-- ─────────────────────────────────────────────
|
|
-- nodes - MLM network nodes (distributors)
|
|
-- ─────────────────────────────────────────────
|
|
CREATE TABLE mlm.nodes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
structure_id UUID NOT NULL REFERENCES mlm.structures(id) ON DELETE CASCADE,
|
|
|
|
user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,
|
|
|
|
-- Hierarchy
|
|
parent_id UUID REFERENCES mlm.nodes(id) ON DELETE SET NULL,
|
|
sponsor_id UUID REFERENCES mlm.nodes(id) ON DELETE SET NULL, -- Who referred them
|
|
position INTEGER, -- For binary: 1=left, 2=right. For matrix: 1,2,3...width
|
|
|
|
-- Materialized path for efficient queries (LTREE)
|
|
path LTREE,
|
|
depth INTEGER DEFAULT 0,
|
|
|
|
-- Current and highest rank
|
|
rank_id UUID REFERENCES mlm.ranks(id) ON DELETE SET NULL,
|
|
highest_rank_id UUID REFERENCES mlm.ranks(id) ON DELETE SET NULL,
|
|
|
|
-- Performance metrics
|
|
personal_volume DECIMAL(15,2) DEFAULT 0,
|
|
group_volume DECIMAL(15,2) DEFAULT 0,
|
|
direct_referrals INTEGER DEFAULT 0,
|
|
total_downline INTEGER DEFAULT 0,
|
|
|
|
-- Lifetime earnings
|
|
total_earnings DECIMAL(15,2) DEFAULT 0,
|
|
|
|
-- Status
|
|
status mlm.node_status NOT NULL DEFAULT 'active',
|
|
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
-- Invitation
|
|
invite_code VARCHAR(20) UNIQUE,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
CONSTRAINT unique_user_per_structure UNIQUE (structure_id, user_id)
|
|
);
|
|
|
|
COMMENT ON TABLE mlm.nodes IS 'MLM network nodes representing distributors in the hierarchy';
|
|
COMMENT ON COLUMN mlm.nodes.path IS 'LTREE path for efficient ancestor/descendant queries';
|
|
COMMENT ON COLUMN mlm.nodes.position IS 'Position under parent (left/right for binary, slot for matrix)';
|
|
|
|
-- Trigger for updated_at
|
|
CREATE TRIGGER set_nodes_updated_at
|
|
BEFORE UPDATE ON mlm.nodes
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION public.set_updated_at();
|
|
|
|
-- ─────────────────────────────────────────────
|
|
-- commissions - MLM commission entries
|
|
-- ─────────────────────────────────────────────
|
|
CREATE TABLE mlm.commissions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
node_id UUID NOT NULL REFERENCES mlm.nodes(id) ON DELETE CASCADE, -- Who receives
|
|
source_node_id UUID NOT NULL REFERENCES mlm.nodes(id) ON DELETE CASCADE, -- Who generated
|
|
|
|
-- Commission type
|
|
type mlm.commission_type NOT NULL,
|
|
|
|
-- Level difference (1 = direct, 2 = second level, etc.)
|
|
level INTEGER NOT NULL,
|
|
|
|
-- Amounts
|
|
source_amount DECIMAL(15,2) NOT NULL, -- Original sale/volume amount
|
|
rate_applied DECIMAL(10,4) NOT NULL, -- Rate used for calculation
|
|
commission_amount DECIMAL(15,2) NOT NULL, -- Final commission
|
|
currency VARCHAR(3) DEFAULT 'USD',
|
|
|
|
-- Reference to period and source
|
|
period_id UUID REFERENCES commissions.periods(id) ON DELETE SET NULL,
|
|
source_reference VARCHAR(200), -- Reference to sale/transaction
|
|
|
|
-- Status
|
|
status mlm.commission_status NOT NULL DEFAULT 'pending',
|
|
paid_at TIMESTAMPTZ,
|
|
|
|
notes TEXT,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE mlm.commissions IS 'MLM commission entries from downline activity';
|
|
COMMENT ON COLUMN mlm.commissions.level IS 'Level depth from source to beneficiary';
|
|
|
|
-- ─────────────────────────────────────────────
|
|
-- bonuses - MLM bonus entries
|
|
-- ─────────────────────────────────────────────
|
|
CREATE TABLE mlm.bonuses (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
node_id UUID NOT NULL REFERENCES mlm.nodes(id) ON DELETE CASCADE,
|
|
rank_id UUID REFERENCES mlm.ranks(id) ON DELETE SET NULL,
|
|
|
|
type mlm.bonus_type NOT NULL,
|
|
amount DECIMAL(15,2) NOT NULL,
|
|
currency VARCHAR(3) DEFAULT 'USD',
|
|
|
|
-- Reference to period
|
|
period_id UUID REFERENCES commissions.periods(id) ON DELETE SET NULL,
|
|
|
|
-- Status
|
|
status mlm.commission_status NOT NULL DEFAULT 'pending',
|
|
paid_at TIMESTAMPTZ,
|
|
|
|
achieved_at TIMESTAMPTZ DEFAULT NOW(),
|
|
notes TEXT,
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE mlm.bonuses IS 'MLM bonus entries for rank achievements and other bonuses';
|
|
|
|
-- ─────────────────────────────────────────────
|
|
-- rank_history - Historical rank achievements
|
|
-- ─────────────────────────────────────────────
|
|
CREATE TABLE mlm.rank_history (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
node_id UUID NOT NULL REFERENCES mlm.nodes(id) ON DELETE CASCADE,
|
|
rank_id UUID NOT NULL REFERENCES mlm.ranks(id) ON DELETE CASCADE,
|
|
|
|
previous_rank_id UUID REFERENCES mlm.ranks(id) ON DELETE SET NULL,
|
|
|
|
-- Snapshot of metrics at achievement
|
|
personal_volume_at DECIMAL(15,2),
|
|
group_volume_at DECIMAL(15,2),
|
|
direct_referrals_at INTEGER,
|
|
|
|
achieved_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE mlm.rank_history IS 'Historical record of rank achievements';
|