@flowselections/floriday-voorraad 1.0.113 → 1.0.117

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 (25) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.js +63 -7
  3. package/dist-lib/components/voorraad/PublishProgressCard.d.ts +5 -0
  4. package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -1
  5. package/dist-lib/components/voorraad/PublishProgressCard.js +16 -1
  6. package/dist-lib/components/voorraad/VoorraadPage.d.ts +13 -0
  7. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  8. package/dist-lib/components/voorraad/VoorraadPage.js +33 -20
  9. package/dist-lib/components/voorraad/__tests__/count-stock-free.test.d.ts +2 -0
  10. package/dist-lib/components/voorraad/__tests__/count-stock-free.test.d.ts.map +1 -0
  11. package/dist-lib/components/voorraad/__tests__/count-stock-free.test.js +12 -0
  12. package/dist-lib/integrations/supabase/types.d.ts +371 -0
  13. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  14. package/dist-lib/lib/__tests__/floricode-required-features.test.js +7 -3
  15. package/dist-lib/lib/__tests__/publish-114.test.d.ts +2 -0
  16. package/dist-lib/lib/__tests__/publish-114.test.d.ts.map +1 -0
  17. package/dist-lib/lib/__tests__/publish-114.test.js +31 -0
  18. package/dist-lib/lib/__tests__/publish-packing-block.test.d.ts +2 -0
  19. package/dist-lib/lib/__tests__/publish-packing-block.test.d.ts.map +1 -0
  20. package/dist-lib/lib/__tests__/publish-packing-block.test.js +14 -0
  21. package/dist-lib/lib/publish-errors.d.ts +15 -1
  22. package/dist-lib/lib/publish-errors.d.ts.map +1 -1
  23. package/dist-lib/lib/publish-errors.js +64 -2
  24. package/package.json +1 -1
  25. package/sql/AGENTS.md +1 -0
@@ -155,7 +155,8 @@ describe("computeVbnRequiredFeatures — VBN 100023", () => {
155
155
  });
156
156
  it("sorteert verplicht vóór advies", async () => {
157
157
  const res = await computeVbnRequiredFeatures(client, "100023");
158
- expect(res.features.map((f) => f.code)).toEqual(["S01", "S02", "S05"]);
158
+ // S62 en S98 eist Floriday altijd (FORCED_REQUIRED), dus ook verplicht.
159
+ expect(res.features.map((f) => f.code)).toEqual(["S01", "S02", "S62", "S98", "S05"]);
159
160
  });
