fix(registry): дата и время самовывоза в реестре групп
Владелец: «в таблице всех доставок и самовывоза, если согласована доставка, дата и время указаны, а если самовывоз — нет; нужно и самовывоз выводить». Причина: колонка «Дата» (LogisticsReadinessBoard) читала только delivery_date/ delivery_time, а у самовывоза дата и половина дня лежат в pickup_date/ pickup_time_slot — поэтому строка самовывоза показывала «—», хотя слот согласован. Тот же пропуск был в фильтре по периоду и группировке по датам: getDeliveryDate() смотрела только на delivery_date, и при выбранном периоде заказы самовывоза пропадали из реестра (падали в дату заказа). Введены общие хелперы в orderGroupViews: isPickupGroup, getGroupSlotDate, getGroupSlotTime (камелкеис и snake_case). Реестр показывает дату и время слота по типу заказа, сортировка — по этой же показанной дате, фильтр по периоду и группировка — тоже по ней. Подпись колонки «Доставка» → «Дата» (в колонке теперь и дата выдачи). Попутно поправлено устаревшее ожидание в тесте (getOrderGroupDisplayStatusLabel для черновика ждало «draft», подпись локализована в «Черновик») — тест падал и до этой правки. SW: v121 -> v122.
This commit is contained in:
parent
6944386b3b
commit
3b90b79f9f
|
|
@ -1,7 +1,7 @@
|
|||
const isLocalhost = self.location.hostname === "localhost" || self.location.hostname === "127.0.0.1";
|
||||
|
||||
if (!isLocalhost) {
|
||||
const STATIC_CACHE = "construction-delivery-static-v121";
|
||||
const STATIC_CACHE = "construction-delivery-static-v122";
|
||||
const RUNTIME_CACHE = "construction-delivery-runtime-v119";
|
||||
const APP_SHELL_URLS = ["/", "/client", "/index.html", "/manifest.webmanifest", "/icons/icon-192.png", "/icons/icon-512.png"];
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import {
|
|||
import { CSS } from "@dnd-kit/utilities";
|
||||
import {
|
||||
filterOrderGroups,
|
||||
getGroupSlotDate,
|
||||
getGroupSlotTime,
|
||||
getOrderGroupDisplayStatusLabel,
|
||||
getOrderGroupDisplayStatusValue,
|
||||
getOrderGroupStatusTone,
|
||||
|
|
@ -109,7 +111,7 @@ const TABLE_COLUMNS = [
|
|||
{ key: "customer", label: "Клиент" },
|
||||
{ key: "city", label: "Город" },
|
||||
{ key: "deliveryType", label: "Тип" },
|
||||
{ key: "deliveryDate", label: "Доставка" },
|
||||
{ key: "deliveryDate", label: "Дата" },
|
||||
{ key: "driver", label: "Водитель" },
|
||||
{ key: "status", label: "Статус" },
|
||||
{ key: "updatedAt", label: "Обновлён" },
|
||||
|
|
@ -189,6 +191,9 @@ const renderRow = (g, onSelectSet) => {
|
|||
const smsTone = getSmsProblemTone(g);
|
||||
const smsTag = smsTone !== "ok" ? SMS_TAG[smsTone] : null;
|
||||
const notAgreedHighlight = getNotAgreedHighlight(g);
|
||||
// Дата и время слота: у самовывоза — выдача, у доставки — доставка (владелец: показывать оба типа).
|
||||
const slotDate = getGroupSlotDate(g);
|
||||
const slotTime = getGroupSlotTime(g);
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -228,8 +233,8 @@ const renderRow = (g, onSelectSet) => {
|
|||
</span>
|
||||
</div>
|
||||
<div className="px-3 py-2.5 text-sm text-[var(--color-text-muted)]">
|
||||
{g.deliveryDate ? fmtDate(g.deliveryDate) : "—"}
|
||||
{g.deliveryTime ? <span className="ml-1 text-xs text-[var(--color-text-muted)]">{g.deliveryTime}</span> : null}
|
||||
{slotDate ? fmtDate(slotDate) : "—"}
|
||||
{slotTime ? <span className="ml-1 text-xs text-[var(--color-text-muted)]">{slotTime}</span> : null}
|
||||
</div>
|
||||
<div className="px-3 py-2.5 text-sm text-[var(--color-text-muted)]">{g.assignedDriverName || "—"}</div>
|
||||
<div className="px-3 py-2.5">
|
||||
|
|
@ -382,7 +387,7 @@ export const LogisticsReadinessBoard = ({ orderGroups = [], totalGroups: totalGr
|
|||
case "customer": return text(group.customerName || group.groupKey);
|
||||
case "city": return text(group.city);
|
||||
case "deliveryType": return text(group.deliveryType === "pickup" ? "Самовывоз" : "Доставка");
|
||||
case "deliveryDate": return time(group.deliveryDate);
|
||||
case "deliveryDate": return time(getGroupSlotDate(group));
|
||||
case "driver": return text(group.assignedDriverName);
|
||||
case "status": return text(getOrderGroupDisplayStatusLabel(group));
|
||||
case "updatedAt": return time(group.updatedAt);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,26 @@
|
|||
const normalizeDate = (value) => (value ? String(value) : "");
|
||||
|
||||
const getDeliveryDate = (group) => normalizeDate(group.deliveryDate || group.customerDate || "");
|
||||
// Слот заказа: у самовывоза дата и время выдачи лежат в pickup_*, у доставки — в delivery_*.
|
||||
// Панели и фильтры раньше смотрели только на delivery_*, поэтому у самовывоза колонка даты
|
||||
// пустовала, а фильтр по периоду выкидывал такие заказы.
|
||||
export const isPickupGroup = (group) =>
|
||||
(group?.deliveryType || group?.delivery_type) === "pickup" ||
|
||||
(group?.deliveryStatus || group?.delivery_status) === "pickup";
|
||||
|
||||
export const getGroupSlotDate = (group) =>
|
||||
normalizeDate(
|
||||
isPickupGroup(group)
|
||||
? group?.pickupDate || group?.pickup_date || ""
|
||||
: group?.deliveryDate || group?.delivery_date || "",
|
||||
);
|
||||
|
||||
export const getGroupSlotTime = (group) =>
|
||||
(isPickupGroup(group)
|
||||
? group?.pickupTimeSlot || group?.pickup_time_slot || ""
|
||||
: group?.deliveryTime || group?.delivery_time || "") || "";
|
||||
|
||||
// Дата, по которой группируют и фильтруют реестр: согласованный слот, иначе дата заказа.
|
||||
const getDeliveryDate = (group) => normalizeDate(getGroupSlotDate(group) || group.customerDate || "");
|
||||
|
||||
export const DELIVERY_GROUP_STATUS_LABELS = {
|
||||
pending_confirmation: "Ожидает согласования",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ import { describe, expect, it } from "vitest";
|
|||
import {
|
||||
buildOrderGroupBuckets,
|
||||
filterOrderGroups,
|
||||
getGroupSlotDate,
|
||||
getGroupSlotTime,
|
||||
isPickupGroup,
|
||||
getOrderGroupDisplayStatusLabel,
|
||||
getOrderGroupDisplayStatusValue,
|
||||
getOrderGroupDeliveryStatusLabel,
|
||||
|
|
@ -104,7 +107,7 @@ describe("orderGroupViews", () => {
|
|||
expect(getOrderGroupDisplayStatusLabel(groups[0])).toBe("Согласовано");
|
||||
expect(getOrderGroupDisplayStatusValue(groups[0])).toBe("delivery:agreed");
|
||||
expect(getOrderGroupStatusTone(groups[0])).toBe("accent");
|
||||
expect(getOrderGroupDisplayStatusLabel(groups[2])).toBe("draft");
|
||||
expect(getOrderGroupDisplayStatusLabel(groups[2])).toBe("Черновик"); // подпись локализована
|
||||
});
|
||||
|
||||
it("filters by the visible card status", () => {
|
||||
|
|
@ -115,4 +118,62 @@ describe("orderGroupViews", () => {
|
|||
expect(agreedGroups.map((group) => group.id)).toEqual(["g-1"]);
|
||||
expect(deliveredGroups.map((group) => group.id)).toEqual(["g-2"]);
|
||||
});
|
||||
|
||||
it("берёт дату и время самовывоза из pickup_*, а не из delivery_*", () => {
|
||||
const pickupGroup = {
|
||||
id: "p-1",
|
||||
deliveryType: "pickup",
|
||||
deliveryStatus: "agreed",
|
||||
deliveryDate: null,
|
||||
deliveryTime: null,
|
||||
pickupDate: "2026-09-30",
|
||||
pickupTimeSlot: "Вторая половина дня",
|
||||
customerDate: "28.09.26",
|
||||
orderNumbers: ["P-1"],
|
||||
status: "ready_for_notification",
|
||||
smsSentAt: null,
|
||||
ordersCount: 1,
|
||||
readyCount: 1,
|
||||
notReadyCount: 0,
|
||||
createdAt: "2026-09-28T07:00:00Z",
|
||||
updatedAt: "2026-09-28T07:00:00Z",
|
||||
};
|
||||
const deliveryGroup = { ...groups[0], deliveryType: "delivery", deliveryDate: "2026-09-30", deliveryTime: "Первая половина дня" };
|
||||
|
||||
expect(isPickupGroup(pickupGroup)).toBe(true);
|
||||
expect(getGroupSlotDate(pickupGroup)).toBe("2026-09-30");
|
||||
expect(getGroupSlotTime(pickupGroup)).toBe("Вторая половина дня");
|
||||
expect(getGroupSlotDate(deliveryGroup)).toBe("2026-09-30");
|
||||
expect(getGroupSlotTime(deliveryGroup)).toBe("Первая половина дня");
|
||||
|
||||
// snake_case из сырой строки БД тоже поддержан
|
||||
expect(getGroupSlotDate({ delivery_type: "pickup", pickup_date: "2026-10-01" })).toBe("2026-10-01");
|
||||
expect(getGroupSlotTime({ delivery_type: "pickup", pickup_time_slot: "Первая половина дня" })).toBe("Первая половина дня");
|
||||
});
|
||||
|
||||
it("фильтр по периоду ловит самовывоз по дате выдачи", () => {
|
||||
const pickupGroup = {
|
||||
id: "p-2",
|
||||
deliveryType: "pickup",
|
||||
deliveryStatus: "agreed",
|
||||
deliveryDate: null,
|
||||
pickupDate: "2026-09-30",
|
||||
pickupTimeSlot: "Вторая половина дня",
|
||||
customerDate: "28.09.26",
|
||||
orderNumbers: ["P-2"],
|
||||
status: "ready_for_notification",
|
||||
smsSentAt: null,
|
||||
ordersCount: 1,
|
||||
readyCount: 1,
|
||||
notReadyCount: 0,
|
||||
createdAt: "2026-09-28T07:00:00Z",
|
||||
updatedAt: "2026-09-28T07:00:00Z",
|
||||
};
|
||||
|
||||
expect(filterOrderGroups([pickupGroup], { dateFrom: "2026-09-30", dateTo: "2026-09-30" })).toHaveLength(1);
|
||||
expect(filterOrderGroups([pickupGroup], { dateFrom: "2026-10-01", dateTo: "2026-10-05" })).toHaveLength(0);
|
||||
|
||||
const grouped = groupOrderGroupsByDate([pickupGroup]);
|
||||
expect(grouped[0].date).toBe("2026-09-30");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue