@flowselections/floriday-verkoop-module 1.0.77 → 1.0.82
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 +33 -8
- package/dist-lib/components/verkoop/EditLocalOrderDialog.d.ts.map +1 -1
- package/dist-lib/components/verkoop/EditLocalOrderDialog.js +1 -1
- package/dist-lib/components/verkoop/FloridayStageLine.d.ts +15 -0
- package/dist-lib/components/verkoop/FloridayStageLine.d.ts.map +1 -0
- package/dist-lib/components/verkoop/FloridayStageLine.js +25 -0
- package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts +1 -1
- package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts.map +1 -1
- package/dist-lib/components/verkoop/PlanCoverageBadge.js +5 -2
- package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailPage.js +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.js +38 -6
- package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.js +4 -8
- package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/VerkoopOrdersPage.js +7 -1
- package/dist-lib/integrations/supabase/types.d.ts +1486 -2
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/belading.server.d.ts +0 -11
- package/dist-lib/lib/belading.server.d.ts.map +1 -1
- package/dist-lib/lib/belading.server.js +4 -41
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.js +3 -22
- package/dist-lib/lib/central-secrets.server.d.ts +5 -0
- package/dist-lib/lib/central-secrets.server.d.ts.map +1 -0
- package/dist-lib/lib/central-secrets.server.js +65 -0
- package/dist-lib/lib/create-order.functions.d.ts +15 -0
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.js +28 -13
- package/dist-lib/lib/customer-name.d.ts +8 -0
- package/dist-lib/lib/customer-name.d.ts.map +1 -0
- package/dist-lib/lib/customer-name.js +9 -0
- package/dist-lib/lib/engine-block-alerts.d.ts +22 -0
- package/dist-lib/lib/engine-block-alerts.d.ts.map +1 -0
- package/dist-lib/lib/engine-block-alerts.js +43 -0
- package/dist-lib/lib/engine-block-alerts.server.d.ts +7 -0
- package/dist-lib/lib/engine-block-alerts.server.d.ts.map +1 -0
- package/dist-lib/lib/engine-block-alerts.server.js +75 -0
- package/dist-lib/lib/floriday-change-diff.check.d.ts +2 -0
- package/dist-lib/lib/floriday-change-diff.check.d.ts.map +1 -0
- package/dist-lib/lib/floriday-change-diff.check.js +27 -0
- package/dist-lib/lib/floriday-change-diff.d.ts +7 -0
- package/dist-lib/lib/floriday-change-diff.d.ts.map +1 -0
- package/dist-lib/lib/floriday-change-diff.js +51 -0
- package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.js +38 -39
- package/dist-lib/lib/floriday-status.d.ts +48 -0
- package/dist-lib/lib/floriday-status.d.ts.map +1 -0
- package/dist-lib/lib/floriday-status.js +99 -0
- package/dist-lib/lib/floriday-status.server.d.ts +7 -0
- package/dist-lib/lib/floriday-status.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-status.server.js +74 -0
- package/dist-lib/lib/load-carriers.server.d.ts +1 -1
- package/dist-lib/lib/load-carriers.server.d.ts.map +1 -1
- package/dist-lib/lib/load-carriers.server.js +3 -6
- package/dist-lib/lib/logistics-engine.server.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.server.js +2 -22
- package/dist-lib/lib/order-block-mail.server.d.ts.map +1 -1
- package/dist-lib/lib/order-block-mail.server.js +31 -15
- package/dist-lib/lib/order-cancel.server.d.ts +9 -0
- package/dist-lib/lib/order-cancel.server.d.ts.map +1 -1
- package/dist-lib/lib/order-cancel.server.js +90 -0
- package/dist-lib/lib/plan-coverage.check.d.ts +2 -0
- package/dist-lib/lib/plan-coverage.check.d.ts.map +1 -0
- package/dist-lib/lib/plan-coverage.check.js +17 -0
- package/dist-lib/lib/plan-coverage.d.ts +12 -0
- package/dist-lib/lib/plan-coverage.d.ts.map +1 -1
- package/dist-lib/lib/plan-coverage.js +27 -1
- package/dist-lib/lib/plan-coverage.server.d.ts.map +1 -1
- package/dist-lib/lib/plan-coverage.server.js +20 -13
- package/dist-lib/lib/review-dismissals.server.d.ts +3 -0
- package/dist-lib/lib/review-dismissals.server.d.ts.map +1 -0
- package/dist-lib/lib/review-dismissals.server.js +26 -0
- package/dist-lib/lib/salesorders-list.functions.d.ts +1 -1
- package/dist-lib/lib/salesorders-list.functions.js +1 -1
- package/dist-lib/lib/salesorders-list.server.d.ts +1 -1
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +104 -3
- package/dist-lib/lib/salesorders-mapping.d.ts +17 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-mapping.js +1 -0
- package/dist-lib/lib/salesorders.functions.d.ts +1 -5
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +32 -2
- package/dist-lib/lib/useSalesOrdersQuery.d.ts +1 -1
- package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { diffBuyerChanges } from "./floriday-change-diff";
|
|
1
2
|
// Spiegelt Floriday-verkooporders (FO's) uit `floriday_salesorders_cache`
|
|
2
3
|
// naar de lokale `orders`-tabel, zodat ze via `resolveBufferForOrderInternal`
|
|
3
4
|
// in dezelfde buffer terechtkomen als lokale orders en gedeelde karren
|
|
@@ -385,6 +386,7 @@ async function mirrorUnlocked(limit) {
|
|
|
385
386
|
let inserted = 0, updated = 0, skipped = 0, unchanged = 0, failed = 0;
|
|
386
387
|
const errors = [];
|
|
387
388
|
const affectedOrderIds = [];
|
|
389
|
+
const floridayCancelIds = [];
|
|
388
390
|
// Orders die in deze ronde echt zijn aangemaakt of gewijzigd: alleen die
|
|
389
391
|
// melden we expliciet aan bij de planwachtrij van de logistiek-engine.
|
|
390
392
|
const changedOrderIds = [];
|
|
@@ -829,12 +831,21 @@ async function mirrorUnlocked(limit) {
|
|
|
829
831
|
frozenCandidates.set(String(c.id), c);
|
|
830
832
|
}
|
|
831
833
|
if (frozenCandidates.size > 0) {
|
|
834
|
+
// Alleen echte koperwijzigingen (artikel, aantal, leverdatum,
|
|
835
|
+
// bestelling erbij/weg). Status, belading en Floriday-echo niet.
|
|
832
836
|
const signature = JSON.stringify({
|
|
833
|
-
|
|
834
|
-
|
|
837
|
+
lines: floridayLines.map((l) => ({
|
|
838
|
+
id: l.sales_order_id,
|
|
839
|
+
t: l.trade_item_id,
|
|
840
|
+
p: l.pieces,
|
|
841
|
+
})),
|
|
842
|
+
d: groupLvl.deliveryDate ?? null,
|
|
835
843
|
});
|
|
836
844
|
for (const fo of frozenCandidates.values()) {
|
|
837
845
|
const cf = (fo.custom_fields ?? {});
|
|
846
|
+
const changes = diffBuyerChanges(cf.floriday_lines, floridayLines, fo.delivery_date, groupLvl.deliveryDate);
|
|
847
|
+
if (changes.length === 0)
|
|
848
|
+
continue; // niets door de koper gewijzigd
|
|
838
849
|
if (cf.floriday_change_signature === signature)
|
|
839
850
|
continue; // al gemeld
|
|
840
851
|
const { error: revErr } = await supabaseAdmin.from("orders")
|
|
@@ -845,7 +856,8 @@ async function mirrorUnlocked(limit) {
|
|
|
845
856
|
floriday_change_detected_at: new Date().toISOString(),
|
|
846
857
|
floriday_change_signature: signature,
|
|
847
858
|
needs_manual_review: true,
|
|
848
|
-
|
|
859
|
+
floriday_change_summary: changes,
|
|
860
|
+
manual_review_reason: `Wijziging door de koper na afronding: ${changes.join("; ")} — handmatige beoordeling nodig`,
|
|
849
861
|
},
|
|
850
862
|
updated_at: new Date().toISOString(),
|
|
851
863
|
})
|
|
@@ -855,6 +867,9 @@ async function mirrorUnlocked(limit) {
|
|
|
855
867
|
}
|
|
856
868
|
}
|
|
857
869
|
skipped += group.length;
|
|
870
|
+
// Afgeronde order: deze ronde overslaan; alleen melden bij echte wijziging.
|
|
871
|
+
if (![...frozenCandidates.values()].some((fo) => diffBuyerChanges((fo.custom_fields ?? {}).floriday_lines, floridayLines, fo.delivery_date, groupLvl.deliveryDate).length > 0))
|
|
872
|
+
continue;
|
|
858
873
|
errors.push(`${primary.foId}: wijziging na afronding — order gemarkeerd voor handmatige beoordeling`);
|
|
859
874
|
continue;
|
|
860
875
|
}
|
|
@@ -992,7 +1007,12 @@ async function mirrorUnlocked(limit) {
|
|
|
992
1007
|
const prevFloridayStatus = prevCf.floriday_status ?? null;
|
|
993
1008
|
const floridayStatusChanged = String(lvl.floridayStatusSummary ?? "") !== String(prevFloridayStatus ?? "");
|
|
994
1009
|
const localStatus = String(target?.status ?? "");
|
|
995
|
-
|
|
1010
|
+
// Annulering uit Floriday: niet zelf de status zetten, maar na de ronde
|
|
1011
|
+
// via handleFloridayCancellation (pickstand bij de Engine controleren).
|
|
1012
|
+
const isFloridayCancel = lvl.status === "geannuleerd" && localStatus.toLowerCase() !== "geannuleerd";
|
|
1013
|
+
if (floridayStatusChanged && isFloridayCancel)
|
|
1014
|
+
floridayCancelIds.push(String(target.id));
|
|
1015
|
+
const statusPatch = floridayStatusChanged && lvl.status !== localStatus && !isFloridayCancel
|
|
996
1016
|
? { status: lvl.status, status_sync_token: globalThis.crypto.randomUUID() }
|
|
997
1017
|
: {};
|
|
998
1018
|
// Alleen regels die eerder bij déze bestelling hoorden mogen als
|
|
@@ -1265,41 +1285,8 @@ async function mirrorUnlocked(limit) {
|
|
|
1265
1285
|
foIds: memberIds,
|
|
1266
1286
|
});
|
|
1267
1287
|
}
|
|
1268
|
-
//
|
|
1269
|
-
//
|
|
1270
|
-
// mag ook via Floriday niet doorlopen: blokkeren met reden i.p.v. plannen.
|
|
1271
|
-
try {
|
|
1272
|
-
const { assertBeladingAllowed } = await import("./belading.server");
|
|
1273
|
-
await assertBeladingAllowed(supabaseAdmin, lineRows
|
|
1274
|
-
.filter((r) => r.product_id)
|
|
1275
|
-
.map((r) => ({
|
|
1276
|
-
product_id: r.product_id,
|
|
1277
|
-
plants_per_crate: r.plants_per_crate,
|
|
1278
|
-
crates_per_layer: r.crates_per_layer,
|
|
1279
|
-
layers_per_cart: r.layers_per_cart,
|
|
1280
|
-
})));
|
|
1281
|
-
}
|
|
1282
|
-
catch (belErr) {
|
|
1283
|
-
const reason = `Order ${orderId}: ${belErr.message}`;
|
|
1284
|
-
const { error: blockErr } = await supabaseAdmin.rpc("logistics_plan_block", {
|
|
1285
|
-
p_order_ids: [orderId],
|
|
1286
|
-
p_reason: reason,
|
|
1287
|
-
p_details: { source: "floriday-verkoop-module/floriday-mirror" },
|
|
1288
|
-
});
|
|
1289
|
-
if (blockErr)
|
|
1290
|
-
console.warn("[floriday-mirror] blokkade vastleggen faalde:", blockErr.message);
|
|
1291
|
-
// Eenmalige mailmelding naar het interne adres uit de e-mailmodule.
|
|
1292
|
-
const { notifyOrderBlocked } = await import("./order-block-mail.server");
|
|
1293
|
-
const mail = await notifyOrderBlocked({
|
|
1294
|
-
orderIds: [orderId],
|
|
1295
|
-
reasonKey: "belading_niet_toegestaan",
|
|
1296
|
-
reason: belErr.message,
|
|
1297
|
-
details: { source: "floriday-verkoop-module/floriday-mirror" },
|
|
1298
|
-
});
|
|
1299
|
-
for (const w of mail.warnings)
|
|
1300
|
-
console.warn("[floriday-mirror]", w);
|
|
1301
|
-
errors.push(`${primary.foId}: ${reason.slice(0, 200)}`);
|
|
1302
|
-
}
|
|
1288
|
+
// Geen eigen beladings-/ladingdragercontrole: alleen de Logistiek Engine
|
|
1289
|
+
// beslist of een belading kan (koppelprompt 22).
|
|
1303
1290
|
// Buffer resolven en in de replan-queue zetten. Alleen orders die buiten
|
|
1304
1291
|
// ons ERP om in Floriday zijn afgehandeld hoeven niet meer gepland te
|
|
1305
1292
|
// worden.
|
|
@@ -1351,6 +1338,18 @@ async function mirrorUnlocked(limit) {
|
|
|
1351
1338
|
}
|
|
1352
1339
|
// Batch enqueue voor karherplanning. processed_at resetten, anders blijft een
|
|
1353
1340
|
// eerder verwerkte order voor altijd overgeslagen door de cron.
|
|
1341
|
+
// Annuleringen uit Floriday afhandelen (koppelprompt 1.0.77).
|
|
1342
|
+
for (const cid of Array.from(new Set(floridayCancelIds))) {
|
|
1343
|
+
try {
|
|
1344
|
+
const { handleFloridayCancellation } = await import("./order-cancel.server");
|
|
1345
|
+
const res = await handleFloridayCancellation(cid);
|
|
1346
|
+
if (res !== "cancelled")
|
|
1347
|
+
errors.push(`${cid}: ${res}`);
|
|
1348
|
+
}
|
|
1349
|
+
catch (err) {
|
|
1350
|
+
errors.push(`${cid}: annulering uit Floriday faalde: ${err.message}`.slice(0, 200));
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1354
1353
|
if (affectedOrderIds.length > 0) {
|
|
1355
1354
|
const queueRows = affectedOrderIds.map((oid) => ({
|
|
1356
1355
|
order_id: oid,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { OrderStage } from "./order-status";
|
|
2
|
+
export type FloridayStage = "open" | "partially_fulfilled" | "fulfilled" | "cancelled" | "expired" | "unknown";
|
|
3
|
+
export interface FloridayStatusEntry {
|
|
4
|
+
salesOrderId: string;
|
|
5
|
+
found: boolean;
|
|
6
|
+
salesOrderStatus: string | null;
|
|
7
|
+
floridayStage: FloridayStage;
|
|
8
|
+
fulfillmentRequests: {
|
|
9
|
+
fulfillmentRequestId: string | null;
|
|
10
|
+
deliveryOrderId: string | null;
|
|
11
|
+
fulfillmentOrderIds: string[];
|
|
12
|
+
}[];
|
|
13
|
+
fulfillmentOrders: {
|
|
14
|
+
fulfillmentOrderId: string | null;
|
|
15
|
+
status: string | null;
|
|
16
|
+
}[];
|
|
17
|
+
salesChannel: string | null;
|
|
18
|
+
tradeInstrument: string | null;
|
|
19
|
+
incoterm: string | null;
|
|
20
|
+
customerOrderId: string | null;
|
|
21
|
+
salesChannelOrderId: string | null;
|
|
22
|
+
customerOrganizationId: string | null;
|
|
23
|
+
customerGln: string | null;
|
|
24
|
+
lastModifiedDateTime: string | null;
|
|
25
|
+
cachedAt: string | null;
|
|
26
|
+
}
|
|
27
|
+
/** Samenvatting op orderniveau (een order kan meerdere bestellingen hebben). */
|
|
28
|
+
export interface FloridayStatusSummary {
|
|
29
|
+
stage: FloridayStage | "onbekend";
|
|
30
|
+
/** Foutcode als het ophalen mislukte (bv. invalid_signature). */
|
|
31
|
+
error: string | null;
|
|
32
|
+
entries: FloridayStatusEntry[];
|
|
33
|
+
}
|
|
34
|
+
export declare function parseFloridayStatusEntry(raw: any): FloridayStatusEntry | null;
|
|
35
|
+
/** Eén stand voor een order met meerdere Floriday-bestellingen. */
|
|
36
|
+
export declare function combineStages(stages: FloridayStage[]): FloridayStage;
|
|
37
|
+
export declare function floridayStageLabel(stage: FloridayStatusSummary["stage"]): string;
|
|
38
|
+
/** Wijkt de Floriday-stand af van de ERP-stand? Alleen voor weergave. */
|
|
39
|
+
export declare function deviatesFromFloriday(stage: FloridayStatusSummary["stage"], erp: OrderStage | undefined): boolean;
|
|
40
|
+
/** Eigen kenmerk: verzonden in het ERP en nog niet afgemeld bij Floriday. */
|
|
41
|
+
export declare function isNotReportedLocally(erp: OrderStage | undefined, floridayFulfilled: boolean): boolean;
|
|
42
|
+
export type NotReportedState = "waarschuwing" | "waarschuwing_onbekend" | "al_afgehandeld" | null;
|
|
43
|
+
/**
|
|
44
|
+
* Welke melding hoort bij een verzonden order? Floriday-stand gaat voor;
|
|
45
|
+
* bij onbekende stand valt het terug op ons eigen kenmerk. Alleen weergave.
|
|
46
|
+
*/
|
|
47
|
+
export declare function notReportedState(erp: OrderStage | undefined, floriday: FloridayStatusSummary["stage"] | null | undefined, floridayFulfilled: boolean): NotReportedState;
|
|
48
|
+
//# sourceMappingURL=floriday-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-status.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-status.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,qBAAqB,GACrB,WAAW,GACX,WAAW,GACX,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,aAAa,CAAC;IAC7B,mBAAmB,EAAE;QAAE,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,mBAAmB,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAC9H,iBAAiB,EAAE;QAAE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IAClF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,gFAAgF;AAChF,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,aAAa,GAAG,UAAU,CAAC;IAClC,iEAAiE;IACjE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAMD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,GAAG,GAAG,mBAAmB,GAAG,IAAI,CA6B7E;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,aAAa,CAWpE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,MAAM,CAUhF;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAQhH;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,EAAE,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAErG;AAED,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,uBAAuB,GAAG,gBAAgB,GAAG,IAAI,CAAC;AAElG;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,UAAU,GAAG,SAAS,EAC3B,QAAQ,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3D,iBAAiB,EAAE,OAAO,GACzB,gBAAgB,CAMlB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const STAGES = ["open", "partially_fulfilled", "fulfilled", "cancelled", "expired", "unknown"];
|
|
2
|
+
const str = (v) => (typeof v === "string" && v.trim() ? v : null);
|
|
3
|
+
const arr = (v) => (Array.isArray(v) ? v : []);
|
|
4
|
+
export function parseFloridayStatusEntry(raw) {
|
|
5
|
+
if (!raw || typeof raw !== "object")
|
|
6
|
+
return null;
|
|
7
|
+
const id = str(raw.salesOrderId);
|
|
8
|
+
if (!id)
|
|
9
|
+
return null;
|
|
10
|
+
const stage = STAGES.includes(raw.floridayStage) ? raw.floridayStage : "unknown";
|
|
11
|
+
return {
|
|
12
|
+
salesOrderId: id,
|
|
13
|
+
found: raw.found === true,
|
|
14
|
+
salesOrderStatus: str(raw.salesOrderStatus),
|
|
15
|
+
floridayStage: stage,
|
|
16
|
+
fulfillmentRequests: arr(raw.fulfillmentRequests).map((r) => ({
|
|
17
|
+
fulfillmentRequestId: str(r?.fulfillmentRequestId),
|
|
18
|
+
deliveryOrderId: str(r?.deliveryOrderId),
|
|
19
|
+
fulfillmentOrderIds: arr(r?.fulfillmentOrderIds).map(String),
|
|
20
|
+
})),
|
|
21
|
+
fulfillmentOrders: arr(raw.fulfillmentOrders).map((f) => ({
|
|
22
|
+
fulfillmentOrderId: str(f?.fulfillmentOrderId),
|
|
23
|
+
status: str(f?.status),
|
|
24
|
+
})),
|
|
25
|
+
salesChannel: str(raw.salesChannel),
|
|
26
|
+
tradeInstrument: str(raw.tradeInstrument),
|
|
27
|
+
incoterm: str(raw.incoterm),
|
|
28
|
+
customerOrderId: str(raw.customerOrderId),
|
|
29
|
+
salesChannelOrderId: str(raw.salesChannelOrderId),
|
|
30
|
+
customerOrganizationId: str(raw.customerOrganizationId),
|
|
31
|
+
customerGln: str(raw.customerGln),
|
|
32
|
+
lastModifiedDateTime: str(raw.lastModifiedDateTime),
|
|
33
|
+
cachedAt: str(raw.cachedAt),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Eén stand voor een order met meerdere Floriday-bestellingen. */
|
|
37
|
+
export function combineStages(stages) {
|
|
38
|
+
if (stages.length === 0)
|
|
39
|
+
return "unknown";
|
|
40
|
+
const all = (s) => stages.every((x) => x === s);
|
|
41
|
+
if (all("cancelled"))
|
|
42
|
+
return "cancelled";
|
|
43
|
+
if (all("expired"))
|
|
44
|
+
return "expired";
|
|
45
|
+
if (stages.every((x) => x === "cancelled" || x === "expired"))
|
|
46
|
+
return "cancelled";
|
|
47
|
+
const active = stages.filter((x) => x !== "cancelled" && x !== "expired");
|
|
48
|
+
if (active.includes("unknown"))
|
|
49
|
+
return "unknown";
|
|
50
|
+
if (active.every((x) => x === "fulfilled"))
|
|
51
|
+
return "fulfilled";
|
|
52
|
+
if (active.some((x) => x === "fulfilled" || x === "partially_fulfilled"))
|
|
53
|
+
return "partially_fulfilled";
|
|
54
|
+
return "open";
|
|
55
|
+
}
|
|
56
|
+
export function floridayStageLabel(stage) {
|
|
57
|
+
switch (stage) {
|
|
58
|
+
case "open": return "open";
|
|
59
|
+
case "partially_fulfilled": return "deels geleverd";
|
|
60
|
+
case "fulfilled": return "geleverd";
|
|
61
|
+
case "cancelled": return "geannuleerd";
|
|
62
|
+
case "expired": return "vervallen";
|
|
63
|
+
case "unknown": return "onbekend";
|
|
64
|
+
default: return "stand onbekend";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Wijkt de Floriday-stand af van de ERP-stand? Alleen voor weergave. */
|
|
68
|
+
export function deviatesFromFloriday(stage, erp) {
|
|
69
|
+
if (!erp || stage === "onbekend" || stage === "unknown")
|
|
70
|
+
return false;
|
|
71
|
+
const erpCancelled = erp === "cancelled";
|
|
72
|
+
const erpShipped = erp === "shipped";
|
|
73
|
+
if (stage === "cancelled" || stage === "expired")
|
|
74
|
+
return !erpCancelled;
|
|
75
|
+
if (stage === "fulfilled")
|
|
76
|
+
return !erpShipped && !erpCancelled;
|
|
77
|
+
if (stage === "open")
|
|
78
|
+
return erpCancelled; // verzonden+open: aparte melding
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/** Eigen kenmerk: verzonden in het ERP en nog niet afgemeld bij Floriday. */
|
|
82
|
+
export function isNotReportedLocally(erp, floridayFulfilled) {
|
|
83
|
+
return erp === "shipped" && !floridayFulfilled;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Welke melding hoort bij een verzonden order? Floriday-stand gaat voor;
|
|
87
|
+
* bij onbekende stand valt het terug op ons eigen kenmerk. Alleen weergave.
|
|
88
|
+
*/
|
|
89
|
+
export function notReportedState(erp, floriday, floridayFulfilled) {
|
|
90
|
+
if (erp !== "shipped")
|
|
91
|
+
return null;
|
|
92
|
+
if (floriday === "open" || floriday === "partially_fulfilled")
|
|
93
|
+
return "waarschuwing";
|
|
94
|
+
if (floriday === "fulfilled")
|
|
95
|
+
return floridayFulfilled ? null : "al_afgehandeld";
|
|
96
|
+
if (floriday === "cancelled" || floriday === "expired")
|
|
97
|
+
return null;
|
|
98
|
+
return floridayFulfilled ? null : "waarschuwing_onbekend";
|
|
99
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type FloridayStatusEntry } from "./floriday-status";
|
|
2
|
+
export interface FloridayStatusResult {
|
|
3
|
+
entries: Map<string, FloridayStatusEntry>;
|
|
4
|
+
error: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function loadFloridayStatuses(salesOrderIds: string[]): Promise<FloridayStatusResult>;
|
|
7
|
+
//# sourceMappingURL=floriday-status.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-status.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-status.server.ts"],"names":[],"mappings":"AAOA,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAoBvF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC1C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmDjG"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Server-only: Floriday-stand per verkooporder ophalen bij de gateway van de
|
|
2
|
+
// Authenticatie-module (POST /api/public/gateway/fulfillment-order/status).
|
|
3
|
+
// - Alleen lezen; schrijft nooit iets.
|
|
4
|
+
// - Ondertekend volgens het gatewaycontract: tijd in epoch-ms, HMAC-SHA256 over
|
|
5
|
+
// precies `${timestamp}.${body}`, header `x-floriday-signature: sha256=<hex>`.
|
|
6
|
+
// - Max 100 id's per verzoek, kort geheugen (30 s) per id.
|
|
7
|
+
// - Fail closed: bij een fout geen stand, wel de foutcode (bv. invalid_signature).
|
|
8
|
+
import { parseFloridayStatusEntry } from "./floriday-status";
|
|
9
|
+
const TTL_MS = 30000;
|
|
10
|
+
const MAX_IDS = 100;
|
|
11
|
+
const cache = new Map();
|
|
12
|
+
async function hmacHex(secret, payload) {
|
|
13
|
+
const key = await globalThis.crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
14
|
+
const sig = await globalThis.crypto.subtle.sign("HMAC", key, new TextEncoder().encode(payload));
|
|
15
|
+
return Array.from(new Uint8Array(sig))
|
|
16
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
17
|
+
.join("");
|
|
18
|
+
}
|
|
19
|
+
export async function loadFloridayStatuses(salesOrderIds) {
|
|
20
|
+
const entries = new Map();
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
const wanted = [];
|
|
23
|
+
for (const id of Array.from(new Set(salesOrderIds.filter(Boolean)))) {
|
|
24
|
+
const hit = cache.get(id);
|
|
25
|
+
if (hit && now - hit.at < TTL_MS)
|
|
26
|
+
entries.set(id, hit.value);
|
|
27
|
+
else
|
|
28
|
+
wanted.push(id);
|
|
29
|
+
}
|
|
30
|
+
if (wanted.length === 0)
|
|
31
|
+
return { entries, error: null };
|
|
32
|
+
let error = null;
|
|
33
|
+
try {
|
|
34
|
+
const { getFloridayGatewaySecret, getGatewayBaseUrl } = await import("./central-secrets.server");
|
|
35
|
+
const secret = await getFloridayGatewaySecret();
|
|
36
|
+
const base = await getGatewayBaseUrl();
|
|
37
|
+
for (let i = 0; i < wanted.length; i += MAX_IDS) {
|
|
38
|
+
const chunk = wanted.slice(i, i + MAX_IDS);
|
|
39
|
+
const body = JSON.stringify({ salesOrderIds: chunk });
|
|
40
|
+
const timestamp = String(Date.now());
|
|
41
|
+
const signature = await hmacHex(secret, `${timestamp}.${body}`);
|
|
42
|
+
const res = await fetch(`${base}/api/public/gateway/fulfillment-order/status`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
"x-floriday-timestamp": timestamp,
|
|
47
|
+
"x-floriday-signature": `sha256=${signature}`,
|
|
48
|
+
"x-floriday-caller": "verkoop",
|
|
49
|
+
},
|
|
50
|
+
body,
|
|
51
|
+
});
|
|
52
|
+
const json = await res.json().catch(() => null);
|
|
53
|
+
if (!res.ok || json?.ok === false) {
|
|
54
|
+
const code = json?.code ?? json?.error ?? `HTTP ${res.status}`;
|
|
55
|
+
error = String(code);
|
|
56
|
+
console.warn("[floriday-status] gateway weigerde:", error, json?.error ?? "");
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
const rows = Array.isArray(json?.orders) ? json.orders : [];
|
|
60
|
+
for (const r of rows) {
|
|
61
|
+
const e = parseFloridayStatusEntry(r);
|
|
62
|
+
if (!e)
|
|
63
|
+
continue;
|
|
64
|
+
entries.set(e.salesOrderId, e);
|
|
65
|
+
cache.set(e.salesOrderId, { at: Date.now(), value: e });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
error = err.message;
|
|
71
|
+
console.warn("[floriday-status] ophalen faalde:", error);
|
|
72
|
+
}
|
|
73
|
+
return { entries, error };
|
|
74
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare function fetchAllowedLoadCarriers(supabase: any, strategyId?: string | null): Promise<import("./load-carriers").AllowedLoadCarriers>;
|
|
2
2
|
/**
|
|
3
3
|
* Valideert de gekozen ladingdrager. Zonder keuze wordt de default van de
|
|
4
|
-
* Engine teruggegeven.
|
|
4
|
+
* Engine teruggegeven. Geen eigen controle: alleen de Engine beslist.
|
|
5
5
|
*/
|
|
6
6
|
export declare function resolveLoadCarrierType(supabase: any, value: string | null | undefined, strategyId?: string | null): Promise<string | null>;
|
|
7
7
|
//# sourceMappingURL=load-carriers.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-carriers.server.d.ts","sourceRoot":"","sources":["../../src/lib/load-carriers.server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-carriers.server.d.ts","sourceRoot":"","sources":["../../src/lib/load-carriers.server.ts"],"names":[],"mappings":"AAMA,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,GAAG,EACb,UAAU,GAAE,MAAM,GAAG,IAAW,0DAOjC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,GAAG,EACb,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,UAAU,GAAE,MAAM,GAAG,IAAW,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
import { DISABLED_LOAD_CARRIER_MESSAGE, normalizeAllowedLoadCarriers, normalizeLoadCarrierCode, } from "./load-carriers";
|
|
1
|
+
// Leest de ladingdragerlijst van de Engine. Geen eigen blokkade (koppelprompt 22).
|
|
2
|
+
import { normalizeAllowedLoadCarriers, normalizeLoadCarrierCode, } from "./load-carriers";
|
|
4
3
|
export async function fetchAllowedLoadCarriers(supabase, strategyId = null) {
|
|
5
4
|
const { data, error } = await supabase.rpc("logistics_allowed_load_carriers", {
|
|
6
5
|
p_strategy_id: strategyId,
|
|
@@ -11,14 +10,12 @@ export async function fetchAllowedLoadCarriers(supabase, strategyId = null) {
|
|
|
11
10
|
}
|
|
12
11
|
/**
|
|
13
12
|
* Valideert de gekozen ladingdrager. Zonder keuze wordt de default van de
|
|
14
|
-
* Engine teruggegeven.
|
|
13
|
+
* Engine teruggegeven. Geen eigen controle: alleen de Engine beslist.
|
|
15
14
|
*/
|
|
16
15
|
export async function resolveLoadCarrierType(supabase, value, strategyId = null) {
|
|
17
16
|
const allowed = await fetchAllowedLoadCarriers(supabase, strategyId);
|
|
18
17
|
if (value == null || String(value).trim() === "")
|
|
19
18
|
return allowed.default;
|
|
20
19
|
const code = normalizeLoadCarrierCode(value) ?? String(value).trim();
|
|
21
|
-
if (!allowed.allowed.includes(code))
|
|
22
|
-
throw new Error(DISABLED_LOAD_CARRIER_MESSAGE);
|
|
23
20
|
return code;
|
|
24
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logistics-engine.server.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-engine.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"logistics-engine.server.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-engine.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAuDpC;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,SAAS,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,MAAM,CAAC;IACzE,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBnE;AAED,iEAAiE;AACjE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,gBAAgB,GAAG,eAAe,GACvC,OAAO,CAAC,IAAI,CAAC,CAWf"}
|
|
@@ -41,28 +41,8 @@ export async function buildEngineOrderPayload(orderId) {
|
|
|
41
41
|
const reason = rawCarrier == null || String(rawCarrier).trim() === ""
|
|
42
42
|
? `Order ${orderRef}: geen ladingdrager bekend — niet ingedeeld, er wordt geen type geraden.`
|
|
43
43
|
: `Order ${orderRef}: onbekende ladingdrager "${String(rawCarrier)}" — niet ingedeeld, er wordt geen type geraden.`;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
p_reason: reason,
|
|
47
|
-
p_details: {
|
|
48
|
-
source: "floriday-verkoop-module/logistics-engine",
|
|
49
|
-
load_carrier_type: rawCarrier ?? null,
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
if (blockErr)
|
|
53
|
-
console.warn("[logistics-engine] blokkade vastleggen faalde:", blockErr.message, reason);
|
|
54
|
-
else
|
|
55
|
-
console.warn("[logistics-engine]", reason);
|
|
56
|
-
// Eenmalige mailmelding naar het interne adres uit de e-mailmodule.
|
|
57
|
-
const { notifyOrderBlocked } = await import("./order-block-mail.server");
|
|
58
|
-
const mail = await notifyOrderBlocked({
|
|
59
|
-
orderIds: [String(o.id)],
|
|
60
|
-
reasonKey: "ladingdrager_onbekend",
|
|
61
|
-
reason,
|
|
62
|
-
details: { load_carrier_type: rawCarrier ?? null },
|
|
63
|
-
});
|
|
64
|
-
for (const w of mail.warnings)
|
|
65
|
-
console.warn("[logistics-engine]", w);
|
|
44
|
+
// Geen eigen blokkade of mail meer (koppelprompt 22): de Engine beslist.
|
|
45
|
+
console.warn("[logistics-engine]", reason);
|
|
66
46
|
return null;
|
|
67
47
|
}
|
|
68
48
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-block-mail.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-block-mail.server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"order-block-mail.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-block-mail.server.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AASD;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwHhC;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// De mail wordt in public.email_auth_outbox gezet; de e-mailmodule verstuurt
|
|
8
8
|
// hem in haar eigen verwerkingsronde.
|
|
9
|
+
import { buildBlockMailLines, buyerLabel } from "./engine-block-alerts";
|
|
9
10
|
const EMAIL_PURPOSE = "internal";
|
|
10
11
|
function escapeHtml(value) {
|
|
11
12
|
return value
|
|
@@ -52,34 +53,49 @@ export async function notifyOrderBlocked(input) {
|
|
|
52
53
|
warnings.push('Geen actieve e-mailverbinding met doel "intern" — melding van geblokkeerde order(s) niet verstuurd.');
|
|
53
54
|
return { sent: 0, warnings };
|
|
54
55
|
}
|
|
55
|
-
// 3) Ordergegevens voor een leesbare mail.
|
|
56
|
+
// 3) Ordergegevens voor een leesbare mail. Klant = koper via klant-id;
|
|
57
|
+
// afleverlocatie op een eigen regel (op id, nooit op naam).
|
|
56
58
|
const { data: orderRows } = await supabaseAdmin
|
|
57
59
|
.from("orders")
|
|
58
|
-
.select("id, order_number, delivery_date, customer_id")
|
|
60
|
+
.select("id, order_number, delivery_date, customer_id, custom_fields")
|
|
59
61
|
.in("id", claimed);
|
|
60
|
-
const
|
|
62
|
+
const rowsArr = (orderRows ?? []);
|
|
63
|
+
const customerIds = Array.from(new Set(rowsArr.map((o) => o.customer_id).filter(Boolean)));
|
|
61
64
|
const customerNames = new Map();
|
|
62
65
|
if (customerIds.length > 0) {
|
|
63
66
|
const { data: custRows } = await supabaseAdmin
|
|
64
67
|
.from("customers")
|
|
65
|
-
.select("id, company_name")
|
|
68
|
+
.select("id, company_name, commercial_name")
|
|
66
69
|
.in("id", customerIds);
|
|
67
70
|
for (const c of (custRows ?? [])) {
|
|
68
|
-
customerNames.set(String(c.id),
|
|
71
|
+
customerNames.set(String(c.id), buyerLabel(c.company_name, c.commercial_name));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const locIds = Array.from(new Set(rowsArr.map((o) => o.custom_fields?.delivery_location_id).filter(Boolean).map(String)));
|
|
75
|
+
const locNames = new Map();
|
|
76
|
+
if (locIds.length > 0) {
|
|
77
|
+
const { data: locRows } = await supabaseAdmin
|
|
78
|
+
.from("customer_delivery_locations")
|
|
79
|
+
.select("id, name, gln")
|
|
80
|
+
.in("id", locIds);
|
|
81
|
+
for (const l of (locRows ?? [])) {
|
|
82
|
+
locNames.set(String(l.id), String(l.name ?? l.gln ?? ""));
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
85
|
let sent = 0;
|
|
72
|
-
for (const o of
|
|
86
|
+
for (const o of rowsArr) {
|
|
73
87
|
const nummer = o.order_number ? String(o.order_number) : String(o.id).slice(0, 8);
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
`
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
const cf = (o.custom_fields ?? {});
|
|
89
|
+
const locId = cf.delivery_location_id ? String(cf.delivery_location_id) : null;
|
|
90
|
+
const afleverlocatie = (locId && locNames.get(locId)) ||
|
|
91
|
+
(cf.delivery_location_gln ? `GLN ${String(cf.delivery_location_gln)}` : null);
|
|
92
|
+
const regels = buildBlockMailLines({
|
|
93
|
+
orderNumber: nummer,
|
|
94
|
+
klant: o.customer_id ? (customerNames.get(String(o.customer_id)) ?? "") : "",
|
|
95
|
+
afleverlocatie,
|
|
96
|
+
leverdatum: o.delivery_date ? String(o.delivery_date) : null,
|
|
97
|
+
reden: input.reason,
|
|
98
|
+
});
|
|
83
99
|
const text = regels.join("\n");
|
|
84
100
|
const html = `<p>${regels.map((r) => escapeHtml(r)).join("<br/>")}</p>`;
|
|
85
101
|
const { error: outErr } = await supabaseAdmin.from("email_auth_outbox").insert({
|
|
@@ -21,4 +21,13 @@ export declare function cancelLocalOrderInternal(input: {
|
|
|
21
21
|
alreadyCancelled: boolean;
|
|
22
22
|
reversedKeys: number;
|
|
23
23
|
}>;
|
|
24
|
+
export declare const FLORIDAY_CANCEL_REASON = "Koper heeft geannuleerd in Floriday";
|
|
25
|
+
export declare const FLORIDAY_CANCEL_PICKED_MESSAGE = "Koper heeft geannuleerd in Floriday, order is al gepickt";
|
|
26
|
+
/**
|
|
27
|
+
* Annulering uit Floriday (op order-id). Nog niet gepickt/gescand → order
|
|
28
|
+
* annuleren (voorraad terug, uit herplanning en buffer, Engine melden).
|
|
29
|
+
* Al gepickt/gescand, of pickstand onbekend → niet annuleren, wel een duidelijke
|
|
30
|
+
* melding voor handmatige beoordeling. Schrijft nooit order_carts (Engine).
|
|
31
|
+
*/
|
|
32
|
+
export declare function handleFloridayCancellation(orderId: string): Promise<"cancelled" | "already_picked" | "pick_unknown" | "already_cancelled">;
|
|
24
33
|
//# sourceMappingURL=order-cancel.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-cancel.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-cancel.server.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5E,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC,CAsE5D;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDzE"}
|
|
1
|
+
{"version":3,"file":"order-cancel.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-cancel.server.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5E,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC,CAsE5D;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDzE;AAED,eAAO,MAAM,sBAAsB,wCAAwC,CAAC;AAC5E,eAAO,MAAM,8BAA8B,6DACiB,CAAC;AAE7D;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,GAAG,gBAAgB,GAAG,cAAc,GAAG,mBAAmB,CAAC,CA+EhF"}
|