@flowselections/logistiek-engine 1.0.12 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist-lib/components/FloridayPushCard.d.ts.map +1 -1
  2. package/dist-lib/components/FloridayPushCard.js +7 -2
  3. package/dist-lib/components/PrintjobsCard.d.ts.map +1 -1
  4. package/dist-lib/components/PrintjobsCard.js +3 -1
  5. package/dist-lib/hooks/useFloridayPushes.d.ts +7 -0
  6. package/dist-lib/hooks/useFloridayPushes.d.ts.map +1 -1
  7. package/dist-lib/hooks/useFloridayPushes.js +11 -0
  8. package/dist-lib/hooks/usePrintjobs.d.ts +1 -0
  9. package/dist-lib/hooks/usePrintjobs.d.ts.map +1 -1
  10. package/dist-lib/hooks/usePrintjobs.js +17 -1
  11. package/dist-lib/integrations/supabase/auth-middleware.d.ts +292 -0
  12. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  13. package/dist-lib/integrations/supabase/client.server.d.ts +292 -0
  14. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  15. package/dist-lib/integrations/supabase/moduleClient.d.ts +292 -0
  16. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  17. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +6 -0
  18. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -0
  19. package/dist-lib/integrations/supabase/previewAuthStorage.js +92 -0
  20. package/dist-lib/integrations/supabase/types.d.ts +299 -0
  21. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  22. package/dist-lib/lib/floridayDocuments.functions.d.ts +876 -0
  23. package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
  24. package/dist-lib/lib/floridayPush.functions.d.ts +292 -0
  25. package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/floridayPush.server.d.ts +39 -21
  27. package/dist-lib/lib/floridayPush.server.d.ts.map +1 -1
  28. package/dist-lib/lib/floridayPush.server.js +156 -49
  29. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +1460 -0
  30. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
  31. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +584 -0
  32. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/logisticsRecalculate.functions.d.ts +292 -0
  34. package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
  35. package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
  36. package/dist-lib/lib/modulePrompts.js +39 -0
  37. package/package.json +1 -1
@@ -4,38 +4,56 @@ export declare const FLORIDAY_PUSH_LEASE_SECONDS = 120;
4
4
  /** FNV-1a, 64 bits als hex. Geen crypto nodig, deterministisch in elke runtime. */
5
5
  export declare function hashPayload(value: unknown): string;
6
6
  export interface FloridayPushPayload {
7
- orderRef: string;
8
- fulfillmentOrderId: string | null;
9
- salesOrderId: string | null;
10
- loadCarriers: Array<{
11
- cartNumber: number | null;
12
- carrierType: string | null;
13
- layersUsed: number | null;
14
- maxLayers: number | null;
15
- occupancyPct: number | null;
16
- weightKg: number | null;
17
- items: Array<{
18
- productId: string | null;
19
- productName: string | null;
20
- crates: number | null;
21
- boxes: number | null;
22
- plantsPerCrate: number | null;
23
- }>;
24
- }>;
7
+ deliveryOrderId: string;
8
+ fulfillmentRequestIds: string[];
25
9
  }
26
- /** Bouwt de gewenste stand voor één order. null = niets te pushen. */
27
- export declare function buildFloridayPayload(sb: SupabaseClient, orderRef: string): Promise<{
10
+ export type BuildResult = {
11
+ ok: true;
28
12
  payload: FloridayPushPayload;
29
13
  hash: string;
30
- } | null>;
14
+ } | {
15
+ ok: false;
16
+ code: string;
17
+ error: string;
18
+ permanent: boolean;
19
+ };
20
+ /**
21
+ * Zoekt de delivery-order die bij een sales-order hoort. De koppeling ligt op
22
+ * de delivery-order: data->'fulfillmentRequests'[]->>'salesOrderId'.
23
+ * Bij meerdere treffers wint de meest recent opgehaalde rij (fetched_at desc).
24
+ */
25
+ export declare function findDeliveryOrderForSalesOrder(sb: SupabaseClient, salesOrderId: string): Promise<{
26
+ ok: true;
27
+ deliveryOrderId: string;
28
+ fulfillmentRequestIds: string[];
29
+ matches: number;
30
+ } | {
31
+ ok: false;
32
+ code: string;
33
+ error: string;
34
+ }>;
35
+ /** Bouwt de gewenste stand voor één order. */
36
+ export declare function buildFloridayPayload(sb: SupabaseClient, orderRef: string): Promise<BuildResult>;
31
37
  export declare function queueFloridayPush(sb: SupabaseClient, orderRef: string): Promise<void>;
