186 lines
5.1 KiB
PL/PgSQL
186 lines
5.1 KiB
PL/PgSQL
-- Auto-create public delivery links for rows imported into public.order_groups.
|
||
-- Run this in Supabase SQL Editor after the order_groups delivery columns exist.
|
||
|
||
create extension if not exists pgcrypto with schema extensions;
|
||
|
||
create or replace function public.next_order_group_sms_check_at(
|
||
start_from timestamptz default now(),
|
||
delay interval default interval '0 minutes'
|
||
)
|
||
returns timestamptz
|
||
language plpgsql
|
||
stable
|
||
as $$
|
||
declare
|
||
v_timezone text := 'Europe/Simferopol';
|
||
v_local_time timestamp;
|
||
v_local_date date;
|
||
v_work_start timestamp;
|
||
v_work_end timestamp;
|
||
v_candidate timestamp;
|
||
begin
|
||
v_local_time := (start_from at time zone v_timezone) + delay;
|
||
v_local_date := v_local_time::date;
|
||
v_work_start := v_local_date + time '09:00';
|
||
v_work_end := v_local_date + time '20:00';
|
||
|
||
if v_local_time < v_work_start then
|
||
v_candidate := v_work_start;
|
||
elsif v_local_time >= v_work_end then
|
||
v_candidate := (v_local_date + 1) + time '09:00';
|
||
else
|
||
v_candidate := v_local_time;
|
||
end if;
|
||
|
||
return v_candidate at time zone v_timezone;
|
||
end;
|
||
$$;
|
||
|
||
create or replace function public.build_order_group_default_available_slots(
|
||
start_from timestamptz default now()
|
||
)
|
||
returns text[]
|
||
language sql
|
||
stable
|
||
as $$
|
||
with candidate_days as (
|
||
select ((start_from at time zone 'Europe/Simferopol')::date + offset_days) as delivery_day
|
||
from generate_series(1, 2) as offset_days
|
||
),
|
||
slots as (
|
||
select format('%s, %s', delivery_day, half_day) as slot_name
|
||
from candidate_days
|
||
cross join (
|
||
values ('Первая половина дня'), ('Вторая половина дня')
|
||
) as halves(half_day)
|
||
)
|
||
select coalesce(array_agg(slot_name order by slot_name), array[]::text[])
|
||
from slots;
|
||
$$;
|
||
|
||
create or replace function public.ensure_order_group_delivery_link()
|
||
returns trigger
|
||
language plpgsql
|
||
security definer
|
||
set search_path = public, extensions
|
||
as $$
|
||
declare
|
||
v_customer_name text;
|
||
v_customer_phone text;
|
||
v_order_number text;
|
||
v_token text;
|
||
v_token_hash text;
|
||
v_delivery_link text;
|
||
v_invitation_id uuid;
|
||
v_base_url text := 'https://dost.supersamsev.ru';
|
||
begin
|
||
if pg_trigger_depth() > 1 then
|
||
return new;
|
||
end if;
|
||
|
||
if coalesce(new.status, '') <> 'ready_for_notification' then
|
||
return new;
|
||
end if;
|
||
|
||
if coalesce(new.delivery_status, 'pending_confirmation') <> 'pending_confirmation' then
|
||
return new;
|
||
end if;
|
||
|
||
if coalesce(new.notification_status, 'not_started') not in ('not_started', 'send_failed') then
|
||
return new;
|
||
end if;
|
||
|
||
if new.delivery_link is not null or new.delivery_invitation_id is not null then
|
||
return new;
|
||
end if;
|
||
|
||
v_customer_name := nullif(
|
||
coalesce(new.customer_name, new.customer ->> 'name'),
|
||
''
|
||
);
|
||
v_customer_phone := nullif(
|
||
coalesce(
|
||
new.customer_phone_normalized,
|
||
new.customer_phone,
|
||
new.customer ->> 'phone_normalized',
|
||
new.customer ->> 'phone',
|
||
split_part(new.group_key, '|', 1)
|
||
),
|
||
''
|
||
);
|
||
v_order_number := coalesce(new.group_key::text, new.order_numbers[1]::text);
|
||
|
||
if v_customer_phone is null then
|
||
update public.order_groups
|
||
set
|
||
notification_status = 'manual_required',
|
||
last_sms_error = 'Не найден телефон клиента для формирования SMS-ссылки',
|
||
next_notification_check_at = null,
|
||
updated_at = timezone('utc', now())
|
||
where id = new.id;
|
||
|
||
return new;
|
||
end if;
|
||
|
||
v_token := replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', '');
|
||
v_token_hash := encode(digest(v_token, 'sha256'), 'hex');
|
||
v_delivery_link := v_base_url || '/delivery/' || v_token;
|
||
|
||
insert into public.delivery_invitations (
|
||
order_id,
|
||
order_group_id,
|
||
token_hash,
|
||
state,
|
||
order_number,
|
||
customer_name,
|
||
customer_phone,
|
||
available_slots,
|
||
expires_at,
|
||
sent_at
|
||
)
|
||
values (
|
||
null,
|
||
new.id,
|
||
v_token_hash,
|
||
'awaiting_choice',
|
||
v_order_number,
|
||
v_customer_name,
|
||
v_customer_phone,
|
||
public.build_order_group_default_available_slots(),
|
||
timezone('utc', now()) + interval '7 days',
|
||
null
|
||
)
|
||
returning id into v_invitation_id;
|
||
|
||
update public.order_groups
|
||
set
|
||
delivery_invitation_id = v_invitation_id,
|
||
delivery_link = v_delivery_link,
|
||
notification_status = 'link_ready',
|
||
last_sms_error = null,
|
||
next_notification_check_at = public.next_order_group_sms_check_at(),
|
||
updated_at = timezone('utc', now())
|
||
where id = new.id;
|
||
|
||
return new;
|
||
end;
|
||
$$;
|
||
|
||
drop trigger if exists order_groups_ensure_delivery_link on public.order_groups;
|
||
create trigger order_groups_ensure_delivery_link
|
||
after insert or update of status, delivery_status, notification_status, delivery_link, delivery_invitation_id
|
||
on public.order_groups
|
||
for each row
|
||
execute function public.ensure_order_group_delivery_link();
|
||
|
||
-- Backfill links for already imported groups that are still waiting for SMS.
|
||
update public.order_groups
|
||
set
|
||
notification_status = notification_status,
|
||
updated_at = timezone('utc', now())
|
||
where status = 'ready_for_notification'
|
||
and delivery_status = 'pending_confirmation'
|
||
and notification_status in ('not_started', 'send_failed')
|
||
and delivery_link is null
|
||
and delivery_invitation_id is null;
|