From 9e2c88392e1a4520edabd9e6a478a460e88ae01a Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 00:47:06 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/integrations/supabase/types.ts | 160 ++++++++++++++++++ ...2_f587e02a-d1c2-4181-94ce-00a4ccf70026.sql | 142 ++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 supabase/migrations/20260417004702_f587e02a-d1c2-4181-94ce-00a4ccf70026.sql diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 2d88199..d57bd90 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -141,6 +141,7 @@ export type Database = { Row: { address_line1: string | null address_line2: string | null + annual_interest_rate: number | null board_members: Json city: string | null client_type: Database["public"]["Enums"]["client_type"] @@ -161,6 +162,7 @@ export type Database = { Insert: { address_line1?: string | null address_line2?: string | null + annual_interest_rate?: number | null board_members?: Json city?: string | null client_type?: Database["public"]["Enums"]["client_type"] @@ -181,6 +183,7 @@ export type Database = { Update: { address_line1?: string | null address_line2?: string | null + annual_interest_rate?: number | null board_members?: Json city?: string | null client_type?: Database["public"]["Enums"]["client_type"] @@ -200,6 +203,107 @@ export type Database = { } Relationships: [] } + collection_ledger_entries: { + Row: { + collection_id: string + created_at: string + created_by: string | null + credit: number + debit: number + description: string | null + entry_date: string + id: string + transaction_type: string + updated_at: string + } + Insert: { + collection_id: string + created_at?: string + created_by?: string | null + credit?: number + debit?: number + description?: string | null + entry_date?: string + id?: string + transaction_type: string + updated_at?: string + } + Update: { + collection_id?: string + created_at?: string + created_by?: string | null + credit?: number + debit?: number + description?: string | null + entry_date?: string + id?: string + transaction_type?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "collection_ledger_entries_collection_id_fkey" + columns: ["collection_id"] + isOneToOne: false + referencedRelation: "collections" + referencedColumns: ["id"] + }, + ] + } + collections: { + Row: { + case_id: string + closed_at: string | null + created_at: string + created_by: string | null + homeowner_id: string + id: string + notes: string | null + opened_at: string + status: string + updated_at: string + } + Insert: { + case_id: string + closed_at?: string | null + created_at?: string + created_by?: string | null + homeowner_id: string + id?: string + notes?: string | null + opened_at?: string + status?: string + updated_at?: string + } + Update: { + case_id?: string + closed_at?: string | null + created_at?: string + created_by?: string | null + homeowner_id?: string + id?: string + notes?: string | null + opened_at?: string + status?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "collections_case_id_fkey" + columns: ["case_id"] + isOneToOne: false + referencedRelation: "cases" + referencedColumns: ["id"] + }, + { + foreignKeyName: "collections_homeowner_id_fkey" + columns: ["homeowner_id"] + isOneToOne: false + referencedRelation: "homeowners" + referencedColumns: ["id"] + }, + ] + } documents: { Row: { case_id: string @@ -301,6 +405,62 @@ export type Database = { }, ] } + homeowners: { + Row: { + address: string | null + client_id: string + created_at: string + created_by: string | null + email: string | null + first_name: string + id: string + last_name: string + notes: string | null + opening_balance: number + phone: string | null + unit_number: string | null + updated_at: string + } + Insert: { + address?: string | null + client_id: string + created_at?: string + created_by?: string | null + email?: string | null + first_name: string + id?: string + last_name: string + notes?: string | null + opening_balance?: number + phone?: string | null + unit_number?: string | null + updated_at?: string + } + Update: { + address?: string | null + client_id?: string + created_at?: string + created_by?: string | null + email?: string | null + first_name?: string + id?: string + last_name?: string + notes?: string | null + opening_balance?: number + phone?: string | null + unit_number?: string | null + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "homeowners_client_id_fkey" + columns: ["client_id"] + isOneToOne: false + referencedRelation: "clients" + referencedColumns: ["id"] + }, + ] + } invoices: { Row: { amount_paid: number diff --git a/supabase/migrations/20260417004702_f587e02a-d1c2-4181-94ce-00a4ccf70026.sql b/supabase/migrations/20260417004702_f587e02a-d1c2-4181-94ce-00a4ccf70026.sql new file mode 100644 index 0000000..ad50e0e --- /dev/null +++ b/supabase/migrations/20260417004702_f587e02a-d1c2-4181-94ce-00a4ccf70026.sql @@ -0,0 +1,142 @@ +-- 1. Annual interest rate on clients (per annum, non-compounded, applied monthly) +ALTER TABLE public.clients + ADD COLUMN IF NOT EXISTS annual_interest_rate numeric; + +-- 2. Homeowners (belong to a client / HOA) +CREATE TABLE IF NOT EXISTS public.homeowners ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + client_id uuid NOT NULL REFERENCES public.clients(id) ON DELETE CASCADE, + first_name text NOT NULL, + last_name text NOT NULL, + unit_number text, + address text, + email text, + phone text, + opening_balance numeric NOT NULL DEFAULT 0, + notes text, + created_by uuid, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_homeowners_client ON public.homeowners(client_id); + +ALTER TABLE public.homeowners ENABLE ROW LEVEL SECURITY; + +CREATE POLICY homeowners_select_auth ON public.homeowners + FOR SELECT TO authenticated USING (true); + +CREATE POLICY homeowners_insert_auth ON public.homeowners + FOR INSERT TO authenticated WITH CHECK (auth.uid() IS NOT NULL); + +CREATE POLICY homeowners_update_owner ON public.homeowners + FOR UPDATE TO authenticated + USING (public.is_admin(auth.uid()) OR created_by = auth.uid()); + +CREATE POLICY homeowners_delete_admin ON public.homeowners + FOR DELETE TO authenticated + USING (public.is_admin(auth.uid()) OR created_by = auth.uid()); + +CREATE TRIGGER homeowners_set_updated_at + BEFORE UPDATE ON public.homeowners + FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at(); + +-- 3. Collections (one record per homeowner per case/matter) +CREATE TABLE IF NOT EXISTS public.collections ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + case_id uuid NOT NULL REFERENCES public.cases(id) ON DELETE CASCADE, + homeowner_id uuid NOT NULL REFERENCES public.homeowners(id) ON DELETE RESTRICT, + status text NOT NULL DEFAULT 'late_notice', + notes text, + opened_at date NOT NULL DEFAULT CURRENT_DATE, + closed_at date, + created_by uuid, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + UNIQUE (case_id, homeowner_id) +); +CREATE INDEX IF NOT EXISTS idx_collections_case ON public.collections(case_id); +CREATE INDEX IF NOT EXISTS idx_collections_homeowner ON public.collections(homeowner_id); + +ALTER TABLE public.collections ENABLE ROW LEVEL SECURITY; + +CREATE POLICY collections_select_case ON public.collections + FOR SELECT TO authenticated + USING (public.can_access_case(case_id, auth.uid())); + +CREATE POLICY collections_insert_case ON public.collections + FOR INSERT TO authenticated + WITH CHECK (public.can_access_case(case_id, auth.uid())); + +CREATE POLICY collections_update_case ON public.collections + FOR UPDATE TO authenticated + USING (public.can_access_case(case_id, auth.uid())); + +CREATE POLICY collections_delete_case ON public.collections + FOR DELETE TO authenticated + USING (public.can_access_case(case_id, auth.uid())); + +CREATE TRIGGER collections_set_updated_at + BEFORE UPDATE ON public.collections + FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at(); + +-- 4. Collection ledger entries (debit/credit per collection) +CREATE TABLE IF NOT EXISTS public.collection_ledger_entries ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + collection_id uuid NOT NULL REFERENCES public.collections(id) ON DELETE CASCADE, + entry_date date NOT NULL DEFAULT CURRENT_DATE, + transaction_type text NOT NULL, + description text, + debit numeric NOT NULL DEFAULT 0, + credit numeric NOT NULL DEFAULT 0, + created_by uuid, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_cle_collection ON public.collection_ledger_entries(collection_id); +CREATE INDEX IF NOT EXISTS idx_cle_date ON public.collection_ledger_entries(entry_date); + +ALTER TABLE public.collection_ledger_entries ENABLE ROW LEVEL SECURITY; + +CREATE POLICY cle_select_case ON public.collection_ledger_entries + FOR SELECT TO authenticated + USING ( + EXISTS ( + SELECT 1 FROM public.collections c + WHERE c.id = collection_id + AND public.can_access_case(c.case_id, auth.uid()) + ) + ); + +CREATE POLICY cle_insert_case ON public.collection_ledger_entries + FOR INSERT TO authenticated + WITH CHECK ( + EXISTS ( + SELECT 1 FROM public.collections c + WHERE c.id = collection_id + AND public.can_access_case(c.case_id, auth.uid()) + ) + ); + +CREATE POLICY cle_update_case ON public.collection_ledger_entries + FOR UPDATE TO authenticated + USING ( + EXISTS ( + SELECT 1 FROM public.collections c + WHERE c.id = collection_id + AND public.can_access_case(c.case_id, auth.uid()) + ) + ); + +CREATE POLICY cle_delete_case ON public.collection_ledger_entries + FOR DELETE TO authenticated + USING ( + EXISTS ( + SELECT 1 FROM public.collections c + WHERE c.id = collection_id + AND public.can_access_case(c.case_id, auth.uid()) + ) + ); + +CREATE TRIGGER cle_set_updated_at + BEFORE UPDATE ON public.collection_ledger_entries + FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at(); \ No newline at end of file