@flowselections/floriday-authenticatie-module 1.0.58 → 1.0.65

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 (43) hide show
  1. package/dist-lib/integrations/supabase/types.d.ts +537 -3
  2. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  3. package/dist-lib/lib/base-supply-decision.d.ts +52 -0
  4. package/dist-lib/lib/base-supply-decision.d.ts.map +1 -0
  5. package/dist-lib/lib/base-supply-decision.js +76 -0
  6. package/dist-lib/lib/cron/process-stock-queue.d.ts +14 -3
  7. package/dist-lib/lib/cron/process-stock-queue.d.ts.map +1 -1
  8. package/dist-lib/lib/cron/process-stock-queue.js +35 -39
  9. package/dist-lib/lib/floriday-auto-process.server.d.ts.map +1 -1
  10. package/dist-lib/lib/floriday-auto-process.server.js +12 -64
  11. package/dist-lib/lib/floriday-cart-approval.server.d.ts.map +1 -1
  12. package/dist-lib/lib/floriday-cart-approval.server.js +30 -16
  13. package/dist-lib/lib/floriday-order-stage.d.ts +31 -0
  14. package/dist-lib/lib/floriday-order-stage.d.ts.map +1 -0
  15. package/dist-lib/lib/floriday-order-stage.js +77 -0
  16. package/dist-lib/lib/floriday-sync.functions.d.ts +15 -0
  17. package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
  18. package/dist-lib/lib/floriday-sync.functions.js +124 -1
  19. package/dist-lib/lib/floriday.functions.d.ts +0 -8
  20. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  21. package/dist-lib/lib/floriday.functions.js +61 -45
  22. package/dist-lib/lib/gateway/fulfillment-order-body.d.ts +2 -0
  23. package/dist-lib/lib/gateway/fulfillment-order-body.d.ts.map +1 -1
  24. package/dist-lib/lib/gateway/fulfillment-order-body.js +6 -1
  25. package/dist-lib/lib/gateway/fulfillment-order-send.d.ts.map +1 -1
  26. package/dist-lib/lib/gateway/fulfillment-order-send.js +57 -4
  27. package/dist-lib/lib/gateway/fulfillment-order-status.d.ts +10 -0
  28. package/dist-lib/lib/gateway/fulfillment-order-status.d.ts.map +1 -1
  29. package/dist-lib/lib/gateway/fulfillment-order-status.js +107 -1
  30. package/dist-lib/lib/gateway/supply-line.d.ts.map +1 -1
  31. package/dist-lib/lib/gateway/supply-line.js +140 -63
  32. package/dist-lib/lib/gateway/webhook-subscribe.d.ts.map +1 -1
  33. package/dist-lib/lib/gateway/webhook-subscribe.js +24 -4
  34. package/dist-lib/lib/gateway/webhook.d.ts +6 -0
  35. package/dist-lib/lib/gateway/webhook.d.ts.map +1 -1
  36. package/dist-lib/lib/gateway/webhook.js +9 -0
  37. package/dist-lib/lib/shell-base.d.ts +14 -0
  38. package/dist-lib/lib/shell-base.d.ts.map +1 -1
  39. package/dist-lib/lib/shell-base.js +17 -0
  40. package/dist-lib/lib/shell-routes.d.ts +1 -1
  41. package/dist-lib/lib/shell-routes.d.ts.map +1 -1
  42. package/dist-lib/lib/shell-routes.js +1 -0
  43. package/package.json +9 -1
