@flowselections/floriday-voorraad 1.0.133 → 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.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +36 -6
- package/dist-lib/integrations/supabase/types.d.ts +6 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/release-134.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/release-134.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/release-134.test.js +50 -0
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-warehouse.functions.js +4 -2
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.js +11 -10
- package/dist-lib/lib/idempotency-key.d.ts +11 -0
- package/dist-lib/lib/idempotency-key.d.ts.map +1 -0
- package/dist-lib/lib/idempotency-key.js +41 -0
- package/dist-lib/lib/publish-errors.d.ts +1 -1
- package/dist-lib/lib/publish-errors.d.ts.map +1 -1
- package/dist-lib/lib/publish-errors.js +10 -0
- package/dist-lib/lib/publish-pipeline.js +1 -1
- package/dist-lib/lib/publish-status.d.ts +3 -1
- package/dist-lib/lib/publish-status.d.ts.map +1 -1
- package/dist-lib/lib/publish-status.js +4 -1
- package/dist-lib/lib/publish-verify.d.ts +25 -0
- package/dist-lib/lib/publish-verify.d.ts.map +1 -0
- package/dist-lib/lib/publish-verify.js +44 -0
- package/package.json +1 -1
- package/sql/floriday-voorraad-schema.sql +46 -18
|
@@ -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,
|
|
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:
|
|
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:
|
|
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":"
|
|
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
|
|
13
|
-
*
|
|
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:
|
|
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:
|
|
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
|
+
}
|
|
@@ -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,
|
|
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
|
|
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,4 +1,4 @@
|
|
|
1
|
-
-- @flowselections/floriday-voorraad 1.0.
|
|
1
|
+
-- @flowselections/floriday-voorraad 1.0.134 – volledig schema (idempotent, geen gegevens).
|
|
2
2
|
-- Gegenereerd uit de database met scripts/generate-voorraad-schema.py. Niet met de hand wijzigen.
|
|
3
3
|
-- Volgorde 20. Ontbrekende tabellen van andere modules worden overgeslagen met een NOTICE; draai opnieuw na die module.
|
|
4
4
|
-- De statuscontrole van floriday_continuous_stock_queue is van Voorraad en wordt hier opnieuw gemaakt.
|
|
@@ -5181,12 +5181,11 @@ DECLARE k text;
|
|
|
5181
5181
|
BEGIN
|
|
5182
5182
|
PERFORM public.voorraad_vereis('ingelogd', 'recompute_reserved_for_order');
|
|
5183
5183
|
FOR k IN
|
|
5184
|
-
SELECT DISTINCT
|
|
5184
|
+
SELECT DISTINCT public.voorraad_orderregel_sleutel(oi.product_id::text)
|
|
5185
5185
|
FROM logistics_active.order_items oi
|
|
5186
|
-
|
|
5187
|
-
WHERE oi.order_id = p_order_id AND p.trade_item_key IS NOT NULL
|
|
5186
|
+
WHERE oi.order_id = p_order_id AND oi.product_id IS NOT NULL
|
|
5188
5187
|
LOOP
|
|
5189
|
-
PERFORM public.recompute_reserved_stock(k);
|
|
5188
|
+
IF k IS NOT NULL THEN PERFORM public.recompute_reserved_stock(k); END IF;
|
|
5190
5189
|
END LOOP;
|
|
5191
5190
|
END $function$
|
|
5192
5191
|
;
|
|
@@ -5210,16 +5209,15 @@ DECLARE
|
|
|
5210
5209
|
v_products uuid[];
|
|
5211
5210
|
v_pid uuid;
|
|
5212
5211
|
v_leads boolean;
|
|
5212
|
+
v_refs text[];
|
|
5213
5213
|
BEGIN
|
|
5214
5214
|
PERFORM public.voorraad_vereis('ingelogd', 'recompute_reserved_stock');
|
|
5215
5215
|
IF p_trade_item_key IS NULL OR btrim(p_trade_item_key) = '' THEN RETURN; END IF;
|
|
5216
5216
|
IF current_setting('app.recomputing_reserved', true) = 'on' THEN RETURN; END IF;
|
|
5217
5217
|
|
|
5218
|
-
-- B1: nieuwe of losse sleutel zonder orderregels, toewijzing of set-/groepsrelatie en reservering 0: niets te doen.
|
|
5219
5218
|
v_key := public.logistics_reserved_key(p_trade_item_key);
|
|
5220
5219
|
IF public.logistics_reserved_fast_skip(p_trade_item_key, v_key) THEN RETURN; END IF;
|
|
5221
5220
|
|
|
5222
|
-
-- Set of groepshoofdproduct: gericht herberekenen op de onderdelen en leden (M7).
|
|
5223
5221
|
SELECT array_agg(p.id) INTO v_products
|
|
5224
5222
|
FROM public.products p
|
|
5225
5223
|
WHERE (p.trade_item_key = btrim(p_trade_item_key)
|
|
@@ -5247,8 +5245,6 @@ BEGIN
|
|
|
5247
5245
|
RETURN;
|
|
5248
5246
|
END IF;
|
|
5249
5247
|
|
|
5250
|
-
-- Sleutel hoort bij een groep of samenstelling: de toewijzing hangt van andere
|
|
5251
|
-
-- regels af, dus volledig herberekenen.
|
|
5252
5248
|
IF v_key LIKE 'group:%'
|
|
5253
5249
|
OR EXISTS (SELECT 1 FROM public.logistics_reservation_allocations a
|
|
5254
5250
|
WHERE a.trade_item_key = v_key AND a.source = 'group')
|
|
@@ -5265,18 +5261,28 @@ BEGIN
|
|
|
5265
5261
|
RETURN;
|
|
5266
5262
|
END IF;
|
|
5267
5263
|
|
|
5264
|
+
SELECT array_agg(DISTINCT r) INTO v_refs FROM (
|
|
5265
|
+
SELECT p.id::text AS r FROM public.products p
|
|
5266
|
+
WHERE p.trade_item_key = v_key OR p.floriday_trade_item_id = v_key OR p.id::text = v_key
|
|
5267
|
+
OR 'product:' || p.id::text = v_key
|
|
5268
|
+
UNION
|
|
5269
|
+
SELECT btrim(p.floriday_trade_item_id) FROM public.products p
|
|
5270
|
+
WHERE (p.trade_item_key = v_key OR p.floriday_trade_item_id = v_key OR 'product:' || p.id::text = v_key)
|
|
5271
|
+
AND NULLIF(btrim(COALESCE(p.floriday_trade_item_id,'')),'') IS NOT NULL
|
|
5272
|
+
UNION SELECT v_key
|
|
5273
|
+
) x WHERE r IS NOT NULL;
|
|
5274
|
+
|
|
5268
5275
|
v_leads := public.voorraad_floriday_leads_key(v_key);
|
|
5269
5276
|
SELECT CEIL(COALESCE(SUM(COALESCE(oi.quantity, 0)), 0))::integer INTO v_sum
|
|
5270
5277
|
FROM logistics_active.order_items oi
|
|
5271
5278
|
JOIN public.orders o ON o.id = oi.order_id
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
NULLIF(btrim(COALESCE(p.trade_item_key, '')), ''), p.id::text) = v_key
|
|
5279
|
+
WHERE oi.product_id::text = ANY(v_refs)
|
|
5280
|
+
AND public.voorraad_orderregel_sleutel(oi.product_id::text) = v_key
|
|
5275
5281
|
AND o.status = 'Nog te picken' AND NOT public.logistics_reserved_order_excluded(o.id)
|
|
5276
5282
|
AND NOT (public.normalize_order_source(o.order_source) = 'FO' AND v_leads)
|
|
5277
|
-
AND NOT EXISTS (SELECT 1 FROM public.product_bom_items b WHERE b.parent_product_id =
|
|
5283
|
+
AND NOT EXISTS (SELECT 1 FROM public.product_bom_items b WHERE b.parent_product_id = oi.product_id)
|
|
5278
5284
|
AND NOT EXISTS (SELECT 1 FROM public.product_groups pg
|
|
5279
|
-
WHERE pg.is_active = true AND (pg.id =
|
|
5285
|
+
WHERE pg.is_active = true AND (pg.id = oi.product_id OR pg.head_product_id = oi.product_id))
|
|
5280
5286
|
AND NOT EXISTS (
|
|
5281
5287
|
SELECT 1 FROM public.stock_mutation_log sml
|
|
5282
5288
|
WHERE sml.order_item_id = oi.id
|
|
@@ -6752,7 +6758,7 @@ AS $function$
|
|
|
6752
6758
|
DECLARE v_key text; v_old_key text;
|
|
6753
6759
|
BEGIN
|
|
6754
6760
|
IF (TG_OP = 'INSERT') THEN
|
|
6755
|
-
|
|
6761
|
+
v_key := public.voorraad_orderregel_sleutel(NEW.product_id::text);
|
|
6756
6762
|
PERFORM public.recompute_reserved_stock(v_key);
|
|
6757
6763
|
RETURN NEW;
|
|
6758
6764
|
ELSIF (TG_OP = 'UPDATE') THEN
|
|
@@ -6763,15 +6769,15 @@ BEGIN
|
|
|
6763
6769
|
AND NEW.source_line_key IS NOT DISTINCT FROM OLD.source_line_key THEN
|
|
6764
6770
|
RETURN NEW;
|
|
6765
6771
|
END IF;
|
|
6766
|
-
|
|
6767
|
-
|
|
6772
|
+
v_key := public.voorraad_orderregel_sleutel(NEW.product_id::text);
|
|
6773
|
+
v_old_key := public.voorraad_orderregel_sleutel(OLD.product_id::text);
|
|
6768
6774
|
PERFORM public.recompute_reserved_stock(v_key);
|
|
6769
6775
|
IF v_old_key IS DISTINCT FROM v_key THEN
|
|
6770
6776
|
PERFORM public.recompute_reserved_stock(v_old_key);
|
|
6771
6777
|
END IF;
|
|
6772
6778
|
RETURN NEW;
|
|
6773
6779
|
ELSIF (TG_OP = 'DELETE') THEN
|
|
6774
|
-
|
|
6780
|
+
v_old_key := public.voorraad_orderregel_sleutel(OLD.product_id::text);
|
|
6775
6781
|
PERFORM public.recompute_reserved_stock(v_old_key);
|
|
6776
6782
|
RETURN OLD;
|
|
6777
6783
|
END IF;
|
|
@@ -9252,6 +9258,28 @@ GRANT EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zo
|
|
|
9252
9258
|
REVOKE EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zone,timestamp with time zone) FROM anon;
|
|
9253
9259
|
GRANT EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zone,timestamp with time zone) TO authenticated;
|
|
9254
9260
|
GRANT EXECUTE ON FUNCTION voorraad_order_geannuleerd(text,timestamp with time zone,timestamp with time zone) TO service_role;
|
|
9261
|
+
CREATE OR REPLACE FUNCTION public.voorraad_orderregel_sleutel(p_ref text)
|
|
9262
|
+
RETURNS text
|
|
9263
|
+
LANGUAGE plpgsql
|
|
9264
|
+
STABLE SECURITY DEFINER
|
|
9265
|
+
SET search_path TO 'public'
|
|
9266
|
+
AS $function$
|
|
9267
|
+
DECLARE v_key text;
|
|
9268
|
+
BEGIN
|
|
9269
|
+
PERFORM public.voorraad_vereis('ingelogd', 'voorraad_orderregel_sleutel');
|
|
9270
|
+
IF p_ref IS NULL OR btrim(p_ref) = '' THEN RETURN NULL; END IF;
|
|
9271
|
+
BEGIN
|
|
9272
|
+
SELECT c.trade_item_key INTO v_key FROM public.voorraad_canoniek_artikel(p_ref) c;
|
|
9273
|
+
EXCEPTION WHEN others THEN
|
|
9274
|
+
RETURN NULL;
|
|
9275
|
+
END;
|
|
9276
|
+
RETURN v_key;
|
|
9277
|
+
END $function$
|
|
9278
|
+
;
|
|
9279
|
+
REVOKE ALL ON FUNCTION voorraad_orderregel_sleutel(text) FROM PUBLIC;
|
|
9280
|
+
REVOKE ALL ON FUNCTION voorraad_orderregel_sleutel(text) FROM anon;
|
|
9281
|
+
GRANT EXECUTE ON FUNCTION voorraad_orderregel_sleutel(text) TO authenticated;
|
|
9282
|
+
GRANT EXECUTE ON FUNCTION voorraad_orderregel_sleutel(text) TO service_role;
|
|
9255
9283
|
CREATE OR REPLACE FUNCTION public.voorraad_packing_from_floriday(p_product_id uuid, p_uitvoeren boolean)
|
|
9256
9284
|
RETURNS jsonb
|
|
9257
9285
|
LANGUAGE plpgsql
|