@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.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +63 -7
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts +5 -0
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/PublishProgressCard.js +16 -1
- package/dist-lib/components/voorraad/VoorraadPage.d.ts +13 -0
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +33 -20
- package/dist-lib/components/voorraad/__tests__/count-stock-free.test.d.ts +2 -0
- package/dist-lib/components/voorraad/__tests__/count-stock-free.test.d.ts.map +1 -0
- package/dist-lib/components/voorraad/__tests__/count-stock-free.test.js +12 -0
- package/dist-lib/integrations/supabase/types.d.ts +371 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/floricode-required-features.test.js +7 -3
- package/dist-lib/lib/__tests__/publish-114.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/publish-114.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/publish-114.test.js +31 -0
- package/dist-lib/lib/__tests__/publish-packing-block.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/publish-packing-block.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/publish-packing-block.test.js +14 -0
- package/dist-lib/lib/publish-errors.d.ts +15 -1
- package/dist-lib/lib/publish-errors.d.ts.map +1 -1
- package/dist-lib/lib/publish-errors.js +64 -2
- package/package.json +1 -1
- 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
|
-
|
|
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
|
|
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).
|
|
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 @@
|
|
|
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 @@
|
|
|
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,
|
|
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.
|
|
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
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.
|