fix(delivery): show invoice refs in client flow

This commit is contained in:
Codex 2026-05-14 12:51:25 +03:00
parent eeb2620547
commit fc74241378
8 changed files with 65 additions and 12 deletions

View File

@ -72,8 +72,8 @@ begin
v_order_number := coalesce( v_order_number := coalesce(
nullif(v_invitation.order_number, ''), nullif(v_invitation.order_number, ''),
nullif(v_group.group_key, ''), to_jsonb(v_group.order_numbers) ->> 0,
to_jsonb(v_group.order_numbers) ->> 0 nullif(v_group.group_key, '')
); );
v_customer_name := coalesce( v_customer_name := coalesce(
nullif(v_group.customer_name, ''), nullif(v_group.customer_name, ''),

View File

@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import { getInvitationReferenceLabel } from "./invitationReference";
import { Badge } from "../UI/Badge"; import { Badge } from "../UI/Badge";
import { Button } from "../UI/Button"; import { Button } from "../UI/Button";
import { Panel } from "../UI/Panel"; import { Panel } from "../UI/Panel";
@ -55,8 +56,7 @@ export const DeliveryChoiceFlow = ({
}) => { }) => {
const state = invitation.state || "awaiting_choice"; const state = invitation.state || "awaiting_choice";
const isActive = ACTIVE_STATES.has(state); const isActive = ACTIVE_STATES.has(state);
const orderNumber = invitation.orderNumber || "—"; const invitationReference = getInvitationReferenceLabel(invitation);
const customerName = invitation.customerName || "Клиент";
const orderItems = (invitation.orderItems || invitation.items || []) const orderItems = (invitation.orderItems || invitation.items || [])
.map(splitOrderItem) .map(splitOrderItem)
.filter(Boolean); .filter(Boolean);
@ -79,7 +79,7 @@ export const DeliveryChoiceFlow = ({
<Badge tone="warning">{STATE_LABELS[state]}</Badge> <Badge tone="warning">{STATE_LABELS[state]}</Badge>
</div> </div>
<p className="text-sm leading-6 text-[var(--color-text-muted)]"> <p className="text-sm leading-6 text-[var(--color-text-muted)]">
Заказ {orderNumber} для {customerName}. Проверьте состав заказа и выберите удобную половину дня. {invitationReference}. Проверьте состав заказа и выберите удобную половину дня.
</p> </p>
</div> </div>

View File

@ -114,7 +114,7 @@ describe("DeliveryChoiceFlow", () => {
/>, />,
); );
expect(markup).toContain("CD-240032"); expect(markup).toContain("Счет CD-240032");
expect(markup).toContain("Александр Савин"); expect(markup).not.toContain("Александр Савин");
}); });
}); });

View File

@ -0,0 +1,39 @@
const getOrderItemNames = (invitation) => {
const rawItems = Array.isArray(invitation?.orderItems)
? invitation.orderItems
: Array.isArray(invitation?.items)
? invitation.items
: [];
return rawItems
.map((item) => {
if (typeof item === "string") {
return item.trim();
}
if (item && typeof item === "object" && typeof item.name === "string") {
return item.name.trim();
}
return "";
})
.filter(Boolean);
};
export const getInvitationReferenceLabel = (invitation) => {
const invoiceNumbers = [...new Set(getOrderItemNames(invitation))];
if (invoiceNumbers.length === 1) {
return `Счет ${invoiceNumbers[0]}`;
}
if (invoiceNumbers.length > 1) {
return `Счета: ${invoiceNumbers.join(", ")}`;
}
const orderNumber = typeof invitation?.orderNumber === "string" ? invitation.orderNumber.trim() : "";
if (orderNumber) {
return `Счет ${orderNumber}`;
}
return "Счет —";
};

View File

