From 586727287381e38ccef74486ce8590293ba8d0a5 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 16 Jun 2026 06:53:12 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20pickup=5Fcode=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=81=D0=B0=D0=BC=D0=BE=D0=B2=D1=8B=D0=B2=D0=BE=D0=B7=D0=B0=20?= =?UTF-8?q?+=20=D1=83=D0=BF=D1=80=D0=BE=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D1=82=D1=83=D1=81=D0=B0=20=D0=BE=D1=82=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B7=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pickup_code: автогенерация 6-значного кода при выборе самовывоза - Колонка pickup_code в order_groups + SELECT - Отображение кода выдачи в карточках (логист, клиент) - Упрощение: любая отгрузка → delivered (проблемы в has_delivery_problem) - RPC: добавлены deliveryType, pickupCode, pickupDate, pickupTimeSlot --- src/components/orders/OrderDetailPanel.jsx | 17 +++++----- src/pages/ClientDeliveryPage.jsx | 7 +++++ src/services/supabase/orderGroupRepository.js | 31 ++++++++++--------- .../confirm-delivery-choice/index.ts | 11 +++++++ .../index.ts | 7 ++++- 5 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/components/orders/OrderDetailPanel.jsx b/src/components/orders/OrderDetailPanel.jsx index 4aacb0b..bb2f6b2 100644 --- a/src/components/orders/OrderDetailPanel.jsx +++ b/src/components/orders/OrderDetailPanel.jsx @@ -863,6 +863,12 @@ export const OrderDetailPanel = ({

{isPickupOrder ? "Статус самовывоза" : "Статус доставки"}

{getOrderGroupDeliveryStatusLabel(order.deliveryStatus || order.delivery_status)}

+ {(order.pickupCode || order.pickup_code) ? ( +
+

Код выдачи

+

{order.pickupCode || order.pickup_code}

+
+ ) : null} @@ -1087,14 +1093,11 @@ export const OrderDetailPanel = ({ return statusOptions.map((statusOption) => { const isSelected = pendingStatus?.value === statusOption.value; - const isDeliveredBtn = statusOption.value === "delivered"; - const deliveryBlocked = isDeliveredBtn && shipmentState && !shipmentState.canMarkDelivered; return ( ); }); @@ -1120,7 +1118,6 @@ export const OrderDetailPanel = ({ variant="primary" disabled={isSavingStatusChange} onClick={() => { - if (pendingStatus.value === "delivered" && shipmentState && !shipmentState.canMarkDelivered) return; onChangeDeliveryStatus({ orderGroupId: order.id, status: pendingStatus.value, diff --git a/src/pages/ClientDeliveryPage.jsx b/src/pages/ClientDeliveryPage.jsx index 8816c41..8974e4e 100644 --- a/src/pages/ClientDeliveryPage.jsx +++ b/src/pages/ClientDeliveryPage.jsx @@ -378,6 +378,13 @@ export const ClientDeliveryPage = () => {

Статус: {invitation?.deliveryType === "pickup" ? "самовывоз" : "доставка"} уже согласован. При повторном открытии этой ссылки будет показан тот же выбор.

+ {(invitation?.pickupCode || invitation?.pickup_code) ? ( +
+

Код выдачи

+

{invitation.pickupCode || invitation.pickup_code}

+

Покажите этот код при получении заказа

+
+ ) : null} ) : null} diff --git a/src/services/supabase/orderGroupRepository.js b/src/services/supabase/orderGroupRepository.js index bf5f36e..c929fb7 100644 --- a/src/services/supabase/orderGroupRepository.js +++ b/src/services/supabase/orderGroupRepository.js @@ -223,6 +223,7 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => { driverShipmentData: row.driver_shipment_data || null, hasDeliveryProblem: row.has_delivery_problem || false, deliveryProblemNote: row.delivery_problem_note || null, + pickupCode: row.pickup_code || null, syncedTo1cAt: row.synced_to_1c_at || null, deliveryHalfDay: getOrderGroupDeliveryHalfDay({ deliveryHalfDay: rawDeliveryHalfDay, @@ -264,7 +265,7 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => { }; }; -const ORDER_GROUP_SELECT_FIELDS = `id, group_key, order_numbers, status, delivery_status, sms_sent_at, created_at, updated_at, created_from_exchange_at, source_key, customer_name, customer_phone, customer_phone_normalized, customer_date, orders_total, orders_ready, orders_not_ready, source_orders, order_list, order_list_structured, delivery_invitation_id, delivery_link, notification_status, sms_attempts, first_sms_sent_at, second_sms_sent_at, last_sms_error, next_notification_check_at, delivery_date, delivery_time, delivery_address, customer_address, delivery_date_source, manual_confirmation_at, paid_storage_at, assigned_driver_id, assigned_driver:users!order_groups_assigned_driver_id_fkey(id, name), driver_shipment_data, delivery_type, pickup_date, pickup_time_slot, has_delivery_problem, delivery_problem_note, synced_to_1c_at`; +const ORDER_GROUP_SELECT_FIELDS = `id, group_key, order_numbers, status, delivery_status, sms_sent_at, created_at, updated_at, created_from_exchange_at, source_key, customer_name, customer_phone, customer_phone_normalized, customer_date, orders_total, orders_ready, orders_not_ready, source_orders, order_list, order_list_structured, delivery_invitation_id, delivery_link, notification_status, sms_attempts, first_sms_sent_at, second_sms_sent_at, last_sms_error, next_notification_check_at, delivery_date, delivery_time, delivery_address, customer_address, delivery_date_source, manual_confirmation_at, paid_storage_at, assigned_driver_id, assigned_driver:users!order_groups_assigned_driver_id_fkey(id, name), driver_shipment_data, delivery_type, pickup_date, pickup_time_slot, has_delivery_problem, delivery_problem_note, pickup_code, synced_to_1c_at`; export const updateOrderGroupDeliveryChoice = async ({ orderGroupId, @@ -292,6 +293,16 @@ export const updateOrderGroupDeliveryChoice = async ({ updatePayload.pickup_time_slot = pickupTimeSlot || null; // Pickup orders don't need a driver — clear assignment updatePayload.assigned_driver_id = null; + // Generate pickup code if not already set + const { data: existing } = await client + .from("order_groups") + .select("pickup_code") + .eq("id", orderGroupId) + .single(); + if (!existing?.pickup_code) { + const code = Math.random().toString(36).substring(2, 8).toUpperCase(); + updatePayload.pickup_code = code; + } } else { updatePayload.pickup_date = null; updatePayload.pickup_time_slot = null; @@ -393,22 +404,14 @@ export const assignDriverToOrderGroup = async ({ export const saveShipmentData = async ({ orderGroupId, shipmentData }) => { return safeSupabaseCall(async () => { const client = requireSupabase(); - const unshipped = shipmentData.filter((i) => !i.shipped); - const allShipped = unshipped.length === 0 && shipmentData.length > 0; - const unshippedWithoutComment = unshipped.filter((i) => !i.comment?.trim()).length; - const canMarkDelivered = allShipped || unshippedWithoutComment === 0; - const hasProblem = unshipped.length > 0; + const hasAnyShipped = shipmentData.some((i) => i.shipped); + const hasProblem = shipmentData.some((i) => !i.shipped); const problemNote = hasProblem - ? unshipped.map((i) => `${i.name}${i.quantity ? ` (${i.quantity}${i.unit ? ` ${i.unit}` : ""})` : ""}${i.comment ? ` — ${i.comment}` : ""}`).join("; ") + ? shipmentData.filter((i) => !i.shipped).map((i) => `${i.name}${i.quantity ? ` (${i.quantity}${i.unit ? ` ${i.unit}` : ""})` : ""}${i.comment ? ` — ${i.comment}` : ""}`).join("; ") : null; - // Auto-update delivery status based on shipment - let newDeliveryStatus = undefined; - if (canMarkDelivered) { - newDeliveryStatus = "delivered"; - } else if (hasProblem) { - newDeliveryStatus = "problem"; - } + // Any shipment data saved = delivered status. Problems go into has_delivery_problem/delivery_problem_note columns. + const newDeliveryStatus = hasAnyShipped ? "delivered" : undefined; const updatePayload = { driver_shipment_data: shipmentData, diff --git a/supabase/functions/confirm-delivery-choice/index.ts b/supabase/functions/confirm-delivery-choice/index.ts index a9d1945..9433766 100644 --- a/supabase/functions/confirm-delivery-choice/index.ts +++ b/supabase/functions/confirm-delivery-choice/index.ts @@ -210,6 +210,16 @@ Deno.serve(async (request) => { groupUpdateData.pickup_time_slot = body.pickupTimeSlot || requestedSlot.deliveryTime || null; // Pickup orders don't need a driver — clear assignment groupUpdateData.assigned_driver_id = null; + // Generate pickup code if not already set + const { data: existingGroup } = await supabase + .from("order_groups") + .select("pickup_code") + .eq("id", invitation.order_group_id) + .single(); + if (!existingGroup?.pickup_code) { + const code = Math.random().toString(36).substring(2, 8).toUpperCase(); + groupUpdateData.pickup_code = code; + } } const { error: groupUpdateError } = await supabase @@ -258,6 +268,7 @@ Deno.serve(async (request) => { ok: true, orderGroupId: invitation.order_group_id, deliveryStatus: effectiveDeliveryStatus, + pickupCode: groupUpdateData.pickup_code || null, }, 200, corsHeaders, diff --git a/supabase/functions/update-order-group-delivery-choice/index.ts b/supabase/functions/update-order-group-delivery-choice/index.ts index 4ace189..5671f38 100644 --- a/supabase/functions/update-order-group-delivery-choice/index.ts +++ b/supabase/functions/update-order-group-delivery-choice/index.ts @@ -152,7 +152,7 @@ Deno.serve(async (request) => { const { data: currentGroup, error: currentGroupError } = await supabase .from("order_groups") - .select("id, delivery_status, delivery_invitation_id") + .select("id, delivery_status, delivery_invitation_id, pickup_code") .eq("id", orderGroupId) .single(); @@ -175,6 +175,11 @@ Deno.serve(async (request) => { updateData.pickup_date = body.pickupDate || deliveryDate || null; updateData.pickup_time_slot = body.pickupTimeSlot || deliveryTime || null; updateData.assigned_driver_id = null; + // Generate pickup code if not already set + if (!currentGroup.pickup_code) { + const code = Math.random().toString(36).substring(2, 8).toUpperCase(); + updateData.pickup_code = code; + } } else { updateData.pickup_date = null; updateData.pickup_time_slot = null;