diff --git a/ops/sql/SUPABASE-V17.7.2-CLIENTS-FOUNDATION.sql b/ops/sql/SUPABASE-V17.7.2-CLIENTS-FOUNDATION.sql new file mode 100644 index 0000000..ddf9e0c --- /dev/null +++ b/ops/sql/SUPABASE-V17.7.2-CLIENTS-FOUNDATION.sql @@ -0,0 +1,73 @@ +-- Caterium v17.7.2 — normalized client foundation +-- Additive migration: preserves legacy state and existing client rows. + +create or replace function public.sun_v17_save_client_v1772( + p_workspace uuid, + p_client_key text, + p_profile jsonb, + p_expected_version bigint default null, + p_client_id text default null +) +returns table(client_key text, version bigint, data jsonb, updated_at timestamptz) +language plpgsql +security definer +set search_path = public +as $function$ +declare + cur bigint; + saved public.sun_v17_clients%rowtype; + next_name text; + next_phone text; + next_address text; +begin + if public.sun_member_role(p_workspace) is null then + raise exception 'Access denied'; + end if; + if public.sun_subscription_access_mode(p_workspace) <> 'full' + or not public.sun_workspace_has_feature(p_workspace,'clients') then + raise exception 'Клиенты недоступны для изменения'; + end if; + if not public.sun_has_permission(p_workspace,'clients.edit') then + raise exception 'Нет права изменять клиентов'; + end if; + if coalesce(nullif(trim(p_client_key),''),'') = '' then + raise exception 'Client key is required'; + end if; + if jsonb_typeof(coalesce(p_profile,'{}'::jsonb)) <> 'object' then + raise exception 'Invalid client profile'; + end if; + + select c.version into cur + from public.sun_v17_clients c + where c.workspace_id=p_workspace and c.client_key=p_client_key + for update; + + if found and p_expected_version is not null and cur <> p_expected_version then + raise exception using errcode='40001',message=format('SUN_CLIENT_CONFLICT expected=%s actual=%s',p_expected_version,cur); + end if; + + next_name=nullif(trim(coalesce(p_profile#>>'{identity,name}',p_profile->>'name','')),''); + next_phone=nullif(trim(coalesce(p_profile#>>'{identity,phone}',p_profile->>'phone','')),''); + next_address=nullif(trim(coalesce(p_profile#>>'{identity,latestAddress}',p_profile->>'latestAddress','')),''); + + insert into public.sun_v17_clients(workspace_id,client_key,name,phone,latest_address,data,version,created_at,updated_at) + values(p_workspace,p_client_key,next_name,next_phone,next_address,p_profile,1,now(),now()) + on conflict(workspace_id,client_key) do update set + name=coalesce(excluded.name,sun_v17_clients.name), + phone=coalesce(excluded.phone,sun_v17_clients.phone), + latest_address=coalesce(excluded.latest_address,sun_v17_clients.latest_address), + data=excluded.data, + version=case when sun_v17_clients.data is distinct from excluded.data then sun_v17_clients.version+1 else sun_v17_clients.version end, + updated_at=case when sun_v17_clients.data is distinct from excluded.data then now() else sun_v17_clients.updated_at end + returning * into saved; + + if not found or cur is distinct from saved.version then + insert into public.sun_v17_change_events(workspace_id,entity,entity_key,operation,version,client_id,created_by) + values(p_workspace,'client',p_client_key,'upsert',saved.version,p_client_id,auth.uid()); + end if; + + return query select saved.client_key,saved.version,saved.data,saved.updated_at; +end; +$function$; + +grant execute on function public.sun_v17_save_client_v1772(uuid,text,jsonb,bigint,text) to authenticated;