fix: správné mapování stavu waiting_delivery ve stepperu Bolt
CI / Generate TypeScript types (push) Successful in 12s
CI / Server unit tests (push) Successful in 20s
CI / Build server (push) Successful in 28s
CI / Build client (push) Successful in 1m10s
CI / Playwright E2E tests (push) Successful in 1m22s
CI / Build and push Docker image (push) Successful in 42s
CI / Notify (push) Successful in 1s

Stav waiting_delivery znamená "jídlo čeká v podniku na vyzvednutí",
heuristika ho ale kvůli slovu "delivery" mapovala na krok "Na cestě".

- waiting_delivery (a obecně waiting_*) se nyní mapuje na "Příprava"
- server nově ukládá i stav kurýra (boltCourierState z courier.state);
  krok "Na cestě" se aktivuje až když kurýr objednávku skutečně veze
  (picked_up, heading_to_client, ...), kurýr u podniku zůstává v "Příprava"
- tooltip stepperu zobrazuje oba raw stavy pro snadnější diagnostiku
- regresní test s reálnou odpovědí Bolt API

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 12:55:21 +02:00
parent 84b95c6c70
commit c404a3a03b
6 changed files with 78 additions and 14 deletions
+6 -1
View File
@@ -53,6 +53,7 @@ interface BoltOrder {
order_id: number;
order_state: string;
expected_time_to_client_in_seconds?: number;
courier?: { state?: string } | null;
}
/** Dotáže se veřejného Bolt API na stav sdílené objednávky. Vrátí null, pokud objednávka už neexistuje. */
@@ -103,6 +104,7 @@ export async function checkBoltTracking(): Promise<void> {
for (const group of candidates) {
let deliveryAt: string | undefined;
let orderState: string | undefined;
let courierState: string | undefined;
let clearToken = false;
try {
@@ -114,6 +116,7 @@ export async function checkBoltTracking(): Promise<void> {
clearToken = true;
} else {
orderState = order.order_state || undefined;
courierState = order.courier?.state || undefined;
if (TERMINAL_STATE_REGEX.test(order.order_state ?? '')) {
clearToken = true;
} else if (typeof order.expected_time_to_client_in_seconds === 'number') {
@@ -131,7 +134,8 @@ export async function checkBoltTracking(): Promise<void> {
const timeChanged = deliveryAt !== undefined && deliveryAt !== group.deliveryAt;
const stateChanged = orderState !== undefined && orderState !== group.boltOrderState;
if (!clearToken && !timeChanged && !stateChanged) continue;
const courierChanged = courierState !== group.boltCourierState && !clearToken;
if (!clearToken && !timeChanged && !stateChanged && !courierChanged) continue;
updated = await storage.updateData<ClientData>(key, current => {
const d = current ?? data!;
@@ -139,6 +143,7 @@ export async function checkBoltTracking(): Promise<void> {
if (g?.boltTrackingToken) {
if (timeChanged) g.deliveryAt = deliveryAt;
if (stateChanged) g.boltOrderState = orderState;
if (courierChanged) g.boltCourierState = courierState;
if (clearToken) g.boltTrackingToken = undefined;
}
return d;
+3
View File
@@ -153,6 +153,7 @@ export async function setGroupState(login: string, groupId: string, newState: Gr
group.qrGenerated = undefined;
group.boltTrackingToken = undefined;
group.boltOrderState = undefined;
group.boltCourierState = undefined;
for (const ml of memberLogins) {
group.members[ml] = { ...group.members[ml], paid: undefined };
}
@@ -211,6 +212,7 @@ export async function setGroupBoltTracking(login: string, groupId: string, share
if (!shareUrl) {
group.boltTrackingToken = undefined;
group.boltOrderState = undefined;
group.boltCourierState = undefined;
} else {
if (group.state !== GroupState.ORDERED) throw new Error('Sledování Bolt lze nastavit pouze ve stavu "objednáno"');
const token = extractBoltToken(shareUrl);
@@ -219,6 +221,7 @@ export async function setGroupBoltTracking(login: string, groupId: string, share
group.boltTrackingToken = token;
// Stav patří k předchozí objednávce — vyčistíme, doplní ho první poll
group.boltOrderState = undefined;
group.boltCourierState = undefined;
}
}
return saveExtraData(data, date);
+24
View File
@@ -197,6 +197,30 @@ describe('checkBoltTracking', () => {
expect(group.boltOrderState).toBe('delivered');
});
test('ukládá stav kurýra (reálná odpověď s waiting_delivery)', async () => {
mockedAxios.post.mockResolvedValue(boltResponse({
order_id: 312222357,
order_state: 'waiting_delivery',
expected_time_to_client_in_seconds: 911,
provider: { provider_id: 82859, state: 'waiting_pickup' },
courier: { courier_id: 1958424, state: 'arrived_to_provider', lat: 49.7, lng: 13.3 },
}));
await checkBoltTracking();
let group = await getGroup();
expect(group.boltOrderState).toBe('waiting_delivery');
expect(group.boltCourierState).toBe('arrived_to_provider');
// Kurýr vyzvedl — změní se jen courier state
mockedAxios.post.mockResolvedValue(boltResponse({
order_state: 'waiting_delivery',
expected_time_to_client_in_seconds: 911,
courier: { state: 'picked_up' },
}));
await checkBoltTracking();
group = await getGroup();
expect(group.boltCourierState).toBe('picked_up');
});
test('aktualizuje boltOrderState při změně stavu beze změny času', async () => {
mockedAxios.post.mockResolvedValue(boltResponse({ order_state: 'waiting_preparation', expected_time_to_client_in_seconds: 1800 }));
await checkBoltTracking();