@flowselections/floriday-verkoop-module 1.0.56 → 1.0.58

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 (138) hide show
  1. package/dist-lib/components/verkoop/AnalyticsCards.d.ts +1 -1
  2. package/dist-lib/components/verkoop/AnalyticsCards.d.ts.map +1 -1
  3. package/dist-lib/components/verkoop/AutoFulfillmentStatusCard.d.ts +1 -1
  4. package/dist-lib/components/verkoop/AutoFulfillmentStatusCard.d.ts.map +1 -1
  5. package/dist-lib/components/verkoop/CartAssignmentWizard.d.ts +1 -1
  6. package/dist-lib/components/verkoop/CartAssignmentWizard.d.ts.map +1 -1
  7. package/dist-lib/components/verkoop/CreateOrderPage.d.ts +1 -1
  8. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
  9. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.d.ts +1 -1
  10. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.d.ts.map +1 -1
  11. package/dist-lib/components/verkoop/EditLocalOrderDialog.d.ts +1 -1
  12. package/dist-lib/components/verkoop/EditLocalOrderDialog.d.ts.map +1 -1
  13. package/dist-lib/components/verkoop/EngineBufferCard.d.ts +1 -1
  14. package/dist-lib/components/verkoop/EngineBufferCard.d.ts.map +1 -1
  15. package/dist-lib/components/verkoop/InvoiceTimingSettings.d.ts +1 -1
  16. package/dist-lib/components/verkoop/InvoiceTimingSettings.d.ts.map +1 -1
  17. package/dist-lib/components/verkoop/OrderAfleverbrievenCard.d.ts +1 -1
  18. package/dist-lib/components/verkoop/OrderAfleverbrievenCard.d.ts.map +1 -1
  19. package/dist-lib/components/verkoop/OrderAfleverbrievenCard.js +5 -5
  20. package/dist-lib/components/verkoop/OrderCarrierPicker.d.ts +1 -1
  21. package/dist-lib/components/verkoop/OrderCarrierPicker.d.ts.map +1 -1
  22. package/dist-lib/components/verkoop/OrderControlStatusCard.d.ts +1 -1
  23. package/dist-lib/components/verkoop/OrderControlStatusCard.d.ts.map +1 -1
  24. package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts +1 -1
  25. package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -1
  26. package/dist-lib/components/verkoop/OrderPickupMomentPicker.d.ts +1 -1
  27. package/dist-lib/components/verkoop/OrderPickupMomentPicker.d.ts.map +1 -1
  28. package/dist-lib/components/verkoop/OrderShipmentsCard.d.ts +1 -1
  29. package/dist-lib/components/verkoop/OrderShipmentsCard.d.ts.map +1 -1
  30. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts +1 -1
  31. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts.map +1 -1
  32. package/dist-lib/components/verkoop/SalesOrderDetailPage.js +6 -6
  33. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts +1 -1
  34. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
  35. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.d.ts +1 -1
  36. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.d.ts.map +1 -1
  37. package/dist-lib/components/verkoop/SalesOrdersFilters.d.ts +1 -1
  38. package/dist-lib/components/verkoop/SalesOrdersFilters.d.ts.map +1 -1
  39. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts +2 -1
  40. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  41. package/dist-lib/components/verkoop/SalesOrdersTable.js +17 -4
  42. package/dist-lib/components/verkoop/SearchableSelect.d.ts +1 -1
  43. package/dist-lib/components/verkoop/SearchableSelect.d.ts.map +1 -1
  44. package/dist-lib/components/verkoop/StockConsumptionAlertsBanner.d.ts +1 -1
  45. package/dist-lib/components/verkoop/StockConsumptionAlertsBanner.d.ts.map +1 -1
  46. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts +1 -1
  47. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
  48. package/dist-lib/components/verkoop/WeekCalendar.d.ts +1 -1
  49. package/dist-lib/components/verkoop/WeekCalendar.d.ts.map +1 -1
  50. package/dist-lib/integrations/supabase/auth-middleware.d.ts +596 -10
  51. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  52. package/dist-lib/integrations/supabase/client.d.ts +596 -10
  53. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  54. package/dist-lib/integrations/supabase/client.js +1 -1
  55. package/dist-lib/integrations/supabase/client.server.d.ts +596 -10
  56. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  57. package/dist-lib/integrations/supabase/client.server.js +1 -1
  58. package/dist-lib/integrations/supabase/types.d.ts +598 -10
  59. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  60. package/dist-lib/lib/cart-lock.functions.d.ts +596 -10
  61. package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
  62. package/dist-lib/lib/cart-planning-settings.functions.d.ts +6556 -110
  63. package/dist-lib/lib/cart-planning-settings.functions.d.ts.map +1 -1
  64. package/dist-lib/lib/cart-replan.functions.d.ts +1788 -30
  65. package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
  66. package/dist-lib/lib/create-order.functions.d.ts +10132 -170
  67. package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
  68. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +1788 -30
  69. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
  70. package/dist-lib/lib/floriday-gateway.server.d.ts +14 -0
  71. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
  72. package/dist-lib/lib/floriday-gateway.server.js +46 -0
  73. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +24465 -20
  74. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
  75. package/dist-lib/lib/floriday-logistic-labels.functions.js +209 -90
  76. package/dist-lib/lib/floriday-mirror.functions.d.ts +596 -10
  77. package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
  78. package/dist-lib/lib/floriday-public.functions.d.ts +4172 -70
  79. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  80. package/dist-lib/lib/floriday-sync.d.ts.map +1 -1
  81. package/dist-lib/lib/floriday-writes.functions.d.ts +2980 -50
  82. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  83. package/dist-lib/lib/invoice-timing.functions.d.ts +2384 -40
  84. package/dist-lib/lib/invoice-timing.functions.d.ts.map +1 -1
  85. package/dist-lib/lib/load-carriers.functions.d.ts +596 -10
  86. package/dist-lib/lib/load-carriers.functions.d.ts.map +1 -1
  87. package/dist-lib/lib/logistics-engine.functions.d.ts +2995 -55
  88. package/dist-lib/lib/logistics-engine.functions.d.ts.map +1 -1
  89. package/dist-lib/lib/logistics-stock.functions.d.ts +1192 -20
  90. package/dist-lib/lib/logistics-stock.functions.d.ts.map +1 -1
  91. package/dist-lib/lib/order-addresses.functions.d.ts +596 -10
  92. package/dist-lib/lib/order-addresses.functions.d.ts.map +1 -1
  93. package/dist-lib/lib/order-buffers.functions.d.ts +1788 -30
  94. package/dist-lib/lib/order-buffers.functions.d.ts.map +1 -1
  95. package/dist-lib/lib/order-carrier.functions.d.ts +1192 -20
  96. package/dist-lib/lib/order-carrier.functions.d.ts.map +1 -1
  97. package/dist-lib/lib/order-pickup.functions.d.ts +1192 -20
  98. package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
  99. package/dist-lib/lib/order-shipments.functions.d.ts +2384 -40
  100. package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
  101. package/dist-lib/lib/salesorders-list.functions.d.ts +596 -10
  102. package/dist-lib/lib/salesorders-list.functions.d.ts.map +1 -1
  103. package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
  104. package/dist-lib/lib/salesorders-list.server.js +43 -1
  105. package/dist-lib/lib/salesorders-mapping.d.ts +8 -0
  106. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
  107. package/dist-lib/lib/salesorders.functions.d.ts +2980 -50
  108. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  109. package/dist-lib/lib/stock-consumption-alerts.functions.d.ts +1192 -20
  110. package/dist-lib/lib/stock-consumption-alerts.functions.d.ts.map +1 -1
  111. package/dist-lib/lib/tradeitems.functions.d.ts +596 -10
  112. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
  113. package/dist-lib/lib/useSalesOrderDetailQuery.d.ts +1 -1
  114. package/dist-lib/lib/useSalesOrderDetailQuery.d.ts.map +1 -1
  115. package/dist-lib/lib/useSalesOrdersQuery.d.ts +1 -1
  116. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
  117. package/package.json +1 -1
  118. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts +0 -9
  119. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts.map +0 -1
  120. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.js +0 -26
  121. package/dist-lib/components/karindeling/AfmetingenTab.d.ts +0 -6
  122. package/dist-lib/components/karindeling/AfmetingenTab.d.ts.map +0 -1
  123. package/dist-lib/components/karindeling/AfmetingenTab.js +0 -128
  124. package/dist-lib/components/karindeling/KarindelingPage.d.ts +0 -2
  125. package/dist-lib/components/karindeling/KarindelingPage.d.ts.map +0 -1
  126. package/dist-lib/components/karindeling/KarindelingPage.js +0 -102
  127. package/dist-lib/components/ui/dropdown-menu.d.ts +0 -9
  128. package/dist-lib/components/ui/dropdown-menu.d.ts.map +0 -1
  129. package/dist-lib/components/ui/dropdown-menu.js +0 -14
  130. package/dist-lib/components/verkoop/CreateOrderDialog.d.ts +0 -5
  131. package/dist-lib/components/verkoop/CreateOrderDialog.d.ts.map +0 -1
  132. package/dist-lib/components/verkoop/CreateOrderDialog.js +0 -33
  133. package/dist-lib/lib/floriday/floriday.server.d.ts +0 -14
  134. package/dist-lib/lib/floriday/floriday.server.d.ts.map +0 -1
  135. package/dist-lib/lib/floriday/floriday.server.js +0 -92
  136. package/dist-lib/lib/floriday/sales-orders.functions.d.ts +0 -4736
  137. package/dist-lib/lib/floriday/sales-orders.functions.d.ts.map +0 -1
  138. package/dist-lib/lib/floriday/sales-orders.functions.js +0 -193
