@flowselections/floriday-voorraad 1.0.15 → 1.0.17

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 (84) hide show
  1. package/dist-lib/components/voorraad/AdditionalPackagingDialog.d.ts +9 -0
  2. package/dist-lib/components/voorraad/AdditionalPackagingDialog.d.ts.map +1 -0
  3. package/dist-lib/components/voorraad/AdditionalPackagingDialog.js +51 -0
  4. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  5. package/dist-lib/components/voorraad/ArtikelWizard.js +411 -28
  6. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  7. package/dist-lib/components/voorraad/EditArtikelPage.js +78 -4
  8. package/dist-lib/components/voorraad/ExcelExportButton.d.ts +2 -1
  9. package/dist-lib/components/voorraad/ExcelExportButton.d.ts.map +1 -1
  10. package/dist-lib/components/voorraad/ExcelExportButton.js +24 -5
  11. package/dist-lib/components/voorraad/OrderHistoryCard.d.ts +8 -0
  12. package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -0
  13. package/dist-lib/components/voorraad/OrderHistoryCard.js +133 -0
  14. package/dist-lib/components/voorraad/PriceStatusBadge.d.ts +7 -0
  15. package/dist-lib/components/voorraad/PriceStatusBadge.d.ts.map +1 -0
  16. package/dist-lib/components/voorraad/PriceStatusBadge.js +67 -0
  17. package/dist-lib/components/voorraad/ProductLinkCard.d.ts +13 -0
  18. package/dist-lib/components/voorraad/ProductLinkCard.d.ts.map +1 -0
  19. package/dist-lib/components/voorraad/ProductLinkCard.js +159 -0
  20. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
  21. package/dist-lib/components/voorraad/StockBulkButtons.js +88 -7
  22. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts +2 -1
  23. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  24. package/dist-lib/components/voorraad/VoorraadStockCard.js +5 -4
  25. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  26. package/dist-lib/components/voorraad/VoorraadTable.js +6 -1
  27. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  28. package/dist-lib/hooks/useLocalTradeItems.js +4 -0
  29. package/dist-lib/hooks/useTradeItemSpec.d.ts.map +1 -1
  30. package/dist-lib/hooks/useTradeItemSpec.js +4 -1
  31. package/dist-lib/hooks/useTradeItemStock.d.ts +14 -1
  32. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  33. package/dist-lib/hooks/useTradeItemStock.js +76 -17
  34. package/dist-lib/hooks/useTradeItemSupplyLine.d.ts +21 -0
  35. package/dist-lib/hooks/useTradeItemSupplyLine.d.ts.map +1 -0
  36. package/dist-lib/hooks/useTradeItemSupplyLine.js +49 -0
  37. package/dist-lib/hooks/useVoorraadData.d.ts +5 -4
  38. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  39. package/dist-lib/hooks/useVoorraadData.js +73 -7
  40. package/dist-lib/index.d.ts +1 -0
  41. package/dist-lib/index.d.ts.map +1 -1
  42. package/dist-lib/index.js +1 -0
  43. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1307 -94
  44. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  45. package/dist-lib/integrations/supabase/client.d.ts +1307 -94
  46. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  47. package/dist-lib/integrations/supabase/client.server.d.ts +1307 -94
  48. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  49. package/dist-lib/integrations/supabase/types.d.ts +1328 -87
  50. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  51. package/dist-lib/lib/ensure-article-codes.d.ts +8 -0
  52. package/dist-lib/lib/ensure-article-codes.d.ts.map +1 -0
  53. package/dist-lib/lib/ensure-article-codes.js +78 -0
  54. package/dist-lib/lib/floricode-required-features.functions.d.ts +1313 -94
  55. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  56. package/dist-lib/lib/floricode-required-features.functions.js +20 -2
  57. package/dist-lib/lib/floriday-client.d.ts +6 -0
  58. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  59. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +11264 -0
  60. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -0
  61. package/dist-lib/lib/floriday-customer-offer.functions.js +175 -0
  62. package/dist-lib/lib/floriday-gateway.functions.d.ts +16129 -17
  63. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  64. package/dist-lib/lib/floriday-gateway.functions.js +150 -0
  65. package/dist-lib/lib/floriday-gateway.server.d.ts +22 -0
  66. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
  67. package/dist-lib/lib/floriday-gateway.server.js +3 -0
  68. package/dist-lib/lib/floriday-payload.d.ts +76 -8
  69. package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
  70. package/dist-lib/lib/floriday-payload.js +209 -39
  71. package/dist-lib/lib/floriday-supply-line.d.ts +53 -0
  72. package/dist-lib/lib/floriday-supply-line.d.ts.map +1 -0
  73. package/dist-lib/lib/floriday-supply-line.js +109 -0
  74. package/dist-lib/lib/floriday-warehouse.functions.d.ts +33638 -0
  75. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -0
  76. package/dist-lib/lib/floriday-warehouse.functions.js +98 -0
  77. package/dist-lib/lib/floriday-writes.functions.d.ts +2707 -281
  78. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  79. package/dist-lib/lib/floriday-writes.functions.js +39 -6
  80. package/dist-lib/lib/vrs-catalog.d.ts +27 -0
  81. package/dist-lib/lib/vrs-catalog.d.ts.map +1 -0
  82. package/dist-lib/lib/vrs-catalog.js +98 -0
  83. package/package.json +3 -2
  84. package/public/flowselections-assets/Voorraad/floriday-logo.png +0 -0
