| Type | Name | Status | PK | FK | Columns | Index | Script | Diff Script |
|---|---|---|---|---|---|---|---|---|
| Table | residents | Mismatch |
Source Script
Target Script
1
CREATE TABLE "residents" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text, "email" text, "contact_number" text, "permanent_address_id" uuid, "resident_type_id" integer NOT NULL, "occupancy_status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "user_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "is_primary_owner" boolean DEFAULT false, PRIMARY KEY ("id"));
1
CREATE TABLE "residents" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text, "email" text, "contact_number" text, "permanent_address_id" uuid, "resident_type_id" integer NOT NULL, "occupancy_status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "user_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | countries | Match | ||||||
| Table | event_occurrence_status | Match | ||||||
| Table | guest_approvals | Match | ||||||
| Table | community_directory_verification_statuses | Missing in Target |
|
|||||
| Table | visitor_phones | Missing in Target |
|
|||||
| Table | service_provider_ticket_categories | Match | ||||||
| Table | visitors | Mismatch |
Source Script
Target Script
1
CREATE TABLE "visitors" ("id" bigint NOT NULL, "first_name" text NOT NULL, "last_name" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "is_active" boolean DEFAULT true NOT NULL, "visitor_type_id" integer DEFAULT 0 NOT NULL, "image_url" text, PRIMARY KEY ("id"));
1
CREATE TABLE "visitors" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text, "email" text, "visiting_from" text, "contact_number" text NOT NULL, "visitor_type_id" integer NOT NULL, "vehicle_number" text, "identity_type_id" integer, "identity_number" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | poll_votes | Missing in Target |
|
|||||
| Table | __EFMigrationsHistory | Mismatch |
Source Script
Target Script
1
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL);
1
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL, PRIMARY KEY ("migration_id"));
|
|||||
| Table | occupant_types | Match | ||||||
| Table | organizations | Match | ||||||
| Table | community_calenders | Match | ||||||
| Table | buildings | Match | ||||||
| Table | committee_members | Mismatch |
|
|||||
| Table | UserToRoleMapping | Match | ||||||
| Table | ticket_number_sequences | Missing in Target |
|
|||||
| Table | pre_approved_entries | Missing in Target |
|
|||||
| Table | meeting_agenda_items | Mismatch |
Source Script
Target Script
1
CREATE TABLE "meeting_agenda_items" ("id" integer NOT NULL, "meeting_occurrence_id" integer NOT NULL, "item_text" text NOT NULL, "order_no" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "modified_by" uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "meeting_agenda_items" ("id" integer NOT NULL, "occurrence_id" integer NOT NULL, "item_text" text NOT NULL, "order_no" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "modified_by" uuid, PRIMARY KEY ("id"));
|
|||||
| Table | ticket_logs | Missing in Target |
|
|||||
| Table | noc_approvals | Missing in Target |
|
|||||
| Table | vehicles | Match | ||||||
| Table | unit_statuses | Match | ||||||
| Table | states | Match | ||||||
| Table | service_provider_logs | Mismatch |
Source Script
Target Script
1
CREATE TABLE "service_provider_logs" ("id" integer NOT NULL, "service_provider_id" integer NOT NULL, "current_status_id" integer NOT NULL, "entry_time" timestamp without time zone NOT NULL, "exit_time" timestamp without time zone, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id", "entry_time"));
1
CREATE TABLE "service_provider_logs" ("id" integer NOT NULL, "service_provider_id" integer NOT NULL, "visiting_unit_id" integer NOT NULL, "visit_purpose_id" integer NOT NULL, "visiting_from" text NOT NULL, "current_status_id" integer NOT NULL, "entry_time" timestamp without time zone NOT NULL, "exit_time" timestamp without time zone NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
|
|||||
| Table | noc_certificates | Missing in Target |
|
|||||
| Table | sub_categories | Match | ||||||
| Table | delivery_companies | Match | ||||||
| Table | noc_due_snapshots | Missing in Target |
|
|||||
| Table | noc_requests | Missing in Target |
|
|||||
| Table | tickets | Mismatch |
Source Script
Target Script
1
CREATE TABLE "tickets" ("id" uuid NOT NULL, "unit_id" integer NOT NULL, "title" varchar(255) NOT NULL, "description" text NOT NULL, "ticket_category_id" integer NOT NULL, "ticket_priority_id" integer NOT NULL, "ticket_status_id" integer NOT NULL, "ticket_assigned_to" integer, "created_by" uuid NOT NULL, "modified_by" uuid, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "sort_order" integer DEFAULT 0 NOT NULL, "is_on_hold" boolean DEFAULT false NOT NULL, "ticket_for_id" integer DEFAULT 0 NOT NULL, "ticket_number" text DEFAULT ''::text NOT NULL, "is_re_opened" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "tickets" ("id" uuid NOT NULL, "unit_id" integer NOT NULL, "title" varchar(255) NOT NULL, "description" text NOT NULL, "ticket_category_id" integer NOT NULL, "ticket_priority_id" integer NOT NULL, "ticket_status_id" integer NOT NULL, "ticket_assigned_to" integer, "created_by" uuid NOT NULL, "modified_by" uuid, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | unit_vehicle_limits | Match | ||||||
| Table | noc_types | Missing in Target |
|
|||||
| Table | visitor_statuses | Match | ||||||
| Table | service_provider_logs_2022_2023 | Missing in Target |
|
|||||
| Table | banks | Match | ||||||
| Table | visitor_vehicles | Match | ||||||
| Table | occupancy_types | Match | ||||||
| Table | noc_approval_roles | Missing in Target |
|
|||||
| Table | water_tanker_deliveries | Match | ||||||
| Table | units | Match | ||||||
| Table | visitor_approvals | Match | ||||||
| Table | visitor_types | Match | ||||||
| Table | visitor_sp_company_visits | Missing in Target |
|
|||||
| Table | service_provider_verifications | Match | ||||||
| Table | resident_tokens | Match | ||||||
| Table | pre_approved_schedule_rules | Missing in Target |
|
|||||
| Table | visitor_details | Missing in Target |
|
|||||
| Table | visitor_apartments | Missing in Target |
|
|||||
| Table | visitor_emails | Missing in Target |
|
|||||
| Table | floors | Match | ||||||
| Table | gate_types | Match | ||||||
| Table | emergency_contacts | Match | ||||||
| Table | resident_types | Match | ||||||
| Table | visitor_identities | Missing in Target |
|
|||||
| Table | visitor_logs | Mismatch |
Source Script
Target Script
1
CREATE TABLE "visitor_logs" ("id" bigint NOT NULL, "visitor_id" integer NOT NULL, "apartment_id" uuid NOT NULL, "visitor_type_id" integer NOT NULL, "entry_time" timestamp without time zone, "exit_time" timestamp without time zone, "gate_id" integer, "verified_by_guard_id" uuid, "pre_approved_entry_id" integer, "vehicle_number" text, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "visitor_status_id" integer, "modified_by" uuid, "modified_on_utc" timestamp without time zone, PRIMARY KEY ("id"));
1
CREATE TABLE "visitor_logs" ("id" integer NOT NULL, "visitor_id" integer NOT NULL, "visitor_type_id" integer NOT NULL, "visiting_from" text NOT NULL, "entry_time" timestamp without time zone, "exit_time" timestamp without time zone, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "visitor_status_id" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | member_additional_details | Match | ||||||
| Table | push_notifications | Match | ||||||
| Table | multi_unit_visits | Mismatch |
Source Script
Target Script
1
CREATE TABLE "multi_unit_visits" ("id" bigint NOT NULL, "visitor_log_id" integer NOT NULL, "unit_id" integer NOT NULL, "visitor_statuses" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "approval_reason" integer, "approval_source" integer DEFAULT 1, "approved_by_resident_id" integer, "is_manual_approval" boolean DEFAULT false, PRIMARY KEY ("id"));
1
CREATE TABLE "multi_unit_visits" ("id" bigint NOT NULL, "visitor_log_id" integer NOT NULL, "unit_id" integer NOT NULL, "visitor_statuses" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
|
|||||
| Table | meeting_action_items | Mismatch |
Source Script
Target Script
1
CREATE TABLE "meeting_action_items" ("id" integer NOT NULL, "meeting_occurrence_id" integer NOT NULL, "action_description" text NOT NULL, "assigned_to_user_id" uuid, "due_date" timestamp without time zone, "status" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "meeting_action_items" ("id" integer NOT NULL, "occurrence_id" integer NOT NULL, "action_description" text NOT NULL, "assigned_to_user_id" uuid, "due_date" timestamp without time zone, "status" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
|
|||||
| Table | meeting_notes | Mismatch |
Source Script
Target Script
1
CREATE TABLE "meeting_notes" ("id" integer NOT NULL, "meeting_occurrence_id" integer NOT NULL, "note_text" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "meeting_notes" ("id" integer NOT NULL, "occurrence_id" integer NOT NULL, "note_text" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
|
|||||
| Table | event_status | Match | ||||||
| Table | event_types | Match | ||||||
| Table | identity_types | Match | ||||||
| Table | ticket_comment_documents | Missing in Target |
|
|||||
| Table | gates | Match | ||||||
| Table | ticket_comments | Missing in Target |
|
|||||
| Table | users | Mismatch |
Source Script
Target Script
1
CREATE TABLE "users" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) NOT NULL, "password_hash" varchar(255), "is_owner" boolean DEFAULT true NOT NULL, "contact_number" varchar(255), "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "phone_number" varchar(15), PRIMARY KEY ("id"));
1
CREATE TABLE "users" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) NOT NULL, "contact_number" varchar(255), "is_owner" boolean DEFAULT true NOT NULL, "password_hash" varchar(255), "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "phone_number" varchar(15), PRIMARY KEY ("id"));
|
|||||
| Table | event_occurrences | Match | ||||||
| Table | has_default | Missing in Target |
|
|||||
| Table | service_provider_apartments | Missing in Target |
|
|||||
| Table | community_directory_location_types | Missing in Target |
|
|||||
| Table | community_directory_source_types | Missing in Target |
|
|||||
| Table | community_directory_listing_types | Missing in Target |
|
|||||
| Table | community_directory_categories | Missing in Target |
|
|||||
| Table | community_directory_status_types | Missing in Target |
|
|||||
| Table | pins | Match | ||||||
| Table | building_types | Match | ||||||
| Table | facility_bookings | Match | ||||||
| Table | resident_units | Mismatch |
Source Script
Target Script
1
CREATE TABLE "resident_units" ("id" integer DEFAULT nextval('resident_units_id_seq'::regclass) NOT NULL, "unit_id" integer NOT NULL, "resident_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "is_primary_owner" boolean DEFAULT false NOT NULL, "resident_type_id" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "resident_units" ("id" integer NOT NULL, "unit_id" integer NOT NULL, "resident_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | resident_requests | Match | ||||||
| Table | ticket_priorities | Match | ||||||
| Table | cities | Match | ||||||
| Table | companies | Match | ||||||
| Table | delivery_personnel | Match | ||||||
| Table | events | Match | ||||||
| Table | verification_types | Match | ||||||
| Table | apartments | Mismatch |
Source Script
Target Script
1
CREATE TABLE "apartments" ("id" uuid NOT NULL, "name" text NOT NULL, "apartment_type_id" integer NOT NULL, "address_id" uuid, "phone" text, "pan" text, "tan" text, "association_name" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "organization_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "apartments" ("id" uuid NOT NULL, "name" text NOT NULL, "apartment_type_id" integer NOT NULL, "address_id" uuid NOT NULL, "phone" text NOT NULL, "pan" text NOT NULL, "tan" text NOT NULL, "association_name" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
|
|||||
| Table | apartment_requests | Missing in Target |
|
|||||
| Table | ticket_statuses | Match | ||||||
| Table | polls | Missing in Target |
|
|||||
| Table | vehicle_types | Match | ||||||
| Table | UserToPaidModulesMapping | Mismatch |
|
|||||
| Table | ticket_documents | Missing in Target |
|
|||||
| Table | calendars | Match | ||||||
| Table | apartment_types | Match | ||||||
| Table | addresses | Match | ||||||
| Table | service_provider_companies | Missing in Target |
|
|||||
| Table | service_provider_company_categories | Missing in Target |
|
|||||
| Table | service_provider_company_subtypes | Missing in Target |
|
|||||
| Table | service_providers | Mismatch |
Source Script
Target Script
1
CREATE TABLE "service_providers" ("id" integer NOT NULL, "service_provider_type_id" integer NOT NULL, "service_provider_sub_type_id" integer NOT NULL, "visitor_id" bigint, "is_frequent_visitor" boolean DEFAULT false NOT NULL, "is_hireable" boolean DEFAULT false NOT NULL, "is_visible" boolean DEFAULT true NOT NULL, "police_verification_status" boolean DEFAULT false NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "user_id" uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "service_providers" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text NOT NULL, "email" text NOT NULL, "visiting_from" text NOT NULL, "contact_number" text NOT NULL, "permanent_address_id" uuid NOT NULL, "present_address_id" uuid NOT NULL, "service_provider_type_id" integer NOT NULL, "service_provider_sub_type_id" integer NOT NULL, "vehicle_number" text NOT NULL, "identity_type_id" integer NOT NULL, "identity_number" text NOT NULL, "identity_image" bytea, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "is_frequent_visitor" boolean DEFAULT false NOT NULL, "is_hireable" boolean DEFAULT false NOT NULL, "is_visible" boolean DEFAULT false NOT NULL, "policeverification_status" boolean DEFAULT false NOT NULL, "validity_date" timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "pin" text DEFAULT 'A'::bpchar NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | unit_service_providers | Match | ||||||
| Table | ticket_categories | Mismatch |
Source Script
Target Script
1
CREATE TABLE "ticket_categories" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "short_code" varchar(2) DEFAULT ''::character varying NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "ticket_categories" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | possible_visitors | Missing in Target |
|
|||||
| Table | user_roles | Missing in Target |
|
|||||
| Table | community_documents | Missing in Target |
|
|||||
| Table | document_types | Missing in Target |
|
|||||
| Table | user_fcm_tokens | Missing in Target |
|
|||||
| Table | security_guard_apartments | Missing in Target |
|
|||||
| Table | service_provider_users | Missing in Target |
|
|||||
| Table | community_directory_listings | Missing in Target |
|
|||||
| Table | community_directory_contacts | Missing in Target |
|
|||||
| Table | community_directory_recommendations | Missing in Target |
|
|||||
| Table | decisions | Missing in Target |
|
|||||
| Table | poll_types | Missing in Target |
|
|||||
| Table | decision_outcomes | Missing in Target |
|
|||||
| Table | poll_statuses | Missing in Target |
|
|||||
| Table | poll_options | Missing in Target |
|
|||||
| Table | visitor_delivery_company | Missing in Target |
|
|||||
| Table | ticket_workflow | Match | ||||||
| Table | categories | Match | ||||||
| Table | complaint | Match | ||||||
| Table | delivery_company_categories | Match | ||||||
| Table | ticket_fors | Missing in Target |
|
|||||
| Table | notice_categories | Missing in Target |
|
|||||
| Table | notice_priorities | Missing in Target |
|
|||||
| Table | notices | Missing in Target |
|
|||||
| Table | schema_versions | Mismatch |
Source Script
Target Script
1
CREATE TABLE "schema_versions" ("script_name" varchar(255) NOT NULL, "applied_on_utc" timestamp without time zone NOT NULL, "hash" varchar(64));
1
CREATE TABLE "schema_versions" ("script_name" varchar(255) NOT NULL, "applied_on_utc" timestamp without time zone NOT NULL, "hash" varchar(64), PRIMARY KEY ("script_name"));
|
|||||
| Table | service_provider_sub_types | Match | ||||||
| Table | service_provider_types | Mismatch |
Source Script
Target Script
1
CREATE TABLE "service_provider_types" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "is_in_house" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "service_provider_types" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | service_provider_addresses | Missing in Target |
|
|||||
| Table | meeting_occurrences | Missing in Target |
|
|||||
| Table | meeting_statuses | Missing in Target |
|
|||||
| Table | occurrence_participants | Missing in Target |
|
|||||
| Table | roles | Match | ||||||
| Table | portfolios | Match | ||||||
| Table | rsvp_statuses | Missing in Target |
|
|||||
| Table | attendance_statuses | Missing in Target |
|
|||||
| Table | user_device_tokens | Match | ||||||
| Table | visit_types | Match | ||||||
| Table | unit_types | Match | ||||||
| Table | service_provider_logs_2024_2025 | Missing in Target |
|
|||||
| Table | meeting_modes | Missing in Target |
|
|||||
| Table | gate_passes | Missing in Target |
|
|||||
| Table | participant_roles | Missing in Target |
|
|||||
| Table | raise_tickets | Match | ||||||
| Table | resident_request_statuses | Match | ||||||
| Table | service_provider_logs_2023_2024 | Missing in Target |
|
|||||
| Table | community_directory_locations | Missing in Target |
|
|||||
| Table | option_types | Missing in Target |
|
|||||
| Table | poll_decisions | Missing in Target |
|
|||||
| Table | poll_eligibility_types | Missing in Target |
|
|||||
| Table | ticket_service_provider_otps | Match | ||||||
| Table | service_provider_logs_2025_2026 | Missing in Target |
|
|||||
| Table | event_participants | Missing in Source |
|
|||||
| Table | meeting_participants | Missing in Source |
|
|||||
| Table | visitor_unit_logs | Missing in Source |
|
|||||
| Table | visitor_contacts | Missing in Source |
|
|||||
| Function | get_all_unit_names_by_apartment_id | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_unit_names_by_apartment_id(p_apartment_id uuid)
2
RETURNS TABLE(id integer, unit_id integer, unit_name text, building_name text, floor_name text)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
CAST(ROW_NUMBER() OVER (ORDER BY u.id) AS INT) AS id,
9
u.id AS unit_id,
10
u."name" AS unit_name,
11
b."name" AS building_name,
12
f."name" AS floor_name
13
FROM units u
14
INNER JOIN buildings b ON u.building_id = b.id
15
INNER JOIN floors f ON u.floor_id = f.id
16
WHERE u.apartment_id = p_apartment_id
17
AND u.is_deleted = false
18
AND b.is_deleted = false
19
AND f.is_deleted = false;
20
END;
21
$function$
|
|||||
| Function | generate_random_pin | Match | ||||||
| Function | get_active_committee_members_by_apartment | Match | ||||||
| Function | get_current_visitors_last24h | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_current_visitors_last24h(p_apartment_id uuid)
2
RETURNS TABLE(visitor_id integer, first_name text, last_name text, email text, visiting_from text, contact_number text, vehicle_number text, entry_time timestamp without time zone, visitor_type_id integer, identity_type_id integer, identity_number text)
3
LANGUAGE plpgsql
4
STABLE
5
AS $function$
6
BEGIN
7
RETURN QUERY
8
SELECT
9
v.id AS visitor_id,
10
v.first_name,
11
v.last_name,
12
v.email,
13
v.visiting_from,
14
v.contact_number,
15
v.vehicle_number,
16
vl.entry_time,
17
v.visitor_type_id,
18
v.identity_type_id,
19
v.identity_number
20
FROM
21
public.visitors v
22
INNER JOIN public.visitor_logs vl ON vl.visitor_id = v.id
23
WHERE
24
v.apartment_id = p_apartment_id
25
AND vl.exit_time IS NULL
26
AND vl.entry_time >= (NOW() AT TIME ZONE 'UTC') - INTERVAL '24 hours'
27
AND vl.is_deleted = false
28
AND v.is_deleted = false;
29
END;
30
$function$
|
|||||
| Function | get_resident_fcm_tokens_by_visitor_log_id | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_resident_fcm_tokens_by_visitor_log_id(p_visitorlogid integer)
2
RETURNS TABLE(id integer, user_ids uuid[], visitor_id integer, first_name text, last_name text, fcm_tokens text[], device_ids text[])
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
ROW_NUMBER() OVER ()::INT AS id, -- synthetic serial-like column
7
ARRAY_AGG(DISTINCT r.user_id) AS user_ids,
8
v.id AS visitor_id,
9
v.first_name AS first_name,
10
v.last_name AS last_name,
11
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.fcm_token), NULL) AS fcm_tokens,
12
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.device_id), NULL) AS device_ids
13
FROM multi_unit_visits muv
14
INNER JOIN resident_units ru ON muv.unit_id = ru.unit_id
15
INNER JOIN residents r ON ru.resident_id = r.id
16
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
17
INNER JOIN visitors v ON v.id = vl.visitor_id
18
LEFT JOIN user_fcm_tokens uft ON uft.user_id = r.user_id
19
WHERE muv.visitor_log_id = p_visitorlogid
20
AND muv.is_deleted = FALSE
21
AND ru.is_deleted = FALSE
22
AND r.is_deleted = FALSE
23
GROUP BY v.id, v.first_name, v.last_name;
24
$function$
|
|||||
| Function | get_user_ids_by_visitor_log_id | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_user_ids_by_visitor_log_id(p_visitorlogid integer)
2
RETURNS TABLE(id integer, user_ids uuid[], visitor_id integer, first_name text, last_name text)
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
ROW_NUMBER() OVER ()::INT AS id, -- synthetic serial-like column
7
ARRAY_AGG(DISTINCT r.user_id) AS user_ids,
8
v.id AS visitor_id,
9
v.first_name As first_name,
10
v.last_name As last_name
11
FROM multi_unit_visits muv
12
INNER JOIN resident_units ru ON muv.unit_id = ru.unit_id
13
INNER JOIN residents r ON ru.resident_id = r.id
14
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
15
INNER JOIN visitors v ON v.id = vl.visitor_id
16
WHERE muv.visitor_log_id = p_VisitorLogId
17
AND muv.is_deleted = FALSE
18
AND ru.is_deleted = FALSE
19
AND r.is_deleted = FALSE
20
GROUP BY v.id, v.first_name, v.last_name;
21
$function$
|
|||||
| Function | get_visitor_logs | Match | ||||||
| Function | pg_get_tabledef | Match | ||||||
| Function | unit_resident_fcm_tokens | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.unit_resident_fcm_tokens(p_unit_id integer)
2
RETURNS TABLE(resident_id integer, user_id uuid, device_id text, fcm_token text)
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
ru.resident_id,
7
r.user_id,
8
uft.device_id,
9
uft.fcm_token
10
FROM resident_units ru
11
INNER JOIN residents r ON ru.resident_id = r.id
12
LEFT JOIN user_fcm_tokens uft ON uft.user_id = r.user_id
13
WHERE ru.unit_id = p_unit_id
14
AND ru.is_deleted = FALSE
15
AND r.is_deleted = FALSE
16
AND (uft.fcm_token IS NOT NULL OR uft.device_id IS NOT NULL);
17
$function$
|
|||||
| Function | update_visitor_log | Match | ||||||
| Function | update_visitor_approval | Match | ||||||
| Function | upsert_user_fcm_token | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.upsert_user_fcm_token(p_user_id uuid, p_device_id text, p_fcm_token text, p_platform text)
2
RETURNS integer
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_id int4;
7
BEGIN
8
INSERT INTO public.user_fcm_tokens (user_id, device_id, fcm_token, platform, upsert_on_utc)
9
VALUES (p_user_id, p_device_id, p_fcm_token, p_platform, now())
10
ON CONFLICT (user_id, device_id)
11
DO UPDATE SET
12
fcm_token = EXCLUDED.fcm_token,
13
platform = EXCLUDED.platform,
14
upsert_on_utc = now()
15
RETURNING id INTO v_id;
16
17
RETURN v_id;
18
END;
19
$function$
|
|||||
| Function | get_all_committee_members_by_apartment | Match | ||||||
| Function | get_all_roles | Match | ||||||
| Function | get_water_tanker_summary | Match | ||||||
| Function | get_unit_info_by_user_apartment | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_unit_info_by_user_apartment(p_user_id uuid, p_apartment_id uuid)
2
RETURNS TABLE(id integer, unit_id integer, customer_id uuid, unit_name text, user_name text, apartment_id uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
ru.id, -- added resident_units primary key
9
ru.unit_id,
10
u.customer_id,
11
u.name,
12
usr.first_name || ' ' || usr.last_name AS user_name,
13
r.apartment_id
14
FROM resident_units ru
15
INNER JOIN residents r ON ru.resident_id = r.id
16
INNER JOIN units u ON ru.unit_id = u.id
17
INNER JOIN users usr ON r.user_id = usr.id
18
WHERE r.user_id = p_user_id
19
AND r.apartment_id = p_apartment_id;
20
END;
21
$function$
|
|||||
| Function | get_portfolios_by_apartment | Match | ||||||
| Function | get_committee_members_by_apartment | Match | ||||||
| Function | get_units_with_primary_owner_name | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_units_with_primary_owner_name(p_apartment_id uuid)
2
RETURNS TABLE(id integer, customer_id uuid, unit_id integer, unit_name text, owner_first_name text, owner_last_name text)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
ROW_NUMBER() OVER (ORDER BY u.name)::int AS id, -- 1,2,3...
9
u.customer_id AS customer_id,
10
u.id AS unit_id,
11
u.name AS unit_name,
12
r.first_name AS owner_first_name,
13
r.last_name AS owner_last_name
14
FROM public.units u
15
JOIN public.resident_units ru
16
ON ru.unit_id = u.id
17
AND (ru.is_deleted = false OR ru.is_deleted IS NULL)
18
AND ru.is_primary_owner = true
19
JOIN public.residents r
20
ON r.id = ru.resident_id
21
AND (r.is_deleted = false OR r.is_deleted IS NULL)
22
WHERE (u.is_deleted = false OR u.is_deleted IS NULL)
23
AND u.apartment_id = p_apartment_id
24
ORDER BY u.name;
25
END;
26
$function$
|
|||||
| Function | get_visitors_of_unit | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitors_of_unit(p_unit_id integer, p_datetime timestamp without time zone)
2
RETURNS TABLE(id integer, visitor_log_id integer, name text, visiting_from text, checked_in_at timestamp without time zone, checked_out_at timestamp without time zone, status text, status_id integer, visitor_type text, visitor_type_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_visit_date DATE := p_datetime::date;
7
BEGIN
8
RETURN QUERY
9
SELECT
10
ROW_NUMBER() OVER ()::integer AS id, -- Cast ROW_NUMBER() result to integer
11
vl.id AS visitor_log_id,
12
TRIM(v.first_name || ' ' || COALESCE(v.last_name, '')) AS name,
13
vl.visiting_from,
14
vl.entry_time AS checked_in_at,
15
vl.exit_time AS checked_out_at,
16
vs.name::text AS status,
17
vs.id AS status_id,
18
vt.name::text AS visitor_type,
19
vt.id AS visitor_type_id
20
FROM
21
multi_unit_visits muv
22
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
23
INNER JOIN visitors v ON v.id = vl.visitor_id
24
INNER JOIN visitor_statuses vs ON vs.id = muv.visitor_statuses
25
INNER JOIN visitor_types vt ON vt.id = vl.visitor_type_id
26
WHERE
27
muv.unit_id = p_unit_id
28
AND DATE(vl.entry_time) = v_visit_date
29
AND muv.is_deleted = FALSE
30
AND vl.is_deleted = FALSE
31
AND v.is_deleted = FALSE;
32
END;
33
$function$
|
|||||
| Function | get_resident_details_by_user | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_resident_details_by_user(p_company_id uuid, p_user_id uuid)
2
RETURNS TABLE(id integer, resident_id integer, first_name text, last_name text, email text, resident_primary_owner boolean, unit_id integer, unit_name text, customer_id uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
r.id AS id,
9
r.id AS resident_id,
10
r.first_name,
11
r.last_name,
12
r.email,
13
r.is_primary_owner AS resident_primary_owner,
14
ru.unit_id,
15
u.name AS unit_name,
16
u.customer_id
17
FROM residents r
18
LEFT JOIN resident_units ru
19
ON ru.resident_id = r.id
20
AND ru.is_deleted = false
21
LEFT JOIN units u
22
ON u.id = ru.unit_id
23
AND u.apartment_id = p_company_id
24
WHERE r.user_id = p_user_id
25
AND r.is_deleted = false;
26
END;
27
$function$
|
|||||
| Function | get_vehicles_by_unit | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_vehicles_by_unit(p_unit_id integer)
2
RETURNS TABLE(id integer, vehicle_number text, vehicle_type_id integer, unit_id integer, vehicle_rf_id text, vehicle_rf_id_secretcode text, created_on_utc timestamp without time zone, modified_on_utc timestamp without time zone, deleted_on_utc timestamp without time zone, is_deleted boolean, created_by uuid, modified_by uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
v.id, v.vehicle_number, v.vehicle_type_id, v.unit_id,
9
v.vehicle_rf_id, v.vehicle_rf_id_secretcode,
10
v.created_on_utc, v.modified_on_utc, v.deleted_on_utc,
11
v.is_deleted, v.created_by, v.modified_by
12
FROM public.vehicles v
13
WHERE v.is_deleted = false
14
AND v.unit_id = p_unit_id;
15
END;
16
$function$
|
|||||
| Function | set_sequence_for_table | Match | ||||||
| Function | get_user_details | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_user_details(p_user_id uuid, p_apartment_id uuid)
2
RETURNS TABLE(user_id uuid, user_email text, resident_id integer, first_name text, last_name text, contact_number text, is_primary_owner boolean, unit_id integer, unit_name text, building_id integer, building_name text, floor_id integer, floor_name text, area numeric, bhk_type numeric, customer_id uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
u.id AS user_id,
9
u.email::text AS user_email, -- Fix: varchar → text
10
r.id AS resident_id,
11
r.first_name,
12
r.last_name,
13
r.contact_number,
14
r.is_primary_owner,
15
ru.unit_id,
16
un.name AS unit_name,
17
18
b.id AS building_id,
19
b.name AS building_name,
20
21
f.id AS floor_id,
22
f.name AS floor_name,
23
24
un.area,
25
un.bhk_type,
26
un.customer_id
27
FROM residents r
28
JOIN users u
29
ON u.id = r.user_id
30
31
LEFT JOIN resident_units ru
32
ON ru.resident_id = r.id
33
AND ru.is_deleted = false
34
35
LEFT JOIN units un
36
ON un.id = ru.unit_id
37
AND un.is_deleted = false
38
39
LEFT JOIN floors f
40
ON f.id = un.floor_id
41
AND f.is_deleted = false
42
43
LEFT JOIN buildings b
44
ON b.id = f.building_id
45
AND b.is_deleted = false
46
47
WHERE r.is_deleted = false
48
AND r.user_id = p_user_id
49
AND r.apartment_id = p_apartment_id;
50
END;
51
$function$
|
|||||
| Function | get_units_by_user | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_units_by_user(p_company_id uuid, p_user_id uuid)
2
RETURNS TABLE(id integer, resident_unit_id integer, unit_id integer, unit_name text, customer_id uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
ru.id AS id, -- ✅ NEW COLUMN
9
ru.id AS resident_unit_id, -- existing
10
u.id AS unit_id,
11
u.name AS unit_name,
12
u.customer_id
13
FROM residents r
14
INNER JOIN resident_units ru
15
ON ru.resident_id = r.id
16
AND ru.is_deleted = FALSE
17
INNER JOIN units u
18
ON u.id = ru.unit_id
19
INNER JOIN apartments a
20
ON a.id = u.apartment_id
21
WHERE a.id = p_company_id -- ✅ company filter
22
AND r.user_id = p_user_id -- ✅ user filter
23
AND r.is_deleted = FALSE;
24
END;
25
$function$
|
|||||
| Function | get_inhouse_service_providers | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_inhouse_service_providers(p_apartment_id uuid, p_types integer[])
2
RETURNS TABLE(id integer, service_provider_id integer, first_name text, last_name text, service_provider_type_id integer, service_provider_type_name character varying)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
CAST(ROW_NUMBER() OVER () AS int) AS id, -- index
9
sp.id AS service_provider_id, -- actual service provider ID
10
sp.first_name,
11
sp.last_name,
12
sp.service_provider_type_id,
13
spt.name AS service_provider_type_name
14
FROM public.service_providers sp
15
INNER JOIN public.service_provider_types spt
16
ON sp.service_provider_type_id = spt.id
17
WHERE sp.apartment_id = p_apartment_id
18
AND sp.service_provider_type_id = ANY(p_types)
19
AND sp.is_deleted = false
20
AND COALESCE(spt.is_deleted, false) = false;
21
END;
22
$function$
|
|||||
| Function | get_visitor_by_contact_or_email | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitor_by_contact_or_email(p_apartment_id uuid DEFAULT NULL::uuid, p_contact_number text DEFAULT NULL::text, p_email text DEFAULT NULL::text)
2
RETURNS TABLE(id integer, first_name text, last_name text, email text, contact_number text, visitor_type_id integer, vehicle_number text, identity_type_id integer, identity_number text, apartment_id uuid)
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
v.id,
7
v.first_name,
8
v.last_name,
9
v.email,
10
v.contact_number,
11
v.visitor_type_id,
12
v.vehicle_number,
13
v.identity_type_id,
14
v.identity_number,
15
v.apartment_id
16
FROM public.visitors v
17
WHERE v.is_deleted = false
18
AND (p_apartment_id IS NULL OR v.apartment_id = p_apartment_id)
19
AND (
20
(p_contact_number IS NOT NULL AND trim(v.contact_number) = trim(p_contact_number))
21
OR
22
(p_email IS NOT NULL AND lower(trim(v.email)) = lower(trim(p_email)))
23
);
24
$function$
|
|||||
| Function | grant_full_schema_access | Match | ||||||
| Function | get_apartment_visitors_in_timespan | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_apartment_visitors_in_timespan(p_apartment_id uuid, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
2
RETURNS TABLE(id integer, visitor_id integer, visitor_type_id integer, visitor_type text, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_person_type text, visitor_person_sub_type text, visitor_status_id integer, visitor_status text, building_id integer, building_name text, floor_id integer, floor_name text, unit_id integer, unit_name text)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
CAST(ROW_NUMBER() OVER (ORDER BY vl.entry_time, v.id) AS int) AS serial_id,
9
v.id as visitor_id,
10
vt.id AS visitor_type_id,
11
'visitor'::text AS visitor_type,
12
v.first_name::text,
13
v.last_name::text,
14
vl.entry_time,
15
vl.exit_time,
16
vt.name::text AS visitor_person_type,
17
NULL::text AS visitor_person_sub_type,
18
CASE
19
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 3
20
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 4
21
ELSE vl.visitor_status_id
22
END AS visitor_status_id,
23
(
24
CASE
25
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 'Checked In'
26
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 'Checked Out'
27
ELSE COALESCE(vs.name::text, 'Unknown')
28
END
29
)::text AS visitor_status,
30
b.id AS building_id,
31
b.name::text AS building_name,
32
f.id AS floor_id,
33
f.name::text AS floor_name,
34
u.id AS unit_id,
35
u.name::text AS unit_name
36
FROM public.visitor_logs vl
37
JOIN public.visitors v ON vl.visitor_id = v.id
38
JOIN public.visitor_types vt ON v.visitor_type_id = vt.id
39
LEFT JOIN public.visitor_statuses vs ON vl.visitor_status_id = vs.id
40
-- 🔗 Join chain for hierarchy
41
LEFT JOIN public.multi_unit_visits muv ON muv.visitor_log_id = vl.id AND COALESCE(muv.is_deleted, FALSE) = FALSE
42
LEFT JOIN public.units u ON u.id = muv.unit_id AND COALESCE(u.is_deleted, FALSE) = FALSE
43
LEFT JOIN public.floors f ON f.id = u.floor_id AND COALESCE(f.is_deleted, FALSE) = FALSE
44
LEFT JOIN public.buildings b ON b.id = f.building_id AND COALESCE(b.is_deleted, FALSE) = FALSE
45
--LEFT JOIN public.apartments a ON a.id = COALESCE(v.apartment_id, b.apartment_id) AND COALESCE(a.is_deleted, FALSE) = FALSE
46
WHERE
47
vl.entry_time BETWEEN p_start_date AND p_end_date
48
AND COALESCE(vl.is_deleted, FALSE) = FALSE
49
AND COALESCE(v.is_deleted, FALSE) = FALSE
50
ORDER BY vl.entry_time, v.id;
51
END;
52
$function$
|
|||||
| Function | get_unit_related_details_by_user | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_unit_related_details_by_user(p_user_id uuid)
2
RETURNS jsonb
3
LANGUAGE sql
4
AS $function$
5
6
SELECT COALESCE(
7
8
/* 1️⃣ Unit-based JSON (resident users) */
9
(
10
SELECT jsonb_build_object(
11
'id', u.id,
12
'name', u.name,
13
'unit_type_id', u.unit_type_id,
14
'phone_extension', u.phone_extention,
15
'area', u.area,
16
'bhk_type', u.bhk_type,
17
'occupant_type_id', u.occupant_type_id,
18
'occupancy_type_id', u.occupancy_type_id,
19
20
'residents', COALESCE(
21
(
22
SELECT jsonb_agg(
23
DISTINCT jsonb_build_object(
24
'id', r2.id,
25
'first_name', r2.first_name,
26
'last_name', r2.last_name,
27
'email', r2.email,
28
'contact_number', r2.contact_number,
29
'resident_type', rt.name
30
)
31
)
32
FROM resident_units ru2
33
JOIN residents r2
34
ON r2.id = ru2.resident_id
35
AND r2.is_deleted = false
36
JOIN resident_types rt
37
ON rt.id = r2.resident_type_id
38
AND rt.is_deleted = false
39
WHERE ru2.unit_id = u.id
40
AND ru2.is_deleted = false
41
),
42
'[]'::jsonb
43
),
44
45
'vehicles', COALESCE(
46
(
47
SELECT jsonb_agg(
48
DISTINCT jsonb_build_object(
49
'id', v.id,
50
'vehicle_number', v.vehicle_number,
51
'vehicle_type', vt.name
52
)
53
)
54
FROM vehicles v
55
JOIN vehicle_types vt
56
ON vt.id = v.vehicle_type_id
57
AND vt.is_deleted = false
58
WHERE v.unit_id = u.id
59
AND v.is_deleted = false
60
),
61
'[]'::jsonb
62
)
63
)
64
FROM residents r
65
JOIN resident_units ru
66
ON ru.resident_id = r.id
67
AND ru.is_deleted = false
68
JOIN units u
69
ON u.id = ru.unit_id
70
AND u.is_deleted = false
71
WHERE r.user_id = p_user_id
72
AND r.is_deleted = false
73
ORDER BY r.is_primary_owner DESC NULLS LAST
74
LIMIT 1
75
),
76
77
/* 2️⃣ Fallback: user-only JSON (non-resident users) */
78
(
79
SELECT jsonb_build_object(
80
'user_id', u.id,
81
'first_name', u.first_name,
82
'last_name', u.last_name,
83
'email', u.email,
84
'contact_number', u.contact_number
85
)
86
FROM users u
87
WHERE u.id = p_user_id
88
AND u.is_deleted = false
89
)
90
91
);
92
93
$function$
|
|||||
| Function | get_pre_approved_entries_by_unit_date_range | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_pre_approved_entries_by_unit_date_range(p_apartment_id uuid, p_unit_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
2
RETURNS TABLE(id integer, apartment_id uuid, unit_id integer, unit_name text, visitor_id bigint, visitor_name text, possible_visitor_id bigint, possible_visitor_name text, visitor_type_id integer, visitor_type_name text, notes text, is_once boolean, valid_from timestamp without time zone, valid_until timestamp without time zone, pin text, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
pae.id,
7
pae.apartment_id,
8
pae.unit_id,
9
u.name AS unit_name,
10
11
pae.visitor_id,
12
CASE
13
WHEN v.id IS NOT NULL
14
THEN concat_ws(' ', v.first_name, v.last_name)
15
ELSE NULL
16
END AS visitor_name,
17
18
pae.possible_visitor_id,
19
CASE
20
WHEN pv.id IS NOT NULL
21
THEN concat_ws(' ', pv.first_name, pv.last_name)
22
ELSE NULL
23
END AS possible_visitor_name,
24
25
v.visitor_type_id,
26
vt.name AS visitor_type_name,
27
28
pae.notes,
29
pae.is_once,
30
pae.valid_from,
31
pae.valid_until,
32
pae.pin,
33
34
pae.created_by,
35
concat_ws(' ',
36
COALESCE(cu.first_name, null),
37
COALESCE(cu.last_name, null)
38
) AS created_by_name,
39
pae.created_on_utc,
40
41
pae.modified_by,
42
concat_ws(' ',
43
COALESCE(mu.first_name, null),
44
COALESCE(mu.last_name, null)
45
) AS modified_by_name,
46
pae.modified_on_utc
47
FROM pre_approved_entries pae
48
LEFT JOIN units u
49
ON u.id = pae.unit_id
50
AND u.is_deleted = false
51
52
LEFT JOIN visitors v
53
ON v.id = pae.visitor_id
54
AND v.is_deleted = false
55
56
LEFT JOIN possible_visitors pv
57
ON pv.id = pae.possible_visitor_id
58
AND pv.is_deleted = false
59
60
LEFT JOIN visitor_types vt
61
ON vt.id = v.visitor_type_id
62
AND vt.is_deleted = false
63
64
LEFT JOIN users cu
65
ON cu.id = pae.created_by
66
AND cu.is_deleted = false
67
68
LEFT JOIN users mu
69
ON mu.id = pae.modified_by
70
AND mu.is_deleted = false
71
72
WHERE
73
pae.apartment_id = p_apartment_id
74
AND pae.unit_id = p_unit_id
75
AND pae.is_deleted = false
76
77
-- OVERLAPPING DATE LOGIC
78
AND pae.valid_from <= p_end_date
79
AND COALESCE(pae.valid_until, pae.valid_from) >= p_start_date
80
81
ORDER BY
82
pae.valid_from,
83
pae.id;
84
$function$
|
|||||
| Function | get_fcm_tokens_by_roles | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_fcm_tokens_by_roles(p_apartment_id uuid, p_role_id integer)
2
RETURNS TABLE(user_id uuid, device_id text, fcm_token text)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
SELECT DISTINCT ON (uft.user_id, uft.device_id)
7
u.id AS user_id,
8
uft.device_id,
9
uft.fcm_token
10
FROM users u
11
INNER JOIN fdw_common.user_roles ur
12
ON ur.user_id = u.id
13
INNER JOIN user_fcm_tokens uft
14
ON uft.user_id = u.id
15
WHERE ur.role_id = p_role_id
16
AND u.company_id = p_apartment_id
17
AND u.is_deleted = FALSE
18
AND uft.fcm_token IS NOT NULL
19
AND uft.device_id IS NOT NULL;
20
$function$
|
|||||
| Function | checkin_service_provider | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.checkin_service_provider(p_apartment_id uuid, p_pin text, p_created_by uuid, p_entry_time timestamp without time zone)
2
RETURNS integer
3
LANGUAGE plpgsql
4
AS $function$
5
6
DECLARE
7
v_service_provider_id int;
8
v_log_id int;
9
BEGIN
10
-- Step 1: Validate service provider by apartmentId and pin
11
SELECT id INTO v_service_provider_id
12
FROM public.service_providers
13
WHERE apartment_id = p_apartment_id
14
AND pin = p_pin
15
AND is_deleted = false
16
LIMIT 1;
17
18
IF v_service_provider_id IS NULL THEN
19
RAISE EXCEPTION 'Service provider not found for apartment % with pin %', p_apartment_id, p_pin
20
USING ERRCODE = 'no_data_found';
21
END IF;
22
23
-- Step 2: Insert service provider log (CheckedIn)
24
INSERT INTO public.service_provider_logs (
25
service_provider_id,
26
current_status_id,
27
entry_time,
28
created_on_utc,
29
created_by
30
)
31
VALUES (
32
v_service_provider_id,
33
1, -- CheckedIn
34
p_entry_time,
35
p_entry_time,
36
p_created_by
37
)
38
RETURNING id INTO v_log_id;
39
40
RETURN v_log_id;
41
END;
42
$function$
|
|||||
| Function | get_community_home_memory_snapshot | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_community_home_memory_snapshot(p_apartment_id uuid)
2
RETURNS TABLE(announcement jsonb, meeting jsonb, decision jsonb, poll jsonb)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
WITH params AS (
7
SELECT
8
now() AS now_ts,
9
now() - interval '3 months' AS last_3_months
10
)
11
SELECT
12
/* ------------------------------------------------------------------
13
1) Recent Announcement (last 3 months)
14
------------------------------------------------------------------ */
15
(
16
SELECT to_jsonb(a)
17
FROM (
18
SELECT
19
n.id,
20
n.title,
21
n.created_on_utc AS "created_on_utc"
22
FROM notices n
23
CROSS JOIN params p
24
WHERE n.apartment_id = p_apartment_id
25
AND n.is_deleted = false
26
AND n.created_on_utc >= p.last_3_months
27
ORDER BY n.created_on_utc DESC
28
LIMIT 1
29
) a
30
) AS announcement,
31
32
/* ------------------------------------------------------------------
33
2) Upcoming Meeting (nearest future occurrence)
34
------------------------------------------------------------------ */
35
(
36
SELECT to_jsonb(m)
37
FROM (
38
SELECT
39
e.id,
40
e.title,
41
make_timestamp(
42
EXTRACT(YEAR FROM eo.occurrence_date)::int,
43
EXTRACT(MONTH FROM eo.occurrence_date)::int,
44
EXTRACT(DAY FROM eo.occurrence_date)::int,
45
EXTRACT(HOUR FROM eo.start_time)::int,
46
EXTRACT(MINUTE FROM eo.start_time)::int,
47
0
48
) AS "meeting_date"
49
FROM events e
50
JOIN event_occurrences eo
51
ON eo.event_id = e.id
52
CROSS JOIN params p
53
WHERE e.company_id = p_apartment_id
54
AND e.event_type_id IN (2, 3, 4) -- Meeting, Committee Meeting, AGM
55
AND e.is_deleted = false
56
AND eo.is_deleted = false
57
AND eo.occurrence_date >= CURRENT_DATE
58
ORDER BY eo.occurrence_date ASC
59
LIMIT 1
60
) m
61
) AS meeting,
62
63
/* ------------------------------------------------------------------
64
3) Latest Decision (most recent)
65
------------------------------------------------------------------ */
66
(
67
SELECT to_jsonb(d)
68
FROM (
69
SELECT
70
d.id,
71
d.title,
72
d.created_on_utc AS "created_on_utc"
73
FROM decisions d
74
WHERE d.apartment_id = p_apartment_id
75
AND d.is_deleted = false
76
ORDER BY d.created_on_utc DESC
77
LIMIT 1
78
) d
79
) AS decision,
80
81
/* ------------------------------------------------------------------
82
4) Active Poll (currently running)
83
------------------------------------------------------------------ */
84
(
85
SELECT to_jsonb(p)
86
FROM (
87
SELECT
88
p.id,
89
p.title,
90
p.start_date AS "starts_on_utc",
91
p.end_date AS "ends_on_utc"
92
FROM polls p
93
WHERE p.apartment_id = p_apartment_id
94
AND p.is_deleted = false
95
AND p.end_date >= now()
96
ORDER BY p.end_date ASC
97
LIMIT 1
98
) p
99
) AS poll;
100
$function$
|
|||||
| Function | save_visitor_and_approval | Match | ||||||
| Function | take_visitor_action | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.take_visitor_action(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid, p_action_status integer)
2
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, approver_first_name text, approver_last_name text, final_status_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
6
DECLARE
7
local_total_visits INT;
8
local_approved_count INT;
9
local_rejected_count INT;
10
local_new_visitor_status INT;
11
12
v_pending CONSTANT INT := 1;
13
v_partial_approved CONSTANT INT := 2;
14
v_approved CONSTANT INT := 3;
15
v_rejected CONSTANT INT := 7;
16
BEGIN
17
/*
18
* 1. Update unit-level status (approve / reject)
19
*/
20
UPDATE public.multi_unit_visits AS muv
21
SET visitor_statuses = p_action_status,
22
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
23
modified_by = p_resident_user
24
WHERE muv.visitor_log_id = p_visitor_log_id
25
AND muv.unit_id = p_unit_id
26
AND muv.is_deleted = FALSE;
27
28
IF NOT FOUND THEN
29
RETURN;
30
END IF;
31
32
/*
33
* 2. Recalculate unit status counts
34
*/
35
SELECT
36
COUNT(*),
37
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_approved),
38
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_rejected)
39
INTO
40
local_total_visits,
41
local_approved_count,
42
local_rejected_count
43
FROM public.multi_unit_visits AS muv
44
WHERE muv.visitor_log_id = p_visitor_log_id
45
AND muv.is_deleted = FALSE;
46
47
/*
48
* 3. Decide parent visitor_logs status
49
*/
50
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
51
local_new_visitor_status := v_approved; -- 3 Approved
52
ELSIF local_approved_count > 0 THEN
53
local_new_visitor_status := v_partial_approved; -- 2 Partial Approved
54
ELSIF local_rejected_count > 0 THEN
55
local_new_visitor_status := v_rejected; -- 7 Rejected
56
ELSE
57
local_new_visitor_status := v_pending; -- 1 Pending
58
END IF;
59
60
/*
61
* 4. Update visitor_logs parent status
62
*/
63
UPDATE public.visitor_logs AS vl
64
SET
65
visitor_status_id = local_new_visitor_status,
66
entry_time = CASE
67
WHEN local_new_visitor_status = v_approved
68
AND vl.entry_time IS NULL
69
--THEN (NOW() AT TIME ZONE '+05:30')::time
70
THEN (NOW() AT TIME ZONE 'Asia/Kolkata')
71
ELSE vl.entry_time
72
END,
73
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
74
modified_by = p_resident_user
75
WHERE vl.id = p_visitor_log_id;
76
77
/*
78
* 5. Return response with approver details
79
*/
80
RETURN QUERY
81
SELECT
82
p_visitor_log_id,
83
p_unit_id,
84
r.id,
85
r.first_name,
86
r.last_name,
87
local_new_visitor_status
88
FROM public.residents r
89
WHERE r.user_id = p_resident_user
90
AND r.is_deleted = FALSE;
91
END;
92
$function$
|
|||||
| Function | create_pre_approved_entry_generic | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_pre_approved_entry_generic(p_apartment_id uuid, p_unit_id integer, p_visitor_type_id integer, p_visitor_id bigint DEFAULT NULL::bigint, p_delivery_company_id integer DEFAULT NULL::integer, p_possible_first_name text DEFAULT NULL::text, p_possible_last_name text DEFAULT NULL::text, p_possible_phone text DEFAULT NULL::text, p_possible_email text DEFAULT NULL::text, p_possible_vehicle text DEFAULT NULL::text, p_possible_notes text DEFAULT NULL::text, p_valid_from timestamp without time zone DEFAULT NULL::timestamp without time zone, p_valid_to timestamp without time zone DEFAULT NULL::timestamp without time zone, p_is_once boolean DEFAULT true, p_notes text DEFAULT NULL::text, p_created_by uuid DEFAULT NULL::uuid, p_schedule_rule jsonb DEFAULT NULL::jsonb)
2
RETURNS TABLE(pre_approved_entry_id integer, first_name text, last_name text, pin text, valid_from timestamp without time zone, valid_to timestamp without time zone, possible_visitor_id bigint, visitor_id bigint, schedule_days_of_week integer[], schedule_start_time text, schedule_end_time text, schedule_max_entries_per_day integer, schedule_validity_in_months integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_delivery_type_id constant int := 2; -- Delivery from visitor_types table
7
8
v_now timestamp := (NOW() AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Kolkata';
9
v_valid_from timestamp := COALESCE(p_valid_from, NOW());
10
v_valid_to timestamp := p_valid_to;
11
12
v_visitor_id bigint := NULL;
13
v_possible_visitor_id bigint := NULL;
14
15
v_pin text := NULL;
16
v_attempt int := 0;
17
18
v_entry_id int := NULL;
19
20
v_days int[] := NULL;
21
v_start_time time := NULL;
22
v_end_time time := NULL;
23
v_max_entries int := NULL;
24
v_validity_months int := NULL;
25
26
v_first_name text := NULL;
27
v_last_name text := NULL;
28
BEGIN
29
-- =======================
30
-- VALIDATIONS
31
-- =======================
32
IF p_apartment_id IS NULL THEN
33
RAISE EXCEPTION 'ApartmentId is required';
34
END IF;
35
36
IF p_unit_id IS NULL OR p_unit_id <= 0 THEN
37
RAISE EXCEPTION 'UnitId must be > 0';
38
END IF;
39
40
IF p_created_by IS NULL THEN
41
RAISE EXCEPTION 'CreatedBy is required';
42
END IF;
43
44
-- normalize visitor id (EF code treats 0 as null)
45
IF p_visitor_id = 0 THEN
46
p_visitor_id := NULL;
47
END IF;
48
49
v_visitor_id := p_visitor_id;
50
51
-- One-at-a-time per apartment for PIN generation & inserts
52
PERFORM pg_advisory_xact_lock(hashtext(p_apartment_id::text));
53
54
-- =======================
55
-- STEP 1: Resolve VisitorId via phone
56
-- =======================
57
IF v_visitor_id IS NULL AND COALESCE(trim(p_possible_phone), '') <> '' THEN
58
SELECT vp.visitor_id::bigint
59
INTO v_visitor_id
60
FROM public.visitor_phones vp
61
WHERE vp.phone_number = p_possible_phone
62
AND vp.is_active = true
63
ORDER BY vp.id DESC
64
LIMIT 1;
65
END IF;
66
67
-- =======================
68
-- STEP 2: Resolve PossibleVisitor via phone
69
-- =======================
70
IF v_visitor_id IS NULL AND COALESCE(trim(p_possible_phone), '') <> '' THEN
71
SELECT pv.id
72
INTO v_possible_visitor_id
73
FROM public.possible_visitors pv
74
WHERE pv.phone_number = p_possible_phone
75
AND pv.is_deleted = false
76
ORDER BY pv.id DESC
77
LIMIT 1;
78
END IF;
79
80
-- =======================
81
-- STEP 3: Create PossibleVisitor if needed
82
-- =======================
83
IF v_visitor_id IS NULL AND v_possible_visitor_id IS NULL THEN
84
INSERT INTO public.possible_visitors (
85
first_name,
86
last_name,
87
phone_number,
88
email,
89
vehicle_number,
90
notes,
91
created_on_utc,
92
created_by,
93
is_deleted
94
)
95
VALUES (
96
COALESCE(p_possible_first_name, ''),
97
p_possible_last_name,
98
p_possible_phone,
99
p_possible_email,
100
p_possible_vehicle,
101
p_possible_notes,
102
v_now,
103
p_created_by,
104
false
105
)
106
RETURNING id INTO v_possible_visitor_id;
107
END IF;
108
109
-- =======================
110
-- STEP 4: Generate Unique PIN
111
-- Must be unique across:
112
-- 1) pre_approved_entries
113
-- 2) service_provider_apartments
114
-- for the same apartment and overlapping time window
115
-- =======================
116
WHILE v_attempt < 10 LOOP
117
v_attempt := v_attempt + 1;
118
v_pin := (floor(random() * 900000) + 100000)::int::text;
119
120
IF NOT EXISTS (
121
-- Check visitor pre-approved entries
122
SELECT 1
123
FROM public.pre_approved_entries pae
124
WHERE pae.apartment_id = p_apartment_id
125
AND pae.is_deleted = false
126
AND pae.pin = v_pin
127
AND pae.valid_from <= COALESCE(v_valid_to, 'infinity'::timestamp)
128
AND COALESCE(pae.valid_until, 'infinity'::timestamp) >= v_valid_from
129
130
UNION ALL
131
132
-- Check service provider apartment pins
133
SELECT 1
134
FROM public.service_provider_apartments spa
135
WHERE spa.apartment_id = p_apartment_id
136
AND spa.is_deleted = false
137
AND spa.is_active = true
138
AND spa.pin = v_pin
139
AND spa.valid_from <= COALESCE(v_valid_to, 'infinity'::timestamp)
140
AND COALESCE(spa.valid_to, 'infinity'::timestamp) >= v_valid_from
141
) THEN
142
EXIT;
143
END IF;
144
END LOOP;
145
146
IF v_pin IS NULL OR v_attempt >= 10 THEN
147
RAISE EXCEPTION
148
'Unable to generate a unique PIN for apartment % after % attempts',
149
p_apartment_id, v_attempt;
150
END IF;
151
152
-- =======================
153
-- STEP 5: Insert pre_approved_entries
154
-- =======================
155
INSERT INTO public.pre_approved_entries (
156
apartment_id,
157
unit_id,
158
notes,
159
is_once,
160
valid_from,
161
valid_until,
162
created_by,
163
created_on_utc,
164
is_deleted,
165
possible_visitor_id,
166
visitor_id,
167
pin
168
)
169
VALUES (
170
p_apartment_id,
171
p_unit_id,
172
p_notes,
173
COALESCE(p_is_once, true),
174
v_valid_from,
175
v_valid_to,
176
p_created_by,
177
v_now,
178
false,
179
v_possible_visitor_id,
180
v_visitor_id,
181
v_pin
182
)
183
RETURNING id INTO v_entry_id;
184
185
-- =======================
186
-- STEP 6: Delivery company mapping (Delivery = 2)
187
-- Now supports BOTH visitor_id and possible_visitor_id
188
-- =======================
189
IF p_visitor_type_id = v_delivery_type_id
190
AND p_delivery_company_id IS NOT NULL
191
AND (v_visitor_id IS NOT NULL OR v_possible_visitor_id IS NOT NULL) THEN
192
193
INSERT INTO public.visitor_delivery_company (
194
visitor_id,
195
possible_visitor_id,
196
delivery_company_id,
197
is_active,
198
valid_from,
199
valid_to
200
)
201
VALUES (
202
v_visitor_id,
203
v_possible_visitor_id,
204
p_delivery_company_id,
205
true,
206
NOW(),
207
NULL
208
);
209
END IF;
210
211
-- =======================
212
-- STEP 7: Schedule rule insert (optional)
213
-- =======================
214
IF p_schedule_rule IS NOT NULL THEN
215
-- Parse JSON safely
216
-- daysOfWeek (int array)
217
IF (p_schedule_rule ? 'daysOfWeek') THEN
218
SELECT COALESCE(array_agg(value::int), NULL)
219
INTO v_days
220
FROM jsonb_array_elements_text(p_schedule_rule->'daysOfWeek') AS t(value);
221
END IF;
222
223
-- startTime / endTime as time
224
IF (p_schedule_rule ? 'startTime') THEN
225
v_start_time := NULLIF(p_schedule_rule->>'startTime', '')::time;
226
END IF;
227
228
IF (p_schedule_rule ? 'endTime') THEN
229
v_end_time := NULLIF(p_schedule_rule->>'endTime', '')::time;
230
END IF;
231
232
-- maxEntriesPerDay default 1
233
v_max_entries :=
234
COALESCE(NULLIF(p_schedule_rule->>'maxEntriesPerDay', '')::int, 1);
235
236
-- validityInMonths nullable
237
IF (p_schedule_rule ? 'validityInMonths') THEN
238
v_validity_months := NULLIF(p_schedule_rule->>'validityInMonths', '')::int;
239
END IF;
240
241
INSERT INTO public.pre_approved_schedule_rules (
242
pre_approved_entry_id,
243
days_of_week,
244
start_time,
245
end_time,
246
max_entries_per_day,
247
validity_in_months
248
)
249
VALUES (
250
v_entry_id,
251
v_days,
252
v_start_time,
253
v_end_time,
254
v_max_entries,
255
v_validity_months
256
);
257
END IF;
258
259
-- =======================
260
-- STEP 8: Resolve names
261
-- =======================
262
IF v_visitor_id IS NOT NULL THEN
263
SELECT v.first_name, v.last_name
264
INTO v_first_name, v_last_name
265
FROM public.visitors v
266
WHERE v.id = v_visitor_id
267
AND v.is_deleted = false
268
LIMIT 1;
269
ELSIF v_possible_visitor_id IS NOT NULL THEN
270
SELECT pv.first_name, pv.last_name
271
INTO v_first_name, v_last_name
272
FROM public.possible_visitors pv
273
WHERE pv.id = v_possible_visitor_id
274
AND pv.is_deleted = false
275
LIMIT 1;
276
END IF;
277
278
-- =======================
279
-- RETURN
280
-- =======================
281
pre_approved_entry_id := v_entry_id;
282
first_name := v_first_name;
283
last_name := v_last_name;
284
pin := v_pin;
285
valid_from := v_valid_from;
286
valid_to := v_valid_to;
287
possible_visitor_id := v_possible_visitor_id;
288
visitor_id := v_visitor_id;
289
290
schedule_days_of_week := v_days;
291
schedule_start_time := CASE WHEN v_start_time IS NULL THEN NULL ELSE to_char(v_start_time, 'HH24:MI') END;
292
schedule_end_time := CASE WHEN v_end_time IS NULL THEN NULL ELSE to_char(v_end_time, 'HH24:MI') END;
293
schedule_max_entries_per_day := v_max_entries;
294
schedule_validity_in_months := v_validity_months;
295
296
RETURN NEXT;
297
END;
298
$function$
|
|||||
| Function | visitor_bulk_check_out | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.visitor_bulk_check_out(p_visitor_log_ids bigint[])
2
RETURNS SETOF visitor_logs
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
UPDATE public.visitor_logs
8
SET
9
exit_time = (NOW() AT TIME ZONE 'Asia/Kolkata'),
10
visitor_status_id = 5, -- Checked Out
11
created_on_utc = created_on_utc -- no-op, keeps row shape stable
12
WHERE id = ANY(p_visitor_log_ids)
13
RETURNING *;
14
END;
15
$function$
|
|||||
| Function | verify_user_password | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.verify_user_password(p_user_name text, p_password text)
2
RETURNS uuid
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_user_id UUID;
7
BEGIN
8
SELECT id
9
INTO v_user_id
10
FROM users
11
WHERE user_name = p_user_name
12
AND is_deleted = false
13
AND password_hash = crypt(p_password, password_hash);
14
15
RETURN v_user_id;
16
END;
17
$function$
|
|||||
| Function | get_community_memory_snapshot | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_community_memory_snapshot(p_apartment_id uuid)
2
RETURNS TABLE(announcement jsonb, meeting jsonb, decision jsonb, poll jsonb)
3
LANGUAGE plpgsql
4
STABLE
5
AS $function$
6
BEGIN
7
RETURN QUERY
8
SELECT
9
(
10
SELECT to_jsonb(a)
11
FROM notices a
12
WHERE a.apartment_id = p_apartment_id
13
AND a.created_on_utc >= now() - interval '3 months'
14
AND a.is_deleted = false
15
ORDER BY a.created_on_utc DESC
16
LIMIT 1
17
) AS announcement,
18
19
(
20
SELECT to_jsonb(m)
21
FROM meetings m
22
WHERE m.apartment_id = p_apartment_id
23
AND m.meeting_date >= now()
24
AND m.is_cancelled = false
25
ORDER BY m.meeting_date ASC
26
LIMIT 1
27
) AS meeting,
28
29
(
30
SELECT to_jsonb(d)
31
FROM decisions d
32
WHERE d.apartment_id = p_apartment_id
33
AND d.decision_date >= now() - interval '3 months'
34
AND d.is_deleted = false
35
ORDER BY d.decision_date DESC
36
LIMIT 1
37
) AS decision,
38
39
(
40
SELECT to_jsonb(p)
41
FROM polls p
42
WHERE p.apartment_id = p_apartment_id
43
AND p.start_date <= now()
44
AND p.end_date >= now()
45
AND p.is_deleted = false
46
ORDER BY p.end_date ASC
47
LIMIT 1
48
) AS poll;
49
END;
50
$function$
|
|||||
| Function | get_service_provider_app_context | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_service_provider_app_context(p_user_id uuid, p_apartment_id uuid)
2
RETURNS TABLE(service_provider_id integer, role text, category jsonb, verified boolean, onboarded_on timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
sp.id AS service_provider_id,
9
'Service Provider'::text AS role,
10
jsonb_build_object(
11
'id', sp.service_provider_sub_type_id,
12
'name', sps.name
13
) AS category,
14
15
sp.police_verification_status AS verified,
16
sp.created_on_utc AS onboarded_on
17
FROM service_provider_users spu
18
JOIN service_providers sp
19
ON sp.id = spu.service_provider_id
20
AND sp.is_deleted = false
21
JOIN service_provider_sub_types sps
22
ON sps.id = sp.service_provider_sub_type_id
23
JOIN service_provider_apartments spa
24
ON spa.service_provider_id = sp.id
25
AND spa.apartment_id = p_apartment_id
26
AND spa.is_deleted = false
27
AND spa.is_active = true
28
WHERE spu.user_id = p_user_id;
29
END;
30
$function$
|
|||||
| Function | get_facility_manager_app_context | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_facility_manager_app_context(p_user_id uuid, p_apartment_id uuid)
2
RETURNS TABLE(user_id uuid, role text, apartment_id uuid, apartment_name text, assigned_since timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
ur.user_id,
9
r.name::text AS role,
10
a.id AS apartment_id,
11
a.name::text AS apartment_name,
12
13
-- Assigned since = role start date (fallback to created_on)
14
CASE
15
WHEN ur.start_date > '0001-01-01' THEN ur.start_date
16
ELSE ur.created_on_utc
17
END AS assigned_since
18
19
FROM fdw_common.user_roles ur
20
JOIN roles r
21
ON r.id = ur.role_id
22
AND r.id = 11 -- Facility Manager
23
JOIN organizations o
24
ON o.id = ur.organization_id
25
JOIN apartments a
26
ON a.organization_id = o.id
27
AND a.id = p_apartment_id
28
WHERE ur.user_id = p_user_id
29
AND ur.is_deleted = false
30
AND (ur.end_date = '0001-01-01' OR ur.end_date > now());
31
END;
32
$function$
|
|||||
| Function | get_security_guard_app_context | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_security_guard_app_context(p_user_id uuid, p_apartment_id uuid)
2
RETURNS TABLE(user_id uuid, role text, apartment_id uuid, apartment_name text, assigned_gate_ids integer[], active_since timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
ur.user_id,
9
r.name::text AS role,
10
a.id AS apartment_id,
11
a.name AS apartment_name,
12
-- FUTURE: security_guard_gates
13
-- CURRENT: return empty array safely
14
ARRAY[]::int[] AS assigned_gate_ids,
15
sga.valid_from
16
FROM fdw_common.user_roles ur
17
JOIN roles r
18
ON r.id = ur.role_id
19
AND r.name = 'Security Guard'
20
JOIN public.security_guard_apartments sga
21
ON sga.user_id = ur.user_id
22
AND sga.apartment_id = p_apartment_id
23
AND sga.is_active = true
24
AND sga.is_deleted = false
25
JOIN apartments a
26
ON a.id = p_apartment_id
27
WHERE ur.user_id = p_user_id
28
AND ur.is_deleted = false;
29
END;
30
$function$
|
|||||
| Function | postgres_fdw_handler | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.postgres_fdw_handler()
2
RETURNS fdw_handler
3
LANGUAGE c
4
STRICT
5
AS '$libdir/postgres_fdw', $function$postgres_fdw_handler$function$
|
|||||
| Function | postgres_fdw_validator | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.postgres_fdw_validator(text[], oid)
2
RETURNS void
3
LANGUAGE c
4
STRICT
5
AS '$libdir/postgres_fdw', $function$postgres_fdw_validator$function$
|
|||||
| Function | postgres_fdw_disconnect | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.postgres_fdw_disconnect(text)
2
RETURNS boolean
3
LANGUAGE c
4
PARALLEL RESTRICTED STRICT
5
AS '$libdir/postgres_fdw', $function$postgres_fdw_disconnect$function$
|
|||||
| Function | postgres_fdw_disconnect_all | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.postgres_fdw_disconnect_all()
2
RETURNS boolean
3
LANGUAGE c
4
PARALLEL RESTRICTED STRICT
5
AS '$libdir/postgres_fdw', $function$postgres_fdw_disconnect_all$function$
|
|||||
| Function | postgres_fdw_get_connections | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.postgres_fdw_get_connections(check_conn boolean DEFAULT false, OUT server_name text, OUT user_name text, OUT valid boolean, OUT used_in_xact boolean, OUT closed boolean, OUT remote_backend_pid integer)
2
RETURNS SETOF record
3
LANGUAGE c
4
PARALLEL RESTRICTED STRICT
5
AS '$libdir/postgres_fdw', $function$postgres_fdw_get_connections_1_2$function$
|
|||||
| Function | get_service_provider_categories | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_service_provider_categories()
2
RETURNS TABLE(id integer, name text)
3
LANGUAGE sql
4
AS $function$
5
SELECT spc.id, spc.name
6
FROM public.service_provider_company_categories spc;
7
$function$
|
|||||
| Function | get_security_guard_fcm_tokens | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_security_guard_fcm_tokens(p_apartment_id uuid)
2
RETURNS TABLE(user_id uuid, device_id text, fcm_token text)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
7
SELECT DISTINCT ON (uft.user_id, uft.device_id)
8
u.id AS user_id,
9
uft.device_id,
10
uft.fcm_token
11
FROM users u
12
INNER JOIN fdw_common.user_roles ur
13
ON ur.user_id = u.id
14
INNER JOIN user_fcm_tokens uft
15
ON uft.user_id = u.id
16
WHERE ur.role_id = 7
17
AND u.company_id = p_apartment_id
18
AND u.is_deleted = FALSE
19
AND uft.fcm_token IS NOT NULL
20
AND uft.device_id IS NOT NULL;
21
$function$
|
|||||
| Function | get_community_feed | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_community_feed(p_apartment_id uuid, p_unit_id integer, p_resident_id integer)
2
RETURNS TABLE(type text, priority text, occurred_on timestamp without time zone, payload jsonb)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
7
/* ------------------------------------------------------------------
8
📢 Announcements
9
------------------------------------------------------------------ */
10
(
11
SELECT
12
'Announcement',
13
'High',
14
n.created_on_utc,
15
jsonb_build_object(
16
'id', n.id,
17
'title', n.title,
18
'summary', n.short_description,
19
'createdBy', u.first_name || ' ' || u.last_name
20
)
21
FROM notices n
22
JOIN users u ON u.id = n.created_by
23
WHERE n.apartment_id = p_apartment_id
24
AND n.is_deleted = false
25
AND u.is_deleted = false
26
ORDER BY n.created_on_utc DESC
27
LIMIT 1
28
)
29
30
/* ------------------------------------------------------------------
31
🗳 Decisions
32
------------------------------------------------------------------ */
33
UNION ALL
34
(
35
SELECT
36
'Decision',
37
'Medium',
38
d.created_on_utc,
39
jsonb_build_object(
40
'id', d.id,
41
'title', d.title,
42
'description', d.description,
43
'decisionOutcome', deo.name,
44
'createdBy', u.first_name || ' ' || u.last_name
45
)
46
FROM decisions d
47
JOIN decision_outcomes deo ON deo.id = d.decision_outcome_id
48
JOIN users u ON u.id = d.created_by
49
WHERE d.apartment_id = p_apartment_id
50
AND d.is_deleted = false
51
AND u.is_deleted = false
52
ORDER BY d.created_on_utc DESC
53
LIMIT 2
54
)
55
56
/* ------------------------------------------------------------------
57
📊 Polls (CORRECT & DERIVED)
58
------------------------------------------------------------------ */
59
UNION ALL
60
(
61
WITH vote_stats AS (
62
SELECT
63
pv.poll_id,
64
COUNT(*) AS total_votes
65
FROM poll_votes pv
66
WHERE pv.is_deleted = false
67
GROUP BY pv.poll_id
68
),
69
option_stats AS (
70
SELECT
71
pv.poll_option_id,
72
COUNT(*) AS votes
73
FROM poll_votes pv
74
WHERE pv.is_deleted = false
75
GROUP BY pv.poll_option_id
76
),
77
possible_voters AS (
78
SELECT
79
p.id AS poll_id,
80
CASE
81
WHEN p.poll_eligibility_type_id = 1 THEN (
82
SELECT COUNT(*)
83
FROM units u
84
WHERE u.apartment_id = p_apartment_id
85
AND u.unit_type_id = 1
86
AND u.is_deleted = false
87
)
88
WHEN p.poll_eligibility_type_id = 2 THEN (
89
SELECT COUNT(*)
90
FROM residents r
91
WHERE r.apartment_id = p_apartment_id
92
AND r.is_deleted = false
93
)
94
ELSE 0
95
END AS total_possible
96
FROM polls p
97
WHERE p.apartment_id = p_apartment_id
98
)
99
100
SELECT
101
'Poll',
102
'Medium',
103
p.start_date,
104
105
jsonb_build_object(
106
'id', p.id,
107
'title', p.title,
108
109
'status', CASE
110
WHEN p.start_date > now() THEN 'Not Started'
111
WHEN p.end_date < now() THEN 'Closed'
112
ELSE 'Ongoing'
113
END,
114
115
'endsAt', p.end_date,
116
117
'hasVoted',
118
EXISTS (
119
SELECT 1
120
FROM poll_votes pv
121
WHERE pv.poll_id = p.id
122
AND pv.is_deleted = false
123
AND (
124
(p.poll_eligibility_type_id = 1 AND pv.unit_id = p_unit_id)
125
OR (p.poll_eligibility_type_id = 2 AND pv.resident_id = p_resident_id)
126
)
127
),
128
129
'selectedOptionId',
130
(
131
SELECT pv.poll_option_id
132
FROM poll_votes pv
133
WHERE pv.poll_id = p.id
134
AND pv.is_deleted = false
135
AND (
136
(p.poll_eligibility_type_id = 1 AND pv.unit_id = p_unit_id)
137
OR (p.poll_eligibility_type_id = 2 AND pv.resident_id = p_resident_id)
138
)
139
LIMIT 1
140
),
141
142
'votesCast', COALESCE(vs.total_votes, 0),
143
'possibleVoters', pv.total_possible,
144
145
'participationPercent',
146
CASE
147
WHEN pv.total_possible = 0 THEN 0
148
ELSE ROUND((COALESCE(vs.total_votes, 0) * 100.0) / pv.total_possible, 1)
149
END,
150
151
'options',
152
COALESCE(
153
(
154
SELECT jsonb_agg(
155
jsonb_build_object(
156
'id', po.id,
157
'text', po.option_text,
158
'voteCount', COALESCE(os.votes, 0),
159
'percentage',
160
CASE
161
WHEN COALESCE(vs.total_votes, 0) = 0 THEN 0
162
ELSE ROUND(
163
(COALESCE(os.votes, 0) * 100.0)
164
/ COALESCE(vs.total_votes, 0),
165
1
166
)
167
END
168
)
169
ORDER BY po.id
170
)
171
FROM poll_options po
172
LEFT JOIN option_stats os ON os.poll_option_id = po.id
173
WHERE po.poll_id = p.id
174
),
175
'[]'::jsonb
176
)
177
)
178
179
FROM polls p
180
LEFT JOIN vote_stats vs ON vs.poll_id = p.id
181
LEFT JOIN possible_voters pv ON pv.poll_id = p.id
182
WHERE p.apartment_id = p_apartment_id
183
AND p.is_deleted = false
184
ORDER BY p.created_on_utc DESC
185
LIMIT 1
186
)
187
188
/* ------------------------------------------------------------------
189
📅 Meetings
190
------------------------------------------------------------------ */
191
UNION ALL
192
(
193
SELECT
194
'Meeting',
195
'High',
196
e.start_time,
197
jsonb_build_object(
198
'id', e.id,
199
'title', e.title,
200
'description', e.description,
201
'startsAt', e.start_time,
202
'endsAt', e.end_time,
203
'isLive', (e.start_time <= now() AND now() <= e.end_time)
204
)
205
FROM events e
206
WHERE e.company_id = p_apartment_id
207
AND e.is_deleted = false
208
ORDER BY e.created_on_utc DESC
209
LIMIT 1
210
);
211
212
$function$
|
|||||
| Function | get_meeting_data | Match | ||||||
| Function | create_poll_vote | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_poll_vote(p_poll_id integer, p_unit_id integer, p_resident_id integer, p_poll_option_id integer, p_vote_comment text)
2
RETURNS void
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
-- 1️⃣ Validate required identifiers
7
IF p_unit_id IS NULL OR p_resident_id IS NULL THEN
8
RAISE EXCEPTION 'Both unitId and residentId are required';
9
END IF;
10
11
-- 2️⃣ Validate poll active
12
IF NOT EXISTS (
13
SELECT 1
14
FROM polls
15
WHERE id = p_poll_id
16
AND is_deleted = false
17
AND start_date <= now()
18
AND end_date >= now()
19
) THEN
20
RAISE EXCEPTION 'Poll is not active';
21
END IF;
22
23
-- 3️⃣ Validate option
24
IF NOT EXISTS (
25
SELECT 1
26
FROM poll_options
27
WHERE id = p_poll_option_id
28
AND poll_id = p_poll_id
29
) THEN
30
RAISE EXCEPTION 'Invalid poll option';
31
END IF;
32
33
-- 4️⃣ Insert vote (unit-based)
34
INSERT INTO poll_votes (
35
poll_id,
36
unit_id,
37
resident_id,
38
poll_option_id,
39
vote_comment,
40
vote_date,
41
created_on_utc,
42
is_deleted
43
)
44
VALUES (
45
p_poll_id,
46
p_unit_id,
47
p_resident_id,
48
p_poll_option_id,
49
p_vote_comment,
50
now(),
51
now(),
52
false
53
)
54
ON CONFLICT ON CONSTRAINT uq_poll_votes_unit_per_poll
55
DO NOTHING;
56
END;
57
$function$
|
|||||
| Function | create_poll_with_options | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_poll_with_options(p_apartment_id uuid, p_title character varying, p_description text, p_poll_type_id integer, p_poll_eligibility_type_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone, p_created_by uuid, p_options jsonb)
2
RETURNS TABLE(poll_id integer, options_count integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_poll_id int;
7
v_option jsonb;
8
BEGIN
9
-- 1️⃣ Validate date range
10
IF p_end_date <= p_start_date THEN
11
RAISE EXCEPTION 'Poll end date must be greater than start date';
12
END IF;
13
14
-- 2️⃣ Validate poll type
15
IF NOT EXISTS (
16
SELECT 1 FROM poll_types WHERE id = p_poll_type_id
17
) THEN
18
RAISE EXCEPTION 'Invalid poll_type_id: %', p_poll_type_id;
19
END IF;
20
21
-- 3️⃣ Validate eligibility type
22
IF NOT EXISTS (
23
SELECT 1 FROM poll_eligibility_types WHERE id = p_poll_eligibility_type_id
24
) THEN
25
RAISE EXCEPTION 'Invalid poll_eligibility_type_id: %', p_poll_eligibility_type_id;
26
END IF;
27
28
-- 4️⃣ Create poll
29
INSERT INTO polls (
30
apartment_id,
31
title,
32
description,
33
poll_type_id,
34
poll_eligibility_type_id,
35
status_id,
36
start_date,
37
end_date,
38
created_on_utc,
39
created_by,
40
is_deleted
41
)
42
VALUES (
43
p_apartment_id,
44
p_title,
45
p_description,
46
p_poll_type_id,
47
p_poll_eligibility_type_id,
48
1, -- Draft / Active (adjust if needed)
49
p_start_date,
50
p_end_date,
51
now(),
52
p_created_by,
53
false
54
)
55
RETURNING id INTO v_poll_id;
56
57
-- 5️⃣ Insert options
58
FOR v_option IN
59
SELECT * FROM jsonb_array_elements(p_options)
60
LOOP
61
INSERT INTO poll_options (
62
poll_id,
63
option_text,
64
option_type_id
65
)
66
VALUES (
67
v_poll_id,
68
v_option ->> 'text',
69
1
70
);
71
END LOOP;
72
73
-- 6️⃣ Return
74
RETURN QUERY
75
SELECT
76
v_poll_id,
77
jsonb_array_length(p_options);
78
79
END;
80
$function$
|
|||||
| Function | update_visitor_status | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.update_visitor_status(p_visitor_log_id integer, p_unit_id integer, p_visitor_status_id integer, p_modified_by uuid, p_is_manual_approval boolean DEFAULT false, p_approval_source integer DEFAULT 1, p_approval_reason integer DEFAULT 0, p_approved_by_resident_id integer DEFAULT NULL::integer)
2
RETURNS TABLE(visitor_id integer, total_visits_count integer, approved_visits_count integer, final_status_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
local_total_visits int;
7
local_approved_count int;
8
local_rejected_count int; -- Added to track rejections
9
local_new_visitor_status int;
10
BEGIN
11
-- Step 1: Update the status for the specific unit
12
UPDATE public.multi_unit_visits
13
SET visitor_statuses = p_visitor_status_id,
14
is_manual_approval = COALESCE(p_is_manual_approval, false),
15
approval_source = COALESCE(p_approval_source, 1),
16
approval_reason = COALESCE(p_approval_reason, 0),
17
approved_by_resident_id = p_approved_by_resident_id,
18
modified_on_utc = NOW(),
19
modified_by = p_modified_by
20
WHERE visitor_log_id = p_visitor_log_id
21
AND unit_id = p_unit_id
22
AND is_deleted = false;
23
24
-- Step 2: Check if the update was successful.
25
IF NOT FOUND THEN
26
RETURN;
27
END IF;
28
29
-- Step 3: Recalculate the overall visitor log status.
30
SELECT
31
COUNT(*),
32
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2), -- Approved
33
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6) -- Rejected
34
INTO
35
local_total_visits,
36
local_approved_count,
37
local_rejected_count
38
FROM public.multi_unit_visits AS muv
39
WHERE muv.visitor_log_id = p_visitor_log_id
40
AND muv.is_deleted = false;
41
42
-- Step 4: Determine the new overall status with corrected logic.
43
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
44
local_new_visitor_status := 2; -- Rule 1: All units have approved.
45
ELSIF local_approved_count > 0 THEN
46
-- FIX: If at least one unit approves, the status is "Partial Approved",
47
-- even if another unit rejects.
48
local_new_visitor_status := 7; -- Rule 2: At least one approval, but not all.
49
ELSIF local_rejected_count > 0 THEN
50
local_new_visitor_status := 6; -- Rule 3: No approvals yet, but at least one rejection.
51
ELSE
52
local_new_visitor_status := 1; -- Rule 4: No approvals and no rejections yet.
53
END IF;
54
55
-- Step 5: Update the parent visitor_logs table.
56
UPDATE public.visitor_logs
57
SET visitor_status_id = local_new_visitor_status,
58
modified_on_utc = NOW(),
59
modified_by = p_modified_by
60
WHERE id = p_visitor_log_id;
61
62
-- Step 6: On success, return the calculated values.
63
RETURN QUERY SELECT p_visitor_log_id, local_total_visits, local_approved_count, local_new_visitor_status;
64
END;
65
$function$
|
|||||
| Function | get_unit_visitors | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_unit_visitors(p_unit_id integer, p_for_date date DEFAULT CURRENT_DATE)
2
RETURNS TABLE(id integer, source text, visitor_id bigint, possible_visitor_id bigint, pre_approved_entry_id integer, unit_id integer, visitor_type_id integer, visitor_type text, visitor_name text, phone_number text, vehicle_number text, entry_time timestamp without time zone, exit_time timestamp without time zone, expected_from timestamp without time zone, expected_until timestamp without time zone, visitor_status_id integer, visitor_status_name text)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
7
WITH unit_ctx AS (
8
SELECT
9
u.id AS unit_id,
10
u.apartment_id AS apartment_id
11
FROM units u
12
WHERE u.id = p_unit_id
13
AND u.is_deleted = false
14
)
15
16
SELECT
17
ROW_NUMBER() OVER (
18
ORDER BY
19
entry_time DESC NULLS LAST,
20
expected_from DESC NULLS LAST
21
)::int AS id,
22
q.*
23
FROM (
24
/* ===============================
25
1️⃣ ACTUAL VISITS (LOG)
26
=============================== */
27
SELECT
28
'LOG' AS source,
29
v.id AS visitor_id,
30
NULL::bigint AS possible_visitor_id,
31
vl.pre_approved_entry_id,
32
muv.unit_id,
33
v.visitor_type_id,
34
vt.name AS visitor_type,
35
concat_ws(' ', v.first_name, v.last_name) AS visitor_name,
36
NULL::text AS phone_number,
37
vl.vehicle_number,
38
vl.entry_time,
39
vl.exit_time,
40
NULL::timestamp AS expected_from,
41
NULL::timestamp AS expected_until,
42
muv.visitor_statuses AS visitor_status_id,
43
vs.name AS visitor_status_name
44
FROM unit_ctx uc
45
JOIN visitor_logs vl
46
ON vl.apartment_id = uc.apartment_id
47
Join visitor_types vt
48
On vl.visitor_type_id = vt.id
49
JOIN visitors v
50
ON v.id = vl.visitor_id
51
AND v.is_deleted = false
52
JOIN multi_unit_visits muv
53
ON muv.visitor_log_id = vl.id
54
AND muv.is_deleted = false
55
AND muv.unit_id = uc.unit_id
56
JOIN visitor_statuses vs
57
ON vs.id = muv.visitor_statuses
58
AND vs.is_deleted = false
59
WHERE
60
DATE(vl.created_on_utc) = p_for_date
61
62
UNION ALL
63
64
/* ===============================
65
2️⃣ EXPECTED VISITORS (PRE_APPROVED)
66
=============================== */
67
SELECT
68
'PRE_APPROVED' AS source,
69
pv2.id AS visitor_id, -- optional (known visitor)
70
pv.id AS possible_visitor_id,
71
pae.id AS pre_approved_entry_id,
72
pae.unit_id,
73
pv.visitor_type_id,
74
vt.name AS visitor_type,
75
concat_ws(' ', pv.first_name, pv.last_name) AS visitor_name,
76
pv.phone_number,
77
pv.vehicle_number,
78
NULL::timestamp AS entry_time,
79
NULL::timestamp AS exit_time,
80
pae.valid_from AS expected_from,
81
pae.valid_until AS expected_until,
82
8 AS visitor_status_id, -- virtual EXPECTED
83
'Expected' AS visitor_status_name
84
FROM unit_ctx uc
85
JOIN pre_approved_entries pae
86
ON pae.unit_id = uc.unit_id
87
AND pae.apartment_id = uc.apartment_id
88
AND pae.is_deleted = false
89
JOIN possible_visitors pv
90
ON pv.id = pae.possible_visitor_id
91
AND pv.is_deleted = false
92
Join visitor_types vt
93
On pv.visitor_type_id = vt.id
94
LEFT JOIN visitors pv2
95
ON pv2.id = pae.visitor_id
96
AND pv2.is_deleted = false
97
WHERE
98
pae.valid_from::date <= p_for_date
99
AND (pae.valid_until IS NULL OR pae.valid_until::date >= p_for_date)
100
) q;
101
$function$
|
|||||
| Function | get_visitors_inside_with_units | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitors_inside_with_units(p_apartment_id uuid, p_today_only boolean DEFAULT false)
2
RETURNS TABLE(id integer, visitor_log_id bigint, visitor_id bigint, first_name text, last_name text, visitor_type_id integer, visitor_type_name text, visiting_units jsonb, entry_time timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
CAST(row_number() OVER (ORDER BY vl.entry_time DESC) AS integer) AS id,
9
vl.id AS visitor_log_id,
10
v.id AS visitor_id,
11
v.first_name,
12
v.last_name,
13
v.visitor_type_id,
14
vt.name::text AS visitor_type_name,
15
COALESCE(
16
jsonb_agg(
17
jsonb_build_object(
18
'unitId', u.id,
19
'unitName', u.name
20
)
21
ORDER BY u.id
22
) FILTER (WHERE u.id IS NOT NULL),
23
'[]'::jsonb
24
) AS visiting_units,
25
vl.entry_time
26
FROM
27
visitor_logs vl
28
JOIN
29
visitors v ON v.id = vl.visitor_id
30
JOIN
31
visitor_types vt ON vt.id = vl.visitor_type_id
32
LEFT JOIN
33
multi_unit_visits muv
34
ON muv.visitor_log_id = vl.id
35
AND muv.is_deleted = FALSE
36
LEFT JOIN
37
units u
38
ON u.id = muv.unit_id
39
AND u.is_deleted = FALSE
40
And u.apartment_id = p_apartment_id
41
WHERE
42
vl.apartment_id = p_apartment_id
43
AND vl.exit_time IS NULL
44
AND v.is_deleted = FALSE
45
AND vt.is_deleted = FALSE
46
--AND (p_today_only = FALSE OR vl.entry_time::date = CURRENT_DATE)
47
AND (NOT p_today_only OR vl.entry_time::date = CURRENT_DATE)
48
GROUP BY
49
vl.id,
50
v.id,
51
v.first_name,
52
v.last_name,
53
v.visitor_type_id,
54
vt.name,
55
vl.entry_time
56
ORDER BY
57
vl.entry_time DESC;
58
END;
59
$function$
|
|||||
| Function | get_visitors_inside_counts_by_type | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitors_inside_counts_by_type(p_apartment_id uuid, p_today_only boolean)
2
RETURNS TABLE(id integer, visitor_type_id integer, visitor_type_name text, count integer)
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
ROW_NUMBER() OVER (ORDER BY vt.id)::integer AS id,
7
vt.id AS visitor_type_id,
8
vt.name::text AS visitor_type_name,
9
COUNT(*) AS count
10
FROM visitor_logs vl
11
JOIN visitor_types vt ON vt.id = vl.visitor_type_id
12
JOIN visitors v ON v.id = vl.visitor_id
13
WHERE
14
vl.apartment_id = p_apartment_id
15
AND vl.exit_time IS NULL
16
AND (NOT p_today_only OR vl.entry_time::date = CURRENT_DATE)
17
AND v.is_deleted = FALSE
18
AND vt.is_deleted = FALSE
19
GROUP BY
20
vt.id,
21
vt.name
22
ORDER BY
23
vt.id;
24
$function$
|
|||||
| Function | get_unit_related_details | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_unit_related_details(unit_id integer)
2
RETURNS jsonb
3
LANGUAGE sql
4
AS $function$
5
SELECT jsonb_build_object(
6
'id', u.id,
7
'name', u.name,
8
'unit_type_id', u.unit_type_id,
9
'phone_extension', u.phone_extention,
10
'area', u.area,
11
'bhk_type', u.bhk_type,
12
'occupant_type_id', u.occupant_type_id,
13
'occupancy_type_id', u.occupancy_type_id,
14
'residents', COALESCE(
15
(SELECT jsonb_agg(DISTINCT jsonb_build_object(
16
'id', r.id,
17
'first_name', r.first_name,
18
'last_name', r.last_name,
19
'email', r.email,
20
'contact_number', r.contact_number,
21
'resident_type', rt.name
22
))
23
FROM resident_units ru
24
JOIN residents r ON r.id = ru.resident_id AND r.is_deleted = false
25
JOIN resident_types rt ON rt.id = r.resident_type_id AND rt.is_deleted = false
26
WHERE ru.unit_id = u.id AND ru.is_deleted = false),
27
'[]'::jsonb),
28
'vehicles', COALESCE(
29
(SELECT jsonb_agg(DISTINCT jsonb_build_object(
30
'id', v.id,
31
'vehicle_number', v.vehicle_number,
32
'vehicle_type', vt.name
33
))
34
FROM vehicles v
35
JOIN vehicle_types vt ON vt.id = v.vehicle_type_id AND vt.is_deleted = false
36
WHERE v.unit_id = u.id AND v.is_deleted = false),
37
'[]'::jsonb)
38
)
39
FROM units u
40
WHERE u.id = unit_id AND u.is_deleted = false;
41
$function$
|
|||||
| Function | approve_visitor_for_unit | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.approve_visitor_for_unit(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid, p_resident_id integer)
2
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, final_status_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
local_total_visits INT;
7
local_approved_count INT;
8
local_rejected_count INT;
9
local_new_visitor_status INT;
10
BEGIN
11
-- Step 1: Approve for this unit only
12
UPDATE public.multi_unit_visits
13
SET visitor_statuses = 2, -- Approved
14
modified_on_utc = NOW(),
15
modified_by = p_resident_user
16
WHERE visitor_log_id = p_visitor_log_id
17
AND unit_id = p_unit_id
18
AND is_deleted = FALSE;
19
20
IF NOT FOUND THEN
21
-- No update happened (invalid visitor_log_id or unit_id)
22
RETURN;
23
END IF;
24
25
-- Step 2: Recalculate parent visitor log status
26
SELECT
27
COUNT(*),
28
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2), -- Approved
29
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6) -- Rejected
30
INTO
31
local_total_visits,
32
local_approved_count,
33
local_rejected_count
34
FROM public.multi_unit_visits muv
35
WHERE muv.visitor_log_id = p_visitor_log_id
36
AND muv.is_deleted = FALSE;
37
38
-- Step 3: Decide new parent status
39
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
40
local_new_visitor_status := 2; -- All Approved
41
ELSIF local_approved_count > 0 THEN
42
local_new_visitor_status := 7; -- Partial Approved
43
ELSIF local_rejected_count > 0 THEN
44
local_new_visitor_status := 6; -- Rejected
45
ELSE
46
local_new_visitor_status := 1; -- Pending
47
END IF;
48
49
UPDATE public.visitor_logs
50
SET visitor_status_id = local_new_visitor_status,
51
modified_on_utc = NOW(),
52
modified_by = p_resident_user
53
WHERE id = p_visitor_log_id;
54
55
-- Step 4: Return minimal information for the service layer
56
RETURN QUERY
57
SELECT
58
p_visitor_log_id,
59
p_unit_id,
60
p_resident_id, -- approver resident
61
local_new_visitor_status;
62
63
END;
64
$function$
|
|||||
| Function | get_notice_list_by_date_range | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_notice_list_by_date_range(p_apartment_id uuid, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
2
RETURNS TABLE(id bigint, title character varying, short_description character varying, category_id integer, category_name character varying, priority_id integer, priority_name character varying, expires_on timestamp without time zone, created_by uuid, created_by_name character varying, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name character varying, modified_on_utc timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
n.id::bigint AS id,
9
n.title::varchar AS title,
10
n.short_description::varchar AS short_description,
11
n.category_id AS category_id,
12
nc.name::varchar AS category_name,
13
n.priority_id AS priority_id,
14
np.name::varchar AS priority_name,
15
n.expires_on AS expires_on,
16
n.created_by AS created_by,
17
(cu.first_name || ' ' || cu.last_name)::varchar AS created_by_name,
18
n.created_on_utc AS created_on_utc,
19
n.modified_by AS modified_by,
20
COALESCE(
21
(mu.first_name || ' ' || mu.last_name)::varchar,
22
''
23
) AS modified_by_name,
24
n.modified_on_utc AS modified_on_utc
25
FROM notices n
26
JOIN notice_categories nc ON n.category_id = nc.id
27
JOIN notice_priorities np ON n.priority_id = np.id
28
JOIN users cu ON n.created_by = cu.id
29
LEFT JOIN users mu ON n.modified_by = mu.id
30
WHERE
31
n.is_deleted = false
32
AND n.apartment_id = p_apartment_id
33
AND n.expires_on >= p_start_date
34
AND n.expires_on < p_end_date
35
ORDER BY n.expires_on DESC, n.id DESC;
36
END;
37
$function$
|
|||||
| Function | get_visitor_multiple_unit_logs | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitor_multiple_unit_logs(v_apartment_id uuid, log_date timestamp without time zone)
1
CREATE OR REPLACE FUNCTION public.get_visitor_multiple_unit_logs(v_apartment_id uuid, log_date timestamp without time zone)
2
RETURNS TABLE(id bigint, visitor_id bigint, first_name text, last_name text, unit_details jsonb, latest_entry_time timestamp without time zone, latest_exit_time timestamp without time zone, contact_number text, visitor_type_id integer, visitor_type_name text)
2
RETURNS TABLE(id integer, visitor_id integer, first_name text, last_name text, unit_id integer, unit_name text, latest_entry_time timestamp without time zone, latest_exit_time timestamp without time zone, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name text)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
BEGIN
5
BEGIN
6
RETURN QUERY
6
RETURN QUERY
7
SELECT
7
SELECT
8
vl.id,
8
vl.id AS id,
9
vl.visitor_id::BIGINT AS visitor_id,
9
v.id AS visitor_id,
10
v.first_name,
10
v.first_name,
11
v.last_name,
11
v.last_name,
12
12
muv.unit_id,
13
/* units aggregation */
13
u.name AS unit_name,
14
COALESCE(
14
MAX(vl.entry_time) AS latest_entry_time,
15
jsonb_agg(
15
MAX(vl.exit_time) AS latest_exit_time,
16
jsonb_build_object(
16
vl.visiting_from,
17
'unit_id', muv.unit_id,
17
v.contact_number,
18
'unit_name', u.name
18
v.visitor_type_id,
19
)
19
vt.name :: text AS visitor_type_name
20
) FILTER (WHERE muv.unit_id IS NOT NULL),
20
FROM
21
'[]'::jsonb
21
visitors v
22
) AS unit_details,
22
JOIN
23
23
visitor_logs vl ON v.id = vl.visitor_id
24
/* entry / exit from log */
24
LEFT JOIN
25
vl.entry_time AS latest_entry_time,
25
multi_unit_visits muv ON muv.visitor_log_id = vl.id
26
vl.exit_time AS latest_exit_time,
26
LEFT JOIN
27
27
units u ON u.id = muv.unit_id
28
/* single phone per visitor (no fan-out, no ambiguity) */
28
LEFT JOIN
29
vp.phone_number AS contact_number,
29
visitor_types vt ON vt.id = v.visitor_type_id
30
30
WHERE
31
/* visitor type from log */
31
v.apartment_id = v_apartment_id
32
vl.visitor_type_id,
32
AND DATE(vl.entry_time) = DATE(log_date)
33
vt.name::TEXT AS visitor_type_name
34
35
FROM visitor_logs vl
36
37
/* visitor metadata */
38
LEFT JOIN visitors v
39
ON v.id = vl.visitor_id
40
AND v.is_deleted = FALSE
33
AND v.is_deleted = FALSE
41
34
AND vl.is_deleted = FALSE
42
/* SAFE phone join (fixes ambiguity + fan-out) */
43
LEFT JOIN LATERAL (
44
SELECT vp2.phone_number
45
FROM visitor_phones vp2
46
WHERE vp2.visitor_id = vl.visitor_id
47
ORDER BY vp2.id
48
LIMIT 1
49
) vp ON true
50
51
/* unit mapping */
52
LEFT JOIN public.multi_unit_visits muv
53
ON muv.visitor_log_id = vl.id
54
AND muv.is_deleted = FALSE
35
AND muv.is_deleted = FALSE
55
56
LEFT JOIN units u
57
ON u.id = muv.unit_id
58
59
/* type lookup from LOG */
60
LEFT JOIN visitor_types vt
61
ON vt.id = vl.visitor_type_id
62
63
WHERE
64
vl.apartment_id = v_apartment_id
65
AND vl.created_on_utc >= date_trunc('day', log_date)
66
AND vl.created_on_utc < date_trunc('day', log_date) + interval '1 day'
67
68
GROUP BY
36
GROUP BY
69
vl.id,
37
vl.id, v.id, v.first_name, v.last_name, muv.unit_id, u.name,
70
vl.visitor_id,
38
vl.visiting_from, v.contact_number, v.visitor_type_id, vt.name;
71
v.first_name,
72
v.last_name,
73
vl.entry_time,
74
vl.exit_time,
75
vp.phone_number,
76
vl.visitor_type_id,
77
vt.name
78
79
ORDER BY MAX(vl.created_on_utc) DESC;
80
END;
39
END;
81
$function$
40
$function$
|
|||||
| Function | get_apartment_visitors | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_apartment_visitors(p_apartment_id uuid, p_unit_id integer DEFAULT NULL::integer, p_for_date date DEFAULT CURRENT_DATE)
2
RETURNS TABLE(row_id integer, source text, visitor_id bigint, possible_visitor_id bigint, pre_approved_entry_id integer, unit_id integer, visitor_type_id integer, visitor_name text, phone_number text, vehicle_number text, entry_time timestamp without time zone, exit_time timestamp without time zone, expected_from timestamp without time zone, expected_until timestamp without time zone, visitor_status_id integer, visitor_status_name text)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
7
SELECT
8
ROW_NUMBER() OVER (ORDER BY
9
entry_time DESC NULLS LAST,
10
expected_from DESC NULLS LAST
11
)::int AS row_id,
12
q.*
13
FROM (
14
/* ===============================
15
1️⃣ ACTUAL VISITS (LOG)
16
=============================== */
17
SELECT
18
'LOG' AS source,
19
v.id AS visitor_id,
20
NULL::bigint AS possible_visitor_id,
21
vl.pre_approved_entry_id,
22
muv.unit_id,
23
v.visitor_type_id,
24
concat_ws(' ', v.first_name, v.last_name) AS visitor_name,
25
NULL::text AS phone_number,
26
vl.vehicle_number,
27
vl.entry_time,
28
vl.exit_time,
29
NULL::timestamp AS expected_from,
30
NULL::timestamp AS expected_until,
31
muv.visitor_statuses AS visitor_status_id,
32
vs.name AS visitor_status_name
33
FROM visitor_logs vl
34
JOIN visitors v
35
ON v.id = vl.visitor_id
36
AND v.is_deleted = false
37
JOIN multi_unit_visits muv
38
ON muv.visitor_log_id = vl.id
39
AND muv.is_deleted = false
40
JOIN visitor_statuses vs
41
ON vs.id = muv.visitor_statuses
42
AND vs.is_deleted = false
43
WHERE
44
vl.apartment_id = p_apartment_id
45
AND DATE(vl.created_on_utc) = p_for_date
46
AND (p_unit_id IS NULL OR muv.unit_id = p_unit_id)
47
48
UNION ALL
49
50
/* ===============================
51
2️⃣ EXPECTED VISITORS
52
=============================== */
53
SELECT
54
'PRE_APPROVED' AS source,
55
pv2.id AS visitor_id,
56
pv.id AS possible_visitor_id,
57
pae.id AS pre_approved_entry_id,
58
pae.unit_id,
59
pv.visitor_type_id,
60
concat_ws(' ', pv.first_name, pv.last_name) AS visitor_name,
61
pv.phone_number,
62
pv.vehicle_number,
63
NULL::timestamp AS entry_time,
64
NULL::timestamp AS exit_time,
65
pae.valid_from AS expected_from,
66
pae.valid_until AS expected_until,
67
8 AS visitor_status_id,
68
'Expected' AS visitor_status_name
69
FROM pre_approved_entries pae
70
JOIN possible_visitors pv
71
ON pv.id = pae.possible_visitor_id
72
AND pv.is_deleted = false
73
LEFT JOIN visitors pv2
74
ON pv2.id = pae.visitor_id
75
AND pv2.is_deleted = false
76
WHERE
77
pae.apartment_id = p_apartment_id
78
AND pae.is_deleted = false
79
AND pae.valid_from::date <= p_for_date
80
AND (pae.valid_until IS NULL OR pae.valid_until::date >= p_for_date)
81
AND (p_unit_id IS NULL OR pae.unit_id = p_unit_id)
82
) q;
83
84
$function$
|
|||||
| Function | take_visitor_action | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.take_visitor_action(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid, p_action_status integer, p_approval_reason integer, p_approval_source integer, p_approved_by_resident_id integer, p_is_manual_approval boolean)
2
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, approver_first_name text, approver_last_name text, final_status_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
6
DECLARE
7
local_total_visits INT;
8
local_approved_count INT;
9
local_rejected_count INT;
10
local_new_visitor_status INT;
11
12
v_pending CONSTANT INT := 1;
13
v_partial_approved CONSTANT INT := 2;
14
v_approved CONSTANT INT := 3;
15
v_rejected CONSTANT INT := 7;
16
v_checked_in CONSTANT INT := 4;
17
BEGIN
18
/*
19
* 1. Update unit-level status (approve / reject)
20
*/
21
UPDATE public.multi_unit_visits AS muv
22
SET visitor_statuses = p_action_status,
23
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
24
modified_by = p_resident_user,
25
approval_reason = p_approval_reason,
26
approval_source = p_approval_source,
27
approved_by_resident_id = p_approved_by_resident_id,
28
is_manual_approval = p_is_manual_approval
29
WHERE muv.visitor_log_id = p_visitor_log_id
30
AND muv.unit_id = p_unit_id
31
AND muv.is_deleted = FALSE;
32
33
IF NOT FOUND THEN
34
RETURN;
35
END IF;
36
37
/*
38
* 2. Recalculate unit status counts
39
*/
40
SELECT
41
COUNT(*),
42
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_approved),
43
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_rejected)
44
INTO
45
local_total_visits,
46
local_approved_count,
47
local_rejected_count
48
FROM public.multi_unit_visits AS muv
49
WHERE muv.visitor_log_id = p_visitor_log_id
50
AND muv.is_deleted = FALSE;
51
52
/*
53
* 3. Decide parent visitor_logs status
54
*/
55
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
56
local_new_visitor_status := v_approved; -- 3 Approved
57
ELSIF local_approved_count > 0 THEN
58
local_new_visitor_status := v_partial_approved; -- 2 Partial Approved
59
ELSIF local_rejected_count > 0 THEN
60
local_new_visitor_status := v_rejected; -- 7 Rejected
61
ELSE
62
local_new_visitor_status := v_pending; -- 1 Pending
63
END IF;
64
65
/*
66
* 4. Update visitor_logs parent status
67
*/
68
UPDATE public.visitor_logs AS vl
69
SET
70
--visitor_status_id = local_new_visitor_status,
71
visitor_status_id = CASE
72
WHEN (
73
(local_new_visitor_status IN (v_approved, v_partial_approved)
74
AND vl.entry_time IS NULL)
75
OR COALESCE(p_is_manual_approval, false) = true
76
)
77
THEN v_checked_in
78
ELSE visitor_status_id
79
END,
80
81
entry_time = CASE
82
WHEN (
83
(local_new_visitor_status IN (v_approved, v_partial_approved)
84
AND vl.entry_time IS NULL)
85
OR COALESCE(p_is_manual_approval, false) = true
86
)
87
AND vl.entry_time IS NULL
88
THEN (NOW() AT TIME ZONE 'Asia/Kolkata')
89
ELSE vl.entry_time
90
END,
91
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
92
modified_by = p_resident_user
93
WHERE vl.id = p_visitor_log_id;
94
95
/*
96
* 5. Return response with approver details
97
*/
98
RETURN QUERY
99
SELECT
100
p_visitor_log_id,
101
p_unit_id,
102
r.id,
103
r.first_name,
104
r.last_name,
105
local_new_visitor_status
106
FROM public.residents r
107
WHERE r.user_id = p_resident_user
108
AND r.is_deleted = FALSE;
109
END;
110
$function$
|
|||||
| Function | create_poll | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_poll(p_apartment_id uuid, p_title character varying, p_description text, p_poll_type_id integer, p_poll_eligibility_type integer, p_status_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone, p_created_by uuid)
2
RETURNS integer
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_poll_id int;
7
BEGIN
8
/* -----------------------------------------
9
1️⃣ Validate eligibility type
10
----------------------------------------- */
11
IF p_poll_eligibility_type NOT IN (1, 2) THEN
12
RAISE EXCEPTION
13
'Invalid poll eligibility type %',
14
p_poll_eligibility_type;
15
END IF;
16
17
/* -----------------------------------------
18
2️⃣ Create poll (NO SNAPSHOT DATA)
19
----------------------------------------- */
20
INSERT INTO polls (
21
apartment_id,
22
title,
23
description,
24
poll_type_id,
25
poll_eligibility_type_id,
26
status_id,
27
start_date,
28
end_date,
29
created_on_utc,
30
created_by,
31
is_deleted
32
)
33
VALUES (
34
p_apartment_id,
35
p_title,
36
p_description,
37
p_poll_type_id,
38
p_poll_eligibility_type,
39
p_status_id,
40
p_start_date,
41
p_end_date,
42
(NOW() AT TIME ZONE 'UTC'),
43
p_created_by,
44
false
45
)
46
RETURNING id INTO v_poll_id;
47
48
RETURN v_poll_id;
49
END;
50
$function$
|
|||||
| Function | get_visitors_by_unit_and_date_range | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitors_by_unit_and_date_range(p_apartment_id uuid, p_unit_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
2
RETURNS TABLE(id bigint, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_status_id integer, visitor_status text, first_name text, last_name text, image_url text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type text, vehicle_number text, identity_type_id integer, identity_type text, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
6
BEGIN
7
RETURN QUERY
8
SELECT
9
vl.id AS id,
10
vl.entry_time AS entry_time,
11
vl.exit_time AS exit_time,
12
vl.visitor_status_id AS visitor_status_id,
13
vs.name::text AS visitor_status,
14
v.first_name::text,
15
v.last_name::text,
16
COALESCE(v.image_url, '')::text AS image_url,
17
18
ve.email::text,
19
vd.visiting_from::text,
20
vp.phone_number::text AS contact_number,
21
22
vl.visitor_type_id AS visitor_type_id,
23
vt.name::text AS visitor_type, -- ✅ FIX
24
25
COALESCE(vd.vehicle_number, vl.vehicle_number)::text AS vehicle_number,
26
27
vi.identity_type_id,
28
it.name::text AS identity_type, -- ✅ FIX
29
vi.identity_number::text,
30
31
v.created_by,
32
v.created_on_utc,
33
v.modified_by,
34
v.modified_on_utc
35
36
FROM multi_unit_visits muv
37
JOIN visitor_logs vl
38
ON vl.id = muv.visitor_log_id
39
40
JOIN visitors v
41
ON v.id = vl.visitor_id
42
AND v.is_deleted = false
43
44
JOIN visitor_types vt
45
ON vt.id = vl.visitor_type_id
46
AND vt.is_deleted = false
47
48
JOIN visitor_statuses vs
49
ON vs.id = vl.visitor_status_id
50
AND vs.is_deleted = false
51
52
JOIN units u
53
ON u.id = muv.unit_id
54
AND u.apartment_id = p_apartment_id
55
AND u.is_deleted = false
56
57
-- OPTIONAL DATA (SAFE LEFT JOINS)
58
LEFT JOIN visitor_details vd
59
ON vd.visitor_id = v.id
60
61
LEFT JOIN visitor_emails ve
62
ON ve.visitor_id = v.id
63
AND ve.is_active = true
64
65
LEFT JOIN visitor_phones vp
66
ON vp.visitor_id = v.id
67
AND vp.is_active = true
68
69
LEFT JOIN visitor_identities vi
70
ON vi.visitor_id = v.id
71
AND vi.is_active = true
72
73
LEFT JOIN identity_types it
74
ON it.id = vi.identity_type_id
75
AND it.is_deleted = false
76
77
WHERE
78
muv.unit_id = p_unit_id
79
AND muv.is_deleted = false
80
AND (
81
(vl.entry_time IS NOT NULL AND vl.entry_time >= p_start_date AND vl.entry_time < p_end_date)
82
OR
83
(vl.entry_time IS NULL AND vl.created_on_utc BETWEEN p_start_date AND p_end_date)
84
)
85
86
ORDER BY vl.entry_time DESC;
87
END;
88
$function$
|
|||||
| Function | get_unit_by_id | Match | ||||||
| Function | get_visitor_approval_info_by_approval_id | Match | ||||||
| Function | get_water_tanker_deliveries | Match | ||||||
| Function | insert_bulk_water_tanker_deliveries | Match | ||||||
| Function | approve_visitor_for_unit | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.approve_visitor_for_unit(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid)
2
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, approver_first_name text, approver_last_name text, final_status_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
local_total_visits INT;
7
local_approved_count INT;
8
local_rejected_count INT;
9
local_new_visitor_status INT;
10
local_resident_id INT;
11
BEGIN
12
-- Step 0: Resolve user_id → resident_id
13
SELECT r.id
14
INTO local_resident_id
15
FROM public.residents r
16
INNER JOIN public.resident_units ru ON ru.resident_id = r.id
17
WHERE r.user_id = p_resident_user
18
AND ru.unit_id = p_unit_id
19
AND r.is_deleted = FALSE
20
AND ru.is_deleted = FALSE
21
LIMIT 1;
22
23
IF local_resident_id IS NULL THEN
24
-- invalid user or unit
25
RETURN;
26
END IF;
27
28
-- Step 1: Approve for this unit only
29
UPDATE public.multi_unit_visits
30
SET visitor_statuses = 2, -- Approved
31
modified_on_utc = NOW(),
32
modified_by = p_resident_user
33
WHERE visitor_log_id = p_visitor_log_id
34
AND unit_id = p_unit_id
35
AND is_deleted = FALSE;
36
37
IF NOT FOUND THEN
38
RETURN;
39
END IF;
40
41
-- Step 2: Recalculate parent visitor log status
42
SELECT
43
COUNT(*),
44
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2),
45
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6)
46
INTO
47
local_total_visits,
48
local_approved_count,
49
local_rejected_count
50
FROM public.multi_unit_visits muv
51
WHERE muv.visitor_log_id = p_visitor_log_id
52
AND muv.is_deleted = FALSE;
53
54
-- Step 3: Decide new parent status
55
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
56
local_new_visitor_status := 2; -- All Approved
57
ELSIF local_approved_count > 0 THEN
58
local_new_visitor_status := 7; -- Partial Approved
59
ELSIF local_rejected_count > 0 THEN
60
local_new_visitor_status := 6; -- Rejected
61
ELSE
62
local_new_visitor_status := 1; -- Pending
63
END IF;
64
65
UPDATE public.visitor_logs
66
SET visitor_status_id = local_new_visitor_status,
67
modified_on_utc = NOW(),
68
modified_by = p_resident_user
69
WHERE id = p_visitor_log_id;
70
71
-- Step 4: Return info + approver's name
72
RETURN QUERY
73
SELECT
74
p_visitor_log_id,
75
p_unit_id,
76
r.id,
77
r.first_name,
78
r.last_name,
79
local_new_visitor_status
80
FROM public.residents r
81
WHERE r.id = local_resident_id
82
AND r.is_deleted = FALSE;
83
84
END;
85
$function$
|
|||||
| Function | get_current_visitors_and_service_providers | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_current_visitors_and_service_providers(p_unit_id integer)
2
RETURNS TABLE(id integer, visitor_type_id integer, visitor_type text, serial_id integer, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_person_type text, visitor_person_sub_type text, visitor_status_id integer, visitor_status text)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
CAST(ROW_NUMBER() OVER (ORDER BY combined.entry_time, combined.id) AS INT) AS serial_id,
9
combined.visitor_type_id,
10
combined.visitor_type,
11
combined.id,
12
combined.first_name,
13
combined.last_name,
14
combined.entry_time,
15
combined.exit_time,
16
combined.visitor_person_type,
17
combined.visitor_person_sub_type,
18
combined.visitor_status_id,
19
combined.visitor_status_name
20
FROM (
21
-- Visitors
22
SELECT
23
vt.id AS visitor_type_id,
24
'visitor'::TEXT AS visitor_type,
25
v.id,
26
v.first_name::TEXT,
27
v.last_name::TEXT,
28
vl.entry_time,
29
vl.exit_time,
30
vt."name"::TEXT AS visitor_person_type,
31
NULL::TEXT AS visitor_person_sub_type,
32
CASE
33
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 3
34
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 4
35
ELSE vl.visitor_status_id
36
END AS visitor_status_id,
37
CASE
38
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 'Checked In'
39
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 'Checked Out'
40
ELSE vs."name"::TEXT
41
END AS visitor_status_name
42
FROM
43
public.visitor_logs vl
44
JOIN public.visitors v ON vl.visitor_id = v.id
45
JOIN public.multi_unit_visits muv ON muv.visitor_log_id = vl.id
46
JOIN public.visitor_types vt ON v.visitor_type_id = vt.id
47
LEFT JOIN public.visitor_statuses vs ON vl.visitor_status_id = vs.id
48
WHERE
49
muv.unit_id = p_unit_id
50
AND vl.entry_time >= NOW() - INTERVAL '120 hour'
51
AND vl.is_deleted = FALSE
52
AND v.is_deleted = FALSE
53
AND muv.is_deleted = FALSE
54
UNION ALL
55
-- Service Providers
56
SELECT
57
vt_sp.id AS visitor_type_id,
58
'service_provider'::TEXT AS visitor_type,
59
sp.id,
60
sp.first_name::TEXT,
61
sp.last_name::TEXT,
62
spl.entry_time,
63
spl.exit_time,
64
spt."name"::TEXT AS visitor_person_type,
65
spst."name"::TEXT AS visitor_person_sub_type,
66
CASE
67
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 3
68
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 4
69
ELSE NULL
70
END AS visitor_status_id,
71
CASE
72
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 'Checked In'
73
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 'Checked Out'
74
ELSE NULL::TEXT
75
END AS visitor_status_name
76
FROM
77
public.service_provider_logs spl
78
JOIN public.service_providers sp ON spl.service_provider_id = sp.id
79
JOIN public.service_provider_types spt ON sp.service_provider_type_id = spt.id
80
JOIN public.service_provider_sub_types spst ON sp.service_provider_sub_type_id = spst.id
81
JOIN public.multi_unit_visits muv ON muv.unit_id = p_unit_id
82
LEFT JOIN public.visitor_types vt_sp ON vt_sp."name" = 'Service Provider' AND vt_sp.is_deleted = FALSE
83
WHERE
84
spl.entry_time >= NOW() - INTERVAL '120 hour'
85
AND spl.is_deleted = FALSE
86
AND sp.is_deleted = FALSE
87
) AS combined;
88
END;
89
$function$
|
|||||
| Function | insert_pin_record | Match | ||||||
| Function | update_visitor_status | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.update_visitor_status(p_visitor_log_id integer, p_unit_id integer, p_visitor_status_id integer, p_modified_by uuid)
2
RETURNS TABLE(visitor_id integer, total_visits_count integer, approved_visits_count integer, final_status_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
local_total_visits int;
7
local_approved_count int;
8
local_rejected_count int; -- Added to track rejections
9
local_new_visitor_status int;
10
BEGIN
11
-- Step 1: Update the status for the specific unit.
12
UPDATE public.multi_unit_visits
13
SET visitor_statuses = p_visitor_status_id,
14
modified_on_utc = NOW(),
15
modified_by = p_modified_by
16
WHERE public.multi_unit_visits.visitor_log_id = p_visitor_log_id
17
AND public.multi_unit_visits.unit_id = p_unit_id
18
AND public.multi_unit_visits.is_deleted = false;
19
20
-- Step 2: Check if the update was successful.
21
IF NOT FOUND THEN
22
RETURN;
23
END IF;
24
25
-- Step 3: Recalculate the overall visitor log status.
26
SELECT
27
COUNT(*),
28
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2), -- Approved
29
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6) -- Rejected
30
INTO
31
local_total_visits,
32
local_approved_count,
33
local_rejected_count
34
FROM public.multi_unit_visits AS muv
35
WHERE muv.visitor_log_id = p_visitor_log_id
36
AND muv.is_deleted = false;
37
38
-- Step 4: Determine the new overall status with corrected logic.
39
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
40
local_new_visitor_status := 2; -- Rule 1: All units have approved.
41
ELSIF local_approved_count > 0 THEN
42
-- FIX: If at least one unit approves, the status is "Partial Approved",
43
-- even if another unit rejects.
44
local_new_visitor_status := 7; -- Rule 2: At least one approval, but not all.
45
ELSIF local_rejected_count > 0 THEN
46
local_new_visitor_status := 6; -- Rule 3: No approvals yet, but at least one rejection.
47
ELSE
48
local_new_visitor_status := 1; -- Rule 4: No approvals and no rejections yet.
49
END IF;
50
51
-- Step 5: Update the parent visitor_logs table.
52
UPDATE public.visitor_logs
53
SET visitor_status_id = local_new_visitor_status,
54
modified_on_utc = NOW(),
55
modified_by = p_modified_by
56
WHERE id = p_visitor_log_id;
57
58
-- Step 6: On success, return the calculated values.
59
RETURN QUERY SELECT p_visitor_log_id, local_total_visits, local_approved_count, local_new_visitor_status;
60
END;
61
$function$
|
|||||
| Function | check_or_create_event_occurrence | Match | ||||||
| Function | debug_visitor_logs | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.debug_visitor_logs(v_apartment_id uuid, log_date timestamp without time zone)
2
RETURNS TABLE(id integer, visitor_id integer, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visiting_from text, contact_number text, visitor_type_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
vl.id, v.id, v.first_name, v.last_name, vl.entry_time, vl.exit_time,
9
vl.visiting_from, v.contact_number, v.visitor_type_id
10
FROM
11
visitors v
12
JOIN
13
visitor_logs vl ON v.id = vl.visitor_id
14
WHERE
15
v.apartment_id = v_apartment_id
16
AND DATE(vl.entry_time) = DATE(log_date)
17
AND v.is_deleted = FALSE
18
AND vl.is_deleted = FALSE;
19
END;
20
$function$
|
|||||
| Function | get_all_apartment_or_org_residents | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_apartment_or_org_residents(apartmentid uuid, isgetall boolean)
2
RETURNS TABLE(id integer, resident_name text, user_id uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
org_id uuid;
7
BEGIN
8
IF isGetAll THEN
9
SELECT a.organization_id INTO org_id
10
FROM public.apartments a
11
WHERE a.id = apartmentid
12
AND a.is_deleted = false;
13
RETURN QUERY
14
SELECT
15
row_number() OVER ()::int AS id,
16
r.first_name || ' ' || r.last_name AS resident_name,
17
r.user_id
18
FROM public.apartments a
19
INNER JOIN public.residents r ON r.apartment_id = a.id
20
WHERE a.organization_id = org_id
21
AND r.is_deleted = false
22
AND a.is_deleted = false;
23
ELSE
24
RETURN QUERY
25
SELECT
26
row_number() OVER ()::int AS id,
27
r.first_name || ' ' || r.last_name AS resident_name,
28
r.user_id
29
FROM public.apartments a
30
INNER JOIN public.residents r ON r.apartment_id = a.id
31
WHERE a.id = apartmentid
32
AND r.is_deleted = false
33
AND a.is_deleted = false;
34
END IF;
35
END;
36
$function$
|
|||||
| Function | get_all_visitor_logs | Match | ||||||
| Function | process_visitor_unit_response | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.process_visitor_unit_response(p_visitor_log_id integer, p_unit_id integer, p_visitor_status_id integer, p_modified_by uuid)
2
RETURNS TABLE(visitor_log_id integer, total_visits_count integer, approved_visits_count integer, final_status_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
local_total_visits int;
7
local_approved_count int;
8
local_new_visitor_status int;
9
BEGIN
10
-- Step 1: Update the status for the specific unit.
11
UPDATE public.multi_unit_visits
12
SET visitor_statuses = p_visitor_status_id,
13
modified_on_utc = NOW(),
14
modified_by = p_modified_by
15
-- FIX: Explicitly reference the table's column to avoid ambiguity
16
WHERE public.multi_unit_visits.visitor_log_id = p_visitor_log_id
17
AND public.multi_unit_visits.unit_id = p_unit_id
18
AND public.multi_unit_visits.is_deleted = false;
19
20
-- Step 2: Check if the update was successful.
21
IF NOT FOUND THEN
22
RETURN;
23
END IF;
24
25
-- Step 3: Recalculate the overall visitor log status.
26
SELECT
27
COUNT(*),
28
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2) -- Approved
29
INTO
30
local_total_visits,
31
local_approved_count
32
FROM public.multi_unit_visits AS muv
33
-- FIX: Use an alias for clarity and to prevent ambiguity
34
WHERE muv.visitor_log_id = p_visitor_log_id
35
AND muv.is_deleted = false;
36
37
-- Step 4: Determine the new overall status.
38
IF EXISTS (
39
SELECT 1
40
FROM public.multi_unit_visits
41
-- FIX: Explicitly reference the table's column
42
WHERE public.multi_unit_visits.visitor_log_id = p_visitor_log_id
43
AND public.multi_unit_visits.visitor_statuses = 6 -- Rejected
44
AND public.multi_unit_visits.is_deleted = false
45
) THEN
46
local_new_visitor_status := 6; -- Rejected
47
ELSIF local_approved_count > 0 AND local_approved_count = local_total_visits THEN
48
local_new_visitor_status := 2; -- Approved
49
ELSE
50
local_new_visitor_status := 1; -- Pending
51
END IF;
52
53
-- Step 5: Update the parent visitor_logs table.
54
UPDATE public.visitor_logs
55
SET visitor_status_id = local_new_visitor_status,
56
modified_on_utc = NOW(),
57
modified_by = p_modified_by
58
WHERE id = p_visitor_log_id;
59
60
-- Step 6: On success, return the calculated values.
61
RETURN QUERY SELECT p_visitor_log_id, local_total_visits, local_approved_count, local_new_visitor_status;
62
END;
63
$function$
|
|||||
| Function | save_visitor_and_pending | Match | ||||||
| Function | get_visitors_and_service_providers_in_timespan | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitors_and_service_providers_in_timespan(p_unit_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
2
RETURNS TABLE(id integer, visitor_type_id integer, visitor_type text, serial_id integer, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_person_type text, visitor_person_sub_type text, visitor_status_id integer, visitor_status text)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
CAST(ROW_NUMBER() OVER (ORDER BY combined.entry_time, combined.id) AS INT) AS serial_id,
9
combined.visitor_type_id,
10
combined.visitor_type,
11
combined.id,
12
combined.first_name,
13
combined.last_name,
14
combined.entry_time,
15
combined.exit_time,
16
combined.visitor_person_type,
17
combined.visitor_person_sub_type,
18
combined.visitor_status_id,
19
combined.visitor_status_name
20
FROM (
21
-- Visitors
22
SELECT
23
vt.id AS visitor_type_id,
24
'visitor'::TEXT AS visitor_type,
25
v.id,
26
v.first_name::TEXT,
27
v.last_name::TEXT,
28
vl.entry_time,
29
vl.exit_time,
30
vt."name"::TEXT AS visitor_person_type,
31
NULL::TEXT AS visitor_person_sub_type,
32
CASE
33
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 3
34
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 4
35
ELSE vl.visitor_status_id
36
END AS visitor_status_id,
37
CASE
38
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 'Checked In'
39
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 'Checked Out'
40
ELSE vs."name"::TEXT
41
END AS visitor_status_name
42
FROM
43
public.visitor_logs vl
44
JOIN public.visitors v ON vl.visitor_id = v.id
45
JOIN public.multi_unit_visits muv ON muv.visitor_log_id = vl.id
46
JOIN public.visitor_types vt ON v.visitor_type_id = vt.id
47
LEFT JOIN public.visitor_statuses vs ON vl.visitor_status_id = vs.id
48
WHERE
49
muv.unit_id = p_unit_id
50
AND vl.entry_time BETWEEN p_start_date AND p_end_date
51
AND vl.is_deleted = FALSE
52
AND v.is_deleted = FALSE
53
AND muv.is_deleted = FALSE
54
UNION ALL
55
-- Service Providers
56
SELECT
57
vt_sp.id AS visitor_type_id,
58
'service_provider'::TEXT AS visitor_type,
59
sp.id,
60
sp.first_name::TEXT,
61
sp.last_name::TEXT,
62
spl.entry_time,
63
spl.exit_time,
64
spt."name"::TEXT AS visitor_person_type,
65
spst."name"::TEXT AS visitor_person_sub_type,
66
CASE
67
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 3
68
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 4
69
ELSE NULL
70
END AS visitor_status_id,
71
CASE
72
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 'Checked In'
73
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 'Checked Out'
74
ELSE NULL::TEXT
75
END AS visitor_status_name
76
FROM
77
public.service_provider_logs spl
78
JOIN public.service_providers sp ON spl.service_provider_id = sp.id
79
JOIN public.service_provider_types spt ON sp.service_provider_type_id = spt.id
80
JOIN public.service_provider_sub_types spst ON sp.service_provider_sub_type_id = spst.id
81
JOIN public.multi_unit_visits muv ON muv.unit_id = p_unit_id
82
LEFT JOIN public.visitor_types vt_sp ON vt_sp."name" = 'Service Provider' AND vt_sp.is_deleted = FALSE
83
WHERE
84
spl.entry_time BETWEEN p_start_date AND p_end_date
85
AND spl.is_deleted = FALSE
86
AND sp.is_deleted = FALSE
87
) AS combined;
88
END;
89
$function$
|
|||||
| Function | get_user_full_details | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_user_full_details(p_user_id uuid, p_apartment_id uuid)
2
RETURNS TABLE(user_id uuid, full_name character varying, email character varying, contact_number character varying, unit_name character varying, vehicles text[], address_line1 character varying, address_line2 character varying, zip_code character varying, country_name character varying, state_name character varying, city_name character varying, apartment_id uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
u.id AS user_id,
9
CONCAT(u.first_name, ' ', u.last_name)::VARCHAR AS full_name,
10
u.email::VARCHAR,
11
u.contact_number::VARCHAR,
12
un.name::VARCHAR AS unit_name,
13
ARRAY_AGG(DISTINCT CONCAT(vt.name, ' - ', v.vehicle_number))::TEXT[] AS vehicles,
14
a.address_line1::VARCHAR,
15
a.address_line2::VARCHAR,
16
a.zip_code::VARCHAR,
17
co.name::VARCHAR AS country_name,
18
s.name::VARCHAR AS state_name,
19
ci.name::VARCHAR AS city_name,
20
un.apartment_id
21
FROM
22
users u
23
INNER JOIN
24
residents r
25
ON u.id = r.user_id
26
LEFT JOIN
27
resident_units ru
28
ON r.id = ru.resident_id
29
AND ru.is_deleted = FALSE
30
LEFT JOIN
31
units un
32
ON ru.unit_id = un.id
33
AND un.apartment_id = p_apartment_id -- ✅ Filter by company
34
LEFT JOIN
35
vehicles v
36
ON un.id = v.unit_id
37
AND v.is_deleted = FALSE
38
LEFT JOIN
39
vehicle_types vt
40
ON v.vehicle_type_id = vt.id
41
LEFT JOIN
42
addresses a
43
ON r.permanent_address_id = a.id
44
LEFT JOIN
45
countries co
46
ON a.country_id = co.id
47
LEFT JOIN
48
states s
49
ON a.state_id = s.id
50
LEFT JOIN
51
cities ci
52
ON a.city_id = ci.id
53
WHERE
54
u.id = p_user_id
55
AND u.is_deleted = FALSE
56
AND r.is_deleted = FALSE
57
AND r.apartment_id = p_apartment_id -- ✅ Restrict resident scope to the company
58
GROUP BY
59
u.id, u.first_name, u.last_name, u.email, u.contact_number,
60
un.name, a.address_line1, a.address_line2, a.zip_code,
61
co.name, s.name, ci.name, un.apartment_id
62
ORDER BY
63
un.name;
64
END;
65
$function$
|
|||||
| Function | get_all_visitors_by_unit | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_visitors_by_unit(p_apartment_id uuid, p_unit_id integer)
2
RETURNS TABLE(id integer, first_name text, last_name text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name character varying, vehicle_number text, identity_type_id integer, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
v.id,
9
v.first_name,
10
v.last_name,
11
v.email,
12
v.visiting_from,
13
v.contact_number,
14
v.visitor_type_id,
15
vt.name AS visitor_type_name,
16
v.vehicle_number,
17
v.identity_type_id,
18
v.identity_number,
19
v.created_by,
20
v.created_on_utc,
21
v.modified_by,
22
v.modified_on_utc
23
FROM
24
public.visitors v
25
INNER JOIN
26
public.visitor_types vt ON v.visitor_type_id = vt.id
27
WHERE
28
v.apartment_id = p_apartment_id
29
AND v.unit_id = p_unit_id
30
AND v.is_deleted = false
31
AND (vt.is_deleted = false OR vt.is_deleted IS NULL);
32
END;
33
$function$
|
|||||
| Function | generate_ticket_number | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.generate_ticket_number(p_apartment_id uuid, p_category_id integer)
2
RETURNS text
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_today date := CURRENT_DATE;
7
v_short_code varchar(10);
8
v_last_number int;
9
v_formatted_date text;
10
v_formatted_sequence text;
11
v_ticket_number text;
12
BEGIN
13
-- 1️⃣ Fetch short code for the category
14
SELECT short_code INTO v_short_code
15
FROM public.ticket_categories
16
WHERE id = p_category_id AND is_deleted = false;
17
18
IF v_short_code IS NULL OR v_short_code = '' THEN
19
RAISE EXCEPTION 'ShortCode not found for category %', p_category_id;
20
END IF;
21
22
23
-- 2️⃣ Insert or update sequence (handles concurrency)
24
INSERT INTO public.ticket_number_sequences (apartment_id, ticket_date, category_id, last_number)
25
VALUES (p_apartment_id, v_today, p_category_id, 1)
26
ON CONFLICT (apartment_id, ticket_date, category_id)
27
DO UPDATE
28
SET last_number = ticket_number_sequences.last_number + 1
29
RETURNING last_number
30
INTO v_last_number;
31
32
33
-- 3️⃣ Format date as DDMMYY
34
v_formatted_date := to_char(v_today, 'DDMMYY');
35
36
-- 4️⃣ Format sequence as 2-digit (01, 02, 03…)
37
v_formatted_sequence := LPAD(v_last_number::text, 2, '0');
38
39
-- 5️⃣ Build final ticket number
40
v_ticket_number := v_formatted_date || '-' || v_short_code || '-' || v_formatted_sequence;
41
42
RETURN v_ticket_number;
43
END;
44
$function$
|
|||||
| Function | get_ticket_analytics | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_ticket_analytics(p_apartment_id uuid, p_from timestamp without time zone DEFAULT NULL::timestamp without time zone, p_to timestamp without time zone DEFAULT NULL::timestamp without time zone, p_recent_limit integer DEFAULT 5)
2
RETURNS jsonb
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
result jsonb;
7
BEGIN
8
WITH base AS (
9
SELECT *
10
FROM public.tickets t
11
WHERE t.apartment_id = p_apartment_id
12
AND NOT t.is_deleted
13
AND (p_from IS NULL OR t.created_on_utc >= p_from)
14
AND (p_to IS NULL OR t.created_on_utc <= p_to)
15
),
16
by_status AS (
17
SELECT
18
coalesce(t.ticket_status_id, 0) AS status_id,
19
count(*) AS cnt
20
FROM base t
21
GROUP BY coalesce(t.ticket_status_id, 0)
22
ORDER BY cnt DESC
23
),
24
by_priority AS (
25
SELECT
26
coalesce(t.ticket_priority_id, 0) AS priority_id,
27
count(*) AS cnt
28
FROM base t
29
GROUP BY coalesce(t.ticket_priority_id, 0)
30
ORDER BY cnt DESC
31
),
32
by_category AS (
33
SELECT
34
coalesce(t.ticket_category_id, 0) AS category_id,
35
count(*) AS cnt
36
FROM base t
37
GROUP BY coalesce(t.ticket_category_id, 0)
38
ORDER BY cnt DESC
39
),
40
recent AS (
41
SELECT
42
t.id,
43
t.ticket_number,
44
t.title,
45
t.ticket_status_id,
46
t.ticket_priority_id,
47
t.ticket_category_id,
48
t.is_on_hold,
49
t.created_on_utc
50
FROM base t
51
ORDER BY t.created_on_utc DESC
52
LIMIT p_recent_limit
53
)
54
SELECT jsonb_build_object(
55
'totalTickets', (SELECT count(*) FROM base),
56
'onHoldTickets', (SELECT count(*) FROM base WHERE is_on_hold),
57
'ticketsByStatus', COALESCE((
58
SELECT jsonb_agg(jsonb_build_object(
59
'id', s.status_id,
60
'name', ts.name,
61
'count', s.cnt
62
) ORDER BY s.cnt DESC)
63
FROM by_status s
64
LEFT JOIN public.ticket_statuses ts ON ts.id = s.status_id
65
), '[]'::jsonb),
66
'ticketsByPriority', COALESCE((
67
SELECT jsonb_agg(jsonb_build_object(
68
'id', p.priority_id,
69
'name', tp.name,
70
'count', p.cnt
71
) ORDER BY p.cnt DESC)
72
FROM by_priority p
73
LEFT JOIN public.ticket_priorities tp ON tp.id = p.priority_id
74
), '[]'::jsonb),
75
'ticketsByCategory', COALESCE((
76
SELECT jsonb_agg(jsonb_build_object(
77
'id', c.category_id,
78
'name', tc.name,
79
'count', c.cnt
80
) ORDER BY c.cnt DESC)
81
FROM by_category c
82
LEFT JOIN public.ticket_categories tc ON tc.id = c.category_id
83
), '[]'::jsonb),
84
'recentTickets', COALESCE((
85
SELECT jsonb_agg(row_to_json(r))
86
FROM (
87
SELECT
88
r.id,
89
r.ticket_number,
90
r.title,
91
COALESCE(ts.name, '') AS status,
92
COALESCE(tp.name, '') AS priority,
93
COALESCE(tc.name, '') AS category,
94
r.is_on_hold,
95
r.created_on_utc
96
FROM recent r
97
LEFT JOIN public.ticket_statuses ts ON ts.id = r.ticket_status_id
98
LEFT JOIN public.ticket_priorities tp ON tp.id = r.ticket_priority_id
99
LEFT JOIN public.ticket_categories tc ON tc.id = r.ticket_category_id
100
ORDER BY r.created_on_utc DESC
101
) r
102
), '[]'::jsonb)
103
) INTO result;
104
105
RETURN result;
106
END;
107
$function$
|
|||||
| Function | create_service_provider_api | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_service_provider_api(p_first_name text, p_last_name text, p_contact_number text, p_visitor_type_id integer, p_email text, p_visiting_from text, p_service_provider_type_id integer, p_service_provider_sub_type_id integer, p_vehicle_number text, p_identity_type_id integer, p_identity_number text, p_validity_date date, p_police_verification_status boolean, p_is_hireable boolean, p_is_visible boolean, p_is_frequent_visitor boolean, p_apartment_id uuid, p_pin text, p_created_by uuid, p_permanent_address_id uuid, p_present_address_id uuid)
2
RETURNS integer
3
LANGUAGE sql
4
AS $function$
5
SELECT service_provider_id
6
FROM public.create_service_provider(
7
p_first_name,
8
p_last_name,
9
p_contact_number,
10
p_visitor_type_id,
11
p_email,
12
p_visiting_from,
13
p_service_provider_type_id,
14
p_service_provider_sub_type_id,
15
p_vehicle_number,
16
p_identity_type_id,
17
p_identity_number,
18
p_validity_date,
19
p_police_verification_status,
20
p_is_hireable,
21
p_is_visible,
22
p_is_frequent_visitor,
23
p_apartment_id,
24
p_pin,
25
p_created_by,
26
p_permanent_address_id,
27
p_present_address_id
28
);
29
$function$
|
|||||
| Function | create_service_provider | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_service_provider(p_first_name character varying, p_last_name character varying, p_contact_number character varying, p_visitor_type_id integer, p_email character varying, p_visiting_from character varying, p_service_provider_type_id integer, p_service_provider_sub_type_id integer, p_vehicle_number character varying, p_identity_type_id integer, p_identity_number character varying, p_validity_date timestamp without time zone, p_police_verification_status boolean, p_is_hireable boolean, p_is_visible boolean, p_is_frequent_visitor boolean, p_apartment_id uuid, p_pin character varying, p_created_by uuid, p_permanent_address_id uuid, p_present_address_id uuid)
2
RETURNS TABLE(service_provider_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_visitor_id integer;
7
v_service_provider_id integer;
8
BEGIN
9
/* ----------------------------------------------------
10
1. Create Visitor
11
---------------------------------------------------- */
12
INSERT INTO visitors (
13
first_name,
14
last_name,
15
visitor_type_id,
16
created_by,
17
created_on_utc
18
)
19
VALUES (
20
p_first_name,
21
p_last_name,
22
p_visitor_type_id,
23
p_created_by,
24
now()
25
)
26
RETURNING id INTO v_visitor_id;
27
28
/* ----------------------------------------------------
29
2. Visitor Phone
30
---------------------------------------------------- */
31
INSERT INTO visitor_phones (
32
visitor_id,
33
phone_number,
34
is_active,
35
valid_from
36
)
37
VALUES (
38
v_visitor_id,
39
p_contact_number,
40
true,
41
now()
42
);
43
44
/* ----------------------------------------------------
45
3. Visitor Email (optional)
46
---------------------------------------------------- */
47
IF p_email IS NOT NULL THEN
48
INSERT INTO visitor_emails (
49
visitor_id,
50
email,
51
is_active,
52
valid_from
53
)
54
VALUES (
55
v_visitor_id,
56
p_email,
57
true,
58
now()
59
);
60
END IF;
61
62
/* ----------------------------------------------------
63
4. Visitor Identity (optional)
64
---------------------------------------------------- */
65
IF p_identity_type_id IS NOT NULL AND p_identity_number IS NOT NULL THEN
66
INSERT INTO visitor_identities (
67
visitor_id,
68
identity_type_id,
69
identity_number,
70
is_active,
71
created_by,
72
created_on_utc
73
)
74
VALUES (
75
v_visitor_id,
76
p_identity_type_id,
77
p_identity_number,
78
true,
79
p_created_by,
80
now()
81
);
82
END IF;
83
84
/* ----------------------------------------------------
85
5. Visitor Details (optional)
86
---------------------------------------------------- */
87
IF p_visiting_from IS NOT NULL OR p_vehicle_number IS NOT NULL THEN
88
INSERT INTO visitor_details (
89
visitor_id,
90
visiting_from,
91
vehicle_number,
92
created_by,
93
created_on_utc
94
)
95
VALUES (
96
v_visitor_id,
97
p_visiting_from,
98
p_vehicle_number,
99
p_created_by,
100
now()
101
);
102
END IF;
103
104
/* ----------------------------------------------------
105
6. Visitor ↔ Apartment mapping
106
---------------------------------------------------- */
107
INSERT INTO visitor_apartments (
108
visitor_id,
109
visitor_apartment_id,
110
first_seen,
111
is_blocked
112
)
113
VALUES (
114
v_visitor_id,
115
p_apartment_id,
116
now(),
117
false
118
);
119
120
/* ----------------------------------------------------
121
7. Create Service Provider
122
---------------------------------------------------- */
123
INSERT INTO service_providers (
124
service_provider_type_id,
125
service_provider_sub_type_id,
126
visitor_id,
127
is_frequent_visitor,
128
is_hireable,
129
is_visible,
130
police_verification_status,
131
pin,
132
valid_from,
133
created_by,
134
created_on_utc
135
)
136
VALUES (
137
p_service_provider_type_id,
138
p_service_provider_sub_type_id,
139
v_visitor_id,
140
p_is_frequent_visitor,
141
p_is_hireable,
142
p_is_visible,
143
p_police_verification_status,
144
p_pin,
145
p_validity_date,
146
p_created_by,
147
now()
148
)
149
RETURNING id INTO v_service_provider_id;
150
151
/* ----------------------------------------------------
152
8. Addresses (optional)
153
---------------------------------------------------- */
154
IF p_permanent_address_id IS NOT NULL THEN
155
INSERT INTO service_provider_addresses (
156
service_provider_id,
157
address_id,
158
is_permanent_address,
159
is_present_address
160
)
161
VALUES (
162
v_service_provider_id,
163
p_permanent_address_id,
164
true,
165
false
166
);
167
END IF;
168
169
IF p_present_address_id IS NOT NULL THEN
170
INSERT INTO service_provider_addresses (
171
service_provider_id,
172
address_id,
173
is_permanent_address,
174
is_present_address
175
)
176
VALUES (
177
v_service_provider_id,
178
p_present_address_id,
179
false,
180
true
181
);
182
END IF;
183
184
/* ----------------------------------------------------
185
9. Return
186
---------------------------------------------------- */
187
service_provider_id := v_service_provider_id;
188
RETURN;
189
END;
190
$function$
|
|||||
| Function | get_visitor_inside_counts | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_visitor_inside_counts(p_apartment_id uuid, p_today_only boolean)
2
RETURNS TABLE(id bigint, visitor_log_id bigint, visitor_id bigint, first_name text, last_name text, visitor_type_id integer, visitor_type_name text, entry_time timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
ROW_NUMBER() OVER (ORDER BY vl.entry_time ASC)::bigint AS id, -- Incremental ID
9
vl.id AS visitor_log_id,
10
v.id AS visitor_id,
11
v.first_name,
12
v.last_name,
13
vl.visitor_type_id,
14
vt.name::text AS visitor_type_name,
15
vl.entry_time
16
FROM
17
visitor_logs vl
18
INNER JOIN
19
visitors v ON v.id = vl.visitor_id
20
INNER JOIN
21
visitor_types vt ON vt.id = vl.visitor_type_id
22
WHERE
23
vl.apartment_id = p_apartment_id
24
AND vl.exit_time IS NULL
25
AND (NOT p_today_only OR vl.entry_time::date = CURRENT_DATE)
26
AND v.is_deleted = FALSE
27
AND vt.is_deleted = FALSE
28
ORDER BY
29
vl.entry_time ASC;
30
END;
31
$function$
|
|||||
| Function | get_all_visitors_by_apartment | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_visitors_by_apartment(p_apartment_id uuid)
1
CREATE OR REPLACE FUNCTION public.get_all_visitors_by_apartment(p_apartment_id uuid)
2
RETURNS TABLE(id bigint, first_name text, last_name text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name character varying, vehicle_number text, identity_type_id integer, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
2
RETURNS TABLE(id integer, first_name text, last_name text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name character varying, vehicle_number text, identity_type_id integer, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
BEGIN
5
BEGIN
6
RETURN QUERY
6
RETURN QUERY
7
SELECT
7
SELECT
8
v.id, -- this is bigint, matching the table definition
8
v.id,
9
v.first_name,
9
v.first_name,
10
v.last_name,
10
v.last_name,
11
ve.email,
11
v.email,
12
vd.visiting_from,
12
v.visiting_from,
13
vp.phone_number AS contact_number,
13
v.contact_number,
14
v.visitor_type_id,
14
v.visitor_type_id,
15
vt.name AS visitor_type_name,
15
vt.name AS visitor_type_name,
16
vd.vehicle_number,
16
v.vehicle_number,
17
vi.identity_type_id,
17
v.identity_type_id,
18
vi.identity_number,
18
v.identity_number,
19
v.created_by,
19
v.created_by,
20
v.created_on_utc,
20
v.created_on_utc,
21
v.modified_by,
21
v.modified_by,
22
v.modified_on_utc
22
v.modified_on_utc
23
FROM
23
FROM
24
public.visitors v
24
public.visitors v
25
INNER JOIN
25
INNER JOIN
26
public.visitor_types vt ON v.visitor_type_id = vt.id
26
public.visitor_types vt ON v.visitor_type_id = vt.id
27
LEFT JOIN
28
public.visitor_emails ve ON v.id = ve.visitor_id AND ve.is_active = TRUE
29
LEFT JOIN
30
public.visitor_details vd ON v.id = vd.visitor_id
31
LEFT JOIN
32
public.visitor_phones vp ON v.id = vp.visitor_id AND vp.is_active = TRUE
33
LEFT JOIN
34
public.visitor_identities vi ON v.id = vi.visitor_id AND vi.is_active = TRUE
35
INNER JOIN
36
public.visitor_apartments va ON v.id = va.visitor_id
37
WHERE
27
WHERE
38
va.visitor_apartment_id = p_apartment_id
28
v.apartment_id = p_apartment_id
39
AND v.is_deleted = FALSE
29
AND v.is_deleted = false
40
AND (vt.is_deleted = FALSE OR vt.is_deleted IS NULL);
30
AND (vt.is_deleted = false OR vt.is_deleted IS NULL);
41
END;
31
END;
42
$function$
32
$function$
|
|||||
| Function | get_service_provider_companies | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_service_provider_companies(p_category_id integer DEFAULT NULL::integer, p_subtype_id integer DEFAULT NULL::integer)
2
RETURNS TABLE(id integer, name text, description text)
3
LANGUAGE sql
4
AS $function$
5
SELECT spc.id, spc.name, spc.description
6
FROM public.service_provider_companies spc
7
WHERE
8
(p_category_id IS NULL OR spc.category_id = p_category_id)
9
AND (p_subtype_id IS NULL OR spc.subtype_id = p_subtype_id);
10
$function$
|
|||||
| Function | create_visitor | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_visitor(p_first_name character varying, p_last_name character varying, p_contact_number character varying, p_visitor_type_id integer, p_email character varying, p_visiting_from character varying, p_vehicle_number character varying, p_identity_type_id integer, p_identity_number character varying, p_apartment_id uuid, p_created_by uuid, p_delivery_company_id integer, p_image_url character varying)
2
RETURNS bigint
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_visitor_id BIGINT;
7
BEGIN
8
-- Insert into the visitors table
9
INSERT INTO public.visitors (
10
first_name,
11
last_name,
12
created_on_utc,
13
created_by,
14
is_active,
15
visitor_type_id,
16
image_url
17
)
18
VALUES (
19
p_first_name,
20
p_last_name,
21
current_timestamp,
22
p_created_by,
23
TRUE,
24
p_visitor_type_id,
25
p_image_url
26
)
27
RETURNING id INTO v_visitor_id; -- Store the newly created visitor's ID
28
29
-- Insert into visitor_apartments table
30
INSERT INTO public.visitor_apartments (
31
visitor_id,
32
visitor_apartment_id,
33
first_seen,
34
is_blocked
35
)
36
VALUES (
37
v_visitor_id,
38
p_apartment_id,
39
current_timestamp, -- Set first_seen as the current timestamp
40
FALSE -- Default is_blocked to FALSE
41
);
42
43
-- Insert into visitor_details table (if visiting_from or vehicle_number is provided)
44
IF p_visiting_from IS NOT NULL AND TRIM(p_visiting_from) <> '' OR p_vehicle_number IS NOT NULL AND TRIM(p_vehicle_number) <> '' THEN
45
INSERT INTO public.visitor_details (
46
visitor_id,
47
visiting_from,
48
vehicle_number,
49
created_by,
50
created_on_utc
51
) VALUES (
52
v_visitor_id,
53
p_visiting_from,
54
p_vehicle_number,
55
p_created_by,
56
current_timestamp
57
);
58
END IF;
59
60
-- Insert into visitor_phones table
61
INSERT INTO public.visitor_phones (
62
visitor_id,
63
phone_number,
64
is_active,
65
valid_from
66
) VALUES (
67
v_visitor_id,
68
p_contact_number,
69
TRUE,
70
current_timestamp
71
);
72
73
-- Insert into visitor_emails table (if email is provided and not an empty string)
74
IF p_email IS NOT NULL AND TRIM(p_email) <> '' THEN
75
INSERT INTO public.visitor_emails (
76
visitor_id,
77
email,
78
is_active,
79
valid_from
80
) VALUES (
81
v_visitor_id,
82
p_email,
83
TRUE,
84
current_timestamp
85
);
86
END IF;
87
88
-- Insert into visitor_identities table (if identity number is provided and not an empty string)
89
IF p_identity_number IS NOT NULL AND TRIM(p_identity_number) <> '' THEN
90
INSERT INTO public.visitor_identities (
91
visitor_id,
92
identity_type_id,
93
identity_number,
94
is_active,
95
valid_from,
96
created_by,
97
created_on_utc
98
) VALUES (
99
v_visitor_id,
100
p_identity_type_id,
101
p_identity_number,
102
TRUE,
103
current_timestamp,
104
p_created_by,
105
current_timestamp
106
);
107
END IF;
108
109
-- Insert into visitor_delivery_company table (if delivery_company_id is provided and not NULL)
110
IF p_delivery_company_id IS NOT NULL AND p_delivery_company_id > 0 THEN
111
INSERT INTO public.visitor_delivery_company (
112
visitor_id,
113
delivery_company_id,
114
is_active,
115
valid_from
116
) VALUES (
117
v_visitor_id,
118
p_delivery_company_id,
119
TRUE,
120
current_timestamp
121
);
122
END IF;
123
124
-- Return the newly created visitor ID
125
RETURN v_visitor_id;
126
END;
127
$function$
|
|||||
| Function | create_visitor_with_pending_approval | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_visitor_with_pending_approval(p_apartment_id uuid, p_first_name text, p_last_name text, p_image_url text, p_contact_number text, p_visitor_type_id integer, p_unit_ids integer[], p_created_by uuid)
2
RETURNS integer
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_visitor_id BIGINT;
7
v_visitor_log_id BIGINT;
8
v_unit_id INTEGER;
9
v_pending_status CONSTANT INTEGER := 1; -- Pending
10
BEGIN
11
/*
12
* 1. Find visitor by active phone number
13
*/
14
SELECT vp.visitor_id
15
INTO v_visitor_id
16
FROM visitor_phones vp
17
JOIN visitors v ON v.id = vp.visitor_id
18
WHERE vp.phone_number = p_contact_number
19
AND vp.is_active = TRUE
20
AND v.is_deleted = FALSE
21
LIMIT 1;
22
23
/*
24
* 2. Create visitor if not exists
25
*/
26
IF NOT FOUND THEN
27
INSERT INTO visitors (
28
first_name,
29
last_name,
30
image_url,
31
visitor_type_id,
32
created_on_utc,
33
created_by
34
)
35
VALUES (
36
p_first_name,
37
p_last_name,
38
p_image_url,
39
p_visitor_type_id,
40
NOW(),
41
p_created_by
42
)
43
RETURNING id INTO v_visitor_id;
44
45
INSERT INTO visitor_phones (
46
visitor_id,
47
phone_number,
48
is_active,
49
valid_from
50
)
51
VALUES (
52
v_visitor_id,
53
p_contact_number,
54
TRUE,
55
NOW()
56
);
57
END IF;
58
59
/*
60
* 3. Ensure apartment association exists
61
*/
62
IF NOT EXISTS (
63
SELECT 1
64
FROM visitor_apartments
65
WHERE visitor_id = v_visitor_id
66
AND visitor_apartment_id = p_apartment_id
67
) THEN
68
INSERT INTO visitor_apartments (
69
visitor_id,
70
visitor_apartment_id,
71
first_seen
72
)
73
VALUES (
74
v_visitor_id,
75
p_apartment_id,
76
NOW()
77
);
78
END IF;
79
80
/*
81
* 4. Insert visitor log with PENDING status
82
*/
83
INSERT INTO visitor_logs (
84
visitor_id,
85
apartment_id,
86
visitor_type_id,
87
entry_time,
88
created_on_utc,
89
created_by,
90
visitor_status_id
91
)
92
VALUES (
93
v_visitor_id,
94
p_apartment_id,
95
p_visitor_type_id,
96
NULL, -- Entry happens later
97
NOW(),
98
p_created_by,
99
v_pending_status
100
)
101
RETURNING id INTO v_visitor_log_id;
102
103
/*
104
* 5. Insert multi-unit visits with PENDING status
105
*/
106
FOREACH v_unit_id IN ARRAY p_unit_ids LOOP
107
INSERT INTO multi_unit_visits (
108
visitor_log_id,
109
unit_id,
110
visitor_statuses,
111
created_on_utc,
112
is_deleted,
113
created_by
114
)
115
VALUES (
116
v_visitor_log_id,
117
v_unit_id,
118
v_pending_status,
119
NOW(),
120
FALSE,
121
p_created_by
122
);
123
END LOOP;
124
125
RETURN v_visitor_log_id;
126
END;
127
$function$
|
|||||
| Function | get_all_tickets | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_tickets(p_apartment_id uuid)
2
RETURNS TABLE(id uuid, unit_id integer, unit text, title text, description text, ticket_category_id integer, ticket_category text, ticket_priority_id integer, ticket_priority text, ticket_status_id integer, sort_order integer, ticket_status text, ticket_assigned_to integer, ticket_assigned text, ticket_assignee_phone text, is_on_hold boolean, is_re_opened boolean, ticket_number text, ticket_for_id integer, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
SELECT
7
t.id,
8
t.unit_id,
9
u.name AS unit,
10
t.title,
11
t.description,
12
t.ticket_category_id,
13
tc.name AS ticket_category,
14
t.ticket_priority_id,
15
tp.name AS ticket_priority,
16
t.ticket_status_id,
17
t.sort_order,
18
ts.name AS ticket_status,
19
t.ticket_assigned_to,
20
COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''),'Not Assigned' ) AS ticket_assigned,
21
vp.phone_number AS ticket_assignee_phone,
22
t.is_on_hold,
23
t.is_re_opened,
24
t.ticket_number,
25
t.ticket_for_id,
26
t.created_by,
27
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS created_by_name,
28
t.created_on_utc,
29
t.modified_by,
30
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS modified_by_name,
31
t.modified_on_utc
32
FROM tickets t
33
JOIN units u
34
ON u.id = t.unit_id
35
AND u.is_deleted = false
36
JOIN ticket_categories tc
37
ON tc.id = t.ticket_category_id
38
AND tc.is_deleted = false
39
JOIN ticket_priorities tp
40
ON tp.id = t.ticket_priority_id
41
AND tp.is_deleted = false
42
JOIN ticket_statuses ts
43
ON ts.id = t.ticket_status_id
44
AND ts.is_deleted = false
45
LEFT JOIN service_providers sp
46
ON sp.id = t.ticket_assigned_to
47
AND sp.is_deleted = false
48
LEFT JOIN visitors v
49
ON v.id = sp.visitor_id
50
AND v.is_deleted = false
51
LEFT JOIN visitor_phones vp
52
ON vp.visitor_id = v.id
53
AND vp.is_active = TRUE
54
LEFT JOIN users uc
55
ON uc.id = t.created_by
56
LEFT JOIN users um
57
ON um.id = t.modified_by
58
59
WHERE
60
t.apartment_id = p_apartment_id
61
AND t.is_deleted = false
62
ORDER BY
63
t.ticket_status_id,
64
t.sort_order,
65
t.created_on_utc DESC;
66
$function$
|
|||||
| Function | get_all_tickets | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_tickets(p_apartment_id uuid, p_from_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_to_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_service_provider_id integer DEFAULT NULL::integer, p_is_active boolean DEFAULT false)
2
RETURNS TABLE(id uuid, unit_id integer, unit text, title text, description text, ticket_category_id integer, ticket_category text, ticket_priority_id integer, ticket_priority text, ticket_status_id integer, sort_order integer, ticket_status text, ticket_assigned_to integer, ticket_assigned text, ticket_assignee_phone text, is_on_hold boolean, is_re_opened boolean, ticket_number text, ticket_for_id integer, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
SELECT
7
t.id,
8
t.unit_id,
9
u.name AS unit,
10
t.title,
11
t.description,
12
t.ticket_category_id,
13
tc.name AS ticket_category,
14
t.ticket_priority_id,
15
tp.name AS ticket_priority,
16
t.ticket_status_id,
17
t.sort_order,
18
ts.name AS ticket_status,
19
t.ticket_assigned_to,
20
--COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''),'Not Assigned') AS ticket_assigned,
21
--NULLIF(concat_ws(' ', v.first_name, v.last_name), '') AS ticket_assigned,
22
COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''), NULL) AS ticket_assigned,
23
vp.phone_number AS ticket_assignee_phone,
24
t.is_on_hold,
25
t.is_re_opened,
26
t.ticket_number,
27
t.ticket_for_id,
28
t.created_by,
29
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS created_by_name,
30
t.created_on_utc,
31
t.modified_by,
32
COALESCE(NULLIF(TRIM(um.first_name || ' ' || um.last_name), ''), NULL) AS modified_by_name,
33
t.modified_on_utc
34
FROM tickets t
35
JOIN units u
36
ON u.id = t.unit_id
37
AND u.is_deleted = false
38
JOIN ticket_categories tc
39
ON tc.id = t.ticket_category_id
40
AND tc.is_deleted = false
41
JOIN ticket_priorities tp
42
ON tp.id = t.ticket_priority_id
43
AND tp.is_deleted = false
44
JOIN ticket_statuses ts
45
ON ts.id = t.ticket_status_id
46
AND ts.is_deleted = false
47
LEFT JOIN service_providers sp
48
ON sp.id = t.ticket_assigned_to
49
AND sp.is_deleted = false
50
LEFT JOIN visitors v
51
ON v.id = sp.visitor_id
52
AND v.is_deleted = false
53
LEFT JOIN visitor_phones vp
54
ON vp.visitor_id = v.id
55
AND vp.is_active = true
56
LEFT JOIN users uc
57
ON uc.id = t.created_by
58
LEFT JOIN users um
59
ON um.id = t.modified_by
60
WHERE
61
t.apartment_id = p_apartment_id
62
AND t.is_deleted = false
63
64
-- Date filters
65
AND (p_from_datetime IS NULL OR t.created_on_utc >= p_from_datetime)
66
AND (p_to_datetime IS NULL OR t.created_on_utc <= p_to_datetime)
67
68
-- Service provider filter
69
AND (p_service_provider_id IS NULL OR t.ticket_assigned_to = p_service_provider_id)
70
71
-- Active / All status filter
72
AND (
73
p_is_active = false
74
OR ts.name IN (
75
'In Queue',
76
'Assigned',
77
'In Progress',
78
'Reopened'
79
)
80
)
81
ORDER BY
82
t.ticket_status_id,
83
t.sort_order,
84
t.created_on_utc DESC;
85
$function$
|
|||||
| Function | get_defaulter_contact | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_defaulter_contact(p_customer_id uuid)
2
RETURNS text
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_contact_number text;
7
BEGIN
8
SELECT
9
r.contact_number INTO v_contact_number
10
FROM
11
public.units u
12
INNER JOIN
13
public.resident_units ru ON u.id = ru.unit_id
14
INNER JOIN
15
public.residents r ON ru.resident_id = r.id
16
WHERE
17
u.customer_id = p_customer_id
18
AND u.is_deleted = false
19
AND ru.is_deleted = false
20
AND r.is_deleted = false
21
AND r.contact_number IS NOT NULL
22
LIMIT 1;
23
24
RETURN v_contact_number;
25
END;
26
$function$
|
|||||
| Function | get_service_provider_subcategories | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_service_provider_subcategories(p_category_id integer)
2
RETURNS TABLE(id integer, name text, description text)
3
LANGUAGE sql
4
AS $function$
5
SELECT sps.id, sps.name, sps.description
6
FROM public.service_provider_company_subtypes sps
7
WHERE sps.category_id = p_category_id;
8
$function$
|
|||||
| Function | get_all_escalated_tickets | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_escalated_tickets(p_apartment_id uuid, p_from_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_to_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_service_provider_id integer DEFAULT NULL::integer, p_is_active boolean DEFAULT false)
2
RETURNS TABLE(id uuid, unit_id integer, unit text, title text, description text, ticket_category_id integer, ticket_category text, ticket_priority_id integer, ticket_priority text, ticket_status_id integer, sort_order integer, ticket_status text, ticket_assigned_to integer, ticket_assigned text, ticket_assignee_phone text, is_on_hold boolean, is_re_opened boolean, ticket_number text, ticket_for_id integer, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
3
LANGUAGE sql
4
STABLE
5
AS $function$
6
WITH latest_ticket_log AS (
7
SELECT DISTINCT ON (tl.ticket_id)
8
tl.ticket_id,
9
tl.is_escalated
10
FROM ticket_logs tl
11
ORDER BY tl.ticket_id, tl.created_on_utc DESC
12
)
13
SELECT
14
t.id,
15
t.unit_id,
16
u.name AS unit,
17
t.title,
18
t.description,
19
t.ticket_category_id,
20
tc.name AS ticket_category,
21
t.ticket_priority_id,
22
tp.name AS ticket_priority,
23
t.ticket_status_id,
24
t.sort_order,
25
ts.name AS ticket_status,
26
t.ticket_assigned_to,
27
COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''), NULL) AS ticket_assigned,
28
vp.phone_number AS ticket_assignee_phone,
29
t.is_on_hold,
30
t.is_re_opened,
31
t.ticket_number,
32
t.ticket_for_id,
33
t.created_by,
34
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS created_by_name,
35
t.created_on_utc,
36
t.modified_by,
37
COALESCE(NULLIF(TRIM(um.first_name || ' ' || um.last_name), ''), NULL) AS modified_by_name,
38
t.modified_on_utc
39
FROM tickets t
40
JOIN latest_ticket_log ltl
41
ON ltl.ticket_id = t.id
42
AND ltl.is_escalated = true
43
JOIN units u
44
ON u.id = t.unit_id
45
AND u.is_deleted = false
46
JOIN ticket_categories tc
47
ON tc.id = t.ticket_category_id
48
AND tc.is_deleted = false
49
JOIN ticket_priorities tp
50
ON tp.id = t.ticket_priority_id
51
AND tp.is_deleted = false
52
JOIN ticket_statuses ts
53
ON ts.id = t.ticket_status_id
54
AND ts.is_deleted = false
55
LEFT JOIN service_providers sp
56
ON sp.id = t.ticket_assigned_to
57
AND sp.is_deleted = false
58
LEFT JOIN visitors v
59
ON v.id = sp.visitor_id
60
AND v.is_deleted = false
61
LEFT JOIN visitor_phones vp
62
ON vp.visitor_id = v.id
63
AND vp.is_active = true
64
LEFT JOIN users uc
65
ON uc.id = t.created_by
66
LEFT JOIN users um
67
ON um.id = t.modified_by
68
WHERE
69
t.apartment_id = p_apartment_id
70
AND t.is_deleted = false
71
72
-- Date filters
73
AND (p_from_datetime IS NULL OR t.created_on_utc >= p_from_datetime)
74
AND (p_to_datetime IS NULL OR t.created_on_utc <= p_to_datetime)
75
76
-- Service provider filter
77
AND (p_service_provider_id IS NULL OR t.ticket_assigned_to = p_service_provider_id)
78
79
-- Active / All status filter
80
AND (
81
p_is_active = false
82
OR ts.name IN (
83
'In Queue',
84
'Assigned',
85
'In Progress',
86
'Reopened'
87
)
88
)
89
ORDER BY
90
t.ticket_status_id,
91
t.sort_order,
92
t.created_on_utc DESC;
93
$function$
|
|||||
| Function | visitor_approval_fcm_tokens_by_visitor_log_id | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.visitor_approval_fcm_tokens_by_visitor_log_id(p_visitorlogid bigint)
2
RETURNS TABLE(id integer, user_ids uuid[], visitor_id integer, first_name text, last_name text, fcm_tokens text[], device_ids text[])
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
ROW_NUMBER() OVER ()::INT AS id,
7
ARRAY_AGG(DISTINCT r.user_id) AS user_ids,
8
v.id AS visitor_id,
9
v.first_name,
10
v.last_name,
11
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.fcm_token), NULL) AS fcm_tokens,
12
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.device_id), NULL) AS device_ids
13
FROM multi_unit_visits muv
14
INNER JOIN resident_units ru ON muv.unit_id = ru.unit_id
15
INNER JOIN residents r ON ru.resident_id = r.id
16
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
17
INNER JOIN visitors v ON v.id = vl.visitor_id
18
LEFT JOIN user_fcm_tokens uft ON uft.user_id = r.user_id
19
WHERE muv.visitor_log_id = p_visitorlogid
20
AND muv.is_deleted = FALSE
21
AND ru.is_deleted = FALSE
22
AND r.is_deleted = FALSE
23
GROUP BY v.id, v.first_name, v.last_name;
24
$function$
|
|||||
| Function | create_visitor_with_pending_approval1 | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_visitor_with_pending_approval1(p_apartment_id uuid, p_first_name text, p_last_name text, p_image_url text, p_contact_number text, p_visitor_type_id integer, p_unit_ids integer[], p_created_by uuid, p_sp_category_id integer DEFAULT NULL::integer, p_sp_subtype_id integer DEFAULT NULL::integer, p_sp_company_id integer DEFAULT NULL::integer)
2
RETURNS integer
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_visitor_id BIGINT;
7
v_visitor_log_id BIGINT;
8
v_unit_id INTEGER;
9
v_pending_status CONSTANT INTEGER := 1;
10
BEGIN
11
-- 1. Find visitor by phone
12
SELECT vp.visitor_id
13
INTO v_visitor_id
14
FROM visitor_phones vp
15
JOIN visitors v ON v.id = vp.visitor_id
16
WHERE vp.phone_number = p_contact_number
17
AND vp.is_active = TRUE
18
AND v.is_deleted = FALSE
19
LIMIT 1;
20
21
-- 2. Create visitor if not exists
22
IF NOT FOUND THEN
23
INSERT INTO visitors (
24
first_name,
25
last_name,
26
image_url,
27
visitor_type_id,
28
created_on_utc,
29
created_by
30
)
31
VALUES (
32
p_first_name,
33
p_last_name,
34
p_image_url,
35
p_visitor_type_id,
36
NOW(),
37
p_created_by
38
)
39
RETURNING id INTO v_visitor_id;
40
41
INSERT INTO visitor_phones (
42
visitor_id,
43
phone_number,
44
is_active,
45
valid_from
46
)
47
VALUES (
48
v_visitor_id,
49
p_contact_number,
50
TRUE,
51
NOW()
52
);
53
END IF;
54
55
-- 3. Apartment mapping
56
IF NOT EXISTS (
57
SELECT 1 FROM visitor_apartments
58
WHERE visitor_id = v_visitor_id
59
AND visitor_apartment_id = p_apartment_id
60
) THEN
61
INSERT INTO visitor_apartments (
62
visitor_id,
63
visitor_apartment_id,
64
first_seen
65
)
66
VALUES (
67
v_visitor_id,
68
p_apartment_id,
69
NOW()
70
);
71
END IF;
72
73
-- 4. Visitor log
74
INSERT INTO visitor_logs (
75
visitor_id,
76
apartment_id,
77
visitor_type_id,
78
entry_time,
79
created_on_utc,
80
created_by,
81
visitor_status_id
82
)
83
VALUES (
84
v_visitor_id,
85
p_apartment_id,
86
p_visitor_type_id,
87
NULL,
88
NOW(),
89
p_created_by,
90
v_pending_status
91
)
92
RETURNING id INTO v_visitor_log_id;
93
94
-- 5. Multi-unit visits
95
FOREACH v_unit_id IN ARRAY p_unit_ids LOOP
96
INSERT INTO multi_unit_visits (
97
visitor_log_id,
98
unit_id,
99
visitor_statuses,
100
created_on_utc,
101
is_deleted,
102
created_by
103
)
104
VALUES (
105
v_visitor_log_id,
106
v_unit_id,
107
v_pending_status,
108
NOW(),
109
FALSE,
110
p_created_by
111
);
112
END LOOP;
113
114
-- 6. OPTIONAL service provider company info
115
IF p_sp_company_id IS NOT NULL THEN
116
INSERT INTO visitor_sp_company_visits (
117
visitor_log_id,
118
sp_company_category_id,
119
sp_company_subtype_id,
120
sp_company_id
121
)
122
VALUES (
123
v_visitor_log_id,
124
p_sp_category_id,
125
p_sp_subtype_id,
126
p_sp_company_id
127
);
128
END IF;
129
130
RETURN v_visitor_log_id;
131
END;
132
$function$
|
|||||
| Function | create_visitor_with_pending_approval | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_visitor_with_pending_approval(p_apartment_id uuid, p_first_name text, p_last_name text, p_image_url text, p_contact_number text, p_visitor_type_id integer, p_unit_ids integer[], p_created_by uuid, p_sp_category_id integer DEFAULT NULL::integer, p_sp_subtype_id integer DEFAULT NULL::integer, p_sp_company_id integer DEFAULT NULL::integer)
2
RETURNS integer
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_visitor_id BIGINT;
7
v_visitor_log_id BIGINT;
8
v_unit_id INTEGER;
9
v_pending_status CONSTANT INTEGER := 1;
10
BEGIN
11
-- 1. Find visitor by phone
12
SELECT vp.visitor_id
13
INTO v_visitor_id
14
FROM visitor_phones vp
15
JOIN visitors v ON v.id = vp.visitor_id
16
WHERE vp.phone_number = p_contact_number
17
AND vp.is_active = TRUE
18
AND v.is_deleted = FALSE
19
LIMIT 1;
20
21
-- 2. Create visitor if not exists
22
IF NOT FOUND THEN
23
INSERT INTO visitors (
24
first_name,
25
last_name,
26
image_url,
27
visitor_type_id,
28
created_on_utc,
29
created_by
30
)
31
VALUES (
32
p_first_name,
33
p_last_name,
34
p_image_url,
35
p_visitor_type_id,
36
NOW(),
37
p_created_by
38
)
39
RETURNING id INTO v_visitor_id;
40
41
INSERT INTO visitor_phones (
42
visitor_id,
43
phone_number,
44
is_active,
45
valid_from
46
)
47
VALUES (
48
v_visitor_id,
49
p_contact_number,
50
TRUE,
51
NOW()
52
);
53
END IF;
54
55
-- 3. Apartment mapping
56
IF NOT EXISTS (
57
SELECT 1 FROM visitor_apartments
58
WHERE visitor_id = v_visitor_id
59
AND visitor_apartment_id = p_apartment_id
60
) THEN
61
INSERT INTO visitor_apartments (
62
visitor_id,
63
visitor_apartment_id,
64
first_seen
65
)
66
VALUES (
67
v_visitor_id,
68
p_apartment_id,
69
NOW()
70
);
71
END IF;
72
73
-- 4. Visitor log
74
INSERT INTO visitor_logs (
75
visitor_id,
76
apartment_id,
77
visitor_type_id,
78
entry_time,
79
created_on_utc,
80
created_by,
81
visitor_status_id
82
)
83
VALUES (
84
v_visitor_id,
85
p_apartment_id,
86
p_visitor_type_id,
87
NULL,
88
NOW(),
89
p_created_by,
90
v_pending_status
91
)
92
RETURNING id INTO v_visitor_log_id;
93
94
-- 5. Multi-unit visits
95
FOREACH v_unit_id IN ARRAY p_unit_ids LOOP
96
INSERT INTO multi_unit_visits (
97
visitor_log_id,
98
unit_id,
99
visitor_statuses,
100
created_on_utc,
101
is_deleted,
102
created_by
103
)
104
VALUES (
105
v_visitor_log_id,
106
v_unit_id,
107
v_pending_status,
108
NOW(),
109
FALSE,
110
p_created_by
111
);
112
END LOOP;
113
114
-- 6. OPTIONAL service provider company info
115
IF p_sp_company_id IS NOT NULL THEN
116
INSERT INTO visitor_sp_company_visits (
117
visitor_log_id,
118
sp_company_category_id,
119
sp_company_subtype_id,
120
sp_company_id
121
)
122
VALUES (
123
v_visitor_log_id,
124
p_sp_category_id,
125
p_sp_subtype_id,
126
p_sp_company_id
127
);
128
END IF;
129
130
RETURN v_visitor_log_id;
131
END;
132
$function$
|
|||||
| Procedure | purge_community_organization_data | Match | ||||||
| Procedure | create_vehicle | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.create_vehicle(IN p_vehicle_number text, IN p_vehicle_type_id integer, IN p_unit_id integer, IN p_vehicle_rf_id text, IN p_vehicle_rf_id_secretcode text, IN p_created_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
BEGIN
5
INSERT INTO public.vehicles (
6
vehicle_number,
7
vehicle_type_id,
8
unit_id,
9
vehicle_rf_id,
10
vehicle_rf_id_secretcode,
11
created_on_utc,
12
is_deleted,
13
created_by
14
) VALUES (
15
p_vehicle_number,
16
p_vehicle_type_id,
17
p_unit_id,
18
p_vehicle_rf_id,
19
p_vehicle_rf_id_secretcode,
20
now() at time zone 'utc',
21
false,
22
p_created_by
23
);
24
END;
25
$procedure$
|
|||||
| Procedure | delete_residents_by_apartment | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.delete_residents_by_apartment(IN p_apartment_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
BEGIN
5
DELETE FROM public.resident_units
6
WHERE resident_id IN (
7
SELECT id FROM public.residents
8
WHERE apartment_id = p_apartment_id
9
);
10
11
DELETE FROM public.residents
12
WHERE apartment_id = p_apartment_id;
13
END;
14
$procedure$
|
|||||
| Procedure | hard_delete_apartment_data | Match | ||||||
| Procedure | hard_delete_org_community | Match | ||||||
| Procedure | hard_delete_org_inventory | Match | ||||||
| Procedure | hard_delete_org_purchase | Match | ||||||
| Procedure | hard_delete_org_sales | Match | ||||||
| Procedure | insert_visitor_vehicle | Match | ||||||
| Procedure | upsert_meeting_action_items | Match | ||||||
| Procedure | upsert_meeting_agenda_items | Match | ||||||
| Procedure | upsert_meeting_participants | Match | ||||||
| Procedure | create_service_provider | Match | ||||||
| Procedure | hard_delete_organization | Match | ||||||
| Procedure | initialize_organization | Match | ||||||
| Procedure | update_ticket_status | Match | ||||||
| Procedure | upsert_meeting_note | Match | ||||||
| Procedure | initialize_company | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.initialize_company(IN p_company_id uuid, IN p_organization_id uuid, IN p_is_apartment boolean, IN p_name text, IN p_created_by uuid)
1
CREATE OR REPLACE PROCEDURE public.initialize_company(IN p_company_id uuid, IN p_organization_id uuid, IN p_is_apartment boolean, IN p_name text, IN p_created_by uuid)
2
LANGUAGE plpgsql
2
LANGUAGE plpgsql
3
AS $procedure$
3
AS $procedure$
4
4
5
6
BEGIN
5
BEGIN
7
6
8
-- Insert into companies table
7
-- Insert into companies table
9
INSERT INTO public.companies (
8
INSERT INTO public.companies (
10
id,
9
id,
11
organization_id,
10
organization_id,
12
is_apartment,
11
is_apartment,
13
name,
12
name,
14
created_on_utc,
13
created_on_utc,
15
created_by
14
created_by
16
) VALUES (
15
) VALUES (
17
p_company_id,
16
p_company_id,
18
p_organization_id,
17
p_organization_id,
19
p_is_apartment,
18
p_is_apartment,
20
p_name,
21
NOW(),
22
p_created_by
23
);
24
25
-- If this company is an apartment, insert into apartments table as well
26
IF p_is_apartment THEN
27
INSERT INTO public.apartments (
28
id,
29
organization_id,
30
name,
31
apartment_type_id,
32
created_on_utc,
33
created_by
34
) VALUES (
35
p_company_id,
36
p_organization_id,
37
p_name,
19
p_name,
38
1, -- default apartment_type_id
39
NOW(),
20
NOW(),
40
p_created_by
21
p_created_by
41
);
22
);
42
END IF;
43
23
44
END;
24
END;
45
$procedure$
25
$procedure$
|
|||||
| Procedure | update_ticket_status_with_logs | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.update_ticket_status_with_logs(IN p_apartment_id uuid, IN p_ticket_status_id integer, IN p_ticket_ids uuid[], IN p_modified_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_ticket RECORD;
6
v_old_status int;
7
v_is_on_hold bool;
8
v_new_log_id uuid;
9
BEGIN
10
-- Process each ticket in the given list
11
FOR v_ticket IN
12
SELECT id, ticket_status_id, is_on_hold
13
FROM public.tickets
14
WHERE id = ANY(p_ticket_ids)
15
LOOP
16
v_old_status := v_ticket.ticket_status_id;
17
v_is_on_hold := v_ticket.is_on_hold;
18
19
-- Validate status transition
20
IF EXISTS (
21
SELECT 1
22
FROM public.ticket_workflow
23
WHERE status = v_old_status
24
AND next_status = p_ticket_status_id
25
AND apartment_id = p_apartment_id
26
) THEN
27
28
-- Update the ticket
29
UPDATE public.tickets
30
SET ticket_status_id = p_ticket_status_id,
31
modified_by = p_modified_by,
32
modified_on_utc = now()
33
WHERE id = v_ticket.id;
34
35
-- Insert log entry
36
v_new_log_id := gen_random_uuid();
37
38
INSERT INTO public.ticket_logs (
39
id,
40
ticket_id,
41
old_status_id,
42
new_status_id,
43
is_on_hold,
44
created_by,
45
created_on_utc
46
)
47
VALUES (
48
v_new_log_id,
49
v_ticket.id,
50
v_old_status,
51
p_ticket_status_id,
52
v_is_on_hold,
53
p_modified_by,
54
now()
55
);
56
57
RAISE NOTICE 'Ticket % updated from % to %', v_ticket.id, v_old_status, p_ticket_status_id;
58
59
ELSE
60
RAISE WARNING 'Invalid status transition for Ticket ID: % (old=% new=%)',
61
v_ticket.id, v_old_status, p_ticket_status_id;
62
END IF;
63
64
END LOOP;
65
66
END;
67
$procedure$
|
|||||
| Procedure | toggle_ticket_hold_with_logs | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.toggle_ticket_hold_with_logs(IN p_ticket_ids uuid[], IN p_hold boolean, IN p_modified_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_ticket RECORD;
6
v_log_id uuid;
7
BEGIN
8
FOR v_ticket IN
9
SELECT id, ticket_status_id, is_on_hold
10
FROM public.tickets
11
WHERE id = ANY(p_ticket_ids)
12
LOOP
13
14
----------------------------------------------------------------
15
-- Skip if state is already same
16
----------------------------------------------------------------
17
IF v_ticket.is_on_hold = p_hold THEN
18
IF p_hold THEN
19
RAISE NOTICE 'Ticket % is already ON HOLD', v_ticket.id;
20
ELSE
21
RAISE NOTICE 'Ticket % is already NOT on hold', v_ticket.id;
22
END IF;
23
CONTINUE;
24
END IF;
25
26
----------------------------------------------------------------
27
-- Update ticket hold state
28
----------------------------------------------------------------
29
UPDATE public.tickets
30
SET is_on_hold = p_hold,
31
modified_by = p_modified_by,
32
modified_on_utc = now()
33
WHERE id = v_ticket.id;
34
35
----------------------------------------------------------------
36
-- Insert TicketLog
37
----------------------------------------------------------------
38
v_log_id := gen_random_uuid();
39
40
INSERT INTO public.ticket_logs (
41
id,
42
ticket_id,
43
old_status_id,
44
new_status_id,
45
is_on_hold,
46
created_by,
47
created_on_utc
48
)
49
VALUES (
50
v_log_id,
51
v_ticket.id,
52
v_ticket.ticket_status_id,
53
v_ticket.ticket_status_id, -- no status change
54
p_hold, -- NEW hold value
55
p_modified_by,
56
now()
57
);
58
59
----------------------------------------------------------------
60
-- Messages
61
----------------------------------------------------------------
62
IF p_hold THEN
63
RAISE NOTICE 'Ticket % set to ON HOLD', v_ticket.id;
64
ELSE
65
RAISE NOTICE 'Ticket % UN-HOLD done', v_ticket.id;
66
END IF;
67
68
END LOOP;
69
70
END;
71
$procedure$
|
|||||
| Procedure | mark_ticket_reopened_with_logs | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.mark_ticket_reopened_with_logs(IN p_ticket_ids uuid[], IN p_reopen_status_id integer, IN p_modified_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_ticket RECORD;
6
v_log_id uuid;
7
BEGIN
8
FOR v_ticket IN
9
SELECT id, ticket_status_id, is_re_opened
10
FROM public.tickets
11
WHERE id = ANY(p_ticket_ids)
12
LOOP
13
14
----------------------------------------------------------------
15
-- Skip if already reopened once (idempotent)
16
----------------------------------------------------------------
17
IF v_ticket.is_re_opened THEN
18
RAISE NOTICE 'Ticket % is already marked as reopened', v_ticket.id;
19
CONTINUE;
20
END IF;
21
22
----------------------------------------------------------------
23
-- Update ticket (set reopened flag + change status)
24
----------------------------------------------------------------
25
UPDATE public.tickets
26
SET
27
ticket_status_id = p_reopen_status_id, -- dynamic
28
is_re_opened = true, -- YOUR COLUMN NAME
29
modified_by = p_modified_by,
30
modified_on_utc = now()
31
WHERE id = v_ticket.id;
32
33
----------------------------------------------------------------
34
-- Insert Ticket Log (NO EXTRA BOOLEAN)
35
----------------------------------------------------------------
36
v_log_id := gen_random_uuid();
37
38
INSERT INTO public.ticket_logs (
39
id,
40
ticket_id,
41
old_status_id,
42
new_status_id,
43
is_on_hold,
44
created_by,
45
created_on_utc
46
)
47
VALUES (
48
v_log_id,
49
v_ticket.id,
50
v_ticket.ticket_status_id,
51
p_reopen_status_id, -- new status
52
false, -- hold unchanged
53
p_modified_by,
54
now()
55
);
56
57
RAISE NOTICE 'Ticket % marked as REOPENED', v_ticket.id;
58
59
END LOOP;
60
61
END;
62
$procedure$
|
|||||
| Procedure | assign_ticket_with_logs | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.assign_ticket_with_logs(IN p_ticket_id uuid, IN p_service_provider_id integer, IN p_new_status_id integer, IN p_modified_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_ticket RECORD;
6
v_log_id uuid;
7
BEGIN
8
----------------------------------------------------------------
9
-- Fetch full ticket row
10
----------------------------------------------------------------
11
SELECT
12
id,
13
ticket_status_id,
14
ticket_assigned_to,
15
is_on_hold
16
INTO v_ticket
17
FROM public.tickets
18
WHERE id = p_ticket_id;
19
20
IF v_ticket.id IS NULL THEN
21
RAISE EXCEPTION 'Ticket % not found', p_ticket_id;
22
END IF;
23
24
----------------------------------------------------------------
25
-- Update assigned_to and status
26
----------------------------------------------------------------
27
UPDATE public.tickets
28
SET
29
ticket_assigned_to = p_service_provider_id,
30
ticket_status_id = p_new_status_id,
31
modified_by = p_modified_by,
32
modified_on_utc = now()
33
WHERE id = p_ticket_id;
34
35
----------------------------------------------------------------
36
-- Insert assignment log
37
----------------------------------------------------------------
38
v_log_id := gen_random_uuid();
39
40
INSERT INTO public.ticket_logs (
41
id,
42
ticket_id,
43
old_status_id,
44
new_status_id,
45
is_on_hold,
46
created_by,
47
created_on_utc
48
)
49
VALUES (
50
v_log_id,
51
p_ticket_id,
52
v_ticket.ticket_status_id, -- OLD STATUS
53
p_new_status_id, -- NEW STATUS
54
v_ticket.is_on_hold, -- Hold flag unchanged
55
p_modified_by,
56
now()
57
);
58
59
END;
60
$procedure$
|
|||||
| Procedure | assign_multiple_tickets_with_logs_json | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.assign_multiple_tickets_with_logs_json(IN p_assignments jsonb, IN p_new_status_id integer, IN p_modified_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_item jsonb;
6
v_ticket_id uuid;
7
v_sp_id int;
8
v_ticket RECORD;
9
v_log_id uuid;
10
BEGIN
11
FOR v_item IN SELECT * FROM jsonb_array_elements(p_assignments)
12
LOOP
13
v_ticket_id := (v_item->>'ticketId')::uuid;
14
v_sp_id := (v_item->>'serviceProviderId')::int;
15
16
-- Fetch ticket
17
SELECT id, ticket_status_id, ticket_assigned_to, is_on_hold
18
INTO v_ticket
19
FROM public.tickets
20
WHERE id = v_ticket_id;
21
22
IF v_ticket.id IS NULL THEN
23
RAISE EXCEPTION 'Ticket % not found', v_ticket_id;
24
END IF;
25
26
-- Update ticket
27
UPDATE public.tickets
28
SET
29
ticket_assigned_to = v_sp_id,
30
ticket_status_id = p_new_status_id,
31
modified_by = p_modified_by,
32
modified_on_utc = now()
33
WHERE id = v_ticket_id;
34
35
-- Insert log
36
v_log_id := gen_random_uuid();
37
38
INSERT INTO public.ticket_logs (
39
id, ticket_id, old_status_id, new_status_id,
40
is_on_hold, created_by, created_on_utc
41
)
42
VALUES (
43
v_log_id,
44
v_ticket_id,
45
v_ticket.ticket_status_id,
46
p_new_status_id,
47
v_ticket.is_on_hold,
48
p_modified_by,
49
now()
50
);
51
END LOOP;
52
END;
53
$procedure$
|
|||||
| Procedure | add_existing_user_role_permissions | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.add_existing_user_role_permissions(IN p_user_id uuid, IN p_created_by uuid, IN p_organization_id uuid DEFAULT NULL::uuid, IN p_company_id uuid DEFAULT NULL::uuid, IN p_permission_ids uuid[] DEFAULT NULL::uuid[], IN p_role_ids integer[] DEFAULT NULL::integer[])
2
LANGUAGE plpgsql
3
AS $procedure$
4
BEGIN
5
-- Insert user to organization if organization_id is provided
6
IF p_organization_id IS NOT NULL THEN
7
CALL public.insert_organization_user(p_user_id, p_created_by, p_organization_id);
8
END IF;
9
10
-- Insert user to company if company_id is provided
11
IF p_company_id IS NOT NULL THEN
12
CALL public.insert_company_user(p_user_id, p_created_by, p_company_id);
13
END IF;
14
15
-- Insert permissions if both permission_ids array and organization_id are provided
16
IF p_permission_ids IS NOT NULL AND p_organization_id IS NOT NULL THEN
17
CALL public.insert_user_permission(p_user_id, p_organization_id, p_permission_ids, p_created_by);
18
END IF;
19
20
-- Insert roles if role_ids array is provided
21
IF p_role_ids IS NOT NULL THEN
22
CALL public.insert_user_roles(p_user_id, p_role_ids, p_organization_id, p_created_by);
23
END IF;
24
END;
25
$procedure$
|
-- Table: residents
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:apartment_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:contact_number:text:True:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:email:text:True:::False|COL:first_name:text:False:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_primary_owner:boolean:True::false:False|COL:last_name:text:True:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:occupancy_status_id:integer:False:::False|COL:permanent_address_id:uuid:True:::False|COL:resident_type_id:integer:False:::False|COL:user_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|PK:|FK:user_id→users.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:apartment_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:contact_number:text:True:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:email:text:True:::False|COL:first_name:text:False:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:last_name:text:True:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:occupancy_status_id:integer:False:::False|COL:permanent_address_id:uuid:True:::False|COL:resident_type_id:integer:False:::False|COL:user_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "residents" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text, "email" text, "contact_number" text, "permanent_address_id" uuid, "resident_type_id" integer NOT NULL, "occupancy_status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "user_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "is_primary_owner" boolean DEFAULT false, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "residents" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text, "email" text, "contact_number" text, "permanent_address_id" uuid, "resident_type_id" integer NOT NULL, "occupancy_status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "user_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "residents" ADD COLUMN "is_primary_owner" boolean ;
-- ForeignKeys: MissingInTarget
ALTER TABLE "residents" ADD CONSTRAINT "fk_residents_user_id" FOREIGN KEY (user_id) REFERENCES users(id);
-- Table: community_directory_verification_statuses
Exists in source, missing in target
-- Table: visitor_phones
Exists in source, missing in target
-- Table: visitors
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:first_name:text:False:::False|COL:id:bigint:False:::False|COL:image_url:text:True:::False|COL:is_active:boolean:False::true:False|COL:is_deleted:boolean:False::false:False|COL:last_name:text:True:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:visitor_type_id:integer:False::0:False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:apartment_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:contact_number:text:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:email:text:True:::False|COL:first_name:text:False:::False|COL:id:integer:False:::False|COL:identity_number:text:True:::False|COL:identity_type_id:integer:True:::False|COL:is_deleted:boolean:False::false:False|COL:last_name:text:True:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:vehicle_number:text:True:::False|COL:visiting_from:text:True:::False|COL:visitor_type_id:integer:False:::False|PK:|FK:identity_type_id→identity_types.id|FK:visitor_type_id→visitor_types.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "visitors" ("id" bigint NOT NULL, "first_name" text NOT NULL, "last_name" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "is_active" boolean DEFAULT true NOT NULL, "visitor_type_id" integer DEFAULT 0 NOT NULL, "image_url" text, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "visitors" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text, "email" text, "visiting_from" text, "contact_number" text NOT NULL, "visitor_type_id" integer NOT NULL, "vehicle_number" text, "identity_type_id" integer, "identity_number" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "visitors" ADD COLUMN "is_active" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "visitors" ADD COLUMN "image_url" text ;
-- ForeignKeys: MissingInSource
ALTER TABLE "visitors" ADD CONSTRAINT "visitors_identity_type_id_fkey" FOREIGN KEY (identity_type_id) REFERENCES identity_types(id);
-- ForeignKeys: MissingInSource
ALTER TABLE "visitors" ADD CONSTRAINT "visitors_visitor_type_id_fkey" FOREIGN KEY (visitor_type_id) REFERENCES visitor_types(id);
-- Table: poll_votes
Exists in source, missing in target
-- Table: __EFMigrationsHistory
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:migration_id:character varying:False:150::False|COL:product_version:character varying:False:32::False
-- TARGET SIGNATURE
COL:migration_id:character varying:False:150::False|COL:product_version:character varying:False:32::False|PK:|IDX:btree:unique:migration_id:
-- SOURCE SCRIPT
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL);
-- TARGET SCRIPT
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL, PRIMARY KEY ("migration_id"));
-- Indexes: MissingInSource
-- Optionally drop: DROP INDEX IF EXISTS "pk___ef_migrations_history";
-- Table: committee_members
-- ForeignKeys: MissingInTarget
ALTER TABLE "committee_members" ADD CONSTRAINT "fk_committee_members_user" FOREIGN KEY (user_id) REFERENCES users(id);
-- Table: ticket_number_sequences
Exists in source, missing in target
-- Table: pre_approved_entries
Exists in source, missing in target
-- Table: meeting_agenda_items
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:item_text:text:False:::False|COL:meeting_occurrence_id:integer:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:order_no:integer:False:::False|PK:|FK:meeting_occurrence_id→meeting_occurrences.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:item_text:text:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:occurrence_id:integer:False:::False|COL:order_no:integer:False:::False|PK:|FK:occurrence_id→event_occurrences.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "meeting_agenda_items" ("id" integer NOT NULL, "meeting_occurrence_id" integer NOT NULL, "item_text" text NOT NULL, "order_no" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "modified_by" uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "meeting_agenda_items" ("id" integer NOT NULL, "occurrence_id" integer NOT NULL, "item_text" text NOT NULL, "order_no" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "modified_by" uuid, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "meeting_agenda_items" ADD COLUMN "meeting_occurrence_id" integer NOT NULL;
-- ForeignKeys: MissingInTarget
ALTER TABLE "meeting_agenda_items" ADD CONSTRAINT "fk_meeting_agenda_items_occurrence" FOREIGN KEY (meeting_occurrence_id) REFERENCES meeting_occurrences(id) ON DELETE CASCADE;
-- ForeignKeys: MissingInSource
ALTER TABLE "meeting_agenda_items" ADD CONSTRAINT "fk_meeting_agenda_items_occurrence" FOREIGN KEY (occurrence_id) REFERENCES event_occurrences(id);
-- Table: ticket_logs
Exists in source, missing in target
-- Table: noc_approvals
Exists in source, missing in target
-- Table: service_provider_logs
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:current_status_id:integer:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:entry_time:timestamp without time zone:False:::False|COL:exit_time:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:service_provider_id:integer:False:::False|PK:|PK:|FK:service_provider_id→service_providers.id|IDX:btree:unique:id,entry_time:
-- TARGET SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:current_status_id:integer:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:entry_time:timestamp without time zone:False:::False|COL:exit_time:timestamp without time zone:False:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:service_provider_id:integer:False:::False|COL:visiting_from:text:False:::False|COL:visiting_unit_id:integer:False:::False|COL:visit_purpose_id:integer:False:::False|PK:|FK:service_provider_id→service_providers.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "service_provider_logs" ("id" integer NOT NULL, "service_provider_id" integer NOT NULL, "current_status_id" integer NOT NULL, "entry_time" timestamp without time zone NOT NULL, "exit_time" timestamp without time zone, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id", "entry_time"));
-- TARGET SCRIPT
CREATE TABLE "service_provider_logs" ("id" integer NOT NULL, "service_provider_id" integer NOT NULL, "visiting_unit_id" integer NOT NULL, "visit_purpose_id" integer NOT NULL, "visiting_from" text NOT NULL, "current_status_id" integer NOT NULL, "entry_time" timestamp without time zone NOT NULL, "exit_time" timestamp without time zone NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- Indexes: MissingInTarget
CREATE UNIQUE INDEX service_provider_logs_pkey ON ONLY public.service_provider_logs USING btree (id, entry_time)
-- Indexes: MissingInSource
-- Optionally drop: DROP INDEX IF EXISTS "pk_service_provider_logs";
-- Table: noc_certificates
Exists in source, missing in target
-- Table: noc_due_snapshots
Exists in source, missing in target
-- Table: noc_requests
Exists in source, missing in target
-- Table: tickets
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:apartment_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_on_hold:boolean:False::false:False|COL:is_re_opened:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:sort_order:integer:False::0:False|COL:ticket_assigned_to:integer:True:::False|COL:ticket_category_id:integer:False:::False|COL:ticket_for_id:integer:False::0:False|COL:ticket_number:text:False::''::text:False|COL:ticket_priority_id:integer:False:::False|COL:ticket_status_id:integer:False:::False|COL:title:character varying:False:255::False|COL:unit_id:integer:False:::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:apartment_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:ticket_assigned_to:integer:True:::False|COL:ticket_category_id:integer:False:::False|COL:ticket_priority_id:integer:False:::False|COL:ticket_status_id:integer:False:::False|COL:title:character varying:False:255::False|COL:unit_id:integer:False:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "tickets" ("id" uuid NOT NULL, "unit_id" integer NOT NULL, "title" varchar(255) NOT NULL, "description" text NOT NULL, "ticket_category_id" integer NOT NULL, "ticket_priority_id" integer NOT NULL, "ticket_status_id" integer NOT NULL, "ticket_assigned_to" integer, "created_by" uuid NOT NULL, "modified_by" uuid, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "sort_order" integer DEFAULT 0 NOT NULL, "is_on_hold" boolean DEFAULT false NOT NULL, "ticket_for_id" integer DEFAULT 0 NOT NULL, "ticket_number" text DEFAULT ''::text NOT NULL, "is_re_opened" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "tickets" ("id" uuid NOT NULL, "unit_id" integer NOT NULL, "title" varchar(255) NOT NULL, "description" text NOT NULL, "ticket_category_id" integer NOT NULL, "ticket_priority_id" integer NOT NULL, "ticket_status_id" integer NOT NULL, "ticket_assigned_to" integer, "created_by" uuid NOT NULL, "modified_by" uuid, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "tickets" ADD COLUMN "sort_order" integer NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "tickets" ADD COLUMN "is_on_hold" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "tickets" ADD COLUMN "ticket_for_id" integer NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "tickets" ADD COLUMN "ticket_number" text NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "tickets" ADD COLUMN "is_re_opened" boolean NOT NULL;
-- Table: noc_types
Exists in source, missing in target
-- Table: service_provider_logs_2022_2023
Exists in source, missing in target
-- Table: noc_approval_roles
Exists in source, missing in target
-- Table: visitor_sp_company_visits
Exists in source, missing in target
-- Table: pre_approved_schedule_rules
Exists in source, missing in target
-- Table: visitor_details
Exists in source, missing in target
-- Table: visitor_apartments
Exists in source, missing in target
-- Table: visitor_emails
Exists in source, missing in target
-- Table: visitor_identities
Exists in source, missing in target
-- Table: visitor_logs
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:apartment_id:uuid:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:entry_time:timestamp without time zone:True:::False|COL:exit_time:timestamp without time zone:True:::False|COL:gate_id:integer:True:::False|COL:id:bigint:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:pre_approved_entry_id:integer:True:::False|COL:vehicle_number:text:True:::False|COL:verified_by_guard_id:uuid:True:::False|COL:visitor_id:integer:False:::False|COL:visitor_status_id:integer:True:::False|COL:visitor_type_id:integer:False:::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:entry_time:timestamp without time zone:True:::False|COL:exit_time:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:visiting_from:text:False:::False|COL:visitor_id:integer:False:::False|COL:visitor_status_id:integer:False::0:False|COL:visitor_type_id:integer:False:::False|PK:|FK:visitor_id→visitors.id|FK:visitor_type_id→visitor_types.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "visitor_logs" ("id" bigint NOT NULL, "visitor_id" integer NOT NULL, "apartment_id" uuid NOT NULL, "visitor_type_id" integer NOT NULL, "entry_time" timestamp without time zone, "exit_time" timestamp without time zone, "gate_id" integer, "verified_by_guard_id" uuid, "pre_approved_entry_id" integer, "vehicle_number" text, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "visitor_status_id" integer, "modified_by" uuid, "modified_on_utc" timestamp without time zone, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "visitor_logs" ("id" integer NOT NULL, "visitor_id" integer NOT NULL, "visitor_type_id" integer NOT NULL, "visiting_from" text NOT NULL, "entry_time" timestamp without time zone, "exit_time" timestamp without time zone, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "visitor_status_id" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "visitor_logs" ADD COLUMN "apartment_id" uuid NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "visitor_logs" ADD COLUMN "gate_id" integer ;
-- Columns: MissingInTarget
ALTER TABLE "visitor_logs" ADD COLUMN "verified_by_guard_id" uuid ;
-- Columns: MissingInTarget
ALTER TABLE "visitor_logs" ADD COLUMN "pre_approved_entry_id" integer ;
-- Columns: MissingInTarget
ALTER TABLE "visitor_logs" ADD COLUMN "vehicle_number" text ;
-- ForeignKeys: MissingInSource
ALTER TABLE "visitor_logs" ADD CONSTRAINT "visitor_logs_visitor_id_fkey" FOREIGN KEY (visitor_id) REFERENCES visitors(id);
-- ForeignKeys: MissingInSource
ALTER TABLE "visitor_logs" ADD CONSTRAINT "visitor_logs_visitor_type_id_fkey" FOREIGN KEY (visitor_type_id) REFERENCES visitor_types(id);
-- Table: multi_unit_visits
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:approval_reason:integer:True:::False|COL:approval_source:integer:True::1:False|COL:approved_by_resident_id:integer:True:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:bigint:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_manual_approval:boolean:True::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:unit_id:integer:False:::False|COL:visitor_log_id:integer:False:::False|COL:visitor_statuses:integer:False:::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:bigint:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:unit_id:integer:False:::False|COL:visitor_log_id:integer:False:::False|COL:visitor_statuses:integer:False:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "multi_unit_visits" ("id" bigint NOT NULL, "visitor_log_id" integer NOT NULL, "unit_id" integer NOT NULL, "visitor_statuses" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "approval_reason" integer, "approval_source" integer DEFAULT 1, "approved_by_resident_id" integer, "is_manual_approval" boolean DEFAULT false, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "multi_unit_visits" ("id" bigint NOT NULL, "visitor_log_id" integer NOT NULL, "unit_id" integer NOT NULL, "visitor_statuses" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "multi_unit_visits" ADD COLUMN "approval_reason" integer ;
-- Columns: MissingInTarget
ALTER TABLE "multi_unit_visits" ADD COLUMN "approval_source" integer ;
-- Columns: MissingInTarget
ALTER TABLE "multi_unit_visits" ADD COLUMN "approved_by_resident_id" integer ;
-- Columns: MissingInTarget
ALTER TABLE "multi_unit_visits" ADD COLUMN "is_manual_approval" boolean ;
-- Table: meeting_action_items
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:action_description:text:False:::False|COL:assigned_to_user_id:uuid:True:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:due_date:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:meeting_occurrence_id:integer:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:status:text:True:::False|PK:|FK:assigned_to_user_id→users.id|FK:meeting_occurrence_id→meeting_occurrences.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:action_description:text:False:::False|COL:assigned_to_user_id:uuid:True:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:due_date:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:occurrence_id:integer:False:::False|COL:status:text:True:::False|PK:|FK:assigned_to_user_id→users.id|FK:occurrence_id→event_occurrences.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "meeting_action_items" ("id" integer NOT NULL, "meeting_occurrence_id" integer NOT NULL, "action_description" text NOT NULL, "assigned_to_user_id" uuid, "due_date" timestamp without time zone, "status" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "meeting_action_items" ("id" integer NOT NULL, "occurrence_id" integer NOT NULL, "action_description" text NOT NULL, "assigned_to_user_id" uuid, "due_date" timestamp without time zone, "status" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "meeting_action_items" ADD COLUMN "meeting_occurrence_id" integer NOT NULL;
-- ForeignKeys: MissingInTarget
ALTER TABLE "meeting_action_items" ADD CONSTRAINT "fk_meeting_action_items_occurrence" FOREIGN KEY (meeting_occurrence_id) REFERENCES meeting_occurrences(id) ON DELETE CASCADE;
-- ForeignKeys: MissingInSource
ALTER TABLE "meeting_action_items" ADD CONSTRAINT "fk_meeting_action_items_occurrence" FOREIGN KEY (occurrence_id) REFERENCES event_occurrences(id);
-- Table: meeting_notes
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:meeting_occurrence_id:integer:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:note_text:text:True:::False|PK:|FK:meeting_occurrence_id→meeting_occurrences.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:note_text:text:True:::False|COL:occurrence_id:integer:False:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "meeting_notes" ("id" integer NOT NULL, "meeting_occurrence_id" integer NOT NULL, "note_text" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "meeting_notes" ("id" integer NOT NULL, "occurrence_id" integer NOT NULL, "note_text" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "meeting_notes" ADD COLUMN "meeting_occurrence_id" integer NOT NULL;
-- ForeignKeys: MissingInTarget
ALTER TABLE "meeting_notes" ADD CONSTRAINT "fk_meeting_notes_occurrence" FOREIGN KEY (meeting_occurrence_id) REFERENCES meeting_occurrences(id) ON DELETE CASCADE;
-- Table: ticket_comment_documents
Exists in source, missing in target
-- Table: ticket_comments
Exists in source, missing in target
-- Table: users
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:company_id:uuid:False:::False|COL:contact_number:character varying:True:255::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:email:character varying:False:256::False|COL:first_name:character varying:False:100::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_owner:boolean:False::true:False|COL:last_name:character varying:False:100::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:password_hash:character varying:True:255::False|COL:phone_number:character varying:True:15::False|PK:|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|IDX:btree:unique:email,phone_number:|IDX:btree:unique:email:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:company_id:uuid:False:::False|COL:contact_number:character varying:True:255::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:email:character varying:False:256::False|COL:first_name:character varying:False:100::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_owner:boolean:False::true:False|COL:last_name:character varying:False:100::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:password_hash:character varying:True:255::False|COL:phone_number:character varying:True:15::False|PK:|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|IDX:btree:unique:email,phone_number:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "users" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) NOT NULL, "password_hash" varchar(255), "is_owner" boolean DEFAULT true NOT NULL, "contact_number" varchar(255), "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "phone_number" varchar(15), PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "users" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) NOT NULL, "contact_number" varchar(255), "is_owner" boolean DEFAULT true NOT NULL, "password_hash" varchar(255), "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "phone_number" varchar(15), PRIMARY KEY ("id"));
-- Indexes: MissingInTarget
CREATE UNIQUE INDEX uq_users_email ON public.users USING btree (email)
-- Table: has_default
Exists in source, missing in target
-- Table: service_provider_apartments
Exists in source, missing in target
-- Table: community_directory_location_types
Exists in source, missing in target
-- Table: community_directory_source_types
Exists in source, missing in target
-- Table: community_directory_listing_types
Exists in source, missing in target
-- Table: community_directory_categories
Exists in source, missing in target
-- Table: community_directory_status_types
Exists in source, missing in target
-- Table: resident_units
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False::nextval('resident_units_id_seq'::regclass):False|COL:is_deleted:boolean:False::false:False|COL:is_primary_owner:boolean:False::false:False|COL:resident_id:integer:False:::False|COL:resident_type_id:integer:False::0:False|COL:unit_id:integer:False:::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:resident_id:integer:False:::False|COL:unit_id:integer:False:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "resident_units" ("id" integer DEFAULT nextval('resident_units_id_seq'::regclass) NOT NULL, "unit_id" integer NOT NULL, "resident_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "is_primary_owner" boolean DEFAULT false NOT NULL, "resident_type_id" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "resident_units" ("id" integer NOT NULL, "unit_id" integer NOT NULL, "resident_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "resident_units" ADD COLUMN "is_primary_owner" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "resident_units" ADD COLUMN "resident_type_id" integer NOT NULL;
-- Table: apartments
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:address_id:uuid:True:::False|COL:apartment_type_id:integer:False:::False|COL:association_name:text:True:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:name:text:False:::False|COL:organization_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:pan:text:True:::False|COL:phone:text:True:::False|COL:tan:text:True:::False|PK:|FK:address_id→addresses.id|FK:apartment_type_id→apartment_types.id|FK:created_by→users.id|FK:modified_by→users.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:address_id:uuid:False:::False|COL:apartment_type_id:integer:False:::False|COL:association_name:text:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:name:text:False:::False|COL:pan:text:False:::False|COL:phone:text:False:::False|COL:tan:text:False:::False|PK:|FK:address_id→addresses.id|FK:apartment_type_id→apartment_types.id|FK:created_by→users.id|FK:modified_by→users.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "apartments" ("id" uuid NOT NULL, "name" text NOT NULL, "apartment_type_id" integer NOT NULL, "address_id" uuid, "phone" text, "pan" text, "tan" text, "association_name" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "organization_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "apartments" ("id" uuid NOT NULL, "name" text NOT NULL, "apartment_type_id" integer NOT NULL, "address_id" uuid NOT NULL, "phone" text NOT NULL, "pan" text NOT NULL, "tan" text NOT NULL, "association_name" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "apartments" ADD COLUMN "organization_id" uuid ;
-- Table: apartment_requests
Exists in source, missing in target
-- Table: polls
Exists in source, missing in target
-- Table: UserToPaidModulesMapping
-- SOURCE
CREATE TABLE "UserToPaidModulesMapping" ("user_id" uuid NOT NULL, "paid_modules" bigint NOT NULL, PRIMARY KEY ("user_id"));
-- TARGET
CREATE TABLE "UserToPaidModulesMapping" ("user_id" uuid NOT NULL, "paid_modules" bigint NOT NULL, PRIMARY KEY ("user_id"));
-- Table: ticket_documents
Exists in source, missing in target
-- Table: service_provider_companies
Exists in source, missing in target
-- Table: service_provider_company_categories
Exists in source, missing in target
-- Table: service_provider_company_subtypes
Exists in source, missing in target
-- Table: service_providers
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_frequent_visitor:boolean:False::false:False|COL:is_hireable:boolean:False::false:False|COL:is_visible:boolean:False::true:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:police_verification_status:boolean:False::false:False|COL:service_provider_sub_type_id:integer:False:::False|COL:service_provider_type_id:integer:False:::False|COL:user_id:uuid:True:::False|COL:visitor_id:bigint:True:::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:apartment_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:contact_number:text:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:email:text:False:::False|COL:first_name:text:False:::False|COL:id:integer:False:::False|COL:identity_image:bytea:True:::False|COL:identity_number:text:False:::False|COL:identity_type_id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_frequent_visitor:boolean:False::false:False|COL:is_hireable:boolean:False::false:False|COL:is_visible:boolean:False::false:False|COL:last_name:text:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:permanent_address_id:uuid:False:::False|COL:pin:text:False::'a'::bpchar:False|COL:policeverification_status:boolean:False::false:False|COL:present_address_id:uuid:False:::False|COL:service_provider_sub_type_id:integer:False:::False|COL:service_provider_type_id:integer:False:::False|COL:validity_date:timestamp without time zone:False::'0001-01-01 00:00:00'::timestamp without time zone:False|COL:vehicle_number:text:False:::False|COL:visiting_from:text:False:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "service_providers" ("id" integer NOT NULL, "service_provider_type_id" integer NOT NULL, "service_provider_sub_type_id" integer NOT NULL, "visitor_id" bigint, "is_frequent_visitor" boolean DEFAULT false NOT NULL, "is_hireable" boolean DEFAULT false NOT NULL, "is_visible" boolean DEFAULT true NOT NULL, "police_verification_status" boolean DEFAULT false NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "user_id" uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "service_providers" ("id" integer NOT NULL, "first_name" text NOT NULL, "last_name" text NOT NULL, "email" text NOT NULL, "visiting_from" text NOT NULL, "contact_number" text NOT NULL, "permanent_address_id" uuid NOT NULL, "present_address_id" uuid NOT NULL, "service_provider_type_id" integer NOT NULL, "service_provider_sub_type_id" integer NOT NULL, "vehicle_number" text NOT NULL, "identity_type_id" integer NOT NULL, "identity_number" text NOT NULL, "identity_image" bytea, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "is_frequent_visitor" boolean DEFAULT false NOT NULL, "is_hireable" boolean DEFAULT false NOT NULL, "is_visible" boolean DEFAULT false NOT NULL, "policeverification_status" boolean DEFAULT false NOT NULL, "validity_date" timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL, "apartment_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "pin" text DEFAULT 'A'::bpchar NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "service_providers" ADD COLUMN "visitor_id" bigint ;
-- Columns: MissingInTarget
ALTER TABLE "service_providers" ADD COLUMN "police_verification_status" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "service_providers" ADD COLUMN "user_id" uuid ;
-- Table: ticket_categories
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:name:character varying:False:100::False|COL:short_code:character varying:False:2:''::character varying:False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:name:character varying:False:100::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "ticket_categories" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "short_code" varchar(2) DEFAULT ''::character varying NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "ticket_categories" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "ticket_categories" ADD COLUMN "short_code" character varying NOT NULL;
-- Table: possible_visitors
Exists in source, missing in target
-- Table: user_roles
Exists in source, missing in target
-- Table: community_documents
Exists in source, missing in target
-- Table: document_types
Exists in source, missing in target
-- Table: user_fcm_tokens
Exists in source, missing in target
-- Table: security_guard_apartments
Exists in source, missing in target
-- Table: service_provider_users
Exists in source, missing in target
-- Table: community_directory_listings
Exists in source, missing in target
-- Table: community_directory_contacts
Exists in source, missing in target
-- Table: community_directory_recommendations
Exists in source, missing in target
-- Table: decisions
Exists in source, missing in target
-- Table: poll_types
Exists in source, missing in target
-- Table: decision_outcomes
Exists in source, missing in target
-- Table: poll_statuses
Exists in source, missing in target
-- Table: poll_options
Exists in source, missing in target
-- Table: visitor_delivery_company
Exists in source, missing in target
-- Table: ticket_fors
Exists in source, missing in target
-- Table: notice_categories
Exists in source, missing in target
-- Table: notice_priorities
Exists in source, missing in target
-- Table: notices
Exists in source, missing in target
-- Table: schema_versions
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:applied_on_utc:timestamp without time zone:False:::False|COL:hash:character varying:True:64::False|COL:script_name:character varying:False:255::False|IDX:btree:unique:script_name:
-- TARGET SIGNATURE
COL:applied_on_utc:timestamp without time zone:False:::False|COL:hash:character varying:True:64::False|COL:script_name:character varying:False:255::False|PK:|IDX:btree:unique:script_name:
-- SOURCE SCRIPT
CREATE TABLE "schema_versions" ("script_name" varchar(255) NOT NULL, "applied_on_utc" timestamp without time zone NOT NULL, "hash" varchar(64));
-- TARGET SCRIPT
CREATE TABLE "schema_versions" ("script_name" varchar(255) NOT NULL, "applied_on_utc" timestamp without time zone NOT NULL, "hash" varchar(64), PRIMARY KEY ("script_name"));
-- Table: service_provider_types
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_in_house:boolean:False::false:False|COL:name:character varying:False:100::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:name:character varying:False:100::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "service_provider_types" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "is_in_house" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "service_provider_types" ("id" integer NOT NULL, "name" varchar(100) NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "service_provider_types" ADD COLUMN "is_in_house" boolean NOT NULL;
-- Table: service_provider_addresses
Exists in source, missing in target
-- Table: meeting_occurrences
Exists in source, missing in target
-- Table: meeting_statuses
Exists in source, missing in target
-- Table: occurrence_participants
Exists in source, missing in target
-- Table: rsvp_statuses
Exists in source, missing in target
-- Table: attendance_statuses
Exists in source, missing in target
-- Table: service_provider_logs_2024_2025
Exists in source, missing in target
-- Table: meeting_modes
Exists in source, missing in target
-- Table: gate_passes
Exists in source, missing in target
-- Table: participant_roles
Exists in source, missing in target
-- Table: service_provider_logs_2023_2024
Exists in source, missing in target
-- Table: community_directory_locations
Exists in source, missing in target
-- Table: option_types
Exists in source, missing in target
-- Table: poll_decisions
Exists in source, missing in target
-- Table: poll_eligibility_types
Exists in source, missing in target
-- Table: service_provider_logs_2025_2026
Exists in source, missing in target
-- Table: event_participants
-- CreateScript: MissingInSource
CREATE TABLE "event_participants" ("id" uuid NOT NULL, "event_id" uuid NOT NULL, "user_id" uuid NOT NULL, "event_status_id" integer NOT NULL, "notification_sent" boolean NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- PrimaryKeys: MissingInSource
ALTER TABLE "event_participants" ADD CONSTRAINT "pk_event_participants" PRIMARY KEY (id);
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "id" uuid NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "event_id" uuid NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "user_id" uuid NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "event_status_id" integer NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "notification_sent" boolean NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "created_by" uuid NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "modified_by" uuid ;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "created_on_utc" timestamp without time zone NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "modified_on_utc" timestamp without time zone ;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "deleted_on_utc" timestamp without time zone ;
-- Columns: MissingInSource
ALTER TABLE "event_participants" ADD COLUMN "is_deleted" boolean NOT NULL;
-- Indexes: MissingInSource
CREATE UNIQUE INDEX pk_event_participants ON public.event_participants USING btree (id)
-- Table: meeting_participants
-- CreateScript: MissingInSource
CREATE TABLE "meeting_participants" ("id" integer NOT NULL, "occurrence_id" integer NOT NULL, "user_id" uuid NOT NULL, "role" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "deleted_on_utc" timestamp without time zone, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- PrimaryKeys: MissingInSource
ALTER TABLE "meeting_participants" ADD CONSTRAINT "pk_meeting_participants" PRIMARY KEY (id);
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "id" integer NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "occurrence_id" integer NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "user_id" uuid NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "role" text ;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "created_on_utc" timestamp without time zone NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "modified_on_utc" timestamp without time zone ;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "is_deleted" boolean NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "deleted_on_utc" timestamp without time zone ;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "created_by" uuid NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "meeting_participants" ADD COLUMN "modified_by" uuid ;
-- ForeignKeys: MissingInSource
ALTER TABLE "meeting_participants" ADD CONSTRAINT "fk_meeting_participants_user" FOREIGN KEY (user_id) REFERENCES users(id);
-- Indexes: MissingInSource
CREATE UNIQUE INDEX pk_meeting_participants ON public.meeting_participants USING btree (id)
-- Table: visitor_unit_logs
-- CreateScript: MissingInSource
CREATE TABLE "visitor_unit_logs" ("id" integer NOT NULL, "visitor_log_id" integer NOT NULL, "unit_id" integer NOT NULL, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- PrimaryKeys: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD CONSTRAINT "pk_visitor_unit_logs" PRIMARY KEY (id);
-- Columns: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD COLUMN "id" integer NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD COLUMN "visitor_log_id" integer NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD COLUMN "unit_id" integer NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD COLUMN "deleted_on_utc" timestamp without time zone ;
-- Columns: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD COLUMN "is_deleted" boolean NOT NULL;
-- ForeignKeys: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD CONSTRAINT "visitor_unit_logs_unit_id_fkey" FOREIGN KEY (unit_id) REFERENCES units(id);
-- ForeignKeys: MissingInSource
ALTER TABLE "visitor_unit_logs" ADD CONSTRAINT "visitor_unit_logs_visitor_log_id_fkey" FOREIGN KEY (visitor_log_id) REFERENCES visitor_logs(id);
-- Indexes: MissingInSource
CREATE UNIQUE INDEX pk_visitor_unit_logs ON public.visitor_unit_logs USING btree (id)
-- Table: visitor_contacts
-- CreateScript: MissingInSource
CREATE TABLE "visitor_contacts" ("id" bigint NOT NULL, "visitor_id" integer NOT NULL, "contact_number" text NOT NULL, "is_current" boolean NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- PrimaryKeys: MissingInSource
ALTER TABLE "visitor_contacts" ADD CONSTRAINT "pk_visitor_contacts" PRIMARY KEY (id);
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "id" bigint NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "visitor_id" integer NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "contact_number" text NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "is_current" boolean NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "created_on_utc" timestamp without time zone NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "modified_on_utc" timestamp without time zone ;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "deleted_on_utc" timestamp without time zone ;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "is_deleted" boolean NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "created_by" uuid NOT NULL;
-- Columns: MissingInSource
ALTER TABLE "visitor_contacts" ADD COLUMN "modified_by" uuid ;
-- Indexes: MissingInSource
CREATE UNIQUE INDEX pk_visitor_contacts ON public.visitor_contacts USING btree (id)
-- Function: get_all_unit_names_by_apartment_id
CREATE OR REPLACE FUNCTION public.get_all_unit_names_by_apartment_id(p_apartment_id uuid)
RETURNS TABLE(id integer, unit_id integer, unit_name text, building_name text, floor_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
CAST(ROW_NUMBER() OVER (ORDER BY u.id) AS INT) AS id,
u.id AS unit_id,
u."name" AS unit_name,
b."name" AS building_name,
f."name" AS floor_name
FROM units u
INNER JOIN buildings b ON u.building_id = b.id
INNER JOIN floors f ON u.floor_id = f.id
WHERE u.apartment_id = p_apartment_id
AND u.is_deleted = false
AND b.is_deleted = false
AND f.is_deleted = false;
END;
$function$
-- Function: get_current_visitors_last24h
CREATE OR REPLACE FUNCTION public.get_current_visitors_last24h(p_apartment_id uuid)
RETURNS TABLE(visitor_id integer, first_name text, last_name text, email text, visiting_from text, contact_number text, vehicle_number text, entry_time timestamp without time zone, visitor_type_id integer, identity_type_id integer, identity_number text)
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN QUERY
SELECT
v.id AS visitor_id,
v.first_name,
v.last_name,
v.email,
v.visiting_from,
v.contact_number,
v.vehicle_number,
vl.entry_time,
v.visitor_type_id,
v.identity_type_id,
v.identity_number
FROM
public.visitors v
INNER JOIN public.visitor_logs vl ON vl.visitor_id = v.id
WHERE
v.apartment_id = p_apartment_id
AND vl.exit_time IS NULL
AND vl.entry_time >= (NOW() AT TIME ZONE 'UTC') - INTERVAL '24 hours'
AND vl.is_deleted = false
AND v.is_deleted = false;
END;
$function$
-- Function: get_resident_fcm_tokens_by_visitor_log_id
CREATE OR REPLACE FUNCTION public.get_resident_fcm_tokens_by_visitor_log_id(p_visitorlogid integer)
RETURNS TABLE(id integer, user_ids uuid[], visitor_id integer, first_name text, last_name text, fcm_tokens text[], device_ids text[])
LANGUAGE sql
AS $function$
SELECT
ROW_NUMBER() OVER ()::INT AS id, -- synthetic serial-like column
ARRAY_AGG(DISTINCT r.user_id) AS user_ids,
v.id AS visitor_id,
v.first_name AS first_name,
v.last_name AS last_name,
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.fcm_token), NULL) AS fcm_tokens,
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.device_id), NULL) AS device_ids
FROM multi_unit_visits muv
INNER JOIN resident_units ru ON muv.unit_id = ru.unit_id
INNER JOIN residents r ON ru.resident_id = r.id
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
INNER JOIN visitors v ON v.id = vl.visitor_id
LEFT JOIN user_fcm_tokens uft ON uft.user_id = r.user_id
WHERE muv.visitor_log_id = p_visitorlogid
AND muv.is_deleted = FALSE
AND ru.is_deleted = FALSE
AND r.is_deleted = FALSE
GROUP BY v.id, v.first_name, v.last_name;
$function$
-- Function: get_user_ids_by_visitor_log_id
CREATE OR REPLACE FUNCTION public.get_user_ids_by_visitor_log_id(p_visitorlogid integer)
RETURNS TABLE(id integer, user_ids uuid[], visitor_id integer, first_name text, last_name text)
LANGUAGE sql
AS $function$
SELECT
ROW_NUMBER() OVER ()::INT AS id, -- synthetic serial-like column
ARRAY_AGG(DISTINCT r.user_id) AS user_ids,
v.id AS visitor_id,
v.first_name As first_name,
v.last_name As last_name
FROM multi_unit_visits muv
INNER JOIN resident_units ru ON muv.unit_id = ru.unit_id
INNER JOIN residents r ON ru.resident_id = r.id
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
INNER JOIN visitors v ON v.id = vl.visitor_id
WHERE muv.visitor_log_id = p_VisitorLogId
AND muv.is_deleted = FALSE
AND ru.is_deleted = FALSE
AND r.is_deleted = FALSE
GROUP BY v.id, v.first_name, v.last_name;
$function$
-- Function: unit_resident_fcm_tokens
CREATE OR REPLACE FUNCTION public.unit_resident_fcm_tokens(p_unit_id integer)
RETURNS TABLE(resident_id integer, user_id uuid, device_id text, fcm_token text)
LANGUAGE sql
AS $function$
SELECT
ru.resident_id,
r.user_id,
uft.device_id,
uft.fcm_token
FROM resident_units ru
INNER JOIN residents r ON ru.resident_id = r.id
LEFT JOIN user_fcm_tokens uft ON uft.user_id = r.user_id
WHERE ru.unit_id = p_unit_id
AND ru.is_deleted = FALSE
AND r.is_deleted = FALSE
AND (uft.fcm_token IS NOT NULL OR uft.device_id IS NOT NULL);
$function$
-- Function: upsert_user_fcm_token
CREATE OR REPLACE FUNCTION public.upsert_user_fcm_token(p_user_id uuid, p_device_id text, p_fcm_token text, p_platform text)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
v_id int4;
BEGIN
INSERT INTO public.user_fcm_tokens (user_id, device_id, fcm_token, platform, upsert_on_utc)
VALUES (p_user_id, p_device_id, p_fcm_token, p_platform, now())
ON CONFLICT (user_id, device_id)
DO UPDATE SET
fcm_token = EXCLUDED.fcm_token,
platform = EXCLUDED.platform,
upsert_on_utc = now()
RETURNING id INTO v_id;
RETURN v_id;
END;
$function$
-- Function: get_unit_info_by_user_apartment
CREATE OR REPLACE FUNCTION public.get_unit_info_by_user_apartment(p_user_id uuid, p_apartment_id uuid)
RETURNS TABLE(id integer, unit_id integer, customer_id uuid, unit_name text, user_name text, apartment_id uuid)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
ru.id, -- added resident_units primary key
ru.unit_id,
u.customer_id,
u.name,
usr.first_name || ' ' || usr.last_name AS user_name,
r.apartment_id
FROM resident_units ru
INNER JOIN residents r ON ru.resident_id = r.id
INNER JOIN units u ON ru.unit_id = u.id
INNER JOIN users usr ON r.user_id = usr.id
WHERE r.user_id = p_user_id
AND r.apartment_id = p_apartment_id;
END;
$function$
-- Function: get_units_with_primary_owner_name
CREATE OR REPLACE FUNCTION public.get_units_with_primary_owner_name(p_apartment_id uuid)
RETURNS TABLE(id integer, customer_id uuid, unit_id integer, unit_name text, owner_first_name text, owner_last_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
ROW_NUMBER() OVER (ORDER BY u.name)::int AS id, -- 1,2,3...
u.customer_id AS customer_id,
u.id AS unit_id,
u.name AS unit_name,
r.first_name AS owner_first_name,
r.last_name AS owner_last_name
FROM public.units u
JOIN public.resident_units ru
ON ru.unit_id = u.id
AND (ru.is_deleted = false OR ru.is_deleted IS NULL)
AND ru.is_primary_owner = true
JOIN public.residents r
ON r.id = ru.resident_id
AND (r.is_deleted = false OR r.is_deleted IS NULL)
WHERE (u.is_deleted = false OR u.is_deleted IS NULL)
AND u.apartment_id = p_apartment_id
ORDER BY u.name;
END;
$function$
-- Function: get_visitors_of_unit
CREATE OR REPLACE FUNCTION public.get_visitors_of_unit(p_unit_id integer, p_datetime timestamp without time zone)
RETURNS TABLE(id integer, visitor_log_id integer, name text, visiting_from text, checked_in_at timestamp without time zone, checked_out_at timestamp without time zone, status text, status_id integer, visitor_type text, visitor_type_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_visit_date DATE := p_datetime::date;
BEGIN
RETURN QUERY
SELECT
ROW_NUMBER() OVER ()::integer AS id, -- Cast ROW_NUMBER() result to integer
vl.id AS visitor_log_id,
TRIM(v.first_name || ' ' || COALESCE(v.last_name, '')) AS name,
vl.visiting_from,
vl.entry_time AS checked_in_at,
vl.exit_time AS checked_out_at,
vs.name::text AS status,
vs.id AS status_id,
vt.name::text AS visitor_type,
vt.id AS visitor_type_id
FROM
multi_unit_visits muv
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
INNER JOIN visitors v ON v.id = vl.visitor_id
INNER JOIN visitor_statuses vs ON vs.id = muv.visitor_statuses
INNER JOIN visitor_types vt ON vt.id = vl.visitor_type_id
WHERE
muv.unit_id = p_unit_id
AND DATE(vl.entry_time) = v_visit_date
AND muv.is_deleted = FALSE
AND vl.is_deleted = FALSE
AND v.is_deleted = FALSE;
END;
$function$
-- Function: get_resident_details_by_user
CREATE OR REPLACE FUNCTION public.get_resident_details_by_user(p_company_id uuid, p_user_id uuid)
RETURNS TABLE(id integer, resident_id integer, first_name text, last_name text, email text, resident_primary_owner boolean, unit_id integer, unit_name text, customer_id uuid)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
r.id AS id,
r.id AS resident_id,
r.first_name,
r.last_name,
r.email,
r.is_primary_owner AS resident_primary_owner,
ru.unit_id,
u.name AS unit_name,
u.customer_id
FROM residents r
LEFT JOIN resident_units ru
ON ru.resident_id = r.id
AND ru.is_deleted = false
LEFT JOIN units u
ON u.id = ru.unit_id
AND u.apartment_id = p_company_id
WHERE r.user_id = p_user_id
AND r.is_deleted = false;
END;
$function$
-- Function: get_vehicles_by_unit
CREATE OR REPLACE FUNCTION public.get_vehicles_by_unit(p_unit_id integer)
RETURNS TABLE(id integer, vehicle_number text, vehicle_type_id integer, unit_id integer, vehicle_rf_id text, vehicle_rf_id_secretcode text, created_on_utc timestamp without time zone, modified_on_utc timestamp without time zone, deleted_on_utc timestamp without time zone, is_deleted boolean, created_by uuid, modified_by uuid)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
v.id, v.vehicle_number, v.vehicle_type_id, v.unit_id,
v.vehicle_rf_id, v.vehicle_rf_id_secretcode,
v.created_on_utc, v.modified_on_utc, v.deleted_on_utc,
v.is_deleted, v.created_by, v.modified_by
FROM public.vehicles v
WHERE v.is_deleted = false
AND v.unit_id = p_unit_id;
END;
$function$
-- Function: get_user_details
CREATE OR REPLACE FUNCTION public.get_user_details(p_user_id uuid, p_apartment_id uuid)
RETURNS TABLE(user_id uuid, user_email text, resident_id integer, first_name text, last_name text, contact_number text, is_primary_owner boolean, unit_id integer, unit_name text, building_id integer, building_name text, floor_id integer, floor_name text, area numeric, bhk_type numeric, customer_id uuid)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
u.id AS user_id,
u.email::text AS user_email, -- Fix: varchar → text
r.id AS resident_id,
r.first_name,
r.last_name,
r.contact_number,
r.is_primary_owner,
ru.unit_id,
un.name AS unit_name,
b.id AS building_id,
b.name AS building_name,
f.id AS floor_id,
f.name AS floor_name,
un.area,
un.bhk_type,
un.customer_id
FROM residents r
JOIN users u
ON u.id = r.user_id
LEFT JOIN resident_units ru
ON ru.resident_id = r.id
AND ru.is_deleted = false
LEFT JOIN units un
ON un.id = ru.unit_id
AND un.is_deleted = false
LEFT JOIN floors f
ON f.id = un.floor_id
AND f.is_deleted = false
LEFT JOIN buildings b
ON b.id = f.building_id
AND b.is_deleted = false
WHERE r.is_deleted = false
AND r.user_id = p_user_id
AND r.apartment_id = p_apartment_id;
END;
$function$
-- Function: get_units_by_user
CREATE OR REPLACE FUNCTION public.get_units_by_user(p_company_id uuid, p_user_id uuid)
RETURNS TABLE(id integer, resident_unit_id integer, unit_id integer, unit_name text, customer_id uuid)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
ru.id AS id, -- ✅ NEW COLUMN
ru.id AS resident_unit_id, -- existing
u.id AS unit_id,
u.name AS unit_name,
u.customer_id
FROM residents r
INNER JOIN resident_units ru
ON ru.resident_id = r.id
AND ru.is_deleted = FALSE
INNER JOIN units u
ON u.id = ru.unit_id
INNER JOIN apartments a
ON a.id = u.apartment_id
WHERE a.id = p_company_id -- ✅ company filter
AND r.user_id = p_user_id -- ✅ user filter
AND r.is_deleted = FALSE;
END;
$function$
-- Function: get_inhouse_service_providers
CREATE OR REPLACE FUNCTION public.get_inhouse_service_providers(p_apartment_id uuid, p_types integer[])
RETURNS TABLE(id integer, service_provider_id integer, first_name text, last_name text, service_provider_type_id integer, service_provider_type_name character varying)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
CAST(ROW_NUMBER() OVER () AS int) AS id, -- index
sp.id AS service_provider_id, -- actual service provider ID
sp.first_name,
sp.last_name,
sp.service_provider_type_id,
spt.name AS service_provider_type_name
FROM public.service_providers sp
INNER JOIN public.service_provider_types spt
ON sp.service_provider_type_id = spt.id
WHERE sp.apartment_id = p_apartment_id
AND sp.service_provider_type_id = ANY(p_types)
AND sp.is_deleted = false
AND COALESCE(spt.is_deleted, false) = false;
END;
$function$
-- Function: get_visitor_by_contact_or_email
CREATE OR REPLACE FUNCTION public.get_visitor_by_contact_or_email(p_apartment_id uuid DEFAULT NULL::uuid, p_contact_number text DEFAULT NULL::text, p_email text DEFAULT NULL::text)
RETURNS TABLE(id integer, first_name text, last_name text, email text, contact_number text, visitor_type_id integer, vehicle_number text, identity_type_id integer, identity_number text, apartment_id uuid)
LANGUAGE sql
AS $function$
SELECT
v.id,
v.first_name,
v.last_name,
v.email,
v.contact_number,
v.visitor_type_id,
v.vehicle_number,
v.identity_type_id,
v.identity_number,
v.apartment_id
FROM public.visitors v
WHERE v.is_deleted = false
AND (p_apartment_id IS NULL OR v.apartment_id = p_apartment_id)
AND (
(p_contact_number IS NOT NULL AND trim(v.contact_number) = trim(p_contact_number))
OR
(p_email IS NOT NULL AND lower(trim(v.email)) = lower(trim(p_email)))
);
$function$
-- Function: get_apartment_visitors_in_timespan
CREATE OR REPLACE FUNCTION public.get_apartment_visitors_in_timespan(p_apartment_id uuid, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
RETURNS TABLE(id integer, visitor_id integer, visitor_type_id integer, visitor_type text, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_person_type text, visitor_person_sub_type text, visitor_status_id integer, visitor_status text, building_id integer, building_name text, floor_id integer, floor_name text, unit_id integer, unit_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
CAST(ROW_NUMBER() OVER (ORDER BY vl.entry_time, v.id) AS int) AS serial_id,
v.id as visitor_id,
vt.id AS visitor_type_id,
'visitor'::text AS visitor_type,
v.first_name::text,
v.last_name::text,
vl.entry_time,
vl.exit_time,
vt.name::text AS visitor_person_type,
NULL::text AS visitor_person_sub_type,
CASE
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 3
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 4
ELSE vl.visitor_status_id
END AS visitor_status_id,
(
CASE
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 'Checked In'
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 'Checked Out'
ELSE COALESCE(vs.name::text, 'Unknown')
END
)::text AS visitor_status,
b.id AS building_id,
b.name::text AS building_name,
f.id AS floor_id,
f.name::text AS floor_name,
u.id AS unit_id,
u.name::text AS unit_name
FROM public.visitor_logs vl
JOIN public.visitors v ON vl.visitor_id = v.id
JOIN public.visitor_types vt ON v.visitor_type_id = vt.id
LEFT JOIN public.visitor_statuses vs ON vl.visitor_status_id = vs.id
-- 🔗 Join chain for hierarchy
LEFT JOIN public.multi_unit_visits muv ON muv.visitor_log_id = vl.id AND COALESCE(muv.is_deleted, FALSE) = FALSE
LEFT JOIN public.units u ON u.id = muv.unit_id AND COALESCE(u.is_deleted, FALSE) = FALSE
LEFT JOIN public.floors f ON f.id = u.floor_id AND COALESCE(f.is_deleted, FALSE) = FALSE
LEFT JOIN public.buildings b ON b.id = f.building_id AND COALESCE(b.is_deleted, FALSE) = FALSE
--LEFT JOIN public.apartments a ON a.id = COALESCE(v.apartment_id, b.apartment_id) AND COALESCE(a.is_deleted, FALSE) = FALSE
WHERE
vl.entry_time BETWEEN p_start_date AND p_end_date
AND COALESCE(vl.is_deleted, FALSE) = FALSE
AND COALESCE(v.is_deleted, FALSE) = FALSE
ORDER BY vl.entry_time, v.id;
END;
$function$
-- Function: get_unit_related_details_by_user
CREATE OR REPLACE FUNCTION public.get_unit_related_details_by_user(p_user_id uuid)
RETURNS jsonb
LANGUAGE sql
AS $function$
SELECT COALESCE(
/* 1️⃣ Unit-based JSON (resident users) */
(
SELECT jsonb_build_object(
'id', u.id,
'name', u.name,
'unit_type_id', u.unit_type_id,
'phone_extension', u.phone_extention,
'area', u.area,
'bhk_type', u.bhk_type,
'occupant_type_id', u.occupant_type_id,
'occupancy_type_id', u.occupancy_type_id,
'residents', COALESCE(
(
SELECT jsonb_agg(
DISTINCT jsonb_build_object(
'id', r2.id,
'first_name', r2.first_name,
'last_name', r2.last_name,
'email', r2.email,
'contact_number', r2.contact_number,
'resident_type', rt.name
)
)
FROM resident_units ru2
JOIN residents r2
ON r2.id = ru2.resident_id
AND r2.is_deleted = false
JOIN resident_types rt
ON rt.id = r2.resident_type_id
AND rt.is_deleted = false
WHERE ru2.unit_id = u.id
AND ru2.is_deleted = false
),
'[]'::jsonb
),
'vehicles', COALESCE(
(
SELECT jsonb_agg(
DISTINCT jsonb_build_object(
'id', v.id,
'vehicle_number', v.vehicle_number,
'vehicle_type', vt.name
)
)
FROM vehicles v
JOIN vehicle_types vt
ON vt.id = v.vehicle_type_id
AND vt.is_deleted = false
WHERE v.unit_id = u.id
AND v.is_deleted = false
),
'[]'::jsonb
)
)
FROM residents r
JOIN resident_units ru
ON ru.resident_id = r.id
AND ru.is_deleted = false
JOIN units u
ON u.id = ru.unit_id
AND u.is_deleted = false
WHERE r.user_id = p_user_id
AND r.is_deleted = false
ORDER BY r.is_primary_owner DESC NULLS LAST
LIMIT 1
),
/* 2️⃣ Fallback: user-only JSON (non-resident users) */
(
SELECT jsonb_build_object(
'user_id', u.id,
'first_name', u.first_name,
'last_name', u.last_name,
'email', u.email,
'contact_number', u.contact_number
)
FROM users u
WHERE u.id = p_user_id
AND u.is_deleted = false
)
);
$function$
-- Function: get_pre_approved_entries_by_unit_date_range
CREATE OR REPLACE FUNCTION public.get_pre_approved_entries_by_unit_date_range(p_apartment_id uuid, p_unit_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
RETURNS TABLE(id integer, apartment_id uuid, unit_id integer, unit_name text, visitor_id bigint, visitor_name text, possible_visitor_id bigint, possible_visitor_name text, visitor_type_id integer, visitor_type_name text, notes text, is_once boolean, valid_from timestamp without time zone, valid_until timestamp without time zone, pin text, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
LANGUAGE sql
AS $function$
SELECT
pae.id,
pae.apartment_id,
pae.unit_id,
u.name AS unit_name,
pae.visitor_id,
CASE
WHEN v.id IS NOT NULL
THEN concat_ws(' ', v.first_name, v.last_name)
ELSE NULL
END AS visitor_name,
pae.possible_visitor_id,
CASE
WHEN pv.id IS NOT NULL
THEN concat_ws(' ', pv.first_name, pv.last_name)
ELSE NULL
END AS possible_visitor_name,
v.visitor_type_id,
vt.name AS visitor_type_name,
pae.notes,
pae.is_once,
pae.valid_from,
pae.valid_until,
pae.pin,
pae.created_by,
concat_ws(' ',
COALESCE(cu.first_name, null),
COALESCE(cu.last_name, null)
) AS created_by_name,
pae.created_on_utc,
pae.modified_by,
concat_ws(' ',
COALESCE(mu.first_name, null),
COALESCE(mu.last_name, null)
) AS modified_by_name,
pae.modified_on_utc
FROM pre_approved_entries pae
LEFT JOIN units u
ON u.id = pae.unit_id
AND u.is_deleted = false
LEFT JOIN visitors v
ON v.id = pae.visitor_id
AND v.is_deleted = false
LEFT JOIN possible_visitors pv
ON pv.id = pae.possible_visitor_id
AND pv.is_deleted = false
LEFT JOIN visitor_types vt
ON vt.id = v.visitor_type_id
AND vt.is_deleted = false
LEFT JOIN users cu
ON cu.id = pae.created_by
AND cu.is_deleted = false
LEFT JOIN users mu
ON mu.id = pae.modified_by
AND mu.is_deleted = false
WHERE
pae.apartment_id = p_apartment_id
AND pae.unit_id = p_unit_id
AND pae.is_deleted = false
-- OVERLAPPING DATE LOGIC
AND pae.valid_from <= p_end_date
AND COALESCE(pae.valid_until, pae.valid_from) >= p_start_date
ORDER BY
pae.valid_from,
pae.id;
$function$
-- Function: get_fcm_tokens_by_roles
CREATE OR REPLACE FUNCTION public.get_fcm_tokens_by_roles(p_apartment_id uuid, p_role_id integer)
RETURNS TABLE(user_id uuid, device_id text, fcm_token text)
LANGUAGE sql
STABLE
AS $function$
SELECT DISTINCT ON (uft.user_id, uft.device_id)
u.id AS user_id,
uft.device_id,
uft.fcm_token
FROM users u
INNER JOIN fdw_common.user_roles ur
ON ur.user_id = u.id
INNER JOIN user_fcm_tokens uft
ON uft.user_id = u.id
WHERE ur.role_id = p_role_id
AND u.company_id = p_apartment_id
AND u.is_deleted = FALSE
AND uft.fcm_token IS NOT NULL
AND uft.device_id IS NOT NULL;
$function$
-- Function: checkin_service_provider
CREATE OR REPLACE FUNCTION public.checkin_service_provider(p_apartment_id uuid, p_pin text, p_created_by uuid, p_entry_time timestamp without time zone)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
v_service_provider_id int;
v_log_id int;
BEGIN
-- Step 1: Validate service provider by apartmentId and pin
SELECT id INTO v_service_provider_id
FROM public.service_providers
WHERE apartment_id = p_apartment_id
AND pin = p_pin
AND is_deleted = false
LIMIT 1;
IF v_service_provider_id IS NULL THEN
RAISE EXCEPTION 'Service provider not found for apartment % with pin %', p_apartment_id, p_pin
USING ERRCODE = 'no_data_found';
END IF;
-- Step 2: Insert service provider log (CheckedIn)
INSERT INTO public.service_provider_logs (
service_provider_id,
current_status_id,
entry_time,
created_on_utc,
created_by
)
VALUES (
v_service_provider_id,
1, -- CheckedIn
p_entry_time,
p_entry_time,
p_created_by
)
RETURNING id INTO v_log_id;
RETURN v_log_id;
END;
$function$
-- Function: get_community_home_memory_snapshot
CREATE OR REPLACE FUNCTION public.get_community_home_memory_snapshot(p_apartment_id uuid)
RETURNS TABLE(announcement jsonb, meeting jsonb, decision jsonb, poll jsonb)
LANGUAGE sql
STABLE
AS $function$
WITH params AS (
SELECT
now() AS now_ts,
now() - interval '3 months' AS last_3_months
)
SELECT
/* ------------------------------------------------------------------
1) Recent Announcement (last 3 months)
------------------------------------------------------------------ */
(
SELECT to_jsonb(a)
FROM (
SELECT
n.id,
n.title,
n.created_on_utc AS "created_on_utc"
FROM notices n
CROSS JOIN params p
WHERE n.apartment_id = p_apartment_id
AND n.is_deleted = false
AND n.created_on_utc >= p.last_3_months
ORDER BY n.created_on_utc DESC
LIMIT 1
) a
) AS announcement,
/* ------------------------------------------------------------------
2) Upcoming Meeting (nearest future occurrence)
------------------------------------------------------------------ */
(
SELECT to_jsonb(m)
FROM (
SELECT
e.id,
e.title,
make_timestamp(
EXTRACT(YEAR FROM eo.occurrence_date)::int,
EXTRACT(MONTH FROM eo.occurrence_date)::int,
EXTRACT(DAY FROM eo.occurrence_date)::int,
EXTRACT(HOUR FROM eo.start_time)::int,
EXTRACT(MINUTE FROM eo.start_time)::int,
0
) AS "meeting_date"
FROM events e
JOIN event_occurrences eo
ON eo.event_id = e.id
CROSS JOIN params p
WHERE e.company_id = p_apartment_id
AND e.event_type_id IN (2, 3, 4) -- Meeting, Committee Meeting, AGM
AND e.is_deleted = false
AND eo.is_deleted = false
AND eo.occurrence_date >= CURRENT_DATE
ORDER BY eo.occurrence_date ASC
LIMIT 1
) m
) AS meeting,
/* ------------------------------------------------------------------
3) Latest Decision (most recent)
------------------------------------------------------------------ */
(
SELECT to_jsonb(d)
FROM (
SELECT
d.id,
d.title,
d.created_on_utc AS "created_on_utc"
FROM decisions d
WHERE d.apartment_id = p_apartment_id
AND d.is_deleted = false
ORDER BY d.created_on_utc DESC
LIMIT 1
) d
) AS decision,
/* ------------------------------------------------------------------
4) Active Poll (currently running)
------------------------------------------------------------------ */
(
SELECT to_jsonb(p)
FROM (
SELECT
p.id,
p.title,
p.start_date AS "starts_on_utc",
p.end_date AS "ends_on_utc"
FROM polls p
WHERE p.apartment_id = p_apartment_id
AND p.is_deleted = false
AND p.end_date >= now()
ORDER BY p.end_date ASC
LIMIT 1
) p
) AS poll;
$function$
-- Function: take_visitor_action
CREATE OR REPLACE FUNCTION public.take_visitor_action(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid, p_action_status integer)
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, approver_first_name text, approver_last_name text, final_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
local_total_visits INT;
local_approved_count INT;
local_rejected_count INT;
local_new_visitor_status INT;
v_pending CONSTANT INT := 1;
v_partial_approved CONSTANT INT := 2;
v_approved CONSTANT INT := 3;
v_rejected CONSTANT INT := 7;
BEGIN
/*
* 1. Update unit-level status (approve / reject)
*/
UPDATE public.multi_unit_visits AS muv
SET visitor_statuses = p_action_status,
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
modified_by = p_resident_user
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.unit_id = p_unit_id
AND muv.is_deleted = FALSE;
IF NOT FOUND THEN
RETURN;
END IF;
/*
* 2. Recalculate unit status counts
*/
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_approved),
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_rejected)
INTO
local_total_visits,
local_approved_count,
local_rejected_count
FROM public.multi_unit_visits AS muv
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.is_deleted = FALSE;
/*
* 3. Decide parent visitor_logs status
*/
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
local_new_visitor_status := v_approved; -- 3 Approved
ELSIF local_approved_count > 0 THEN
local_new_visitor_status := v_partial_approved; -- 2 Partial Approved
ELSIF local_rejected_count > 0 THEN
local_new_visitor_status := v_rejected; -- 7 Rejected
ELSE
local_new_visitor_status := v_pending; -- 1 Pending
END IF;
/*
* 4. Update visitor_logs parent status
*/
UPDATE public.visitor_logs AS vl
SET
visitor_status_id = local_new_visitor_status,
entry_time = CASE
WHEN local_new_visitor_status = v_approved
AND vl.entry_time IS NULL
--THEN (NOW() AT TIME ZONE '+05:30')::time
THEN (NOW() AT TIME ZONE 'Asia/Kolkata')
ELSE vl.entry_time
END,
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
modified_by = p_resident_user
WHERE vl.id = p_visitor_log_id;
/*
* 5. Return response with approver details
*/
RETURN QUERY
SELECT
p_visitor_log_id,
p_unit_id,
r.id,
r.first_name,
r.last_name,
local_new_visitor_status
FROM public.residents r
WHERE r.user_id = p_resident_user
AND r.is_deleted = FALSE;
END;
$function$
-- Function: create_pre_approved_entry_generic
CREATE OR REPLACE FUNCTION public.create_pre_approved_entry_generic(p_apartment_id uuid, p_unit_id integer, p_visitor_type_id integer, p_visitor_id bigint DEFAULT NULL::bigint, p_delivery_company_id integer DEFAULT NULL::integer, p_possible_first_name text DEFAULT NULL::text, p_possible_last_name text DEFAULT NULL::text, p_possible_phone text DEFAULT NULL::text, p_possible_email text DEFAULT NULL::text, p_possible_vehicle text DEFAULT NULL::text, p_possible_notes text DEFAULT NULL::text, p_valid_from timestamp without time zone DEFAULT NULL::timestamp without time zone, p_valid_to timestamp without time zone DEFAULT NULL::timestamp without time zone, p_is_once boolean DEFAULT true, p_notes text DEFAULT NULL::text, p_created_by uuid DEFAULT NULL::uuid, p_schedule_rule jsonb DEFAULT NULL::jsonb)
RETURNS TABLE(pre_approved_entry_id integer, first_name text, last_name text, pin text, valid_from timestamp without time zone, valid_to timestamp without time zone, possible_visitor_id bigint, visitor_id bigint, schedule_days_of_week integer[], schedule_start_time text, schedule_end_time text, schedule_max_entries_per_day integer, schedule_validity_in_months integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_delivery_type_id constant int := 2; -- Delivery from visitor_types table
v_now timestamp := (NOW() AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Kolkata';
v_valid_from timestamp := COALESCE(p_valid_from, NOW());
v_valid_to timestamp := p_valid_to;
v_visitor_id bigint := NULL;
v_possible_visitor_id bigint := NULL;
v_pin text := NULL;
v_attempt int := 0;
v_entry_id int := NULL;
v_days int[] := NULL;
v_start_time time := NULL;
v_end_time time := NULL;
v_max_entries int := NULL;
v_validity_months int := NULL;
v_first_name text := NULL;
v_last_name text := NULL;
BEGIN
-- =======================
-- VALIDATIONS
-- =======================
IF p_apartment_id IS NULL THEN
RAISE EXCEPTION 'ApartmentId is required';
END IF;
IF p_unit_id IS NULL OR p_unit_id <= 0 THEN
RAISE EXCEPTION 'UnitId must be > 0';
END IF;
IF p_created_by IS NULL THEN
RAISE EXCEPTION 'CreatedBy is required';
END IF;
-- normalize visitor id (EF code treats 0 as null)
IF p_visitor_id = 0 THEN
p_visitor_id := NULL;
END IF;
v_visitor_id := p_visitor_id;
-- One-at-a-time per apartment for PIN generation & inserts
PERFORM pg_advisory_xact_lock(hashtext(p_apartment_id::text));
-- =======================
-- STEP 1: Resolve VisitorId via phone
-- =======================
IF v_visitor_id IS NULL AND COALESCE(trim(p_possible_phone), '') <> '' THEN
SELECT vp.visitor_id::bigint
INTO v_visitor_id
FROM public.visitor_phones vp
WHERE vp.phone_number = p_possible_phone
AND vp.is_active = true
ORDER BY vp.id DESC
LIMIT 1;
END IF;
-- =======================
-- STEP 2: Resolve PossibleVisitor via phone
-- =======================
IF v_visitor_id IS NULL AND COALESCE(trim(p_possible_phone), '') <> '' THEN
SELECT pv.id
INTO v_possible_visitor_id
FROM public.possible_visitors pv
WHERE pv.phone_number = p_possible_phone
AND pv.is_deleted = false
ORDER BY pv.id DESC
LIMIT 1;
END IF;
-- =======================
-- STEP 3: Create PossibleVisitor if needed
-- =======================
IF v_visitor_id IS NULL AND v_possible_visitor_id IS NULL THEN
INSERT INTO public.possible_visitors (
first_name,
last_name,
phone_number,
email,
vehicle_number,
notes,
created_on_utc,
created_by,
is_deleted
)
VALUES (
COALESCE(p_possible_first_name, ''),
p_possible_last_name,
p_possible_phone,
p_possible_email,
p_possible_vehicle,
p_possible_notes,
v_now,
p_created_by,
false
)
RETURNING id INTO v_possible_visitor_id;
END IF;
-- =======================
-- STEP 4: Generate Unique PIN
-- Must be unique across:
-- 1) pre_approved_entries
-- 2) service_provider_apartments
-- for the same apartment and overlapping time window
-- =======================
WHILE v_attempt < 10 LOOP
v_attempt := v_attempt + 1;
v_pin := (floor(random() * 900000) + 100000)::int::text;
IF NOT EXISTS (
-- Check visitor pre-approved entries
SELECT 1
FROM public.pre_approved_entries pae
WHERE pae.apartment_id = p_apartment_id
AND pae.is_deleted = false
AND pae.pin = v_pin
AND pae.valid_from <= COALESCE(v_valid_to, 'infinity'::timestamp)
AND COALESCE(pae.valid_until, 'infinity'::timestamp) >= v_valid_from
UNION ALL
-- Check service provider apartment pins
SELECT 1
FROM public.service_provider_apartments spa
WHERE spa.apartment_id = p_apartment_id
AND spa.is_deleted = false
AND spa.is_active = true
AND spa.pin = v_pin
AND spa.valid_from <= COALESCE(v_valid_to, 'infinity'::timestamp)
AND COALESCE(spa.valid_to, 'infinity'::timestamp) >= v_valid_from
) THEN
EXIT;
END IF;
END LOOP;
IF v_pin IS NULL OR v_attempt >= 10 THEN
RAISE EXCEPTION
'Unable to generate a unique PIN for apartment % after % attempts',
p_apartment_id, v_attempt;
END IF;
-- =======================
-- STEP 5: Insert pre_approved_entries
-- =======================
INSERT INTO public.pre_approved_entries (
apartment_id,
unit_id,
notes,
is_once,
valid_from,
valid_until,
created_by,
created_on_utc,
is_deleted,
possible_visitor_id,
visitor_id,
pin
)
VALUES (
p_apartment_id,
p_unit_id,
p_notes,
COALESCE(p_is_once, true),
v_valid_from,
v_valid_to,
p_created_by,
v_now,
false,
v_possible_visitor_id,
v_visitor_id,
v_pin
)
RETURNING id INTO v_entry_id;
-- =======================
-- STEP 6: Delivery company mapping (Delivery = 2)
-- Now supports BOTH visitor_id and possible_visitor_id
-- =======================
IF p_visitor_type_id = v_delivery_type_id
AND p_delivery_company_id IS NOT NULL
AND (v_visitor_id IS NOT NULL OR v_possible_visitor_id IS NOT NULL) THEN
INSERT INTO public.visitor_delivery_company (
visitor_id,
possible_visitor_id,
delivery_company_id,
is_active,
valid_from,
valid_to
)
VALUES (
v_visitor_id,
v_possible_visitor_id,
p_delivery_company_id,
true,
NOW(),
NULL
);
END IF;
-- =======================
-- STEP 7: Schedule rule insert (optional)
-- =======================
IF p_schedule_rule IS NOT NULL THEN
-- Parse JSON safely
-- daysOfWeek (int array)
IF (p_schedule_rule ? 'daysOfWeek') THEN
SELECT COALESCE(array_agg(value::int), NULL)
INTO v_days
FROM jsonb_array_elements_text(p_schedule_rule->'daysOfWeek') AS t(value);
END IF;
-- startTime / endTime as time
IF (p_schedule_rule ? 'startTime') THEN
v_start_time := NULLIF(p_schedule_rule->>'startTime', '')::time;
END IF;
IF (p_schedule_rule ? 'endTime') THEN
v_end_time := NULLIF(p_schedule_rule->>'endTime', '')::time;
END IF;
-- maxEntriesPerDay default 1
v_max_entries :=
COALESCE(NULLIF(p_schedule_rule->>'maxEntriesPerDay', '')::int, 1);
-- validityInMonths nullable
IF (p_schedule_rule ? 'validityInMonths') THEN
v_validity_months := NULLIF(p_schedule_rule->>'validityInMonths', '')::int;
END IF;
INSERT INTO public.pre_approved_schedule_rules (
pre_approved_entry_id,
days_of_week,
start_time,
end_time,
max_entries_per_day,
validity_in_months
)
VALUES (
v_entry_id,
v_days,
v_start_time,
v_end_time,
v_max_entries,
v_validity_months
);
END IF;
-- =======================
-- STEP 8: Resolve names
-- =======================
IF v_visitor_id IS NOT NULL THEN
SELECT v.first_name, v.last_name
INTO v_first_name, v_last_name
FROM public.visitors v
WHERE v.id = v_visitor_id
AND v.is_deleted = false
LIMIT 1;
ELSIF v_possible_visitor_id IS NOT NULL THEN
SELECT pv.first_name, pv.last_name
INTO v_first_name, v_last_name
FROM public.possible_visitors pv
WHERE pv.id = v_possible_visitor_id
AND pv.is_deleted = false
LIMIT 1;
END IF;
-- =======================
-- RETURN
-- =======================
pre_approved_entry_id := v_entry_id;
first_name := v_first_name;
last_name := v_last_name;
pin := v_pin;
valid_from := v_valid_from;
valid_to := v_valid_to;
possible_visitor_id := v_possible_visitor_id;
visitor_id := v_visitor_id;
schedule_days_of_week := v_days;
schedule_start_time := CASE WHEN v_start_time IS NULL THEN NULL ELSE to_char(v_start_time, 'HH24:MI') END;
schedule_end_time := CASE WHEN v_end_time IS NULL THEN NULL ELSE to_char(v_end_time, 'HH24:MI') END;
schedule_max_entries_per_day := v_max_entries;
schedule_validity_in_months := v_validity_months;
RETURN NEXT;
END;
$function$
-- Function: visitor_bulk_check_out
CREATE OR REPLACE FUNCTION public.visitor_bulk_check_out(p_visitor_log_ids bigint[])
RETURNS SETOF visitor_logs
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
UPDATE public.visitor_logs
SET
exit_time = (NOW() AT TIME ZONE 'Asia/Kolkata'),
visitor_status_id = 5, -- Checked Out
created_on_utc = created_on_utc -- no-op, keeps row shape stable
WHERE id = ANY(p_visitor_log_ids)
RETURNING *;
END;
$function$
-- Function: verify_user_password
CREATE OR REPLACE FUNCTION public.verify_user_password(p_user_name text, p_password text)
RETURNS uuid
LANGUAGE plpgsql
AS $function$
DECLARE
v_user_id UUID;
BEGIN
SELECT id
INTO v_user_id
FROM users
WHERE user_name = p_user_name
AND is_deleted = false
AND password_hash = crypt(p_password, password_hash);
RETURN v_user_id;
END;
$function$
-- Function: get_community_memory_snapshot
CREATE OR REPLACE FUNCTION public.get_community_memory_snapshot(p_apartment_id uuid)
RETURNS TABLE(announcement jsonb, meeting jsonb, decision jsonb, poll jsonb)
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN QUERY
SELECT
(
SELECT to_jsonb(a)
FROM notices a
WHERE a.apartment_id = p_apartment_id
AND a.created_on_utc >= now() - interval '3 months'
AND a.is_deleted = false
ORDER BY a.created_on_utc DESC
LIMIT 1
) AS announcement,
(
SELECT to_jsonb(m)
FROM meetings m
WHERE m.apartment_id = p_apartment_id
AND m.meeting_date >= now()
AND m.is_cancelled = false
ORDER BY m.meeting_date ASC
LIMIT 1
) AS meeting,
(
SELECT to_jsonb(d)
FROM decisions d
WHERE d.apartment_id = p_apartment_id
AND d.decision_date >= now() - interval '3 months'
AND d.is_deleted = false
ORDER BY d.decision_date DESC
LIMIT 1
) AS decision,
(
SELECT to_jsonb(p)
FROM polls p
WHERE p.apartment_id = p_apartment_id
AND p.start_date <= now()
AND p.end_date >= now()
AND p.is_deleted = false
ORDER BY p.end_date ASC
LIMIT 1
) AS poll;
END;
$function$
-- Function: get_service_provider_app_context
CREATE OR REPLACE FUNCTION public.get_service_provider_app_context(p_user_id uuid, p_apartment_id uuid)
RETURNS TABLE(service_provider_id integer, role text, category jsonb, verified boolean, onboarded_on timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
sp.id AS service_provider_id,
'Service Provider'::text AS role,
jsonb_build_object(
'id', sp.service_provider_sub_type_id,
'name', sps.name
) AS category,
sp.police_verification_status AS verified,
sp.created_on_utc AS onboarded_on
FROM service_provider_users spu
JOIN service_providers sp
ON sp.id = spu.service_provider_id
AND sp.is_deleted = false
JOIN service_provider_sub_types sps
ON sps.id = sp.service_provider_sub_type_id
JOIN service_provider_apartments spa
ON spa.service_provider_id = sp.id
AND spa.apartment_id = p_apartment_id
AND spa.is_deleted = false
AND spa.is_active = true
WHERE spu.user_id = p_user_id;
END;
$function$
-- Function: get_facility_manager_app_context
CREATE OR REPLACE FUNCTION public.get_facility_manager_app_context(p_user_id uuid, p_apartment_id uuid)
RETURNS TABLE(user_id uuid, role text, apartment_id uuid, apartment_name text, assigned_since timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
ur.user_id,
r.name::text AS role,
a.id AS apartment_id,
a.name::text AS apartment_name,
-- Assigned since = role start date (fallback to created_on)
CASE
WHEN ur.start_date > '0001-01-01' THEN ur.start_date
ELSE ur.created_on_utc
END AS assigned_since
FROM fdw_common.user_roles ur
JOIN roles r
ON r.id = ur.role_id
AND r.id = 11 -- Facility Manager
JOIN organizations o
ON o.id = ur.organization_id
JOIN apartments a
ON a.organization_id = o.id
AND a.id = p_apartment_id
WHERE ur.user_id = p_user_id
AND ur.is_deleted = false
AND (ur.end_date = '0001-01-01' OR ur.end_date > now());
END;
$function$
-- Function: get_security_guard_app_context
CREATE OR REPLACE FUNCTION public.get_security_guard_app_context(p_user_id uuid, p_apartment_id uuid)
RETURNS TABLE(user_id uuid, role text, apartment_id uuid, apartment_name text, assigned_gate_ids integer[], active_since timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
ur.user_id,
r.name::text AS role,
a.id AS apartment_id,
a.name AS apartment_name,
-- FUTURE: security_guard_gates
-- CURRENT: return empty array safely
ARRAY[]::int[] AS assigned_gate_ids,
sga.valid_from
FROM fdw_common.user_roles ur
JOIN roles r
ON r.id = ur.role_id
AND r.name = 'Security Guard'
JOIN public.security_guard_apartments sga
ON sga.user_id = ur.user_id
AND sga.apartment_id = p_apartment_id
AND sga.is_active = true
AND sga.is_deleted = false
JOIN apartments a
ON a.id = p_apartment_id
WHERE ur.user_id = p_user_id
AND ur.is_deleted = false;
END;
$function$
-- Function: postgres_fdw_handler
CREATE OR REPLACE FUNCTION public.postgres_fdw_handler()
RETURNS fdw_handler
LANGUAGE c
STRICT
AS '$libdir/postgres_fdw', $function$postgres_fdw_handler$function$
-- Function: postgres_fdw_validator
CREATE OR REPLACE FUNCTION public.postgres_fdw_validator(text[], oid)
RETURNS void
LANGUAGE c
STRICT
AS '$libdir/postgres_fdw', $function$postgres_fdw_validator$function$
-- Function: postgres_fdw_disconnect
CREATE OR REPLACE FUNCTION public.postgres_fdw_disconnect(text)
RETURNS boolean
LANGUAGE c
PARALLEL RESTRICTED STRICT
AS '$libdir/postgres_fdw', $function$postgres_fdw_disconnect$function$
-- Function: postgres_fdw_disconnect_all
CREATE OR REPLACE FUNCTION public.postgres_fdw_disconnect_all()
RETURNS boolean
LANGUAGE c
PARALLEL RESTRICTED STRICT
AS '$libdir/postgres_fdw', $function$postgres_fdw_disconnect_all$function$
-- Function: postgres_fdw_get_connections
CREATE OR REPLACE FUNCTION public.postgres_fdw_get_connections(check_conn boolean DEFAULT false, OUT server_name text, OUT user_name text, OUT valid boolean, OUT used_in_xact boolean, OUT closed boolean, OUT remote_backend_pid integer)
RETURNS SETOF record
LANGUAGE c
PARALLEL RESTRICTED STRICT
AS '$libdir/postgres_fdw', $function$postgres_fdw_get_connections_1_2$function$
-- Function: get_service_provider_categories
CREATE OR REPLACE FUNCTION public.get_service_provider_categories()
RETURNS TABLE(id integer, name text)
LANGUAGE sql
AS $function$
SELECT spc.id, spc.name
FROM public.service_provider_company_categories spc;
$function$
-- Function: get_security_guard_fcm_tokens
CREATE OR REPLACE FUNCTION public.get_security_guard_fcm_tokens(p_apartment_id uuid)
RETURNS TABLE(user_id uuid, device_id text, fcm_token text)
LANGUAGE sql
STABLE
AS $function$
SELECT DISTINCT ON (uft.user_id, uft.device_id)
u.id AS user_id,
uft.device_id,
uft.fcm_token
FROM users u
INNER JOIN fdw_common.user_roles ur
ON ur.user_id = u.id
INNER JOIN user_fcm_tokens uft
ON uft.user_id = u.id
WHERE ur.role_id = 7
AND u.company_id = p_apartment_id
AND u.is_deleted = FALSE
AND uft.fcm_token IS NOT NULL
AND uft.device_id IS NOT NULL;
$function$
-- Function: get_community_feed
CREATE OR REPLACE FUNCTION public.get_community_feed(p_apartment_id uuid, p_unit_id integer, p_resident_id integer)
RETURNS TABLE(type text, priority text, occurred_on timestamp without time zone, payload jsonb)
LANGUAGE sql
STABLE
AS $function$
/* ------------------------------------------------------------------
📢 Announcements
------------------------------------------------------------------ */
(
SELECT
'Announcement',
'High',
n.created_on_utc,
jsonb_build_object(
'id', n.id,
'title', n.title,
'summary', n.short_description,
'createdBy', u.first_name || ' ' || u.last_name
)
FROM notices n
JOIN users u ON u.id = n.created_by
WHERE n.apartment_id = p_apartment_id
AND n.is_deleted = false
AND u.is_deleted = false
ORDER BY n.created_on_utc DESC
LIMIT 1
)
/* ------------------------------------------------------------------
🗳 Decisions
------------------------------------------------------------------ */
UNION ALL
(
SELECT
'Decision',
'Medium',
d.created_on_utc,
jsonb_build_object(
'id', d.id,
'title', d.title,
'description', d.description,
'decisionOutcome', deo.name,
'createdBy', u.first_name || ' ' || u.last_name
)
FROM decisions d
JOIN decision_outcomes deo ON deo.id = d.decision_outcome_id
JOIN users u ON u.id = d.created_by
WHERE d.apartment_id = p_apartment_id
AND d.is_deleted = false
AND u.is_deleted = false
ORDER BY d.created_on_utc DESC
LIMIT 2
)
/* ------------------------------------------------------------------
📊 Polls (CORRECT & DERIVED)
------------------------------------------------------------------ */
UNION ALL
(
WITH vote_stats AS (
SELECT
pv.poll_id,
COUNT(*) AS total_votes
FROM poll_votes pv
WHERE pv.is_deleted = false
GROUP BY pv.poll_id
),
option_stats AS (
SELECT
pv.poll_option_id,
COUNT(*) AS votes
FROM poll_votes pv
WHERE pv.is_deleted = false
GROUP BY pv.poll_option_id
),
possible_voters AS (
SELECT
p.id AS poll_id,
CASE
WHEN p.poll_eligibility_type_id = 1 THEN (
SELECT COUNT(*)
FROM units u
WHERE u.apartment_id = p_apartment_id
AND u.unit_type_id = 1
AND u.is_deleted = false
)
WHEN p.poll_eligibility_type_id = 2 THEN (
SELECT COUNT(*)
FROM residents r
WHERE r.apartment_id = p_apartment_id
AND r.is_deleted = false
)
ELSE 0
END AS total_possible
FROM polls p
WHERE p.apartment_id = p_apartment_id
)
SELECT
'Poll',
'Medium',
p.start_date,
jsonb_build_object(
'id', p.id,
'title', p.title,
'status', CASE
WHEN p.start_date > now() THEN 'Not Started'
WHEN p.end_date < now() THEN 'Closed'
ELSE 'Ongoing'
END,
'endsAt', p.end_date,
'hasVoted',
EXISTS (
SELECT 1
FROM poll_votes pv
WHERE pv.poll_id = p.id
AND pv.is_deleted = false
AND (
(p.poll_eligibility_type_id = 1 AND pv.unit_id = p_unit_id)
OR (p.poll_eligibility_type_id = 2 AND pv.resident_id = p_resident_id)
)
),
'selectedOptionId',
(
SELECT pv.poll_option_id
FROM poll_votes pv
WHERE pv.poll_id = p.id
AND pv.is_deleted = false
AND (
(p.poll_eligibility_type_id = 1 AND pv.unit_id = p_unit_id)
OR (p.poll_eligibility_type_id = 2 AND pv.resident_id = p_resident_id)
)
LIMIT 1
),
'votesCast', COALESCE(vs.total_votes, 0),
'possibleVoters', pv.total_possible,
'participationPercent',
CASE
WHEN pv.total_possible = 0 THEN 0
ELSE ROUND((COALESCE(vs.total_votes, 0) * 100.0) / pv.total_possible, 1)
END,
'options',
COALESCE(
(
SELECT jsonb_agg(
jsonb_build_object(
'id', po.id,
'text', po.option_text,
'voteCount', COALESCE(os.votes, 0),
'percentage',
CASE
WHEN COALESCE(vs.total_votes, 0) = 0 THEN 0
ELSE ROUND(
(COALESCE(os.votes, 0) * 100.0)
/ COALESCE(vs.total_votes, 0),
1
)
END
)
ORDER BY po.id
)
FROM poll_options po
LEFT JOIN option_stats os ON os.poll_option_id = po.id
WHERE po.poll_id = p.id
),
'[]'::jsonb
)
)
FROM polls p
LEFT JOIN vote_stats vs ON vs.poll_id = p.id
LEFT JOIN possible_voters pv ON pv.poll_id = p.id
WHERE p.apartment_id = p_apartment_id
AND p.is_deleted = false
ORDER BY p.created_on_utc DESC
LIMIT 1
)
/* ------------------------------------------------------------------
📅 Meetings
------------------------------------------------------------------ */
UNION ALL
(
SELECT
'Meeting',
'High',
e.start_time,
jsonb_build_object(
'id', e.id,
'title', e.title,
'description', e.description,
'startsAt', e.start_time,
'endsAt', e.end_time,
'isLive', (e.start_time <= now() AND now() <= e.end_time)
)
FROM events e
WHERE e.company_id = p_apartment_id
AND e.is_deleted = false
ORDER BY e.created_on_utc DESC
LIMIT 1
);
$function$
-- Function: create_poll_vote
CREATE OR REPLACE FUNCTION public.create_poll_vote(p_poll_id integer, p_unit_id integer, p_resident_id integer, p_poll_option_id integer, p_vote_comment text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- 1️⃣ Validate required identifiers
IF p_unit_id IS NULL OR p_resident_id IS NULL THEN
RAISE EXCEPTION 'Both unitId and residentId are required';
END IF;
-- 2️⃣ Validate poll active
IF NOT EXISTS (
SELECT 1
FROM polls
WHERE id = p_poll_id
AND is_deleted = false
AND start_date <= now()
AND end_date >= now()
) THEN
RAISE EXCEPTION 'Poll is not active';
END IF;
-- 3️⃣ Validate option
IF NOT EXISTS (
SELECT 1
FROM poll_options
WHERE id = p_poll_option_id
AND poll_id = p_poll_id
) THEN
RAISE EXCEPTION 'Invalid poll option';
END IF;
-- 4️⃣ Insert vote (unit-based)
INSERT INTO poll_votes (
poll_id,
unit_id,
resident_id,
poll_option_id,
vote_comment,
vote_date,
created_on_utc,
is_deleted
)
VALUES (
p_poll_id,
p_unit_id,
p_resident_id,
p_poll_option_id,
p_vote_comment,
now(),
now(),
false
)
ON CONFLICT ON CONSTRAINT uq_poll_votes_unit_per_poll
DO NOTHING;
END;
$function$
-- Function: create_poll_with_options
CREATE OR REPLACE FUNCTION public.create_poll_with_options(p_apartment_id uuid, p_title character varying, p_description text, p_poll_type_id integer, p_poll_eligibility_type_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone, p_created_by uuid, p_options jsonb)
RETURNS TABLE(poll_id integer, options_count integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_poll_id int;
v_option jsonb;
BEGIN
-- 1️⃣ Validate date range
IF p_end_date <= p_start_date THEN
RAISE EXCEPTION 'Poll end date must be greater than start date';
END IF;
-- 2️⃣ Validate poll type
IF NOT EXISTS (
SELECT 1 FROM poll_types WHERE id = p_poll_type_id
) THEN
RAISE EXCEPTION 'Invalid poll_type_id: %', p_poll_type_id;
END IF;
-- 3️⃣ Validate eligibility type
IF NOT EXISTS (
SELECT 1 FROM poll_eligibility_types WHERE id = p_poll_eligibility_type_id
) THEN
RAISE EXCEPTION 'Invalid poll_eligibility_type_id: %', p_poll_eligibility_type_id;
END IF;
-- 4️⃣ Create poll
INSERT INTO polls (
apartment_id,
title,
description,
poll_type_id,
poll_eligibility_type_id,
status_id,
start_date,
end_date,
created_on_utc,
created_by,
is_deleted
)
VALUES (
p_apartment_id,
p_title,
p_description,
p_poll_type_id,
p_poll_eligibility_type_id,
1, -- Draft / Active (adjust if needed)
p_start_date,
p_end_date,
now(),
p_created_by,
false
)
RETURNING id INTO v_poll_id;
-- 5️⃣ Insert options
FOR v_option IN
SELECT * FROM jsonb_array_elements(p_options)
LOOP
INSERT INTO poll_options (
poll_id,
option_text,
option_type_id
)
VALUES (
v_poll_id,
v_option ->> 'text',
1
);
END LOOP;
-- 6️⃣ Return
RETURN QUERY
SELECT
v_poll_id,
jsonb_array_length(p_options);
END;
$function$
-- Function: update_visitor_status
CREATE OR REPLACE FUNCTION public.update_visitor_status(p_visitor_log_id integer, p_unit_id integer, p_visitor_status_id integer, p_modified_by uuid, p_is_manual_approval boolean DEFAULT false, p_approval_source integer DEFAULT 1, p_approval_reason integer DEFAULT 0, p_approved_by_resident_id integer DEFAULT NULL::integer)
RETURNS TABLE(visitor_id integer, total_visits_count integer, approved_visits_count integer, final_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
local_total_visits int;
local_approved_count int;
local_rejected_count int; -- Added to track rejections
local_new_visitor_status int;
BEGIN
-- Step 1: Update the status for the specific unit
UPDATE public.multi_unit_visits
SET visitor_statuses = p_visitor_status_id,
is_manual_approval = COALESCE(p_is_manual_approval, false),
approval_source = COALESCE(p_approval_source, 1),
approval_reason = COALESCE(p_approval_reason, 0),
approved_by_resident_id = p_approved_by_resident_id,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE visitor_log_id = p_visitor_log_id
AND unit_id = p_unit_id
AND is_deleted = false;
-- Step 2: Check if the update was successful.
IF NOT FOUND THEN
RETURN;
END IF;
-- Step 3: Recalculate the overall visitor log status.
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2), -- Approved
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6) -- Rejected
INTO
local_total_visits,
local_approved_count,
local_rejected_count
FROM public.multi_unit_visits AS muv
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.is_deleted = false;
-- Step 4: Determine the new overall status with corrected logic.
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
local_new_visitor_status := 2; -- Rule 1: All units have approved.
ELSIF local_approved_count > 0 THEN
-- FIX: If at least one unit approves, the status is "Partial Approved",
-- even if another unit rejects.
local_new_visitor_status := 7; -- Rule 2: At least one approval, but not all.
ELSIF local_rejected_count > 0 THEN
local_new_visitor_status := 6; -- Rule 3: No approvals yet, but at least one rejection.
ELSE
local_new_visitor_status := 1; -- Rule 4: No approvals and no rejections yet.
END IF;
-- Step 5: Update the parent visitor_logs table.
UPDATE public.visitor_logs
SET visitor_status_id = local_new_visitor_status,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE id = p_visitor_log_id;
-- Step 6: On success, return the calculated values.
RETURN QUERY SELECT p_visitor_log_id, local_total_visits, local_approved_count, local_new_visitor_status;
END;
$function$
-- Function: get_unit_visitors
CREATE OR REPLACE FUNCTION public.get_unit_visitors(p_unit_id integer, p_for_date date DEFAULT CURRENT_DATE)
RETURNS TABLE(id integer, source text, visitor_id bigint, possible_visitor_id bigint, pre_approved_entry_id integer, unit_id integer, visitor_type_id integer, visitor_type text, visitor_name text, phone_number text, vehicle_number text, entry_time timestamp without time zone, exit_time timestamp without time zone, expected_from timestamp without time zone, expected_until timestamp without time zone, visitor_status_id integer, visitor_status_name text)
LANGUAGE sql
STABLE
AS $function$
WITH unit_ctx AS (
SELECT
u.id AS unit_id,
u.apartment_id AS apartment_id
FROM units u
WHERE u.id = p_unit_id
AND u.is_deleted = false
)
SELECT
ROW_NUMBER() OVER (
ORDER BY
entry_time DESC NULLS LAST,
expected_from DESC NULLS LAST
)::int AS id,
q.*
FROM (
/* ===============================
1️⃣ ACTUAL VISITS (LOG)
=============================== */
SELECT
'LOG' AS source,
v.id AS visitor_id,
NULL::bigint AS possible_visitor_id,
vl.pre_approved_entry_id,
muv.unit_id,
v.visitor_type_id,
vt.name AS visitor_type,
concat_ws(' ', v.first_name, v.last_name) AS visitor_name,
NULL::text AS phone_number,
vl.vehicle_number,
vl.entry_time,
vl.exit_time,
NULL::timestamp AS expected_from,
NULL::timestamp AS expected_until,
muv.visitor_statuses AS visitor_status_id,
vs.name AS visitor_status_name
FROM unit_ctx uc
JOIN visitor_logs vl
ON vl.apartment_id = uc.apartment_id
Join visitor_types vt
On vl.visitor_type_id = vt.id
JOIN visitors v
ON v.id = vl.visitor_id
AND v.is_deleted = false
JOIN multi_unit_visits muv
ON muv.visitor_log_id = vl.id
AND muv.is_deleted = false
AND muv.unit_id = uc.unit_id
JOIN visitor_statuses vs
ON vs.id = muv.visitor_statuses
AND vs.is_deleted = false
WHERE
DATE(vl.created_on_utc) = p_for_date
UNION ALL
/* ===============================
2️⃣ EXPECTED VISITORS (PRE_APPROVED)
=============================== */
SELECT
'PRE_APPROVED' AS source,
pv2.id AS visitor_id, -- optional (known visitor)
pv.id AS possible_visitor_id,
pae.id AS pre_approved_entry_id,
pae.unit_id,
pv.visitor_type_id,
vt.name AS visitor_type,
concat_ws(' ', pv.first_name, pv.last_name) AS visitor_name,
pv.phone_number,
pv.vehicle_number,
NULL::timestamp AS entry_time,
NULL::timestamp AS exit_time,
pae.valid_from AS expected_from,
pae.valid_until AS expected_until,
8 AS visitor_status_id, -- virtual EXPECTED
'Expected' AS visitor_status_name
FROM unit_ctx uc
JOIN pre_approved_entries pae
ON pae.unit_id = uc.unit_id
AND pae.apartment_id = uc.apartment_id
AND pae.is_deleted = false
JOIN possible_visitors pv
ON pv.id = pae.possible_visitor_id
AND pv.is_deleted = false
Join visitor_types vt
On pv.visitor_type_id = vt.id
LEFT JOIN visitors pv2
ON pv2.id = pae.visitor_id
AND pv2.is_deleted = false
WHERE
pae.valid_from::date <= p_for_date
AND (pae.valid_until IS NULL OR pae.valid_until::date >= p_for_date)
) q;
$function$
-- Function: get_visitors_inside_with_units
CREATE OR REPLACE FUNCTION public.get_visitors_inside_with_units(p_apartment_id uuid, p_today_only boolean DEFAULT false)
RETURNS TABLE(id integer, visitor_log_id bigint, visitor_id bigint, first_name text, last_name text, visitor_type_id integer, visitor_type_name text, visiting_units jsonb, entry_time timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
CAST(row_number() OVER (ORDER BY vl.entry_time DESC) AS integer) AS id,
vl.id AS visitor_log_id,
v.id AS visitor_id,
v.first_name,
v.last_name,
v.visitor_type_id,
vt.name::text AS visitor_type_name,
COALESCE(
jsonb_agg(
jsonb_build_object(
'unitId', u.id,
'unitName', u.name
)
ORDER BY u.id
) FILTER (WHERE u.id IS NOT NULL),
'[]'::jsonb
) AS visiting_units,
vl.entry_time
FROM
visitor_logs vl
JOIN
visitors v ON v.id = vl.visitor_id
JOIN
visitor_types vt ON vt.id = vl.visitor_type_id
LEFT JOIN
multi_unit_visits muv
ON muv.visitor_log_id = vl.id
AND muv.is_deleted = FALSE
LEFT JOIN
units u
ON u.id = muv.unit_id
AND u.is_deleted = FALSE
And u.apartment_id = p_apartment_id
WHERE
vl.apartment_id = p_apartment_id
AND vl.exit_time IS NULL
AND v.is_deleted = FALSE
AND vt.is_deleted = FALSE
--AND (p_today_only = FALSE OR vl.entry_time::date = CURRENT_DATE)
AND (NOT p_today_only OR vl.entry_time::date = CURRENT_DATE)
GROUP BY
vl.id,
v.id,
v.first_name,
v.last_name,
v.visitor_type_id,
vt.name,
vl.entry_time
ORDER BY
vl.entry_time DESC;
END;
$function$
-- Function: get_visitors_inside_counts_by_type
CREATE OR REPLACE FUNCTION public.get_visitors_inside_counts_by_type(p_apartment_id uuid, p_today_only boolean)
RETURNS TABLE(id integer, visitor_type_id integer, visitor_type_name text, count integer)
LANGUAGE sql
AS $function$
SELECT
ROW_NUMBER() OVER (ORDER BY vt.id)::integer AS id,
vt.id AS visitor_type_id,
vt.name::text AS visitor_type_name,
COUNT(*) AS count
FROM visitor_logs vl
JOIN visitor_types vt ON vt.id = vl.visitor_type_id
JOIN visitors v ON v.id = vl.visitor_id
WHERE
vl.apartment_id = p_apartment_id
AND vl.exit_time IS NULL
AND (NOT p_today_only OR vl.entry_time::date = CURRENT_DATE)
AND v.is_deleted = FALSE
AND vt.is_deleted = FALSE
GROUP BY
vt.id,
vt.name
ORDER BY
vt.id;
$function$
-- Function: get_unit_related_details
CREATE OR REPLACE FUNCTION public.get_unit_related_details(unit_id integer)
RETURNS jsonb
LANGUAGE sql
AS $function$
SELECT jsonb_build_object(
'id', u.id,
'name', u.name,
'unit_type_id', u.unit_type_id,
'phone_extension', u.phone_extention,
'area', u.area,
'bhk_type', u.bhk_type,
'occupant_type_id', u.occupant_type_id,
'occupancy_type_id', u.occupancy_type_id,
'residents', COALESCE(
(SELECT jsonb_agg(DISTINCT jsonb_build_object(
'id', r.id,
'first_name', r.first_name,
'last_name', r.last_name,
'email', r.email,
'contact_number', r.contact_number,
'resident_type', rt.name
))
FROM resident_units ru
JOIN residents r ON r.id = ru.resident_id AND r.is_deleted = false
JOIN resident_types rt ON rt.id = r.resident_type_id AND rt.is_deleted = false
WHERE ru.unit_id = u.id AND ru.is_deleted = false),
'[]'::jsonb),
'vehicles', COALESCE(
(SELECT jsonb_agg(DISTINCT jsonb_build_object(
'id', v.id,
'vehicle_number', v.vehicle_number,
'vehicle_type', vt.name
))
FROM vehicles v
JOIN vehicle_types vt ON vt.id = v.vehicle_type_id AND vt.is_deleted = false
WHERE v.unit_id = u.id AND v.is_deleted = false),
'[]'::jsonb)
)
FROM units u
WHERE u.id = unit_id AND u.is_deleted = false;
$function$
-- Function: approve_visitor_for_unit
CREATE OR REPLACE FUNCTION public.approve_visitor_for_unit(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid, p_resident_id integer)
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, final_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
local_total_visits INT;
local_approved_count INT;
local_rejected_count INT;
local_new_visitor_status INT;
BEGIN
-- Step 1: Approve for this unit only
UPDATE public.multi_unit_visits
SET visitor_statuses = 2, -- Approved
modified_on_utc = NOW(),
modified_by = p_resident_user
WHERE visitor_log_id = p_visitor_log_id
AND unit_id = p_unit_id
AND is_deleted = FALSE;
IF NOT FOUND THEN
-- No update happened (invalid visitor_log_id or unit_id)
RETURN;
END IF;
-- Step 2: Recalculate parent visitor log status
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2), -- Approved
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6) -- Rejected
INTO
local_total_visits,
local_approved_count,
local_rejected_count
FROM public.multi_unit_visits muv
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.is_deleted = FALSE;
-- Step 3: Decide new parent status
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
local_new_visitor_status := 2; -- All Approved
ELSIF local_approved_count > 0 THEN
local_new_visitor_status := 7; -- Partial Approved
ELSIF local_rejected_count > 0 THEN
local_new_visitor_status := 6; -- Rejected
ELSE
local_new_visitor_status := 1; -- Pending
END IF;
UPDATE public.visitor_logs
SET visitor_status_id = local_new_visitor_status,
modified_on_utc = NOW(),
modified_by = p_resident_user
WHERE id = p_visitor_log_id;
-- Step 4: Return minimal information for the service layer
RETURN QUERY
SELECT
p_visitor_log_id,
p_unit_id,
p_resident_id, -- approver resident
local_new_visitor_status;
END;
$function$
-- Function: get_notice_list_by_date_range
CREATE OR REPLACE FUNCTION public.get_notice_list_by_date_range(p_apartment_id uuid, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
RETURNS TABLE(id bigint, title character varying, short_description character varying, category_id integer, category_name character varying, priority_id integer, priority_name character varying, expires_on timestamp without time zone, created_by uuid, created_by_name character varying, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name character varying, modified_on_utc timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
n.id::bigint AS id,
n.title::varchar AS title,
n.short_description::varchar AS short_description,
n.category_id AS category_id,
nc.name::varchar AS category_name,
n.priority_id AS priority_id,
np.name::varchar AS priority_name,
n.expires_on AS expires_on,
n.created_by AS created_by,
(cu.first_name || ' ' || cu.last_name)::varchar AS created_by_name,
n.created_on_utc AS created_on_utc,
n.modified_by AS modified_by,
COALESCE(
(mu.first_name || ' ' || mu.last_name)::varchar,
''
) AS modified_by_name,
n.modified_on_utc AS modified_on_utc
FROM notices n
JOIN notice_categories nc ON n.category_id = nc.id
JOIN notice_priorities np ON n.priority_id = np.id
JOIN users cu ON n.created_by = cu.id
LEFT JOIN users mu ON n.modified_by = mu.id
WHERE
n.is_deleted = false
AND n.apartment_id = p_apartment_id
AND n.expires_on >= p_start_date
AND n.expires_on < p_end_date
ORDER BY n.expires_on DESC, n.id DESC;
END;
$function$
-- Function: get_visitor_multiple_unit_logs
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_visitor_multiple_unit_logs(v_apartment_id uuid, log_date timestamp without time zone)
RETURNS TABLE(id bigint, visitor_id bigint, first_name text, last_name text, unit_details jsonb, latest_entry_time timestamp without time zone, latest_exit_time timestamp without time zone, contact_number text, visitor_type_id integer, visitor_type_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
vl.id,
vl.visitor_id::BIGINT AS visitor_id,
v.first_name,
v.last_name,
/* units aggregation */
COALESCE(
jsonb_agg(
jsonb_build_object(
'unit_id', muv.unit_id,
'unit_name', u.name
)
) FILTER (WHERE muv.unit_id IS NOT NULL),
'[]'::jsonb
) AS unit_details,
/* entry / exit from log */
vl.entry_time AS latest_entry_time,
vl.exit_time AS latest_exit_time,
/* single phone per visitor (no fan-out, no ambiguity) */
vp.phone_number AS contact_number,
/* visitor type from log */
vl.visitor_type_id,
vt.name::TEXT AS visitor_type_name
FROM visitor_logs vl
/* visitor metadata */
LEFT JOIN visitors v
ON v.id = vl.visitor_id
AND v.is_deleted = FALSE
/* SAFE phone join (fixes ambiguity + fan-out) */
LEFT JOIN LATERAL (
SELECT vp2.phone_number
FROM visitor_phones vp2
WHERE vp2.visitor_id = vl.visitor_id
ORDER BY vp2.id
LIMIT 1
) vp ON true
/* unit mapping */
LEFT JOIN public.multi_unit_visits muv
ON muv.visitor_log_id = vl.id
AND muv.is_deleted = FALSE
LEFT JOIN units u
ON u.id = muv.unit_id
/* type lookup from LOG */
LEFT JOIN visitor_types vt
ON vt.id = vl.visitor_type_id
WHERE
vl.apartment_id = v_apartment_id
AND vl.created_on_utc >= date_trunc('day', log_date)
AND vl.created_on_utc < date_trunc('day', log_date) + interval '1 day'
GROUP BY
vl.id,
vl.visitor_id,
v.first_name,
v.last_name,
vl.entry_time,
vl.exit_time,
vp.phone_number,
vl.visitor_type_id,
vt.name
ORDER BY MAX(vl.created_on_utc) DESC;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_visitor_multiple_unit_logs(v_apartment_id uuid, log_date timestamp without time zone)
RETURNS TABLE(id integer, visitor_id integer, first_name text, last_name text, unit_id integer, unit_name text, latest_entry_time timestamp without time zone, latest_exit_time timestamp without time zone, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
vl.id AS id,
v.id AS visitor_id,
v.first_name,
v.last_name,
muv.unit_id,
u.name AS unit_name,
MAX(vl.entry_time) AS latest_entry_time,
MAX(vl.exit_time) AS latest_exit_time,
vl.visiting_from,
v.contact_number,
v.visitor_type_id,
vt.name :: text AS visitor_type_name
FROM
visitors v
JOIN
visitor_logs vl ON v.id = vl.visitor_id
LEFT JOIN
multi_unit_visits muv ON muv.visitor_log_id = vl.id
LEFT JOIN
units u ON u.id = muv.unit_id
LEFT JOIN
visitor_types vt ON vt.id = v.visitor_type_id
WHERE
v.apartment_id = v_apartment_id
AND DATE(vl.entry_time) = DATE(log_date)
AND v.is_deleted = FALSE
AND vl.is_deleted = FALSE
AND muv.is_deleted = FALSE
GROUP BY
vl.id, v.id, v.first_name, v.last_name, muv.unit_id, u.name,
vl.visiting_from, v.contact_number, v.visitor_type_id, vt.name;
END;
$function$
-- Function: get_apartment_visitors
CREATE OR REPLACE FUNCTION public.get_apartment_visitors(p_apartment_id uuid, p_unit_id integer DEFAULT NULL::integer, p_for_date date DEFAULT CURRENT_DATE)
RETURNS TABLE(row_id integer, source text, visitor_id bigint, possible_visitor_id bigint, pre_approved_entry_id integer, unit_id integer, visitor_type_id integer, visitor_name text, phone_number text, vehicle_number text, entry_time timestamp without time zone, exit_time timestamp without time zone, expected_from timestamp without time zone, expected_until timestamp without time zone, visitor_status_id integer, visitor_status_name text)
LANGUAGE sql
STABLE
AS $function$
SELECT
ROW_NUMBER() OVER (ORDER BY
entry_time DESC NULLS LAST,
expected_from DESC NULLS LAST
)::int AS row_id,
q.*
FROM (
/* ===============================
1️⃣ ACTUAL VISITS (LOG)
=============================== */
SELECT
'LOG' AS source,
v.id AS visitor_id,
NULL::bigint AS possible_visitor_id,
vl.pre_approved_entry_id,
muv.unit_id,
v.visitor_type_id,
concat_ws(' ', v.first_name, v.last_name) AS visitor_name,
NULL::text AS phone_number,
vl.vehicle_number,
vl.entry_time,
vl.exit_time,
NULL::timestamp AS expected_from,
NULL::timestamp AS expected_until,
muv.visitor_statuses AS visitor_status_id,
vs.name AS visitor_status_name
FROM visitor_logs vl
JOIN visitors v
ON v.id = vl.visitor_id
AND v.is_deleted = false
JOIN multi_unit_visits muv
ON muv.visitor_log_id = vl.id
AND muv.is_deleted = false
JOIN visitor_statuses vs
ON vs.id = muv.visitor_statuses
AND vs.is_deleted = false
WHERE
vl.apartment_id = p_apartment_id
AND DATE(vl.created_on_utc) = p_for_date
AND (p_unit_id IS NULL OR muv.unit_id = p_unit_id)
UNION ALL
/* ===============================
2️⃣ EXPECTED VISITORS
=============================== */
SELECT
'PRE_APPROVED' AS source,
pv2.id AS visitor_id,
pv.id AS possible_visitor_id,
pae.id AS pre_approved_entry_id,
pae.unit_id,
pv.visitor_type_id,
concat_ws(' ', pv.first_name, pv.last_name) AS visitor_name,
pv.phone_number,
pv.vehicle_number,
NULL::timestamp AS entry_time,
NULL::timestamp AS exit_time,
pae.valid_from AS expected_from,
pae.valid_until AS expected_until,
8 AS visitor_status_id,
'Expected' AS visitor_status_name
FROM pre_approved_entries pae
JOIN possible_visitors pv
ON pv.id = pae.possible_visitor_id
AND pv.is_deleted = false
LEFT JOIN visitors pv2
ON pv2.id = pae.visitor_id
AND pv2.is_deleted = false
WHERE
pae.apartment_id = p_apartment_id
AND pae.is_deleted = false
AND pae.valid_from::date <= p_for_date
AND (pae.valid_until IS NULL OR pae.valid_until::date >= p_for_date)
AND (p_unit_id IS NULL OR pae.unit_id = p_unit_id)
) q;
$function$
-- Function: take_visitor_action
CREATE OR REPLACE FUNCTION public.take_visitor_action(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid, p_action_status integer, p_approval_reason integer, p_approval_source integer, p_approved_by_resident_id integer, p_is_manual_approval boolean)
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, approver_first_name text, approver_last_name text, final_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
local_total_visits INT;
local_approved_count INT;
local_rejected_count INT;
local_new_visitor_status INT;
v_pending CONSTANT INT := 1;
v_partial_approved CONSTANT INT := 2;
v_approved CONSTANT INT := 3;
v_rejected CONSTANT INT := 7;
v_checked_in CONSTANT INT := 4;
BEGIN
/*
* 1. Update unit-level status (approve / reject)
*/
UPDATE public.multi_unit_visits AS muv
SET visitor_statuses = p_action_status,
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
modified_by = p_resident_user,
approval_reason = p_approval_reason,
approval_source = p_approval_source,
approved_by_resident_id = p_approved_by_resident_id,
is_manual_approval = p_is_manual_approval
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.unit_id = p_unit_id
AND muv.is_deleted = FALSE;
IF NOT FOUND THEN
RETURN;
END IF;
/*
* 2. Recalculate unit status counts
*/
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_approved),
COUNT(*) FILTER (WHERE muv.visitor_statuses = v_rejected)
INTO
local_total_visits,
local_approved_count,
local_rejected_count
FROM public.multi_unit_visits AS muv
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.is_deleted = FALSE;
/*
* 3. Decide parent visitor_logs status
*/
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
local_new_visitor_status := v_approved; -- 3 Approved
ELSIF local_approved_count > 0 THEN
local_new_visitor_status := v_partial_approved; -- 2 Partial Approved
ELSIF local_rejected_count > 0 THEN
local_new_visitor_status := v_rejected; -- 7 Rejected
ELSE
local_new_visitor_status := v_pending; -- 1 Pending
END IF;
/*
* 4. Update visitor_logs parent status
*/
UPDATE public.visitor_logs AS vl
SET
--visitor_status_id = local_new_visitor_status,
visitor_status_id = CASE
WHEN (
(local_new_visitor_status IN (v_approved, v_partial_approved)
AND vl.entry_time IS NULL)
OR COALESCE(p_is_manual_approval, false) = true
)
THEN v_checked_in
ELSE visitor_status_id
END,
entry_time = CASE
WHEN (
(local_new_visitor_status IN (v_approved, v_partial_approved)
AND vl.entry_time IS NULL)
OR COALESCE(p_is_manual_approval, false) = true
)
AND vl.entry_time IS NULL
THEN (NOW() AT TIME ZONE 'Asia/Kolkata')
ELSE vl.entry_time
END,
modified_on_utc = (NOW() AT TIME ZONE 'UTC'),
modified_by = p_resident_user
WHERE vl.id = p_visitor_log_id;
/*
* 5. Return response with approver details
*/
RETURN QUERY
SELECT
p_visitor_log_id,
p_unit_id,
r.id,
r.first_name,
r.last_name,
local_new_visitor_status
FROM public.residents r
WHERE r.user_id = p_resident_user
AND r.is_deleted = FALSE;
END;
$function$
-- Function: create_poll
CREATE OR REPLACE FUNCTION public.create_poll(p_apartment_id uuid, p_title character varying, p_description text, p_poll_type_id integer, p_poll_eligibility_type integer, p_status_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone, p_created_by uuid)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
v_poll_id int;
BEGIN
/* -----------------------------------------
1️⃣ Validate eligibility type
----------------------------------------- */
IF p_poll_eligibility_type NOT IN (1, 2) THEN
RAISE EXCEPTION
'Invalid poll eligibility type %',
p_poll_eligibility_type;
END IF;
/* -----------------------------------------
2️⃣ Create poll (NO SNAPSHOT DATA)
----------------------------------------- */
INSERT INTO polls (
apartment_id,
title,
description,
poll_type_id,
poll_eligibility_type_id,
status_id,
start_date,
end_date,
created_on_utc,
created_by,
is_deleted
)
VALUES (
p_apartment_id,
p_title,
p_description,
p_poll_type_id,
p_poll_eligibility_type,
p_status_id,
p_start_date,
p_end_date,
(NOW() AT TIME ZONE 'UTC'),
p_created_by,
false
)
RETURNING id INTO v_poll_id;
RETURN v_poll_id;
END;
$function$
-- Function: get_visitors_by_unit_and_date_range
CREATE OR REPLACE FUNCTION public.get_visitors_by_unit_and_date_range(p_apartment_id uuid, p_unit_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
RETURNS TABLE(id bigint, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_status_id integer, visitor_status text, first_name text, last_name text, image_url text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type text, vehicle_number text, identity_type_id integer, identity_type text, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
vl.id AS id,
vl.entry_time AS entry_time,
vl.exit_time AS exit_time,
vl.visitor_status_id AS visitor_status_id,
vs.name::text AS visitor_status,
v.first_name::text,
v.last_name::text,
COALESCE(v.image_url, '')::text AS image_url,
ve.email::text,
vd.visiting_from::text,
vp.phone_number::text AS contact_number,
vl.visitor_type_id AS visitor_type_id,
vt.name::text AS visitor_type, -- ✅ FIX
COALESCE(vd.vehicle_number, vl.vehicle_number)::text AS vehicle_number,
vi.identity_type_id,
it.name::text AS identity_type, -- ✅ FIX
vi.identity_number::text,
v.created_by,
v.created_on_utc,
v.modified_by,
v.modified_on_utc
FROM multi_unit_visits muv
JOIN visitor_logs vl
ON vl.id = muv.visitor_log_id
JOIN visitors v
ON v.id = vl.visitor_id
AND v.is_deleted = false
JOIN visitor_types vt
ON vt.id = vl.visitor_type_id
AND vt.is_deleted = false
JOIN visitor_statuses vs
ON vs.id = vl.visitor_status_id
AND vs.is_deleted = false
JOIN units u
ON u.id = muv.unit_id
AND u.apartment_id = p_apartment_id
AND u.is_deleted = false
-- OPTIONAL DATA (SAFE LEFT JOINS)
LEFT JOIN visitor_details vd
ON vd.visitor_id = v.id
LEFT JOIN visitor_emails ve
ON ve.visitor_id = v.id
AND ve.is_active = true
LEFT JOIN visitor_phones vp
ON vp.visitor_id = v.id
AND vp.is_active = true
LEFT JOIN visitor_identities vi
ON vi.visitor_id = v.id
AND vi.is_active = true
LEFT JOIN identity_types it
ON it.id = vi.identity_type_id
AND it.is_deleted = false
WHERE
muv.unit_id = p_unit_id
AND muv.is_deleted = false
AND (
(vl.entry_time IS NOT NULL AND vl.entry_time >= p_start_date AND vl.entry_time < p_end_date)
OR
(vl.entry_time IS NULL AND vl.created_on_utc BETWEEN p_start_date AND p_end_date)
)
ORDER BY vl.entry_time DESC;
END;
$function$
-- Function: approve_visitor_for_unit
CREATE OR REPLACE FUNCTION public.approve_visitor_for_unit(p_visitor_log_id integer, p_unit_id integer, p_resident_user uuid)
RETURNS TABLE(visitor_log_id integer, unit_id integer, approver_resident_id integer, approver_first_name text, approver_last_name text, final_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
local_total_visits INT;
local_approved_count INT;
local_rejected_count INT;
local_new_visitor_status INT;
local_resident_id INT;
BEGIN
-- Step 0: Resolve user_id → resident_id
SELECT r.id
INTO local_resident_id
FROM public.residents r
INNER JOIN public.resident_units ru ON ru.resident_id = r.id
WHERE r.user_id = p_resident_user
AND ru.unit_id = p_unit_id
AND r.is_deleted = FALSE
AND ru.is_deleted = FALSE
LIMIT 1;
IF local_resident_id IS NULL THEN
-- invalid user or unit
RETURN;
END IF;
-- Step 1: Approve for this unit only
UPDATE public.multi_unit_visits
SET visitor_statuses = 2, -- Approved
modified_on_utc = NOW(),
modified_by = p_resident_user
WHERE visitor_log_id = p_visitor_log_id
AND unit_id = p_unit_id
AND is_deleted = FALSE;
IF NOT FOUND THEN
RETURN;
END IF;
-- Step 2: Recalculate parent visitor log status
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2),
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6)
INTO
local_total_visits,
local_approved_count,
local_rejected_count
FROM public.multi_unit_visits muv
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.is_deleted = FALSE;
-- Step 3: Decide new parent status
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
local_new_visitor_status := 2; -- All Approved
ELSIF local_approved_count > 0 THEN
local_new_visitor_status := 7; -- Partial Approved
ELSIF local_rejected_count > 0 THEN
local_new_visitor_status := 6; -- Rejected
ELSE
local_new_visitor_status := 1; -- Pending
END IF;
UPDATE public.visitor_logs
SET visitor_status_id = local_new_visitor_status,
modified_on_utc = NOW(),
modified_by = p_resident_user
WHERE id = p_visitor_log_id;
-- Step 4: Return info + approver's name
RETURN QUERY
SELECT
p_visitor_log_id,
p_unit_id,
r.id,
r.first_name,
r.last_name,
local_new_visitor_status
FROM public.residents r
WHERE r.id = local_resident_id
AND r.is_deleted = FALSE;
END;
$function$
-- Function: get_current_visitors_and_service_providers
CREATE OR REPLACE FUNCTION public.get_current_visitors_and_service_providers(p_unit_id integer)
RETURNS TABLE(id integer, visitor_type_id integer, visitor_type text, serial_id integer, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_person_type text, visitor_person_sub_type text, visitor_status_id integer, visitor_status text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
CAST(ROW_NUMBER() OVER (ORDER BY combined.entry_time, combined.id) AS INT) AS serial_id,
combined.visitor_type_id,
combined.visitor_type,
combined.id,
combined.first_name,
combined.last_name,
combined.entry_time,
combined.exit_time,
combined.visitor_person_type,
combined.visitor_person_sub_type,
combined.visitor_status_id,
combined.visitor_status_name
FROM (
-- Visitors
SELECT
vt.id AS visitor_type_id,
'visitor'::TEXT AS visitor_type,
v.id,
v.first_name::TEXT,
v.last_name::TEXT,
vl.entry_time,
vl.exit_time,
vt."name"::TEXT AS visitor_person_type,
NULL::TEXT AS visitor_person_sub_type,
CASE
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 3
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 4
ELSE vl.visitor_status_id
END AS visitor_status_id,
CASE
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 'Checked In'
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 'Checked Out'
ELSE vs."name"::TEXT
END AS visitor_status_name
FROM
public.visitor_logs vl
JOIN public.visitors v ON vl.visitor_id = v.id
JOIN public.multi_unit_visits muv ON muv.visitor_log_id = vl.id
JOIN public.visitor_types vt ON v.visitor_type_id = vt.id
LEFT JOIN public.visitor_statuses vs ON vl.visitor_status_id = vs.id
WHERE
muv.unit_id = p_unit_id
AND vl.entry_time >= NOW() - INTERVAL '120 hour'
AND vl.is_deleted = FALSE
AND v.is_deleted = FALSE
AND muv.is_deleted = FALSE
UNION ALL
-- Service Providers
SELECT
vt_sp.id AS visitor_type_id,
'service_provider'::TEXT AS visitor_type,
sp.id,
sp.first_name::TEXT,
sp.last_name::TEXT,
spl.entry_time,
spl.exit_time,
spt."name"::TEXT AS visitor_person_type,
spst."name"::TEXT AS visitor_person_sub_type,
CASE
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 3
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 4
ELSE NULL
END AS visitor_status_id,
CASE
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 'Checked In'
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 'Checked Out'
ELSE NULL::TEXT
END AS visitor_status_name
FROM
public.service_provider_logs spl
JOIN public.service_providers sp ON spl.service_provider_id = sp.id
JOIN public.service_provider_types spt ON sp.service_provider_type_id = spt.id
JOIN public.service_provider_sub_types spst ON sp.service_provider_sub_type_id = spst.id
JOIN public.multi_unit_visits muv ON muv.unit_id = p_unit_id
LEFT JOIN public.visitor_types vt_sp ON vt_sp."name" = 'Service Provider' AND vt_sp.is_deleted = FALSE
WHERE
spl.entry_time >= NOW() - INTERVAL '120 hour'
AND spl.is_deleted = FALSE
AND sp.is_deleted = FALSE
) AS combined;
END;
$function$
-- Function: update_visitor_status
CREATE OR REPLACE FUNCTION public.update_visitor_status(p_visitor_log_id integer, p_unit_id integer, p_visitor_status_id integer, p_modified_by uuid)
RETURNS TABLE(visitor_id integer, total_visits_count integer, approved_visits_count integer, final_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
local_total_visits int;
local_approved_count int;
local_rejected_count int; -- Added to track rejections
local_new_visitor_status int;
BEGIN
-- Step 1: Update the status for the specific unit.
UPDATE public.multi_unit_visits
SET visitor_statuses = p_visitor_status_id,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE public.multi_unit_visits.visitor_log_id = p_visitor_log_id
AND public.multi_unit_visits.unit_id = p_unit_id
AND public.multi_unit_visits.is_deleted = false;
-- Step 2: Check if the update was successful.
IF NOT FOUND THEN
RETURN;
END IF;
-- Step 3: Recalculate the overall visitor log status.
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2), -- Approved
COUNT(*) FILTER (WHERE muv.visitor_statuses = 6) -- Rejected
INTO
local_total_visits,
local_approved_count,
local_rejected_count
FROM public.multi_unit_visits AS muv
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.is_deleted = false;
-- Step 4: Determine the new overall status with corrected logic.
IF local_approved_count = local_total_visits AND local_total_visits > 0 THEN
local_new_visitor_status := 2; -- Rule 1: All units have approved.
ELSIF local_approved_count > 0 THEN
-- FIX: If at least one unit approves, the status is "Partial Approved",
-- even if another unit rejects.
local_new_visitor_status := 7; -- Rule 2: At least one approval, but not all.
ELSIF local_rejected_count > 0 THEN
local_new_visitor_status := 6; -- Rule 3: No approvals yet, but at least one rejection.
ELSE
local_new_visitor_status := 1; -- Rule 4: No approvals and no rejections yet.
END IF;
-- Step 5: Update the parent visitor_logs table.
UPDATE public.visitor_logs
SET visitor_status_id = local_new_visitor_status,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE id = p_visitor_log_id;
-- Step 6: On success, return the calculated values.
RETURN QUERY SELECT p_visitor_log_id, local_total_visits, local_approved_count, local_new_visitor_status;
END;
$function$
-- Function: debug_visitor_logs
CREATE OR REPLACE FUNCTION public.debug_visitor_logs(v_apartment_id uuid, log_date timestamp without time zone)
RETURNS TABLE(id integer, visitor_id integer, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visiting_from text, contact_number text, visitor_type_id integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
vl.id, v.id, v.first_name, v.last_name, vl.entry_time, vl.exit_time,
vl.visiting_from, v.contact_number, v.visitor_type_id
FROM
visitors v
JOIN
visitor_logs vl ON v.id = vl.visitor_id
WHERE
v.apartment_id = v_apartment_id
AND DATE(vl.entry_time) = DATE(log_date)
AND v.is_deleted = FALSE
AND vl.is_deleted = FALSE;
END;
$function$
-- Function: get_all_apartment_or_org_residents
CREATE OR REPLACE FUNCTION public.get_all_apartment_or_org_residents(apartmentid uuid, isgetall boolean)
RETURNS TABLE(id integer, resident_name text, user_id uuid)
LANGUAGE plpgsql
AS $function$
DECLARE
org_id uuid;
BEGIN
IF isGetAll THEN
SELECT a.organization_id INTO org_id
FROM public.apartments a
WHERE a.id = apartmentid
AND a.is_deleted = false;
RETURN QUERY
SELECT
row_number() OVER ()::int AS id,
r.first_name || ' ' || r.last_name AS resident_name,
r.user_id
FROM public.apartments a
INNER JOIN public.residents r ON r.apartment_id = a.id
WHERE a.organization_id = org_id
AND r.is_deleted = false
AND a.is_deleted = false;
ELSE
RETURN QUERY
SELECT
row_number() OVER ()::int AS id,
r.first_name || ' ' || r.last_name AS resident_name,
r.user_id
FROM public.apartments a
INNER JOIN public.residents r ON r.apartment_id = a.id
WHERE a.id = apartmentid
AND r.is_deleted = false
AND a.is_deleted = false;
END IF;
END;
$function$
-- Function: process_visitor_unit_response
CREATE OR REPLACE FUNCTION public.process_visitor_unit_response(p_visitor_log_id integer, p_unit_id integer, p_visitor_status_id integer, p_modified_by uuid)
RETURNS TABLE(visitor_log_id integer, total_visits_count integer, approved_visits_count integer, final_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
local_total_visits int;
local_approved_count int;
local_new_visitor_status int;
BEGIN
-- Step 1: Update the status for the specific unit.
UPDATE public.multi_unit_visits
SET visitor_statuses = p_visitor_status_id,
modified_on_utc = NOW(),
modified_by = p_modified_by
-- FIX: Explicitly reference the table's column to avoid ambiguity
WHERE public.multi_unit_visits.visitor_log_id = p_visitor_log_id
AND public.multi_unit_visits.unit_id = p_unit_id
AND public.multi_unit_visits.is_deleted = false;
-- Step 2: Check if the update was successful.
IF NOT FOUND THEN
RETURN;
END IF;
-- Step 3: Recalculate the overall visitor log status.
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE muv.visitor_statuses = 2) -- Approved
INTO
local_total_visits,
local_approved_count
FROM public.multi_unit_visits AS muv
-- FIX: Use an alias for clarity and to prevent ambiguity
WHERE muv.visitor_log_id = p_visitor_log_id
AND muv.is_deleted = false;
-- Step 4: Determine the new overall status.
IF EXISTS (
SELECT 1
FROM public.multi_unit_visits
-- FIX: Explicitly reference the table's column
WHERE public.multi_unit_visits.visitor_log_id = p_visitor_log_id
AND public.multi_unit_visits.visitor_statuses = 6 -- Rejected
AND public.multi_unit_visits.is_deleted = false
) THEN
local_new_visitor_status := 6; -- Rejected
ELSIF local_approved_count > 0 AND local_approved_count = local_total_visits THEN
local_new_visitor_status := 2; -- Approved
ELSE
local_new_visitor_status := 1; -- Pending
END IF;
-- Step 5: Update the parent visitor_logs table.
UPDATE public.visitor_logs
SET visitor_status_id = local_new_visitor_status,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE id = p_visitor_log_id;
-- Step 6: On success, return the calculated values.
RETURN QUERY SELECT p_visitor_log_id, local_total_visits, local_approved_count, local_new_visitor_status;
END;
$function$
-- Function: get_visitors_and_service_providers_in_timespan
CREATE OR REPLACE FUNCTION public.get_visitors_and_service_providers_in_timespan(p_unit_id integer, p_start_date timestamp without time zone, p_end_date timestamp without time zone)
RETURNS TABLE(id integer, visitor_type_id integer, visitor_type text, serial_id integer, first_name text, last_name text, entry_time timestamp without time zone, exit_time timestamp without time zone, visitor_person_type text, visitor_person_sub_type text, visitor_status_id integer, visitor_status text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
CAST(ROW_NUMBER() OVER (ORDER BY combined.entry_time, combined.id) AS INT) AS serial_id,
combined.visitor_type_id,
combined.visitor_type,
combined.id,
combined.first_name,
combined.last_name,
combined.entry_time,
combined.exit_time,
combined.visitor_person_type,
combined.visitor_person_sub_type,
combined.visitor_status_id,
combined.visitor_status_name
FROM (
-- Visitors
SELECT
vt.id AS visitor_type_id,
'visitor'::TEXT AS visitor_type,
v.id,
v.first_name::TEXT,
v.last_name::TEXT,
vl.entry_time,
vl.exit_time,
vt."name"::TEXT AS visitor_person_type,
NULL::TEXT AS visitor_person_sub_type,
CASE
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 3
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 4
ELSE vl.visitor_status_id
END AS visitor_status_id,
CASE
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NULL THEN 'Checked In'
WHEN vl.entry_time IS NOT NULL AND vl.exit_time IS NOT NULL THEN 'Checked Out'
ELSE vs."name"::TEXT
END AS visitor_status_name
FROM
public.visitor_logs vl
JOIN public.visitors v ON vl.visitor_id = v.id
JOIN public.multi_unit_visits muv ON muv.visitor_log_id = vl.id
JOIN public.visitor_types vt ON v.visitor_type_id = vt.id
LEFT JOIN public.visitor_statuses vs ON vl.visitor_status_id = vs.id
WHERE
muv.unit_id = p_unit_id
AND vl.entry_time BETWEEN p_start_date AND p_end_date
AND vl.is_deleted = FALSE
AND v.is_deleted = FALSE
AND muv.is_deleted = FALSE
UNION ALL
-- Service Providers
SELECT
vt_sp.id AS visitor_type_id,
'service_provider'::TEXT AS visitor_type,
sp.id,
sp.first_name::TEXT,
sp.last_name::TEXT,
spl.entry_time,
spl.exit_time,
spt."name"::TEXT AS visitor_person_type,
spst."name"::TEXT AS visitor_person_sub_type,
CASE
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 3
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 4
ELSE NULL
END AS visitor_status_id,
CASE
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NULL THEN 'Checked In'
WHEN spl.entry_time IS NOT NULL AND spl.exit_time IS NOT NULL THEN 'Checked Out'
ELSE NULL::TEXT
END AS visitor_status_name
FROM
public.service_provider_logs spl
JOIN public.service_providers sp ON spl.service_provider_id = sp.id
JOIN public.service_provider_types spt ON sp.service_provider_type_id = spt.id
JOIN public.service_provider_sub_types spst ON sp.service_provider_sub_type_id = spst.id
JOIN public.multi_unit_visits muv ON muv.unit_id = p_unit_id
LEFT JOIN public.visitor_types vt_sp ON vt_sp."name" = 'Service Provider' AND vt_sp.is_deleted = FALSE
WHERE
spl.entry_time BETWEEN p_start_date AND p_end_date
AND spl.is_deleted = FALSE
AND sp.is_deleted = FALSE
) AS combined;
END;
$function$
-- Function: get_user_full_details
CREATE OR REPLACE FUNCTION public.get_user_full_details(p_user_id uuid, p_apartment_id uuid)
RETURNS TABLE(user_id uuid, full_name character varying, email character varying, contact_number character varying, unit_name character varying, vehicles text[], address_line1 character varying, address_line2 character varying, zip_code character varying, country_name character varying, state_name character varying, city_name character varying, apartment_id uuid)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
u.id AS user_id,
CONCAT(u.first_name, ' ', u.last_name)::VARCHAR AS full_name,
u.email::VARCHAR,
u.contact_number::VARCHAR,
un.name::VARCHAR AS unit_name,
ARRAY_AGG(DISTINCT CONCAT(vt.name, ' - ', v.vehicle_number))::TEXT[] AS vehicles,
a.address_line1::VARCHAR,
a.address_line2::VARCHAR,
a.zip_code::VARCHAR,
co.name::VARCHAR AS country_name,
s.name::VARCHAR AS state_name,
ci.name::VARCHAR AS city_name,
un.apartment_id
FROM
users u
INNER JOIN
residents r
ON u.id = r.user_id
LEFT JOIN
resident_units ru
ON r.id = ru.resident_id
AND ru.is_deleted = FALSE
LEFT JOIN
units un
ON ru.unit_id = un.id
AND un.apartment_id = p_apartment_id -- ✅ Filter by company
LEFT JOIN
vehicles v
ON un.id = v.unit_id
AND v.is_deleted = FALSE
LEFT JOIN
vehicle_types vt
ON v.vehicle_type_id = vt.id
LEFT JOIN
addresses a
ON r.permanent_address_id = a.id
LEFT JOIN
countries co
ON a.country_id = co.id
LEFT JOIN
states s
ON a.state_id = s.id
LEFT JOIN
cities ci
ON a.city_id = ci.id
WHERE
u.id = p_user_id
AND u.is_deleted = FALSE
AND r.is_deleted = FALSE
AND r.apartment_id = p_apartment_id -- ✅ Restrict resident scope to the company
GROUP BY
u.id, u.first_name, u.last_name, u.email, u.contact_number,
un.name, a.address_line1, a.address_line2, a.zip_code,
co.name, s.name, ci.name, un.apartment_id
ORDER BY
un.name;
END;
$function$
-- Function: get_all_visitors_by_unit
CREATE OR REPLACE FUNCTION public.get_all_visitors_by_unit(p_apartment_id uuid, p_unit_id integer)
RETURNS TABLE(id integer, first_name text, last_name text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name character varying, vehicle_number text, identity_type_id integer, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
v.id,
v.first_name,
v.last_name,
v.email,
v.visiting_from,
v.contact_number,
v.visitor_type_id,
vt.name AS visitor_type_name,
v.vehicle_number,
v.identity_type_id,
v.identity_number,
v.created_by,
v.created_on_utc,
v.modified_by,
v.modified_on_utc
FROM
public.visitors v
INNER JOIN
public.visitor_types vt ON v.visitor_type_id = vt.id
WHERE
v.apartment_id = p_apartment_id
AND v.unit_id = p_unit_id
AND v.is_deleted = false
AND (vt.is_deleted = false OR vt.is_deleted IS NULL);
END;
$function$
-- Function: generate_ticket_number
CREATE OR REPLACE FUNCTION public.generate_ticket_number(p_apartment_id uuid, p_category_id integer)
RETURNS text
LANGUAGE plpgsql
AS $function$
DECLARE
v_today date := CURRENT_DATE;
v_short_code varchar(10);
v_last_number int;
v_formatted_date text;
v_formatted_sequence text;
v_ticket_number text;
BEGIN
-- 1️⃣ Fetch short code for the category
SELECT short_code INTO v_short_code
FROM public.ticket_categories
WHERE id = p_category_id AND is_deleted = false;
IF v_short_code IS NULL OR v_short_code = '' THEN
RAISE EXCEPTION 'ShortCode not found for category %', p_category_id;
END IF;
-- 2️⃣ Insert or update sequence (handles concurrency)
INSERT INTO public.ticket_number_sequences (apartment_id, ticket_date, category_id, last_number)
VALUES (p_apartment_id, v_today, p_category_id, 1)
ON CONFLICT (apartment_id, ticket_date, category_id)
DO UPDATE
SET last_number = ticket_number_sequences.last_number + 1
RETURNING last_number
INTO v_last_number;
-- 3️⃣ Format date as DDMMYY
v_formatted_date := to_char(v_today, 'DDMMYY');
-- 4️⃣ Format sequence as 2-digit (01, 02, 03…)
v_formatted_sequence := LPAD(v_last_number::text, 2, '0');
-- 5️⃣ Build final ticket number
v_ticket_number := v_formatted_date || '-' || v_short_code || '-' || v_formatted_sequence;
RETURN v_ticket_number;
END;
$function$
-- Function: get_ticket_analytics
CREATE OR REPLACE FUNCTION public.get_ticket_analytics(p_apartment_id uuid, p_from timestamp without time zone DEFAULT NULL::timestamp without time zone, p_to timestamp without time zone DEFAULT NULL::timestamp without time zone, p_recent_limit integer DEFAULT 5)
RETURNS jsonb
LANGUAGE plpgsql
AS $function$
DECLARE
result jsonb;
BEGIN
WITH base AS (
SELECT *
FROM public.tickets t
WHERE t.apartment_id = p_apartment_id
AND NOT t.is_deleted
AND (p_from IS NULL OR t.created_on_utc >= p_from)
AND (p_to IS NULL OR t.created_on_utc <= p_to)
),
by_status AS (
SELECT
coalesce(t.ticket_status_id, 0) AS status_id,
count(*) AS cnt
FROM base t
GROUP BY coalesce(t.ticket_status_id, 0)
ORDER BY cnt DESC
),
by_priority AS (
SELECT
coalesce(t.ticket_priority_id, 0) AS priority_id,
count(*) AS cnt
FROM base t
GROUP BY coalesce(t.ticket_priority_id, 0)
ORDER BY cnt DESC
),
by_category AS (
SELECT
coalesce(t.ticket_category_id, 0) AS category_id,
count(*) AS cnt
FROM base t
GROUP BY coalesce(t.ticket_category_id, 0)
ORDER BY cnt DESC
),
recent AS (
SELECT
t.id,
t.ticket_number,
t.title,
t.ticket_status_id,
t.ticket_priority_id,
t.ticket_category_id,
t.is_on_hold,
t.created_on_utc
FROM base t
ORDER BY t.created_on_utc DESC
LIMIT p_recent_limit
)
SELECT jsonb_build_object(
'totalTickets', (SELECT count(*) FROM base),
'onHoldTickets', (SELECT count(*) FROM base WHERE is_on_hold),
'ticketsByStatus', COALESCE((
SELECT jsonb_agg(jsonb_build_object(
'id', s.status_id,
'name', ts.name,
'count', s.cnt
) ORDER BY s.cnt DESC)
FROM by_status s
LEFT JOIN public.ticket_statuses ts ON ts.id = s.status_id
), '[]'::jsonb),
'ticketsByPriority', COALESCE((
SELECT jsonb_agg(jsonb_build_object(
'id', p.priority_id,
'name', tp.name,
'count', p.cnt
) ORDER BY p.cnt DESC)
FROM by_priority p
LEFT JOIN public.ticket_priorities tp ON tp.id = p.priority_id
), '[]'::jsonb),
'ticketsByCategory', COALESCE((
SELECT jsonb_agg(jsonb_build_object(
'id', c.category_id,
'name', tc.name,
'count', c.cnt
) ORDER BY c.cnt DESC)
FROM by_category c
LEFT JOIN public.ticket_categories tc ON tc.id = c.category_id
), '[]'::jsonb),
'recentTickets', COALESCE((
SELECT jsonb_agg(row_to_json(r))
FROM (
SELECT
r.id,
r.ticket_number,
r.title,
COALESCE(ts.name, '') AS status,
COALESCE(tp.name, '') AS priority,
COALESCE(tc.name, '') AS category,
r.is_on_hold,
r.created_on_utc
FROM recent r
LEFT JOIN public.ticket_statuses ts ON ts.id = r.ticket_status_id
LEFT JOIN public.ticket_priorities tp ON tp.id = r.ticket_priority_id
LEFT JOIN public.ticket_categories tc ON tc.id = r.ticket_category_id
ORDER BY r.created_on_utc DESC
) r
), '[]'::jsonb)
) INTO result;
RETURN result;
END;
$function$
-- Function: create_service_provider_api
CREATE OR REPLACE FUNCTION public.create_service_provider_api(p_first_name text, p_last_name text, p_contact_number text, p_visitor_type_id integer, p_email text, p_visiting_from text, p_service_provider_type_id integer, p_service_provider_sub_type_id integer, p_vehicle_number text, p_identity_type_id integer, p_identity_number text, p_validity_date date, p_police_verification_status boolean, p_is_hireable boolean, p_is_visible boolean, p_is_frequent_visitor boolean, p_apartment_id uuid, p_pin text, p_created_by uuid, p_permanent_address_id uuid, p_present_address_id uuid)
RETURNS integer
LANGUAGE sql
AS $function$
SELECT service_provider_id
FROM public.create_service_provider(
p_first_name,
p_last_name,
p_contact_number,
p_visitor_type_id,
p_email,
p_visiting_from,
p_service_provider_type_id,
p_service_provider_sub_type_id,
p_vehicle_number,
p_identity_type_id,
p_identity_number,
p_validity_date,
p_police_verification_status,
p_is_hireable,
p_is_visible,
p_is_frequent_visitor,
p_apartment_id,
p_pin,
p_created_by,
p_permanent_address_id,
p_present_address_id
);
$function$
-- Function: create_service_provider
CREATE OR REPLACE FUNCTION public.create_service_provider(p_first_name character varying, p_last_name character varying, p_contact_number character varying, p_visitor_type_id integer, p_email character varying, p_visiting_from character varying, p_service_provider_type_id integer, p_service_provider_sub_type_id integer, p_vehicle_number character varying, p_identity_type_id integer, p_identity_number character varying, p_validity_date timestamp without time zone, p_police_verification_status boolean, p_is_hireable boolean, p_is_visible boolean, p_is_frequent_visitor boolean, p_apartment_id uuid, p_pin character varying, p_created_by uuid, p_permanent_address_id uuid, p_present_address_id uuid)
RETURNS TABLE(service_provider_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_visitor_id integer;
v_service_provider_id integer;
BEGIN
/* ----------------------------------------------------
1. Create Visitor
---------------------------------------------------- */
INSERT INTO visitors (
first_name,
last_name,
visitor_type_id,
created_by,
created_on_utc
)
VALUES (
p_first_name,
p_last_name,
p_visitor_type_id,
p_created_by,
now()
)
RETURNING id INTO v_visitor_id;
/* ----------------------------------------------------
2. Visitor Phone
---------------------------------------------------- */
INSERT INTO visitor_phones (
visitor_id,
phone_number,
is_active,
valid_from
)
VALUES (
v_visitor_id,
p_contact_number,
true,
now()
);
/* ----------------------------------------------------
3. Visitor Email (optional)
---------------------------------------------------- */
IF p_email IS NOT NULL THEN
INSERT INTO visitor_emails (
visitor_id,
email,
is_active,
valid_from
)
VALUES (
v_visitor_id,
p_email,
true,
now()
);
END IF;
/* ----------------------------------------------------
4. Visitor Identity (optional)
---------------------------------------------------- */
IF p_identity_type_id IS NOT NULL AND p_identity_number IS NOT NULL THEN
INSERT INTO visitor_identities (
visitor_id,
identity_type_id,
identity_number,
is_active,
created_by,
created_on_utc
)
VALUES (
v_visitor_id,
p_identity_type_id,
p_identity_number,
true,
p_created_by,
now()
);
END IF;
/* ----------------------------------------------------
5. Visitor Details (optional)
---------------------------------------------------- */
IF p_visiting_from IS NOT NULL OR p_vehicle_number IS NOT NULL THEN
INSERT INTO visitor_details (
visitor_id,
visiting_from,
vehicle_number,
created_by,
created_on_utc
)
VALUES (
v_visitor_id,
p_visiting_from,
p_vehicle_number,
p_created_by,
now()
);
END IF;
/* ----------------------------------------------------
6. Visitor ↔ Apartment mapping
---------------------------------------------------- */
INSERT INTO visitor_apartments (
visitor_id,
visitor_apartment_id,
first_seen,
is_blocked
)
VALUES (
v_visitor_id,
p_apartment_id,
now(),
false
);
/* ----------------------------------------------------
7. Create Service Provider
---------------------------------------------------- */
INSERT INTO service_providers (
service_provider_type_id,
service_provider_sub_type_id,
visitor_id,
is_frequent_visitor,
is_hireable,
is_visible,
police_verification_status,
pin,
valid_from,
created_by,
created_on_utc
)
VALUES (
p_service_provider_type_id,
p_service_provider_sub_type_id,
v_visitor_id,
p_is_frequent_visitor,
p_is_hireable,
p_is_visible,
p_police_verification_status,
p_pin,
p_validity_date,
p_created_by,
now()
)
RETURNING id INTO v_service_provider_id;
/* ----------------------------------------------------
8. Addresses (optional)
---------------------------------------------------- */
IF p_permanent_address_id IS NOT NULL THEN
INSERT INTO service_provider_addresses (
service_provider_id,
address_id,
is_permanent_address,
is_present_address
)
VALUES (
v_service_provider_id,
p_permanent_address_id,
true,
false
);
END IF;
IF p_present_address_id IS NOT NULL THEN
INSERT INTO service_provider_addresses (
service_provider_id,
address_id,
is_permanent_address,
is_present_address
)
VALUES (
v_service_provider_id,
p_present_address_id,
false,
true
);
END IF;
/* ----------------------------------------------------
9. Return
---------------------------------------------------- */
service_provider_id := v_service_provider_id;
RETURN;
END;
$function$
-- Function: get_visitor_inside_counts
CREATE OR REPLACE FUNCTION public.get_visitor_inside_counts(p_apartment_id uuid, p_today_only boolean)
RETURNS TABLE(id bigint, visitor_log_id bigint, visitor_id bigint, first_name text, last_name text, visitor_type_id integer, visitor_type_name text, entry_time timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
ROW_NUMBER() OVER (ORDER BY vl.entry_time ASC)::bigint AS id, -- Incremental ID
vl.id AS visitor_log_id,
v.id AS visitor_id,
v.first_name,
v.last_name,
vl.visitor_type_id,
vt.name::text AS visitor_type_name,
vl.entry_time
FROM
visitor_logs vl
INNER JOIN
visitors v ON v.id = vl.visitor_id
INNER JOIN
visitor_types vt ON vt.id = vl.visitor_type_id
WHERE
vl.apartment_id = p_apartment_id
AND vl.exit_time IS NULL
AND (NOT p_today_only OR vl.entry_time::date = CURRENT_DATE)
AND v.is_deleted = FALSE
AND vt.is_deleted = FALSE
ORDER BY
vl.entry_time ASC;
END;
$function$
-- Function: get_all_visitors_by_apartment
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_all_visitors_by_apartment(p_apartment_id uuid)
RETURNS TABLE(id bigint, first_name text, last_name text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name character varying, vehicle_number text, identity_type_id integer, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
v.id, -- this is bigint, matching the table definition
v.first_name,
v.last_name,
ve.email,
vd.visiting_from,
vp.phone_number AS contact_number,
v.visitor_type_id,
vt.name AS visitor_type_name,
vd.vehicle_number,
vi.identity_type_id,
vi.identity_number,
v.created_by,
v.created_on_utc,
v.modified_by,
v.modified_on_utc
FROM
public.visitors v
INNER JOIN
public.visitor_types vt ON v.visitor_type_id = vt.id
LEFT JOIN
public.visitor_emails ve ON v.id = ve.visitor_id AND ve.is_active = TRUE
LEFT JOIN
public.visitor_details vd ON v.id = vd.visitor_id
LEFT JOIN
public.visitor_phones vp ON v.id = vp.visitor_id AND vp.is_active = TRUE
LEFT JOIN
public.visitor_identities vi ON v.id = vi.visitor_id AND vi.is_active = TRUE
INNER JOIN
public.visitor_apartments va ON v.id = va.visitor_id
WHERE
va.visitor_apartment_id = p_apartment_id
AND v.is_deleted = FALSE
AND (vt.is_deleted = FALSE OR vt.is_deleted IS NULL);
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_all_visitors_by_apartment(p_apartment_id uuid)
RETURNS TABLE(id integer, first_name text, last_name text, email text, visiting_from text, contact_number text, visitor_type_id integer, visitor_type_name character varying, vehicle_number text, identity_type_id integer, identity_number text, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
v.id,
v.first_name,
v.last_name,
v.email,
v.visiting_from,
v.contact_number,
v.visitor_type_id,
vt.name AS visitor_type_name,
v.vehicle_number,
v.identity_type_id,
v.identity_number,
v.created_by,
v.created_on_utc,
v.modified_by,
v.modified_on_utc
FROM
public.visitors v
INNER JOIN
public.visitor_types vt ON v.visitor_type_id = vt.id
WHERE
v.apartment_id = p_apartment_id
AND v.is_deleted = false
AND (vt.is_deleted = false OR vt.is_deleted IS NULL);
END;
$function$
-- Function: get_service_provider_companies
CREATE OR REPLACE FUNCTION public.get_service_provider_companies(p_category_id integer DEFAULT NULL::integer, p_subtype_id integer DEFAULT NULL::integer)
RETURNS TABLE(id integer, name text, description text)
LANGUAGE sql
AS $function$
SELECT spc.id, spc.name, spc.description
FROM public.service_provider_companies spc
WHERE
(p_category_id IS NULL OR spc.category_id = p_category_id)
AND (p_subtype_id IS NULL OR spc.subtype_id = p_subtype_id);
$function$
-- Function: create_visitor
CREATE OR REPLACE FUNCTION public.create_visitor(p_first_name character varying, p_last_name character varying, p_contact_number character varying, p_visitor_type_id integer, p_email character varying, p_visiting_from character varying, p_vehicle_number character varying, p_identity_type_id integer, p_identity_number character varying, p_apartment_id uuid, p_created_by uuid, p_delivery_company_id integer, p_image_url character varying)
RETURNS bigint
LANGUAGE plpgsql
AS $function$
DECLARE
v_visitor_id BIGINT;
BEGIN
-- Insert into the visitors table
INSERT INTO public.visitors (
first_name,
last_name,
created_on_utc,
created_by,
is_active,
visitor_type_id,
image_url
)
VALUES (
p_first_name,
p_last_name,
current_timestamp,
p_created_by,
TRUE,
p_visitor_type_id,
p_image_url
)
RETURNING id INTO v_visitor_id; -- Store the newly created visitor's ID
-- Insert into visitor_apartments table
INSERT INTO public.visitor_apartments (
visitor_id,
visitor_apartment_id,
first_seen,
is_blocked
)
VALUES (
v_visitor_id,
p_apartment_id,
current_timestamp, -- Set first_seen as the current timestamp
FALSE -- Default is_blocked to FALSE
);
-- Insert into visitor_details table (if visiting_from or vehicle_number is provided)
IF p_visiting_from IS NOT NULL AND TRIM(p_visiting_from) <> '' OR p_vehicle_number IS NOT NULL AND TRIM(p_vehicle_number) <> '' THEN
INSERT INTO public.visitor_details (
visitor_id,
visiting_from,
vehicle_number,
created_by,
created_on_utc
) VALUES (
v_visitor_id,
p_visiting_from,
p_vehicle_number,
p_created_by,
current_timestamp
);
END IF;
-- Insert into visitor_phones table
INSERT INTO public.visitor_phones (
visitor_id,
phone_number,
is_active,
valid_from
) VALUES (
v_visitor_id,
p_contact_number,
TRUE,
current_timestamp
);
-- Insert into visitor_emails table (if email is provided and not an empty string)
IF p_email IS NOT NULL AND TRIM(p_email) <> '' THEN
INSERT INTO public.visitor_emails (
visitor_id,
email,
is_active,
valid_from
) VALUES (
v_visitor_id,
p_email,
TRUE,
current_timestamp
);
END IF;
-- Insert into visitor_identities table (if identity number is provided and not an empty string)
IF p_identity_number IS NOT NULL AND TRIM(p_identity_number) <> '' THEN
INSERT INTO public.visitor_identities (
visitor_id,
identity_type_id,
identity_number,
is_active,
valid_from,
created_by,
created_on_utc
) VALUES (
v_visitor_id,
p_identity_type_id,
p_identity_number,
TRUE,
current_timestamp,
p_created_by,
current_timestamp
);
END IF;
-- Insert into visitor_delivery_company table (if delivery_company_id is provided and not NULL)
IF p_delivery_company_id IS NOT NULL AND p_delivery_company_id > 0 THEN
INSERT INTO public.visitor_delivery_company (
visitor_id,
delivery_company_id,
is_active,
valid_from
) VALUES (
v_visitor_id,
p_delivery_company_id,
TRUE,
current_timestamp
);
END IF;
-- Return the newly created visitor ID
RETURN v_visitor_id;
END;
$function$
-- Function: create_visitor_with_pending_approval
CREATE OR REPLACE FUNCTION public.create_visitor_with_pending_approval(p_apartment_id uuid, p_first_name text, p_last_name text, p_image_url text, p_contact_number text, p_visitor_type_id integer, p_unit_ids integer[], p_created_by uuid)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
v_visitor_id BIGINT;
v_visitor_log_id BIGINT;
v_unit_id INTEGER;
v_pending_status CONSTANT INTEGER := 1; -- Pending
BEGIN
/*
* 1. Find visitor by active phone number
*/
SELECT vp.visitor_id
INTO v_visitor_id
FROM visitor_phones vp
JOIN visitors v ON v.id = vp.visitor_id
WHERE vp.phone_number = p_contact_number
AND vp.is_active = TRUE
AND v.is_deleted = FALSE
LIMIT 1;
/*
* 2. Create visitor if not exists
*/
IF NOT FOUND THEN
INSERT INTO visitors (
first_name,
last_name,
image_url,
visitor_type_id,
created_on_utc,
created_by
)
VALUES (
p_first_name,
p_last_name,
p_image_url,
p_visitor_type_id,
NOW(),
p_created_by
)
RETURNING id INTO v_visitor_id;
INSERT INTO visitor_phones (
visitor_id,
phone_number,
is_active,
valid_from
)
VALUES (
v_visitor_id,
p_contact_number,
TRUE,
NOW()
);
END IF;
/*
* 3. Ensure apartment association exists
*/
IF NOT EXISTS (
SELECT 1
FROM visitor_apartments
WHERE visitor_id = v_visitor_id
AND visitor_apartment_id = p_apartment_id
) THEN
INSERT INTO visitor_apartments (
visitor_id,
visitor_apartment_id,
first_seen
)
VALUES (
v_visitor_id,
p_apartment_id,
NOW()
);
END IF;
/*
* 4. Insert visitor log with PENDING status
*/
INSERT INTO visitor_logs (
visitor_id,
apartment_id,
visitor_type_id,
entry_time,
created_on_utc,
created_by,
visitor_status_id
)
VALUES (
v_visitor_id,
p_apartment_id,
p_visitor_type_id,
NULL, -- Entry happens later
NOW(),
p_created_by,
v_pending_status
)
RETURNING id INTO v_visitor_log_id;
/*
* 5. Insert multi-unit visits with PENDING status
*/
FOREACH v_unit_id IN ARRAY p_unit_ids LOOP
INSERT INTO multi_unit_visits (
visitor_log_id,
unit_id,
visitor_statuses,
created_on_utc,
is_deleted,
created_by
)
VALUES (
v_visitor_log_id,
v_unit_id,
v_pending_status,
NOW(),
FALSE,
p_created_by
);
END LOOP;
RETURN v_visitor_log_id;
END;
$function$
-- Function: get_all_tickets
CREATE OR REPLACE FUNCTION public.get_all_tickets(p_apartment_id uuid)
RETURNS TABLE(id uuid, unit_id integer, unit text, title text, description text, ticket_category_id integer, ticket_category text, ticket_priority_id integer, ticket_priority text, ticket_status_id integer, sort_order integer, ticket_status text, ticket_assigned_to integer, ticket_assigned text, ticket_assignee_phone text, is_on_hold boolean, is_re_opened boolean, ticket_number text, ticket_for_id integer, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
LANGUAGE sql
STABLE
AS $function$
SELECT
t.id,
t.unit_id,
u.name AS unit,
t.title,
t.description,
t.ticket_category_id,
tc.name AS ticket_category,
t.ticket_priority_id,
tp.name AS ticket_priority,
t.ticket_status_id,
t.sort_order,
ts.name AS ticket_status,
t.ticket_assigned_to,
COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''),'Not Assigned' ) AS ticket_assigned,
vp.phone_number AS ticket_assignee_phone,
t.is_on_hold,
t.is_re_opened,
t.ticket_number,
t.ticket_for_id,
t.created_by,
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS created_by_name,
t.created_on_utc,
t.modified_by,
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS modified_by_name,
t.modified_on_utc
FROM tickets t
JOIN units u
ON u.id = t.unit_id
AND u.is_deleted = false
JOIN ticket_categories tc
ON tc.id = t.ticket_category_id
AND tc.is_deleted = false
JOIN ticket_priorities tp
ON tp.id = t.ticket_priority_id
AND tp.is_deleted = false
JOIN ticket_statuses ts
ON ts.id = t.ticket_status_id
AND ts.is_deleted = false
LEFT JOIN service_providers sp
ON sp.id = t.ticket_assigned_to
AND sp.is_deleted = false
LEFT JOIN visitors v
ON v.id = sp.visitor_id
AND v.is_deleted = false
LEFT JOIN visitor_phones vp
ON vp.visitor_id = v.id
AND vp.is_active = TRUE
LEFT JOIN users uc
ON uc.id = t.created_by
LEFT JOIN users um
ON um.id = t.modified_by
WHERE
t.apartment_id = p_apartment_id
AND t.is_deleted = false
ORDER BY
t.ticket_status_id,
t.sort_order,
t.created_on_utc DESC;
$function$
-- Function: get_all_tickets
CREATE OR REPLACE FUNCTION public.get_all_tickets(p_apartment_id uuid, p_from_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_to_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_service_provider_id integer DEFAULT NULL::integer, p_is_active boolean DEFAULT false)
RETURNS TABLE(id uuid, unit_id integer, unit text, title text, description text, ticket_category_id integer, ticket_category text, ticket_priority_id integer, ticket_priority text, ticket_status_id integer, sort_order integer, ticket_status text, ticket_assigned_to integer, ticket_assigned text, ticket_assignee_phone text, is_on_hold boolean, is_re_opened boolean, ticket_number text, ticket_for_id integer, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
LANGUAGE sql
STABLE
AS $function$
SELECT
t.id,
t.unit_id,
u.name AS unit,
t.title,
t.description,
t.ticket_category_id,
tc.name AS ticket_category,
t.ticket_priority_id,
tp.name AS ticket_priority,
t.ticket_status_id,
t.sort_order,
ts.name AS ticket_status,
t.ticket_assigned_to,
--COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''),'Not Assigned') AS ticket_assigned,
--NULLIF(concat_ws(' ', v.first_name, v.last_name), '') AS ticket_assigned,
COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''), NULL) AS ticket_assigned,
vp.phone_number AS ticket_assignee_phone,
t.is_on_hold,
t.is_re_opened,
t.ticket_number,
t.ticket_for_id,
t.created_by,
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS created_by_name,
t.created_on_utc,
t.modified_by,
COALESCE(NULLIF(TRIM(um.first_name || ' ' || um.last_name), ''), NULL) AS modified_by_name,
t.modified_on_utc
FROM tickets t
JOIN units u
ON u.id = t.unit_id
AND u.is_deleted = false
JOIN ticket_categories tc
ON tc.id = t.ticket_category_id
AND tc.is_deleted = false
JOIN ticket_priorities tp
ON tp.id = t.ticket_priority_id
AND tp.is_deleted = false
JOIN ticket_statuses ts
ON ts.id = t.ticket_status_id
AND ts.is_deleted = false
LEFT JOIN service_providers sp
ON sp.id = t.ticket_assigned_to
AND sp.is_deleted = false
LEFT JOIN visitors v
ON v.id = sp.visitor_id
AND v.is_deleted = false
LEFT JOIN visitor_phones vp
ON vp.visitor_id = v.id
AND vp.is_active = true
LEFT JOIN users uc
ON uc.id = t.created_by
LEFT JOIN users um
ON um.id = t.modified_by
WHERE
t.apartment_id = p_apartment_id
AND t.is_deleted = false
-- Date filters
AND (p_from_datetime IS NULL OR t.created_on_utc >= p_from_datetime)
AND (p_to_datetime IS NULL OR t.created_on_utc <= p_to_datetime)
-- Service provider filter
AND (p_service_provider_id IS NULL OR t.ticket_assigned_to = p_service_provider_id)
-- Active / All status filter
AND (
p_is_active = false
OR ts.name IN (
'In Queue',
'Assigned',
'In Progress',
'Reopened'
)
)
ORDER BY
t.ticket_status_id,
t.sort_order,
t.created_on_utc DESC;
$function$
-- Function: get_defaulter_contact
CREATE OR REPLACE FUNCTION public.get_defaulter_contact(p_customer_id uuid)
RETURNS text
LANGUAGE plpgsql
AS $function$
DECLARE
v_contact_number text;
BEGIN
SELECT
r.contact_number INTO v_contact_number
FROM
public.units u
INNER JOIN
public.resident_units ru ON u.id = ru.unit_id
INNER JOIN
public.residents r ON ru.resident_id = r.id
WHERE
u.customer_id = p_customer_id
AND u.is_deleted = false
AND ru.is_deleted = false
AND r.is_deleted = false
AND r.contact_number IS NOT NULL
LIMIT 1;
RETURN v_contact_number;
END;
$function$
-- Function: get_service_provider_subcategories
CREATE OR REPLACE FUNCTION public.get_service_provider_subcategories(p_category_id integer)
RETURNS TABLE(id integer, name text, description text)
LANGUAGE sql
AS $function$
SELECT sps.id, sps.name, sps.description
FROM public.service_provider_company_subtypes sps
WHERE sps.category_id = p_category_id;
$function$
-- Function: get_all_escalated_tickets
CREATE OR REPLACE FUNCTION public.get_all_escalated_tickets(p_apartment_id uuid, p_from_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_to_datetime timestamp without time zone DEFAULT NULL::timestamp without time zone, p_service_provider_id integer DEFAULT NULL::integer, p_is_active boolean DEFAULT false)
RETURNS TABLE(id uuid, unit_id integer, unit text, title text, description text, ticket_category_id integer, ticket_category text, ticket_priority_id integer, ticket_priority text, ticket_status_id integer, sort_order integer, ticket_status text, ticket_assigned_to integer, ticket_assigned text, ticket_assignee_phone text, is_on_hold boolean, is_re_opened boolean, ticket_number text, ticket_for_id integer, created_by uuid, created_by_name text, created_on_utc timestamp without time zone, modified_by uuid, modified_by_name text, modified_on_utc timestamp without time zone)
LANGUAGE sql
STABLE
AS $function$
WITH latest_ticket_log AS (
SELECT DISTINCT ON (tl.ticket_id)
tl.ticket_id,
tl.is_escalated
FROM ticket_logs tl
ORDER BY tl.ticket_id, tl.created_on_utc DESC
)
SELECT
t.id,
t.unit_id,
u.name AS unit,
t.title,
t.description,
t.ticket_category_id,
tc.name AS ticket_category,
t.ticket_priority_id,
tp.name AS ticket_priority,
t.ticket_status_id,
t.sort_order,
ts.name AS ticket_status,
t.ticket_assigned_to,
COALESCE(NULLIF(TRIM(v.first_name || ' ' || v.last_name), ''), NULL) AS ticket_assigned,
vp.phone_number AS ticket_assignee_phone,
t.is_on_hold,
t.is_re_opened,
t.ticket_number,
t.ticket_for_id,
t.created_by,
COALESCE(NULLIF(TRIM(uc.first_name || ' ' || uc.last_name), ''), NULL) AS created_by_name,
t.created_on_utc,
t.modified_by,
COALESCE(NULLIF(TRIM(um.first_name || ' ' || um.last_name), ''), NULL) AS modified_by_name,
t.modified_on_utc
FROM tickets t
JOIN latest_ticket_log ltl
ON ltl.ticket_id = t.id
AND ltl.is_escalated = true
JOIN units u
ON u.id = t.unit_id
AND u.is_deleted = false
JOIN ticket_categories tc
ON tc.id = t.ticket_category_id
AND tc.is_deleted = false
JOIN ticket_priorities tp
ON tp.id = t.ticket_priority_id
AND tp.is_deleted = false
JOIN ticket_statuses ts
ON ts.id = t.ticket_status_id
AND ts.is_deleted = false
LEFT JOIN service_providers sp
ON sp.id = t.ticket_assigned_to
AND sp.is_deleted = false
LEFT JOIN visitors v
ON v.id = sp.visitor_id
AND v.is_deleted = false
LEFT JOIN visitor_phones vp
ON vp.visitor_id = v.id
AND vp.is_active = true
LEFT JOIN users uc
ON uc.id = t.created_by
LEFT JOIN users um
ON um.id = t.modified_by
WHERE
t.apartment_id = p_apartment_id
AND t.is_deleted = false
-- Date filters
AND (p_from_datetime IS NULL OR t.created_on_utc >= p_from_datetime)
AND (p_to_datetime IS NULL OR t.created_on_utc <= p_to_datetime)
-- Service provider filter
AND (p_service_provider_id IS NULL OR t.ticket_assigned_to = p_service_provider_id)
-- Active / All status filter
AND (
p_is_active = false
OR ts.name IN (
'In Queue',
'Assigned',
'In Progress',
'Reopened'
)
)
ORDER BY
t.ticket_status_id,
t.sort_order,
t.created_on_utc DESC;
$function$
-- Function: visitor_approval_fcm_tokens_by_visitor_log_id
CREATE OR REPLACE FUNCTION public.visitor_approval_fcm_tokens_by_visitor_log_id(p_visitorlogid bigint)
RETURNS TABLE(id integer, user_ids uuid[], visitor_id integer, first_name text, last_name text, fcm_tokens text[], device_ids text[])
LANGUAGE sql
AS $function$
SELECT
ROW_NUMBER() OVER ()::INT AS id,
ARRAY_AGG(DISTINCT r.user_id) AS user_ids,
v.id AS visitor_id,
v.first_name,
v.last_name,
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.fcm_token), NULL) AS fcm_tokens,
ARRAY_REMOVE(ARRAY_AGG(DISTINCT uft.device_id), NULL) AS device_ids
FROM multi_unit_visits muv
INNER JOIN resident_units ru ON muv.unit_id = ru.unit_id
INNER JOIN residents r ON ru.resident_id = r.id
INNER JOIN visitor_logs vl ON vl.id = muv.visitor_log_id
INNER JOIN visitors v ON v.id = vl.visitor_id
LEFT JOIN user_fcm_tokens uft ON uft.user_id = r.user_id
WHERE muv.visitor_log_id = p_visitorlogid
AND muv.is_deleted = FALSE
AND ru.is_deleted = FALSE
AND r.is_deleted = FALSE
GROUP BY v.id, v.first_name, v.last_name;
$function$
-- Function: create_visitor_with_pending_approval1
CREATE OR REPLACE FUNCTION public.create_visitor_with_pending_approval1(p_apartment_id uuid, p_first_name text, p_last_name text, p_image_url text, p_contact_number text, p_visitor_type_id integer, p_unit_ids integer[], p_created_by uuid, p_sp_category_id integer DEFAULT NULL::integer, p_sp_subtype_id integer DEFAULT NULL::integer, p_sp_company_id integer DEFAULT NULL::integer)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
v_visitor_id BIGINT;
v_visitor_log_id BIGINT;
v_unit_id INTEGER;
v_pending_status CONSTANT INTEGER := 1;
BEGIN
-- 1. Find visitor by phone
SELECT vp.visitor_id
INTO v_visitor_id
FROM visitor_phones vp
JOIN visitors v ON v.id = vp.visitor_id
WHERE vp.phone_number = p_contact_number
AND vp.is_active = TRUE
AND v.is_deleted = FALSE
LIMIT 1;
-- 2. Create visitor if not exists
IF NOT FOUND THEN
INSERT INTO visitors (
first_name,
last_name,
image_url,
visitor_type_id,
created_on_utc,
created_by
)
VALUES (
p_first_name,
p_last_name,
p_image_url,
p_visitor_type_id,
NOW(),
p_created_by
)
RETURNING id INTO v_visitor_id;
INSERT INTO visitor_phones (
visitor_id,
phone_number,
is_active,
valid_from
)
VALUES (
v_visitor_id,
p_contact_number,
TRUE,
NOW()
);
END IF;
-- 3. Apartment mapping
IF NOT EXISTS (
SELECT 1 FROM visitor_apartments
WHERE visitor_id = v_visitor_id
AND visitor_apartment_id = p_apartment_id
) THEN
INSERT INTO visitor_apartments (
visitor_id,
visitor_apartment_id,
first_seen
)
VALUES (
v_visitor_id,
p_apartment_id,
NOW()
);
END IF;
-- 4. Visitor log
INSERT INTO visitor_logs (
visitor_id,
apartment_id,
visitor_type_id,
entry_time,
created_on_utc,
created_by,
visitor_status_id
)
VALUES (
v_visitor_id,
p_apartment_id,
p_visitor_type_id,
NULL,
NOW(),
p_created_by,
v_pending_status
)
RETURNING id INTO v_visitor_log_id;
-- 5. Multi-unit visits
FOREACH v_unit_id IN ARRAY p_unit_ids LOOP
INSERT INTO multi_unit_visits (
visitor_log_id,
unit_id,
visitor_statuses,
created_on_utc,
is_deleted,
created_by
)
VALUES (
v_visitor_log_id,
v_unit_id,
v_pending_status,
NOW(),
FALSE,
p_created_by
);
END LOOP;
-- 6. OPTIONAL service provider company info
IF p_sp_company_id IS NOT NULL THEN
INSERT INTO visitor_sp_company_visits (
visitor_log_id,
sp_company_category_id,
sp_company_subtype_id,
sp_company_id
)
VALUES (
v_visitor_log_id,
p_sp_category_id,
p_sp_subtype_id,
p_sp_company_id
);
END IF;
RETURN v_visitor_log_id;
END;
$function$
-- Function: create_visitor_with_pending_approval
CREATE OR REPLACE FUNCTION public.create_visitor_with_pending_approval(p_apartment_id uuid, p_first_name text, p_last_name text, p_image_url text, p_contact_number text, p_visitor_type_id integer, p_unit_ids integer[], p_created_by uuid, p_sp_category_id integer DEFAULT NULL::integer, p_sp_subtype_id integer DEFAULT NULL::integer, p_sp_company_id integer DEFAULT NULL::integer)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
v_visitor_id BIGINT;
v_visitor_log_id BIGINT;
v_unit_id INTEGER;
v_pending_status CONSTANT INTEGER := 1;
BEGIN
-- 1. Find visitor by phone
SELECT vp.visitor_id
INTO v_visitor_id
FROM visitor_phones vp
JOIN visitors v ON v.id = vp.visitor_id
WHERE vp.phone_number = p_contact_number
AND vp.is_active = TRUE
AND v.is_deleted = FALSE
LIMIT 1;
-- 2. Create visitor if not exists
IF NOT FOUND THEN
INSERT INTO visitors (
first_name,
last_name,
image_url,
visitor_type_id,
created_on_utc,
created_by
)
VALUES (
p_first_name,
p_last_name,
p_image_url,
p_visitor_type_id,
NOW(),
p_created_by
)
RETURNING id INTO v_visitor_id;
INSERT INTO visitor_phones (
visitor_id,
phone_number,
is_active,
valid_from
)
VALUES (
v_visitor_id,
p_contact_number,
TRUE,
NOW()
);
END IF;
-- 3. Apartment mapping
IF NOT EXISTS (
SELECT 1 FROM visitor_apartments
WHERE visitor_id = v_visitor_id
AND visitor_apartment_id = p_apartment_id
) THEN
INSERT INTO visitor_apartments (
visitor_id,
visitor_apartment_id,
first_seen
)
VALUES (
v_visitor_id,
p_apartment_id,
NOW()
);
END IF;
-- 4. Visitor log
INSERT INTO visitor_logs (
visitor_id,
apartment_id,
visitor_type_id,
entry_time,
created_on_utc,
created_by,
visitor_status_id
)
VALUES (
v_visitor_id,
p_apartment_id,
p_visitor_type_id,
NULL,
NOW(),
p_created_by,
v_pending_status
)
RETURNING id INTO v_visitor_log_id;
-- 5. Multi-unit visits
FOREACH v_unit_id IN ARRAY p_unit_ids LOOP
INSERT INTO multi_unit_visits (
visitor_log_id,
unit_id,
visitor_statuses,
created_on_utc,
is_deleted,
created_by
)
VALUES (
v_visitor_log_id,
v_unit_id,
v_pending_status,
NOW(),
FALSE,
p_created_by
);
END LOOP;
-- 6. OPTIONAL service provider company info
IF p_sp_company_id IS NOT NULL THEN
INSERT INTO visitor_sp_company_visits (
visitor_log_id,
sp_company_category_id,
sp_company_subtype_id,
sp_company_id
)
VALUES (
v_visitor_log_id,
p_sp_category_id,
p_sp_subtype_id,
p_sp_company_id
);
END IF;
RETURN v_visitor_log_id;
END;
$function$
-- Procedure: create_vehicle
CREATE OR REPLACE PROCEDURE public.create_vehicle(IN p_vehicle_number text, IN p_vehicle_type_id integer, IN p_unit_id integer, IN p_vehicle_rf_id text, IN p_vehicle_rf_id_secretcode text, IN p_created_by uuid)
LANGUAGE plpgsql
AS $procedure$
BEGIN
INSERT INTO public.vehicles (
vehicle_number,
vehicle_type_id,
unit_id,
vehicle_rf_id,
vehicle_rf_id_secretcode,
created_on_utc,
is_deleted,
created_by
) VALUES (
p_vehicle_number,
p_vehicle_type_id,
p_unit_id,
p_vehicle_rf_id,
p_vehicle_rf_id_secretcode,
now() at time zone 'utc',
false,
p_created_by
);
END;
$procedure$
-- Procedure: delete_residents_by_apartment
CREATE OR REPLACE PROCEDURE public.delete_residents_by_apartment(IN p_apartment_id uuid)
LANGUAGE plpgsql
AS $procedure$
BEGIN
DELETE FROM public.resident_units
WHERE resident_id IN (
SELECT id FROM public.residents
WHERE apartment_id = p_apartment_id
);
DELETE FROM public.residents
WHERE apartment_id = p_apartment_id;
END;
$procedure$
-- Procedure: initialize_company
-- SOURCE
CREATE OR REPLACE PROCEDURE public.initialize_company(IN p_company_id uuid, IN p_organization_id uuid, IN p_is_apartment boolean, IN p_name text, IN p_created_by uuid)
LANGUAGE plpgsql
AS $procedure$
BEGIN
-- Insert into companies table
INSERT INTO public.companies (
id,
organization_id,
is_apartment,
name,
created_on_utc,
created_by
) VALUES (
p_company_id,
p_organization_id,
p_is_apartment,
p_name,
NOW(),
p_created_by
);
-- If this company is an apartment, insert into apartments table as well
IF p_is_apartment THEN
INSERT INTO public.apartments (
id,
organization_id,
name,
apartment_type_id,
created_on_utc,
created_by
) VALUES (
p_company_id,
p_organization_id,
p_name,
1, -- default apartment_type_id
NOW(),
p_created_by
);
END IF;
END;
$procedure$
-- TARGET
CREATE OR REPLACE PROCEDURE public.initialize_company(IN p_company_id uuid, IN p_organization_id uuid, IN p_is_apartment boolean, IN p_name text, IN p_created_by uuid)
LANGUAGE plpgsql
AS $procedure$
BEGIN
-- Insert into companies table
INSERT INTO public.companies (
id,
organization_id,
is_apartment,
name,
created_on_utc,
created_by
) VALUES (
p_company_id,
p_organization_id,
p_is_apartment,
p_name,
NOW(),
p_created_by
);
END;
$procedure$
-- Procedure: update_ticket_status_with_logs
CREATE OR REPLACE PROCEDURE public.update_ticket_status_with_logs(IN p_apartment_id uuid, IN p_ticket_status_id integer, IN p_ticket_ids uuid[], IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_ticket RECORD;
v_old_status int;
v_is_on_hold bool;
v_new_log_id uuid;
BEGIN
-- Process each ticket in the given list
FOR v_ticket IN
SELECT id, ticket_status_id, is_on_hold
FROM public.tickets
WHERE id = ANY(p_ticket_ids)
LOOP
v_old_status := v_ticket.ticket_status_id;
v_is_on_hold := v_ticket.is_on_hold;
-- Validate status transition
IF EXISTS (
SELECT 1
FROM public.ticket_workflow
WHERE status = v_old_status
AND next_status = p_ticket_status_id
AND apartment_id = p_apartment_id
) THEN
-- Update the ticket
UPDATE public.tickets
SET ticket_status_id = p_ticket_status_id,
modified_by = p_modified_by,
modified_on_utc = now()
WHERE id = v_ticket.id;
-- Insert log entry
v_new_log_id := gen_random_uuid();
INSERT INTO public.ticket_logs (
id,
ticket_id,
old_status_id,
new_status_id,
is_on_hold,
created_by,
created_on_utc
)
VALUES (
v_new_log_id,
v_ticket.id,
v_old_status,
p_ticket_status_id,
v_is_on_hold,
p_modified_by,
now()
);
RAISE NOTICE 'Ticket % updated from % to %', v_ticket.id, v_old_status, p_ticket_status_id;
ELSE
RAISE WARNING 'Invalid status transition for Ticket ID: % (old=% new=%)',
v_ticket.id, v_old_status, p_ticket_status_id;
END IF;
END LOOP;
END;
$procedure$
-- Procedure: toggle_ticket_hold_with_logs
CREATE OR REPLACE PROCEDURE public.toggle_ticket_hold_with_logs(IN p_ticket_ids uuid[], IN p_hold boolean, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_ticket RECORD;
v_log_id uuid;
BEGIN
FOR v_ticket IN
SELECT id, ticket_status_id, is_on_hold
FROM public.tickets
WHERE id = ANY(p_ticket_ids)
LOOP
----------------------------------------------------------------
-- Skip if state is already same
----------------------------------------------------------------
IF v_ticket.is_on_hold = p_hold THEN
IF p_hold THEN
RAISE NOTICE 'Ticket % is already ON HOLD', v_ticket.id;
ELSE
RAISE NOTICE 'Ticket % is already NOT on hold', v_ticket.id;
END IF;
CONTINUE;
END IF;
----------------------------------------------------------------
-- Update ticket hold state
----------------------------------------------------------------
UPDATE public.tickets
SET is_on_hold = p_hold,
modified_by = p_modified_by,
modified_on_utc = now()
WHERE id = v_ticket.id;
----------------------------------------------------------------
-- Insert TicketLog
----------------------------------------------------------------
v_log_id := gen_random_uuid();
INSERT INTO public.ticket_logs (
id,
ticket_id,
old_status_id,
new_status_id,
is_on_hold,
created_by,
created_on_utc
)
VALUES (
v_log_id,
v_ticket.id,
v_ticket.ticket_status_id,
v_ticket.ticket_status_id, -- no status change
p_hold, -- NEW hold value
p_modified_by,
now()
);
----------------------------------------------------------------
-- Messages
----------------------------------------------------------------
IF p_hold THEN
RAISE NOTICE 'Ticket % set to ON HOLD', v_ticket.id;
ELSE
RAISE NOTICE 'Ticket % UN-HOLD done', v_ticket.id;
END IF;
END LOOP;
END;
$procedure$
-- Procedure: mark_ticket_reopened_with_logs
CREATE OR REPLACE PROCEDURE public.mark_ticket_reopened_with_logs(IN p_ticket_ids uuid[], IN p_reopen_status_id integer, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_ticket RECORD;
v_log_id uuid;
BEGIN
FOR v_ticket IN
SELECT id, ticket_status_id, is_re_opened
FROM public.tickets
WHERE id = ANY(p_ticket_ids)
LOOP
----------------------------------------------------------------
-- Skip if already reopened once (idempotent)
----------------------------------------------------------------
IF v_ticket.is_re_opened THEN
RAISE NOTICE 'Ticket % is already marked as reopened', v_ticket.id;
CONTINUE;
END IF;
----------------------------------------------------------------
-- Update ticket (set reopened flag + change status)
----------------------------------------------------------------
UPDATE public.tickets
SET
ticket_status_id = p_reopen_status_id, -- dynamic
is_re_opened = true, -- YOUR COLUMN NAME
modified_by = p_modified_by,
modified_on_utc = now()
WHERE id = v_ticket.id;
----------------------------------------------------------------
-- Insert Ticket Log (NO EXTRA BOOLEAN)
----------------------------------------------------------------
v_log_id := gen_random_uuid();
INSERT INTO public.ticket_logs (
id,
ticket_id,
old_status_id,
new_status_id,
is_on_hold,
created_by,
created_on_utc
)
VALUES (
v_log_id,
v_ticket.id,
v_ticket.ticket_status_id,
p_reopen_status_id, -- new status
false, -- hold unchanged
p_modified_by,
now()
);
RAISE NOTICE 'Ticket % marked as REOPENED', v_ticket.id;
END LOOP;
END;
$procedure$
-- Procedure: assign_ticket_with_logs
CREATE OR REPLACE PROCEDURE public.assign_ticket_with_logs(IN p_ticket_id uuid, IN p_service_provider_id integer, IN p_new_status_id integer, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_ticket RECORD;
v_log_id uuid;
BEGIN
----------------------------------------------------------------
-- Fetch full ticket row
----------------------------------------------------------------
SELECT
id,
ticket_status_id,
ticket_assigned_to,
is_on_hold
INTO v_ticket
FROM public.tickets
WHERE id = p_ticket_id;
IF v_ticket.id IS NULL THEN
RAISE EXCEPTION 'Ticket % not found', p_ticket_id;
END IF;
----------------------------------------------------------------
-- Update assigned_to and status
----------------------------------------------------------------
UPDATE public.tickets
SET
ticket_assigned_to = p_service_provider_id,
ticket_status_id = p_new_status_id,
modified_by = p_modified_by,
modified_on_utc = now()
WHERE id = p_ticket_id;
----------------------------------------------------------------
-- Insert assignment log
----------------------------------------------------------------
v_log_id := gen_random_uuid();
INSERT INTO public.ticket_logs (
id,
ticket_id,
old_status_id,
new_status_id,
is_on_hold,
created_by,
created_on_utc
)
VALUES (
v_log_id,
p_ticket_id,
v_ticket.ticket_status_id, -- OLD STATUS
p_new_status_id, -- NEW STATUS
v_ticket.is_on_hold, -- Hold flag unchanged
p_modified_by,
now()
);
END;
$procedure$
-- Procedure: assign_multiple_tickets_with_logs_json
CREATE OR REPLACE PROCEDURE public.assign_multiple_tickets_with_logs_json(IN p_assignments jsonb, IN p_new_status_id integer, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_item jsonb;
v_ticket_id uuid;
v_sp_id int;
v_ticket RECORD;
v_log_id uuid;
BEGIN
FOR v_item IN SELECT * FROM jsonb_array_elements(p_assignments)
LOOP
v_ticket_id := (v_item->>'ticketId')::uuid;
v_sp_id := (v_item->>'serviceProviderId')::int;
-- Fetch ticket
SELECT id, ticket_status_id, ticket_assigned_to, is_on_hold
INTO v_ticket
FROM public.tickets
WHERE id = v_ticket_id;
IF v_ticket.id IS NULL THEN
RAISE EXCEPTION 'Ticket % not found', v_ticket_id;
END IF;
-- Update ticket
UPDATE public.tickets
SET
ticket_assigned_to = v_sp_id,
ticket_status_id = p_new_status_id,
modified_by = p_modified_by,
modified_on_utc = now()
WHERE id = v_ticket_id;
-- Insert log
v_log_id := gen_random_uuid();
INSERT INTO public.ticket_logs (
id, ticket_id, old_status_id, new_status_id,
is_on_hold, created_by, created_on_utc
)
VALUES (
v_log_id,
v_ticket_id,
v_ticket.ticket_status_id,
p_new_status_id,
v_ticket.is_on_hold,
p_modified_by,
now()
);
END LOOP;
END;
$procedure$
-- Procedure: add_existing_user_role_permissions
CREATE OR REPLACE PROCEDURE public.add_existing_user_role_permissions(IN p_user_id uuid, IN p_created_by uuid, IN p_organization_id uuid DEFAULT NULL::uuid, IN p_company_id uuid DEFAULT NULL::uuid, IN p_permission_ids uuid[] DEFAULT NULL::uuid[], IN p_role_ids integer[] DEFAULT NULL::integer[])
LANGUAGE plpgsql
AS $procedure$
BEGIN
-- Insert user to organization if organization_id is provided
IF p_organization_id IS NOT NULL THEN
CALL public.insert_organization_user(p_user_id, p_created_by, p_organization_id);
END IF;
-- Insert user to company if company_id is provided
IF p_company_id IS NOT NULL THEN
CALL public.insert_company_user(p_user_id, p_created_by, p_company_id);
END IF;
-- Insert permissions if both permission_ids array and organization_id are provided
IF p_permission_ids IS NOT NULL AND p_organization_id IS NOT NULL THEN
CALL public.insert_user_permission(p_user_id, p_organization_id, p_permission_ids, p_created_by);
END IF;
-- Insert roles if role_ids array is provided
IF p_role_ids IS NOT NULL THEN
CALL public.insert_user_roles(p_user_id, p_role_ids, p_organization_id, p_created_by);
END IF;
END;
$procedure$