160
161
  it("levert toegestane waarden per feature (gededupliceerd, zonder vervallen)", async () => {
161
162
  const res = await computeVbnRequiredFeatures(client, "100023");
@@ -163,9 +164,10 @@ describe("computeVbnRequiredFeatures — VBN 100023", () => {
163
164
  expect(s01?.values.map((v) => v.code).sort()).toEqual(["003", "004"]);
164
165
  expect(s01?.values.some((v) => v.code === "999")).toBe(false);
165
166
  });
166
- it("geeft lege lijst terug voor onbekende VBN-code", async () => {
167
+ it("geeft alleen de door Floriday altijd verplichte kenmerken voor onbekende VBN-code", async () => {
167
168
  const res = await computeVbnRequiredFeatures(client, "99999");
168
- expect(res).toEqual({ vbnCode: "99999", features: [] });
169
+ expect(res.vbnCode).toBe("99999");
170
+ expect(res.features.map((f) => f.code)).toEqual(["S62", "S98"]);
169
171
  });
170
172
  it("vindt regulatory rows via record_id-prefix als data.product_id ontbreekt", async () => {
171
173
  const prefixOnlyClient = makeFakeClient({
@@ -180,6 +182,8 @@ describe("computeVbnRequiredFeatures — VBN 100023", () => {
180
182
  expect(res.features.map((f) => [f.code, f.regulation])).toEqual([
181
183
  ["S01", "required"],
182
184
  ["S02", "required"],
185
+ ["S62", "required"],
186
+ ["S98", "required"],
183
187
  ["S05", "advised"],
184
188
  ]);
185
189
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=publish-114.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-114.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/publish-114.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { translatePublishError } from "../publish-errors.js";
3
+ import { effectiveJobStatus } from "../../components/voorraad/PublishProgressCard.js";
4
+ import { countStock } from "../../components/voorraad/VoorraadPage.js";
5
+ describe("1.0.114 publiceren en telling", () => {
6
+ it("toont de echte reden als de poort schrijven tegenhoudt", () => {
7
+ const f = translatePublishError(new Error("publishTradeItem: gateway /api/public/gateway/write → Tegengehouden door poort: in de Floriday-kopie wijkt de verpakking af van Floriday."));
8
+ expect(f.code).toBe("GATE_BLOCKED");
9
+ expect(f.message).toContain("wijkt de verpakking af");
10
+ });
11
+ it("onbekende fout toont de technische reden", () => {
12
+ const f = translatePublishError(new Error("stap x → iets raars"));
13
+ expect(f.message).toContain("iets raars");
14
+ });
15
+ it("job blijft niet op bezig na mislukte productstap", () => {
16
+ expect(effectiveJobStatus("running", [
17
+ { status: "failed", step_key: "trade-item" },
18
+ { status: "pending", step_key: "stock" },
19
+ ])).toBe("failed");
20
+ expect(effectiveJobStatus("running", [{ status: "running", step_key: "trade-item" }])).toBe("running");
21
+ });
22
+ it("volledig gereserveerd telt als op voorraad", () => {
23
+ const c = countStock([
24
+ { quantity: 100, availableQuantity: 0, minQuantity: 0 },
25
+ { quantity: 0, availableQuantity: 0, minQuantity: 0 },
26
+ ]);
27
+ expect(c.inStock).toBe(1);
28
+ expect(c.outOfStock).toBe(1);
29
+ expect(c.fullyReserved).toBe(1);
30
+ });
31
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=publish-packing-block.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-packing-block.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/publish-packing-block.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parsePackingBlock } from "../publish-errors.js";
3
+ describe("parsePackingBlock", () => {
4
+ it("leest code, data.message en mismatchTradeItemIds", () => {
5
+ const r = parsePackingBlock('fout: {"code":"packing_mismatch","data":{"message":"Afwijking","mismatchTradeItemIds":["a1"]}}');
6
+ expect(r).toEqual({ code: "packing_mismatch", message: "Afwijking", ids: ["a1"] });
7
+ });
8
+ it("onderscheidt packing_mismatch_connection", () => {
9
+ expect(parsePackingBlock("packing_mismatch_connection")?.code).toBe("packing_mismatch_connection");
10
+ });
11
+ it("geeft null zonder code", () => {
12
+ expect(parsePackingBlock("iets anders")).toBeNull();
13
+ });
14
+ });
@@ -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" | "UNKNOWN";
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";
10
10
  export interface PublishErrorAction {
11
11
  label: string;
12
12
  /** Interne route waar de gebruiker het kan oplossen. */
@@ -34,4 +34,18 @@ export declare function translatePublishError(error: unknown, opts?: {
34
34
  characteristicLabels?: Record<string, string>;
35
35
  }): FriendlyPublishError;
36
36
  export declare function friendlyFieldName(field?: string): string;
37
+ /**
38
+ * Haalt de leesbare reden uit een technische foutketen
39
+ * ("stap: route → reden") zodat de gebruiker altijd de echte oorzaak ziet.
40
+ */
41
+ export declare function realReason(raw: string): string;
42
+ declare const PACKING_CODES: readonly ["packing_mismatch_connection", "packing_unverified", "packing_mismatch"];
43
+ type PackingCode = (typeof PACKING_CODES)[number];
44
+ /** Herkent de blokkadecodes van Floriday-auth (1.0.122) in JSON of tekst. */
45
+ export declare function parsePackingBlock(raw: string): {
46
+ code: PackingCode;
47
+ message: string | null;
48
+ ids: string[];
49
+ } | null;
50
+ export {};
37
51
  //# sourceMappingURL=publish-errors.d.ts.map
@@ -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,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,CAyItB;AAgBD,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxD"}
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"}
@@ -140,10 +140,38 @@ export function translatePublishError(error, opts) {
140
140
  detail: raw,
141
141
  };
142
142
  }
143
+ // Floriday-auth 1.0.122: packing_mismatch / packing_mismatch_connection / packing_unverified
144
+ // met data.message en mismatchTradeItemIds.
145
+ const pm = parsePackingBlock(raw);
146
+ if (pm) {
147
+ const ids = pm.ids.length ? ` (artikel${pm.ids.length > 1 ? "en" : ""}: ${pm.ids.join(", ")})` : "";
148
+ const lead = pm.code === "packing_unverified"
149
+ ? "Floriday-koppeling kan de verpakking nog niet controleren"
150
+ : pm.code === "packing_mismatch_connection"
151
+ ? "Floriday-koppeling houdt versturen tegen: verpakkingsverschil in deze koppeling"
152
+ : "Floriday-koppeling houdt versturen tegen: verpakking wijkt af";
153
+ return {
154
+ code: "GATE_BLOCKED",
155
+ message: `${lead}${ids}${pm.message ? `: ${pm.message}` : ""}`,
156
+ hint: "Kijk bij Floriday koppeling → Verpakking en probeer opnieuw. Je ingevoerde gegevens zijn bewaard.",
157
+ retryable: pm.code === "packing_unverified",
158
+ detail: raw,
159
+ };
160
+ }
161
+ // Floriday-auth houdt schrijven tegen (floriday_write_mode read_only): toon de echte reden.
162
+ if (lower.includes("tegengehouden door poort") || lower.includes("blocked_by_gate")) {
163
+ return {
164
+ code: "GATE_BLOCKED",
165
+ message: `Floriday-koppeling houdt versturen tegen: ${realReason(raw)}`,
166
+ hint: "Los het verschil op bij Floriday koppeling → Verpakking en probeer opnieuw. Je ingevoerde gegevens zijn bewaard.",
167
+ retryable: false,
168
+ detail: raw,
169
+ };
170
+ }
143
171
  return {
144
172
  code: "UNKNOWN",
145
- message: "Het publiceren is niet gelukt.",
146
- hint: "Probeer het opnieuw. Blijft dit gebeuren, geef dan het referentienummer door aan support.",
173
+ message: raw ? `Het publiceren is niet gelukt: ${realReason(raw)}` : "Het publiceren is niet gelukt.",
174
+ hint: "Probeer het opnieuw. Je ingevoerde gegevens zijn bewaard.",
147
175
  retryable: false,
148
176
  detail: raw,
149
177
  };
@@ -181,3 +209,37 @@ function stepForFieldName(field) {
181
209
  return "vbn";
182
210
  return "artikel";
183
211
  }
212
+ /**
213
+ * Haalt de leesbare reden uit een technische foutketen
214
+ * ("stap: route → reden") zodat de gebruiker altijd de echte oorzaak ziet.
215
+ */
216
+ export function realReason(raw) {
217
+ const parts = raw.split("→");
218
+ const last = (parts[parts.length - 1] ?? raw).trim();
219
+ const text = last.replace(/^[a-zA-Z]+:\s*/, "").trim() || raw.trim();
220
+ return text.length > 300 ? `${text.slice(0, 297)}…` : text;
221
+ }
222
+ const PACKING_CODES = ["packing_mismatch_connection", "packing_unverified", "packing_mismatch"];
223
+ /** Herkent de blokkadecodes van Floriday-auth (1.0.122) in JSON of tekst. */
224
+ export function parsePackingBlock(raw) {
225
+ if (!raw)
226
+ return null;
227
+ const code = PACKING_CODES.find((c) => raw.includes(c));
228
+ if (!code)
229
+ return null;
230
+ let message = null;
231
+ let ids = [];
232
+ const start = raw.indexOf("{");
233
+ if (start >= 0) {
234
+ try {
235
+ const j = JSON.parse(raw.slice(start, raw.lastIndexOf("}") + 1));
236
+ const d = j?.data ?? j;
237
+ message = typeof d?.message === "string" ? d.message : null;
238
+ ids = Array.isArray(d?.mismatchTradeItemIds) ? d.mismatchTradeItemIds.map(String) : [];
239
+ }
240
+ catch {
241
+ /* geen JSON: alleen de code tonen */
242
+ }
243
+ }
244
+ return { code, message, ids };
245
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-voorraad",
3
- "version": "1.0.113",
3
+ "version": "1.0.117",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "main": "./dist-lib/index.js",
package/sql/AGENTS.md CHANGED
@@ -1,3 +1,4 @@
1
1
 
2
2
  - Voorraad never creates or replaces functions owned by other modules (Engine `logistics_*`, Floriday-auth, Verkoop; list in `FOREIGN_FUNCS` of `scripts/generate-voorraad-schema.py`); the schema file contains own objects only, because the schema sync would overwrite another module's version.
3
3
  - Data corrections only via `voorraad_correctie_<nr>(p_uitvoeren default false)` functions: server-only, match on id, insert/update only (never DELETE), hash-check Floriday cache + queue, log every run in `voorraad_correcties`; `voorraad_correctie_status()` tells other modules whether it ran, because loose SQL per Shell is not repeatable.
4
+ - Fust rows are resolved only via `voorraad_active_fust_id(key, id)` (follows `merged_into`), and an order line books fust once: the ERP line adopts an earlier Floriday-ingest booking and ingest skips lines the ERP already booked, because `fust_key LIMIT 1` hit merged rows and both paths booked the same order.