@@ -0,0 +1,52 @@
1
+ export type IsoWeek = {
2
+ year: number;
3
+ week: number;
4
+ };
5
+ /** ISO-weekjaar + week van een kalenderdatum (y, m 1-12, d). */
6
+ export declare function isoWeekOfDate(y: number, m: number, d: number): IsoWeek;
7
+ /** ISO-week van een moment, gerekend in Europe/Amsterdam (Floriday-tijd). */
8
+ export declare function isoWeekAmsterdam(at?: Date): IsoWeek;
9
+ /** Aantal ISO-weken in een ISO-weekjaar (52 of 53). */
10
+ export declare function isoWeeksInYear(year: number): number;
11
+ export declare function compareWeek(a: IsoWeek, b: IsoWeek): number;
12
+ export type WeekClass = "past" | "current" | "future" | "invalid";
13
+ export declare function classifyWeek(req: IsoWeek, now: IsoWeek): WeekClass;
14
+ export type FloridayProblem = {
15
+ status: number;
16
+ domainErrorCode: string | null;
17
+ title: string | null;
18
+ detail: string | null;
19
+ };
20
+ export declare function toProblem(status: number, data: any): FloridayProblem;
21
+ export declare const PRICE_LOCKED_MESSAGE = "Prijs kan niet meer wijzigen in de lopende week";
22
+ export declare const WEEK_IN_PAST_MESSAGE = "Week ligt in het verleden; weekaanbod is gesloten";
23
+ export type PlanStep = {
24
+ kind: "skip_past";
25
+ } | {
26
+ kind: "invalid_week";
27
+ } | {
28
+ kind: "put";
29
+ } | {
30
+ kind: "patch";
31
+ priceStatus: "unchanged" | "price_locked_current_week";
32
+ };
33
+ /** Wat te doen na de GET: bestaand aanbod (of null) en het verzoek. */
34
+ export declare function planBaseSupply(weekClass: WeekClass, existing: {
35
+ basePricePerPiece?: {
36
+ value?: number;
37
+ } | null;
38
+ } | null, price: number): PlanStep;
39
+ export type PutFailure = {
40
+ kind: "week_in_past";
41
+ } | {
42
+ kind: "price_locked_current_week";
43
+ } | {
44
+ kind: "recheck";
45
+ } | {
46
+ kind: "error";
47
+ retryable: boolean;
48
+ };
49
+ /** Duiding van een afgewezen PUT. Beslist op code + week, nooit op tekst. */
50
+ export declare function interpretPutFailure(weekClass: WeekClass, p: FloridayProblem): PutFailure;
51
+ export declare function isRetryableStatus(status: number): boolean;
52
+ //# sourceMappingURL=base-supply-decision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-supply-decision.d.ts","sourceRoot":"","sources":["../../src/lib/base-supply-decision.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAOtE;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,EAAE,GAAE,IAAiB,GAAG,OAAO,CAM/D;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAE1D;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElE,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CAMlE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,eAAe,CAQpE;AAED,eAAO,MAAM,oBAAoB,oDAAoD,CAAC;AACtF,eAAO,MAAM,oBAAoB,sDAAsD,CAAC;AAExF,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,WAAW,GAAG,2BAA2B,CAAA;CAAE,CAAC;AAE9E,uEAAuE;AACvE,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE;IAAE,iBAAiB,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,EAClE,KAAK,EAAE,MAAM,GACZ,QAAQ,CASV;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,2BAA2B,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1C,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE,eAAe,GAAG,UAAU,CAMxF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,76 @@
1
+ // Pure beslisregels voor weekaanbod (base supply). Geen I/O, los te testen.
2
+ // Bron: Floriday Suppliers API 2026v1 Swagger + staging-proef 12-10-2026:
3
+ // - PATCH op ontbrekend én gesloten weekaanbod: 404 zonder domainErrorCode.
4
+ // - PUT in een week in het verleden: 400 domainErrorCode "weeklist-closed".
5
+ // - PUT met andere prijs in de lopende week: 400 "weeklist-closed"
6
+ // ("closed for price changes"); het aantal mag dan nog via PATCH.
7
+ /** ISO-weekjaar + week van een kalenderdatum (y, m 1-12, d). */
8
+ export function isoWeekOfDate(y, m, d) {
9
+ const date = new Date(Date.UTC(y, m - 1, d));
10
+ const day = date.getUTCDay() || 7;
11
+ date.setUTCDate(date.getUTCDate() + 4 - day);
12
+ const year = date.getUTCFullYear();
13
+ const week = Math.ceil(((date.getTime() - Date.UTC(year, 0, 1)) / 86400000 + 1) / 7);
14
+ return { year, week };
15
+ }
16
+ /** ISO-week van een moment, gerekend in Europe/Amsterdam (Floriday-tijd). */
17
+ export function isoWeekAmsterdam(at = new Date()) {
18
+ const parts = new Intl.DateTimeFormat("en-CA", {
19
+ timeZone: "Europe/Amsterdam", year: "numeric", month: "2-digit", day: "2-digit",
20
+ }).formatToParts(at);
21
+ const get = (t) => Number(parts.find((p) => p.type === t)?.value);
22
+ return isoWeekOfDate(get("year"), get("month"), get("day"));
23
+ }
24
+ /** Aantal ISO-weken in een ISO-weekjaar (52 of 53). */
25
+ export function isoWeeksInYear(year) {
26
+ return isoWeekOfDate(year, 12, 28).week;
27
+ }
28
+ export function compareWeek(a, b) {
29
+ return a.year !== b.year ? a.year - b.year : a.week - b.week;
30
+ }
31
+ export function classifyWeek(req, now) {
32
+ if (!Number.isInteger(req.year) || !Number.isInteger(req.week) || req.week < 1 || req.week > isoWeeksInYear(req.year)) {
33
+ return "invalid";
34
+ }
35
+ const c = compareWeek(req, now);
36
+ return c < 0 ? "past" : c === 0 ? "current" : "future";
37
+ }
38
+ export function toProblem(status, data) {
39
+ const body = data && typeof data === "object" ? (data.body && typeof data.body === "object" ? data.body : data) : {};
40
+ return {
41
+ status,
42
+ domainErrorCode: typeof body.domainErrorCode === "string" ? body.domainErrorCode : null,
43
+ title: typeof body.title === "string" ? body.title : null,
44
+ detail: typeof body.detail === "string" ? body.detail : null,
45
+ };
46
+ }
47
+ export const PRICE_LOCKED_MESSAGE = "Prijs kan niet meer wijzigen in de lopende week";
48
+ export const WEEK_IN_PAST_MESSAGE = "Week ligt in het verleden; weekaanbod is gesloten";
49
+ /** Wat te doen na de GET: bestaand aanbod (of null) en het verzoek. */
50
+ export function planBaseSupply(weekClass, existing, price) {
51
+ if (weekClass === "invalid")
52
+ return { kind: "invalid_week" };
53
+ if (weekClass === "past")
54
+ return { kind: "skip_past" };
55
+ if (!existing)
56
+ return { kind: "put" };
57
+ const current = Number(existing.basePricePerPiece?.value);
58
+ const samePrice = Number.isFinite(current) && Math.abs(current - price) < 0.0005;
59
+ if (samePrice)
60
+ return { kind: "patch", priceStatus: "unchanged" };
61
+ if (weekClass === "current")
62
+ return { kind: "patch", priceStatus: "price_locked_current_week" };
63
+ return { kind: "put" };
64
+ }
65
+ /** Duiding van een afgewezen PUT. Beslist op code + week, nooit op tekst. */
66
+ export function interpretPutFailure(weekClass, p) {
67
+ if (p.domainErrorCode === "weeklist-closed") {
68
+ return weekClass === "past" ? { kind: "week_in_past" } : { kind: "price_locked_current_week" };
69
+ }
70
+ if (p.status === 409 || p.status === 400 || p.status === 422)
71
+ return { kind: "recheck" };
72
+ return { kind: "error", retryable: p.status === 0 || p.status === 423 || p.status === 429 || p.status >= 500 };
73
+ }
74
+ export function isRetryableStatus(status) {
75
+ return status === 0 || status === 423 || status === 429 || status >= 500;
76
+ }
@@ -8,7 +8,7 @@ type StockQueueRow = {
8
8
  desired_available: boolean | null;
9
9
  attempts: number | null;
10
10
  };
