diff --git a/src/components/orders/OrderDetailPanel.jsx b/src/components/orders/OrderDetailPanel.jsx
index c966f75..62b56a8 100644
--- a/src/components/orders/OrderDetailPanel.jsx
+++ b/src/components/orders/OrderDetailPanel.jsx
@@ -50,11 +50,13 @@ import {
getOrderGroupDeliveryStatusLabel,
getOrderGroupDisplayStatusLabel,
getOrderGroupStatusTone,
+ DELIVERY_GROUP_STATUS_LABELS,
} from "../../services/orderGroupViews";
+import { getErrorMessage, normalizeNom } from "../../utils/deliveryUtils";
const DELIVERY_TIME_OPTIONS = ["Первая половина дня", "Вторая половина дня"];
const WEEK_DAY_LABELS = ["ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС"];
-const STATUS_LABELS = { pending_confirmation: 'Ожидает согласования', agreed: 'Согласовано', driver_assigned: 'Назначен водитель', loaded: 'Загружено', on_route: 'В пути', delivered: 'Доставлено', pickup: 'Самовывоз', requires_address: 'Требуется адрес', problem: 'Проблема', cancelled: 'Отменено' };
+const STATUS_LABELS = DELIVERY_GROUP_STATUS_LABELS;
const ConfirmModal = ({ open, title, message, onConfirm, onCancel }) => {
if (!open) return null;
@@ -125,12 +127,6 @@ const renderList = (values) => {
const renderValue = (value) => value || "Нет данных";
-const normalizeNom = (nom) => {
- if (!nom) return '';
- // 1C escapes backslashes: "СФ Т\\ЕА-33584" → normalize for comparison
- return String(nom).replace(/\\\\/g, '\\').trim();
-};
-
const getAllBillNumbers = (order) => {
const orders = parseOrderList(order);
if (!orders.length) return order.orderNumbers || [];
@@ -194,22 +190,6 @@ const parseOrderList = (order) => {
return [];
};
-const getErrorMessage = (error, fallbackMessage) => {
- if (!error) {
- return fallbackMessage;
- }
-
- if (error instanceof Error) {
- return error.message || fallbackMessage;
- }
-
- if (typeof error === "string") {
- return error || fallbackMessage;
- }
-
- return error?.message || fallbackMessage;
-};
-
const normalizeDeliveryTimeChoice = (value) => {
const normalized = value ? String(value).trim() : "";
const deliveryTime = DELIVERY_TIME_ALIASES[normalized] || normalized;
@@ -604,6 +584,10 @@ export const OrderDetailPanel = ({
const [pickupTimeSlot, setPickupTimeSlot] = React.useState(DELIVERY_TIME_OPTIONS[0]);
const [deliveryAddress, setDeliveryAddress] = React.useState(order?.originalDeliveryAddress || order?.deliveryAddress || order?.customerAddress || "");
const [confirmAction, setConfirmAction] = React.useState(null);
+ const [isEditingDate, setIsEditingDate] = React.useState(false);
+ const handleShipmentChange = React.useCallback((state) => {
+ setShipmentState(state);
+ }, []);
const minSelectableDateKey = React.useMemo(() => getNextSelectableDateKey(), []);
const [currentMonth, setCurrentMonth] = React.useState(() => {
const existingDeliveryDate = fromDateKey(order?.deliveryDate);
@@ -655,7 +639,6 @@ export const OrderDetailPanel = ({
const isDeliveryAgreed = ["agreed", "driver_assigned", "loaded", "on_route", "delivered"].includes(order.deliveryStatus || order.delivery_status);
const isPickupOrder = order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup";
- const [isEditingDate, setIsEditingDate] = React.useState(false);
// Show "agreed" banner only when selected tab matches the already-agreed type
const agreedTypeMatchesTab = isDeliveryAgreed && !isEditingDate && (
(deliveryType === "pickup" && isPickupOrder)
@@ -667,10 +650,6 @@ export const OrderDetailPanel = ({
order.deliveryTime || order.deliveryHalfDay,
].filter((value) => value && value !== "Нет данных").join(" · ");
- const handleShipmentChange = React.useCallback((state) => {
- setShipmentState(state);
- }, []);
-
const handleSaveDeliveryChoice = async () => {
const effectiveDate = deliveryType === "pickup" ? pickupDate : deliveryDate;
const effectiveTime = deliveryType === "pickup" ? pickupTimeSlot : deliveryTime;
@@ -735,7 +714,7 @@ export const OrderDetailPanel = ({
- {(order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup") ? "Карточка группы самовывоза" : "Карточка группы доставки"}
+ {isPickupOrder ? "Карточка группы самовывоза" : "Карточка группы доставки"}
{order.displayTitle || order.customerName || order.groupKey}
@@ -754,7 +733,7 @@ export const OrderDetailPanel = ({
{(() => {
- const isPickup = order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup";
+ const isPickup = isPickupOrder;
const deliveryTypeLabel = isPickup
? "Самовывоз"
: (order.deliveryStatus === "requires_address" || order.delivery_status === "requires_address")
@@ -882,7 +861,7 @@ export const OrderDetailPanel = ({
{formatDateTime(order.updatedAt)}
-
{(order.deliveryType === "pickup" || order.deliveryStatus === "pickup" || order.delivery_status === "pickup") ? "Статус самовывоза" : "Статус доставки"}
+
{isPickupOrder ? "Статус самовывоза" : "Статус доставки"}
{getOrderGroupDeliveryStatusLabel(order.deliveryStatus || order.delivery_status)}
@@ -1173,7 +1152,7 @@ export const OrderDetailPanel = ({
) : null}
- {canManageDelivery && ["manager", "logistician", "admin", "mega_admin"].includes(userRole) && (order.deliveryType !== "pickup" && order.deliveryStatus !== "pickup" && order.delivery_status !== "pickup") ? (
+ {canManageDelivery && ["manager", "logistician", "admin", "mega_admin"].includes(userRole) && !isPickupOrder ? (
Назначение водителя
diff --git a/src/hooks/useOrderGroups.js b/src/hooks/useOrderGroups.js
index 6d3969b..451523a 100644
--- a/src/hooks/useOrderGroups.js
+++ b/src/hooks/useOrderGroups.js
@@ -7,22 +7,7 @@ import {
ORDER_GROUP_DISPLAY_STATUS_OPTIONS,
getOrderGroupDisplayStatusValue,
} from "../services/orderGroupViews";
-
-const getErrorMessage = (error, fallbackMessage) => {
- if (!error) {
- return fallbackMessage;
- }
-
- if (error instanceof Error) {
- return error.message || fallbackMessage;
- }
-
- if (typeof error === "string") {
- return error || fallbackMessage;
- }
-
- return error?.message || fallbackMessage;
-};
+import { getErrorMessage } from "../utils/deliveryUtils";
export const useOrderGroups = () => {
const [orderGroups, setOrderGroups] = React.useState(() => []);
diff --git a/src/services/orderGroupViews.js b/src/services/orderGroupViews.js
index b24c798..d10ac25 100644
--- a/src/services/orderGroupViews.js
+++ b/src/services/orderGroupViews.js
@@ -5,6 +5,7 @@ const getDeliveryDate = (group) => normalizeDate(group.deliveryDate || group.cus
export const DELIVERY_GROUP_STATUS_LABELS = {
pending_confirmation: "Ожидает согласования",
manual_confirmation_required: "Взят в ручное управление",
+ requires_address: "Требуется адрес",
agreed: "Согласовано",
driver_assigned: "Назначен водитель",
loaded: "Загружено",
diff --git a/src/services/supabase/orderGroupRepository.js b/src/services/supabase/orderGroupRepository.js
index 80f0907..8ad2a0d 100644
--- a/src/services/supabase/orderGroupRepository.js
+++ b/src/services/supabase/orderGroupRepository.js
@@ -8,6 +8,7 @@ import {
getOrderGroupDeliveryStatusLabel,
getOrderGroupStatusLabel,
} from "../orderGroupViews";
+import { normalizeNom } from "../../utils/deliveryUtils";
const requireSupabase = () => {
if (!hasSupabaseConfig || !supabase) {
@@ -68,7 +69,6 @@ export const mapOrderGroupRowToDeliveryGroup = (row) => {
if (!Array.isArray(srcOrders) || !srcOrders.length) return orderNumbers;
const seen = new Set();
const result = [];
- const normalizeNom = (nom) => String(nom || '').replace(/\\\\/g, '\\').trim();
for (const src of srcOrders) {
if (src && Array.isArray(src.orderList)) {
for (const ol of src.orderList) {
diff --git a/src/utils/deliveryUtils.js b/src/utils/deliveryUtils.js
new file mode 100644
index 0000000..d7e49e3
--- /dev/null
+++ b/src/utils/deliveryUtils.js
@@ -0,0 +1,58 @@
+/**
+ * Shared delivery/delivery-group utilities.
+ * Single source of truth for isPickupOrder, getErrorMessage, normalizeNom, and STATUS_LABELS.
+ */
+
+// ── Pickup detection ──────────────────────────────────────────────────────
+
+/**
+ * Returns true if the order group is a pickup (self-pickup) order.
+ * Uses canonical deliveryStatus field (mapper normalizes both delivery_status variants).
+ */
+export const isPickupOrder = (order) =>
+ order?.deliveryType === "pickup" ||
+ order?.deliveryStatus === "pickup" ||
+ order?.delivery_status === "pickup";
+
+// ── Error messages ────────────────────────────────────────────────────────
+
+export const getErrorMessage = (error, fallbackMessage) => {
+ if (!error) {
+ return fallbackMessage;
+ }
+ if (error instanceof Error) {
+ return error.message || fallbackMessage;
+ }
+ if (typeof error === "string") {
+ return error || fallbackMessage;
+ }
+ return error?.message || fallbackMessage;
+};
+
+// ── Nom normalisation ──────────────────────────────────────────────────────
+
+/**
+ * 1C escapes backslashes: "СФ Т\\ЕА-33584" → normalise for comparison.
+ */
+export const normalizeNom = (nom) => {
+ if (!nom) return "";
+ return String(nom).replace(/\\\\/g, "\\").trim();
+};
+
+// ── Status labels ─────────────────────────────────────────────────────────
+
+export const DELIVERY_STATUS_LABELS = {
+ pending_confirmation: "Ожидает подтверждения",
+ requires_address: "Требуется адрес",
+ agreed: "Согласовано",
+ driver_assigned: "Водитель назначен",
+ loaded: "Загружен",
+ on_route: "В пути",
+ delivered: "Доставлено",
+ problem: "Проблема",
+ cancelled: "Отменено",
+ pickup: "Самовывоз",
+};
+
+export const getOrderGroupDeliveryStatusLabel = (status) =>
+ DELIVERY_STATUS_LABELS[status] || status || "Ожидает подтверждения";
\ No newline at end of file