supersam/supabase/functions/confirm-delivery-choice/index.ts

198 lines
5.3 KiB
TypeScript

import {
getOrderUpdateForDeliveryInvitationAction,
hashInvitationToken,
isActiveInvitationState,
} from "../_shared/delivery-invitations.ts";
import { createServiceClient } from "../_shared/chatbot.ts";
import { insertIntegrationEvent } from "../_shared/integration-events.ts";
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
Deno.serve(async (request) => {
if (request.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
if (request.method !== "POST") {
return new Response(JSON.stringify({ error: "Method not allowed" }), {
status: 405,
headers: {
...corsHeaders,
"Content-Type": "application/json",
},
});
}
try {
const body = (await request.json()) as {
token?: string;
deliveryDate?: string;
deliveryTime?: string;
};
if (!body.token) {
return new Response(JSON.stringify({ error: "token is required" }), {
status: 400,
headers: {
...corsHeaders,
"Content-Type": "application/json",
},
});
}
const tokenHash = await hashInvitationToken(body.token);
const supabase = createServiceClient();
const { data: invitation, error: invitationError } = await supabase
.from("delivery_invitations")
.select("*")
.eq("token_hash", tokenHash)
.single();
if (invitationError) {
if (invitationError.code === "PGRST116") {
return new Response(
JSON.stringify({ ok: false, error: "Invitation not found" }),
{
status: 404,
headers: {
...corsHeaders,
"Content-Type": "application/json",
},
},
);
}
throw invitationError;
}
const { data: currentOrder, error: orderError } = await supabase
.from("orders")
.select("id, status, delivery_agreement_status")
.eq("id", invitation.order_id)
.single();
if (orderError) {
throw orderError;
}
if (!isActiveInvitationState(invitation.state) || !["Ожидает ответа клиента", "Ожидает согласования доставки"].includes(currentOrder.status)) {
return new Response(
JSON.stringify({
ok: false,
error: "Invitation is no longer active",
}),
{
status: 409,
headers: {
...corsHeaders,
"Content-Type": "application/json",
},
},
);
}
const orderUpdate = getOrderUpdateForDeliveryInvitationAction("confirm_delivery_choice");
const deliveryDate = body.deliveryDate || new Date().toISOString().slice(0, 10);
const deliveryTime = body.deliveryTime || "Первая половина дня";
const { error: invitationUpdateError } = await supabase
.from("delivery_invitations")
.update({
state: "agreed",
delivery_date: deliveryDate,
delivery_time: deliveryTime,
confirmed_at: new Date().toISOString(),
})
.eq("id", invitation.id);
if (invitationUpdateError) {
throw invitationUpdateError;
}
const { error: orderUpdateError } = await supabase
.from("orders")
.update({
status: orderUpdate?.status,
delivery_agreement_status: orderUpdate?.deliveryAgreementStatus,
})
.eq("id", invitation.order_id);
if (orderUpdateError) {
throw orderUpdateError;
}
const { error: slotError } = await supabase.from("delivery_slots").insert({
order_id: invitation.order_id,
delivery_date: deliveryDate,
delivery_time: deliveryTime,
logistician_id: null,
status: "confirmed_by_client",
});
if (slotError) {
throw slotError;
}
const { error: historyError } = await supabase.from("order_history").insert({
order_id: invitation.order_id,
action: "Подтверждение выбора доставки клиентом",
old_status: currentOrder.status,
new_status: orderUpdate?.status,
metadata: {
old_delivery_agreement_status: currentOrder.delivery_agreement_status,
new_delivery_agreement_status: orderUpdate?.deliveryAgreementStatus,
delivery_date: deliveryDate,
delivery_time: deliveryTime,
},
});
if (historyError) {
throw historyError;
}
await insertIntegrationEvent(supabase, {
order_id: invitation.order_id,
event_type: "delivery_choice_confirmed",
direction: "inbound",
status: "success",
payload: {
delivery_date: deliveryDate,
delivery_time: deliveryTime,
},
});
return new Response(
JSON.stringify({
ok: true,
orderId: invitation.order_id,
status: orderUpdate?.status,
deliveryAgreementStatus: orderUpdate?.deliveryAgreementStatus,
}),
{
headers: {
...corsHeaders,
"Content-Type": "application/json",
},
},
);
} catch (error) {
return new Response(
JSON.stringify({
ok: false,
error: error instanceof Error ? error.message : "Unexpected error",
}),
{
status: 500,
headers: {
...corsHeaders,
"Content-Type": "application/json",
},
},
);
}
});