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$$;