@flowselections/floriday-voorraad 1.0.131 → 1.0.135

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 (36) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.js +36 -6
  3. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  4. package/dist-lib/hooks/useLocalTradeItems.js +4 -0
  5. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  6. package/dist-lib/hooks/useVoorraadData.js +2 -1
  7. package/dist-lib/integrations/supabase/types.d.ts +72 -0
  8. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  9. package/dist-lib/lib/__tests__/mirror-products.test.d.ts +2 -0
  10. package/dist-lib/lib/__tests__/mirror-products.test.d.ts.map +1 -0
  11. package/dist-lib/lib/__tests__/mirror-products.test.js +13 -0
  12. package/dist-lib/lib/__tests__/release-134.test.d.ts +2 -0
  13. package/dist-lib/lib/__tests__/release-134.test.d.ts.map +1 -0
  14. package/dist-lib/lib/__tests__/release-134.test.js +50 -0
  15. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  16. package/dist-lib/lib/floriday-warehouse.functions.js +4 -2
  17. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  18. package/dist-lib/lib/floriday-writes.functions.js +11 -10
  19. package/dist-lib/lib/idempotency-key.d.ts +11 -0
  20. package/dist-lib/lib/idempotency-key.d.ts.map +1 -0
  21. package/dist-lib/lib/idempotency-key.js +41 -0
  22. package/dist-lib/lib/mirror-products.d.ts +12 -0
  23. package/dist-lib/lib/mirror-products.d.ts.map +1 -0
  24. package/dist-lib/lib/mirror-products.js +10 -0
  25. package/dist-lib/lib/publish-errors.d.ts +1 -1
  26. package/dist-lib/lib/publish-errors.d.ts.map +1 -1
  27. package/dist-lib/lib/publish-errors.js +10 -0
  28. package/dist-lib/lib/publish-pipeline.js +1 -1
  29. package/dist-lib/lib/publish-status.d.ts +3 -1
  30. package/dist-lib/lib/publish-status.d.ts.map +1 -1
  31. package/dist-lib/lib/publish-status.js +4 -1
  32. package/dist-lib/lib/publish-verify.d.ts +25 -0
  33. package/dist-lib/lib/publish-verify.d.ts.map +1 -0
  34. package/dist-lib/lib/publish-verify.js +44 -0
  35. package/package.json +1 -1
  36. package/sql/floriday-voorraad-schema.sql +249 -70
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mirror-products.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirror-products.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/mirror-products.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { hideMirrorItems } from "../mirror-products.js";
3
+ describe("hideMirrorItems", () => {
4
+ it("verbergt een spiegel (id = Floriday-id van eigen artikel), ook met voorraad", () => {
5
+ const own = { productId: "27612767", floridayId: "ea7776dd", qty: 450 };
6
+ const mirror = { productId: "ea7776dd", floridayId: null, qty: 500 };
7
+ expect(hideMirrorItems([own, mirror])).toEqual([own]);
8
+ });
9
+ it("laat een spiegel staan zolang er geen canoniek artikel is", () => {
10
+ const mirror = { productId: "ea7776dd", floridayId: null };
11
+ expect(hideMirrorItems([mirror])).toEqual([mirror]);
12
+ });
13
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=release-134.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-134.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/release-134.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { contentIdempotencyKey } from "../idempotency-key.js";
3
+ import { compareAfterUpdate, verifyDifferenceMessage } from "../publish-verify.js";
4
+ import { publishOutcome } from "../publish-status.js";
5
+ import { translatePublishError } from "../publish-errors.js";
6
+ const id = "ea7776dd-cdee-46e3-ab2a-ffd970bd8d7b";
7
+ describe("1.0.134 idempotentiesleutel per inhoud", () => {
8
+ it("naam wijzigen geeft een nieuwe sleutel (nieuwe PUT)", async () => {
9
+ const a = await contentIdempotencyKey("inventory:trade-item:update", id, { tradeItemName: "TEST QA artikel 28-10" });
10
+ const b = await contentIdempotencyKey("inventory:trade-item:update", id, { tradeItemName: "TEST QA artikel 28-10 gewijzigd" });
11
+ expect(a).not.toBe(b);
12
+ });
13
+ it("dezelfde wijziging nog eens geeft dezelfde sleutel (geen tweede PUT)", async () => {
14
+ const a = await contentIdempotencyKey("inventory:trade-item:update", id, { tradeItemName: "X", vbnProductCode: 34407 });
15
+ const b = await contentIdempotencyKey("inventory:trade-item:update", id, { vbnProductCode: 34407, tradeItemName: "X" });
16
+ expect(a).toBe(b);
17
+ });
18
+ it("sleutel bevat geen vaste versie meer", async () => {
19
+ const k = await contentIdempotencyKey("inventory:trade-item:update", id, {});
20
+ expect(k).not.toMatch(/:v1$/);
21
+ expect(k).toMatch(new RegExp(`^inventory:trade-item:update:${id}:[0-9a-f]{64}$`));
22
+ });
23
+ });
24
+ describe("1.0.134 controle na update", () => {
25
+ const item = {
26
+ tradeItemName: "TEST QA artikel 28-10",
27
+ vbnProductCode: 34407,
28
+ packingConfigurations: [{ primary: true, piecesPerPackage: 12, packagesPerLayer: 5, layersPerLoadCarrier: 8, package: { vbnPackageCode: 412 }, loadCarrier: "DANISH_TROLLEY" }],
29
+ };
30
+ it("naamafwijking is een verschil met beide waarden", () => {
31
+ const d = compareAfterUpdate({ name: "TEST QA artikel 28-10 gewijzigd", vbnCode: 34407 }, item);
32
+ expect(d).toHaveLength(1);
33
+ expect(verifyDifferenceMessage(d)).toContain("«TEST QA artikel 28-10»");
34
+ expect(verifyDifferenceMessage(d)).toContain("«TEST QA artikel 28-10 gewijzigd»");
35
+ });
36
+ it("verpakkingsafwijking telt", () => {
37
+ const d = compareAfterUpdate({ packing: { piecesPerPackage: 6, packagesPerLayer: 5, layersPerLoadCarrier: 8, vbnPackageCode: 412, loadCarrier: "DANISH_TROLLEY" } }, item);
38
+ expect(d.map((x) => x.field)).toEqual(["piecesPerPackage"]);
39
+ });
40
+ it("gelijk = geen verschil", () => {
41
+ expect(compareAfterUpdate({ name: "TEST QA artikel 28-10", vbnCode: "34407" }, item)).toEqual([]);
42
+ });
43
+ it("afwijking na update = job mislukt", () => {
44
+ expect(publishOutcome([
45
+ { step_key: "trade-item", status: "done" },
46
+ { step_key: "verify", status: "failed", error_code: "VERIFY_MISMATCH" },
47
+ ])).toBe("failed");
48
+ expect(translatePublishError(new Error("VERIFY_MISMATCH:Naam wijkt af")).code).toBe("VERIFY_MISMATCH");
49
+ });
50
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"floriday-warehouse.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-warehouse.functions.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,gBAAgB,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAoCD,eAAO,MAAM,gBAAgB;;;;;;;;;KAgB1B,CAAC;AAEJ,eAAO,MAAM,iBAAiB;;;;0CAER,cAAc,qFAoBjC,CAAC;AAEJ,eAAO,MAAM,iBAAiB;;;;0CAER,cAAc,qFAqBjC,CAAC"}
1
+ {"version":3,"file":"floriday-warehouse.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-warehouse.functions.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,gBAAgB,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAoCD,eAAO,MAAM,gBAAgB;;;;;;;;;KAgB1B,CAAC;AAEJ,eAAO,MAAM,iBAAiB;;;;0CAER,cAAc,qFAqBjC,CAAC;AAEJ,eAAO,MAAM,iBAAiB;;;;0CAER,cAAc,qFAsBjC,CAAC"}
@@ -62,6 +62,7 @@ export const createWarehouseFn = createServerFn({ method: "POST" })
62
62
  .handler(async ({ data, context }) => safe("createWarehouse", async () => {
63
63
  const { resolveFloridayEnvironment } = await import("./floriday-env.server.js");
64
64
  const { gatewayWrite } = await import("./floriday-gateway.server.js");
65
+ const { contentIdempotencyKey } = await import("./idempotency-key.js");
65
66
  const environment = await resolveFloridayEnvironment(context.supabase);
66
67
  const body = toWarehouseBody(data);
67
68
  const res = await gatewayWrite({
@@ -71,7 +72,7 @@ export const createWarehouseFn = createServerFn({ method: "POST" })
71
72
  method: "POST",
72
73
  path: `/suppliers-api-2026v1/warehouses`,
73
74
  referenceId: data.warehouseId,
74
- idempotencyKey: `inventory:warehouse:create:${data.warehouseId}`,
75
+ idempotencyKey: await contentIdempotencyKey("inventory:warehouse:create", data.warehouseId, body),
75
76
  body,
76
77
  validateEnums: DEFAULT_VALIDATE_ENUMS,
77
78
  });
@@ -83,6 +84,7 @@ export const updateWarehouseFn = createServerFn({ method: "POST" })
83
84
  .handler(async ({ data, context }) => safe("updateWarehouse", async () => {
84
85
  const { resolveFloridayEnvironment } = await import("./floriday-env.server.js");
85
86
  const { gatewayWrite } = await import("./floriday-gateway.server.js");
87
+ const { contentIdempotencyKey } = await import("./idempotency-key.js");
86
88
  const environment = await resolveFloridayEnvironment(context.supabase);
87
89
  const body = toWarehouseBody(data);
88
90
  delete body.warehouseId;
@@ -93,7 +95,7 @@ export const updateWarehouseFn = createServerFn({ method: "POST" })
93
95
  method: "PUT",
94
96
  path: `/suppliers-api-2026v1/warehouses/${data.warehouseId}`,
95
97
  referenceId: data.warehouseId,
96
- idempotencyKey: `inventory:warehouse:update:${data.warehouseId}`,
98
+ idempotencyKey: await contentIdempotencyKey("inventory:warehouse:update", data.warehouseId, body),
97
99
  body,
98
100
  validateEnums: DEFAULT_VALIDATE_ENUMS,
99
101
  });
@@ -1 +1 @@
1
- {"version":3,"file":"floriday-writes.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-writes.functions.ts"],"names":[],"mappings":"AA4BA,KAAK,UAAU,GAAG;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAyBD,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAInE;AAwED,eAAO,MAAM,gBAAgB;;;;0CAEP,UAAU,sCAS7B,CAAC;AAEJ,eAAO,MAAM,iBAAiB;;;;0CAER,UAAU,sCAS7B,CAAC"}
1
+ {"version":3,"file":"floriday-writes.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-writes.functions.ts"],"names":[],"mappings":"AA+BA,KAAK,UAAU,GAAG;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAyBD,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAInE;AAsED,eAAO,MAAM,gBAAgB;;;;0CAEP,UAAU,sCAS7B,CAAC;AAEJ,eAAO,MAAM,iBAAiB;;;;0CAER,UAAU,sCAS7B,CAAC"}
@@ -9,12 +9,15 @@
9
9
  * `PUT /trade-items/{id}` dus één idempotency-guard werkt op een retry
10
10
  * direct na een create.
11
11
  * - method = "PUT" voor beide (de tradeItemId is client-side bekend).
12
- * - idempotencyKey expliciet meegestuurd op basis van tradeItemId+formVersion,
13
- * zodat we niet afhankelijk zijn van body-hashing aan de gateway-kant.
12
+ * - idempotencyKey = tradeItemId + sha256 van de genormaliseerde body: een
13
+ * herhaling van dezelfde wijziging blijft idempotent, een nieuwe wijziging
14
+ * krijgt een nieuwe sleutel (een vaste "v1"-sleutel liet de gateway het
15
+ * oude antwoord teruggeven zonder nieuwe PUT).
14
16
  */
15
17
  import { createServerFn } from "@tanstack/react-start";
16
18
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
17
19
  import { normalizeErrors } from "./normalize-errors-middleware.js";
20
+ import { contentIdempotencyKey } from "./idempotency-key.js";
18
21
  const DEFAULT_VALIDATE_ENUMS = [
19
22
  { field: "currency", listName: "Currency" },
20
23
  { field: "salesUnit", listName: "SalesUnit" },
@@ -78,8 +81,7 @@ async function safe(label, fn) {
78
81
  * (tradeItemId is deel van de body, niet van het pad).
79
82
  * Zie swagger operationId "AddTradeItem".
80
83
  */
81
- function buildCreatePayload(environment, data) {
82
- const formVersion = data.formVersion ?? "v1";
84
+ async function buildCreatePayload(environment, data) {
83
85
  // `warehouseId` is lokaal nodig voor voorraad/aanbod, maar is géén veld op
84
86
  // Floriday TradeItemBase. Niet meesturen naar /trade-items.
85
87
  const body = { ...data.values };
@@ -91,7 +93,7 @@ function buildCreatePayload(environment, data) {
91
93
  method: "POST",
92
94
  path: `/suppliers-api-2026v1/trade-items`,
93
95
  referenceId: data.tradeItemId,
94
- idempotencyKey: `inventory:trade-item:create:${data.tradeItemId}:${formVersion}`,
96
+ idempotencyKey: await contentIdempotencyKey("inventory:trade-item:create", data.tradeItemId, body),
95
97
  body,
96
98
  validateEnums: DEFAULT_VALIDATE_ENUMS,
97
99
  };
@@ -100,8 +102,7 @@ function buildCreatePayload(environment, data) {
100
102
  * UPDATE = PUT /suppliers-api-2026v1/trade-items/{tradeItemId} met TradeItemUpdate-body
101
103
  * (tradeItemId hoort NIET in de body). operationId "EditTradeItem".
102
104
  */
103
- function buildUpdatePayload(environment, data) {
104
- const formVersion = data.formVersion ?? "v1";
105
+ async function buildUpdatePayload(environment, data) {
105
106
  // Verwijder velden die niet in TradeItemUpdate horen (Floriday geeft 400 op
106
107
  // onbekende velden bij strict-parsing van sommige properties).
107
108
  const body = { ...data.values };
@@ -115,7 +116,7 @@ function buildUpdatePayload(environment, data) {
115
116
  method: "PUT",
116
117
  path: `/suppliers-api-2026v1/trade-items/${data.tradeItemId}`,
117
118
  referenceId: data.tradeItemId,
118
- idempotencyKey: `inventory:trade-item:update:${data.tradeItemId}:${formVersion}`,
119
+ idempotencyKey: await contentIdempotencyKey("inventory:trade-item:update", data.tradeItemId, body),
119
120
  body,
120
121
  validateEnums: DEFAULT_VALIDATE_ENUMS,
121
122
  };
@@ -127,7 +128,7 @@ export const publishTradeItem = createServerFn({ method: "POST" })
127
128
  const { resolveFloridayEnvironment } = await import("./floriday-env.server.js");
128
129
  const { gatewayWrite } = await import("./floriday-gateway.server.js");
129
130
  const environment = await resolveFloridayEnvironment(context.supabase);
130
- const res = await gatewayWrite(buildCreatePayload(environment, data));
131
+ const res = await gatewayWrite(await buildCreatePayload(environment, data));
131
132
  return res;
132
133
  }));
133
134
  export const updateTradeItemFn = createServerFn({ method: "POST" })
@@ -137,6 +138,6 @@ export const updateTradeItemFn = createServerFn({ method: "POST" })
137
138
  const { resolveFloridayEnvironment } = await import("./floriday-env.server.js");
138
139
  const { gatewayWrite } = await import("./floriday-gateway.server.js");
139
140
  const environment = await resolveFloridayEnvironment(context.supabase);
140
- const res = await gatewayWrite(buildUpdatePayload(environment, data));
141
+ const res = await gatewayWrite(await buildUpdatePayload(environment, data));
141
142
  return res;
142
143
  }));
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Idempotentiesleutel per inhoud: dezelfde wijziging → dezelfde sleutel (geen
3
+ * tweede schrijfactie), een andere wijziging → een nieuwe sleutel.
4
+ * Werkt op de server-runtime en in de browser (Web Crypto).
5
+ */
6
+ /** Stabiele weergave: sleutels gesorteerd, undefined weggelaten, strings getrimd. */
7
+ export declare function normalizeForKey(value: unknown): unknown;
8
+ export declare function sha256Hex(text: string): Promise<string>;
9
+ /** `${prefix}:${id}:${sha256(genormaliseerde body)}` */
10
+ export declare function contentIdempotencyKey(prefix: string, id: string, body: unknown): Promise<string>;
11
+ //# sourceMappingURL=idempotency-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-key.d.ts","sourceRoot":"","sources":["../../src/lib/idempotency-key.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmBvD;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK7D;AAED,wDAAwD;AACxD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,MAAM,CAAC,CAGjB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Idempotentiesleutel per inhoud: dezelfde wijziging → dezelfde sleutel (geen
3
+ * tweede schrijfactie), een andere wijziging → een nieuwe sleutel.
4
+ * Werkt op de server-runtime en in de browser (Web Crypto).
5
+ */
6
+ /** Stabiele weergave: sleutels gesorteerd, undefined weggelaten, strings getrimd. */
7
+ export function normalizeForKey(value) {
8
+ if (value === undefined)
9
+ return undefined;
10
+ if (value === null)
11
+ return null;
12
+ if (typeof value === "string")
13
+ return value.trim();
14
+ if (Array.isArray(value)) {
15
+ return value.map((v) => {
16
+ const n = normalizeForKey(v);
17
+ return n === undefined ? null : n;
18
+ });
19
+ }
20
+ if (typeof value === "object") {
21
+ const out = {};
22
+ for (const k of Object.keys(value).sort()) {
23
+ const n = normalizeForKey(value[k]);
24
+ if (n !== undefined)
25
+ out[k] = n;
26
+ }
27
+ return out;
28
+ }
29
+ return value;
30
+ }
31
+ export async function sha256Hex(text) {
32
+ const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
33
+ return Array.from(new Uint8Array(buf))
34
+ .map((b) => b.toString(16).padStart(2, "0"))
35
+ .join("");
36
+ }
37
+ /** `${prefix}:${id}:${sha256(genormaliseerde body)}` */
38
+ export async function contentIdempotencyKey(prefix, id, body) {
39
+ const hash = await sha256Hex(JSON.stringify(normalizeForKey(body) ?? null));
40
+ return `${prefix}:${id}:${hash}`;
41
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Spiegelproducten: een product waarvan het id gelijk is aan het Floriday-id
3
+ * van een ánder product (het eigen voorraadartikel). Zolang dat canonieke
4
+ * artikel bestaat, telt de spiegel niet mee in de voorraadlijst en tellingen.
5
+ * Zelfde regel als `voorraad_canoniek_artikel` (stap a) in de database.
6
+ */
7
+ export interface MirrorCandidate {
8
+ productId?: string | null;
9
+ floridayId?: string | null;
10
+ }
11
+ export declare function hideMirrorItems<T extends MirrorCandidate>(items: T[]): T[];
12
+ //# sourceMappingURL=mirror-products.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirror-products.d.ts","sourceRoot":"","sources":["../../src/lib/mirror-products.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAO1E"}
@@ -0,0 +1,10 @@
1
+ export function hideMirrorItems(items) {
2
+ const ownerFloridayIds = new Set();
3
+ for (const i of items) {
4
+ if (i.floridayId && i.productId && i.floridayId !== i.productId)
5
+ ownerFloridayIds.add(i.floridayId);
6
+ }
7
+ if (ownerFloridayIds.size === 0)
8
+ return items;
9
+ return items.filter((i) => !(i.productId && ownerFloridayIds.has(i.productId)));
10
+ }
@@ -6,7 +6,7 @@
6
6
  * Woorden als API, gateway, Floriday-intern jargon en modulenamen komen hier
7
7
  * bewust niet in voor.
8
8
  */
9
- export type PublishErrorCode = "MISSING_CHARACTERISTICS" | "NO_CONNECTION" | "WRONG_ENVIRONMENT" | "NOT_LOGGED_IN" | "INVALID_VALUE" | "DUPLICATE" | "NOT_FOUND" | "TEMPORARY" | "TIMEOUT" | "RATE_LIMIT" | "GATE_BLOCKED" | "UNKNOWN";
9
+ export type PublishErrorCode = "VERIFY_MISMATCH" | "MISSING_CHARACTERISTICS" | "NO_CONNECTION" | "WRONG_ENVIRONMENT" | "NOT_LOGGED_IN" | "INVALID_VALUE" | "DUPLICATE" | "NOT_FOUND" | "TEMPORARY" | "TIMEOUT" | "RATE_LIMIT" | "GATE_BLOCKED" | "UNKNOWN";
10
10
  export interface PublishErrorAction {
11
11
  label: string;
12
12
  /** Interne route waar de gebruiker het kan oplossen. */
@@ -1 +1 @@
1
- {"version":3,"file":"publish-errors.d.ts","sourceRoot":"","sources":["../../src/lib/publish-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,gBAAgB,GACxB,yBAAyB,GACzB,eAAe,GACf,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,WAAW,GACX,WAAW,GACX,WAAW,GACX,SAAS,GACT,YAAY,GACZ,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CAChB;AAiBD,8EAA8E;AAC9E,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAIR;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACvD,oBAAoB,CAwKtB;AAgBD,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxD;AAaD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK9C;AAED,QAAA,MAAM,aAAa,oFAAqF,CAAC;AACzG,KAAK,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAElD,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,GACV;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAkBrE"}
1
+ {"version":3,"file":"publish-errors.d.ts","sourceRoot":"","sources":["../../src/lib/publish-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,yBAAyB,GACzB,eAAe,GACf,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,WAAW,GACX,WAAW,GACX,WAAW,GACX,SAAS,GACT,YAAY,GACZ,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CAChB;AAiBD,8EAA8E;AAC9E,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAIR;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACvD,oBAAoB,CAmLtB;AAgBD,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxD;AAaD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK9C;AAED,QAAA,MAAM,aAAa,oFAAqF,CAAC;AACzG,KAAK,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAElD,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,GACV;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAkBrE"}
@@ -36,6 +36,16 @@ export function translatePublishError(error, opts) {
36
36
  : JSON.stringify(error ?? {});
37
37
  const raw = detail ?? "";
38
38
  const lower = raw.toLowerCase();
39
+ // Controle na update: afwijking tussen Floriday en ERP (zie publish-verify).
40
+ if (raw.startsWith("VERIFY_MISMATCH:")) {
41
+ return {
42
+ code: "VERIFY_MISMATCH",
43
+ message: raw.slice("VERIFY_MISMATCH:".length),
44
+ hint: "Publiceer opnieuw; blijft het verschil, controleer de Floriday-koppeling.",
45
+ retryable: false,
46
+ detail: raw,
47
+ };
48
+ }
39
49
  const missingCodes = extractMissingCodes(raw);
40
50
  if (missingCodes.length > 0) {
41
51
  const readable = describeCodes(missingCodes, opts?.characteristicLabels);
@@ -143,7 +143,7 @@ function sanitizeResult(value) {
143
143
  return { value: String(value) };
144
144
  }
145
145
  export async function finishPublishJob(jobId, patch) {
146
- const { data } = await steps().select("status, step_key").eq("job_id", jobId);
146
+ const { data } = await steps().select("status, step_key, error_code").eq("job_id", jobId);
147
147
  const rows = (data ?? []);
148
148
  const { publishOutcome } = await import("./publish-status.js");
149
149
  const status = publishOutcome(rows);
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Regels voor de publicatiestatus van een artikel (1.0.130).
3
3
  * - Een artikel met een Floriday-id telt als gepubliceerd.
4
- * - Een mislukte controle is een waarschuwing, geen mislukte publicatie.
4
+ * - Een mislukte controle is een waarschuwing, geen mislukte publicatie —
5
+ * behalve een afwijking na een update (VERIFY_MISMATCH): dan is de job mislukt.
5
6
  * - Wijzigingen gaan naar Floriday als er een Floriday-id is, tenzij er een
6
7
  * reden is waarom dat niet kan; die reden wordt dan getoond.
7
8
  */
@@ -9,6 +10,7 @@ export declare function isPublishedToFloriday(...ids: Array<string | null | unde
9
10
  export type StepOutcome = {
10
11
  step_key: string;
11
12
  status: string;
13
+ error_code?: string | null;
12
14
  };
13
15
  export type PublishOutcome = "failed" | "succeeded_with_warnings" | "succeeded";
14
16
  /** Alleen de artikelstap bepaalt of de publicatie mislukt is. */
@@ -1 +1 @@
1
- {"version":3,"file":"publish-status.d.ts","sourceRoot":"","sources":["../../src/lib/publish-status.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,qBAAqB,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,OAAO,CAEvF;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/D,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,yBAAyB,GAAG,WAAW,CAAC;AAEhF,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,cAAc,CAInE;AAED,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,aAAa,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,UAAU,CAWb"}
1
+ {"version":3,"file":"publish-status.d.ts","sourceRoot":"","sources":["../../src/lib/publish-status.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wBAAgB,qBAAqB,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,OAAO,CAEvF;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAC3F,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,yBAAyB,GAAG,WAAW,CAAC;AAEhF,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,cAAc,CAKnE;AAED,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,aAAa,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,UAAU,CAWb"}
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Regels voor de publicatiestatus van een artikel (1.0.130).
3
3
  * - Een artikel met een Floriday-id telt als gepubliceerd.
4
- * - Een mislukte controle is een waarschuwing, geen mislukte publicatie.
4
+ * - Een mislukte controle is een waarschuwing, geen mislukte publicatie —
5
+ * behalve een afwijking na een update (VERIFY_MISMATCH): dan is de job mislukt.
5
6
  * - Wijzigingen gaan naar Floriday als er een Floriday-id is, tenzij er een
6
7
  * reden is waarom dat niet kan; die reden wordt dan getoond.
7
8
  */
@@ -13,6 +14,8 @@ export function publishOutcome(steps) {
13
14
  const failed = steps.filter((s) => s.status === "failed");
14
15
  if (failed.some((s) => s.step_key === "trade-item"))
15
16
  return "failed";
17
+ if (failed.some((s) => s.step_key === "verify" && s.error_code === "VERIFY_MISMATCH"))
18
+ return "failed";
16
19
  return failed.length ? "succeeded_with_warnings" : "succeeded";
17
20
  }
18
21
  /** Wat het bewerkscherm belooft over het versturen van wijzigingen. */
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Controle na een update naar Floriday: naam, VBN-code en hoofdverpakking moeten
3
+ * gelijk zijn aan wat het ERP verstuurde. Een afwijking = de update is niet
4
+ * (volledig) aangekomen; de job moet dan mislukken en het artikel blijft "dirty".
5
+ */
6
+ export interface VerifyExpected {
7
+ name?: string | null;
8
+ vbnCode?: string | number | null;
9
+ packing?: {
10
+ piecesPerPackage?: number | null;
11
+ packagesPerLayer?: number | null;
12
+ layersPerLoadCarrier?: number | null;
13
+ vbnPackageCode?: number | string | null;
14
+ loadCarrier?: string | number | null;
15
+ } | null;
16
+ }
17
+ export interface VerifyDifference {
18
+ field: string;
19
+ label: string;
20
+ floriday: string;
21
+ erp: string;
22
+ }
23
+ export declare function compareAfterUpdate(expected: VerifyExpected, item: any): VerifyDifference[];
24
+ export declare function verifyDifferenceMessage(diffs: VerifyDifference[]): string;
25
+ //# sourceMappingURL=publish-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-verify.d.ts","sourceRoot":"","sources":["../../src/lib/publish-verify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE;QACR,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;QACxC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;KACtC,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAgBD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,GAAG,gBAAgB,EAAE,CAwB1F;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAKzE"}
@@ -0,0 +1,44 @@
1
+ const txt = (v) => (v === null || v === undefined ? "" : String(v).trim());
2
+ const shown = (v) => (v === "" ? "(leeg)" : v);
3
+ function remotePrimary(item) {
4
+ const list = Array.isArray(item?.packingConfigurations) ? item.packingConfigurations : [];
5
+ return list.find((c) => c && (c.primary === true || c.isPrimary === true)) ?? null;
6
+ }
7
+ function remoteLoadCarrier(p) {
8
+ const lc = p.loadCarrier;
9
+ if (lc && typeof lc === "object")
10
+ return txt(lc.loadCarrierType ?? lc.type ?? lc.code);
11
+ return txt(lc);
12
+ }
13
+ export function compareAfterUpdate(expected, item) {
14
+ const out = [];
15
+ const add = (field, label, floriday, erp) => {
16
+ const f = txt(floriday);
17
+ const e = txt(erp);
18
+ if (e === "")
19
+ return; // ERP stuurde niets: niets te vergelijken
20
+ if (f !== e)
21
+ out.push({ field, label, floriday: shown(f), erp: shown(e) });
22
+ };
23
+ add("name", "Naam", item?.tradeItemName ?? item?.name, expected.name);
24
+ add("vbnCode", "VBN-code", item?.vbnProductCode ?? item?.vbnCode, expected.vbnCode);
25
+ const exp = expected.packing;
26
+ if (exp) {
27
+ const p = remotePrimary(item);
28
+ if (!p) {
29
+ out.push({ field: "packing", label: "Hoofdverpakking", floriday: "(geen)", erp: "ingesteld" });
30
+ }
31
+ else {
32
+ add("piecesPerPackage", "Stuks per fust", p.piecesPerPackage, exp.piecesPerPackage);
33
+ add("packagesPerLayer", "Fusten per laag", p.packagesPerLayer, exp.packagesPerLayer);
34
+ add("layersPerLoadCarrier", "Lagen per ladingdrager", p.layersPerLoadCarrier, exp.layersPerLoadCarrier);
35
+ add("vbnPackageCode", "Fust", p.package?.vbnPackageCode ?? p.vbnPackageCode, exp.vbnPackageCode);
36
+ add("loadCarrier", "Ladingdrager", remoteLoadCarrier(p), exp.loadCarrier);
37
+ }
38
+ }
39
+ return out;
40
+ }
41
+ export function verifyDifferenceMessage(diffs) {
42
+ return ("Floriday heeft de wijziging niet (volledig) overgenomen: " +
43
+ diffs.map((d) => `${d.label} in Floriday: «${d.floriday}», in het ERP: «${d.erp}»`).join("; "));
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-voorraad",
3
- "version": "1.0.131",
3
+ "version": "1.0.135",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "main": "./dist-lib/index.js",