@ -2,6 +2,7 @@ import React from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { DeliveryChoiceFlow } from "../components/client/DeliveryChoiceFlow"; import { DeliveryChoiceFlow } from "../components/client/DeliveryChoiceFlow";
import { DeliverySlotsPicker } from "../components/client/DeliverySlotsPicker"; import { DeliverySlotsPicker } from "../components/client/DeliverySlotsPicker";
import { getInvitationReferenceLabel } from "../components/client/invitationReference";
import { DeliveryStateNotice } from "../components/client/DeliveryStateNotice"; import { DeliveryStateNotice } from "../components/client/DeliveryStateNotice";
import { Panel } from "../components/UI/Panel"; import { Panel } from "../components/UI/Panel";
import { formatDeliveryDate } from "../components/client/deliveryDateFormatting"; import { formatDeliveryDate } from "../components/client/deliveryDateFormatting";
@ -312,8 +313,7 @@ export const ClientDeliveryPage = () => {
<p className="text-sm uppercase tracking-[0.24em] text-[var(--color-text-muted)]">Ваш выбор</p> <p className="text-sm uppercase tracking-[0.24em] text-[var(--color-text-muted)]">Ваш выбор</p>
<h2 className="text-xl font-semibold leading-tight">Сохранено: {savedChoiceLabel}</h2> <h2 className="text-xl font-semibold leading-tight">Сохранено: {savedChoiceLabel}</h2>
<p className="text-sm leading-6 text-[var(--color-text-muted)]"> <p className="text-sm leading-6 text-[var(--color-text-muted)]">
Заказ {invitation?.orderNumber || "—"} {getInvitationReferenceLabel(invitation)}
{invitation?.customerName ? ` · ${invitation.customerName}` : ""}
</p> </p>
<p className="text-sm leading-6 text-[var(--color-text-muted)]"> <p className="text-sm leading-6 text-[var(--color-text-muted)]">
Статус: доставка уже согласована. При повторном открытии этой ссылки будет показан тот же выбор. Статус: доставка уже согласована. При повторном открытии этой ссылки будет показан тот же выбор.

View File

@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { getInvitationReferenceLabel } from "../components/client/invitationReference";
import { import {
buildDeliveryConfirmationPayload, buildDeliveryConfirmationPayload,
buildSelectedSlotFromInvitation, buildSelectedSlotFromInvitation,
@ -162,4 +163,17 @@ describe("ClientDeliveryPage helpers", () => {
}, },
]); ]);
}); });
it("prefers the invoice number from order items over the customer name", () => {
expect(
getInvitationReferenceLabel({
orderNumber: "9787464846|04.05.26",
customerName: "Конаков Сергей Алексеевич (ДАРТС)",
orderItems: [
{ name: "СФ Т\\ЕА-28687", quantity: "" },
{ name: "СФ Т\\ЕА-28700", quantity: "" },
],
}),
).toBe("Счета: СФ Т\\ЕА-28687, СФ Т\\ЕА-28700");
});
}); });

View File

@ -226,7 +226,7 @@ export const buildPublicOrderGroupInvitationView = (
orderGroupId: invitation.order_group_id || group.id, orderGroupId: invitation.order_group_id || group.id,
state: invitation.state, state: invitation.state,
token: "", token: "",
orderNumber: invitation.order_number || group.group_key || orderNumbers[0] || null, orderNumber: invitation.order_number || orderNumbers[0] || group.group_key || null,
customerName: maskCustomerName(customerName), customerName: maskCustomerName(customerName),
customerPhone: maskPhoneNumber(customerPhone), customerPhone: maskPhoneNumber(customerPhone),
orderItems: orderNumbers.map((number) => ({ name: number, quantity: "" })), orderItems: orderNumbers.map((number) => ({ name: number, quantity: "" })),

View File

@ -612,8 +612,8 @@ begin
v_order_number := coalesce( v_order_number := coalesce(
nullif(v_invitation.order_number, ''), nullif(v_invitation.order_number, ''),
nullif(v_group.group_key, ''), to_jsonb(v_group.order_numbers) ->> 0,
to_jsonb(v_group.order_numbers) ->> 0 nullif(v_group.group_key, '')
); );
v_customer_name := coalesce( v_customer_name := coalesce(
nullif(v_group.customer_name, ''), nullif(v_group.customer_name, ''),