@flowselections/floriday-voorraad 1.0.135 → 1.0.139

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 (30) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.js +19 -23
  3. package/dist-lib/components/voorraad/FloridayInfoCard.d.ts.map +1 -1
  4. package/dist-lib/components/voorraad/FloridayInfoCard.js +19 -0
  5. package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts.map +1 -1
  6. package/dist-lib/components/voorraad/StockMutationHistoryCard.js +17 -3
  7. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  8. package/dist-lib/components/voorraad/VoorraadPage.js +2 -5
  9. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  10. package/dist-lib/components/voorraad/VoorraadStockCard.js +59 -5
  11. package/dist-lib/hooks/useTradeItemStock.d.ts +2 -0
  12. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  13. package/dist-lib/hooks/useTradeItemStock.js +4 -3
  14. package/dist-lib/integrations/supabase/types.d.ts +637 -0
  15. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  16. package/dist-lib/lib/__tests__/release-136.test.d.ts +2 -0
  17. package/dist-lib/lib/__tests__/release-136.test.d.ts.map +1 -0
  18. package/dist-lib/lib/__tests__/release-136.test.js +36 -0
  19. package/dist-lib/lib/__tests__/stock-save.test.d.ts +2 -0
  20. package/dist-lib/lib/__tests__/stock-save.test.d.ts.map +1 -0
  21. package/dist-lib/lib/__tests__/stock-save.test.js +35 -0
  22. package/dist-lib/lib/publish-status.d.ts +10 -0
  23. package/dist-lib/lib/publish-status.d.ts.map +1 -1
  24. package/dist-lib/lib/publish-status.js +18 -0
  25. package/dist-lib/lib/stock-save.d.ts +32 -0
  26. package/dist-lib/lib/stock-save.d.ts.map +1 -0
  27. package/dist-lib/lib/stock-save.js +28 -0
  28. package/package.json +2 -2
  29. package/sql/AGENTS.md +1 -0
  30. package/sql/floriday-voorraad-schema.sql +63 -10
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=release-136.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-136.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/release-136.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { formSalePrice, publishOutcome, syncAfterPublish } from "../publish-status.js";
3
+ import { compareAfterUpdate, verifyDifferenceMessage } from "../publish-verify.js";
4
+ // Floriday wordt nagebootst: alleen teruggelezen artikelobjecten, geen verzending.
5
+ const floridayItem = (name) => ({ tradeItemName: name, vbnProductCode: 1234 });
6
+ describe("1.0.136 — prijs, dirty en controlestap", () => {
7
+ it("(1) sale_price 1,40 blijft staan, ook als trade_item_prices 1,25 is", () => {
8
+ const tradeItemPrice = 1.25; // wordt bewust niet doorgegeven
9
+ const price = formSalePrice(1.4, undefined);
10
+ expect(price).toBe(1.4);
11
+ expect(price).not.toBe(tradeItemPrice);
12
+ // Ook met een oude waarde in de lokale gegevens wint sale_price.
13
+ expect(formSalePrice("1.40", 1.25)).toBe(1.4);
14
+ });
15
+ it("(2) geslaagde wijziging: job geslaagd en artikel niet dirty", () => {
16
+ const diffs = compareAfterUpdate({ name: "TQA gewijzigd", vbnCode: 1234 }, floridayItem("TQA gewijzigd"));
17
+ expect(diffs).toEqual([]);
18
+ const status = publishOutcome([
19
+ { step_key: "trade-item", status: "succeeded" },
20
+ { step_key: "verify", status: "succeeded" },
21
+ ]);
22
+ expect(status).toBe("succeeded");
23
+ expect(syncAfterPublish(status, null)).toEqual({ dirty: false, last_error: null });
24
+ });
25
+ it("(3) afwijkende naam: job mislukt met VERIFY_MISMATCH en artikel blijft dirty", () => {
26
+ const diffs = compareAfterUpdate({ name: "TQA gewijzigd", vbnCode: 1234 }, floridayItem("TQA oud"));
27
+ expect(diffs.map((d) => d.field)).toEqual(["name"]);
28
+ const msg = verifyDifferenceMessage(diffs);
29
+ const status = publishOutcome([
30
+ { step_key: "trade-item", status: "succeeded" },
31
+ { step_key: "verify", status: "failed", error_code: "VERIFY_MISMATCH" },
32
+ ]);
33
+ expect(status).toBe("failed");
34
+ expect(syncAfterPublish(status, msg)).toEqual({ dirty: true, last_error: msg });
35
+ });
36
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stock-save.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-save.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/stock-save.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { afboekFout, floridayNaOpslaanTekst, valuesAfterSave } from "../stock-save.js";
3
+ const saved = { physical_stock: 40, minimum_stock: 5, reserved_stock: 12, incoming_stock: 0, notes: null };
4
+ describe("lokaal voorraad opslaan (1.0.138)", () => {
5
+ it("mislukte opslag zet de oude waarden terug (geen 'Beschikbaar: 100' zonder opslag)", () => {
6
+ const entered = { ...saved, physical_stock: 112 };
7
+ expect(valuesAfterSave(false, entered, saved).physical_stock).toBe(40);
8
+ });
9
+ it("gereserveerd komt na opslaan uit de opgeslagen rij, niet uit invoer", () => {
10
+ const entered = { ...saved, physical_stock: 100, reserved_stock: 0 };
11
+ const out = valuesAfterSave(true, entered, saved);
12
+ expect(out.physical_stock).toBe(100);
13
+ expect(out.reserved_stock).toBe(12);
14
+ });
15
+ it("Floriday-artikel bij dichte poort: lokaal opgeslagen, melding dat er niets naar Floriday gaat", () => {
16
+ expect(floridayNaOpslaanTekst(true, false)).toMatch(/niets naar Floriday/);
17
+ expect(floridayNaOpslaanTekst(true, null)).toMatch(/niets naar Floriday/);
18
+ expect(floridayNaOpslaanTekst(true, true)).toBeNull();
19
+ expect(floridayNaOpslaanTekst(false, false)).toBeNull();
20
+ });
21
+ });
22
+ describe("afboeken met verplichte reden", () => {
23
+ it("zonder reden geweigerd", () => {
24
+ expect(afboekFout(5, null, 40)).toBe("Kies een reden.");
25
+ expect(afboekFout(5, "zomaar", 40)).toBe("Kies een reden.");
26
+ });
27
+ it("met reden uit de vaste lijst toegestaan", () => {
28
+ for (const r of ["derving", "kapot", "eigen_gebruik", "overig"])
29
+ expect(afboekFout(5, r, 40)).toBeNull();
30
+ });
31
+ it("niet meer dan liggend, en groter dan 0", () => {
32
+ expect(afboekFout(41, "kapot", 40)).toMatch(/niet meer afboeken/);
33
+ expect(afboekFout(0, "kapot", 40)).toMatch(/groter dan 0/);
34
+ });
35
+ });
@@ -25,4 +25,14 @@ export declare function editTargetText(input: {
25
25
  writeReadOnly: boolean | null | undefined;
26
26
  writeReason?: string | null;
27
27
  }): EditTarget;
28
+ /**
29
+ * Verkoopprijs voor het artikelformulier: alleen products.sale_price (> 0),
30
+ * anders de lokale artikelgegevens. trade_item_prices wordt nooit gelezen.
31
+ */
32
+ export declare function formSalePrice(productSalePrice: unknown, fallback: unknown): number;
33
+ /** Lokale synchronisatiestatus na het afronden van een publicatiejob. */
34
+ export declare function syncAfterPublish(status: PublishOutcome, verifyMismatch: string | null): {
35
+ dirty: boolean;
36
+ last_error: string | null;
37
+ };
28
38
  //# sourceMappingURL=publish-status.d.ts.map
@@ -1 +1 @@
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
+ {"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;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAIlF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG;IACvF,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAIA"}
@@ -31,3 +31,21 @@ export function editTargetText(input) {
31
31
  }
32
32
  return { sendsToFloriday: true, text: "Wijzigingen worden direct naar Floriday verstuurd." };
33
33
  }
34
+ /**
35
+ * Verkoopprijs voor het artikelformulier: alleen products.sale_price (> 0),
36
+ * anders de lokale artikelgegevens. trade_item_prices wordt nooit gelezen.
37
+ */
38
+ export function formSalePrice(productSalePrice, fallback) {
39
+ const p = Number(productSalePrice);
40
+ if (p > 0)
41
+ return p;
42
+ return Number(fallback) || 0;
43
+ }
44
+ /** Lokale synchronisatiestatus na het afronden van een publicatiejob. */
45
+ export function syncAfterPublish(status, verifyMismatch) {
46
+ if (status === "failed" && verifyMismatch)
47
+ return { dirty: true, last_error: verifyMismatch };
48
+ if (status === "failed")
49
+ return { dirty: true, last_error: null };
50
+ return { dirty: false, last_error: null };
51
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Regels rond het lokaal opslaan van voorraad (1.0.138).
3
+ * - Na een mislukte opslag staan weer de laatst opgeslagen waarden in beeld.
4
+ * - Gereserveerd komt altijd uit de opgeslagen rij (berekend uit orders), nooit uit invoer.
5
+ * - Afboeken vraagt altijd een reden uit een vaste lijst.
6
+ */
7
+ export interface StockValues {
8
+ physical_stock: number;
9
+ minimum_stock: number;
10
+ reserved_stock: number;
11
+ incoming_stock: number;
12
+ notes: string | null;
13
+ }
14
+ export declare function valuesAfterSave<T extends StockValues>(ok: boolean, entered: T, saved: T): T;
15
+ export declare const AFBOEK_REDENEN: readonly [{
16
+ readonly value: "derving";
17
+ readonly label: "Derving";
18
+ }, {
19
+ readonly value: "kapot";
20
+ readonly label: "Kapot";
21
+ }, {
22
+ readonly value: "eigen_gebruik";
23
+ readonly label: "Eigen gebruik";
24
+ }, {
25
+ readonly value: "overig";
26
+ readonly label: "Overig";
27
+ }];
28
+ export type AfboekReden = (typeof AFBOEK_REDENEN)[number]["value"];
29
+ export declare function afboekFout(aantal: number, reden: string | null, liggend: number): string | null;
30
+ /** Melding na een geslaagde lokale opslag van een Floriday-artikel. */
31
+ export declare function floridayNaOpslaanTekst(isFloridayArtikel: boolean, poortOpen: boolean | null): string | null;
32
+ //# sourceMappingURL=stock-save.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-save.d.ts","sourceRoot":"","sources":["../../src/lib/stock-save.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAG3F;AAED,eAAO,MAAM,cAAc;;;;;;;;;;;;EAKjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;AAEnE,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/F;AAED,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAI3G"}
@@ -0,0 +1,28 @@
1
+ export function valuesAfterSave(ok, entered, saved) {
2
+ if (!ok)
3
+ return { ...saved };
4
+ return { ...entered, reserved_stock: saved.reserved_stock };
5
+ }
6
+ export const AFBOEK_REDENEN = [
7
+ { value: "derving", label: "Derving" },
8
+ { value: "kapot", label: "Kapot" },
9
+ { value: "eigen_gebruik", label: "Eigen gebruik" },
10
+ { value: "overig", label: "Overig" },
11
+ ];
12
+ export function afboekFout(aantal, reden, liggend) {
13
+ if (!reden || !AFBOEK_REDENEN.some((r) => r.value === reden))
14
+ return "Kies een reden.";
15
+ if (!Number.isFinite(aantal) || aantal <= 0)
16
+ return "Vul een aantal groter dan 0 in.";
17
+ if (aantal > liggend)
18
+ return `Je kunt niet meer afboeken (${aantal}) dan er ligt (${liggend}).`;
19
+ return null;
20
+ }
21
+ /** Melding na een geslaagde lokale opslag van een Floriday-artikel. */
22
+ export function floridayNaOpslaanTekst(isFloridayArtikel, poortOpen) {
23
+ if (!isFloridayArtikel)
24
+ return null;
25
+ if (poortOpen === true)
26
+ return null;
27
+ return "Lokaal opgeslagen. Er gaat niets naar Floriday: versturen staat uit.";
28
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-voorraad",
3
- "version": "1.0.135",
3
+ "version": "1.0.139",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "main": "./dist-lib/index.js",
@@ -49,7 +49,7 @@
49
49
  "devDependencies": {
50
50
  "@cloudflare/vite-plugin": "^1.25.5",
51
51
  "@eslint/js": "^9.32.0",
52
- "@flowselections/core": "1.0.30",
52
+ "@flowselections/core": "1.0.34",
53
53
  "@lovable.dev/vite-tanstack-config": "2.26.5",
54
54
  "@rsbuild/core": "^1.0.0",
55
55
  "@tailwindcss/vite": "^4.2.1",
package/sql/AGENTS.md CHANGED
@@ -4,3 +4,4 @@
4
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.
5
5
  - An order line counts as cancelled for fust when `order_items.quarantined_at` is set or its order is cancelled (`voorraad_order_geannuleerd`: status geannuleerd/cancelled case-insensitive, `cancelled_at` or `deleted_at`): `sync_order_item_fusten` wants 0 and triggers on order_items (quarantine on/off) and orders (those three fields) book the difference against the line's own bookings, because quarantined or cancelled orders otherwise kept fust booked.
6
6
  - The correct fust count per order line comes only from `voorraad_fust_juist(order_item_id)` (same rule as `sync_order_item_fusten`; removed, quarantined, externally fulfilled or cancelled = 0); fust corrections compare against it, because every hand-built copy of the rule drifted.
7
+ - Local stock saves never depend on the Floriday write gate: every Voorraad queue insert first checks `voorraad_floriday_klaarzetten_toegestaan()` and runs in its own exception block, because the Engine gate trigger raises and rolled back the local save.
@@ -1,4 +1,4 @@
1
- -- @flowselections/floriday-voorraad 1.0.134 – volledig schema (idempotent, geen gegevens).
1
+ -- @flowselections/floriday-voorraad 1.0.136 – 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.
@@ -8710,31 +8710,51 @@ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1131(p_uitvoeren boolean DE
8710
8710
  SECURITY DEFINER
8711
8711
  SET search_path TO 'public'
8712
8712
  AS $function$
8713
- DECLARE v_rows jsonb; v_result jsonb;
8713
+ DECLARE v_rows jsonb; v_result jsonb; v_done jsonb := '[]'::jsonb; r record;
8714
8714
  BEGIN
8715
8715
  PERFORM public.voorraad_vereis('server', 'voorraad_correctie_1131');
8716
- IF p_uitvoeren THEN
8717
- RAISE EXCEPTION 'Correctie 1131 is alleen als overzicht vrijgegeven; uitvoeren wacht op akkoord' USING ERRCODE = 'VR012';
8718
- END IF;
8716
+ PERFORM pg_advisory_xact_lock(hashtextextended('voorraad_correctie_1131', 0));
8719
8717
  SELECT COALESCE(jsonb_agg(jsonb_build_object(
8720
8718
  'artikelnummer', c.article_number,
8721
8719
  'voorraadartikel', c.id, 'canonieke_sleutel', public.voorraad_canoniek_sleutel(c.id),
8722
8720
  'spiegelproduct', m.id, 'spiegel_bron', m.custom_fields->>'source',
8723
- 'spiegel_herkend_als_verkoop', (m.custom_fields->>'source') = 'sales-module-floriday-mirror',
8724
8721
  'canonieke_regel', (SELECT to_jsonb(s) - 'notes' FROM public.trade_item_stock s
8725
8722
  WHERE s.trade_item_key = public.voorraad_canoniek_sleutel(c.id) AND s.archived_at IS NULL LIMIT 1),
8726
8723
  'spiegelregels', (SELECT jsonb_agg(to_jsonb(s) - 'notes' ORDER BY s.id) FROM public.trade_item_stock s
8727
8724
  WHERE s.trade_item_key IN (m.id::text, m.trade_item_key, 'product:' || m.id::text)
8728
8725
  AND s.trade_item_key IS DISTINCT FROM public.voorraad_canoniek_sleutel(c.id)
8729
- AND s.archived_at IS NULL),
8730
- 'voorstel', 'reserveringen opnieuw berekenen zodat ze op de canonieke regel landen; spiegelregel archiveren (archived_at, archived_into = canonieke sleutel); fysieke voorraad van de spiegel niet optellen, alleen loggen; spiegelproduct verdwijnt uit voorraadlijst en tellingen; niets verwijderen'
8726
+ AND s.archived_at IS NULL)
8731
8727
  ) ORDER BY c.article_number), '[]'::jsonb)
8732
8728
  INTO v_rows
8733
8729
  FROM public.products m
8734
8730
  JOIN public.products c ON c.floriday_trade_item_id = m.id::text AND c.id <> m.id;
8735
- v_result := jsonb_build_object('ok', true, 'status', 'voorstel', 'artikelen', v_rows);
8731
+
8732
+ IF p_uitvoeren THEN
8733
+ -- Archiveren in vaste volgorde; fysieke voorraad van de spiegel wordt niet opgeteld, alleen vastgelegd.
8734
+ FOR r IN
8735
+ SELECT s.id, s.trade_item_key, s.physical_stock, s.reserved_stock, public.voorraad_canoniek_sleutel(c.id) AS canon
8736
+ FROM public.products m
8737
+ JOIN public.products c ON c.floriday_trade_item_id = m.id::text AND c.id <> m.id
8738
+ JOIN public.trade_item_stock s ON s.trade_item_key IN (m.id::text, m.trade_item_key, 'product:' || m.id::text)
8739
+ WHERE s.archived_at IS NULL
8740
+ AND s.trade_item_key IS DISTINCT FROM public.voorraad_canoniek_sleutel(c.id)
8741
+ ORDER BY s.id
8742
+ FOR UPDATE OF s
8743
+ LOOP
8744
+ UPDATE public.trade_item_stock
8745
+ SET archived_at = now(), archived_into = r.canon,
8746
+ archived_reason = 'voorraad_correctie_1131: spiegelregel; reserveringen landen na herberekening op ' || r.canon,
8747
+ reserved_stock = 0
8748
+ WHERE id = r.id AND archived_at IS NULL;
8749
+ v_done := v_done || jsonb_build_object('regel', r.id, 'sleutel', r.trade_item_key, 'fysiek', r.physical_stock,
8750
+ 'gereserveerd_voor', r.reserved_stock, 'naar', r.canon);
8751
+ END LOOP;
8752
+ END IF;
8753
+
8754
+ v_result := jsonb_build_object('ok', true, 'status', CASE WHEN p_uitvoeren THEN 'uitgevoerd' ELSE 'voorstel' END,
8755
+ 'artikelen', v_rows, 'gearchiveerd', v_done);
8736
8756
  INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
8737
- VALUES ('voorraad_correctie_1131', COALESCE(auth.uid()::text, session_user::text), true, v_result, true);
8757
+ VALUES ('voorraad_correctie_1131', COALESCE(auth.uid()::text, session_user::text), NOT p_uitvoeren, v_result, true);
8738
8758
  RETURN v_result;
8739
8759
  END $function$
8740
8760
  ;
@@ -8769,6 +8789,39 @@ END $function$
8769
8789
  REVOKE ALL ON FUNCTION voorraad_correctie_1132(boolean) FROM PUBLIC;
8770
8790
  REVOKE ALL ON FUNCTION voorraad_correctie_1132(boolean) FROM anon;
8771
8791
  GRANT EXECUTE ON FUNCTION voorraad_correctie_1132(boolean) TO service_role;
8792
+ CREATE OR REPLACE FUNCTION public.voorraad_correctie_1136(p_uitvoeren boolean DEFAULT false)
8793
+ RETURNS jsonb
8794
+ LANGUAGE plpgsql
8795
+ SECURITY DEFINER
8796
+ SET search_path TO 'public'
8797
+ AS $function$
8798
+ DECLARE pid constant uuid := '27612767-2b14-468c-b169-a8f9a4abe053'; p record; v_status text; v_result jsonb; v_after numeric;
8799
+ BEGIN
8800
+ PERFORM public.voorraad_vereis('server', 'voorraad_correctie_1136');
8801
+ PERFORM pg_advisory_xact_lock(hashtextextended('voorraad_correctie_1136', 0));
8802
+ SELECT * INTO p FROM public.products WHERE id = pid FOR UPDATE;
8803
+ IF p.id IS NULL OR p.article_number IS DISTINCT FROM 'TQA2810A' THEN v_status := 'overgeslagen: artikel wijkt af';
8804
+ ELSIF p.sale_price = 1.40 THEN v_status := 'al uitgevoerd';
8805
+ ELSIF p.sale_price IS DISTINCT FROM 1.25 THEN v_status := 'overgeslagen: prijs gewijzigd';
8806
+ ELSE v_status := CASE WHEN p_uitvoeren THEN 'uitgevoerd' ELSE 'voorstel' END; END IF;
8807
+ IF v_status = 'uitgevoerd' THEN
8808
+ UPDATE public.products SET sale_price = 1.40, updated_at = now() WHERE id = pid;
8809
+ END IF;
8810
+ SELECT sale_price INTO v_after FROM public.products WHERE id = pid;
8811
+ v_result := jsonb_build_object('ok', v_status NOT LIKE 'overgeslagen%', 'status', v_status, 'product', pid, 'voor', p.sale_price, 'na', v_after);
8812
+ INSERT INTO public.voorraad_correcties(correctie, uitgevoerd_door, proef, aantallen, hash_ok)
8813
+ VALUES ('voorraad_correctie_1136', COALESCE(auth.uid()::text, session_user::text), NOT p_uitvoeren, v_result, true);
8814
+ RETURN v_result;
8815
+ END $function$
8816
+ ;
8817
+ REVOKE ALL ON FUNCTION voorraad_correctie_1136(boolean) FROM PUBLIC;
8818
+ REVOKE ALL ON FUNCTION voorraad_correctie_1136(boolean) FROM anon;
8819
+ REVOKE EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) FROM PUBLIC;
8820
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) TO authenticated;
8821
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) TO service_role;
8822
+ REVOKE EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) FROM anon;
8823
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) TO authenticated;
8824
+ GRANT EXECUTE ON FUNCTION voorraad_correctie_1136(boolean) TO service_role;
8772
8825
  CREATE OR REPLACE FUNCTION public.voorraad_correctie_status(p_correctie text DEFAULT '1098'::text)
8773
8826
  RETURNS jsonb
8774
8827
  LANGUAGE plpgsql