@@ -73,6 +73,11 @@ export const stepSchemas = {
73
73
  .array(z.object({ url: z.string().url() }))
74
74
  .min(1, "Minstens één foto verplicht voor Floriday"),
75
75
  }),
76
+ warehouse: z.object({
77
+ warehouseId: z
78
+ .string()
79
+ .uuid("Kies een warehouse (Floriday-warehouseId, uuid)"),
80
+ }),
76
81
  beladingen: z.object({
77
82
  packingConfigurations: z
78
83
  .array(z
@@ -109,6 +114,25 @@ export const stepSchemas = {
109
114
  message: "Kies jaarrond of selecteer minstens één week",
110
115
  path: ["weeks"],
111
116
  }),
117
+ aanbieden: z
118
+ .object({
119
+ offerForSale: z.boolean(),
120
+ salePricePerPiece: z
121
+ .number()
122
+ .refine((n) => Number.isFinite(n), { message: "Prijs moet een getal zijn" }),
123
+ saleStockPieces: z
124
+ .number()
125
+ .int("Voorraad moet een geheel getal zijn")
126
+ .min(0, "Voorraad kan niet negatief zijn"),
127
+ offerStartDate: z
128
+ .string()
129
+ .regex(/^\d{4}-\d{2}-\d{2}$/, "Ongeldige startdatum"),
130
+ })
131
+ .refine((v) => !v.offerForSale || v.salePricePerPiece > 0, {
132
+ message: "Vul een verkoopprijs > 0 in",
133
+ path: ["salePricePerPiece"],
134
+ })
135
+ .refine((v) => !v.offerForSale || Math.round(v.salePricePerPiece * 1e5) === v.salePricePerPiece * 1e5, { message: "Prijs mag maximaal 5 decimalen hebben", path: ["salePricePerPiece"] }),
112
136
  };
113
137
  export function validateStep(key, data) {
114
138
  switch (key) {
@@ -146,6 +170,12 @@ export function validateStep(key, data) {
146
170
  ? { ok: true }
147
171
  : { ok: false, errors: r.error.issues.map((i) => i.message) };
148
172
  }
173
+ case "warehouse": {
174
+ const r = stepSchemas.warehouse.safeParse(data);
175
+ return r.success
176
+ ? { ok: true }
177
+ : { ok: false, errors: r.error.issues.map((i) => i.message) };
178
+ }
149
179
  case "beladingen": {
150
180
  const r = stepSchemas.beladingen.safeParse(data);
151
181
  return r.success
@@ -158,25 +188,61 @@ export function validateStep(key, data) {
158
188
  ? { ok: true }
159
189
  : { ok: false, errors: r.error.issues.map((i) => i.message) };
160
190
  }
191
+ case "aanbieden": {
192
+ const r = stepSchemas.aanbieden.safeParse(data);
193
+ return r.success
194
+ ? { ok: true }
195
+ : { ok: false, errors: r.error.issues.map((i) => i.message) };
196
+ }
161
197
  case "eigenschappen":
162
198
  case "verpakking":
163
199
  case "samenstelling":
164
200
  return { ok: true };
165
201
  }
166
202
  }
203
+ // -------- CustomerOffer helper (direct-sale via /customer-offers) ---------
204
+ /**
205
+ * Bouwt de PackingConfigurationBase voor `AddCustomerOfferLine` op basis van
206
+ * de primaire packing uit de wizard. Gooit een leesbare fout wanneer de
207
+ * primaire packing ontbreekt of geen geldige VBN-fust heeft — een
208
+ * `POST /customer-offers` zonder dit veld levert een 400 op.
209
+ */
210
+ export function buildPrimaryPackingBase(d) {
211
+ const primary = d.packingConfigurations.find((p) => p.isPrimary) ??
212
+ d.packingConfigurations[0];
213
+ if (!primary) {
214
+ throw new Error("Primaire beladingsconfiguratie ontbreekt — voeg minstens één belading toe.");
215
+ }
216
+ const vbnPackageCode = toIntOrUndefined(primary.packagingType);
217
+ const customPackageId = isUuid(primary.packagingMaterialId)
218
+ ? primary.packagingMaterialId
219
+ : undefined;
220
+ if (!vbnPackageCode && !customPackageId) {
221
+ throw new Error("Primaire belading heeft geen geldige VBN-fust of custom package — kan aanbod niet aanmaken.");
222
+ }
223
+ return {
224
+ piecesPerPackage: Math.max(1, Math.min(9999, Number(primary.unitsPerPackage) || 1)),
225
+ vbnPackageCode: vbnPackageCode ?? 1,
226
+ customPackageId,
227
+ packagesPerLayer: Math.max(1, Math.min(9999, Number(primary.packagesPerLayer) || 1)),
228
+ layersPerLoadCarrier: Math.max(1, Math.min(9999, Number(primary.layersPerLoadCarrier) || 1)),
229
+ loadCarrier: normaliseLoadCarrier(primary.loadCarrier),
230
+ photoUrl: primary.photoUrl,
231
+ };
232
+ }
167
233
  const LOAD_CARRIER_VALUES = [
168
234
  "NONE", "AUCTION_TROLLEY", "DANISH_TROLLEY",
169
235
  "EURO_TROLLEY", "PALLET", "EURO_PALLET",
170
236
  ];
171
237
  function normaliseLoadCarrier(input) {
172
238
  const raw = (input ?? "").trim();
173
- if (/^\d+$/.test(raw))
174
- return Number(raw);
239
+ if (!raw)
240
+ return "NONE";
175
241
  const v = raw.toUpperCase().replace(/[\s-]+/g, "_");
176
242
  if (LOAD_CARRIER_VALUES.includes(v)) {
177
243
  return v;
178
244
  }
179
- return raw || "NONE";
245
+ throw new Error(`Ladingdrager "${raw}" is geen geldige Floriday-waarde. Kies opnieuw in de beladingdialoog (Stapelwagen, Deense container, Euro-rolcontainer, Pallet, Euro-pallet of Geen ladingdrager).`);
180
246
  }
181
247
  function toIntOrUndefined(v) {
182
248
  if (v === undefined || v === null || v === "")
@@ -184,29 +250,45 @@ function toIntOrUndefined(v) {
184
250
  const n = typeof v === "number" ? v : parseInt(String(v).trim(), 10);
185
251
  return Number.isFinite(n) ? n : undefined;
186
252
  }
253
+ function isUuid(v) {
254
+ return typeof v === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v);
255
+ }
256
+ /** Normaliseer whitespace conform Floriday-eis: trim + collapse dubbele whitespace. */
257
+ function normaliseWs(v) {
258
+ return v.trim().replace(/\s+/g, " ");
259
+ }
187
260
  export function buildPackingConfigPayload(p, primary = false) {
188
261
  const vbnPackageCode = toIntOrUndefined(p.packagingType);
189
- if (!vbnPackageCode) {
190
- throw new Error(`Fusttype (VBN package code) ontbreekt of is geen getal voor configuratie "${p.id}"`);
262
+ // customPackageId kan via packagingMaterialId meekomen (wizard sla eerder
263
+ // supplier-material op in dat veld). Alleen accepteren als het een geldig UUID is.
264
+ const customPackageId = isUuid(p.packagingMaterialId) ? p.packagingMaterialId : undefined;
265
+ if (!vbnPackageCode && !customPackageId) {
266
+ throw new Error(`Fusttype ontbreekt voor beladingsconfiguratie "${p.id}" — kies een VBN-fust of een custom package.`);
191
267
  }
268
+ const pkg = {};
269
+ if (vbnPackageCode)
270
+ pkg.vbnPackageCode = vbnPackageCode;
271
+ if (customPackageId)
272
+ pkg.customPackageId = customPackageId;
192
273
  const payload = {
193
274
  primary: primary || Boolean(p.isPrimary),
194
275
  piecesPerPackage: Math.max(1, Math.min(9999, Number(p.unitsPerPackage) || 1)),
195
- package: { vbnPackageCode },
276
+ package: pkg,
196
277
  packagesPerLayer: Math.max(1, Math.min(9999, Number(p.packagesPerLayer) || 1)),
197
278
  layersPerLoadCarrier: Math.max(1, Math.min(9999, Number(p.layersPerLoadCarrier) || 1)),
198
279
  loadCarrier: normaliseLoadCarrier(p.loadCarrier),
199
280
  isHiddenForDirectSales: false,
200
281
  };
201
- if (p.source === "SUPPLIER" && p.packagingSupplierId) {
202
- payload.packagingSupplierId = p.packagingSupplierId;
203
- if (p.packagingMaterialId) {
204
- payload.packagingSupplierMaterialId = p.packagingMaterialId;
205
- }
282
+ // Alleen geldige VRS-packaging UUIDs meesturen; supplierId-string uit de
283
+ // hardcoded lijst is GEEN uuid en hoort niet in de Floriday-payload.
284
+ if (p.source === "SUPPLIER" && isUuid(p.packagingSupplierId)) {
285
+ payload.floricodeVrsPackagingId = p.packagingSupplierId;
206
286
  }
207
287
  if (p.isCustomerSpecific && p.customerOrganizationIds.length > 0) {
208
- payload.customerOrganizationIds = p.customerOrganizationIds;
288
+ payload.allowedCustomerOrganizationIds = p.customerOrganizationIds;
209
289
  }
290
+ if (p.photoUrl)
291
+ payload.photoUrl = p.photoUrl;
210
292
  return payload;
211
293
  }
212
294
  function weeksToSeasonalPeriods(weeks) {
@@ -240,40 +322,108 @@ export function buildAddTradeItemPayload(d) {
240
322
  if (!vbnProductCode) {
241
323
  throw new Error("vbnProductCode moet een geheel getal zijn");
242
324
  }
243
- const characteristics = Object.entries(d.features)
244
- .filter(([code, value]) => code && value && String(value).trim().length > 0)
245
- .map(([vbnCode, vbnValueCode]) => ({
246
- vbnCode: String(vbnCode).trim().slice(0, 3),
247
- vbnValueCode: String(vbnValueCode).trim().slice(0, 3),
248
- }));
325
+ // Characteristics: spec vereist vbnCode en vbnValueCode lengte 1..3.
326
+ // Stille truncate maskeert data-corruptie strict valideren.
327
+ const characteristics = [];
328
+ for (const [code, value] of Object.entries(d.features)) {
329
+ const c = String(code ?? "").trim();
330
+ const v = String(value ?? "").trim();
331
+ if (!c || !v)
332
+ continue;
333
+ if (c.length > 3) {
334
+ throw new Error(`Karakteristiek "${c}": vbnCode mag max 3 tekens zijn`);
335
+ }
336
+ if (v.length > 3) {
337
+ throw new Error(`Karakteristiek "${c}": waarde "${v}" mag max 3 tekens zijn`);
338
+ }
339
+ characteristics.push({ vbnCode: c, vbnValueCode: v });
340
+ }
249
341
  const botanicalNames = (d.botanicalName || "")
250
342
  .split(/[;,\n]/).map((s) => s.trim()).filter(Boolean);
251
- const countryOfOriginIsoCodes = (d.countryOfOrigin || "")
343
+ // Land-van-herkomst: combineer de multi-select uit Plantenpaspoort met
344
+ // karakteristiek S62 (Land van herkomst) die Floriday altijd verplicht
345
+ // stelt. S62 wordt bovenaan geplaatst zodat Floriday hem als primair
346
+ // herkomstland registreert.
347
+ const rawCountries = (d.countryOfOrigin || "")
252
348
  .split(/[,;\s]+/).map((s) => s.trim().toUpperCase()).filter(Boolean);
349
+ const s62 = String(d.features?.["S62"] ?? "").trim().toUpperCase();
350
+ if (s62) {
351
+ if (!/^[A-Z]{2}$/.test(s62)) {
352
+ throw new Error(`Karakteristiek S62 (Land van herkomst): "${s62}" is geen geldige ISO 3166-1 alpha-2 code.`);
353
+ }
354
+ const idx = rawCountries.indexOf(s62);
355
+ if (idx > 0)
356
+ rawCountries.splice(idx, 1);
357
+ if (rawCountries[0] !== s62)
358
+ rawCountries.unshift(s62);
359
+ }
360
+ const invalidCountries = rawCountries.filter((c) => !/^[A-Z]{2}$/.test(c));
361
+ if (invalidCountries.length > 0) {
362
+ throw new Error(`Ongeldige landcode(s): ${invalidCountries.join(", ")} — gebruik ISO 3166-1 alpha-2 (2 letters).`);
363
+ }
364
+ const countryOfOriginIsoCodes = rawCountries;
365
+ // S98 (Kwaliteitsgroep) wordt door Floriday server-side altijd verplicht
366
+ // gesteld. Voorkom een 400 door dit vooraf te valideren met een duidelijke
367
+ // Nederlandstalige foutmelding.
368
+ const s98 = String(d.features?.["S98"] ?? "").trim();
369
+ if (!s98) {
370
+ throw new Error("Karakteristiek S98 (Kwaliteitsgroep) is verplicht — kies een waarde in stap Eigenschappen.");
371
+ }
253
372
  const photos = d.images.map((img, i) => ({
254
373
  primary: i === 0,
255
374
  url: img.url,
256
- type: "WEBSHOPFRIENDLY",
375
+ type: img.type ?? "WEBSHOPFRIENDLY",
257
376
  sortIndex: i,
258
377
  }));
259
378
  const packingConfigurations = d.packingConfigurations.map((p, i) => buildPackingConfigPayload(p, i === 0));
260
379
  const isCustomerSpecific = d.articleType === "CUSTOMER_SPECIFIC";
380
+ const supplierArticleCode = normaliseWs(d.articleCode).slice(0, 13);
381
+ const tradeItemNameNl = normaliseWs(d.name).slice(0, 120);
382
+ if (!supplierArticleCode)
383
+ throw new Error("supplierArticleCode is verplicht");
384
+ if (!tradeItemNameNl)
385
+ throw new Error("tradeItemName.nl is verplicht");
386
+ const warehouseId = (d.warehouseId ?? "").trim();
387
+ if (!isUuid(warehouseId)) {
388
+ throw new Error("warehouseId is verplicht — kies een warehouse.");
389
+ }
390
+ // Alleen echte VRS-materiaal UUIDs meesturen; onze curated fallback-catalogus
391
+ // heeft ID's als "koen-pack:sleeve-50" die Floriday afwijst → filter eruit.
392
+ const additionalPackagingMaterialIds = (d.additionalPackaging ?? [])
393
+ .map((p) => (p.packagingMaterialId ?? "").trim())
394
+ .filter(isUuid);
261
395
  return {
262
396
  tradeItemId: crypto.randomUUID(),
263
- supplierArticleCode: d.articleCode.trim().slice(0, 13),
397
+ supplierArticleCode,
264
398
  articleGtin: d.gtin && d.gtin.trim().length === 13 ? d.gtin.trim() : undefined,
265
399
  vbnProductCode,
266
- tradeItemName: { nl: d.name.trim().slice(0, 120) },
400
+ tradeItemName: { nl: tradeItemNameNl },
267
401
  characteristics,
268
402
  customerOrganizationIds: isCustomerSpecific ? d.customerOrganizationIds : [],
269
- seasonalPeriods: d.yearRound ? [] : weeksToSeasonalPeriods(d.weeks),
403
+ seasonalPeriods: d.yearRound
404
+ ? [{ startWeek: 1, endWeek: 53 }]
405
+ : weeksToSeasonalPeriods(d.weeks),
270
406
  photos,
407
+ warehouseId,
271
408
  packingConfigurations,
272
409
  botanicalNames: botanicalNames.length ? botanicalNames : undefined,
273
410
  countryOfOriginIsoCodes: countryOfOriginIsoCodes.length ? countryOfOriginIsoCodes : undefined,
411
+ additionalPackagingMaterialIds: additionalPackagingMaterialIds.length
412
+ ? additionalPackagingMaterialIds
413
+ : undefined,
274
414
  isHiddenInCatalog: isCustomerSpecific ? true : !d.visibleInExplorer,
275
415
  };
276
416
  }
417
+ /**
418
+ * Bouwt de TradeItemUpdate-payload (PUT /trade-items/{id}) door
419
+ * `tradeItemId` en `supplierArticleCode` uit de create-payload te strippen.
420
+ * Alle andere validatie-regels blijven identiek.
421
+ */
422
+ export function buildUpdateTradeItemPayload(d) {
423
+ const full = buildAddTradeItemPayload(d);
424
+ const { tradeItemId: _id, supplierArticleCode: _code, warehouseId: _warehouseId, ...rest } = full;
425
+ return rest;
426
+ }
277
427
  export const emptyWizardData = {
278
428
  productGroup: null,
279
429
  vbnCode: "",
@@ -292,10 +442,15 @@ export const emptyWizardData = {
292
442
  features: {},
293
443
  customerOrganizationIds: [],
294
444
  images: [],
445
+ warehouseId: "",
295
446
  packingConfigurations: [],
296
447
  additionalPackaging: [],
297
448
  yearRound: true,
298
449
  weeks: [],
450
+ offerForSale: true,
451
+ salePricePerPiece: 0,
452
+ saleStockPieces: 0,
453
+ offerStartDate: new Date().toISOString().slice(0, 10),
299
454
  };
300
455
  // -------- Reverse mapper: Floriday cache row -> WizardData ----------------
301
456
  // Used by the detail/edit page to hydrate the wizard from an existing
@@ -369,20 +524,28 @@ export function floridayCacheToWizardData(raw) {
369
524
  const packings = Array.isArray(raw.packingConfigurations)
370
525
  ? raw.packingConfigurations
371
526
  : [];
372
- const packingConfigurations = packings.map((p, i) => ({
373
- id: `existing-${i}`,
374
- source: p?.packagingSupplierId ? "SUPPLIER" : "VBN",
375
- packagingType: String(p?.package?.vbnPackageCode ?? ""),
376
- loadCarrier: String(p?.loadCarrier ?? "NONE"),
377
- packagingSupplierId: p?.packagingSupplierId ?? undefined,
378
- packagingMaterialId: p?.packagingSupplierMaterialId ?? undefined,
379
- unitsPerPackage: Number(p?.piecesPerPackage ?? 1),
380
- packagesPerLayer: Number(p?.packagesPerLayer ?? 1),
381
- layersPerLoadCarrier: Number(p?.layersPerLoadCarrier ?? 1),
382
- isCustomerSpecific: Array.isArray(p?.customerOrganizationIds) && p.customerOrganizationIds.length > 0,
383
- customerOrganizationIds: Array.isArray(p?.customerOrganizationIds) ? p.customerOrganizationIds : [],
384
- isPrimary: Boolean(p?.primary) || i === 0,
385
- }));
527
+ const packingConfigurations = packings.map((p, i) => {
528
+ const vrsId = p?.floricodeVrsPackagingId ?? p?.packagingSupplierId ?? undefined;
529
+ const allowed = Array.isArray(p?.allowedCustomerOrganizationIds)
530
+ ? p.allowedCustomerOrganizationIds
531
+ : Array.isArray(p?.customerOrganizationIds)
532
+ ? p.customerOrganizationIds
533
+ : [];
534
+ return {
535
+ id: `existing-${i}`,
536
+ source: vrsId ? "SUPPLIER" : "VBN",
537
+ packagingType: String(p?.package?.vbnPackageCode ?? ""),
538
+ loadCarrier: String(p?.loadCarrier ?? "NONE"),
539
+ packagingSupplierId: vrsId ?? undefined,
540
+ packagingMaterialId: p?.package?.customPackageId ?? p?.packagingSupplierMaterialId ?? undefined,
541
+ unitsPerPackage: Number(p?.piecesPerPackage ?? 1),
542
+ packagesPerLayer: Number(p?.packagesPerLayer ?? 1),
543
+ layersPerLoadCarrier: Number(p?.layersPerLoadCarrier ?? 1),
544
+ isCustomerSpecific: allowed.length > 0,
545
+ customerOrganizationIds: allowed,
546
+ isPrimary: Boolean(p?.primary) || i === 0,
547
+ };
548
+ });
386
549
  const customerOrgIds = Array.isArray(raw.customerOrganizationIds)
387
550
  ? raw.customerOrganizationIds
388
551
  : [];
@@ -414,9 +577,16 @@ export function floridayCacheToWizardData(raw) {
414
577
  features,
415
578
  customerOrganizationIds: customerOrgIds,
416
579
  images,
580
+ warehouseId: String(raw.warehouseId ?? raw.warehouse?.warehouseId ?? ""),
417
581
  packingConfigurations,
418
582
  additionalPackaging: [],
419
- yearRound: weeks.length === 0,
420
- weeks,
583
+ yearRound: weeks.length === 0 || weeks.length >= 52,
584
+ weeks: weeks.length >= 52 ? [] : weeks,
585
+ // Reeds gepubliceerd via de wizard — commerciële velden staan uit voor edit,
586
+ // want een bestaand CustomerOffer wordt hier niet opnieuw aangemaakt.
587
+ offerForSale: false,
588
+ salePricePerPiece: 0,
589
+ saleStockPieces: 0,
590
+ offerStartDate: new Date().toISOString().slice(0, 10),
421
591
  };
422
592
  }
@@ -0,0 +1,53 @@
1
+ export type SupplyType = "CATALOG_PRICE" | "BATCH_PRICE" | "WEEKLY_SUPPLY_LINE";
2
+ export interface PostSupplyLineInput {
3
+ tradeItemId: string;
4
+ pricePerPiece: number;
5
+ availableQuantity: number;
6
+ packingConfigurationId?: string;
7
+ supplyType?: SupplyType;
8
+ currency?: string;
9
+ salesUnit?: string;
10
+ periodStart?: string;
11
+ periodEnd?: string;
12
+ supplyLineId?: string;
13
+ customerOrganizationIds?: string[];
14
+ environment?: "staging" | "live";
15
+ }
16
+ export interface PostSupplyLineResult {
17
+ ok: boolean;
18
+ /** Alleen bij WEEKLY_SUPPLY_LINE / BATCH_PRICE. */
19
+ supplyLineId?: string;
20
+ /** Nieuwe gateway: "WEEKLY_BASE" | "WEEKLY_SUPPLY_LINE" | "BATCH". */
21
+ endpointFamily?: string;
22
+ /** Alleen bij WEEKLY_BASE. */
23
+ year?: number;
24
+ week?: number;
25
+ steps?: Array<{
26
+ name: string;
27
+ ok: boolean;
28
+ error?: string;
29
+ }>;
30
+ environment?: string;
31
+ status?: number;
32
+ error?: string;
33
+ }
34
+ export declare function postSupplyLine(input: PostSupplyLineInput): Promise<PostSupplyLineResult>;
35
+ /**
36
+ * Push naar Floriday en persisteer resultaat lokaal. Voor WEEKLY_BASE
37
+ * wordt geen supply_line_id opgeslagen (bestaat niet in Floriday), voor
38
+ * WEEKLY_SUPPLY_LINE / BATCH wél zodat toekomstige updates herprijzen
39
+ * i.p.v. duplicaten aanmaken.
40
+ */
41
+ export declare function upsertSupplyLineForTradeItem(params: {
42
+ connectionId: string;
43
+ tradeItemId: string;
44
+ pricePerPiece: number;
45
+ availableQuantity: number;
46
+ packingConfigurationId?: string;
47
+ customerOrganizationIds?: string[];
48
+ supplyType?: SupplyType;
49
+ currency?: string;
50
+ periodStart?: string;
51
+ periodEnd?: string;
52
+ }): Promise<PostSupplyLineResult>;
53
+ //# sourceMappingURL=floriday-supply-line.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floriday-supply-line.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-supply-line.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,aAAa,GAAG,oBAAoB,CAAC;AAEhF,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAO/B;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAqFhC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Client-side helper voor het pushen van een Floriday supply-line via de
3
+ * externe Authenticatie-module gateway. Zie docs/floriday-integratie.md.
4
+ *
5
+ * Sinds de gateway-herschrijving:
6
+ * - CATALOG_PRICE → Weekly Base Supply + Continuous Stock (per trade item,
7
+ * ISO-jaar/-week). Er is GEEN `supplyLineId`; identificatie loopt via
8
+ * `tradeItemId + year + week`.
9
+ * - WEEKLY_SUPPLY_LINE → klant-specifiek; wél `supplyLineId`.
10
+ * - BATCH_PRICE → apart batch-endpoint, wél `supplyLineId`.
11
+ */
12
+ import { supabase } from "../integrations/supabase/client";
13
+ import { gatewaySupplyLineFn } from "./floriday-gateway.functions";
14
+ export async function postSupplyLine(input) {
15
+ const { environment: _env, ...rest } = input;
16
+ const result = (await gatewaySupplyLineFn({ data: rest }));
17
+ if (!result?.ok) {
18
+ throw new Error(result?.error ?? "Supply-line gateway: onbekende fout");
19
+ }
20
+ return result;
21
+ }
22
+ /**
23
+ * Push naar Floriday en persisteer resultaat lokaal. Voor WEEKLY_BASE
24
+ * wordt geen supply_line_id opgeslagen (bestaat niet in Floriday), voor
25
+ * WEEKLY_SUPPLY_LINE / BATCH wél zodat toekomstige updates herprijzen
26
+ * i.p.v. duplicaten aanmaken.
27
+ */
28
+ export async function upsertSupplyLineForTradeItem(params) {
29
+ const userRes = await supabase.auth.getUser();
30
+ const userId = userRes.data.user?.id;
31
+ if (!userId)
32
+ throw new Error("Niet ingelogd");
33
+ const { data: existing } = await supabase
34
+ .from("trade_item_supply_lines")
35
+ .select("supply_line_id")
36
+ .eq("user_id", userId)
37
+ .eq("trade_item_id", params.tradeItemId)
38
+ .maybeSingle();
39
+ const existingId = existing?.supply_line_id ?? undefined;
40
+ let result;
41
+ try {
42
+ result = await postSupplyLine({
43
+ tradeItemId: params.tradeItemId,
44
+ pricePerPiece: params.pricePerPiece,
45
+ availableQuantity: params.availableQuantity,
46
+ packingConfigurationId: params.packingConfigurationId,
47
+ supplyLineId: existingId,
48
+ customerOrganizationIds: params.customerOrganizationIds,
49
+ supplyType: params.supplyType,
50
+ currency: params.currency,
51
+ periodStart: params.periodStart,
52
+ periodEnd: params.periodEnd,
53
+ });
54
+ }
55
+ catch (e) {
56
+ const msg = String(e?.message ?? e);
57
+ if (existingId && /403|Forbidden/i.test(msg)) {
58
+ await supabase
59
+ .from("trade_item_supply_lines")
60
+ .delete()
61
+ .eq("user_id", userId)
62
+ .eq("trade_item_id", params.tradeItemId);
63
+ result = await postSupplyLine({
64
+ tradeItemId: params.tradeItemId,
65
+ pricePerPiece: params.pricePerPiece,
66
+ availableQuantity: params.availableQuantity,
67
+ packingConfigurationId: params.packingConfigurationId,
68
+ customerOrganizationIds: params.customerOrganizationIds,
69
+ supplyType: params.supplyType,
70
+ currency: params.currency,
71
+ periodStart: params.periodStart,
72
+ periodEnd: params.periodEnd,
73
+ });
74
+ }
75
+ else {
76
+ throw e;
77
+ }
78
+ }
79
+ const supplyType = params.supplyType ??
80
+ (result.endpointFamily === "WEEKLY_SUPPLY_LINE"
81
+ ? "WEEKLY_SUPPLY_LINE"
82
+ : result.endpointFamily === "BATCH"
83
+ ? "BATCH_PRICE"
84
+ : "CATALOG_PRICE");
85
+ const row = {
86
+ user_id: userId,
87
+ connection_id: params.connectionId,
88
+ trade_item_id: params.tradeItemId,
89
+ supply_line_id: result.supplyLineId ?? null,
90
+ supply_type: supplyType,
91
+ price_per_piece: params.pricePerPiece,
92
+ currency: params.currency ?? "EUR",
93
+ sales_unit: "PIECE",
94
+ available_quantity: params.availableQuantity,
95
+ period_start: params.periodStart ?? null,
96
+ period_end: params.periodEnd ?? null,
97
+ packing_configuration_id: params.packingConfigurationId ?? null,
98
+ environment: result.environment ?? null,
99
+ last_status: result.status ?? null,
100
+ last_error: null,
101
+ endpoint_family: result.endpointFamily ?? null,
102
+ year: result.year ?? null,
103
+ week: result.week ?? null,
104
+ };
105
+ await supabase
106
+ .from("trade_item_supply_lines")
107
+ .upsert(row, { onConflict: "user_id,trade_item_id" });
108
+ return result;
109
+ }