fix: аудит доставки — критические и средние баги

Edge function confirm-delivery-choice:
- currentGroup использовался до объявления — перемещён вверх
- pickup не очищал assigned_driver_id — добавлена очистка
- выборка currentGroup теперь включает delivery_address, customer_address

Edge function update-order-group-delivery-choice:
- добавлена поддержка deliveryType=pickup (ранее всегда ставил 'agreed')
- pickup очищает assigned_driver_id, устанавливает pickup_date/time_slot
- дата доставки не обязательна для pickup

Фронтенд:
- assignDriverToOrderGroup: null → 'agreed' (NOT NULL constraint)
- isDeliveryAgreed включает статус 'pickup'
- orderGroupViews: warning-тон для групп с проблемами отгрузки
- OrdersTable: колонка Отгрузка с визуализацией проблем
This commit is contained in:
root 2026-06-15 15:38:42 +00:00
parent 3847625005
commit 41272bdbbf
4 changed files with 44 additions and 22 deletions

View File

@ -635,7 +635,7 @@ export const OrderDetailPanel = ({
);
}
const isDeliveryAgreed = ["agreed", "driver_assigned", "loaded", "on_route", "delivered", "picked_up"].includes(order.deliveryStatus || order.delivery_status);
const isDeliveryAgreed = ["agreed", "driver_assigned", "loaded", "on_route", "delivered", "picked_up", "pickup"].includes(order.deliveryStatus || order.delivery_status);
const isPickupOrder = order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup";
// Show "agreed" banner only when selected tab matches the already-agreed type
const agreedTypeMatchesTab = isDeliveryAgreed && !isEditingDate && (

View File

@ -350,7 +350,7 @@ export const assignDriverToOrderGroup = async ({
const ADVANCED_STATUSES = ["loaded", "on_route", "delivered", "picked_up"];
const newStatus = driverId
? (ADVANCED_STATUSES.includes(currentStatus) ? undefined : "driver_assigned")
: null; // removing driver → reset to pending
: "agreed"; // removing driver → revert to agreed (NOT null — would violate NOT NULL constraint)
const updates = {
assigned_driver_id: driverId || null,
...(newStatus !== undefined && { delivery_status: newStatus }),

View File

@ -138,6 +138,17 @@ Deno.serve(async (request) => {
const deliveryType = body.deliveryType || "delivery";
if (invitation.order_group_id) {
const { data: currentGroup, error: groupError } = await supabase
.from("order_groups")
.select("id, delivery_status, delivery_address, customer_address")
.eq("id", invitation.order_group_id)
.single();
if (groupError) {
throw groupError;
}
// When user switches from pickup to delivery but has no address → requires_address
const hasAddress = invitation.delivery_address?.trim() || currentGroup?.delivery_address?.trim() || currentGroup?.customer_address?.trim();
const effectiveDeliveryStatus = deliveryType === "pickup"
@ -146,17 +157,6 @@ Deno.serve(async (request) => {
? "agreed"
: "requires_address";
if (invitation.order_group_id) {
const { data: currentGroup, error: groupError } = await supabase
.from("order_groups")
.select("id, delivery_status")
.eq("id", invitation.order_group_id)
.single();
if (groupError) {
throw groupError;
}
if (!isActiveInvitationState(invitation.state) || currentGroup.delivery_status !== "pending_confirmation") {
return jsonResponse(
{
@ -208,6 +208,8 @@ Deno.serve(async (request) => {
if (deliveryType === "pickup") {
groupUpdateData.pickup_date = body.pickupDate || requestedSlot.deliveryDate || null;
groupUpdateData.pickup_time_slot = body.pickupTimeSlot || requestedSlot.deliveryTime || null;
// Pickup orders don't need a driver — clear assignment
groupUpdateData.assigned_driver_id = null;
}
const { error: groupUpdateError } = await supabase

View File

@ -23,6 +23,9 @@ type UpdateDeliveryChoiceBody = {
orderGroupId?: string;
deliveryDate?: string;
deliveryTime?: string;
deliveryType?: string;
pickupDate?: string;
pickupTimeSlot?: string;
};
const isValidDate = (value: string) => /^\d{4}-\d{2}-\d{2}$/.test(value);
@ -111,12 +114,14 @@ Deno.serve(async (request) => {
const orderGroupId = String(body.orderGroupId || "").trim();
const deliveryDate = String(body.deliveryDate || "").trim();
const deliveryTime = normalizeDeliveryTime(String(body.deliveryTime || "").trim());
const deliveryType = String(body.deliveryType || "delivery").trim();
if (!orderGroupId) {
return jsonResponse({ ok: false, error: "orderGroupId is required" }, 400, corsHeaders);
}
if (!isAllowedDeliveryDate(deliveryDate)) {
// Pickup orders don't require a delivery date — allow skipping date validation
if (deliveryType !== "pickup" && !isAllowedDeliveryDate(deliveryDate)) {
return jsonResponse({ ok: false, error: "Выберите будущий будний день доставки" }, 400, corsHeaders);
}
@ -155,15 +160,29 @@ Deno.serve(async (request) => {
throw currentGroupError;
}
const effectiveDeliveryStatus = deliveryType === "pickup" ? "pickup" : "agreed";
const updateData: Record<string, unknown> = {
delivery_status: effectiveDeliveryStatus,
delivery_date: deliveryDate || null,
delivery_time: deliveryTime || null,
delivery_type: deliveryType,
notification_status: "confirmed",
updated_at: new Date().toISOString(),
};
if (deliveryType === "pickup") {
updateData.pickup_date = body.pickupDate || deliveryDate || null;
updateData.pickup_time_slot = body.pickupTimeSlot || deliveryTime || null;
updateData.assigned_driver_id = null;
} else {
updateData.pickup_date = null;
updateData.pickup_time_slot = null;
}
const { data: group, error: groupUpdateError } = await supabase
.from("order_groups")
.update({
delivery_status: "agreed",
delivery_date: deliveryDate,
delivery_time: deliveryTime,
notification_status: "confirmed",
updated_at: new Date().toISOString(),
})
.update(updateData)
.eq("id", orderGroupId)
.select("*")
.single();
@ -198,7 +217,8 @@ Deno.serve(async (request) => {
actor_user_id: actor.userId,
actor_role: actor.role,
old_delivery_status: currentGroup.delivery_status || null,
new_delivery_status: "agreed",
new_delivery_status: effectiveDeliveryStatus,
delivery_type: deliveryType,
delivery_date: deliveryDate,
delivery_time: deliveryTime,
},