@@ -1,12 +1,32 @@
1
- // Server functions for Floriday logistic labels (afleverbrieven).
2
- // - Lazy fetch: PDF's worden opgehaald zodra iemand ze opent in de UI.
3
- // - Cache in Supabase Storage bucket "floriday-logistic-labels" (private).
4
- // - Metadata upsert in floriday_logistic_labels_cache.
1
+ // Server functions voor afleverbrieven.
2
+ //
3
+ // Eén waarheid: public.delivery_notes. Elke afleverbrief hangt daar aan onze
4
+ // eigen order (order_id + order_number, bijv. FO-2600033), ongeacht herkomst:
5
+ // - source 'floriday' : PDF opgehaald bij Floriday
6
+ // GET /fulfillment-orders/{fulfillmentOrderId}/logistic-labels → één PDF
7
+ // per fulfillment order; die PDF hoort bij álle codes van die order.
8
+ // - source 'engine' : PDF gegenereerd door onze logistieke engine
9
+ // (HO/handmatige en MO/mail-bestellingen) via registerDeliveryNote().
10
+ //
11
+ // PDF's staan in de private bucket "delivery-notes"; de UI krijgt een
12
+ // signed URL. Fouten worden NIET stil geslikt: statuscode + leesbare tekst
13
+ // komen mee naar de UI (423 = levering nog in concept, 404 = niet gevonden).
5
14
  import { createServerFn } from "@tanstack/react-start";
6
15
  import { z } from "zod";
7
16
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
8
- import { apiBaseFor } from "./floriday-gateway.server";
9
- const BUCKET = "floriday-logistic-labels";
17
+ import { florydayFetchBinary } from "./floriday-gateway.server";
18
+ import { resolveOrderRow } from "./order-row.server";
19
+ const BUCKET = "delivery-notes";
20
+ function foutTekst(status, detail) {
21
+ if (status === 423) {
22
+ return "Levering staat nog in concept — bevestig de levering in Floriday, daarna is de afleverbrief beschikbaar.";
23
+ }
24
+ if (status === 404)
25
+ return "Afleverbrief niet gevonden bij Floriday (404).";
26
+ if (status === 401 || status === 403)
27
+ return `Geen toegang tot Floriday (${status}).`;
28
+ return `Ophalen bij Floriday mislukt (${status})${detail ? `: ${detail}` : ""}.`;
29
+ }
10
30
  async function collectRefsForOrder(supabase, salesOrderId) {
11
31
  const { data, error } = await supabase
12
32
  .from("floriday_fulfillment_orders_cache")
@@ -14,7 +34,6 @@ async function collectRefsForOrder(supabase, salesOrderId) {
14
34
  .eq("is_deleted", false);
15
35
  if (error)
16
36
  throw new Error(`fulfillment orders lezen faalde: ${error.message}`);
17
- // env per connection
18
37
  const connIds = Array.from(new Set((data ?? []).map((r) => r.connection_id).filter(Boolean)));
19
38
  const envByConn = new Map();
20
39
  if (connIds.length > 0) {
@@ -50,7 +69,6 @@ async function collectRefsForOrder(supabase, salesOrderId) {
50
69
  });
51
70
  }
52
71
  }
53
- // dedupe on (fulfillmentOrderId, code) — keep first
54
72
  const seen = new Set();
55
73
  return refs.filter((r) => {
56
74
  const k = `${r.fulfillmentOrderId}:${r.code}`;
@@ -60,45 +78,29 @@ async function collectRefsForOrder(supabase, salesOrderId) {
60
78
  return true;
61
79
  });
62
80
  }
63
- async function fetchPdfFromFloriday(supabase, ref) {
81
+ /** Haalt de gecombineerde PDF van één fulfillment order op bij Floriday. */
82
+ async function fetchPdfForFulfillmentOrder(supabase, connectionId, fulfillmentOrderId) {
64
83
  const { data: conn, error } = await supabase
65
84
  .from("floriday_connections")
66
85
  .select("id, environment, api_key")
67
- .eq("id", ref.connectionId)
86
+ .eq("id", connectionId)
68
87
  .maybeSingle();
69
88
  if (error || !conn) {
70
- return { ok: false, status: 0, error: `connectie ${ref.connectionId} niet gevonden` };
89
+ return { ok: false, status: 0, error: `Floriday-connectie ${connectionId} niet gevonden.` };
71
90
  }
72
- // Eigen central-call: elke resource haalt haar eigen vers token op.
73
- const { getFlorydayTokenFromCentral } = await import("./floriday-central-token.server");
74
- const tokenRes = await getFlorydayTokenFromCentral(conn.environment ?? "staging");
75
- if (!tokenRes.ok) {
76
- return {
77
- ok: false,
78
- status: tokenRes.status,
79
- error: `central token faalde: ${tokenRes.raw.slice(0, 200)}`,
80
- };
81
- }
82
- const base = apiBaseFor(conn.environment);
83
- // Floriday endpoint: GET /logistic-labels/{fulfillmentOrderId}/{code} → application/pdf
84
- const url = `${base}/logistic-labels/${encodeURIComponent(ref.fulfillmentOrderId)}/${encodeURIComponent(ref.code)}`;
85
- const res = await fetch(url, {
91
+ const res = await florydayFetchBinary(supabase, conn, {
86
92
  method: "GET",
87
- headers: {
88
- Accept: "application/pdf",
89
- Authorization: `Bearer ${tokenRes.access_token}`,
90
- ...(conn.api_key ? { "x-api-key": conn.api_key } : {}),
91
- },
93
+ path: `/fulfillment-orders/${encodeURIComponent(fulfillmentOrderId)}/logistic-labels`,
94
+ accept: "application/pdf",
92
95
  });
93
96
  if (!res.ok) {
94
- return { ok: false, status: res.status, error: `Floriday ${res.status}` };
97
+ return { ok: false, status: res.status, error: foutTekst(res.status, res.rawText) };
95
98
  }
96
- const buf = new Uint8Array(await res.arrayBuffer());
97
99
  return {
98
100
  ok: true,
99
101
  status: res.status,
100
- bytes: buf,
101
- contentType: res.headers.get("content-type") ?? "application/pdf",
102
+ bytes: res.bytes ?? undefined,
103
+ contentType: res.contentType ?? "application/pdf",
102
104
  };
103
105
  }
104
106
  const ListSchema = z.object({ salesOrderId: z.string().min(1) });
@@ -107,75 +109,192 @@ export const listLogisticLabelsForOrder = createServerFn({ method: "POST" })
107
109
  .inputValidator((d) => ListSchema.parse(d))
108
110
  .handler(async ({ data, context }) => {
109
111
  const supabase = context.supabase;
112
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
113
+ // 1. Onze eigen order erbij zoeken (lokaal ordernummer koppelen).
114
+ let orderId = null;
115
+ let orderNumber = null;
116
+ try {
117
+ const row = await resolveOrderRow(supabase, data.salesOrderId);
118
+ orderId = row.id;
119
+ orderNumber = row.order_number;
120
+ }
121
+ catch {
122
+ /* order (nog) niet bekend — brief blijft dan zonder koppeling */
123
+ }
124
+ // 2. Al opgeslagen briefjes bij deze order.
125
+ const stored = new Map();
126
+ if (orderId) {
127
+ const { data: rows } = await supabase
128
+ .from("delivery_notes")
129
+ .select("*")
130
+ .eq("order_id", orderId);
131
+ for (const r of rows ?? []) {
132
+ stored.set(`${r.source}:${r.fulfillment_order_id ?? ""}:${r.label_code ?? ""}`, r);
133
+ }
134
+ }
135
+ // 3. Floriday-referenties bepalen en per fulfillment order één keer ophalen.
110
136
  const refs = await collectRefsForOrder(supabase, data.salesOrderId);
111
- if (refs.length === 0)
112
- return { labels: [] };
113
- // Load existing cache rows
114
- const keys = refs.map((r) => ({ fo: r.fulfillmentOrderId, code: r.code }));
115
- const { data: cacheRows } = await supabase
116
- .from("floriday_logistic_labels_cache")
117
- .select("fulfillment_order_id, logistic_label_code, storage_path, content_type, byte_size")
118
- .in("fulfillment_order_id", Array.from(new Set(keys.map((k) => k.fo))));
119
- const cacheMap = new Map();
120
- for (const c of cacheRows ?? []) {
121
- cacheMap.set(`${c.fulfillment_order_id}:${c.logistic_label_code}`, c);
137
+ const perFo = new Map();
138
+ for (const ref of refs) {
139
+ const key = `floriday:${ref.fulfillmentOrderId}:${ref.code}`;
140
+ const existing = stored.get(key);
141
+ if (existing?.storage_path)
142
+ continue; // al gecached
143
+ if (perFo.has(ref.fulfillmentOrderId))
144
+ continue; // deze order al geprobeerd
145
+ const res = await fetchPdfForFulfillmentOrder(supabase, ref.connectionId, ref.fulfillmentOrderId);
146
+ if (!res.ok || !res.bytes) {
147
+ perFo.set(ref.fulfillmentOrderId, { ok: false, status: res.status, error: res.error });
148
+ continue;
149
+ }
150
+ const path = `floriday/${ref.connectionId}/${ref.fulfillmentOrderId}.pdf`;
151
+ const { error: upErr } = await supabaseAdmin.storage.from(BUCKET).upload(path, res.bytes, {
152
+ contentType: res.contentType ?? "application/pdf",
153
+ upsert: true,
154
+ });
155
+ if (upErr) {
156
+ perFo.set(ref.fulfillmentOrderId, { ok: false, status: 0, error: `Opslaan van de PDF mislukt: ${upErr.message}` });
157
+ continue;
158
+ }
159
+ perFo.set(ref.fulfillmentOrderId, {
160
+ ok: true,
161
+ status: res.status,
162
+ storagePath: path,
163
+ contentType: res.contentType ?? "application/pdf",
164
+ byteSize: res.bytes.byteLength,
165
+ });
122
166
  }
123
- // For refs without cache row → fetch PDF now (lazy) using service role client.
124
- const missing = refs.filter((r) => !cacheMap.get(`${r.fulfillmentOrderId}:${r.code}`));
125
- if (missing.length > 0) {
126
- const { supabaseAdmin } = await import("../integrations/supabase/client.server");
127
- for (const ref of missing) {
128
- const res = await fetchPdfFromFloriday(supabase, ref);
129
- if (!res.ok || !res.bytes)
130
- continue; // silently skip; label may not be ready yet
131
- const path = `${ref.connectionId}/${ref.fulfillmentOrderId}/${ref.code}.pdf`;
132
- const { error: upErr } = await supabaseAdmin.storage
133
- .from(BUCKET)
134
- .upload(path, res.bytes, {
135
- contentType: res.contentType ?? "application/pdf",
136
- upsert: true,
137
- });
138
- if (upErr)
139
- continue;
140
- const row = {
141
- connection_id: ref.connectionId,
142
- fulfillment_order_id: ref.fulfillmentOrderId,
143
- logistic_label_code: ref.code,
144
- storage_path: path,
145
- content_type: res.contentType ?? "application/pdf",
146
- byte_size: res.bytes.byteLength,
167
+ // 4. delivery_notes bijwerken per code (dus per afleverbrief-regel).
168
+ for (const ref of refs) {
169
+ const key = `floriday:${ref.fulfillmentOrderId}:${ref.code}`;
170
+ const fetched = perFo.get(ref.fulfillmentOrderId);
171
+ const existing = stored.get(key);
172
+ const row = {
173
+ order_id: orderId,
174
+ order_number: orderNumber,
175
+ source: "floriday",
176
+ connection_id: ref.connectionId,
177
+ fulfillment_order_id: ref.fulfillmentOrderId,
178
+ label_code: ref.code,
179
+ letter: ref.letter,
180
+ load_carrier_index: ref.loadCarrierIndex,
181
+ load_carrier_type: ref.loadCarrierType,
182
+ packages: ref.packages,
183
+ storage_bucket: BUCKET,
184
+ };
185
+ if (fetched?.ok) {
186
+ Object.assign(row, {
187
+ storage_path: fetched.storagePath,
188
+ content_type: fetched.contentType,
189
+ byte_size: fetched.byteSize,
190
+ status: "available",
191
+ http_status: fetched.status,
192
+ error_message: null,
147
193
  fetched_at: new Date().toISOString(),
148
- };
149
- await supabaseAdmin
150
- .from("floriday_logistic_labels_cache")
151
- .upsert(row, { onConflict: "fulfillment_order_id,logistic_label_code" });
152
- cacheMap.set(`${ref.fulfillmentOrderId}:${ref.code}`, row);
194
+ });
195
+ }
196
+ else if (fetched) {
197
+ Object.assign(row, {
198
+ storage_path: existing?.storage_path ?? null,
199
+ status: existing?.storage_path ? "available" : "error",
200
+ http_status: fetched.status,
201
+ error_message: fetched.error ?? null,
202
+ });
203
+ }
204
+ else if (existing) {
205
+ Object.assign(row, {
206
+ storage_path: existing.storage_path,
207
+ content_type: existing.content_type,
208
+ byte_size: existing.byte_size,
209
+ status: "available",
210
+ });
153
211
  }
212
+ const { data: saved } = await supabaseAdmin
213
+ .from("delivery_notes")
214
+ .upsert(row, { onConflict: "fulfillment_order_id,label_code" })
215
+ .select("*")
216
+ .maybeSingle();
217
+ stored.set(key, saved ?? { ...existing, ...row });
154
218
  }
155
- // Build signed URLs and result
156
- const { supabaseAdmin } = await import("../integrations/supabase/client.server");
219
+ // 5. Resultaat opbouwen (Floriday + engine-brieven van deze order).
157
220
  const labels = [];
158
- for (const ref of refs) {
159
- const cache = cacheMap.get(`${ref.fulfillmentOrderId}:${ref.code}`);
221
+ for (const r of stored.values()) {
160
222
  let signedUrl = null;
161
- if (cache?.storage_path) {
223
+ if (r.storage_path) {
162
224
  const { data: signed } = await supabaseAdmin.storage
163
- .from(BUCKET)
164
- .createSignedUrl(cache.storage_path, 600);
225
+ .from(r.storage_bucket ?? BUCKET)
226
+ .createSignedUrl(r.storage_path, 600);
165
227
  signedUrl = signed?.signedUrl ?? null;
166
228
  }
167
229
  labels.push({
168
- code: ref.code,
169
- letter: ref.letter,
170
- loadCarrierIndex: ref.loadCarrierIndex,
171
- loadCarrierType: ref.loadCarrierType,
172
- packages: ref.packages,
173
- fulfillmentOrderId: ref.fulfillmentOrderId,
230
+ source: (r.source ?? "floriday"),
231
+ code: r.label_code ?? null,
232
+ letter: r.letter ?? null,
233
+ loadCarrierIndex: Number(r.load_carrier_index ?? 0),
234
+ loadCarrierType: r.load_carrier_type ?? null,
235
+ packages: Number(r.packages ?? 0),
236
+ fulfillmentOrderId: r.fulfillment_order_id ?? null,
237
+ orderNumber: r.order_number ?? orderNumber,
174
238
  hasPdf: !!signedUrl,
175
239
  signedUrl,
240
+ status: signedUrl ? "available" : (r.status ?? "pending"),
241
+ httpStatus: r.http_status ?? null,
242
+ errorMessage: signedUrl ? null : (r.error_message ?? null),
176
243
  });
177
244
  }
178
- labels.sort((a, b) => a.loadCarrierIndex - b.loadCarrierIndex ||
179
- (a.letter ?? "").localeCompare(b.letter ?? ""));
180
- return { labels };
245
+ labels.sort((a, b) => a.loadCarrierIndex - b.loadCarrierIndex || (a.letter ?? "").localeCompare(b.letter ?? ""));
246
+ return { labels, orderNumber };
247
+ });
248
+ /**
249
+ * Registreert een afleverbrief die buiten Floriday is gemaakt
250
+ * (HO/handmatige en MO/mail-bestellingen, gegenereerd door de logistieke engine).
251
+ * De PDF wordt in dezelfde bucket opgeslagen en aan ons ordernummer gekoppeld.
252
+ */
253
+ const RegisterSchema = z.object({
254
+ orderId: z.string().min(1),
255
+ pdfBase64: z.string().min(1),
256
+ code: z.string().min(1).optional(),
257
+ letter: z.string().optional(),
258
+ loadCarrierIndex: z.number().int().optional(),
259
+ loadCarrierType: z.string().optional(),
260
+ packages: z.number().int().optional(),
261
+ });
262
+ export const registerDeliveryNote = createServerFn({ method: "POST" })
263
+ .middleware([requireSupabaseAuth])
264
+ .inputValidator((d) => RegisterSchema.parse(d))
265
+ .handler(async ({ data, context }) => {
266
+ const supabase = context.supabase;
267
+ const order = await resolveOrderRow(supabase, data.orderId);
268
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
269
+ const binair = atob(data.pdfBase64);
270
+ const bytes = new Uint8Array(binair.length);
271
+ for (let i = 0; i < binair.length; i++)
272
+ bytes[i] = binair.charCodeAt(i);
273
+ const code = data.code ?? order.order_number ?? order.id;
274
+ const path = `engine/${order.id}/${code}.pdf`;
275
+ const { error: upErr } = await supabaseAdmin.storage.from(BUCKET).upload(path, bytes, {
276
+ contentType: "application/pdf",
277
+ upsert: true,
278
+ });
279
+ if (upErr)
280
+ throw new Error(`Opslaan van de afleverbrief mislukt: ${upErr.message}`);
281
+ const { error } = await supabaseAdmin.from("delivery_notes").insert({
282
+ order_id: order.id,
283
+ order_number: order.order_number,
284
+ source: "engine",
285
+ label_code: code,
286
+ letter: data.letter ?? null,
287
+ load_carrier_index: data.loadCarrierIndex ?? 0,
288
+ load_carrier_type: data.loadCarrierType ?? null,
289
+ packages: data.packages ?? 0,
290
+ storage_bucket: BUCKET,
291
+ storage_path: path,
292
+ content_type: "application/pdf",
293
+ byte_size: bytes.byteLength,
294
+ status: "available",
295
+ fetched_at: new Date().toISOString(),
296
+ });
297
+ if (error)
298
+ throw new Error(`Afleverbrief opslaan mislukt: ${error.message}`);
299
+ return { ok: true, orderNumber: order.order_number, storagePath: path };
181
300
  });