Created
January 20, 2026 09:27
-
-
Save fadhilinsemwa/30540bb9583e4302e3c5f5527783398b to your computer and use it in GitHub Desktop.
TIMS Schema Diff: dev vs staging (2026-01-20)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β TIMS Schema Diff Report β | |
| β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£ | |
| β Source: dev | |
| β Target: staging | |
| β Generated: Tue Jan 20 12:27:20 EAT 2026 | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| FULL SCHEMA DIFF: | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| --- /Users/fadhilinsemwa/Documents/apps/tims/.schema-diff/dev_schema_20260120_122714.sql 2026-01-20 12:27:15 | |
| +++ /Users/fadhilinsemwa/Documents/apps/tims/.schema-diff/staging_schema_20260120_122714.sql 2026-01-20 12:27:20 | |
| @@ -2,7 +2,7 @@ | |
| -- PostgreSQL database dump | |
| -- | |
| -\restrict 56P1AFLmECQP4C14SkiPUrItNfXjy5EQcIa9KjiDIyAuWQdvsFS9t7v15EdHUoA | |
| +\restrict IUFQfJQrjUYMofJwdfyWaeH1zGTKdHYUIj80kUoecb3w9BionIrVhi5plZSbss2 | |
| -- Dumped from database version 15.14 | |
| -- Dumped by pg_dump version 15.14 | |
| @@ -146,6 +146,10 @@ | |
| DO UPDATE SET request_count = integration_rate_limits.request_count + 1 | |
| RETURNING request_count INTO current_count; | |
| + -- Clean old windows (older than 1 hour) | |
| + DELETE FROM integration_rate_limits | |
| + WHERE source_id = p_source_id AND window_start < NOW() - INTERVAL '1 hour'; | |
| + | |
| RETURN current_count <= p_limit_rpm; | |
| END; | |
| $$; | |
| @@ -177,7 +181,8 @@ | |
| BEGIN | |
| year_part := TO_CHAR(NEW.receipt_date, 'YYYY'); | |
| - SELECT COALESCE(MAX(CAST(SUBSTRING(receipt_number FROM 4 FOR 7) AS INT)), 0) + 1 | |
| + -- Extract sequence from position 8 (after RCP + year) | |
| + SELECT COALESCE(MAX(CAST(SUBSTRING(receipt_number FROM 8 FOR 7) AS INT)), 0) + 1 | |
| INTO seq_num | |
| FROM payment_receipts | |
| WHERE receipt_number LIKE 'RCP' || year_part || '%'; | |
| @@ -250,6 +255,41 @@ | |
| AND is_active = true; | |
| RETURN v_next_stage_id; | |
| +END; | |
| +$$; | |
| + | |
| + | |
| +-- | |
| +-- Name: get_or_create_external_mapping(uuid, character varying, character varying, uuid); Type: FUNCTION; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE FUNCTION public.get_or_create_external_mapping(p_source_id uuid, p_entity_type character varying, p_external_id character varying, p_tims_id uuid) RETURNS uuid | |
| + LANGUAGE plpgsql | |
| + AS $$ | |
| +DECLARE | |
| + v_mapping_id UUID; | |
| +BEGIN | |
| + -- Try to find existing mapping | |
| + SELECT id INTO v_mapping_id | |
| + FROM external_id_mappings | |
| + WHERE source_id = p_source_id | |
| + AND entity_type = p_entity_type | |
| + AND external_id = p_external_id; | |
| + | |
| + IF v_mapping_id IS NOT NULL THEN | |
| + -- Update usage stats | |
| + UPDATE external_id_mappings | |
| + SET last_used_at = NOW(), use_count = use_count + 1 | |
| + WHERE id = v_mapping_id; | |
| + RETURN v_mapping_id; | |
| + END IF; | |
| + | |
| + -- Create new mapping | |
| + INSERT INTO external_id_mappings (source_id, entity_type, external_id, tims_id, last_used_at, use_count) | |
| + VALUES (p_source_id, p_entity_type, p_external_id, p_tims_id, NOW(), 1) | |
| + RETURNING id INTO v_mapping_id; | |
| + | |
| + RETURN v_mapping_id; | |
| END; | |
| $$; | |
| @@ -589,6 +629,29 @@ | |
| -- | |
| +-- Name: update_integration_source_stats(); Type: FUNCTION; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE FUNCTION public.update_integration_source_stats() RETURNS trigger | |
| + LANGUAGE plpgsql | |
| + AS $$ | |
| +BEGIN | |
| + IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND OLD.status != NEW.status) THEN | |
| + UPDATE integration_sources | |
| + SET | |
| + webhook_count = webhook_count + CASE WHEN TG_OP = 'INSERT' THEN 1 ELSE 0 END, | |
| + success_count = success_count + CASE WHEN NEW.status = 'completed' THEN 1 ELSE 0 END, | |
| + failure_count = failure_count + CASE WHEN NEW.status IN ('failed', 'rejected') THEN 1 ELSE 0 END, | |
| + last_webhook_at = CASE WHEN TG_OP = 'INSERT' THEN NOW() ELSE last_webhook_at END, | |
| + updated_at = NOW() | |
| + WHERE id = NEW.source_id; | |
| + END IF; | |
| + RETURN NEW; | |
| +END; | |
| +$$; | |
| + | |
| + | |
| +-- | |
| -- Name: update_updated_at_column(); Type: FUNCTION; Schema: public; Owner: - | |
| -- | |
| @@ -778,7 +841,7 @@ | |
| rejection_notified_at timestamp with time zone, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT admission_batches_status_check CHECK (((status)::text = ANY ((ARRAY['draft'::character varying, 'processing'::character varying, 'review'::character varying, 'approved'::character varying, 'published'::character varying, 'cancelled'::character varying])::text[]))) | |
| + CONSTRAINT admission_batches_status_check CHECK (((status)::text = ANY (ARRAY[('draft'::character varying)::text, ('processing'::character varying)::text, ('review'::character varying)::text, ('approved'::character varying)::text, ('published'::character varying)::text, ('cancelled'::character varying)::text]))) | |
| ); | |
| @@ -861,7 +924,8 @@ | |
| late_fee_amount numeric(10,2) DEFAULT 0, | |
| tcu_submit_start date, | |
| tcu_submit_end date, | |
| - is_visible boolean DEFAULT true | |
| + is_visible boolean DEFAULT true, | |
| + description text | |
| ); | |
| @@ -982,8 +1046,8 @@ | |
| expires_at date, | |
| uploaded_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_documents_document_type_check CHECK (((document_type)::text = ANY ((ARRAY['birth_certificate'::character varying, 'national_id'::character varying, 'passport'::character varying, 'passport_photo'::character varying, 'csee_certificate'::character varying, 'acsee_certificate'::character varying, 'degree_certificate'::character varying, 'transcript'::character varying, 'professional_certificate'::character varying, 'work_letter'::character varying, 'recommendation_letter'::character varying, 'research_proposal'::character varying, 'cv_resume'::character varying, 'payslip'::character varying, 'sponsor_letter'::character varying, 'medical_report'::character varying, 'disability_certificate'::character varying, 'other'::character varying])::text[]))), | |
| - CONSTRAINT applicant_documents_verification_status_check CHECK (((verification_status)::text = ANY ((ARRAY['pending'::character varying, 'verified'::character varying, 'rejected'::character varying, 'resubmit'::character varying])::text[]))) | |
| + CONSTRAINT applicant_documents_document_type_check CHECK (((document_type)::text = ANY (ARRAY[('birth_certificate'::character varying)::text, ('national_id'::character varying)::text, ('passport'::character varying)::text, ('passport_photo'::character varying)::text, ('csee_certificate'::character varying)::text, ('acsee_certificate'::character varying)::text, ('degree_certificate'::character varying)::text, ('transcript'::character varying)::text, ('professional_certificate'::character varying)::text, ('work_letter'::character varying)::text, ('recommendation_letter'::character varying)::text, ('research_proposal'::character varying)::text, ('cv_resume'::character varying)::text, ('payslip'::character varying)::text, ('sponsor_letter'::character varying)::text, ('medical_report'::character varying)::text, ('disability_certificate'::character varying)::text, ('other'::character varying)::text]))), | |
| + CONSTRAINT applicant_documents_verification_status_check CHECK (((verification_status)::text = ANY (ARRAY[('pending'::character varying)::text, ('verified'::character varying)::text, ('rejected'::character varying)::text, ('resubmit'::character varying)::text]))) | |
| ); | |
| @@ -1014,7 +1078,7 @@ | |
| is_reseat boolean DEFAULT false, | |
| original_sitting_id uuid, | |
| reseat_reason text, | |
| - CONSTRAINT applicant_exam_sittings_exam_type_check CHECK (((exam_type)::text = ANY ((ARRAY['CSEE'::character varying, 'ACSEE'::character varying, 'VETA'::character varying, 'NACTE'::character varying, 'FOREIGN'::character varying, 'DEGREE'::character varying, 'MASTERS'::character varying, 'OTHER'::character varying])::text[]))) | |
| + CONSTRAINT applicant_exam_sittings_exam_type_check CHECK (((exam_type)::text = ANY (ARRAY[('CSEE'::character varying)::text, ('ACSEE'::character varying)::text, ('VETA'::character varying)::text, ('NACTE'::character varying)::text, ('FOREIGN'::character varying)::text, ('DEGREE'::character varying)::text, ('MASTERS'::character varying)::text, ('OTHER'::character varying)::text]))) | |
| ); | |
| @@ -1043,7 +1107,7 @@ | |
| ip_address character varying(45), | |
| user_agent text, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_logs_log_type_check CHECK (((log_type)::text = ANY ((ARRAY['tcu_add'::character varying, 'tcu_check'::character varying, 'tcu_submit'::character varying, 'tcu_verify'::character varying, 'nacte_verify'::character varying, 'nacte_fetch'::character varying, 'necta_csee'::character varying, 'necta_acsee'::character varying, 'necta_reseat'::character varying, 'rita_avn'::character varying, 'rita_birth'::character varying, 'sms_sent'::character varying, 'email_sent'::character varying, 'control_number'::character varying, 'payment_callback'::character varying, 'status_change'::character varying, 'selection'::character varying, 'rejection'::character varying, 'transfer_internal'::character varying, 'transfer_external'::character varying, 'document_upload'::character varying, 'document_verify'::character varying, 'login'::character varying, 'logout'::character varying, 'password_reset'::character varying])::text[]))) | |
| + CONSTRAINT applicant_logs_log_type_check CHECK (((log_type)::text = ANY (ARRAY[('tcu_add'::character varying)::text, ('tcu_check'::character varying)::text, ('tcu_submit'::character varying)::text, ('tcu_verify'::character varying)::text, ('nacte_verify'::character varying)::text, ('nacte_fetch'::character varying)::text, ('necta_csee'::character varying)::text, ('necta_acsee'::character varying)::text, ('necta_reseat'::character varying)::text, ('rita_avn'::character varying)::text, ('rita_birth'::character varying)::text, ('sms_sent'::character varying)::text, ('email_sent'::character varying)::text, ('control_number'::character varying)::text, ('payment_callback'::character varying)::text, ('status_change'::character varying)::text, ('selection'::character varying)::text, ('rejection'::character varying)::text, ('transfer_internal'::character varying)::text, ('transfer_external'::character varying)::text, ('document_upload'::character varying)::text, ('document_verify'::character varying)::text, ('login'::character varying)::text, ('logout'::character varying)::text, ('password_reset'::character varying)::text]))) | |
| ); | |
| @@ -1069,7 +1133,7 @@ | |
| is_emergency_contact boolean DEFAULT false, | |
| is_sponsor boolean DEFAULT false, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_next_of_kin_relationship_check CHECK (((relationship)::text = ANY ((ARRAY['father'::character varying, 'mother'::character varying, 'guardian'::character varying, 'spouse'::character varying, 'sibling'::character varying, 'uncle'::character varying, 'aunt'::character varying, 'grandparent'::character varying, 'other'::character varying])::text[]))) | |
| + CONSTRAINT applicant_next_of_kin_relationship_check CHECK (((relationship)::text = ANY (ARRAY[('father'::character varying)::text, ('mother'::character varying)::text, ('guardian'::character varying)::text, ('spouse'::character varying)::text, ('sibling'::character varying)::text, ('uncle'::character varying)::text, ('aunt'::character varying)::text, ('grandparent'::character varying)::text, ('other'::character varying)::text]))) | |
| ); | |
| @@ -1096,7 +1160,7 @@ | |
| is_primary_contact boolean DEFAULT false, | |
| is_sponsor boolean DEFAULT false, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_parents_parent_type_check CHECK (((parent_type)::text = ANY ((ARRAY['father'::character varying, 'mother'::character varying, 'guardian'::character varying])::text[]))) | |
| + CONSTRAINT applicant_parents_parent_type_check CHECK (((parent_type)::text = ANY (ARRAY[('father'::character varying)::text, ('mother'::character varying)::text, ('guardian'::character varying)::text]))) | |
| ); | |
| @@ -1124,8 +1188,8 @@ | |
| gateway_response jsonb, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_payments_payment_type_check CHECK (((payment_type)::text = ANY ((ARRAY['application_fee'::character varying, 'admission_fee'::character varying, 'registration_fee'::character varying])::text[]))), | |
| - CONSTRAINT applicant_payments_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'processing'::character varying, 'completed'::character varying, 'failed'::character varying, 'cancelled'::character varying, 'refunded'::character varying])::text[]))) | |
| + CONSTRAINT applicant_payments_payment_type_check CHECK (((payment_type)::text = ANY (ARRAY[('application_fee'::character varying)::text, ('admission_fee'::character varying)::text, ('registration_fee'::character varying)::text]))), | |
| + CONSTRAINT applicant_payments_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('processing'::character varying)::text, ('completed'::character varying)::text, ('failed'::character varying)::text, ('cancelled'::character varying)::text, ('refunded'::character varying)::text]))) | |
| ); | |
| @@ -1172,7 +1236,7 @@ | |
| verified_by uuid, | |
| verified_at timestamp with time zone, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_referees_referee_type_check CHECK (((referee_type)::text = ANY ((ARRAY['academic'::character varying, 'professional'::character varying, 'character'::character varying, 'employer'::character varying])::text[]))) | |
| + CONSTRAINT applicant_referees_referee_type_check CHECK (((referee_type)::text = ANY (ARRAY[('academic'::character varying)::text, ('professional'::character varying)::text, ('character'::character varying)::text, ('employer'::character varying)::text]))) | |
| ); | |
| @@ -1195,8 +1259,8 @@ | |
| expires_at timestamp with time zone, | |
| used_at timestamp with time zone, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_resubmissions_reason_check CHECK (((reason)::text = ANY ((ARRAY['technical_error'::character varying, 'incomplete_data'::character varying, 'payment_issue'::character varying, 'results_delay'::character varying, 'admin_error'::character varying, 'appeal_approved'::character varying])::text[]))), | |
| - CONSTRAINT applicant_resubmissions_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'approved'::character varying, 'rejected'::character varying, 'used'::character varying])::text[]))) | |
| + CONSTRAINT applicant_resubmissions_reason_check CHECK (((reason)::text = ANY (ARRAY[('technical_error'::character varying)::text, ('incomplete_data'::character varying)::text, ('payment_issue'::character varying)::text, ('results_delay'::character varying)::text, ('admin_error'::character varying)::text, ('appeal_approved'::character varying)::text]))), | |
| + CONSTRAINT applicant_resubmissions_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('approved'::character varying)::text, ('rejected'::character varying)::text, ('used'::character varying)::text]))) | |
| ); | |
| @@ -1272,8 +1336,8 @@ | |
| rejection_reason text, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_transfers_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'documents_submitted'::character varying, 'under_review'::character varying, 'approved'::character varying, 'rejected'::character varying, 'cancelled'::character varying])::text[]))), | |
| - CONSTRAINT applicant_transfers_transfer_type_check CHECK (((transfer_type)::text = ANY ((ARRAY['internal'::character varying, 'external_in'::character varying, 'external_out'::character varying])::text[]))) | |
| + CONSTRAINT applicant_transfers_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('documents_submitted'::character varying)::text, ('under_review'::character varying)::text, ('approved'::character varying)::text, ('rejected'::character varying)::text, ('cancelled'::character varying)::text]))), | |
| + CONSTRAINT applicant_transfers_transfer_type_check CHECK (((transfer_type)::text = ANY (ARRAY[('internal'::character varying)::text, ('external_in'::character varying)::text, ('external_out'::character varying)::text]))) | |
| ); | |
| @@ -1301,7 +1365,7 @@ | |
| verified_by uuid, | |
| verified_at timestamp with time zone, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_upgraders_previous_qualification_check CHECK (((previous_qualification)::text = ANY ((ARRAY['certificate'::character varying, 'diploma'::character varying, 'advanced_diploma'::character varying, 'bachelor'::character varying, 'postgrad_diploma'::character varying])::text[]))) | |
| + CONSTRAINT applicant_upgraders_previous_qualification_check CHECK (((previous_qualification)::text = ANY (ARRAY[('certificate'::character varying)::text, ('diploma'::character varying)::text, ('advanced_diploma'::character varying)::text, ('bachelor'::character varying)::text, ('postgrad_diploma'::character varying)::text]))) | |
| ); | |
| @@ -1330,7 +1394,7 @@ | |
| verification_letter_url text, | |
| is_verified boolean DEFAULT false, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT applicant_work_experience_employment_type_check CHECK (((employment_type)::text = ANY ((ARRAY['full_time'::character varying, 'part_time'::character varying, 'contract'::character varying, 'internship'::character varying, 'volunteer'::character varying])::text[]))) | |
| + CONSTRAINT applicant_work_experience_employment_type_check CHECK (((employment_type)::text = ANY (ARRAY[('full_time'::character varying)::text, ('part_time'::character varying)::text, ('contract'::character varying)::text, ('internship'::character varying)::text, ('volunteer'::character varying)::text]))) | |
| ); | |
| @@ -1573,11 +1637,18 @@ | |
| code character varying(20) NOT NULL, | |
| name character varying(200) NOT NULL, | |
| credits integer NOT NULL, | |
| + year_of_study integer, | |
| + semester integer, | |
| is_elective boolean DEFAULT false, | |
| is_active boolean DEFAULT true, | |
| created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| description text, | |
| - semester_num integer | |
| + semester_num integer DEFAULT 1, | |
| + lecture_hours integer DEFAULT 2, | |
| + practical_hours integer DEFAULT 0, | |
| + course_type character varying(20) DEFAULT 'core'::character varying, | |
| + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| + CONSTRAINT courses_type_check CHECK (((course_type)::text = ANY ((ARRAY['core'::character varying, 'elective'::character varying, 'general'::character varying])::text[]))) | |
| ); | |
| @@ -1637,7 +1708,7 @@ | |
| opened_at timestamp with time zone, | |
| error_message text, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT email_logs_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'sent'::character varying, 'delivered'::character varying, 'bounced'::character varying, 'failed'::character varying])::text[]))) | |
| + CONSTRAINT email_logs_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('sent'::character varying)::text, ('delivered'::character varying)::text, ('bounced'::character varying)::text, ('failed'::character varying)::text]))) | |
| ); | |
| @@ -1657,7 +1728,7 @@ | |
| is_active boolean DEFAULT true, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT email_templates_category_check CHECK (((category)::text = ANY ((ARRAY['admission'::character varying, 'registration'::character varying, 'payment'::character varying, 'notification'::character varying, 'system'::character varying])::text[]))) | |
| + CONSTRAINT email_templates_category_check CHECK (((category)::text = ANY (ARRAY[('admission'::character varying)::text, ('registration'::character varying)::text, ('payment'::character varying)::text, ('notification'::character varying)::text, ('system'::character varying)::text]))) | |
| ); | |
| @@ -1714,8 +1785,8 @@ | |
| verified_at timestamp with time zone, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT exams_exam_type_check CHECK (((exam_type)::text = ANY ((ARRAY['midterm'::character varying, 'final'::character varying, 'quiz'::character varying, 'assignment'::character varying, 'coursework'::character varying, 'practical'::character varying, 'project'::character varying])::text[]))), | |
| - CONSTRAINT exams_status_check CHECK (((status)::text = ANY ((ARRAY['scheduled'::character varying, 'ongoing'::character varying, 'completed'::character varying, 'cancelled'::character varying, 'grading'::character varying])::text[]))) | |
| + CONSTRAINT exams_exam_type_check CHECK (((exam_type)::text = ANY (ARRAY[('midterm'::character varying)::text, ('final'::character varying)::text, ('quiz'::character varying)::text, ('assignment'::character varying)::text, ('coursework'::character varying)::text, ('practical'::character varying)::text, ('project'::character varying)::text]))), | |
| + CONSTRAINT exams_status_check CHECK (((status)::text = ANY (ARRAY[('scheduled'::character varying)::text, ('ongoing'::character varying)::text, ('completed'::character varying)::text, ('cancelled'::character varying)::text, ('grading'::character varying)::text]))) | |
| ); | |
| @@ -1740,7 +1811,7 @@ | |
| health_status character varying(20), | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT external_api_configs_service_name_check CHECK (((service_name)::text = ANY ((ARRAY['tcu'::character varying, 'nacte'::character varying, 'necta'::character varying, 'rita'::character varying, 'gepg'::character varying, 'crdb'::character varying, 'nmb'::character varying, 'sms_gateway'::character varying, 'email_gateway'::character varying])::text[]))) | |
| + CONSTRAINT external_api_configs_service_name_check CHECK (((service_name)::text = ANY (ARRAY[('tcu'::character varying)::text, ('nacte'::character varying)::text, ('necta'::character varying)::text, ('rita'::character varying)::text, ('gepg'::character varying)::text, ('crdb'::character varying)::text, ('nmb'::character varying)::text, ('sms_gateway'::character varying)::text, ('email_gateway'::character varying)::text]))) | |
| ); | |
| @@ -1754,15 +1825,22 @@ | |
| entity_type character varying(50) NOT NULL, | |
| external_id character varying(255) NOT NULL, | |
| tims_id uuid NOT NULL, | |
| - external_metadata jsonb, | |
| - last_used_at timestamp with time zone DEFAULT now(), | |
| - use_count integer DEFAULT 1 NOT NULL, | |
| + external_metadata jsonb DEFAULT '{}'::jsonb, | |
| + last_used_at timestamp with time zone, | |
| + use_count integer DEFAULT 0, | |
| created_at timestamp with time zone DEFAULT now(), | |
| updated_at timestamp with time zone DEFAULT now() | |
| ); | |
| -- | |
| +-- Name: TABLE external_id_mappings; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.external_id_mappings IS 'Cache of external ID to TIMS ID mappings'; | |
| + | |
| + | |
| +-- | |
| -- Name: faculties; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -1874,14 +1952,22 @@ | |
| CREATE TABLE public.fee_structures ( | |
| id uuid DEFAULT gen_random_uuid() NOT NULL, | |
| program_id uuid, | |
| + fee_type_id uuid, | |
| academic_year_id uuid, | |
| - fee_category_id uuid, | |
| - amount numeric(12,2) NOT NULL, | |
| - currency character varying(10) DEFAULT 'TZS'::character varying, | |
| - semester_num integer, | |
| + nta_level character varying(20), | |
| + semester integer, | |
| + amount_local numeric(15,2) NOT NULL, | |
| + amount_international numeric(15,2), | |
| + currency character varying(3) DEFAULT 'TZS'::character varying, | |
| + installments_allowed integer DEFAULT 1, | |
| + first_installment_percent numeric(5,2) DEFAULT 100.00, | |
| + due_days_from_semester_start integer DEFAULT 30, | |
| + late_fee_percent numeric(5,2) DEFAULT 0, | |
| is_active boolean DEFAULT true, | |
| - created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP | |
| + created_by uuid, | |
| + created_at timestamp with time zone DEFAULT now(), | |
| + updated_at timestamp with time zone DEFAULT now(), | |
| + CONSTRAINT fee_structures_semester_check CHECK ((semester = ANY (ARRAY[1, 2]))) | |
| ); | |
| @@ -1889,7 +1975,7 @@ | |
| -- Name: TABLE fee_structures; Type: COMMENT; Schema: public; Owner: - | |
| -- | |
| -COMMENT ON TABLE public.fee_structures IS 'Fee amounts per program, NTA level, and semester'; | |
| +COMMENT ON TABLE public.fee_structures IS 'Fee amounts per program, fee type, NTA level, and semester'; | |
| -- | |
| @@ -2020,9 +2106,9 @@ | |
| description text, | |
| target_entity character varying(50) NOT NULL, | |
| target_action character varying(30) DEFAULT 'upsert'::character varying NOT NULL, | |
| - schema_definition jsonb, | |
| - process_immediately boolean DEFAULT false NOT NULL, | |
| - batch_size integer DEFAULT 100 NOT NULL, | |
| + schema_definition jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| + process_immediately boolean DEFAULT true, | |
| + batch_size integer DEFAULT 100, | |
| is_active boolean DEFAULT true NOT NULL, | |
| created_at timestamp with time zone DEFAULT now(), | |
| updated_at timestamp with time zone DEFAULT now() | |
| @@ -2030,6 +2116,13 @@ | |
| -- | |
| +-- Name: TABLE integration_data_types; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.integration_data_types IS 'Defines the types of data that external systems can send via webhooks. Each data type has a JSON schema for validation.'; | |
| + | |
| + | |
| +-- | |
| -- Name: integration_entity_matchers; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -2038,18 +2131,25 @@ | |
| source_id uuid NOT NULL, | |
| entity_type character varying(50) NOT NULL, | |
| tims_table character varying(50) NOT NULL, | |
| - match_strategy character varying(30) DEFAULT 'field_match'::character varying NOT NULL, | |
| + match_strategy character varying(30) NOT NULL, | |
| source_field character varying(100), | |
| tims_field character varying(100), | |
| - match_config jsonb, | |
| - fallback_strategy character varying(30) DEFAULT 'reject'::character varying NOT NULL, | |
| - priority integer DEFAULT 0 NOT NULL, | |
| + match_config jsonb DEFAULT '{}'::jsonb, | |
| + fallback_strategy character varying(30) DEFAULT 'reject'::character varying, | |
| + priority integer DEFAULT 0, | |
| is_active boolean DEFAULT true NOT NULL, | |
| created_at timestamp with time zone DEFAULT now() | |
| ); | |
| -- | |
| +-- Name: TABLE integration_entity_matchers; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.integration_entity_matchers IS 'Defines how to match external entities (students, courses) to TIMS records. Supports multiple match strategies.'; | |
| + | |
| + | |
| +-- | |
| -- Name: integration_field_mappings; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -2059,16 +2159,23 @@ | |
| source_field character varying(100) NOT NULL, | |
| target_field character varying(100) NOT NULL, | |
| transform_type character varying(30) DEFAULT 'direct'::character varying NOT NULL, | |
| - transform_config jsonb, | |
| + transform_config jsonb DEFAULT '{}'::jsonb, | |
| is_required boolean DEFAULT false NOT NULL, | |
| default_value text, | |
| - validation_rules jsonb, | |
| - sort_order integer DEFAULT 0 NOT NULL, | |
| + validation_rules jsonb DEFAULT '{}'::jsonb, | |
| + sort_order integer DEFAULT 0, | |
| created_at timestamp with time zone DEFAULT now() | |
| ); | |
| -- | |
| +-- Name: TABLE integration_field_mappings; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.integration_field_mappings IS 'Maps external webhook fields to internal TIMS database fields. Supports transformation rules for data conversion.'; | |
| + | |
| + | |
| +-- | |
| -- Name: integration_processing_results; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -2079,21 +2186,28 @@ | |
| record_key character varying(255), | |
| target_entity character varying(50) NOT NULL, | |
| target_id uuid, | |
| - action character varying(30) NOT NULL, | |
| + action character varying(20) NOT NULL, | |
| input_data jsonb, | |
| mapped_data jsonb, | |
| output_data jsonb, | |
| - status character varying(30) NOT NULL, | |
| + status character varying(20) NOT NULL, | |
| error_message text, | |
| error_details jsonb, | |
| match_strategy character varying(30), | |
| match_field character varying(100), | |
| - match_value text, | |
| - processed_at timestamp with time zone DEFAULT now() NOT NULL | |
| + match_value character varying(255), | |
| + processed_at timestamp with time zone DEFAULT now() | |
| ); | |
| -- | |
| +-- Name: TABLE integration_processing_results; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.integration_processing_results IS 'Per-record processing results'; | |
| + | |
| + | |
| +-- | |
| -- Name: integration_rate_limits; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -2101,6 +2215,7 @@ | |
| id uuid DEFAULT gen_random_uuid() NOT NULL, | |
| source_id uuid NOT NULL, | |
| window_start timestamp with time zone NOT NULL, | |
| + window_minutes integer DEFAULT 1 NOT NULL, | |
| request_count integer DEFAULT 0 NOT NULL | |
| ); | |
| @@ -2118,18 +2233,25 @@ | |
| reason character varying(50) NOT NULL, | |
| reason_details text, | |
| record_data jsonb NOT NULL, | |
| - status character varying(30) DEFAULT 'pending'::character varying NOT NULL, | |
| + status character varying(20) DEFAULT 'pending'::character varying NOT NULL, | |
| resolved_by uuid, | |
| resolved_at timestamp with time zone, | |
| - resolution_action character varying(50), | |
| + resolution_action character varying(30), | |
| resolution_notes text, | |
| matched_entity_type character varying(50), | |
| matched_entity_id uuid, | |
| - created_at timestamp with time zone DEFAULT now() NOT NULL | |
| + created_at timestamp with time zone DEFAULT now() | |
| ); | |
| -- | |
| +-- Name: TABLE integration_review_queue; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.integration_review_queue IS 'Records needing manual review'; | |
| + | |
| + | |
| +-- | |
| -- Name: integration_sources; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -2141,13 +2263,13 @@ | |
| base_url character varying(255), | |
| auth_type character varying(30) DEFAULT 'hmac'::character varying NOT NULL, | |
| auth_config jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| - rate_limit_rpm integer DEFAULT 100 NOT NULL, | |
| - rate_limit_burst integer DEFAULT 20 NOT NULL, | |
| + rate_limit_rpm integer DEFAULT 100, | |
| + rate_limit_burst integer DEFAULT 20, | |
| is_active boolean DEFAULT true NOT NULL, | |
| last_webhook_at timestamp with time zone, | |
| - webhook_count integer DEFAULT 0 NOT NULL, | |
| - success_count integer DEFAULT 0 NOT NULL, | |
| - failure_count integer DEFAULT 0 NOT NULL, | |
| + webhook_count integer DEFAULT 0, | |
| + success_count integer DEFAULT 0, | |
| + failure_count integer DEFAULT 0, | |
| created_by uuid, | |
| created_at timestamp with time zone DEFAULT now(), | |
| updated_at timestamp with time zone DEFAULT now() | |
| @@ -2155,6 +2277,13 @@ | |
| -- | |
| +-- Name: TABLE integration_sources; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.integration_sources IS 'Registry of external systems that can send data to TIMS'; | |
| + | |
| + | |
| +-- | |
| -- Name: integration_webhooks; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -2167,27 +2296,34 @@ | |
| request_id character varying(100), | |
| request_payload jsonb NOT NULL, | |
| request_headers jsonb, | |
| - request_signature text, | |
| - client_ip character varying(45), | |
| - status character varying(30) DEFAULT 'received'::character varying NOT NULL, | |
| + request_signature character varying(255), | |
| + client_ip inet, | |
| + status character varying(20) DEFAULT 'received'::character varying NOT NULL, | |
| status_message text, | |
| error_details jsonb, | |
| - received_at timestamp with time zone DEFAULT now() NOT NULL, | |
| + received_at timestamp with time zone DEFAULT now(), | |
| validated_at timestamp with time zone, | |
| processing_started_at timestamp with time zone, | |
| processed_at timestamp with time zone, | |
| - records_received integer DEFAULT 0 NOT NULL, | |
| - records_succeeded integer DEFAULT 0 NOT NULL, | |
| - records_failed integer DEFAULT 0 NOT NULL, | |
| - records_skipped integer DEFAULT 0 NOT NULL, | |
| - retry_count integer DEFAULT 0 NOT NULL, | |
| - max_retries integer DEFAULT 3 NOT NULL, | |
| + records_received integer DEFAULT 0, | |
| + records_succeeded integer DEFAULT 0, | |
| + records_failed integer DEFAULT 0, | |
| + records_skipped integer DEFAULT 0, | |
| + retry_count integer DEFAULT 0, | |
| + max_retries integer DEFAULT 3, | |
| next_retry_at timestamp with time zone, | |
| last_error text | |
| ); | |
| -- | |
| +-- Name: TABLE integration_webhooks; Type: COMMENT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +COMMENT ON TABLE public.integration_webhooks IS 'Audit log of all incoming webhooks'; | |
| + | |
| + | |
| +-- | |
| -- Name: invoice_audit; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -2237,14 +2373,13 @@ | |
| student_id uuid, | |
| academic_year_id uuid, | |
| semester_id uuid, | |
| - invoice_number character varying(50) NOT NULL, | |
| + invoice_no character varying(50) NOT NULL, | |
| total_amount numeric(12,2) NOT NULL, | |
| paid_amount numeric(12,2) DEFAULT 0, | |
| status character varying(20) DEFAULT 'pending'::character varying, | |
| due_date date, | |
| created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - invoice_no character varying(50), | |
| balance numeric(12,2), | |
| currency character varying(10) DEFAULT 'TZS'::character varying, | |
| notes text | |
| @@ -2432,8 +2567,8 @@ | |
| response_at timestamp with time zone, | |
| submitted_by uuid, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT nacte_submissions_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'submitted'::character varying, 'processing'::character varying, 'verified'::character varying, 'rejected'::character varying, 'error'::character varying])::text[]))), | |
| - CONSTRAINT nacte_submissions_submission_type_check CHECK (((submission_type)::text = ANY ((ARRAY['verification'::character varying, 'admission'::character varying, 'enrollment'::character varying])::text[]))) | |
| + CONSTRAINT nacte_submissions_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('submitted'::character varying)::text, ('processing'::character varying)::text, ('verified'::character varying)::text, ('rejected'::character varying)::text, ('error'::character varying)::text]))), | |
| + CONSTRAINT nacte_submissions_submission_type_check CHECK (((submission_type)::text = ANY (ARRAY[('verification'::character varying)::text, ('admission'::character varying)::text, ('enrollment'::character varying)::text]))) | |
| ); | |
| @@ -2480,7 +2615,7 @@ | |
| max_attempts integer DEFAULT 5, | |
| expires_at timestamp with time zone NOT NULL, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT otp_verifications_otp_type_check CHECK (((otp_type)::text = ANY ((ARRAY['phone'::character varying, 'email'::character varying, 'password_reset'::character varying, 'login'::character varying])::text[]))) | |
| + CONSTRAINT otp_verifications_otp_type_check CHECK (((otp_type)::text = ANY (ARRAY[('phone'::character varying)::text, ('email'::character varying)::text, ('password_reset'::character varying)::text, ('login'::character varying)::text]))) | |
| ); | |
| @@ -2808,15 +2943,7 @@ | |
| reference_number character varying(100), | |
| status character varying(20) DEFAULT 'completed'::character varying, | |
| paid_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - recorded_by uuid, | |
| - payment_no character varying(50), | |
| - student_id uuid, | |
| - currency character varying(10) DEFAULT 'TZS'::character varying, | |
| - transaction_id character varying(100), | |
| - received_by uuid, | |
| - notes text, | |
| - created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP | |
| + recorded_by uuid | |
| ); | |
| @@ -2876,15 +3003,12 @@ | |
| department_id uuid, | |
| name character varying(200) NOT NULL, | |
| code character varying(20) NOT NULL, | |
| + degree_type character varying(50) NOT NULL, | |
| + duration_years integer NOT NULL, | |
| + total_credits integer, | |
| is_active boolean DEFAULT true, | |
| created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - level character varying(50), | |
| - duration integer, | |
| - description text, | |
| - degree_type character varying(50), | |
| - duration_years integer, | |
| - total_credits integer | |
| + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP | |
| ); | |
| @@ -2935,8 +3059,8 @@ | |
| approved_at timestamp with time zone, | |
| approval_status character varying(20), | |
| approval_notes text, | |
| - CONSTRAINT result_changes_approval_status_check CHECK (((approval_status)::text = ANY ((ARRAY['pending'::character varying, 'approved'::character varying, 'rejected'::character varying])::text[]))), | |
| - CONSTRAINT result_changes_change_type_check CHECK (((change_type)::text = ANY ((ARRAY['create'::character varying, 'update'::character varying, 'delete'::character varying, 'verify'::character varying, 'publish'::character varying, 'appeal'::character varying, 'correction'::character varying])::text[]))) | |
| + CONSTRAINT result_changes_approval_status_check CHECK (((approval_status)::text = ANY (ARRAY[('pending'::character varying)::text, ('approved'::character varying)::text, ('rejected'::character varying)::text]))), | |
| + CONSTRAINT result_changes_change_type_check CHECK (((change_type)::text = ANY (ARRAY[('create'::character varying)::text, ('update'::character varying)::text, ('delete'::character varying)::text, ('verify'::character varying)::text, ('publish'::character varying)::text, ('appeal'::character varying)::text, ('correction'::character varying)::text]))) | |
| ); | |
| @@ -2977,9 +3101,9 @@ | |
| options jsonb DEFAULT '{}'::jsonb, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT result_compilation_queue_file_type_check CHECK (((file_type)::text = ANY ((ARRAY['csv'::character varying, 'xlsx'::character varying, 'xls'::character varying, 'json'::character varying])::text[]))), | |
| + CONSTRAINT result_compilation_queue_file_type_check CHECK (((file_type)::text = ANY (ARRAY[('csv'::character varying)::text, ('xlsx'::character varying)::text, ('xls'::character varying)::text, ('json'::character varying)::text]))), | |
| CONSTRAINT result_compilation_queue_priority_check CHECK (((priority >= 1) AND (priority <= 10))), | |
| - CONSTRAINT result_compilation_queue_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'validating'::character varying, 'processing'::character varying, 'completed'::character varying, 'failed'::character varying, 'cancelled'::character varying, 'partial'::character varying])::text[]))) | |
| + CONSTRAINT result_compilation_queue_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('validating'::character varying)::text, ('processing'::character varying)::text, ('completed'::character varying)::text, ('failed'::character varying)::text, ('cancelled'::character varying)::text, ('partial'::character varying)::text]))) | |
| ); | |
| @@ -3010,8 +3134,8 @@ | |
| expires_at timestamp with time zone, | |
| archived_at timestamp with time zone, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT result_upload_files_status_check CHECK (((status)::text = ANY ((ARRAY['uploaded'::character varying, 'validated'::character varying, 'processing'::character varying, 'processed'::character varying, 'failed'::character varying, 'archived'::character varying])::text[]))), | |
| - CONSTRAINT result_upload_files_upload_type_check CHECK (((upload_type)::text = ANY ((ARRAY['results'::character varying, 'corrections'::character varying, 'appeals'::character varying, 'template'::character varying])::text[]))) | |
| + CONSTRAINT result_upload_files_status_check CHECK (((status)::text = ANY (ARRAY[('uploaded'::character varying)::text, ('validated'::character varying)::text, ('processing'::character varying)::text, ('processed'::character varying)::text, ('failed'::character varying)::text, ('archived'::character varying)::text]))), | |
| + CONSTRAINT result_upload_files_upload_type_check CHECK (((upload_type)::text = ANY (ARRAY[('results'::character varying)::text, ('corrections'::character varying)::text, ('appeals'::character varying)::text, ('template'::character varying)::text]))) | |
| ); | |
| @@ -3069,20 +3193,11 @@ | |
| color character varying(20), | |
| is_system boolean DEFAULT false, | |
| created_by uuid, | |
| - updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - dashboard_type character varying(30) DEFAULT 'default'::character varying, | |
| - CONSTRAINT roles_dashboard_type_check CHECK (((dashboard_type)::text = ANY ((ARRAY['admin'::character varying, 'registrar'::character varying, 'finance'::character varying, 'student'::character varying, 'teacher'::character varying, 'default'::character varying])::text[]))) | |
| + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP | |
| ); | |
| -- | |
| --- Name: COLUMN roles.dashboard_type; Type: COMMENT; Schema: public; Owner: - | |
| --- | |
| - | |
| -COMMENT ON COLUMN public.roles.dashboard_type IS 'Determines which dashboard UI to show users with this role. Options: admin, registrar, finance, student, teacher, default'; | |
| - | |
| - | |
| --- | |
| -- Name: roles_backup; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -3191,7 +3306,8 @@ | |
| is_current boolean DEFAULT false, | |
| created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| number integer DEFAULT 1, | |
| - updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP | |
| + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| + CONSTRAINT semesters_number_check CHECK (((number >= 1) AND (number <= 3))) | |
| ); | |
| @@ -3233,8 +3349,8 @@ | |
| cost numeric(8,2), | |
| currency character varying(3) DEFAULT 'TZS'::character varying, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT sms_notifications_message_type_check CHECK (((message_type)::text = ANY ((ARRAY['control_number'::character varying, 'payment_confirmation'::character varying, 'selection'::character varying, 'rejection'::character varying, 'admission_letter'::character varying, 'registration_reminder'::character varying, 'otp_verification'::character varying, 'password_reset'::character varying, 'general'::character varying])::text[]))), | |
| - CONSTRAINT sms_notifications_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'sent'::character varying, 'delivered'::character varying, 'failed'::character varying])::text[]))) | |
| + CONSTRAINT sms_notifications_message_type_check CHECK (((message_type)::text = ANY (ARRAY[('control_number'::character varying)::text, ('payment_confirmation'::character varying)::text, ('selection'::character varying)::text, ('rejection'::character varying)::text, ('admission_letter'::character varying)::text, ('registration_reminder'::character varying)::text, ('otp_verification'::character varying)::text, ('password_reset'::character varying)::text, ('general'::character varying)::text]))), | |
| + CONSTRAINT sms_notifications_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('sent'::character varying)::text, ('delivered'::character varying)::text, ('failed'::character varying)::text]))) | |
| ); | |
| @@ -3382,18 +3498,14 @@ | |
| phone character varying(20), | |
| gender character varying(10) NOT NULL, | |
| date_of_birth date NOT NULL, | |
| + nationality character varying(50) DEFAULT 'Tanzanian'::character varying, | |
| program_id uuid, | |
| + admission_year integer, | |
| + current_year integer DEFAULT 1, | |
| status character varying(20) DEFAULT 'active'::character varying, | |
| photo_url text, | |
| created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, | |
| - national_id character varying(50), | |
| - address text, | |
| - city character varying(100), | |
| - country character varying(100) DEFAULT 'Tanzania'::character varying, | |
| - admission_date date DEFAULT CURRENT_DATE, | |
| - graduation_date date, | |
| - current_semester integer DEFAULT 1 | |
| + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP | |
| ); | |
| @@ -3421,8 +3533,8 @@ | |
| submitted_by uuid, | |
| created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, | |
| - CONSTRAINT tcu_submissions_status_check CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'submitted'::character varying, 'processing'::character varying, 'verified'::character varying, 'rejected'::character varying, 'error'::character varying, 'timeout'::character varying])::text[]))), | |
| - CONSTRAINT tcu_submissions_submission_type_check CHECK (((submission_type)::text = ANY ((ARRAY['verification'::character varying, 'admission'::character varying, 'enrollment'::character varying, 'results'::character varying])::text[]))) | |
| + CONSTRAINT tcu_submissions_status_check CHECK (((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('submitted'::character varying)::text, ('processing'::character varying)::text, ('verified'::character varying)::text, ('rejected'::character varying)::text, ('error'::character varying)::text, ('timeout'::character varying)::text]))), | |
| + CONSTRAINT tcu_submissions_submission_type_check CHECK (((submission_type)::text = ANY (ARRAY[('verification'::character varying)::text, ('admission'::character varying)::text, ('enrollment'::character varying)::text, ('results'::character varying)::text]))) | |
| ); | |
| @@ -3571,27 +3683,6 @@ | |
| -- | |
| --- Name: v_payment_summary; Type: VIEW; Schema: public; Owner: - | |
| --- | |
| - | |
| -CREATE VIEW public.v_payment_summary AS | |
| - SELECT s.id AS student_id, | |
| - s.registration_number, | |
| - concat(s.first_name, ' ', COALESCE(((s.middle_name)::text || ' '::text), ''::text), s.last_name) AS student_name, | |
| - p.name AS program_name, | |
| - COALESCE(sum(i.total_amount), (0)::numeric) AS total_invoiced, | |
| - COALESCE(sum(i.paid_amount), (0)::numeric) AS total_paid, | |
| - COALESCE(sum(i.balance), (0)::numeric) AS total_balance, | |
| - count(DISTINCT i.id) AS invoice_count, | |
| - count(DISTINCT pay.id) AS payment_count | |
| - FROM (((public.students s | |
| - LEFT JOIN public.programs p ON ((s.program_id = p.id))) | |
| - LEFT JOIN public.invoices i ON ((s.id = i.student_id))) | |
| - LEFT JOIN public.payments pay ON (((i.id = pay.invoice_id) AND ((pay.status)::text = 'completed'::text)))) | |
| - GROUP BY s.id, s.registration_number, s.first_name, s.middle_name, s.last_name, p.name; | |
| - | |
| - | |
| --- | |
| -- Name: workflow_approvals; Type: TABLE; Schema: public; Owner: - | |
| -- | |
| @@ -4378,8 +4469,16 @@ | |
| ALTER TABLE ONLY public.fee_structures | |
| ADD CONSTRAINT fee_structures_pkey PRIMARY KEY (id); | |
| + | |
| + | |
| +-- | |
| +-- Name: fee_structures fee_structures_program_id_fee_type_id_academic_year_id_nta__key; Type: CONSTRAINT; Schema: public; Owner: - | |
| +-- | |
| +ALTER TABLE ONLY public.fee_structures | |
| + ADD CONSTRAINT fee_structures_program_id_fee_type_id_academic_year_id_nta__key UNIQUE (program_id, fee_type_id, academic_year_id, nta_level, semester); | |
| + | |
| -- | |
| -- Name: fee_types fee_types_code_key; Type: CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| @@ -4474,8 +4573,24 @@ | |
| ALTER TABLE ONLY public.integration_entity_matchers | |
| ADD CONSTRAINT integration_entity_matchers_pkey PRIMARY KEY (id); | |
| + | |
| + | |
| +-- | |
| +-- Name: integration_entity_matchers integration_entity_matchers_source_id_entity_type_priority_key; Type: CONSTRAINT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +ALTER TABLE ONLY public.integration_entity_matchers | |
| + ADD CONSTRAINT integration_entity_matchers_source_id_entity_type_priority_key UNIQUE (source_id, entity_type, priority); | |
| + | |
| + | |
| +-- | |
| +-- Name: integration_field_mappings integration_field_mappings_data_type_id_source_field_key; Type: CONSTRAINT; Schema: public; Owner: - | |
| +-- | |
| +ALTER TABLE ONLY public.integration_field_mappings | |
| + ADD CONSTRAINT integration_field_mappings_data_type_id_source_field_key UNIQUE (data_type_id, source_field); | |
| + | |
| -- | |
| -- Name: integration_field_mappings integration_field_mappings_pkey; Type: CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| @@ -4501,14 +4616,22 @@ | |
| -- | |
| --- Name: integration_rate_limits integration_rate_limits_source_id_window_start_key; Type: CONSTRAINT; Schema: public; Owner: - | |
| +-- Name: integration_rate_limits integration_rate_limits_source_id_window_start_window_minut_key; Type: CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| ALTER TABLE ONLY public.integration_rate_limits | |
| - ADD CONSTRAINT integration_rate_limits_source_id_window_start_key UNIQUE (source_id, window_start); | |
| + ADD CONSTRAINT integration_rate_limits_source_id_window_start_window_minut_key UNIQUE (source_id, window_start, window_minutes); | |
| -- | |
| +-- Name: integration_rate_limits integration_rate_limits_source_window_unique; Type: CONSTRAINT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +ALTER TABLE ONLY public.integration_rate_limits | |
| + ADD CONSTRAINT integration_rate_limits_source_window_unique UNIQUE (source_id, window_start); | |
| + | |
| + | |
| +-- | |
| -- Name: integration_review_queue integration_review_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| @@ -4561,7 +4684,7 @@ | |
| -- | |
| ALTER TABLE ONLY public.invoices | |
| - ADD CONSTRAINT invoices_invoice_number_key UNIQUE (invoice_number); | |
| + ADD CONSTRAINT invoices_invoice_number_key UNIQUE (invoice_no); | |
| -- | |
| @@ -4754,16 +4877,8 @@ | |
| ALTER TABLE ONLY public.payment_transactions | |
| ADD CONSTRAINT payment_transactions_pkey PRIMARY KEY (id); | |
| - | |
| - | |
| --- | |
| --- Name: payment_transactions payment_transactions_receipt_number_key; Type: CONSTRAINT; Schema: public; Owner: - | |
| --- | |
| -ALTER TABLE ONLY public.payment_transactions | |
| - ADD CONSTRAINT payment_transactions_receipt_number_key UNIQUE (receipt_number); | |
| - | |
| -- | |
| -- Name: payment_transactions payment_transactions_transaction_ref_key; Type: CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| @@ -5698,7 +5813,7 @@ | |
| -- Name: idx_compilation_queue_pending; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| -CREATE INDEX idx_compilation_queue_pending ON public.result_compilation_queue USING btree (status, priority, queued_at) WHERE ((status)::text = ANY ((ARRAY['pending'::character varying, 'validating'::character varying])::text[])); | |
| +CREATE INDEX idx_compilation_queue_pending ON public.result_compilation_queue USING btree (status, priority, queued_at) WHERE ((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('validating'::character varying)::text])); | |
| -- | |
| @@ -5783,8 +5898,22 @@ | |
| -- | |
| CREATE INDEX idx_course_registrations_student ON public.course_registrations USING btree (student_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_courses_semester_num; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_courses_semester_num ON public.courses USING btree (semester_num); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_courses_type; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| +CREATE INDEX idx_courses_type ON public.courses USING btree (course_type); | |
| + | |
| -- | |
| -- Name: idx_disbursements_beneficiary; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| @@ -5891,6 +6020,13 @@ | |
| -- | |
| +-- Name: idx_external_id_mappings_tims; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_external_id_mappings_tims ON public.external_id_mappings USING btree (tims_id); | |
| + | |
| + | |
| +-- | |
| -- Name: idx_features_module; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| @@ -5919,24 +6055,24 @@ | |
| -- | |
| --- Name: idx_fee_structures_academic_year; Type: INDEX; Schema: public; Owner: - | |
| +-- Name: idx_fee_structures_fee_type; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| -CREATE INDEX idx_fee_structures_academic_year ON public.fee_structures USING btree (academic_year_id); | |
| +CREATE INDEX idx_fee_structures_fee_type ON public.fee_structures USING btree (fee_type_id); | |
| -- | |
| --- Name: idx_fee_structures_active; Type: INDEX; Schema: public; Owner: - | |
| +-- Name: idx_fee_structures_program; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| -CREATE INDEX idx_fee_structures_active ON public.fee_structures USING btree (is_active); | |
| +CREATE INDEX idx_fee_structures_program ON public.fee_structures USING btree (program_id); | |
| -- | |
| --- Name: idx_fee_structures_program; Type: INDEX; Schema: public; Owner: - | |
| +-- Name: idx_fee_structures_semester; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| -CREATE INDEX idx_fee_structures_program ON public.fee_structures USING btree (program_id); | |
| +CREATE INDEX idx_fee_structures_semester ON public.fee_structures USING btree (semester); | |
| -- | |
| @@ -5972,27 +6108,104 @@ | |
| -- | |
| CREATE INDEX idx_hostel_rooms_status ON public.hostel_rooms USING btree (status); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_data_types_source; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_data_types_source ON public.integration_data_types USING btree (source_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_entity_matchers_source; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_entity_matchers_source ON public.integration_entity_matchers USING btree (source_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_field_mappings_data_type; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_field_mappings_data_type ON public.integration_field_mappings USING btree (data_type_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_processing_results_status; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_processing_results_status ON public.integration_processing_results USING btree (status); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_processing_results_target; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_processing_results_target ON public.integration_processing_results USING btree (target_entity, target_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_processing_results_webhook; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_processing_results_webhook ON public.integration_processing_results USING btree (webhook_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_rate_limits_lookup; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_rate_limits_lookup ON public.integration_rate_limits USING btree (source_id, window_start); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_review_queue_source; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_review_queue_source ON public.integration_review_queue USING btree (source_id); | |
| -- | |
| -- Name: idx_integration_review_queue_status; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| -CREATE INDEX idx_integration_review_queue_status ON public.integration_review_queue USING btree (status); | |
| +CREATE INDEX idx_integration_review_queue_status ON public.integration_review_queue USING btree (status, created_at); | |
| -- | |
| --- Name: idx_integration_webhooks_received; Type: INDEX; Schema: public; Owner: - | |
| +-- Name: idx_integration_sources_active; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| -CREATE INDEX idx_integration_webhooks_received ON public.integration_webhooks USING btree (received_at DESC); | |
| +CREATE INDEX idx_integration_sources_active ON public.integration_sources USING btree (is_active) WHERE (is_active = true); | |
| -- | |
| +-- Name: idx_integration_sources_code; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_sources_code ON public.integration_sources USING btree (code); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_webhooks_request_id; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_webhooks_request_id ON public.integration_webhooks USING btree (source_id, request_id) WHERE (request_id IS NOT NULL); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_integration_webhooks_retry; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_integration_webhooks_retry ON public.integration_webhooks USING btree (status, next_retry_at) WHERE (((status)::text = ANY ((ARRAY['failed'::character varying, 'partial'::character varying])::text[])) AND (retry_count < max_retries)); | |
| + | |
| + | |
| +-- | |
| -- Name: idx_integration_webhooks_source; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| -CREATE INDEX idx_integration_webhooks_source ON public.integration_webhooks USING btree (source_id); | |
| +CREATE INDEX idx_integration_webhooks_source ON public.integration_webhooks USING btree (source_id, received_at DESC); | |
| -- | |
| @@ -6273,15 +6486,8 @@ | |
| -- | |
| CREATE INDEX idx_role_capabilities_role ON public.role_capabilities USING btree (role_id); | |
| - | |
| - | |
| --- | |
| --- Name: idx_roles_dashboard_type; Type: INDEX; Schema: public; Owner: - | |
| --- | |
| -CREATE INDEX idx_roles_dashboard_type ON public.roles USING btree (dashboard_type); | |
| - | |
| -- | |
| -- Name: idx_room_allocations_room; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| @@ -6311,6 +6517,20 @@ | |
| -- | |
| +-- Name: idx_semesters_academic_year; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_semesters_academic_year ON public.semesters USING btree (academic_year_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: idx_semesters_is_current; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE INDEX idx_semesters_is_current ON public.semesters USING btree (is_current) WHERE (is_current = true); | |
| + | |
| + | |
| +-- | |
| -- Name: idx_sessions_token; Type: INDEX; Schema: public; Owner: - | |
| -- | |
| @@ -6742,6 +6962,13 @@ | |
| -- | |
| CREATE INDEX idx_workflow_stages_workflow ON public.workflow_stages USING btree (workflow_id); | |
| + | |
| + | |
| +-- | |
| +-- Name: payment_transactions_receipt_number_unique; Type: INDEX; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE UNIQUE INDEX payment_transactions_receipt_number_unique ON public.payment_transactions USING btree (receipt_number) WHERE ((receipt_number IS NOT NULL) AND ((receipt_number)::text <> ''::text)); | |
| -- | |
| @@ -6836,6 +7063,13 @@ | |
| -- | |
| +-- Name: integration_webhooks trg_update_integration_source_stats; Type: TRIGGER; Schema: public; Owner: - | |
| +-- | |
| + | |
| +CREATE TRIGGER trg_update_integration_source_stats AFTER INSERT OR UPDATE OF status ON public.integration_webhooks FOR EACH ROW EXECUTE FUNCTION public.update_integration_source_stats(); | |
| + | |
| + | |
| +-- | |
| -- Name: workflow_definitions trg_workflow_definitions_updated; Type: TRIGGER; Schema: public; Owner: - | |
| -- | |
| @@ -7614,11 +7848,19 @@ | |
| -- | |
| --- Name: fee_structures fee_structures_fee_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| +-- Name: fee_structures fee_structures_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| ALTER TABLE ONLY public.fee_structures | |
| - ADD CONSTRAINT fee_structures_fee_category_id_fkey FOREIGN KEY (fee_category_id) REFERENCES public.fee_categories(id); | |
| + ADD CONSTRAINT fee_structures_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); | |
| + | |
| + | |
| +-- | |
| +-- Name: fee_structures fee_structures_fee_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +ALTER TABLE ONLY public.fee_structures | |
| + ADD CONSTRAINT fee_structures_fee_type_id_fkey FOREIGN KEY (fee_type_id) REFERENCES public.fee_types(id) ON DELETE CASCADE; | |
| -- | |
| @@ -7626,7 +7868,7 @@ | |
| -- | |
| ALTER TABLE ONLY public.fee_structures | |
| - ADD CONSTRAINT fee_structures_program_id_fkey FOREIGN KEY (program_id) REFERENCES public.programs(id); | |
| + ADD CONSTRAINT fee_structures_program_id_fkey FOREIGN KEY (program_id) REFERENCES public.programs(id) ON DELETE CASCADE; | |
| -- | |
| @@ -7714,10 +7956,18 @@ | |
| -- | |
| ALTER TABLE ONLY public.integration_review_queue | |
| - ADD CONSTRAINT integration_review_queue_processing_result_id_fkey FOREIGN KEY (processing_result_id) REFERENCES public.integration_processing_results(id); | |
| + ADD CONSTRAINT integration_review_queue_processing_result_id_fkey FOREIGN KEY (processing_result_id) REFERENCES public.integration_processing_results(id) ON DELETE CASCADE; | |
| -- | |
| +-- Name: integration_review_queue integration_review_queue_resolved_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +ALTER TABLE ONLY public.integration_review_queue | |
| + ADD CONSTRAINT integration_review_queue_resolved_by_fkey FOREIGN KEY (resolved_by) REFERENCES public.users(id); | |
| + | |
| + | |
| +-- | |
| -- Name: integration_review_queue integration_review_queue_source_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| @@ -7731,6 +7981,14 @@ | |
| ALTER TABLE ONLY public.integration_review_queue | |
| ADD CONSTRAINT integration_review_queue_webhook_id_fkey FOREIGN KEY (webhook_id) REFERENCES public.integration_webhooks(id) ON DELETE CASCADE; | |
| + | |
| + | |
| +-- | |
| +-- Name: integration_sources integration_sources_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| +-- | |
| + | |
| +ALTER TABLE ONLY public.integration_sources | |
| + ADD CONSTRAINT integration_sources_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id); | |
| -- | |
| @@ -8093,28 +8351,12 @@ | |
| ADD CONSTRAINT payments_invoice_id_fkey FOREIGN KEY (invoice_id) REFERENCES public.invoices(id); | |
| --- | |
| --- Name: payments payments_received_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| - | |
| -ALTER TABLE ONLY public.payments | |
| - ADD CONSTRAINT payments_received_by_fkey FOREIGN KEY (received_by) REFERENCES public.users(id); | |
| - | |
| - | |
| --- | |
| -- Name: payments payments_recorded_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| -- | |
| ALTER TABLE ONLY public.payments | |
| ADD CONSTRAINT payments_recorded_by_fkey FOREIGN KEY (recorded_by) REFERENCES public.users(id); | |
| - | |
| - | |
| --- | |
| --- Name: payments payments_student_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | |
| --- | |
| - | |
| -ALTER TABLE ONLY public.payments | |
| - ADD CONSTRAINT payments_student_id_fkey FOREIGN KEY (student_id) REFERENCES public.students(id); | |
| -- | |
| @@ -8881,5 +9123,5 @@ | |
| -- PostgreSQL database dump complete | |
| -- | |
| -\unrestrict 56P1AFLmECQP4C14SkiPUrItNfXjy5EQcIa9KjiDIyAuWQdvsFS9t7v15EdHUoA | |
| +\unrestrict IUFQfJQrjUYMofJwdfyWaeH1zGTKdHYUIj80kUoecb3w9BionIrVhi5plZSbss2 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>TIMS Schema Diff Report</title> | |
| <style> | |
| :root { | |
| --teal: #319795; | |
| --teal-dark: #2C7A7B; | |
| --green: #38A169; | |
| --red: #E53E3E; | |
| --yellow: #D69E2E; | |
| --gray-50: #F7FAFC; | |
| --gray-100: #EDF2F7; | |
| --gray-200: #E2E8F0; | |
| --gray-600: #718096; | |
| --gray-800: #2D3748; | |
| } | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: var(--gray-50); | |
| color: var(--gray-800); | |
| line-height: 1.6; | |
| } | |
| .container { max-width: 1200px; margin: 0 auto; padding: 2rem; } | |
| header { | |
| background: linear-gradient(135deg, var(--teal) 0%, var(--teal-dark) 100%); | |
| color: white; | |
| padding: 2rem; | |
| border-radius: 12px; | |
| margin-bottom: 2rem; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| } | |
| header h1 { font-size: 1.75rem; margin-bottom: 0.5rem; } | |
| header p { opacity: 0.9; font-size: 0.9rem; } | |
| .meta { display: flex; gap: 2rem; margin-top: 1rem; font-size: 0.85rem; opacity: 0.8; } | |
| .status-badge { | |
| display: inline-block; | |
| padding: 0.5rem 1rem; | |
| border-radius: 9999px; | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| margin-top: 1rem; | |
| } | |
| .status-badge.success { background: var(--green); } | |
| .status-badge.warning { background: var(--yellow); color: var(--gray-800); } | |
| .stats-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 1rem; | |
| margin-bottom: 2rem; | |
| } | |
| .stat-card { | |
| background: white; | |
| padding: 1.5rem; | |
| border-radius: 8px; | |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1); | |
| text-align: center; | |
| } | |
| .stat-card .number { font-size: 2rem; font-weight: 700; color: var(--teal); } | |
| .stat-card .label { color: var(--gray-600); font-size: 0.85rem; } | |
| .stat-card.danger .number { color: var(--red); } | |
| .stat-card.warning .number { color: var(--yellow); } | |
| .section { | |
| background: white; | |
| border-radius: 8px; | |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1); | |
| margin-bottom: 1.5rem; | |
| overflow: hidden; | |
| } | |
| .section-header { | |
| background: var(--gray-100); | |
| padding: 1rem 1.5rem; | |
| font-weight: 600; | |
| border-bottom: 1px solid var(--gray-200); | |
| } | |
| .section-content { padding: 1.5rem; } | |
| .table-list { list-style: none; } | |
| .table-list li { | |
| padding: 0.75rem 1rem; | |
| border-bottom: 1px solid var(--gray-100); | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| } | |
| .table-list li:last-child { border-bottom: none; } | |
| .table-list .icon { font-size: 1.25rem; } | |
| .table-list .name { font-family: 'Monaco', 'Menlo', monospace; font-size: 0.9rem; } | |
| .missing { color: var(--red); } | |
| .extra { color: var(--yellow); } | |
| .synced { color: var(--green); } | |
| .diff-view { | |
| background: var(--gray-800); | |
| color: #E2E8F0; | |
| padding: 1rem; | |
| border-radius: 6px; | |
| overflow-x: auto; | |
| font-family: 'Monaco', 'Menlo', monospace; | |
| font-size: 0.8rem; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| } | |
| .diff-view .add { color: #68D391; } | |
| .diff-view .remove { color: #FC8181; } | |
| .diff-view .context { color: #A0AEC0; } | |
| footer { | |
| text-align: center; | |
| padding: 2rem; | |
| color: var(--gray-600); | |
| font-size: 0.85rem; | |
| } | |
| .actions { | |
| display: flex; | |
| gap: 1rem; | |
| margin-top: 1.5rem; | |
| } | |
| .btn { | |
| padding: 0.75rem 1.5rem; | |
| border-radius: 6px; | |
| border: none; | |
| font-weight: 500; | |
| cursor: pointer; | |
| text-decoration: none; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .btn-primary { background: var(--teal); color: white; } | |
| .btn-secondary { background: var(--gray-200); color: var(--gray-800); } | |
| @media print { | |
| .actions { display: none; } | |
| body { background: white; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <h1>ποΈ TIMS Schema Diff Report</h1> | |
| <p>Database schema comparison across environments</p> | |
| <div class="meta"> | |
| <span>π Source: <strong>dev</strong></span> | |
| <span>π― Target: <strong>staging</strong></span> | |
| <span>π Generated: <strong>Tue Jan 20 12:27:20 EAT 2026</strong></span> | |
| </div> | |
| <span class="status-badge success">β Schemas In Sync</span> | |
| </header> | |
| <div class="stats-grid"> | |
| <div class="stat-card"> | |
| <div class="number">125</div> | |
| <div class="label">Tables in dev</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="number">125</div> | |
| <div class="label">Tables in staging</div> | |
| </div> | |
| <div class="stat-card "> | |
| <div class="number">0</div> | |
| <div class="label">Missing in staging</div> | |
| </div> | |
| <div class="stat-card "> | |
| <div class="number">0</div> | |
| <div class="label">Extra in staging</div> | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <div class="section-header">π All Tables Comparison</div> | |
| <div class="section-content"> | |
| <ul class="table-list"> | |
| <li><span class="icon synced">β</span><span class="name">CREATE TABLE public.academic_years</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.admission_batch_logs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.admission_batches</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.admission_letters</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.admission_statistics</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.admission_windows</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.api_configurations</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.api_logs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_accounts</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_choice_points</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_documents</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_exam_sittings</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_logs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_next_of_kin</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_parents</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_payments</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_qualifications</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_referees</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_resubmissions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_status_history</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_tokens</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_transfers</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_upgraders</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicant_work_experience</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.applicants</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.application_categories</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.audit_logs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.campuses</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.capabilities</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.capability_audit</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.control_number_config</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.course_offerings</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.course_prerequisites</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.course_registrations</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.courses</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.departments</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.educational_backgrounds</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.email_logs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.email_templates</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.enrollments</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.exam_sitting_subjects</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.exams</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.external_api_configs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.external_id_mappings</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.faculties</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.features</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.fee_categories</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.fee_sponsors</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.fee_structure_limits</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.fee_structures</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.fee_types</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.gepg_bills</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.grading_schemes</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.hostel_rooms</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.hostels</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_data_types</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_entity_matchers</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_field_mappings</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_processing_results</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_rate_limits</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_review_queue</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_sources</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.integration_webhooks</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.invoice_audit</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.invoice_items</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.invoices</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.loan_beneficiaries</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.loan_disbursements</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.loan_import_batches</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.modules</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.nacte_avn_verifications</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.nacte_programmes</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.nacte_submissions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.notification_logs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.otp_verifications</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_audit</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_audit_log</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_control_numbers</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_providers</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_receipts</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_reconciliations</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_transactions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payment_webhook_logs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.payments</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.permissions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.points_configuration</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.program_choices</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.programs</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.qualification_subjects</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.result_changes</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.result_compilation_queue</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.result_upload_files</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.results</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.role_capabilities</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.role_permissions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.roles</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.roles_backup</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.room_allocations</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.room_applications</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.rooms</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.semester_number_mapping</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.semesters</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.sessions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.sms_notifications</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.sponsor_invoice_items</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.sponsor_invoices</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.sponsored_students</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.student_enrollments</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.student_results</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.students</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.tcu_submissions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.time_slots</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.timetable_entries</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.user_roles</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.users</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_approvals</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_config_audit</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_definitions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_execution_audit</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_instances</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_notifications</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_stage_actions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_stage_requirements</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_stage_transitions</span></li><li><span class="icon synced">β</span><span class="name">CREATE TABLE public.workflow_stages</span></li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="actions"> | |
| <button class="btn btn-primary" onclick="window.print()">π¨οΈ Print Report</button> | |
| <a href="diff_dev_vs_staging_20260120_122714.txt" class="btn btn-secondary">π View Raw Diff</a> | |
| </div> | |
| </div> | |
| <footer> | |
| <p>TIMS - Tandabui Institute Management System</p> | |
| <p>Generated by schema-diff.sh</p> | |
| </footer> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment