@flowselections/floriday-verkoop-module 1.0.93 → 1.0.98
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/verkoop/CreateOrderPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/CreateOrderPage.js +82 -43
- package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -1
- package/dist-lib/components/verkoop/OrderKarindelingCard.js +1 -1
- package/dist-lib/components/verkoop/OrderPickupMomentPicker.d.ts.map +1 -1
- package/dist-lib/components/verkoop/OrderPickupMomentPicker.js +2 -1
- package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts.map +1 -1
- package/dist-lib/components/verkoop/PlanCoverageBadge.js +7 -5
- package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.js +2 -1
- package/dist-lib/integrations/supabase/types.d.ts +327 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/carrier-availability.d.ts +0 -28
- package/dist-lib/lib/carrier-availability.d.ts.map +1 -1
- package/dist-lib/lib/carrier-availability.js +3 -141
- package/dist-lib/lib/cart-replan.functions.d.ts +5 -0
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.js +25 -1
- package/dist-lib/lib/create-order.functions.d.ts +5 -0
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.js +81 -54
- package/dist-lib/lib/departure-preview.check.d.ts +2 -0
- package/dist-lib/lib/departure-preview.check.d.ts.map +1 -0
- package/dist-lib/lib/departure-preview.check.js +37 -0
- package/dist-lib/lib/departure-preview.d.ts +47 -0
- package/dist-lib/lib/departure-preview.d.ts.map +1 -0
- package/dist-lib/lib/departure-preview.functions.d.ts +13 -0
- package/dist-lib/lib/departure-preview.functions.d.ts.map +1 -0
- package/dist-lib/lib/departure-preview.functions.js +17 -0
- package/dist-lib/lib/departure-preview.js +70 -0
- package/dist-lib/lib/departure-preview.server.d.ts +10 -0
- package/dist-lib/lib/departure-preview.server.d.ts.map +1 -0
- package/dist-lib/lib/departure-preview.server.js +29 -0
- package/dist-lib/lib/floriday-external-fulfillment.check.d.ts +2 -0
- package/dist-lib/lib/floriday-external-fulfillment.check.d.ts.map +1 -0
- package/dist-lib/lib/floriday-external-fulfillment.check.js +21 -0
- package/dist-lib/lib/floriday-external-fulfillment.d.ts +19 -0
- package/dist-lib/lib/floriday-external-fulfillment.d.ts.map +1 -0
- package/dist-lib/lib/floriday-external-fulfillment.js +24 -0
- package/dist-lib/lib/floriday-external-fulfillment.server.d.ts +5 -0
- package/dist-lib/lib/floriday-external-fulfillment.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-external-fulfillment.server.js +110 -0
- package/dist-lib/lib/floriday-fulfillment-status.server.d.ts +2 -0
- package/dist-lib/lib/floriday-fulfillment-status.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-fulfillment-status.server.js +3 -0
- package/dist-lib/lib/floriday-line-key.check.d.ts +2 -0
- package/dist-lib/lib/floriday-line-key.check.d.ts.map +1 -0
- package/dist-lib/lib/floriday-line-key.check.js +31 -0
- package/dist-lib/lib/floriday-line-key.d.ts +12 -0
- package/dist-lib/lib/floriday-line-key.d.ts.map +1 -0
- package/dist-lib/lib/floriday-line-key.js +20 -0
- package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.js +38 -2
- package/dist-lib/lib/logistics-engine.functions.d.ts +1 -1
- package/dist-lib/lib/order-departure.server.d.ts +1 -0
- package/dist-lib/lib/order-departure.server.d.ts.map +1 -1
- package/dist-lib/lib/order-departure.server.js +16 -4
- package/dist-lib/lib/order-pickup.functions.d.ts +4 -0
- package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-pickup.functions.js +7 -0
- package/dist-lib/lib/plan-coverage.d.ts +13 -1
- package/dist-lib/lib/plan-coverage.d.ts.map +1 -1
- package/dist-lib/lib/plan-coverage.js +20 -1
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +6 -6
- package/dist-lib/lib/salesorders-mapping.d.ts +5 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +6 -0
- package/package.json +2 -2
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Voorbeeld van het ophaalmoment vóór er een order is, zoals de Logistiek
|
|
2
|
+
// Engine het geeft (logistics_departure_preview). Alleen normaliseren en
|
|
3
|
+
// beslissen wat je ziet; deze module rekent zelf niets uit.
|
|
4
|
+
import { formatMoment } from "./order-departure.js";
|
|
5
|
+
const s = (v) => (typeof v === "string" && v.trim() !== "" ? v : null);
|
|
6
|
+
export function normalizePreview(raw) {
|
|
7
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
8
|
+
return null;
|
|
9
|
+
const r = raw;
|
|
10
|
+
const moments = Array.isArray(r.moments)
|
|
11
|
+
? r.moments
|
|
12
|
+
.filter((m) => m && s(m.momentKey))
|
|
13
|
+
.map((m) => ({
|
|
14
|
+
momentKey: String(m.momentKey),
|
|
15
|
+
departureAt: s(m.departureAt),
|
|
16
|
+
signupDeadline: s(m.signupDeadline),
|
|
17
|
+
feasible: m.feasible === true,
|
|
18
|
+
}))
|
|
19
|
+
: [];
|
|
20
|
+
const c = r.chosen && typeof r.chosen === "object" ? r.chosen : null;
|
|
21
|
+
return {
|
|
22
|
+
ok: r.ok === true,
|
|
23
|
+
reason: s(r.reason),
|
|
24
|
+
carrierId: s(r.carrierId),
|
|
25
|
+
moments,
|
|
26
|
+
chosen: c
|
|
27
|
+
? { momentKey: s(c.momentKey), departureAt: s(c.departureAt), signupDeadline: s(c.signupDeadline) }
|
|
28
|
+
: null,
|
|
29
|
+
deliveryDateFeasible: r.deliveryDateFeasible === true,
|
|
30
|
+
deliveryDateReason: s(r.deliveryDateReason) ?? s(r.reason),
|
|
31
|
+
firstFeasibleDeliveryDate: s(r.firstFeasibleDeliveryDate),
|
|
32
|
+
firstFeasibleReason: s(r.firstFeasibleReason),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export const PREVIEW_UNKNOWN = "Het ophaalmoment is nu niet bekend. Probeer het over een minuut opnieuw.";
|
|
36
|
+
function momentText(departureAt, signup) {
|
|
37
|
+
const p = formatMoment(departureAt);
|
|
38
|
+
if (!p)
|
|
39
|
+
return null;
|
|
40
|
+
const a = formatMoment(signup);
|
|
41
|
+
return a ? `ophalen ${p} · aanmelden ${a}` : `ophalen ${p}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Wat je ziet / of je mag aanmaken.
|
|
45
|
+
* - ok = false of geen antwoord: geblokkeerd, alleen de reden.
|
|
46
|
+
* - leverdatum niet haalbaar: "te laat" — alleen aanmaken na bewuste keuze.
|
|
47
|
+
*/
|
|
48
|
+
export function decidePreview(p) {
|
|
49
|
+
if (!p)
|
|
50
|
+
return { kind: "blocked", reason: PREVIEW_UNKNOWN };
|
|
51
|
+
if (!p.ok)
|
|
52
|
+
return { kind: "blocked", reason: p.reason ?? "Voor deze leverdatum is geen ophaalmoment mogelijk." };
|
|
53
|
+
const momentKey = p.chosen?.momentKey ?? null;
|
|
54
|
+
const text = momentText(p.chosen?.departureAt ?? null, p.chosen?.signupDeadline ?? null);
|
|
55
|
+
if (!p.deliveryDateFeasible) {
|
|
56
|
+
return {
|
|
57
|
+
kind: "late",
|
|
58
|
+
reason: p.deliveryDateReason ?? "Aanmelden voor deze leverdatum kan niet meer.",
|
|
59
|
+
firstDate: p.firstFeasibleDeliveryDate,
|
|
60
|
+
firstReason: p.firstFeasibleReason,
|
|
61
|
+
momentKey,
|
|
62
|
+
text,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { kind: "ok", momentKey, text };
|
|
66
|
+
}
|
|
67
|
+
export function momentLabel(m) {
|
|
68
|
+
return momentText(m.departureAt, m.signupDeadline) ?? m.momentKey;
|
|
69
|
+
}
|
|
70
|
+
export const LATE_CONFIRM = "Deze order komt na de aanmeldtijd. De vervoerder krijgt hem als te laat aangemeld. Doorgaan?";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type DeparturePreview } from "./departure-preview.js";
|
|
2
|
+
export declare function loadDeparturePreview(supabase: any, input: {
|
|
3
|
+
carrierId: string | null;
|
|
4
|
+
deliveryLocation: string;
|
|
5
|
+
deliveryDate: string;
|
|
6
|
+
momentKey?: string | null;
|
|
7
|
+
}): Promise<DeparturePreview | null>;
|
|
8
|
+
/** Aanlevertijd opzoeken bij het door de Engine gekozen moment (geen berekening). */
|
|
9
|
+
export declare function deliveryTimeForMoment(supabase: any, carrierId: string | null, momentKey: string | null): Promise<string | null>;
|
|
10
|
+
//# sourceMappingURL=departure-preview.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"departure-preview.server.d.ts","sourceRoot":"","sources":["../../src/lib/departure-preview.server.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,GAAG,EACb,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7G,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAiBlC;AAED,qFAAqF;AACrF,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,GAAG,EACb,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Vraagt het voorbeeld-ophaalmoment op bij de Engine. Fout => null (fail closed).
|
|
2
|
+
import { normalizePreview } from "./departure-preview.js";
|
|
3
|
+
export async function loadDeparturePreview(supabase, input) {
|
|
4
|
+
try {
|
|
5
|
+
const { data, error } = await supabase.rpc("logistics_departure_preview", {
|
|
6
|
+
p_carrier_id: input.carrierId ?? null,
|
|
7
|
+
p_delivery_location: input.deliveryLocation,
|
|
8
|
+
p_delivery_date: input.deliveryDate,
|
|
9
|
+
p_moment_key: input.momentKey ?? null,
|
|
10
|
+
});
|
|
11
|
+
if (error) {
|
|
12
|
+
console.warn("[departure-preview]", error.message);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return normalizePreview(data);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
console.warn("[departure-preview]", err);
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Aanlevertijd opzoeken bij het door de Engine gekozen moment (geen berekening). */
|
|
23
|
+
export async function deliveryTimeForMoment(supabase, carrierId, momentKey) {
|
|
24
|
+
if (!carrierId || !momentKey)
|
|
25
|
+
return null;
|
|
26
|
+
const { data } = await supabase.from("carriers").select("pickup_moments").eq("id", carrierId).maybeSingle();
|
|
27
|
+
const { carrierMomentOptions } = await import("./order-pickup.functions.js");
|
|
28
|
+
return carrierMomentOptions(data ?? null).find((o) => o.key === momentKey)?.deliveryTime ?? null;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-external-fulfillment.check.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-external-fulfillment.check.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Controle "afgehandeld in Floriday". Draai: bun src/lib/floriday-external-fulfillment.check.ts
|
|
2
|
+
import { decideExternalFulfillment as d } from "./floriday-external-fulfillment.js";
|
|
3
|
+
let failed = 0;
|
|
4
|
+
const check = (n, got, want) => { const ok = got === want; if (!ok)
|
|
5
|
+
failed++; console.log(`${ok ? "OK " : "FOUT"} ${n}: ${got}${ok ? "" : ` (verwacht ${want})`}`); };
|
|
6
|
+
const base = { status: "Nog te picken", previousIds: [], alreadyFull: false };
|
|
7
|
+
check("één bestelling, minder fust, buiten ERP → verzonden", d({ ...base, salesOrderIds: ["a"], isExternal: () => true }).kind, "full");
|
|
8
|
+
check("door ERP gemaakt → niets", d({ ...base, salesOrderIds: ["a"], isExternal: () => false }).kind, "none");
|
|
9
|
+
check("samengevoegd, alle afgehandeld → verzonden", d({ ...base, salesOrderIds: ["a", "b"], isExternal: () => true }).kind, "full");
|
|
10
|
+
const p = d({ ...base, salesOrderIds: ["a", "b"], isExternal: (id) => id === "a" });
|
|
11
|
+
check("samengevoegd, deel → partial", p.kind, "partial");
|
|
12
|
+
check("deel: alleen a", p.kind === "partial" ? p.fulfilledIds.join() : "", "a");
|
|
13
|
+
check("deel al vastgelegd → niets nieuws", d({ ...base, previousIds: ["a"], salesOrderIds: ["a", "b"], isExternal: (id) => id === "a" }).kind, "none");
|
|
14
|
+
check("al verzonden door ERP → blijft", d({ ...base, status: "Verzonden", salesOrderIds: ["a"], isExternal: () => true }).kind, "none");
|
|
15
|
+
check("geannuleerd → blijft", d({ ...base, status: "geannuleerd", salesOrderIds: ["a"], isExternal: () => true }).kind, "none");
|
|
16
|
+
check("al afgerond → niet nogmaals", d({ ...base, alreadyFull: true, salesOrderIds: ["a"], isExternal: () => true }).kind, "none");
|
|
17
|
+
if (failed) {
|
|
18
|
+
console.error(`${failed} controle(s) mislukt`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
console.log("Alle controles geslaagd");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const FLORIDAY_EXTERNAL_REASON = "Afgehandeld in Floriday";
|
|
2
|
+
export declare const SHIPPED_STATUS = "Verzonden";
|
|
3
|
+
export type ExternalDecision = {
|
|
4
|
+
kind: "none";
|
|
5
|
+
} | {
|
|
6
|
+
kind: "full";
|
|
7
|
+
fulfilledIds: string[];
|
|
8
|
+
} | {
|
|
9
|
+
kind: "partial";
|
|
10
|
+
fulfilledIds: string[];
|
|
11
|
+
};
|
|
12
|
+
export declare function decideExternalFulfillment(input: {
|
|
13
|
+
status: string | null | undefined;
|
|
14
|
+
salesOrderIds: string[];
|
|
15
|
+
isExternal: (salesOrderId: string) => boolean;
|
|
16
|
+
previousIds: string[];
|
|
17
|
+
alreadyFull: boolean;
|
|
18
|
+
}): ExternalDecision;
|
|
19
|
+
//# sourceMappingURL=floriday-external-fulfillment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-external-fulfillment.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-external-fulfillment.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,wBAAwB,4BAA4B,CAAC;AAClE,eAAO,MAAM,cAAc,cAAc,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEhD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG,gBAAgB,CAcnB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Beslisregel "afgehandeld in Floriday" (buiten het ERP om). Zuiver, geen I/O.
|
|
2
|
+
// Per Floriday-bestelling: er bestaat een afhandeling die niet door het ERP is
|
|
3
|
+
// gemaakt. Het aantal fust telt niet mee.
|
|
4
|
+
export const FLORIDAY_EXTERNAL_REASON = "Afgehandeld in Floriday";
|
|
5
|
+
export const SHIPPED_STATUS = "Verzonden";
|
|
6
|
+
export function decideExternalFulfillment(input) {
|
|
7
|
+
const st = String(input.status ?? "").trim().toLowerCase();
|
|
8
|
+
// Al verzonden (ERP) of geannuleerd: blijft zoals hij is.
|
|
9
|
+
if (input.alreadyFull || ["verzonden", "shipped", "geleverd", "delivered", "geannuleerd"].includes(st)) {
|
|
10
|
+
return { kind: "none" };
|
|
11
|
+
}
|
|
12
|
+
const ids = Array.from(new Set(input.salesOrderIds.filter(Boolean)));
|
|
13
|
+
if (ids.length === 0)
|
|
14
|
+
return { kind: "none" };
|
|
15
|
+
const done = ids.filter((id) => input.isExternal(id));
|
|
16
|
+
if (done.length === 0)
|
|
17
|
+
return { kind: "none" };
|
|
18
|
+
if (done.length === ids.length)
|
|
19
|
+
return { kind: "full", fulfilledIds: done };
|
|
20
|
+
const prev = new Set(input.previousIds);
|
|
21
|
+
if (done.length === prev.size && done.every((id) => prev.has(id)))
|
|
22
|
+
return { kind: "none" };
|
|
23
|
+
return { kind: "partial", fulfilledIds: done };
|
|
24
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FloridayFulfillmentState } from "./floriday-fulfillment-status.server.js";
|
|
2
|
+
export declare function handleFloridayExternalFulfillment(orderId: string, states?: Map<string, FloridayFulfillmentState>): Promise<"none" | "shipped" | "partial">;
|
|
3
|
+
/** Haalt het event-id uit het antwoord van logistics_emit_event (uuid, of object met id/eventId). */
|
|
4
|
+
export declare function extractEventId(data: unknown): string | null;
|
|
5
|
+
//# sourceMappingURL=floriday-external-fulfillment.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-external-fulfillment.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-external-fulfillment.server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAErF,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,wBAAwB,CAAC,GAC7C,OAAO,CAAC,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC,CAgFzC;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQ3D"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Order die in Floriday zelf is afgehandeld (buiten het ERP om) netjes afronden.
|
|
2
|
+
// Naar het voorbeeld van handleFloridayCancellation. Geen melding voor kantoor,
|
|
3
|
+
// geen voorraadmutatie (dat is aan Voorraad), schrijft nooit order_carts.
|
|
4
|
+
import { decideExternalFulfillment, FLORIDAY_EXTERNAL_REASON, SHIPPED_STATUS } from "./floriday-external-fulfillment.js";
|
|
5
|
+
import { externalFulfilledIdempotencyKey } from "./floriday-line-key.js";
|
|
6
|
+
export async function handleFloridayExternalFulfillment(orderId, states) {
|
|
7
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
8
|
+
const { data: order, error } = await supabaseAdmin
|
|
9
|
+
.from("orders")
|
|
10
|
+
.select("id, status, custom_fields")
|
|
11
|
+
.eq("id", orderId)
|
|
12
|
+
.maybeSingle();
|
|
13
|
+
if (error)
|
|
14
|
+
throw new Error(error.message);
|
|
15
|
+
if (!order)
|
|
16
|
+
return "none";
|
|
17
|
+
const o = order;
|
|
18
|
+
const cf = (o.custom_fields ?? {});
|
|
19
|
+
const soIds = Array.isArray(cf.floriday_sales_order_ids) && cf.floriday_sales_order_ids.length > 0
|
|
20
|
+
? cf.floriday_sales_order_ids.map(String)
|
|
21
|
+
: cf.floriday_sales_order_id ? [String(cf.floriday_sales_order_id)] : [];
|
|
22
|
+
if (soIds.length === 0)
|
|
23
|
+
return "none";
|
|
24
|
+
// Ontbrekende bestellingen in de meegegeven stand: vers lezen (fail closed = niet afgehandeld).
|
|
25
|
+
let map = states ?? new Map();
|
|
26
|
+
if (soIds.some((id) => !map.has(id))) {
|
|
27
|
+
const { loadFloridayFulfillmentStates } = await import("./floriday-fulfillment-status.server.js");
|
|
28
|
+
const fresh = await loadFloridayFulfillmentStates(supabaseAdmin, soIds);
|
|
29
|
+
map = new Map([...map, ...fresh]);
|
|
30
|
+
}
|
|
31
|
+
const decision = decideExternalFulfillment({
|
|
32
|
+
status: o.status,
|
|
33
|
+
salesOrderIds: soIds,
|
|
34
|
+
isExternal: (id) => Boolean(map.get(id)?.external),
|
|
35
|
+
previousIds: Array.isArray(cf.floriday_external_fulfilled_sales_order_ids) ? cf.floriday_external_fulfilled_sales_order_ids : [],
|
|
36
|
+
alreadyFull: Boolean(cf.floriday_external_fulfilled_at),
|
|
37
|
+
});
|
|
38
|
+
if (decision.kind === "none")
|
|
39
|
+
return "none";
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
const nextCf = { ...cf, floriday_external_fulfilled_sales_order_ids: decision.fulfilledIds };
|
|
42
|
+
let patch;
|
|
43
|
+
if (decision.kind === "full") {
|
|
44
|
+
nextCf.floriday_external_fulfilled_at = now;
|
|
45
|
+
nextCf.floriday_external_fulfilled_notice = FLORIDAY_EXTERNAL_REASON;
|
|
46
|
+
patch = { status: SHIPPED_STATUS, status_sync_token: globalThis.crypto.randomUUID(), custom_fields: nextCf };
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
patch = { custom_fields: nextCf };
|
|
50
|
+
}
|
|
51
|
+
const { error: uErr } = await supabaseAdmin.from("orders").update(patch).eq("id", orderId);
|
|
52
|
+
if (uErr)
|
|
53
|
+
throw new Error(uErr.message);
|
|
54
|
+
if (decision.kind === "full") {
|
|
55
|
+
await supabaseAdmin.from("cart_replan_queue").delete().eq("order_id", orderId);
|
|
56
|
+
}
|
|
57
|
+
// Engine melden via logistics_emit_event (idempotent); event-id of fout op de order bewaren.
|
|
58
|
+
const ids = [...new Set(decision.fulfilledIds.map(String))].sort();
|
|
59
|
+
let eventId = null;
|
|
60
|
+
let emitError = null;
|
|
61
|
+
try {
|
|
62
|
+
const { data, error: rpcErr } = await supabaseAdmin.rpc("logistics_emit_event", {
|
|
63
|
+
p_source_module: "verkoop",
|
|
64
|
+
p_event_type: "order.changed",
|
|
65
|
+
p_order_ref: orderId,
|
|
66
|
+
p_payload: {
|
|
67
|
+
orderId,
|
|
68
|
+
reason: FLORIDAY_EXTERNAL_REASON,
|
|
69
|
+
floridayExternalFulfilledFull: decision.kind === "full",
|
|
70
|
+
floridayExternalFulfilledSalesOrderIds: ids,
|
|
71
|
+
},
|
|
72
|
+
p_idempotency_key: externalFulfilledIdempotencyKey(orderId, ids),
|
|
73
|
+
});
|
|
74
|
+
if (rpcErr)
|
|
75
|
+
emitError = rpcErr.message;
|
|
76
|
+
else
|
|
77
|
+
eventId = extractEventId(data);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
emitError = err.message;
|
|
81
|
+
}
|
|
82
|
+
const afterCf = { ...nextCf };
|
|
83
|
+
if (eventId)
|
|
84
|
+
afterCf.floriday_external_engine_event_id = eventId;
|
|
85
|
+
else
|
|
86
|
+
delete afterCf.floriday_external_engine_event_id;
|
|
87
|
+
if (emitError)
|
|
88
|
+
afterCf.floriday_external_engine_error = emitError.slice(0, 300);
|
|
89
|
+
else
|
|
90
|
+
delete afterCf.floriday_external_engine_error;
|
|
91
|
+
const { error: eErr } = await supabaseAdmin.from("orders").update({ custom_fields: afterCf }).eq("id", orderId);
|
|
92
|
+
if (eErr)
|
|
93
|
+
console.warn("[floriday-external] event-id bewaren faalde:", eErr.message);
|
|
94
|
+
if (emitError)
|
|
95
|
+
console.warn("[floriday-external] engine-melding faalde:", emitError);
|
|
96
|
+
console.info(`[floriday-external] order ${orderId}: ${decision.kind} (${decision.fulfilledIds.length}/${soIds.length})`);
|
|
97
|
+
return decision.kind === "full" ? "shipped" : "partial";
|
|
98
|
+
}
|
|
99
|
+
/** Haalt het event-id uit het antwoord van logistics_emit_event (uuid, of object met id/eventId). */
|
|
100
|
+
export function extractEventId(data) {
|
|
101
|
+
if (typeof data === "string" && data.trim())
|
|
102
|
+
return data.trim();
|
|
103
|
+
if (data && typeof data === "object") {
|
|
104
|
+
const d = data;
|
|
105
|
+
const v = d.eventId ?? d.event_id ?? d.id;
|
|
106
|
+
if (typeof v === "string" && v.trim())
|
|
107
|
+
return v.trim();
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
@@ -11,6 +11,8 @@ export interface FloridayFulfillmentState {
|
|
|
11
11
|
deliveryNoteCodes: string[];
|
|
12
12
|
/** Aangemaakt door ons ERP (auto-fulfillment) i.p.v. rechtstreeks in Floriday. */
|
|
13
13
|
createdByErp: boolean;
|
|
14
|
+
/** Minstens één afhandeling die NIET door het ERP is gemaakt (rechtstreeks in Floriday). */
|
|
15
|
+
external: boolean;
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* Bouwt een map salesOrderId -> afhandelstatus. Eén query over de
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floriday-fulfillment-status.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-fulfillment-status.server.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,kFAAkF;IAClF,YAAY,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"floriday-fulfillment-status.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-fulfillment-status.server.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,kFAAkF;IAClF,YAAY,EAAE,OAAO,CAAC;IACtB,4FAA4F;IAC5F,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,cAAc,EACxB,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAiEhD;AAED,+EAA+E;AAC/E,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAIhE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,wBAAwB,GAAG,SAAS,EAC3C,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAKT"}
|
|
@@ -50,6 +50,7 @@ export async function loadFloridayFulfillmentStates(supabase, salesOrderIds) {
|
|
|
50
50
|
receivedAt: null,
|
|
51
51
|
deliveryNoteCodes: [],
|
|
52
52
|
createdByErp: false,
|
|
53
|
+
external: false,
|
|
53
54
|
};
|
|
54
55
|
prev.fulfilled = true;
|
|
55
56
|
prev.packages += Number(item?.numberOfPackages ?? 0) || 0;
|
|
@@ -63,6 +64,8 @@ export async function loadFloridayFulfillmentStates(supabase, salesOrderIds) {
|
|
|
63
64
|
}
|
|
64
65
|
if (byErp)
|
|
65
66
|
prev.createdByErp = true;
|
|
67
|
+
else
|
|
68
|
+
prev.external = true;
|
|
66
69
|
result.set(soId, prev);
|
|
67
70
|
}
|
|
68
71
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-line-key.check.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-line-key.check.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Draai met: bun src/lib/floriday-line-key.check.ts
|
|
2
|
+
import { externalFulfilledIdempotencyKey, isValidSalesOrderId, splitByLineKey } from "./floriday-line-key.js";
|
|
3
|
+
import { parseExternal, parsePlanCoverage, parsePlanCoverageListRow } from "./plan-coverage.js";
|
|
4
|
+
import { extractEventId } from "./floriday-external-fulfillment.server.js";
|
|
5
|
+
let fail = 0;
|
|
6
|
+
const ok = (c, m) => {
|
|
7
|
+
if (!c)
|
|
8
|
+
fail++;
|
|
9
|
+
console.log(`${c ? "OK " : "FOUT"} ${m}`);
|
|
10
|
+
};
|
|
11
|
+
const A = "11111111-1111-4111-8111-111111111111";
|
|
12
|
+
const B = "22222222-2222-4222-8222-222222222222";
|
|
13
|
+
ok(isValidSalesOrderId(A), "uuid is geldige sleutel");
|
|
14
|
+
ok(!isValidSalesOrderId("undefined") && !isValidSalesOrderId("") && !isValidSalesOrderId(null), "lege/onzin-sleutel ongeldig");
|
|
15
|
+
const s = splitByLineKey([{ foId: A }, { foId: B }, { foId: "null" }]);
|
|
16
|
+
ok(s.valid.length === 2 && s.invalid.length === 1, "samengevoegde order: twee geldige regels, één apart gemeld");
|
|
17
|
+
ok(s.valid.every((l) => isValidSalesOrderId(l.foId)), "elke opgeslagen regel heeft een uuid-sleutel");
|
|
18
|
+
ok(externalFulfilledIdempotencyKey("o1", [B, A, A]) === `extern-afgehandeld:o1:${A},${B}`, "idempotentiesleutel gesorteerd en ontdubbeld");
|
|
19
|
+
const full = parsePlanCoverage({ state: "afgehandeld_extern", externalFulfilled: { full: true, salesOrderIds: [A] } });
|
|
20
|
+
ok(full.state === "afgehandeld_extern" && full.label === "Afgehandeld in Floriday", "volledig: 'Afgehandeld in Floriday'");
|
|
21
|
+
ok(full.externalFulfilled?.full === true, "externalFulfilled.full gelezen");
|
|
22
|
+
const part = parsePlanCoverage({ state: "deels", progressPct: 40, externalFulfilled: { full: false, salesOrderIds: [A] } });
|
|
23
|
+
ok(part.progressPct === 40 && part.externalFulfilled?.full === false, "deels: voortgang van de Engine overgenomen");
|
|
24
|
+
ok(parseExternal({ full: "ja" }) === null, "full geen boolean → genegeerd");
|
|
25
|
+
ok(parsePlanCoverageListRow({ state: "afgehandeld_extern" }).label === "Afgehandeld in Floriday", "lijst: label");
|
|
26
|
+
ok(extractEventId({ eventId: A }) === A && extractEventId(A) === A && extractEventId({}) === null, "event-id uit antwoord");
|
|
27
|
+
if (fail) {
|
|
28
|
+
console.error(`${fail} controle(s) mislukt`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
console.log("Alle controles geslaagd");
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const MISSING_LINE_KEY_NOTICE = "Een regel van deze Floriday-order mist het bestelnummer en is niet overgenomen";
|
|
2
|
+
export declare function isValidSalesOrderId(v: unknown): v is string;
|
|
3
|
+
/** Splitst regels in geldige (met uuid-sleutel) en ongeldige. */
|
|
4
|
+
export declare function splitByLineKey<T extends {
|
|
5
|
+
foId: unknown;
|
|
6
|
+
}>(lines: T[]): {
|
|
7
|
+
valid: T[];
|
|
8
|
+
invalid: T[];
|
|
9
|
+
};
|
|
10
|
+
/** Idempotentiesleutel voor de melding "Afgehandeld in Floriday" (id's gesorteerd). */
|
|
11
|
+
export declare function externalFulfilledIdempotencyKey(orderId: string, salesOrderIds: string[]): string;
|
|
12
|
+
//# sourceMappingURL=floriday-line-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-line-key.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-line-key.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,uBAAuB,mFAC8C,CAAC;AAEnF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAE3D;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,CAAC,EAAE,CAAA;CAAE,CAKpG;AAED,uFAAuF;AACvF,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAEhG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Herkomstsleutel van een Floriday-orderregel: altijd de Floriday-salesOrderId (uuid).
|
|
2
|
+
// Een regel zonder geldige id wordt niet overgenomen, maar nooit stil: de order
|
|
3
|
+
// krijgt een zichtbare melding en het rondeverslag een foutregel.
|
|
4
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
|
+
export const MISSING_LINE_KEY_NOTICE = "Een regel van deze Floriday-order mist het bestelnummer en is niet overgenomen";
|
|
6
|
+
export function isValidSalesOrderId(v) {
|
|
7
|
+
return typeof v === "string" && UUID_RE.test(v.trim());
|
|
8
|
+
}
|
|
9
|
+
/** Splitst regels in geldige (met uuid-sleutel) en ongeldige. */
|
|
10
|
+
export function splitByLineKey(lines) {
|
|
11
|
+
const valid = [];
|
|
12
|
+
const invalid = [];
|
|
13
|
+
for (const l of lines)
|
|
14
|
+
(isValidSalesOrderId(l.foId) ? valid : invalid).push(l);
|
|
15
|
+
return { valid, invalid };
|
|
16
|
+
}
|
|
17
|
+
/** Idempotentiesleutel voor de melding "Afgehandeld in Floriday" (id's gesorteerd). */
|
|
18
|
+
export function externalFulfilledIdempotencyKey(orderId, salesOrderIds) {
|
|
19
|
+
return `extern-afgehandeld:${orderId}:${[...new Set(salesOrderIds)].sort().join(",")}`;
|
|
20
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floriday-mirror.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-mirror.functions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"floriday-mirror.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-mirror.functions.ts"],"names":[],"mappings":"AAgJA,UAAU,YAAY;IACpB,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACnD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAy5CD,wBAAsB,4BAA4B,CAAC,KAAK,SAAgB,yBAEvE;AAED,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;yBAG0C,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { diffBuyerChanges } from "./floriday-change-diff.js";
|
|
2
|
+
import { MISSING_LINE_KEY_NOTICE, splitByLineKey } from "./floriday-line-key.js";
|
|
2
3
|
// Spiegelt Floriday-verkooporders (FO's) uit `floriday_salesorders_cache`
|
|
3
4
|
// naar de lokale `orders`-tabel, zodat ze via `resolveBufferForOrderInternal`
|
|
4
5
|
// in dezelfde buffer terechtkomen als lokale orders en gedeelde karren
|
|
@@ -717,6 +718,7 @@ async function mirrorUnlocked(limit) {
|
|
|
717
718
|
lineInfoByFoId.set(l.foId, lineInfoOf(l));
|
|
718
719
|
// Orders die in deze ronde al door een andere bestelling zijn geschreven.
|
|
719
720
|
const handledOrderIds = new Set();
|
|
721
|
+
const legacyExternalIds = new Set();
|
|
720
722
|
// Welke velden per order echt veranderden (voor melding en syncresultaat).
|
|
721
723
|
const changedFieldsByOrder = new Map();
|
|
722
724
|
const noteChange = (orderId, field) => {
|
|
@@ -756,7 +758,9 @@ async function mirrorUnlocked(limit) {
|
|
|
756
758
|
floriday_status: primary.d?.status ?? null,
|
|
757
759
|
floriday_trade_item_id: primary.tradeItemId || null,
|
|
758
760
|
floriday_fulfilled: anyFulfilled,
|
|
759
|
-
|
|
761
|
+
// Afgehandeld in Floriday = er is een afhandeling die niet door het ERP
|
|
762
|
+
// is gemaakt; het aantal fust doet er niet toe (koppelprompt 1.0.96).
|
|
763
|
+
floriday_fulfilled_externally: group.some((l) => Boolean(l.ff?.external)),
|
|
760
764
|
floriday_fulfilled_by_erp: anyFulfilled && Boolean(ffPrimary?.createdByErp),
|
|
761
765
|
floriday_delivery_notes: ffPrimary?.deliveryNoteCodes ?? [],
|
|
762
766
|
floriday_received_at: ffPrimary?.receivedAt ?? null,
|
|
@@ -921,6 +925,11 @@ async function mirrorUnlocked(limit) {
|
|
|
921
925
|
// samenvoegen, nooit overschrijven — de order kan door de Engine uit
|
|
922
926
|
// meerdere bestellingen zijn samengesteld.
|
|
923
927
|
const prevCf = { ...(target.custom_fields ?? {}) };
|
|
928
|
+
// Bestaande orders die vóór 1.0.96 al als "buiten het ERP afgehandeld"
|
|
929
|
+
// stonden, worden niet vanzelf omgezet (eerst akkoord gebruiker).
|
|
930
|
+
if (prevCf.floriday_fulfilled_externally === true && !prevCf.floriday_external_fulfilled_sales_order_ids && !prevCf.floriday_external_fulfilled_at) {
|
|
931
|
+
legacyExternalIds.add(String(target.id));
|
|
932
|
+
}
|
|
924
933
|
// Oudere orders kennen floriday_groups nog niet: hun eigen bestelling
|
|
925
934
|
// als eerste groep overnemen, zodat die lijst niet verloren gaat.
|
|
926
935
|
const prevGroups = (prevCf.floriday_groups ??
|
|
@@ -952,6 +961,20 @@ async function mirrorUnlocked(limit) {
|
|
|
952
961
|
mergedCf.floriday_sales_order_ids = [...allIds].sort();
|
|
953
962
|
mergedCf.floriday_lines = [...otherLines, ...floridayLines].sort((a, b) => String(a?.sales_order_id).localeCompare(String(b?.sales_order_id)));
|
|
954
963
|
mergedCf.floriday_line_count = mergedCf.floriday_lines.length;
|
|
964
|
+
// Regels zonder geldige Floriday-salesOrderId worden niet overgenomen,
|
|
965
|
+
// maar nooit stil: zichtbare melding op de order + foutregel.
|
|
966
|
+
{
|
|
967
|
+
const { invalid } = splitByLineKey(group);
|
|
968
|
+
if (invalid.length > 0) {
|
|
969
|
+
mergedCf.floriday_missing_line_key_notice = MISSING_LINE_KEY_NOTICE;
|
|
970
|
+
mergedCf.floriday_missing_line_key_count = invalid.length;
|
|
971
|
+
errors.push(`${primary.foId}: ${invalid.length} regel(s) zonder geldig Floriday-bestelnummer niet overgenomen`);
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
delete mergedCf.floriday_missing_line_key_notice;
|
|
975
|
+
delete mergedCf.floriday_missing_line_key_count;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
955
978
|
if (Object.keys(failures).length > 0)
|
|
956
979
|
mergedCf.mirror_failures = failures;
|
|
957
980
|
else {
|
|
@@ -1161,7 +1184,8 @@ async function mirrorUnlocked(limit) {
|
|
|
1161
1184
|
// een vaste herkomstsleutel (source_line_key = Floriday sales order id),
|
|
1162
1185
|
// nooit meer wissen-en-opnieuw-toevoegen: dat veroorzaakte duplicaten
|
|
1163
1186
|
// zodra de verwijdering op een timeout stuk liep.
|
|
1164
|
-
|
|
1187
|
+
// Alleen regels met een geldige Floriday-salesOrderId (uuid) als sleutel.
|
|
1188
|
+
const lineRows = splitByLineKey(group).valid.map((l) => {
|
|
1165
1189
|
const locked = lockedByKey.get(l.foId);
|
|
1166
1190
|
return {
|
|
1167
1191
|
order_id: orderId,
|
|
@@ -1346,6 +1370,18 @@ async function mirrorUnlocked(limit) {
|
|
|
1346
1370
|
// Batch enqueue voor karherplanning. processed_at resetten, anders blijft een
|
|
1347
1371
|
// eerder verwerkte order voor altijd overgeslagen door de cron.
|
|
1348
1372
|
// Annuleringen uit Floriday afhandelen (koppelprompt 1.0.77).
|
|
1373
|
+
// Afgehandeld in Floriday (buiten het ERP om) — na de ronde, per order.
|
|
1374
|
+
for (const oid of Array.from(handledOrderIds)) {
|
|
1375
|
+
if (legacyExternalIds.has(oid))
|
|
1376
|
+
continue;
|
|
1377
|
+
try {
|
|
1378
|
+
const { handleFloridayExternalFulfillment } = await import("./floriday-external-fulfillment.server.js");
|
|
1379
|
+
await handleFloridayExternalFulfillment(oid, fulfillmentStates);
|
|
1380
|
+
}
|
|
1381
|
+
catch (err) {
|
|
1382
|
+
errors.push(`${oid}: afhandeling in Floriday verwerken faalde: ${err.message}`.slice(0, 200));
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1349
1385
|
for (const cid of Array.from(new Set(floridayCancelIds))) {
|
|
1350
1386
|
try {
|
|
1351
1387
|
const { handleFloridayCancellation } = await import("./order-cancel.server.js");
|
|
@@ -5,7 +5,7 @@ export declare const sendOrderEventToEngine: import("@tanstack/start-client-core
|
|
|
5
5
|
claims: import("@supabase/auth-js").User;
|
|
6
6
|
}, undefined, undefined, undefined>], (input: unknown) => {
|
|
7
7
|
orderId: string;
|
|
8
|
-
type: "order.
|
|
8
|
+
type: "order.changed" | "order.received";
|
|
9
9
|
}, Promise<{
|
|
10
10
|
ok: false;
|
|
11
11
|
error: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type OrderDeparture } from "./order-departure.js";
|
|
2
|
+
export declare const LIST_CHUNK = 1000;
|
|
2
3
|
export declare function loadOrderDepartures(supabase: any, orderIds: string[]): Promise<Map<string, OrderDeparture>>;
|
|
3
4
|
export declare function forgetOrderDeparture(orderId: string): void;
|
|
4
5
|
//# sourceMappingURL=order-departure.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-departure.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-departure.server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"order-departure.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-departure.server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG5E,eAAO,MAAM,UAAU,OAAO,CAAC;AAI/B,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAwDtC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,QAEnD"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// order (maximaal 12 tegelijk). Fout => order ontbreekt in het resultaat.
|
|
4
4
|
import { normalizeDeparture } from "./order-departure.js";
|
|
5
5
|
const PARALLEL = 12;
|
|
6
|
+
export const LIST_CHUNK = 1000;
|
|
6
7
|
const CACHE_MS = 30000;
|
|
7
8
|
const cache = new Map();
|
|
8
9
|
export async function loadOrderDepartures(supabase, orderIds) {
|
|
@@ -18,11 +19,22 @@ export async function loadOrderDepartures(supabase, orderIds) {
|
|
|
18
19
|
}
|
|
19
20
|
if (todo.length === 0)
|
|
20
21
|
return out;
|
|
21
|
-
// Lijstversie (Engine 1.0.162+): één aanroep.
|
|
22
|
+
// Lijstversie (Engine 1.0.162+): één aanroep per 1000 orders.
|
|
22
23
|
try {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const rows = [];
|
|
25
|
+
let listOk = true;
|
|
26
|
+
for (let i = 0; i < todo.length; i += LIST_CHUNK) {
|
|
27
|
+
const { data, error } = await supabase.rpc("logistics_order_departure_list", {
|
|
28
|
+
p_order_ids: todo.slice(i, i + LIST_CHUNK),
|
|
29
|
+
});
|
|
30
|
+
if (error) {
|
|
31
|
+
listOk = false;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(data))
|
|
35
|
+
rows.push(...data);
|
|
36
|
+
}
|
|
37
|
+
if (listOk) {
|
|
26
38
|
for (const row of rows) {
|
|
27
39
|
const r = (row && typeof row === "object" && "departure" in row ? row.departure : row);
|
|
28
40
|
const id = String(row?.order_id ?? row?.orderId ?? r?.orderId ?? "");
|
|
@@ -20,6 +20,10 @@ export declare const getOrderPickupMoment: import("@tanstack/start-client-core")
|
|
|
20
20
|
options: PickupMomentOption[];
|
|
21
21
|
hasCarrier: boolean;
|
|
22
22
|
deliveryTime: string | null;
|
|
23
|
+
lateOverride: {
|
|
24
|
+
by: string | null;
|
|
25
|
+
at: string | null;
|
|
26
|
+
} | null;
|
|
23
27
|
}>>;
|
|
24
28
|
export declare const setOrderPickupMoment: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
25
29
|
supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-pickup.functions.d.ts","sourceRoot":"","sources":["../../src/lib/order-pickup.functions.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG3F,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAOD,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAoB9E;AAaD,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO
|
|
1
|
+
{"version":3,"file":"order-pickup.functions.d.ts","sourceRoot":"","sources":["../../src/lib/order-pickup.functions.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG3F,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAOD,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAoB9E;AAaD,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;;;;;;YA2CgE,MAAM,GAAG,IAAI;YACtD,MAAM,GAAG,IAAI;;GAIjE,CAAC;AAEL,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;GAoB7B,CAAC"}
|
|
@@ -68,6 +68,7 @@ export const getOrderPickupMoment = createServerFn({ method: "GET" })
|
|
|
68
68
|
options: [],
|
|
69
69
|
hasCarrier: false,
|
|
70
70
|
deliveryTime: null,
|
|
71
|
+
lateOverride: null,
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
const cf = (order.custom_fields ?? {});
|
|
@@ -88,6 +89,12 @@ export const getOrderPickupMoment = createServerFn({ method: "GET" })
|
|
|
88
89
|
options,
|
|
89
90
|
hasCarrier: !!carrier,
|
|
90
91
|
deliveryTime: normalizeTime(cf.delivery_time),
|
|
92
|
+
lateOverride: cf.late_signup_override
|
|
93
|
+
? {
|
|
94
|
+
by: (cf.late_signup_override.by_name ?? cf.late_signup_override.by_email ?? null),
|
|
95
|
+
at: (cf.late_signup_override.at ?? null),
|
|
96
|
+
}
|
|
97
|
+
: null,
|
|
91
98
|
};
|
|
92
99
|
});
|
|
93
100
|
export const setOrderPickupMoment = createServerFn({ method: "POST" })
|