mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Add ACMCC app source, Supabase backend, and project config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.auto_link_violation_owner()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $function$
|
||||
DECLARE
|
||||
v_owner_id UUID;
|
||||
v_unit_id UUID;
|
||||
v_norm TEXT;
|
||||
BEGIN
|
||||
IF NEW.address IS NOT NULL AND btrim(NEW.address) <> '' THEN
|
||||
-- Aggressive normalization: lowercase, strip all whitespace and common punctuation
|
||||
v_norm := regexp_replace(lower(NEW.address), '[[:space:].,#\-/]+', '', 'g');
|
||||
|
||||
-- 1) Exact normalized match on units.address
|
||||
IF NEW.unit_id IS NULL THEN
|
||||
SELECT u.id INTO v_unit_id
|
||||
FROM public.units u
|
||||
WHERE u.association_id = NEW.association_id
|
||||
AND regexp_replace(lower(coalesce(u.address,'')), '[[:space:].,#\-/]+', '', 'g') = v_norm
|
||||
ORDER BY u.created_at
|
||||
LIMIT 1;
|
||||
|
||||
-- 2) Fallback: match by unit_number or account_number
|
||||
IF v_unit_id IS NULL THEN
|
||||
SELECT u.id INTO v_unit_id
|
||||
FROM public.units u
|
||||
WHERE u.association_id = NEW.association_id
|
||||
AND (
|
||||
regexp_replace(lower(coalesce(u.unit_number,'')), '[[:space:].,#\-/]+', '', 'g') = v_norm
|
||||
OR regexp_replace(lower(coalesce(u.account_number,'')), '[[:space:].,#\-/]+', '', 'g') = v_norm
|
||||
)
|
||||
ORDER BY u.created_at
|
||||
LIMIT 1;
|
||||
END IF;
|
||||
|
||||
-- 3) Fallback: substring match on address
|
||||
IF v_unit_id IS NULL AND length(v_norm) >= 4 THEN
|
||||
SELECT u.id INTO v_unit_id
|
||||
FROM public.units u
|
||||
WHERE u.association_id = NEW.association_id
|
||||
AND regexp_replace(lower(coalesce(u.address,'')), '[[:space:].,#\-/]+', '', 'g') LIKE '%' || v_norm || '%'
|
||||
ORDER BY u.created_at
|
||||
LIMIT 1;
|
||||
END IF;
|
||||
|
||||
IF v_unit_id IS NOT NULL THEN NEW.unit_id := v_unit_id; END IF;
|
||||
END IF;
|
||||
|
||||
-- Owner: exact normalized match on property_address
|
||||
IF NEW.owner_id IS NULL THEN
|
||||
SELECT o.id INTO v_owner_id
|
||||
FROM public.owners o
|
||||
WHERE o.association_id = NEW.association_id
|
||||
AND regexp_replace(lower(coalesce(o.property_address,'')), '[[:space:].,#\-/]+', '', 'g') = v_norm
|
||||
ORDER BY (CASE WHEN o.status = 'active' THEN 0 ELSE 1 END), o.created_at
|
||||
LIMIT 1;
|
||||
|
||||
-- Fallback: substring match
|
||||
IF v_owner_id IS NULL AND length(v_norm) >= 4 THEN
|
||||
SELECT o.id INTO v_owner_id
|
||||
FROM public.owners o
|
||||
WHERE o.association_id = NEW.association_id
|
||||
AND regexp_replace(lower(coalesce(o.property_address,'')), '[[:space:].,#\-/]+', '', 'g') LIKE '%' || v_norm || '%'
|
||||
ORDER BY (CASE WHEN o.status = 'active' THEN 0 ELSE 1 END), o.created_at
|
||||
LIMIT 1;
|
||||
END IF;
|
||||
|
||||
IF v_owner_id IS NOT NULL THEN NEW.owner_id := v_owner_id; END IF;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- If unit known but owner unknown, derive owner from unit
|
||||
IF NEW.owner_id IS NULL AND NEW.unit_id IS NOT NULL THEN
|
||||
SELECT o.id INTO v_owner_id
|
||||
FROM public.owners o
|
||||
WHERE o.association_id = NEW.association_id
|
||||
AND o.unit_id = NEW.unit_id
|
||||
ORDER BY (CASE WHEN o.status = 'active' THEN 0 ELSE 1 END), o.created_at
|
||||
LIMIT 1;
|
||||
IF v_owner_id IS NOT NULL THEN NEW.owner_id := v_owner_id; END IF;
|
||||
END IF;
|
||||
|
||||
-- If owner known but unit unknown, derive unit from owner
|
||||
IF NEW.unit_id IS NULL AND NEW.owner_id IS NOT NULL THEN
|
||||
SELECT o.unit_id INTO v_unit_id
|
||||
FROM public.owners o
|
||||
WHERE o.id = NEW.owner_id;
|
||||
IF v_unit_id IS NOT NULL THEN NEW.unit_id := v_unit_id; END IF;
|
||||
END IF;
|
||||
|
||||
-- Backfill address from owner/unit if address is empty
|
||||
IF (NEW.address IS NULL OR btrim(NEW.address) = '') THEN
|
||||
IF NEW.owner_id IS NOT NULL THEN
|
||||
SELECT o.property_address INTO NEW.address FROM public.owners o WHERE o.id = NEW.owner_id;
|
||||
END IF;
|
||||
IF (NEW.address IS NULL OR btrim(NEW.address) = '') AND NEW.unit_id IS NOT NULL THEN
|
||||
SELECT u.address INTO NEW.address FROM public.units u WHERE u.id = NEW.unit_id;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- Auto-fill violation_type / title
|
||||
IF NEW.violation_type IS NULL OR btrim(NEW.violation_type) = '' THEN
|
||||
NEW.violation_type := NULLIF(btrim(COALESCE(NEW.category, NEW.title)), '');
|
||||
END IF;
|
||||
IF NEW.title IS NULL OR btrim(NEW.title) = '' THEN
|
||||
NEW.title := COALESCE(NEW.violation_type, 'Violation');
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$;
|
||||
|
||||
DROP TRIGGER IF EXISTS auto_link_violation_owner_trigger ON public.violations;
|
||||
CREATE TRIGGER auto_link_violation_owner_trigger
|
||||
BEFORE INSERT OR UPDATE ON public.violations
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.auto_link_violation_owner();
|
||||
|
||||
-- Backfill existing violations: re-run the trigger logic via no-op update
|
||||
UPDATE public.violations
|
||||
SET updated_at = updated_at
|
||||
WHERE (owner_id IS NULL OR unit_id IS NULL)
|
||||
AND address IS NOT NULL
|
||||
AND btrim(address) <> '';
|
||||
Reference in New Issue
Block a user