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:
parent
3847625005
commit
41272bdbbf
|
|
@ -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";
|
const isPickupOrder = order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup";
|
||||||
// Show "agreed" banner only when selected tab matches the already-agreed type
|
// Show "agreed" banner only when selected tab matches the already-agreed type
|
||||||
const agreedTypeMatchesTab = isDeliveryAgreed && !isEditingDate && (
|
const agreedTypeMatchesTab = isDeliveryAgreed && !isEditingDate && (
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,7 @@ export const assignDriverToOrderGroup = async ({
|
||||||
const ADVANCED_STATUSES = ["loaded", "on_route", "delivered", "picked_up"];
|
const ADVANCED_STATUSES = ["loaded", "on_route", "delivered", "picked_up"];
|
||||||
const newStatus = driverId
|
const newStatus = driverId
|
||||||
? (ADVANCED_STATUSES.includes(currentStatus) ? undefined : "driver_assigned")
|
? (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 = {
|
const updates = {
|
||||||
assigned_driver_id: driverId || null,
|
assigned_driver_id: driverId || null,
|
||||||
...(newStatus !== undefined && { delivery_status: newStatus }),
|
...(newStatus !== undefined && { delivery_status: newStatus }),
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,17 @@ Deno.serve(async (request) => {
|
||||||
|
|
||||||
const deliveryType = body.deliveryType || "delivery";
|
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
|
// 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 hasAddress = invitation.delivery_address?.trim() || currentGroup?.delivery_address?.trim() || currentGroup?.customer_address?.trim();
|
||||||
const effectiveDeliveryStatus = deliveryType === "pickup"
|
const effectiveDeliveryStatus = deliveryType === "pickup"
|
||||||
|
|
@ -146,17 +157,6 @@ Deno.serve(async (request) => {
|
||||||
? "agreed"
|
? "agreed"
|
||||||
: "requires_address";
|
: "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") {
|
if (!isActiveInvitationState(invitation.state) || currentGroup.delivery_status !== "pending_confirmation") {
|
||||||
return jsonResponse(
|
return jsonResponse(
|
||||||
{
|
{
|
||||||
|
|
@ -208,6 +208,8 @@ Deno.serve(async (request) => {
|
||||||
if (deliveryType === "pickup") {
|
if (deliveryType === "pickup") {
|
||||||
groupUpdateData.pickup_date = body.pickupDate || requestedSlot.deliveryDate || null;
|
groupUpdateData.pickup_date = body.pickupDate || requestedSlot.deliveryDate || null;
|
||||||
groupUpdateData.pickup_time_slot = body.pickupTimeSlot || requestedSlot.deliveryTime || 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
|
const { error: groupUpdateError } = await supabase
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ type UpdateDeliveryChoiceBody = {
|
||||||
orderGroupId?: string;
|
orderGroupId?: string;
|
||||||
deliveryDate?: string;
|
deliveryDate?: string;
|
||||||
deliveryTime?: string;
|
deliveryTime?: string;
|
||||||
|
deliveryType?: string;
|
||||||
|
pickupDate?: string;
|
||||||
|
pickupTimeSlot?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValidDate = (value: string) => /^\d{4}-\d{2}-\d{2}$/.test(value);
|
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 orderGroupId = String(body.orderGroupId || "").trim();
|
||||||
const deliveryDate = String(body.deliveryDate || "").trim();
|
const deliveryDate = String(body.deliveryDate || "").trim();
|
||||||
const deliveryTime = normalizeDeliveryTime(String(body.deliveryTime || "").trim());
|
const deliveryTime = normalizeDeliveryTime(String(body.deliveryTime || "").trim());
|
||||||
|
const deliveryType = String(body.deliveryType || "delivery").trim();
|
||||||
|
|
||||||
if (!orderGroupId) {
|
if (!orderGroupId) {
|
||||||
return jsonResponse({ ok: false, error: "orderGroupId is required" }, 400, corsHeaders);
|
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);
|
return jsonResponse({ ok: false, error: "Выберите будущий будний день доставки" }, 400, corsHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,15 +160,29 @@ Deno.serve(async (request) => {
|
||||||
throw currentGroupError;
|
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
|
const { data: group, error: groupUpdateError } = await supabase
|
||||||
.from("order_groups")
|
.from("order_groups")
|
||||||
.update({
|
.update(updateData)
|
||||||
delivery_status: "agreed",
|
|
||||||
delivery_date: deliveryDate,
|
|
||||||
delivery_time: deliveryTime,
|
|
||||||
notification_status: "confirmed",
|
|
||||||
updated_at: new Date().toISOString(),
|
|
||||||
})
|
|
||||||
.eq("id", orderGroupId)
|
.eq("id", orderGroupId)
|
||||||
.select("*")
|
.select("*")
|
||||||
.single();
|
.single();
|
||||||
|
|
@ -198,7 +217,8 @@ Deno.serve(async (request) => {
|
||||||
actor_user_id: actor.userId,
|
actor_user_id: actor.userId,
|
||||||
actor_role: actor.role,
|
actor_role: actor.role,
|
||||||
old_delivery_status: currentGroup.delivery_status || null,
|
old_delivery_status: currentGroup.delivery_status || null,
|
||||||
new_delivery_status: "agreed",
|
new_delivery_status: effectiveDeliveryStatus,
|
||||||
|
delivery_type: deliveryType,
|
||||||
delivery_date: deliveryDate,
|
delivery_date: deliveryDate,
|
||||||
delivery_time: deliveryTime,
|
delivery_time: deliveryTime,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue