@flowselections/floriday-voorraad 1.0.115 → 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.
@@ -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-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
+ });
@@ -39,4 +39,13 @@ export declare function friendlyFieldName(field?: string): string;
39
39
  * ("stap: route → reden") zodat de gebruiker altijd de echte oorzaak ziet.
40
40
  */
41
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 {};
42
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,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,CAoJtB;AAgBD,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxD;AAaD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK9C"}
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,6 +140,24 @@ 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
+ }
143
161
  // Floriday-auth houdt schrijven tegen (floriday_write_mode read_only): toon de echte reden.
144
162
  if (lower.includes("tegengehouden door poort") || lower.includes("blocked_by_gate")) {
145
163
  return {
@@ -201,3 +219,27 @@ export function realReason(raw) {
201
219
  const text = last.replace(/^[a-zA-Z]+:\s*/, "").trim() || raw.trim();
202
220
  return text.length > 300 ? `${text.slice(0, 297)}…` : text;
203
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.115",
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.