-- ============================================================================ -- MIGRATION: Complete Schema Updates for Pricing & Lead Requests -- ============================================================================ BEGIN; -- ============================================================================ -- 1. Update pricing_packages table with new columns -- ============================================================================ ALTER TABLE pricing_packages DROP COLUMN IF EXISTS user_role_profile_id, ADD COLUMN IF NOT EXISTS package_type VARCHAR(50) NOT NULL DEFAULT 'TRACECOIN_BUNDLE', ADD COLUMN IF NOT EXISTS applicable_roles TEXT[] DEFAULT '{}', ADD COLUMN IF NOT EXISTS tracecoins_amount INTEGER DEFAULT 0, ADD COLUMN IF NOT EXISTS valid_from TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS valid_until TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS is_promotional BOOLEAN DEFAULT false; -- Add index for package lookup CREATE INDEX IF NOT EXISTS idx_pricing_packages_type ON pricing_packages(package_type); -- ============================================================================ -- 2. Update payments table with new columns -- ============================================================================ ALTER TABLE payments ADD COLUMN IF NOT EXISTS package_id UUID, ADD COLUMN IF NOT EXISTS razorpay_order_id VARCHAR(255), ADD COLUMN IF NOT EXISTS razorpay_payment_id VARCHAR(255), ADD COLUMN IF NOT EXISTS tracecoins_credited INTEGER DEFAULT 0, ADD COLUMN IF NOT EXISTS verified_at TIMESTAMPTZ; -- Add foreign key if not exists ALTER TABLE payments ADD CONSTRAINT payments_package_id_fkey FOREIGN KEY (package_id) REFERENCES pricing_packages(id) ON DELETE SET NULL; CREATE INDEX IF NOT EXISTS idx_payments_package_id ON payments(package_id); CREATE INDEX IF NOT EXISTS idx_payments_razorpay_order_id ON payments(razorpay_order_id); -- ============================================================================ -- 3. Create lead_requests table -- ============================================================================ CREATE TABLE IF NOT EXISTS lead_requests ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lead_id UUID NOT NULL REFERENCES leads(id) ON DELETE CASCADE, user_role_profile_id UUID NOT NULL REFERENCES user_role_profiles(id) ON DELETE CASCADE, customer_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(50) NOT NULL DEFAULT 'PENDING', tracecoins_reserved INTEGER NOT NULL DEFAULT 25, message TEXT, expires_at TIMESTAMPTZ NOT NULL, accepted_at TIMESTAMPTZ, rejected_at TIMESTAMPTZ, rejected_reason TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_lead_requests_lead_id ON lead_requests(lead_id); CREATE INDEX IF NOT EXISTS idx_lead_requests_user_role_profile_id ON lead_requests(user_role_profile_id); CREATE INDEX IF NOT EXISTS idx_lead_requests_customer_user_id ON lead_requests(customer_user_id); CREATE INDEX IF NOT EXISTS idx_lead_requests_status ON lead_requests(status); CREATE INDEX IF NOT EXISTS idx_lead_requests_expires_at ON lead_requests(expires_at); -- ============================================================================ -- 4. Add display_code to company_profiles -- ============================================================================ ALTER TABLE company_profiles ADD COLUMN IF NOT EXISTS display_code VARCHAR(20) UNIQUE; -- ============================================================================ -- 5. Add display_code to customer_profiles -- ============================================================================ ALTER TABLE customer_profiles ADD COLUMN IF NOT EXISTS display_code VARCHAR(20) UNIQUE; -- ============================================================================ -- 6. Add display_code to job_seeker_profiles -- ============================================================================ ALTER TABLE job_seeker_profiles ADD COLUMN IF NOT EXISTS display_code VARCHAR(20) UNIQUE; -- ============================================================================ -- 7. Add free_requirement_slots and purchased_requirement_slots to customer_profiles -- ============================================================================ ALTER TABLE customer_profiles ADD COLUMN IF NOT EXISTS free_requirement_slots INTEGER DEFAULT 2, ADD COLUMN IF NOT EXISTS purchased_requirement_slots INTEGER DEFAULT 0; -- ============================================================================ -- 8. Update leads table with new columns -- ============================================================================ ALTER TABLE leads ADD COLUMN IF NOT EXISTS customer_user_id UUID REFERENCES users(id), ADD COLUMN IF NOT EXISTS max_acceptances INTEGER DEFAULT 10, ADD COLUMN IF NOT EXISTS current_acceptances INTEGER DEFAULT 0, ADD COLUMN IF NOT EXISTS expires_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS cover_image_url TEXT; -- ============================================================================ -- 9. Add tracecoins_reserved column to lead_requests for proper tracking -- ============================================================================ -- Already added in step 3 -- ============================================================================ -- 10. Create function to auto-generate display codes -- ============================================================================ CREATE OR REPLACE FUNCTION generate_display_code(prefix VARCHAR(10)) RETURNS VARCHAR(20) AS $$ DECLARE new_code VARCHAR(20); seq_num INTEGER; BEGIN -- Get the next sequence number for this prefix SELECT COALESCE( (SELECT MAX(CAST(SUBSTRING(code FROM 4) AS INTEGER)) + 1 FROM ( SELECT display_code as code FROM company_profiles WHERE display_code LIKE prefix || '%' UNION ALL SELECT display_code as code FROM customer_profiles WHERE display_code LIKE prefix || '%' UNION ALL SELECT display_code as code FROM job_seeker_profiles WHERE display_code LIKE prefix || '%' ) all_codes ), 1 ) INTO seq_num; new_code := prefix || LPAD(seq_num::TEXT, 4, '0'); RETURN new_code; END; $$ LANGUAGE plpgsql; -- ============================================================================ -- 11. Create indexes for performance -- ============================================================================ CREATE INDEX IF NOT EXISTS idx_leads_customer_user_id ON leads(customer_user_id); CREATE INDEX IF NOT EXISTS idx_leads_status_expires ON leads(status, expires_at); CREATE INDEX IF NOT EXISTS idx_user_role_profiles_role_key ON user_role_profiles(role_key); -- ============================================================================ -- 12. Create tracecoin ledger entry types enum support -- ============================================================================ -- Add transaction_type values for ledger tracking -- The ledger already has 'type' column, ensure we have proper entries -- ============================================================================ -- 13. Add indexes for verification queries -- ============================================================================ CREATE INDEX IF NOT EXISTS idx_verifications_user_role_profile_id ON verifications(user_role_profile_id); CREATE INDEX IF NOT EXISTS idx_verifications_status ON verifications(status); CREATE INDEX IF NOT EXISTS idx_verification_requests_user_role_profile_id ON verification_requests(user_role_profile_id); -- ============================================================================ -- 14. Add tracecoin refund support to ledger -- ============================================================================ -- Reserve tracecoins for lead request CREATE OR REPLACE FUNCTION reserve_tracecoins_for_lead_request( p_user_id UUID, p_amount INTEGER, p_lead_request_id UUID ) RETURNS BOOLEAN AS $$ DECLARE v_wallet_id UUID; v_balance BIGINT; BEGIN -- Get wallet SELECT id, balance INTO v_wallet_id, v_balance FROM tracecoin_wallets WHERE user_id = p_user_id FOR UPDATE; IF v_wallet_id IS NULL THEN RETURN FALSE; END IF; IF v_balance < p_amount THEN RETURN FALSE; END IF; -- Deduct from balance (reserved, not yet spent) UPDATE tracecoin_wallets SET balance = balance - p_amount, reserved = COALESCE(reserved, 0) + p_amount, updated_at = NOW() WHERE id = v_wallet_id; -- Add ledger entry INSERT INTO tracecoin_ledger (wallet_id, type, amount, balance_after, reference_type, reference_id, reason) VALUES (v_wallet_id, 'RESERVE', -p_amount, v_balance - p_amount, 'LEAD_REQUEST', p_lead_request_id, 'Lead request reservation'); RETURN TRUE; END; $$ LANGUAGE plpgsql; -- Confirm tracecoins (when customer accepts) CREATE OR REPLACE FUNCTION confirm_tracecoins_for_lead( p_user_id UUID, p_amount INTEGER, p_lead_request_id UUID ) RETURNS BOOLEAN AS $$ DECLARE v_wallet_id UUID; v_balance BIGINT; BEGIN -- Get wallet SELECT id, balance, COALESCE(reserved, 0) INTO v_wallet_id, v_balance, v_balance FROM tracecoin_wallets WHERE user_id = p_user_id FOR UPDATE; IF v_wallet_id IS NULL THEN RETURN FALSE; END IF; -- Move from reserved to spent UPDATE tracecoin_wallets SET reserved = reserved - p_amount, updated_at = NOW() WHERE id = v_wallet_id; -- Add ledger entry for confirmation INSERT INTO tracecoin_ledger (wallet_id, type, amount, balance_after, reference_type, reference_id, reason) VALUES (v_wallet_id, 'SPEND', -p_amount, v_balance, 'LEAD_REQUEST', p_lead_request_id, 'Lead request accepted'); RETURN TRUE; END; $$ LANGUAGE plpgsql; -- Release tracecoins (when customer rejects or request expires) CREATE OR REPLACE FUNCTION release_tracecoins_for_lead( p_user_id UUID, p_amount INTEGER, p_lead_request_id UUID ) RETURNS BOOLEAN AS $$ DECLARE v_wallet_id UUID; v_balance BIGINT; BEGIN -- Get wallet SELECT id, balance INTO v_wallet_id, v_balance FROM tracecoin_wallets WHERE user_id = p_user_id FOR UPDATE; IF v_wallet_id IS NULL THEN RETURN FALSE; END IF; -- Return to available balance from reserved UPDATE tracecoin_wallets SET balance = balance + p_amount, reserved = reserved - p_amount, updated_at = NOW() WHERE id = v_wallet_id; -- Add ledger entry for release INSERT INTO tracecoin_ledger (wallet_id, type, amount, balance_after, reference_type, reference_id, reason) VALUES (v_wallet_id, 'RELEASE', p_amount, v_balance + p_amount, 'LEAD_REQUEST', p_lead_request_id, 'Lead request rejected/expired'); RETURN TRUE; END; $$ LANGUAGE plpgsql; -- ============================================================================ -- 15. Update tracecoin_wallets to have reserved column -- ============================================================================ ALTER TABLE tracecoin_wallets ADD COLUMN IF NOT EXISTS reserved BIGINT DEFAULT 0; COMMIT;