11
- /** ISO-weeknummer van vandaag. */
11
+ /** ISO-week (weekjaar + week) in Europe/Amsterdam; zonder invoer: vandaag. */
12
12
  export declare function isoWeekParts(input?: string | Date): {
13
13
  year: number;
14
14
  week: number;
@@ -33,8 +33,10 @@ export declare function decideWarehouse(input: {
33
33
  } | null;
34
34
  export declare const SKIPPED_NO_PRICE_MESSAGE = "Prijs ontbreekt, nog niet verzonden";
35
35
  export declare const SKIPPED_NO_WAREHOUSE_MESSAGE = "Geen voorkeursmagazijn voor deze koppeling; kies er een op de Connectie-pagina";
36
+ export declare const NEEDS_BASE_SUPPLY_MESSAGE = "Nog geen weekaanbod in Floriday; aanmaken via gateway/supply-line";
37
+ export declare const WEEK_CLOSED_MESSAGE = "Week is gesloten in Floriday; overgeslagen";
36
38
  export type StockOutcome = {
37
- outcome: "quantity_sent" | "skipped_no_price" | "skipped_no_warehouse" | "error";
39
+ outcome: "quantity_sent" | "skipped_no_price" | "skipped_no_warehouse" | "needs_base_supply" | "skipped_week_closed" | "error";
38
40
  availabilitySent: boolean;
39
41
  quantitySent: boolean;
40
42
  error: string | null;
@@ -51,10 +53,19 @@ type WriteResult = {
51
53
  * Bepaalt de afsluiting van een regel uit de twee Floriday-antwoorden.
52
54
  * Puur (geen I/O) zodat het los te testen is.
53
55
  * - quantity_sent alleen bij een 2xx op de base-supply-PATCH.
54
- * - 404 op de PATCH (geen weekaanbod) = skipped_no_price, nooit verzonden.
56
+ * - 404 op de PATCH = needs_base_supply. Floriday geeft hier voor een
57
+ * ontbrekend én een gesloten weekaanbod dezelfde 404 zonder domainErrorCode
58
+ * (staging 12-10-2026); een gesloten week blijkt pas uit de PUT
59
+ * (400, domainErrorCode "weeklist-closed") in gateway/supply-line.
55
60
  * - beschikbaarheid alleen is nooit quantity_sent.
56
61
  */
57
62
  export declare function decideOutcome(availability: WriteResult | null, quantity: WriteResult | null): StockOutcome;
63
+ /**
64
+ * Achterwaarts compatibel: kent de afsluitfunctie van Voorraad een nieuwe
65
+ * uitkomst nog niet, dan wordt de regel als skipped_no_price afgesloten met
66
+ * een herkenbare prefix in de fout. Puur, voor tests.
67
+ */
68
+ export declare function legacyFallback(o: StockOutcome): StockOutcome | null;
58
69
  /**
59
70
  * Zet één wachtrijregel door naar Floriday. Gooit nooit.
60
71
  * Er wordt bewust GEEN PUT met basePricePerPiece gedaan zolang het prijsplan
@@ -1 +1 @@
1
- {"version":3,"file":"process-stock-queue.d.ts","sourceRoot":"","sources":["../../../src/lib/cron/process-stock-queue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-stock-queue.d.ts","sourceRoot":"","sources":["../../../src/lib/cron/process-stock-queue.ts"],"names":[],"mappings":"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"}
@@ -1,32 +1,12 @@
1
- // ============================================================================
2
- // Vaste verwerker voor de Floriday-voorraadwachtrij.
3
- // ----------------------------------------------------------------------------
4
- // Deze module neemt de voorraadsync volledig over van de voorraadmodule.
5
- // Werkwijze per ronde:
6
- // 1. `claim_floriday_stock_queue` claimt atomair max. N regels (lock 5 min).
7
- // 2. Per regel: werkelijk aantal stuks (weekaanbod) + beschikbaarheids-
8
- // schakelaar via `florydayWrite` — dus met de tokens van deze module.
9
- // 3. `complete_floriday_stock_queue` schrijft het resultaat terug met
10
- // exponentiële backoff (1 → 30 min, max 8 pogingen) en onderscheid
11
- // tussen tijdelijke en definitieve fouten.
12
- // Bij een snelheidslimiet (429) breekt de ronde af; de volgende cron-tick
13
- // pakt het weer op.
14
- // ============================================================================
1
+ import { isoWeekAmsterdam } from "../base-supply-decision.js";
15
2
  import { florydayWrite, getActiveFlorydayEnvironment, syncShellHostRuntimeConfig, } from "../floriday.functions.js";
16
3
  import { verifyGatewayRequest, GATEWAY_HEADER_TIMESTAMP } from "../floriday-gateway-hmac.js";
17
4
  import { isOwnWarehouse } from "../floriday-own-config.js";
18
5
  const BATCH_SIZE = 25;
19
- /** ISO-weeknummer van vandaag. */
6
+ /** ISO-week (weekjaar + week) in Europe/Amsterdam; zonder invoer: vandaag. */
20
7
  export function isoWeekParts(input) {
21
8
  const parsed = input ? new Date(input) : new Date();
22
- const source = Number.isNaN(parsed.getTime()) ? new Date() : parsed;
23
- const date = new Date(Date.UTC(source.getUTCFullYear(), source.getUTCMonth(), source.getUTCDate()));
24
- const day = date.getUTCDay() || 7;
25
- date.setUTCDate(date.getUTCDate() + 4 - day);
26
- const year = date.getUTCFullYear();
27
- const yearStart = new Date(Date.UTC(year, 0, 1));
28
- const week = Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
29
- return { year, week };
9
+ return isoWeekAmsterdam(Number.isNaN(parsed.getTime()) ? new Date() : parsed);
30
10
  }
31
11
  /** Tijdelijk (netwerk/5xx/429) versus definitief (gegevensfout). */
32
12
  export function isPermanentError(message) {
@@ -106,11 +86,16 @@ async function resolveWarehouseId(supabase, connectionId, tradeItemId, cachedIte
106
86
  }
107
87
  export const SKIPPED_NO_PRICE_MESSAGE = "Prijs ontbreekt, nog niet verzonden";
108
88
  export const SKIPPED_NO_WAREHOUSE_MESSAGE = "Geen voorkeursmagazijn voor deze koppeling; kies er een op de Connectie-pagina";
89
+ export const NEEDS_BASE_SUPPLY_MESSAGE = "Nog geen weekaanbod in Floriday; aanmaken via gateway/supply-line";
90
+ export const WEEK_CLOSED_MESSAGE = "Week is gesloten in Floriday; overgeslagen";
109
91
  /**
110
92
  * Bepaalt de afsluiting van een regel uit de twee Floriday-antwoorden.
111
93
  * Puur (geen I/O) zodat het los te testen is.
112
94
  * - quantity_sent alleen bij een 2xx op de base-supply-PATCH.
113
- * - 404 op de PATCH (geen weekaanbod) = skipped_no_price, nooit verzonden.
95
+ * - 404 op de PATCH = needs_base_supply. Floriday geeft hier voor een
96
+ * ontbrekend én een gesloten weekaanbod dezelfde 404 zonder domainErrorCode
97
+ * (staging 12-10-2026); een gesloten week blijkt pas uit de PUT
98
+ * (400, domainErrorCode "weeklist-closed") in gateway/supply-line.
114
99
  * - beschikbaarheid alleen is nooit quantity_sent.
115
100
  */
116
101
  export function decideOutcome(availability, quantity) {
@@ -127,18 +112,23 @@ export function decideOutcome(availability, quantity) {
127
112
  return { outcome: "quantity_sent", availabilitySent, quantitySent: true, error: null, permanent: false, attemptId };
128
113
  }
129
114
  if (quantity.status === 404) {
130
- return {
131
- outcome: "skipped_no_price",
132
- availabilitySent,
133
- quantitySent: false,
134
- error: `${SKIPPED_NO_PRICE_MESSAGE} (geen weekaanbod in Floriday)`,
135
- permanent: false,
136
- attemptId,
137
- };
115
+ return { outcome: "needs_base_supply", availabilitySent, quantitySent: false, error: NEEDS_BASE_SUPPLY_MESSAGE, permanent: false, attemptId };
138
116
  }
139
117
  const message = quantity.error ?? `HTTP ${quantity.status}`;
140
118
  return { outcome: "error", availabilitySent, quantitySent: false, error: message, permanent: isPermanentError(message), attemptId };
141
119
  }
120
+ /**
121
+ * Achterwaarts compatibel: kent de afsluitfunctie van Voorraad een nieuwe
122
+ * uitkomst nog niet, dan wordt de regel als skipped_no_price afgesloten met
123
+ * een herkenbare prefix in de fout. Puur, voor tests.
124
+ */
125
+ export function legacyFallback(o) {
126
+ if (o.outcome === "needs_base_supply")
127
+ return { ...o, outcome: "skipped_no_price", error: `[needs_base_supply] ${o.error}` };
128
+ if (o.outcome === "skipped_week_closed")
129
+ return { ...o, outcome: "skipped_no_price", error: `[week_closed] ${o.error}` };
130
+ return null;
131
+ }
142
132
  function errorOutcome(message, permanent) {
143
133
  return { outcome: "error", availabilitySent: false, quantitySent: false, error: message, permanent, attemptId: null };
144
134
  }
@@ -207,15 +197,19 @@ export async function pushStockRow(supabase, env, row) {
207
197
  }
208
198
  }
209
199
  async function completeRow(supabase, id, o) {
210
- const { error } = await supabase.rpc("complete_floriday_stock_queue_outcome", {
200
+ const call = (x) => supabase.rpc("complete_floriday_stock_queue_outcome", {
211
201
  p_id: id,
212
- p_outcome: o.outcome,
213
- p_availability_sent: o.availabilitySent,
214
- p_quantity_sent: o.quantitySent,
215
- p_error: o.error,
216
- p_attempt_id: o.attemptId,
217
- p_permanent: o.permanent,
202
+ p_outcome: x.outcome,
203
+ p_availability_sent: x.availabilitySent,
204
+ p_quantity_sent: x.quantitySent,
205
+ p_error: x.error,
206
+ p_attempt_id: x.attemptId,
207
+ p_permanent: x.permanent,
218
208
  });
209
+ let { error } = await call(o);
210
+ const fb = error ? legacyFallback(o) : null;
211
+ if (fb)
212
+ ({ error } = await call(fb));
219
213
  if (error)
220
214
  console.error("[stock-queue] afsluiten mislukt:", id, error.message);
221
215
  }
@@ -243,6 +237,8 @@ export async function runStockQueueRound(supabase, limit = BATCH_SIZE) {
243
237
  skippedNoPrice += 1;
244
238
  else if (outcome.outcome === "skipped_no_warehouse")
245
239
  skippedNoWarehouse += 1;
240
+ else if (outcome.outcome === "needs_base_supply" || outcome.outcome === "skipped_week_closed")
241
+ skippedNoPrice += 1;
246
242
  else {
247
243
  failed += 1;
248
244
  if (outcome.error && isRateLimited(outcome.error))
@@ -1 +1 @@
1
- {"version":3,"file":"floriday-auto-process.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-auto-process.server.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,yBAAyB,CAAC;AAKjC,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,oBAAoB,GACpB,4BAA4B,GAC5B,qBAAqB,GACrB,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsHD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC,GACA,OAAO,CAAC,kBAAkB,CAAC,CAoM7B;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC,CA4ED"}
1
+ {"version":3,"file":"floriday-auto-process.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-auto-process.server.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,yBAAyB,CAAC;AAKjC,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,oBAAoB,GACpB,4BAA4B,GAC5B,qBAAqB,GACrB,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsHD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC,GACA,OAAO,CAAC,kBAAkB,CAAC,CAkI7B;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B,CAAC,CA4ED"}
@@ -198,74 +198,22 @@ export async function runAutoProcess(supabase, input) {
198
198
  reason: "auto_process_paused",
199
199
  };
200
200
  }
201
- // ─ Actie: versturen loopt ALTIJD via de gedeelde poort ───────────────────
202
- await logPhase(supabase, conn.id, salesOrderId, "sending_fulfillment");
203
- const requestIds = doMatch.fulfillmentRequests
204
- .map((r) => r?.fulfillmentRequestId ?? r?.id)
205
- .filter(Boolean)
206
- .map((id) => String(id));
207
- const { runFulfillmentOrderSend } = await import("./gateway/fulfillment-order-send.js");
208
- const sent = await runFulfillmentOrderSend(supabase, {
209
- environment: env,
210
- deliveryOrderId,
211
- fulfillmentRequestIds: requestIds,
212
- }, "auto-process");
213
- const payload = sent.payload ?? {};
214
- // Karren nog niet gecontroleerd → niet versturen, zichtbaar loggen, later opnieuw.
215
- if (payload.code === "carts_not_controlled") {
216
- const reason = payload.error ?? "Karren nog niet gecontroleerd door de logistiek-engine";
217
- await logPhase(supabase, conn.id, salesOrderId, "waiting", {
218
- code: "carts_not_controlled",
219
- reason,
220
- });
221
- return {
222
- ok: true,
223
- status: "waiting",
224
- salesOrderId,
225
- deliveryOrderId,
226
- code: "carts_not_controlled",
227
- reason,
228
- };
229
- }
230
- if (payload.ok && payload.code === "already_fulfilled") {
231
- await logPhase(supabase, conn.id, salesOrderId, "done", {
232
- code: "already_fulfilled",
233
- source: payload.source,
234
- fulfillmentOrderId: payload.fulfillmentOrderId,
235
- });
236
- return {
237
- ok: true,
238
- status: "done",
239
- salesOrderId,
240
- deliveryOrderId,
241
- code: "already_fulfilled",
242
- reason: "already_fulfilled_at_floriday",
243
- fulfillmentOrderId: payload.fulfillmentOrderId,
244
- };
245
- }
246
- if (!payload.ok) {
247
- const error = payload.error ?? "Versturen fulfillment-order mislukt";
248
- await logPhase(supabase, conn.id, salesOrderId, "error", {
249
- code: payload.code,
250
- error,
251
- });
252
- return {
253
- ok: false,
254
- status: "error",
255
- salesOrderId,
256
- deliveryOrderId,
257
- code: payload.code,
258
- error,
259
- };
260
- }
261
- const fulfillmentOrderId = payload.fulfillmentOrderId;
262
- await logPhase(supabase, conn.id, salesOrderId, "done", { fulfillmentOrderId });
201
+ // ─ Klaar voor belading (1.0.64) ──────────────────────────────────────────
202
+ // auto-process verstuurt NOOIT zelf belading en neemt niets aan bij Floriday
203
+ // (Floriday zet de order zelf op COMMITTED). De belading komt uitsluitend van
204
+ // Afhandeling via fulfillment-order/send met karren (indeling van de Engine).
205
+ const reason = "Klaar voor belading; belading volgt via Afhandeling (fulfillment-order/send).";
206
+ await logPhase(supabase, conn.id, salesOrderId, "waiting", {
207
+ code: "waiting_for_afhandeling",
208
+ reason,
209
+ });
263
210
  return {
264
211
  ok: true,
265
- status: "done",
212
+ status: "waiting",
266
213
  salesOrderId,
267
214
  deliveryOrderId,
268
- fulfillmentOrderId,
215
+ code: "waiting_for_afhandeling",
216
+ reason,
269
217
  };
270
218
  }
271
219
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"floriday-cart-approval.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-cart-approval.server.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,qBAAqB,GACrB,kBAAkB,GAClB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;IACvB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mDAAmD;IACnD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oEAAoE;IACpE,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAClE;AA4FD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,OAAO,CAAC,oBAAoB,CAAC,CAkE/B"}
1
+ {"version":3,"file":"floriday-cart-approval.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-cart-approval.server.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,qBAAqB,GACrB,kBAAkB,GAClB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;IACvB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mDAAmD;IACnD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oEAAoE;IACpE,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAClE;AA4FD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACA,OAAO,CAAC,oBAAoB,CAAC,CAmF/B"}
@@ -11,11 +11,13 @@
11
11
  //
12
12
  // `_order_ref` is het ordernummer (public.orders.order_number).
13
13
  //
14
- // Draait deze module in een shell zónder logistiek-engine (geen `orders`-tabel
15
- // en geen goedkeuringsfuncties), dan bestaat er geen karrencontrole en blijft
16
- // het gedrag ongewijzigd: er wordt niet geblokkeerd. Bestaat de controle wél,
17
- // dan geldt fail closed — onleesbaar of niet goedgekeurd betekent: niet
18
- // versturen.
14
+ // Wat telt als "gecontroleerd": elke kar van de order (public.order_carts,
15
+ // ook gedeelde karren via order_cart_items) heeft een afgemelde scan aan de
16
+ // controlebalie: public.logistics_control_scan.approved_at IS NOT NULL.
17
+ //
18
+ // Altijd fail closed (sinds 1.0.64): ontbreekt de controle in de Shell, is
19
+ // hij onleesbaar of zijn niet alle karren afgemeld, dan gaat er niets naar
20
+ // Floriday.
19
21
  // ============================================================================
20
22
  const ALLOWED = {
21
23
  allowed: true,
@@ -94,22 +96,34 @@ export async function checkCartApproval(supabase, input) {
94
96
  if (salesOrderIds.length === 0 && deliveryOrderId) {
95
97
  salesOrderIds = await salesOrderIdsForDeliveryOrder(supabase, input.connectionId, deliveryOrderId);
96
98
  }
97
- if (salesOrderIds.length === 0)
98
- return ALLOWED;
99
+ // Fail closed (1.0.64): zonder controle gaat er niets naar Floriday.
100
+ const unavailable = async (reason, refs = []) => {
101
+ await logRefusal(supabase, refs[0] ?? null, reason, module);
102
+ return { allowed: false, code: "geen_karrencontrole", reason, orderRefs: refs, details: [] };
103
+ };
104
+ if (salesOrderIds.length === 0) {
105
+ return unavailable("Geen verkooporder bij deze afleveropdracht gevonden; karrencontrole niet mogelijk.");
106
+ }
99
107
  const { refs, missingModule } = await orderRefsForSalesOrders(supabase, salesOrderIds);
100
- // Geen orders-tabel in deze shell → geen karrencontrole → gedrag ongewijzigd.
101
- if (missingModule)
102
- return ALLOWED;
103
- // Wel een orders-tabel, maar deze order staat er niet in: niets te toetsen.
104
- if (refs.length === 0)
105
- return ALLOWED;
108
+ if (missingModule) {
109
+ return unavailable("Geen karrencontrole in deze Shell (orders/controlescan ontbreekt); er wordt niet verstuurd.");
110
+ }
111
+ if (refs.length === 0) {
112
+ return {
113
+ allowed: false,
114
+ code: "niet_goedgekeurd",
115
+ reason: "Order nog niet bekend in Afhandeling; er zijn nog geen karren ingedeeld of afgemeld.",
116
+ orderRefs: [],
117
+ details: [],
118
+ };
119
+ }
106
120
  const details = [];
107
121
  for (const orderRef of refs) {
108
122
  const { data, error } = await supabase.rpc("logistics_order_cart_approval", { _order_ref: orderRef });
109
123
  if (error) {
110
- // Functie bestaat niet in deze shell → geen karrencontrole aanwezig.
111
- if (isMissingObject(error))
112
- return ALLOWED;
124
+ if (isMissingObject(error)) {
125
+ return unavailable("Karrencontrole-functie ontbreekt in deze Shell; er wordt niet verstuurd.", refs);
126
+ }
113
127
  const reason = "De karrencontrole kan niet gelezen worden; er wordt geen fulfillment-order naar Floriday gestuurd.";
114
128
  await logRefusal(supabase, orderRef, reason, module);
115
129
  return {
@@ -0,0 +1,31 @@
1
+ export type FloridayStage = "open" | "fulfilled" | "cancelled" | "expired" | "unknown";
2
+ export declare function deriveFloridayStage(salesOrderStatus: string | null | undefined, fulfillmentStatuses: Array<string | null | undefined>): FloridayStage;
3
+ /** Incoterm: lege waarde = niet opgegeven (in 2026v2 altijd aanwezig). */
4
+ export declare function readIncoterm(salesOrder: any): string | null;
5
+ export type LoadIssue = {
6
+ code: "missing_fulfillment_order_id" | "missing_load_carrier_reference" | "load_carrier_item_id_missing" | "load_carriers_missing";
7
+ severity: "error" | "warning";
8
+ cartIndex?: number;
9
+ lineIndex?: number;
10
+ fulfillmentRequestId?: string;
11
+ message: string;
12
+ };
13
+ /**
14
+ * Controle van de belading van Afhandeling. De module maakt nooit zelf een
15
+ * kenmerk aan: fulfillmentOrderId en loadCarrierReference zijn verplicht,
16
+ * loadCarrierItemId mag in 2026v1 ontbreken (waarschuwing).
17
+ */
18
+ export declare function checkAfhandelingLoad(body: any): {
19
+ errors: LoadIssue[];
20
+ warnings: LoadIssue[];
21
+ };
22
+ /** Verzendbrief is pas betrouwbaar op te halen bij ACCEPTED of DELIVERED. */
23
+ export declare function isDeliveryNoteReady(fulfillmentStatus: string | null | undefined): boolean;
24
+ /**
25
+ * Beleid voor send zonder karren van Afhandeling. Standaard (schakelaar uit)
26
+ * gaat het door met een waarschuwing; met `require_load_carriers` aan = 400.
27
+ */
28
+ export declare function decideLoadCarriersPolicy(hasCartLoad: boolean, required: boolean): "ok" | "warn" | "reject";
29
+ /** Vertaal de uitkomst van de karrencontrole naar de gateway-code. */
30
+ export declare function cartApprovalGatewayCode(code: "toegestaan" | "geen_karrencontrole" | "niet_goedgekeurd" | "controle_onleesbaar"): null | "carts_not_controlled" | "cart_check_unavailable";
31
+ //# sourceMappingURL=floriday-order-stage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floriday-order-stage.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-order-stage.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;AAIvF,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC3C,mBAAmB,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GACpD,aAAa,CAQf;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,UAAU,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAG3D;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EACA,8BAA8B,GAC9B,gCAAgC,GAChC,8BAA8B,GAC9B,uBAAuB,CAAC;IAC5B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,GAAG,GAAG;IAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,SAAS,EAAE,CAAA;CAAE,CAyB9F;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEzF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,OAAO,EACpB,QAAQ,EAAE,OAAO,GAChB,IAAI,GAAG,MAAM,GAAG,QAAQ,CAG1B;AAED,sEAAsE;AACtE,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,YAAY,GAAG,qBAAqB,GAAG,kBAAkB,GAAG,qBAAqB,GACtF,IAAI,GAAG,sBAAsB,GAAG,wBAAwB,CAI1D"}
@@ -0,0 +1,77 @@
1
+ // Pure afleiding van de Floriday-voortgang van één verkooporder.
2
+ //
3
+ // Een Floriday SalesOrder blijft op COMMITTED staan, ook na levering. De
4
+ // echte voortgang staat in de uitlevering (fulfillment-order) die de
5
+ // fulfillmentRequests van de order claimt. Daarom kijken we naar beide.
6
+ // Bron: Suppliers API 2026v1 Swagger (SalesOrderStatus, FulfillmentStatus).
7
+ const FULFILLED = new Set(["ACCEPTED", "DELIVERED"]);
8
+ export function deriveFloridayStage(salesOrderStatus, fulfillmentStatuses) {
9
+ const so = String(salesOrderStatus ?? "").toUpperCase();
10
+ if (!so)
11
+ return "unknown";
12
+ if (so === "CANCELLED" || so === "REJECTED" || so === "UNFULFILLABLE")
13
+ return "cancelled";
14
+ if (so === "EXPIRED")
15
+ return "expired";
16
+ const fo = fulfillmentStatuses.map((s) => String(s ?? "").toUpperCase()).filter(Boolean);
17
+ if (fo.some((s) => FULFILLED.has(s)))
18
+ return "fulfilled";
19
+ return "open";
20
+ }
21
+ /** Incoterm: lege waarde = niet opgegeven (in 2026v2 altijd aanwezig). */
22
+ export function readIncoterm(salesOrder) {
23
+ const v = salesOrder?.delivery?.incoterm;
24
+ return typeof v === "string" && v.trim() ? v.trim() : null;
25
+ }
26
+ /**
27
+ * Controle van de belading van Afhandeling. De module maakt nooit zelf een
28
+ * kenmerk aan: fulfillmentOrderId en loadCarrierReference zijn verplicht,
29
+ * loadCarrierItemId mag in 2026v1 ontbreken (waarschuwing).
30
+ */
31
+ export function checkAfhandelingLoad(body) {
32
+ const errors = [];
33
+ const warnings = [];
34
+ const foId = typeof body?.fulfillmentOrderId === "string" ? body.fulfillmentOrderId.trim() : "";
35
+ if (!foId) {
36
+ errors.push({ code: "missing_fulfillment_order_id", severity: "error",
37
+ message: "fulfillmentOrderId ontbreekt; Afhandeling moet dit vaste kenmerk meesturen." });
38
+ }
39
+ const carts = Array.isArray(body?.loadCarriers) ? body.loadCarriers : [];
40
+ carts.forEach((cart, ci) => {
41
+ const ref = typeof cart?.loadCarrierReference === "string" ? cart.loadCarrierReference.trim() : "";
42
+ if (!ref) {
43
+ errors.push({ code: "missing_load_carrier_reference", severity: "error", cartIndex: ci,
44
+ message: `Kar ${ci + 1} mist loadCarrierReference.` });
45
+ }
46
+ (Array.isArray(cart?.items) ? cart.items : []).forEach((line, li) => {
47
+ const id = typeof line?.loadCarrierItemId === "string" ? line.loadCarrierItemId.trim() : "";
48
+ if (!id) {
49
+ warnings.push({ code: "load_carrier_item_id_missing", severity: "warning", cartIndex: ci, lineIndex: li,
50
+ fulfillmentRequestId: line?.fulfillmentRequestId ? String(line.fulfillmentRequestId) : undefined,
51
+ message: `Kar ${ci + 1}, regel ${li + 1} mist loadCarrierItemId (in 2026v1 toegestaan, vanaf 2026v2 verplicht).` });
52
+ }
53
+ });
54
+ });
55
+ return { errors, warnings };
56
+ }
57
+ /** Verzendbrief is pas betrouwbaar op te halen bij ACCEPTED of DELIVERED. */
58
+ export function isDeliveryNoteReady(fulfillmentStatus) {
59
+ return FULFILLED.has(String(fulfillmentStatus ?? "").toUpperCase());
60
+ }
61
+ /**
62
+ * Beleid voor send zonder karren van Afhandeling. Standaard (schakelaar uit)
63
+ * gaat het door met een waarschuwing; met `require_load_carriers` aan = 400.
64
+ */
65
+ export function decideLoadCarriersPolicy(hasCartLoad, required) {
66
+ if (hasCartLoad)
67
+ return "ok";
68
+ return required ? "reject" : "warn";
69
+ }
70
+ /** Vertaal de uitkomst van de karrencontrole naar de gateway-code. */
71
+ export function cartApprovalGatewayCode(code) {
72
+ if (code === "toegestaan")
73
+ return null;
74
+ if (code === "niet_goedgekeurd")
75
+ return "carts_not_controlled";
76
+ return "cart_check_unavailable";
77
+ }
@@ -1,6 +1,21 @@
1
1
  import type { SupabaseClient } from "@supabase/supabase-js";
2
2
  import { type FlorydayEnvironment } from "./floriday.functions.js";
3
3
  export type FlorydayResource = "trade-items" | "sales-orders" | "customer-organizations" | "carriers" | "fulfillment-orders" | "customer-stickers" | "warehouses" | "packings" | "delivery-locations" | "clock-supplies" | "delivery-orders" | "clock-presales" | "contracts" | "blanket-orders" | "additional-services" | "commercial-service-types" | "collections" | "price-groups" | "catalog-prices" | "delivery-condition-sets" | "sales-strategies" | "base-items" | "contract-trade-item-groups" | "trade-settings-customers";
4
+ export declare function toNetworkRow(item: any, connectionId: string): {
5
+ connection_id: string;
6
+ organization_id: string;
7
+ organization_name: any;
8
+ gln: any;
9
+ country: any;
10
+ city: any;
11
+ data: any;
12
+ fetched_at: string;
13
+ } | null;
14
+ export declare function ensureBuyersInNetwork(supabase: any, connectionId: string, buyerIds: Array<string | null | undefined>): Promise<any>;
15
+ export declare function backfillNetworkFromCustomersCache(supabase: any, connectionId: string, deadlineMs: number): Promise<{
16
+ copied: number;
17
+ done: boolean;
18
+ }>;
4
19
  export declare function runFloridayResourceSync(supabase: SupabaseClient, env: FlorydayEnvironment, resource: FlorydayResource): Promise<{
5
20
  synced: number;
6
21
  fromSequence: number;