-- Human-readable, sequential, unique reference numbers for user-facing entities. -- Format: NXT-{TYPE}-{YY}-{000001}. Generated via BEFORE INSERT trigger so every -- existing and future insert path (there are many, scattered across apps) picks -- one up automatically without needing per-call-site application changes. -- -- job_applications and lead_requests are handled in their own creation -- migrations instead of here — this file used to ALTER those tables -- unconditionally, which broke the whole migration chain because neither -- table was ever actually created by any active (non .skip) migration. -- ============ verifications ============ CREATE SEQUENCE IF NOT EXISTS verification_number_seq; ALTER TABLE verifications ADD COLUMN IF NOT EXISTS reference_number VARCHAR; CREATE OR REPLACE FUNCTION set_verification_reference_number() RETURNS TRIGGER AS $$ BEGIN IF NEW.reference_number IS NULL THEN NEW.reference_number := 'NXT-VER-' || to_char(NOW(), 'YY') || '-' || lpad(nextval('verification_number_seq')::text, 6, '0'); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_verifications_reference_number ON verifications; CREATE TRIGGER trg_verifications_reference_number BEFORE INSERT ON verifications FOR EACH ROW EXECUTE FUNCTION set_verification_reference_number(); -- ============ support_tickets ============ CREATE SEQUENCE IF NOT EXISTS ticket_number_seq; ALTER TABLE support_tickets ADD COLUMN IF NOT EXISTS reference_number VARCHAR; CREATE OR REPLACE FUNCTION set_ticket_reference_number() RETURNS TRIGGER AS $$ BEGIN IF NEW.reference_number IS NULL THEN NEW.reference_number := 'NXT-TCK-' || to_char(NOW(), 'YY') || '-' || lpad(nextval('ticket_number_seq')::text, 6, '0'); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_support_tickets_reference_number ON support_tickets; CREATE TRIGGER trg_support_tickets_reference_number BEFORE INSERT ON support_tickets FOR EACH ROW EXECUTE FUNCTION set_ticket_reference_number(); -- ============ payments ============ CREATE SEQUENCE IF NOT EXISTS payment_number_seq; ALTER TABLE payments ADD COLUMN IF NOT EXISTS reference_number VARCHAR; CREATE OR REPLACE FUNCTION set_payment_reference_number() RETURNS TRIGGER AS $$ BEGIN IF NEW.reference_number IS NULL THEN NEW.reference_number := 'NXT-PAY-' || to_char(NOW(), 'YY') || '-' || lpad(nextval('payment_number_seq')::text, 6, '0'); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_payments_reference_number ON payments; CREATE TRIGGER trg_payments_reference_number BEFORE INSERT ON payments FOR EACH ROW EXECUTE FUNCTION set_payment_reference_number(); -- ============ users ============ CREATE SEQUENCE IF NOT EXISTS user_number_seq; ALTER TABLE users ADD COLUMN IF NOT EXISTS reference_number VARCHAR; CREATE OR REPLACE FUNCTION set_user_reference_number() RETURNS TRIGGER AS $$ BEGIN IF NEW.reference_number IS NULL THEN NEW.reference_number := 'NXT-USR-' || to_char(NOW(), 'YY') || '-' || lpad(nextval('user_number_seq')::text, 6, '0'); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_users_reference_number ON users; CREATE TRIGGER trg_users_reference_number BEFORE INSERT ON users FOR EACH ROW EXECUTE FUNCTION set_user_reference_number(); -- ============ backfill existing rows, oldest first ============ DO $$ DECLARE r RECORD; BEGIN FOR r IN SELECT id, created_at FROM verifications WHERE reference_number IS NULL ORDER BY created_at, id LOOP UPDATE verifications SET reference_number = 'NXT-VER-' || to_char(r.created_at, 'YY') || '-' || lpad(nextval('verification_number_seq')::text, 6, '0') WHERE id = r.id; END LOOP; FOR r IN SELECT id, created_at FROM support_tickets WHERE reference_number IS NULL ORDER BY created_at, id LOOP UPDATE support_tickets SET reference_number = 'NXT-TCK-' || to_char(r.created_at, 'YY') || '-' || lpad(nextval('ticket_number_seq')::text, 6, '0') WHERE id = r.id; END LOOP; FOR r IN SELECT id, created_at FROM payments WHERE reference_number IS NULL ORDER BY created_at, id LOOP UPDATE payments SET reference_number = 'NXT-PAY-' || to_char(r.created_at, 'YY') || '-' || lpad(nextval('payment_number_seq')::text, 6, '0') WHERE id = r.id; END LOOP; FOR r IN SELECT id, created_at FROM users WHERE reference_number IS NULL ORDER BY created_at, id LOOP UPDATE users SET reference_number = 'NXT-USR-' || to_char(r.created_at, 'YY') || '-' || lpad(nextval('user_number_seq')::text, 6, '0') WHERE id = r.id; END LOOP; END $$; -- ============ enforce NOT NULL + UNIQUE now that every row has one ============ ALTER TABLE verifications ALTER COLUMN reference_number SET NOT NULL; ALTER TABLE support_tickets ALTER COLUMN reference_number SET NOT NULL; ALTER TABLE payments ALTER COLUMN reference_number SET NOT NULL; ALTER TABLE users ALTER COLUMN reference_number SET NOT NULL; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'verifications_reference_number_key') THEN ALTER TABLE verifications ADD CONSTRAINT verifications_reference_number_key UNIQUE (reference_number); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'support_tickets_reference_number_key') THEN ALTER TABLE support_tickets ADD CONSTRAINT support_tickets_reference_number_key UNIQUE (reference_number); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'payments_reference_number_key') THEN ALTER TABLE payments ADD CONSTRAINT payments_reference_number_key UNIQUE (reference_number); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_reference_number_key') THEN ALTER TABLE users ADD CONSTRAINT users_reference_number_key UNIQUE (reference_number); END IF; END $$;