supersam/docs/sql/public-delivery-choice-rpc.sql

389 lines
11 KiB
PL/PgSQL

create extension if not exists pgcrypto;
create or replace function public.get_delivery_invitation_by_token(p_token text)
returns jsonb
language plpgsql
security definer
set search_path = public, extensions
as $$
declare
v_invitation public.delivery_invitations%rowtype;
v_group public.order_groups%rowtype;
v_order record;
v_token_hash text;
v_state text;
v_order_number text;
v_customer_name text;
v_customer_phone text;
v_order_items jsonb;
v_order_numbers jsonb;
v_now timestamptz := timezone('utc', now());
begin
if nullif(trim(coalesce(p_token, '')), '') is null then
raise exception 'token is required';
end if;
v_token_hash := encode(digest(p_token, 'sha256'), 'hex');
select *
into v_invitation
from public.delivery_invitations
where token_hash = v_token_hash;
if not found then
raise exception 'Invitation not found';
end if;
if v_invitation.revoked_at is not null then
raise exception 'Invitation expired';
end if;
if v_invitation.expires_at is not null and v_invitation.expires_at <= v_now then
raise exception 'Invitation expired';
end if;
if v_invitation.order_group_id is not null then
select *
into v_group
from public.order_groups
where id = v_invitation.order_group_id;
if not found then
raise exception 'Order group not found';
end if;
v_state := case
when v_group.delivery_status = 'agreed' then 'agreed'
when v_group.delivery_status = 'delivered' then 'delivered'
when v_invitation.state in ('awaiting_choice', 'opened', 'reminder_sent') then v_invitation.state
else 'default'
end;
update public.delivery_invitations
set
opened_at = case
when v_state in ('awaiting_choice', 'opened', 'reminder_sent') and opened_at is null then v_now
else opened_at
end,
access_count = coalesce(access_count, 0) + 1,
last_accessed_at = v_now
where id = v_invitation.id
returning * into v_invitation;
v_order_number := coalesce(
nullif(v_invitation.order_number, ''),
to_jsonb(v_group.order_numbers) ->> 0,
nullif(v_group.group_key, '')
);
v_customer_name := coalesce(
nullif(v_group.customer_name, ''),
nullif(v_invitation.customer_name, '')
);
v_customer_phone := coalesce(
nullif(v_group.customer_phone, ''),
nullif(v_group.customer_phone_normalized, ''),
nullif(v_invitation.customer_phone, '')
);
select coalesce(
jsonb_agg(jsonb_build_object('name', order_number, 'quantity', '')),
'[]'::jsonb
)
into v_order_items
from jsonb_array_elements_text(
case
when jsonb_typeof(to_jsonb(v_group.order_numbers)) = 'array' then to_jsonb(v_group.order_numbers)
else '[]'::jsonb
end
) as order_number;
return jsonb_build_object(
'ok', true,
'invitation', jsonb_build_object(
'orderId', coalesce(v_invitation.order_group_id, v_group.id)::text,
'orderGroupId', coalesce(v_invitation.order_group_id, v_group.id)::text,
'state', v_state,
'token', p_token,
'orderNumber', v_order_number,
'customerName', v_customer_name,
'customerPhone', v_customer_phone,
'orderItems', v_order_items,
'availableSlots', coalesce(to_jsonb(v_invitation.available_slots), '[]'::jsonb),
'deliveryDate', v_invitation.delivery_date,
'deliveryTime', v_invitation.delivery_time,
'orderStatus', null,
'deliveryAgreementStatus', null
)
);
end if;
select id, order_number, status, delivery_agreement_status, customer
into v_order
from public.orders
where id = v_invitation.order_id;
if not found then
raise exception 'Order not found';
end if;
v_state := case v_order.status
when 'Ожидает ответа клиента' then 'awaiting_choice'
when 'Ожидает согласования доставки' then 'opened'
when 'Напоминание отправлено' then 'reminder_sent'
when 'Переход отправлен' then 'reminder_sent'
when 'Передан логисту' then 'transferred_to_logistics'
when 'Платное хранение' then 'paid_storage'
when 'Доставлен' then 'delivered'
when 'Доставка согласована' then 'agreed'
else 'default'
end;
update public.delivery_invitations
set
opened_at = case
when v_state in ('awaiting_choice', 'opened', 'reminder_sent') and opened_at is null then v_now
else opened_at
end,
access_count = coalesce(access_count, 0) + 1,
last_accessed_at = v_now
where id = v_invitation.id
returning * into v_invitation;
v_order_items := case
when jsonb_typeof(v_order.customer -> 'items') = 'array' then v_order.customer -> 'items'
else '[]'::jsonb
end;
return jsonb_build_object(
'ok', true,
'invitation', jsonb_build_object(
'orderId', v_invitation.order_id::text,
'state', v_state,
'token', p_token,
'orderNumber', coalesce(nullif(v_order.order_number, ''), nullif(v_invitation.order_number, '')),
'customerName', coalesce(nullif(v_order.customer ->> 'name', ''), nullif(v_invitation.customer_name, '')),
'customerPhone', coalesce(nullif(v_order.customer ->> 'phone', ''), nullif(v_invitation.customer_phone, '')),
'orderItems', v_order_items,
'availableSlots', coalesce(to_jsonb(v_invitation.available_slots), '[]'::jsonb),
'deliveryDate', v_invitation.delivery_date,
'deliveryTime', v_invitation.delivery_time,
'orderStatus', v_order.status,
'deliveryAgreementStatus', v_order.delivery_agreement_status
)
);
end;
$$;
create or replace function public.confirm_delivery_choice_by_token(
p_token text,
p_delivery_date date,
p_delivery_time text
)
returns jsonb
language plpgsql
security definer
set search_path = public, extensions
as $$
declare
v_invitation public.delivery_invitations%rowtype;
v_group public.order_groups%rowtype;
v_order record;
v_token_hash text;
v_slot_label text;
v_now timestamptz := timezone('utc', now());
begin
if nullif(trim(coalesce(p_token, '')), '') is null then
raise exception 'token is required';
end if;
if p_delivery_date is null or nullif(trim(coalesce(p_delivery_time, '')), '') is null then
raise exception 'Selected slot is not available';
end if;
v_token_hash := encode(digest(p_token, 'sha256'), 'hex');
v_slot_label := concat(p_delivery_date::text, ', ', trim(p_delivery_time));
select *
into v_invitation
from public.delivery_invitations
where token_hash = v_token_hash
for update;
if not found then
raise exception 'Invitation not found';
end if;
if v_invitation.revoked_at is not null then
raise exception 'Invitation expired';
end if;
if v_invitation.expires_at is not null and v_invitation.expires_at <= v_now then
raise exception 'Invitation expired';
end if;
if v_invitation.state not in ('awaiting_choice', 'opened', 'reminder_sent') then
raise exception 'Invitation is no longer active';
end if;
if cardinality(v_invitation.available_slots) > 0 and not (v_slot_label = any(v_invitation.available_slots)) then
raise exception 'Selected slot is not available';
end if;
if v_invitation.order_group_id is not null then
select *
into v_group
from public.order_groups
where id = v_invitation.order_group_id
for update;
if not found then
raise exception 'Order group not found';
end if;
if v_group.delivery_status <> 'pending_confirmation' then
raise exception 'Invitation is no longer active';
end if;
update public.delivery_invitations
set
state = 'agreed',
delivery_date = p_delivery_date,
delivery_time = trim(p_delivery_time),
confirmed_at = v_now,
access_count = coalesce(access_count, 0) + 1,
last_accessed_at = v_now
where id = v_invitation.id;
update public.order_groups
set
delivery_status = 'agreed',
delivery_date = p_delivery_date,
delivery_time = trim(p_delivery_time),
notification_status = 'confirmed',
updated_at = v_now
where id = v_group.id;
insert into public.integration_events (
order_id,
event_type,
direction,
status,
payload
)
values (
null,
'delivery_choice_confirmed',
'inbound',
'success',
jsonb_build_object(
'order_group_id', v_group.id,
'delivery_invitation_id', v_invitation.id,
'delivery_date', p_delivery_date,
'delivery_time', trim(p_delivery_time)
)
);
return jsonb_build_object(
'ok', true,
'orderGroupId', v_group.id,
'deliveryStatus', 'agreed'
);
end if;
select id, status, delivery_agreement_status
into v_order
from public.orders
where id = v_invitation.order_id
for update;
if not found then
raise exception 'Order not found';
end if;
if v_order.status not in ('Ожидает ответа клиента', 'Ожидает согласования доставки') then
raise exception 'Invitation is no longer active';
end if;
update public.delivery_invitations
set
state = 'agreed',
delivery_date = p_delivery_date,
delivery_time = trim(p_delivery_time),
confirmed_at = v_now,
access_count = coalesce(access_count, 0) + 1,
last_accessed_at = v_now
where id = v_invitation.id;
update public.orders
set
status = 'Доставка согласована',
delivery_agreement_status = 'Подтверждено клиентом'
where id = v_order.id;
insert into public.delivery_slots (
order_id,
delivery_date,
delivery_time,
logistician_id,
status
)
values (
v_order.id,
p_delivery_date,
trim(p_delivery_time),
null,
'confirmed_by_client'
);
insert into public.order_history (
order_id,
action,
old_status,
new_status,
metadata
)
values (
v_order.id,
'Подтверждение выбора доставки клиентом',
v_order.status,
'Доставка согласована',
jsonb_build_object(
'old_delivery_agreement_status', v_order.delivery_agreement_status,
'new_delivery_agreement_status', 'Подтверждено клиентом',
'delivery_date', p_delivery_date,
'delivery_time', trim(p_delivery_time)
)
);
insert into public.integration_events (
order_id,
event_type,
direction,
status,
payload
)
values (
v_order.id,
'delivery_choice_confirmed',
'inbound',
'success',
jsonb_build_object(
'delivery_date', p_delivery_date,
'delivery_time', trim(p_delivery_time)
)
);
return jsonb_build_object(
'ok', true,
'orderId', v_order.id,
'status', 'Доставка согласована',
'deliveryAgreementStatus', 'Подтверждено клиентом'
);
end;
$$;
revoke all on function public.get_delivery_invitation_by_token(text) from public;
grant execute on function public.get_delivery_invitation_by_token(text) to anon, authenticated;
revoke all on function public.confirm_delivery_choice_by_token(text, date, text) from public;
grant execute on function public.confirm_delivery_choice_by_token(text, date, text) to anon, authenticated;