caterium-app/ops/sql/SUPABASE-V17.7.0-SERVER-ORDER-AUTOMATION.sql
pavlov346346-source f8bb0a78b4
Caterium v17.7.0 — architecture foundation
Introduce the first data layer, move order auto-completion to server cron with legacy/cloud compatibility, clean error telemetry, update PWA/versioning, and add regression coverage.
2026-09-08 13:24:34 +03:00

89 lines
5.6 KiB
PL/PgSQL

alter table public.sun_workspaces add column if not exists timezone text not null default 'Europe/Moscow';
create or replace function public.sun_order_due_at(p_data jsonb,p_timezone text)
returns timestamptz
language plpgsql
stable
set search_path='public' as $$
declare v_date date; v_time time; v_tz text;
begin
if coalesce(p_data->>'date','') !~ '^\d{4}-\d{2}-\d{2}$' or coalesce(p_data->>'time','') !~ '^\d{2}:\d{2}$' then return null; end if;
v_date := (p_data->>'date')::date;
v_time := (p_data->>'time')::time;
v_tz := coalesce(nullif(trim(p_timezone),''),'Europe/Moscow');
return ((v_date::timestamp + v_time) at time zone v_tz) + interval '1 minute';
exception when others then return null;
end;$$;
create or replace function public.sun_process_due_orders_internal(p_workspace uuid,p_now timestamptz default now())
returns jsonb
language plpgsql
security definer
set search_path='public' as $$
declare v_tz text; v_changed integer:=0; v_ids text[]:=array[]::text[]; r record; v_total numeric; v_iso text; v_orders jsonb;
begin
select timezone into v_tz from public.sun_workspaces where id=p_workspace;
if v_tz is null then return jsonb_build_object('changed',0,'ids','[]'::jsonb); end if;
v_iso:=to_char(p_now at time zone 'UTC','YYYY-MM-DD"T"HH24:MI:SS.MS"Z"');
for r in
select order_id,data,version from public.sun_v17_orders
where workspace_id=p_workspace
and lower(coalesce(data->>'status','')) not in ('отменён','отменен','отдан заказчику','завершён','завершен')
and coalesce(data->>'sunAutoCompletedAt','')=''
and public.sun_order_due_at(data,v_tz) is not null
and public.sun_order_due_at(data,v_tz) <= p_now
for update
loop
v_total:=case when coalesce(r.data->>'total','') ~ '^-?\d+(\.\d+)?$' then greatest(0,(r.data->>'total')::numeric) else 0 end;
update public.sun_v17_orders
set data=r.data||jsonb_build_object('prepayment',v_total,'balance',0,'status','Отдан заказчику','paymentStatus','paid','completedAt',coalesce(nullif(r.data->>'completedAt',''),v_iso),'paymentCompletedAt',coalesce(nullif(r.data->>'paymentCompletedAt',''),v_iso),'sunAutoCompletedAt',v_iso,'sunAutoCompletedV1770',true,'sunAutoCompletedVersion','17.7.0'),version=version+1,updated_at=p_now,updated_by=null
where workspace_id=p_workspace and order_id=r.order_id;
insert into public.sun_v17_change_events(workspace_id,entity,entity_key,operation,version,client_id,created_by)
values(p_workspace,'order',r.order_id,'upsert',r.version+1,'server:auto-complete',null);
v_changed:=v_changed+1;v_ids:=array_append(v_ids,r.order_id);
end loop;
if v_changed>0 then
select coalesce(jsonb_agg(o.data order by case when o.order_id ~ '^\d+$' then o.order_id::bigint else null end,o.order_id),'[]'::jsonb) into v_orders from public.sun_v17_orders o where o.workspace_id=p_workspace;
update public.sun_app_state s set payload=jsonb_set(coalesce(s.payload,'{}'::jsonb),'{storage,sunOrders}',jsonb_build_object('t','j','v',v_orders),true),revision=s.revision+1,updated_at=p_now,client_id='server:auto-complete' where s.workspace_id=p_workspace;
if not found then insert into public.sun_app_state(workspace_id,payload,revision,updated_at,client_id) values(p_workspace,jsonb_build_object('format','sun-cloud-v2','version',2,'storage',jsonb_build_object('sunOrders',jsonb_build_object('t','j','v',v_orders))),1,p_now,'server:auto-complete'); end if;
end if;
return jsonb_build_object('changed',v_changed,'ids',to_jsonb(v_ids),'workspace',p_workspace,'timezone',v_tz,'processed_at',v_iso);
end;$$;
create or replace function public.sun_run_order_automation(p_workspace uuid)
returns jsonb
language plpgsql
security definer
set search_path='public' as $$
declare v_result jsonb; v_ids text[]; v_orders jsonb;
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)='blocked' then raise exception 'Подписка закончилась'; end if;
v_result:=public.sun_process_due_orders_internal(p_workspace,now());
select coalesce(array_agg(value::text),'{}'::text[]) into v_ids from jsonb_array_elements_text(coalesce(v_result->'ids','[]'::jsonb));
if coalesce(array_length(v_ids,1),0)>0 then select coalesce(jsonb_agg(o.data order by o.order_id),'[]'::jsonb) into v_orders from public.sun_v17_orders o where o.workspace_id=p_workspace and o.order_id=any(v_ids); else v_orders:='[]'::jsonb; end if;
return v_result||jsonb_build_object('orders',v_orders);
end;$$;
create or replace function public.caterium_process_due_orders_all()
returns jsonb
language plpgsql
security definer
set search_path='public' as $$
declare r record; v_total integer:=0; v_result jsonb;
begin
for r in select id from public.sun_workspaces loop v_result:=public.sun_process_due_orders_internal(r.id,now());v_total:=v_total+coalesce((v_result->>'changed')::integer,0);end loop;
return jsonb_build_object('changed',v_total,'processed_at',now());
end;$$;
revoke all on function public.sun_process_due_orders_internal(uuid,timestamptz) from public,anon,authenticated;
revoke all on function public.caterium_process_due_orders_all() from public,anon,authenticated;
revoke all on function public.sun_order_due_at(jsonb,text) from anon;
grant execute on function public.sun_run_order_automation(uuid) to authenticated;
do $$ declare v_job bigint; begin
select jobid into v_job from cron.job where jobname='caterium-order-auto-complete';
if v_job is not null then perform cron.unschedule(v_job); end if;
perform cron.schedule('caterium-order-auto-complete','* * * * *','select public.caterium_process_due_orders_all();');
end$$;