@flowselections/logistiek-engine 1.0.98 → 1.0.100
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/BeladingIndelingCard.d.ts.map +1 -1
- package/dist-lib/components/BeladingIndelingCard.js +5 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +448 -6
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +448 -6
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +448 -6
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +462 -6
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/cartCoverage.functions.d.ts +448 -6
- package/dist-lib/lib/cartCoverage.functions.d.ts.map +1 -1
- package/dist-lib/lib/controlScan.server.d.ts +8 -0
- package/dist-lib/lib/controlScan.server.d.ts.map +1 -1
- package/dist-lib/lib/controlScan.server.js +49 -5
- package/dist-lib/lib/expandGroups.test.d.ts +2 -0
- package/dist-lib/lib/expandGroups.test.d.ts.map +1 -0
- package/dist-lib/lib/expandGroups.test.js +80 -0
- package/dist-lib/lib/floridayCancellation.server.d.ts +15 -0
- package/dist-lib/lib/floridayCancellation.server.d.ts.map +1 -0
- package/dist-lib/lib/floridayCancellation.server.js +89 -0
- package/dist-lib/lib/floridayCancellation.test.d.ts +2 -0
- package/dist-lib/lib/floridayCancellation.test.d.ts.map +1 -0
- package/dist-lib/lib/floridayCancellation.test.js +42 -0
- package/dist-lib/lib/floridayDocuments.functions.d.ts +1344 -18
- package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.functions.d.ts +448 -6
- package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
- package/dist-lib/lib/layerUtilization.d.ts +2 -0
- package/dist-lib/lib/layerUtilization.d.ts.map +1 -0
- package/dist-lib/lib/layerUtilization.js +11 -0
- package/dist-lib/lib/layerUtilization.test.d.ts +2 -0
- package/dist-lib/lib/layerUtilization.test.d.ts.map +1 -0
- package/dist-lib/lib/layerUtilization.test.js +11 -0
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +2240 -30
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +29 -0
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +896 -12
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts +448 -6
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
- package/dist-lib/lib/moduleVersion.d.ts +2 -0
- package/dist-lib/lib/moduleVersion.d.ts.map +1 -0
- package/dist-lib/lib/moduleVersion.js +2 -0
- package/dist-lib/lib/plannerDiff.functions.d.ts +448 -6
- package/dist-lib/lib/plannerDiff.functions.d.ts.map +1 -1
- package/dist-lib/lib/shippingNoteQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/shippingNoteQueue.server.js +11 -0
- package/package.json +3 -2
|
@@ -8,6 +8,14 @@ export interface ControlScanRun {
|
|
|
8
8
|
}
|
|
9
9
|
/** Maximaal aantal wachtrondes op Floriday voordat de controle als mislukt stopt. */
|
|
10
10
|
export declare const CONTROL_SCAN_MAX_ATTEMPTS = 60;
|
|
11
|
+
/** Maximale wachttijd sinds goedkeuring (uren). */
|
|
12
|
+
export declare const CONTROL_SCAN_MAX_HOURS = 24;
|
|
13
|
+
/** Is de wachtgrens (pogingen of tijd) bereikt? */
|
|
14
|
+
export declare function controlWaitExceeded(row: {
|
|
15
|
+
attempts?: number | null;
|
|
16
|
+
approved_at?: string | null;
|
|
17
|
+
created_at?: string | null;
|
|
18
|
+
}, now?: number): "pogingen" | "tijd" | null;
|
|
11
19
|
export declare function runControlScanQueue(sb: SupabaseClient, options?: {
|
|
12
20
|
limit?: number;
|
|
13
21
|
}): Promise<ControlScanRun>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controlScan.server.d.ts","sourceRoot":"","sources":["../../src/lib/controlScan.server.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"controlScan.server.d.ts","sourceRoot":"","sources":["../../src/lib/controlScan.server.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAK5D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAiBD,qFAAqF;AACrF,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C,mDAAmD;AACnD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAgBzC,mDAAmD;AACnD,wBAAgB,mBAAmB,CACjC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC1F,GAAG,GAAE,MAAmB,GACvB,UAAU,GAAG,MAAM,GAAG,IAAI,CAK5B;AA6BD,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,cAAc,EAClB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,OAAO,CAAC,cAAc,CAAC,CAiMzB"}
|
|
@@ -15,8 +15,12 @@
|
|
|
15
15
|
// klantwaarden in deze code; de printer komt uit de werkwijze in de database.
|
|
16
16
|
// ============================================================================
|
|
17
17
|
import { printViaLayout } from "./layoutPrint.server.js";
|
|
18
|
+
import { isFloridayCancelled, FLORIDAY_CANCELLED_REASON } from "./floridayCancellation.server.js";
|
|
19
|
+
import { trace } from "./trace.server.js";
|
|
18
20
|
/** Maximaal aantal wachtrondes op Floriday voordat de controle als mislukt stopt. */
|
|
19
21
|
export const CONTROL_SCAN_MAX_ATTEMPTS = 60;
|
|
22
|
+
/** Maximale wachttijd sinds goedkeuring (uren). */
|
|
23
|
+
export const CONTROL_SCAN_MAX_HOURS = 24;
|
|
20
24
|
function pickPdf(result) {
|
|
21
25
|
const base64 = result["pdfBase64"] ??
|
|
22
26
|
result["base64"] ??
|
|
@@ -28,20 +32,31 @@ function pickPdf(result) {
|
|
|
28
32
|
null;
|
|
29
33
|
return { base64: base64 ?? null, url: url ?? null };
|
|
30
34
|
}
|
|
35
|
+
/** Is de wachtgrens (pogingen of tijd) bereikt? */
|
|
36
|
+
export function controlWaitExceeded(row, now = Date.now()) {
|
|
37
|
+
if (Number(row.attempts ?? 0) >= CONTROL_SCAN_MAX_ATTEMPTS)
|
|
38
|
+
return "pogingen";
|
|
39
|
+
const start = Date.parse(String(row.approved_at ?? row.created_at ?? ""));
|
|
40
|
+
if (Number.isFinite(start) && now - start >= CONTROL_SCAN_MAX_HOURS * 3600000)
|
|
41
|
+
return "tijd";
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
31
44
|
/**
|
|
32
|
-
* Laat de controle wachten op Floriday.
|
|
33
|
-
*
|
|
45
|
+
* Laat de controle wachten op Floriday. Na het maximum (pogingen of 24 uur)
|
|
46
|
+
* eindigt de controle als mislukt in plaats van eeuwig te wachten.
|
|
34
47
|
* Geeft true terug als de controle nu terminaal is afgesloten.
|
|
35
48
|
*/
|
|
36
49
|
async function keepWaiting(sb, row) {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
50
|
+
const grens = controlWaitExceeded(row);
|
|
51
|
+
if (grens) {
|
|
39
52
|
await sb.rpc("logistics_control_scan_finish", {
|
|
40
53
|
_id: row.id,
|
|
41
54
|
_ok: false,
|
|
42
55
|
_status: "mislukt",
|
|
43
56
|
_error_code: "wachtlimiet",
|
|
44
|
-
_error:
|
|
57
|
+
_error: grens === "pogingen"
|
|
58
|
+
? `Na ${row.attempts} pogingen kende Floriday deze order nog steeds niet; de controle is gestopt.`
|
|
59
|
+
: `Na ${CONTROL_SCAN_MAX_HOURS} uur kende Floriday deze order nog steeds niet; de controle is gestopt.`,
|
|
45
60
|
});
|
|
46
61
|
return true;
|
|
47
62
|
}
|
|
@@ -84,6 +99,35 @@ export async function runControlScanQueue(sb, options = {}) {
|
|
|
84
99
|
run.failed++;
|
|
85
100
|
continue;
|
|
86
101
|
}
|
|
102
|
+
// Geannuleerd in Floriday? Dan direct stoppen.
|
|
103
|
+
const annulering = await isFloridayCancelled(sb, {
|
|
104
|
+
orderId: row.order_id,
|
|
105
|
+
orderNumber: row.order_number,
|
|
106
|
+
fulfillmentOrderId: row.fulfillment_order_id ?? null,
|
|
107
|
+
});
|
|
108
|
+
if (annulering.cancelled) {
|
|
109
|
+
await sb.rpc("logistics_control_scan_finish", {
|
|
110
|
+
_id: row.id,
|
|
111
|
+
_ok: false,
|
|
112
|
+
_status: "mislukt",
|
|
113
|
+
_error_code: "floriday_geannuleerd",
|
|
114
|
+
_error: `${FLORIDAY_CANCELLED_REASON} (${annulering.bron}).`,
|
|
115
|
+
});
|
|
116
|
+
await trace(sb, {
|
|
117
|
+
orderRef: row.order_number,
|
|
118
|
+
orderSource: "floriday",
|
|
119
|
+
cartRef: row.cart_id,
|
|
120
|
+
phase: "controle",
|
|
121
|
+
step: "Afleverbrief gestopt: order geannuleerd",
|
|
122
|
+
status: "mislukt",
|
|
123
|
+
actor: "engine:controlepost",
|
|
124
|
+
errorClass: "extern",
|
|
125
|
+
reason: `${FLORIDAY_CANCELLED_REASON} (${annulering.bron}).`,
|
|
126
|
+
error: FLORIDAY_CANCELLED_REASON,
|
|
127
|
+
});
|
|
128
|
+
run.failed++;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
87
131
|
// Stand van de push naar Floriday. De belading gaat via de bestaande
|
|
88
132
|
// pushwachtrij; wij wachten tot Floriday de fulfillment-order kent.
|
|
89
133
|
const { data: push } = await sb
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expandGroups.test.d.ts","sourceRoot":"","sources":["../../src/lib/expandGroups.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { expandOrderItemsForPlanner } from "./loadCarrierPlanning.server";
|
|
3
|
+
function stub(tables) {
|
|
4
|
+
return {
|
|
5
|
+
from(table) {
|
|
6
|
+
let rows = [...(tables[table] ?? [])];
|
|
7
|
+
const b = {
|
|
8
|
+
select: () => b,
|
|
9
|
+
order: () => b,
|
|
10
|
+
in: (col, vals) => {
|
|
11
|
+
rows = rows.filter((r) => vals.includes(r[col]));
|
|
12
|
+
return b;
|
|
13
|
+
},
|
|
14
|
+
eq: (col, v) => {
|
|
15
|
+
rows = rows.filter((r) => r[col] === v);
|
|
16
|
+
return b;
|
|
17
|
+
},
|
|
18
|
+
or: (expr) => {
|
|
19
|
+
const conds = expr.split(",").map((c) => {
|
|
20
|
+
const [col, , ...rest] = c.split(".");
|
|
21
|
+
return [col, rest.join(".")];
|
|
22
|
+
});
|
|
23
|
+
rows = rows.filter((r) => conds.some(([col, v]) => String(r[col]) === v));
|
|
24
|
+
return b;
|
|
25
|
+
},
|
|
26
|
+
then: (res) => res({ data: rows }),
|
|
27
|
+
};
|
|
28
|
+
return b;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const GERBERA = "11111111-1111-4111-8111-111111111111";
|
|
33
|
+
const GROUP = "22222222-2222-4222-8222-222222222222";
|
|
34
|
+
const KOPIE = "33333333-3333-4333-8333-333333333333";
|
|
35
|
+
const ONBEKEND = "44444444-4444-4444-8444-444444444444";
|
|
36
|
+
const products = [
|
|
37
|
+
{ id: GERBERA, product: "Gerbera Red Naomi #9", floriday_trade_item_id: GERBERA, plants_per_crate: 20, crates_per_layer: 8, layers_per_cart: 12 },
|
|
38
|
+
{ id: KOPIE, product: "Flow Selections plant (kopie)", floriday_trade_item_id: KOPIE, plants_per_crate: 6, crates_per_layer: 6, layers_per_cart: 6 },
|
|
39
|
+
];
|
|
40
|
+
describe("expandOrderItemsForPlanner — productgroepen", () => {
|
|
41
|
+
it("gekoppeld artikel gaat voor een productgroep met dezelfde naam", async () => {
|
|
42
|
+
const sb = stub({
|
|
43
|
+
products,
|
|
44
|
+
product_groups: [{ id: GROUP, name: "Gerbera Red Naomi #9" }],
|
|
45
|
+
product_group_items: [{ product_group_id: GROUP, trade_item_id: KOPIE, quantity: 1, sort_order: 0 }],
|
|
46
|
+
});
|
|
47
|
+
const out = await expandOrderItemsForPlanner(sb, [
|
|
48
|
+
{
|
|
49
|
+
id: "regel-1",
|
|
50
|
+
order_id: "o1",
|
|
51
|
+
product_id: GERBERA,
|
|
52
|
+
product_name: "Gerbera Red Naomi #9",
|
|
53
|
+
quantity: 240,
|
|
54
|
+
products: products[0],
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
expect(out).toHaveLength(1);
|
|
58
|
+
expect(out[0].product_id).toBe(GERBERA);
|
|
59
|
+
expect(out[0].product_name).toBe("Gerbera Red Naomi #9");
|
|
60
|
+
expect(out[0].plants).toBe(240);
|
|
61
|
+
});
|
|
62
|
+
it("onbekend groepslid krijgt geen id als naam en blokkeert", async () => {
|
|
63
|
+
const sources = new Map();
|
|
64
|
+
const sb = stub({
|
|
65
|
+
products,
|
|
66
|
+
product_groups: [{ id: GROUP, name: "Mix" }],
|
|
67
|
+
product_group_items: [
|
|
68
|
+
{ product_group_id: GROUP, trade_item_id: ONBEKEND, quantity: 1, sort_order: 0 },
|
|
69
|
+
{ product_group_id: GROUP, trade_item_id: KOPIE, quantity: 1, sort_order: 1 },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
const out = await expandOrderItemsForPlanner(sb, [{ id: "regel-2", order_id: "o1", product_id: GROUP, product_name: "Mix", quantity: 12 }], { sources: sources });
|
|
73
|
+
for (const i of out)
|
|
74
|
+
expect(i.product_name).not.toContain(ONBEKEND);
|
|
75
|
+
const onbekend = out.find((i) => i.order_item_id === `regel-2::${ONBEKEND}`);
|
|
76
|
+
expect(onbekend?.product_id ?? null).toBeNull();
|
|
77
|
+
expect(onbekend?.product_name).toBe("Mix — onbekend groepslid");
|
|
78
|
+
expect(sources.get(`regel-2::${ONBEKEND}`)).toBe("geen");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
export type CancellationResult = {
|
|
3
|
+
cancelled: true;
|
|
4
|
+
bron: string;
|
|
5
|
+
} | {
|
|
6
|
+
cancelled: false;
|
|
7
|
+
unknown?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const FLORIDAY_CANCELLED_REASON = "Floriday heeft deze order geannuleerd";
|
|
10
|
+
export declare function isFloridayCancelled(sb: SupabaseClient, input: {
|
|
11
|
+
orderId?: string | null;
|
|
12
|
+
orderNumber?: string | null;
|
|
13
|
+
fulfillmentOrderId?: string | null;
|
|
14
|
+
}): Promise<CancellationResult>;
|
|
15
|
+
//# sourceMappingURL=floridayCancellation.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floridayCancellation.server.d.ts","sourceRoot":"","sources":["../../src/lib/floridayCancellation.server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAC1B;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5C,eAAO,MAAM,yBAAyB,0CAA0C,CAAC;AAIjF,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAClG,OAAO,CAAC,kBAAkB,CAAC,CAuE7B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/lib/floridayCancellation.server.ts — is de order in Floriday geannuleerd?
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Leest alleen de gegevens die de Floriday-koppelmodule al periodiek ophaalt
|
|
5
|
+
// (salesorders, fulfillment-orders, delivery-orders). Geen extra API-aanroep.
|
|
6
|
+
// Niet leesbaar of geen gegevens => "onbekend": de wachtlus gaat dan gewoon
|
|
7
|
+
// door en de eindgrens vangt het op. Er wordt nooit een annulering verzonnen.
|
|
8
|
+
// ============================================================================
|
|
9
|
+
export const FLORIDAY_CANCELLED_REASON = "Floriday heeft deze order geannuleerd";
|
|
10
|
+
const isCancelledStatus = (s) => String(s ?? "").toUpperCase() === "CANCELLED";
|
|
11
|
+
export async function isFloridayCancelled(sb, input) {
|
|
12
|
+
try {
|
|
13
|
+
let unknown = false;
|
|
14
|
+
// Salesorder-id: eerst van de order zelf, anders via de nummerkoppeling.
|
|
15
|
+
let salesOrderId = null;
|
|
16
|
+
if (input.orderId) {
|
|
17
|
+
const { data, error } = await sb
|
|
18
|
+
.from("orders")
|
|
19
|
+
.select("floriday_sales_order_id")
|
|
20
|
+
.eq("id", input.orderId)
|
|
21
|
+
.maybeSingle();
|
|
22
|
+
if (error)
|
|
23
|
+
unknown = true;
|
|
24
|
+
salesOrderId = String(data?.floriday_sales_order_id ?? "").trim() || null;
|
|
25
|
+
}
|
|
26
|
+
if (!salesOrderId && input.orderNumber) {
|
|
27
|
+
const { data, error } = await sb
|
|
28
|
+
.from("floriday_order_number_map")
|
|
29
|
+
.select("floriday_id")
|
|
30
|
+
.eq("order_number", input.orderNumber)
|
|
31
|
+
.limit(1)
|
|
32
|
+
.maybeSingle();
|
|
33
|
+
if (error)
|
|
34
|
+
unknown = true;
|
|
35
|
+
salesOrderId = String(data?.floriday_id ?? "").trim() || null;
|
|
36
|
+
}
|
|
37
|
+
if (salesOrderId) {
|
|
38
|
+
const { data, error } = await sb
|
|
39
|
+
.from("floriday_salesorders_cache")
|
|
40
|
+
.select("status, is_deleted, data")
|
|
41
|
+
.eq("floriday_id", salesOrderId)
|
|
42
|
+
.maybeSingle();
|
|
43
|
+
if (error)
|
|
44
|
+
unknown = true;
|
|
45
|
+
const so = data;
|
|
46
|
+
if (so) {
|
|
47
|
+
if (isCancelledStatus(so.status) || isCancelledStatus(so.data?.status)) {
|
|
48
|
+
return { cancelled: true, bron: "salesorder" };
|
|
49
|
+
}
|
|
50
|
+
if (so.data?.automaticallyCancelledOn)
|
|
51
|
+
return { cancelled: true, bron: "salesorder (automatisch geannuleerd)" };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (input.fulfillmentOrderId) {
|
|
55
|
+
const { data, error } = await sb
|
|
56
|
+
.from("floriday_fulfillment_orders_cache")
|
|
57
|
+
.select("status, is_deleted, delivery_order_id")
|
|
58
|
+
.eq("floriday_id", input.fulfillmentOrderId)
|
|
59
|
+
.maybeSingle();
|
|
60
|
+
if (error)
|
|
61
|
+
unknown = true;
|
|
62
|
+
const fo = data;
|
|
63
|
+
if (fo) {
|
|
64
|
+
if (isCancelledStatus(fo.status))
|
|
65
|
+
return { cancelled: true, bron: "fulfillment-order" };
|
|
66
|
+
if (fo.is_deleted === true)
|
|
67
|
+
return { cancelled: true, bron: "fulfillment-order (verwijderd)" };
|
|
68
|
+
if (fo.delivery_order_id) {
|
|
69
|
+
const { data: d, error: dErr } = await sb
|
|
70
|
+
.from("floriday_delivery_orders_cache")
|
|
71
|
+
.select("status, is_deleted")
|
|
72
|
+
.eq("floriday_id", fo.delivery_order_id)
|
|
73
|
+
.maybeSingle();
|
|
74
|
+
if (dErr)
|
|
75
|
+
unknown = true;
|
|
76
|
+
const del = d;
|
|
77
|
+
if (del && isCancelledStatus(del.status))
|
|
78
|
+
return { cancelled: true, bron: "delivery-order" };
|
|
79
|
+
if (del && del.is_deleted === true)
|
|
80
|
+
return { cancelled: true, bron: "delivery-order (verwijderd)" };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return unknown ? { cancelled: false, unknown: true } : { cancelled: false };
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return { cancelled: false, unknown: true };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floridayCancellation.test.d.ts","sourceRoot":"","sources":["../../src/lib/floridayCancellation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { isFloridayCancelled } from "./floridayCancellation.server";
|
|
3
|
+
import { controlWaitExceeded } from "./controlScan.server";
|
|
4
|
+
function stub(rows) {
|
|
5
|
+
return {
|
|
6
|
+
from(table) {
|
|
7
|
+
const q = {
|
|
8
|
+
select: () => q,
|
|
9
|
+
eq: () => q,
|
|
10
|
+
limit: () => q,
|
|
11
|
+
maybeSingle: async () => {
|
|
12
|
+
const r = rows[table];
|
|
13
|
+
return r === "error" ? { data: null, error: { message: "x" } } : { data: r ?? null, error: null };
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
return q;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
describe("isFloridayCancelled", () => {
|
|
21
|
+
it("geannuleerde salesorder", async () => {
|
|
22
|
+
const r = await isFloridayCancelled(stub({ orders: { floriday_sales_order_id: "so1" }, floriday_salesorders_cache: { status: "CANCELLED", data: {} } }), { orderId: "o1" });
|
|
23
|
+
expect(r.cancelled).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it("geannuleerde fulfillment-order", async () => {
|
|
26
|
+
const r = await isFloridayCancelled(stub({ orders: null, floriday_fulfillment_orders_cache: { status: "CANCELLED" } }), { orderId: "o1", fulfillmentOrderId: "fo1" });
|
|
27
|
+
expect(r).toEqual({ cancelled: true, bron: "fulfillment-order" });
|
|
28
|
+
});
|
|
29
|
+
it("onleesbare gegevens => niet geannuleerd, onbekend", async () => {
|
|
30
|
+
const r = await isFloridayCancelled(stub({ orders: "error", floriday_order_number_map: "error" }), {
|
|
31
|
+
orderId: "o1",
|
|
32
|
+
orderNumber: "FO-1",
|
|
33
|
+
});
|
|
34
|
+
expect(r).toEqual({ cancelled: false, unknown: true });
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("controlWaitExceeded", () => {
|
|
38
|
+
const now = Date.parse("2026-10-02T12:00:00Z");
|
|
39
|
+
it("na 24 uur", () => expect(controlWaitExceeded({ attempts: 3, approved_at: "2026-10-01T11:00:00Z" }, now)).toBe("tijd"));
|
|
40
|
+
it("na 60 pogingen", () => expect(controlWaitExceeded({ attempts: 60 }, now)).toBe("pogingen"));
|
|
41
|
+
it("binnen grens", () => expect(controlWaitExceeded({ attempts: 5, approved_at: "2026-10-02T10:00:00Z" }, now)).toBeNull());
|
|
42
|
+
});
|