32
38
  export declare function queueFloridayPushes(sb: SupabaseClient, orderRefs: string[]): Promise<void>;
39
+ /**
40
+ * Uitkomst van één push, zoals die aan de database wordt doorgegeven.
41
+ * - "ok" → verstuurd (of identieke stand, niets te doen)
42
+ * - "error" → mislukt, mogelijk opnieuw proberen
43
+ * - "already_fulfilled" → Floriday had de order al afgehandeld; eindstatus
44
+ * - "not_applicable" → order bestaat niet (meer) bij ons; eindstatus
45
+ */
46
+ export type PushOutcome = "ok" | "error" | "already_fulfilled" | "not_applicable";
33
47
  export interface PushQueueResult {
34
48
  claimed: number;
35
49
  sent: number;
36
50
  skipped: number;
37
51
  failed: number;
38
52
  retried: number;
53
+ alreadyFulfilled: number;
54
+ notApplicable: number;
55
+ /** Statuswaarden die we niet kennen — nooit stilzwijgend laten verdwijnen. */
56
+ unknown: number;
39
57
  }
40
58
  export declare function runFloridayPushQueue(sb: SupabaseClient, opts?: {
41
59
  limit?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"floridayPush.server.d.ts","sourceRoot":"","sources":["../../src/lib/floridayPush.server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAc/C,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUlD;AAMD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,KAAK,CAAC;QAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,KAAK,EAAE,KAAK,CAAC;YACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;YACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;YAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;YACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;YACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;SAC/B,CAAC,CAAC;KACJ,CAAC,CAAC;CACJ;AAED,sEAAsE;AACtE,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAkDhE;AAOD,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW3F;AAED,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhG;AA6BD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,OAAO,CAAC,eAAe,CAAC,CAyH1B;AAQD,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAS3F"}
1
+ {"version":3,"file":"floridayPush.server.d.ts","sourceRoot":"","sources":["../../src/lib/floridayPush.server.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAc/C,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUlD;AAcD,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAUnE;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,cAAc,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CACN;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAC7C,CAqDA;AAED,8CAA8C;AAC9C,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CA+EtB;AAQD,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW3F;AAED,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhG;AA6BD;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,OAAO,GAAG,mBAAmB,GAAG,gBAAgB,CAAC;AAElF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,OAAO,CAAC,eAAe,CAAC,CA0J1B;AAQD,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAS3F"}
@@ -36,51 +36,126 @@ export function hashPayload(value) {
36
36
  }
37
37
  return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
38
38
  }
39
- /** Bouwt de gewenste stand voor één order. null = niets te pushen. */
39
+ /**
40
+ * Zoekt de delivery-order die bij een sales-order hoort. De koppeling ligt op
41
+ * de delivery-order: data->'fulfillmentRequests'[]->>'salesOrderId'.
42
+ * Bij meerdere treffers wint de meest recent opgehaalde rij (fetched_at desc).
43
+ */
44
+ export async function findDeliveryOrderForSalesOrder(sb, salesOrderId) {
45
+ const { data, error } = await sb
46
+ .from("floriday_delivery_orders_cache")
47
+ .select("floriday_id, data, fetched_at")
48
+ .contains("data", { fulfillmentRequests: [{ salesOrderId }] })
49
+ .order("fetched_at", { ascending: false })
50
+ .limit(10);
51
+ if (error) {
52
+ return { ok: false, code: "db_error_delivery_orders", error: `Delivery-ordercache niet leesbaar: ${error.message}` };
53
+ }
54
+ const rows = Array.isArray(data) ? data : [];
55
+ if (rows.length === 0) {
56
+ return {
57
+ ok: false,
58
+ code: "delivery_order_not_found_local",
59
+ error: `Nog geen delivery-order in de cache voor sales-order ${salesOrderId}. De push wacht tot de synchronisatie hem binnenhaalt.`,
60
+ };
61
+ }
62
+ if (rows.length > 1) {
63
+ console.info(`[floridayPush] ${rows.length} delivery-orders gevonden voor sales-order ${salesOrderId}; meest recent gefetchte gekozen.`);
64
+ }
65
+ const row = rows[0];
66
+ const payloadData = (row["data"] ?? {});
67
+ const requests = Array.isArray(payloadData["fulfillmentRequests"])
68
+ ? payloadData["fulfillmentRequests"]
69
+ : [];
70
+ const fulfillmentRequestIds = requests
71
+ .filter((r) => String(r["salesOrderId"] ?? "") === salesOrderId)
72
+ .map((r) => String(r["fulfillmentRequestId"] ?? r["id"] ?? ""))
73
+ .filter((s) => s.length > 0)
74
+ .sort();
75
+ if (fulfillmentRequestIds.length === 0) {
76
+ return {
77
+ ok: false,
78
+ code: "delivery_order_without_requests",
79
+ error: `Delivery-order ${String(row["floriday_id"] ?? "")} bevat geen fulfillmentRequests voor sales-order ${salesOrderId}.`,
80
+ };
81
+ }
82
+ return {
83
+ ok: true,
84
+ deliveryOrderId: String(row["floriday_id"] ?? ""),
85
+ fulfillmentRequestIds,
86
+ matches: rows.length,
87
+ };
88
+ }
89
+ /** Bouwt de gewenste stand voor één order. */
40
90
  export async function buildFloridayPayload(sb, orderRef) {
41
- const { data: order } = await sb
91
+ const { data: order, error: orderError } = await sb
42
92
  .from("orders")
43
93
  .select("id, order_number, floriday_sales_order_id")
44
94
  .eq("order_number", orderRef)
45
95
  .maybeSingle();
46
- if (!order)
47
- return null;
96
+ if (orderError) {
97
+ return {
98
+ ok: false,
99
+ code: "db_error_orders",
100
+ error: `Order niet leesbaar: ${orderError.message}`,
101
+ permanent: false,
102
+ };
103
+ }
104
+ if (!order) {
105
+ return { ok: false, code: "order_missing", error: "Order niet gevonden in de Engine.", permanent: true };
106
+ }
48
107
  const row = order;
49
- const { data: carts } = await sb
108
+ const salesOrderId = row["floriday_sales_order_id"] ?? null;
109
+ if (!salesOrderId) {
110
+ return {
111
+ ok: false,
112
+ code: "sales_order_missing",
113
+ error: "Order heeft geen Floriday sales-order; er valt niets te pushen.",
114
+ permanent: true,
115
+ };
116
+ }
117
+ const found = await findDeliveryOrderForSalesOrder(sb, salesOrderId);
118
+ if (!found.ok) {
119
+ // Wachttoestand, geen mislukking: de delivery-order kan later binnenkomen.
120
+ return { ok: false, code: found.code, error: found.error, permanent: false };
121
+ }
122
+ const { data: carts, error: cartsError } = await sb
50
123
  .from("order_carts")
51
124
  .select("id, cart_number, carrier_type, total_layers_used, max_layers, occupancy_pct, weight_kg, order_cart_items(product_id, product_name, crates, boxes, plants_per_crate)")
52
125
  .eq("order_id", row["id"])
53
126
  .order("cart_number", { ascending: true });
54
- const safeCarts = Array.isArray(carts) ? carts : [];
127
+ if (cartsError) {
128
+ return {
129
+ ok: false,
130
+ code: "db_error_carts",
131
+ error: `Karindeling niet leesbaar: ${cartsError.message}`,
132
+ permanent: false,
133
+ };
134
+ }
135
+ const indeling = (Array.isArray(carts) ? carts : []).map((c) => {
136
+ const cart = c;
137
+ const items = Array.isArray(cart["order_cart_items"])
138
+ ? cart["order_cart_items"]
139
+ : [];
140
+ return {
141
+ cartNumber: cart["cart_number"] ?? null,
142
+ carrierType: cart["carrier_type"] ?? null,
143
+ layersUsed: cart["total_layers_used"] ?? null,
144
+ items: items
145
+ .map((i) => ({
146
+ productId: i["product_id"] ?? null,
147
+ crates: i["crates"] ?? null,
148
+ boxes: i["boxes"] ?? null,
149
+ }))
150
+ .sort((a, b) => String(a.productId).localeCompare(String(b.productId))),
151
+ };
152
+ });
55
153
  const payload = {
56
- orderRef,
57
- fulfillmentOrderId: null,
58
- salesOrderId: row["floriday_sales_order_id"] ?? null,
59
- loadCarriers: safeCarts.map((c) => {
60
- const cart = c;
61
- const items = Array.isArray(cart["order_cart_items"])
62
- ? cart["order_cart_items"]
63
- : [];
64
- return {
65
- cartNumber: cart["cart_number"] ?? null,
66
- carrierType: cart["carrier_type"] ?? null,
67
- layersUsed: cart["total_layers_used"] ?? null,
68
- maxLayers: cart["max_layers"] ?? null,
69
- occupancyPct: cart["occupancy_pct"] ?? null,
70
- weightKg: cart["weight_kg"] ?? null,
71
- items: items
72
- .map((i) => ({
73
- productId: i["product_id"] ?? null,
74
- productName: i["product_name"] ?? null,
75
- crates: i["crates"] ?? null,
76
- boxes: i["boxes"] ?? null,
77
- plantsPerCrate: i["plants_per_crate"] ?? null,
78
- }))
79
- .sort((a, b) => String(a.productId).localeCompare(String(b.productId))),
80
- };
81
- }),
154
+ deliveryOrderId: found.deliveryOrderId,
155
+ fulfillmentRequestIds: found.fulfillmentRequestIds,
82
156
  };
83
- return { payload, hash: hashPayload(payload) };
157
+ // De hash bewaakt ook de karindeling, zodat een herindeling opnieuw pusht.
158
+ return { ok: true, payload, hash: hashPayload({ payload, indeling }) };
84
159
  }
85
160
  // ---------------------------------------------------------------------------
86
161
  // In de wachtrij zetten — snel, zonder netwerk, veilig overal aan te roepen
@@ -134,7 +209,16 @@ function gatewayErrorCode(status, payloadOk) {
134
209
  export async function runFloridayPushQueue(sb, opts = {}) {
135
210
  const limit = opts.limit ?? FLORIDAY_PUSH_BATCH;
136
211
  const deadlineAt = opts.deadlineAt ?? Date.now() + 10000;
137
- const out = { claimed: 0, sent: 0, skipped: 0, failed: 0, retried: 0 };
212
+ const out = {
213
+ claimed: 0,
214
+ sent: 0,
215
+ skipped: 0,
216
+ failed: 0,
217
+ retried: 0,
218
+ alreadyFulfilled: 0,
219
+ notApplicable: 0,
220
+ unknown: 0,
221
+ };
138
222
  let claimed = [];
139
223
  try {
140
224
  const { data } = await sb.rpc("logistics_floriday_claim_pushes", {
@@ -158,7 +242,7 @@ export async function runFloridayPushQueue(sb, opts = {}) {
158
242
  const revision = Number(item["revision"] ?? 0);
159
243
  const orderRef = String(item["order_ref"] ?? "");
160
244
  const lastSentHash = item["last_sent_hash"] ?? null;
161
- let ok = false;
245
+ let outcome = "error";
162
246
  let permanent = false;
163
247
  let error = null;
164
248
  let sentHash = null;
@@ -168,16 +252,19 @@ export async function runFloridayPushQueue(sb, opts = {}) {
168
252
  let response = null;
169
253
  try {
170
254
  const built = await buildFloridayPayload(sb, orderRef);
171
- if (!built) {
172
- // Order bestaat niet (meer): niets te pushen, geen eindeloze retry.
173
- ok = false;
174
- permanent = true;
175
- error = "Order niet gevonden in de Engine.";
176
- errorCode = "order_missing";
255
+ if (!built.ok) {
256
+ // Wachttoestand (delivery-order nog niet gesynchroniseerd) of een
257
+ // databasefout; permanent alleen als de order zelf niet kan bestaan.
258
+ // order_missing / sales_order_missing worden in de database vertaald
259
+ // naar de eindstatus not_applicable.
260
+ outcome = "error";
261
+ permanent = built.permanent;
262
+ error = built.error;
263
+ errorCode = built.code;
177
264
  }
178
265
  else if (built.hash === lastSentHash) {
179
266
  // Identieke stand: niets versturen, wel als geslaagd afronden.
180
- ok = true;
267
+ outcome = "ok";
181
268
  sentHash = built.hash;
182
269
  out.skipped++;
183
270
  }
@@ -192,16 +279,24 @@ export async function runFloridayPushQueue(sb, opts = {}) {
192
279
  }
193
280
  const payloadOk = payload?.ok ?? res?.ok;
194
281
  const payloadError = payload?.error ?? res?.error;
195
- ok = payloadOk === true;
282
+ // TODO(already_fulfilled): zodra de auth-module (@flowselections/
283
+ // floriday-authenticatie-module) de uitkomst "al afgehandeld" publiceert,
284
+ // hier de herkenning inbouwen op basis van het gepubliceerde veld —
285
+ // NIET op tekstmatching van last_error. Zet dan:
286
+ // outcome = "already_fulfilled"; sentHash = built.hash;
287
+ // errorCode = "already_fulfilled";
288
+ // Het veld is nog niet vastgesteld: in de nu beschikbare gatewayversie
289
+ // bestaat de code "already_fulfilled" niet. Tot die tijd blijft dit pad
290
+ // ongebruikt; de statuswaarde is verder overal al doorgevoerd.
291
+ outcome = payloadOk === true ? "ok" : "error";
196
292
  sentHash = built.hash;
197
- fulfillmentOrderId =
198
- payload?.fulfillmentOrderId ?? built.payload.fulfillmentOrderId ?? null;
293
+ fulfillmentOrderId = payload?.fulfillmentOrderId ?? null;
199
294
  response = {
200
295
  ok: payloadOk ?? null,
201
296
  error: payloadError ?? null,
202
297
  fulfillmentOrderId: payload?.fulfillmentOrderId ?? null,
203
298
  };
204
- if (!ok) {
299
+ if (outcome !== "ok") {
205
300
  error =
206
301
  payloadError && String(payloadError).length > 0
207
302
  ? String(payloadError)
@@ -212,15 +307,16 @@ export async function runFloridayPushQueue(sb, opts = {}) {
212
307
  }
213
308
  }
214
309
  catch (e) {
310
+ outcome = "error";
215
311
  error = String(e?.message ?? e);
216
312
  errorCode = errorCode ?? "unknown";
217
313
  permanent = isPermanent(null, error);
218
314
  }
219
315
  try {
220
- const { data: outcome } = await sb.rpc("logistics_floriday_finish_push_v2", {
316
+ const { data: finished } = await sb.rpc("logistics_floriday_finish_push_v3", {
221
317
  _id: id,
222
318
  _revision: revision,
223
- _ok: ok,
319
+ _outcome: outcome,
224
320
  _error: error,
225
321
  _fulfillment_order_id: fulfillmentOrderId,
226
322
  _sent_hash: sentHash,
@@ -229,13 +325,24 @@ export async function runFloridayPushQueue(sb, opts = {}) {
229
325
  _error_code: errorCode,
230
326
  _response: response,
231
327
  });
232
- const status = String(outcome ?? "");
328
+ const status = String(finished ?? "");
233
329
  if (status === "sent")
234
330
  out.sent++;
235
331
  else if (status === "failed")
236
332
  out.failed++;
237
333
  else if (status === "pending")
238
334
  out.retried++;
335
+ else if (status === "already_fulfilled")
336
+ out.alreadyFulfilled++;
337
+ else if (status === "not_applicable")
338
+ out.notApplicable++;
339
+ else if (status === "superseded" || status === "missing") {
340
+ // Rij is intussen opnieuw in de wachtrij gezet of verwijderd; geen teller.
341
+ }
342
+ else {
343
+ out.unknown++;
344
+ console.warn(`[floridayPush] onbekende afrondstatus '${status}' voor order ${orderRef}`);
345
+ }
239
346
  }
240
347
  catch {
241
348
  // Lease loopt vanzelf af; de volgende tick pakt de rij opnieuw op.