@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.
- package/dist-lib/components/voorraad/AdditionalPackagingDialog.d.ts +9 -0
- package/dist-lib/components/voorraad/AdditionalPackagingDialog.d.ts.map +1 -0
- package/dist-lib/components/voorraad/AdditionalPackagingDialog.js +51 -0
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +411 -28
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/EditArtikelPage.js +78 -4
- package/dist-lib/components/voorraad/ExcelExportButton.d.ts +2 -1
- package/dist-lib/components/voorraad/ExcelExportButton.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ExcelExportButton.js +24 -5
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts +8 -0
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/OrderHistoryCard.js +133 -0
- package/dist-lib/components/voorraad/PriceStatusBadge.d.ts +7 -0
- package/dist-lib/components/voorraad/PriceStatusBadge.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PriceStatusBadge.js +67 -0
- package/dist-lib/components/voorraad/ProductLinkCard.d.ts +13 -0
- package/dist-lib/components/voorraad/ProductLinkCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/ProductLinkCard.js +159 -0
- package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockBulkButtons.js +88 -7
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts +2 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.js +5 -4
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +6 -1
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +4 -0
- package/dist-lib/hooks/useTradeItemSpec.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemSpec.js +4 -1
- package/dist-lib/hooks/useTradeItemStock.d.ts +14 -1
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +76 -17
- package/dist-lib/hooks/useTradeItemSupplyLine.d.ts +21 -0
- package/dist-lib/hooks/useTradeItemSupplyLine.d.ts.map +1 -0
- package/dist-lib/hooks/useTradeItemSupplyLine.js +49 -0
- package/dist-lib/hooks/useVoorraadData.d.ts +5 -4
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +73 -7
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +1 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1307 -94
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +1307 -94
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1307 -94
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +1328 -87
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/ensure-article-codes.d.ts +8 -0
- package/dist-lib/lib/ensure-article-codes.d.ts.map +1 -0
- package/dist-lib/lib/ensure-article-codes.js +78 -0
- package/dist-lib/lib/floricode-required-features.functions.d.ts +1313 -94
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floricode-required-features.functions.js +20 -2
- package/dist-lib/lib/floriday-client.d.ts +6 -0
- package/dist-lib/lib/floriday-client.d.ts.map +1 -1
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +11264 -0
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-customer-offer.functions.js +175 -0
- package/dist-lib/lib/floriday-gateway.functions.d.ts +16129 -17
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.js +150 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts +22 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.server.js +3 -0
- package/dist-lib/lib/floriday-payload.d.ts +76 -8
- package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
- package/dist-lib/lib/floriday-payload.js +209 -39
- package/dist-lib/lib/floriday-supply-line.d.ts +53 -0
- package/dist-lib/lib/floriday-supply-line.d.ts.map +1 -0
- package/dist-lib/lib/floriday-supply-line.js +109 -0
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +33638 -0
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-warehouse.functions.js +98 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts +2707 -281
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.js +39 -6
- package/dist-lib/lib/vrs-catalog.d.ts +27 -0
- package/dist-lib/lib/vrs-catalog.d.ts.map +1 -0
- package/dist-lib/lib/vrs-catalog.js +98 -0
- package/package.json +3 -2
- 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 (
|
|
174
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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:
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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.
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
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
|
|
397
|
+
supplierArticleCode,
|
|
264
398
|
articleGtin: d.gtin && d.gtin.trim().length === 13 ? d.gtin.trim() : undefined,
|
|
265
399
|
vbnProductCode,
|
|
266
|
-
tradeItemName: { nl:
|
|
400
|
+
tradeItemName: { nl: tradeItemNameNl },
|
|
267
401
|
characteristics,
|
|
268
402
|
customerOrganizationIds: isCustomerSpecific ? d.customerOrganizationIds : [],
|
|
269
|
-
seasonalPeriods: d.yearRound
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
+
}
|