@flowselections/floriday-voorraad 1.0.27 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +2 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  3. package/dist-lib/components/voorraad/ArtikelWizard.js +576 -324
  4. package/dist-lib/components/voorraad/EditArtikelPage.d.ts +2 -1
  5. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  6. package/dist-lib/components/voorraad/EditArtikelPage.js +5 -4
  7. package/dist-lib/components/voorraad/FustenPage.d.ts +2 -0
  8. package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -0
  9. package/dist-lib/components/voorraad/FustenPage.js +97 -0
  10. package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts +2 -0
  11. package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts.map +1 -0
  12. package/dist-lib/components/voorraad/LogistiekTakenCard.js +54 -0
  13. package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts +5 -0
  14. package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts.map +1 -0
  15. package/dist-lib/components/voorraad/ProductCustomFieldsCard.js +65 -0
  16. package/dist-lib/components/voorraad/PublicatiesPage.d.ts +2 -0
  17. package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -0
  18. package/dist-lib/components/voorraad/PublicatiesPage.js +60 -0
  19. package/dist-lib/components/voorraad/PublishProgressCard.d.ts +12 -0
  20. package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -0
  21. package/dist-lib/components/voorraad/PublishProgressCard.js +30 -0
  22. package/dist-lib/components/voorraad/PublishReadinessCard.d.ts +10 -0
  23. package/dist-lib/components/voorraad/PublishReadinessCard.d.ts.map +1 -0
  24. package/dist-lib/components/voorraad/PublishReadinessCard.js +12 -0
  25. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
  26. package/dist-lib/components/voorraad/StockBulkButtons.js +65 -2
  27. package/dist-lib/components/voorraad/StockSyncStatus.d.ts +2 -0
  28. package/dist-lib/components/voorraad/StockSyncStatus.d.ts.map +1 -0
  29. package/dist-lib/components/voorraad/StockSyncStatus.js +23 -0
  30. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
  31. package/dist-lib/components/voorraad/ValidationOverviewTable.js +8 -2
  32. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts.map +1 -1
  33. package/dist-lib/components/voorraad/VoorraadDashboard.js +4 -1
  34. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  35. package/dist-lib/components/voorraad/VoorraadPage.js +5 -1
  36. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  37. package/dist-lib/components/voorraad/VoorraadTable.js +10 -2
  38. package/dist-lib/components/voorraad/VoorraadVeldenCard.d.ts +2 -0
  39. package/dist-lib/components/voorraad/VoorraadVeldenCard.d.ts.map +1 -0
  40. package/dist-lib/components/voorraad/VoorraadVeldenCard.js +97 -0
  41. package/dist-lib/hooks/useFusten.d.ts +17 -0
  42. package/dist-lib/hooks/useFusten.d.ts.map +1 -0
  43. package/dist-lib/hooks/useFusten.js +81 -0
  44. package/dist-lib/hooks/useLocalTradeItems.d.ts +17 -7
  45. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  46. package/dist-lib/hooks/useLocalTradeItems.js +139 -37
  47. package/dist-lib/hooks/useLogisticsTasks.d.ts +13 -0
  48. package/dist-lib/hooks/useLogisticsTasks.d.ts.map +1 -0
  49. package/dist-lib/hooks/useLogisticsTasks.js +155 -0
  50. package/dist-lib/hooks/usePublishJob.d.ts +13 -0
  51. package/dist-lib/hooks/usePublishJob.d.ts.map +1 -0
  52. package/dist-lib/hooks/usePublishJob.js +67 -0
  53. package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts +2 -0
  54. package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts.map +1 -0
  55. package/dist-lib/hooks/useRegisterModuleBaseUrl.js +31 -0
  56. package/dist-lib/hooks/useStockSyncHealth.d.ts +8 -0
  57. package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -0
  58. package/dist-lib/hooks/useStockSyncHealth.js +42 -0
  59. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  60. package/dist-lib/hooks/useTradeItemStock.js +37 -1
  61. package/dist-lib/hooks/useVoorraadCustomFields.d.ts +9 -0
  62. package/dist-lib/hooks/useVoorraadCustomFields.d.ts.map +1 -0
  63. package/dist-lib/hooks/useVoorraadCustomFields.js +26 -0
  64. package/dist-lib/index.d.ts +2 -0
  65. package/dist-lib/index.d.ts.map +1 -1
  66. package/dist-lib/index.js +9 -2
  67. package/dist-lib/integrations/supabase/auth-middleware.d.ts +8467 -4487
  68. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  69. package/dist-lib/integrations/supabase/client.d.ts +8467 -4487
  70. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  71. package/dist-lib/integrations/supabase/client.server.d.ts +8467 -4487
  72. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  73. package/dist-lib/integrations/supabase/types.d.ts +8714 -4631
  74. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  75. package/dist-lib/integrations/supabase/types.js +49 -0
  76. package/dist-lib/lib/floricode-required-features.functions.d.ts +8467 -4487
  77. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  78. package/dist-lib/lib/floriday-client.d.ts +1 -1
  79. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  80. package/dist-lib/lib/floriday-client.js +1 -2
  81. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +8467 -4487
  82. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  83. package/dist-lib/lib/floriday-customer-offer.functions.js +14 -19
  84. package/dist-lib/lib/floriday-env.functions.d.ts +35712 -0
  85. package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -0
  86. package/dist-lib/lib/floriday-env.functions.js +16 -0
  87. package/dist-lib/lib/floriday-env.server.d.ts +12 -0
  88. package/dist-lib/lib/floriday-env.server.d.ts.map +1 -1
  89. package/dist-lib/lib/floriday-env.server.js +15 -2
  90. package/dist-lib/lib/floriday-gateway.functions.d.ts +77902 -40145
  91. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  92. package/dist-lib/lib/floriday-gateway.functions.js +12 -3
  93. package/dist-lib/lib/floriday-readiness.server.d.ts +11 -0
  94. package/dist-lib/lib/floriday-readiness.server.d.ts.map +1 -0
  95. package/dist-lib/lib/floriday-readiness.server.js +43 -0
  96. package/dist-lib/lib/floriday-vbn-required.functions.d.ts +23115 -15155
  97. package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
  98. package/dist-lib/lib/floriday-warehouse.functions.d.ts +31362 -19422
  99. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  100. package/dist-lib/lib/floriday-warehouse.functions.js +3 -0
  101. package/dist-lib/lib/floriday-writes.functions.d.ts +23191 -15231
  102. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  103. package/dist-lib/lib/fusten.d.ts +87 -0
  104. package/dist-lib/lib/fusten.d.ts.map +1 -0
  105. package/dist-lib/lib/fusten.js +389 -0
  106. package/dist-lib/lib/logistics-engine.d.ts +63 -0
  107. package/dist-lib/lib/logistics-engine.d.ts.map +1 -0
  108. package/dist-lib/lib/logistics-engine.js +57 -0
  109. package/dist-lib/lib/logistics-events.d.ts +18 -0
  110. package/dist-lib/lib/logistics-events.d.ts.map +1 -0
  111. package/dist-lib/lib/logistics-events.js +61 -0
  112. package/dist-lib/lib/module-runtime-config.functions.d.ts +14 -0
  113. package/dist-lib/lib/module-runtime-config.functions.d.ts.map +1 -0
  114. package/dist-lib/lib/module-runtime-config.functions.js +107 -0
  115. package/dist-lib/lib/publish-errors.d.ts +37 -0
  116. package/dist-lib/lib/publish-errors.d.ts.map +1 -0
  117. package/dist-lib/lib/publish-errors.js +183 -0
  118. package/dist-lib/lib/publish-pipeline.d.ts +91 -0
  119. package/dist-lib/lib/publish-pipeline.d.ts.map +1 -0
  120. package/dist-lib/lib/publish-pipeline.js +212 -0
  121. package/dist-lib/lib/publish-readiness.d.ts +42 -0
  122. package/dist-lib/lib/publish-readiness.d.ts.map +1 -0
  123. package/dist-lib/lib/publish-readiness.js +102 -0
  124. package/dist-lib/lib/voorraad-custom-fields.d.ts +22 -0
  125. package/dist-lib/lib/voorraad-custom-fields.d.ts.map +1 -0
  126. package/dist-lib/lib/voorraad-custom-fields.js +71 -0
  127. package/package.json +2 -2
@@ -0,0 +1,87 @@
1
+ import type { PackingConfigurationInput } from "./floriday-payload";
2
+ export interface FustType {
3
+ id: string;
4
+ connection_id: string | null;
5
+ fust_key: string;
6
+ name: string;
7
+ source: string;
8
+ vbn_code: number | null;
9
+ custom_package_id: string | null;
10
+ stock: number;
11
+ minimum_stock: number;
12
+ in_use: boolean;
13
+ used_by_count: number;
14
+ length_mm: number | null;
15
+ width_mm: number | null;
16
+ height_mm: number | null;
17
+ notes: string | null;
18
+ updated_at: string;
19
+ }
20
+ export interface FustMutation {
21
+ id: string;
22
+ fust_type_id: string;
23
+ delta: number;
24
+ reason: string;
25
+ note: string | null;
26
+ trade_item_key: string | null;
27
+ created_at: string;
28
+ }
29
+ export interface FustUsage {
30
+ fustKey: string;
31
+ name: string;
32
+ source: string;
33
+ vbnCode: number | null;
34
+ customPackageId: string | null;
35
+ usedByCount: number;
36
+ lengthMm: number | null;
37
+ widthMm: number | null;
38
+ heightMm: number | null;
39
+ }
40
+ /** Afmetingen als "L × B × H mm", of null wanneer onbekend. */
41
+ export declare function formatFustDimensions(f: {
42
+ length_mm: number | null;
43
+ width_mm: number | null;
44
+ height_mm: number | null;
45
+ }): string | null;
46
+ /** Stabiele sleutel per fusttype: VBN-code of custom package. */
47
+ export declare function fustKeyOf(p: Partial<PackingConfigurationInput>): string | null;
48
+ /** Verzamel elk uniek fusttype dat in een belading wordt gebruikt. */
49
+ export declare function collectFustUsage(connectionId: string | null): Promise<FustUsage[]>;
50
+ /**
51
+ * Fusten zijn koppeling-onafhankelijk: één rij per fusttype. Anders ontstaan
52
+ * er dubbele rijen (één per koppeling) en lijkt de voorraad op 0 te staan.
53
+ */
54
+ export declare function fetchFustTypes(_connectionId?: string | null): Promise<FustType[]>;
55
+ /**
56
+ * Synchroniseer de fustenvoorraad met de beladingen van alle producten.
57
+ * - nieuw fusttype → toevoegen (nooit dubbel: unieke `fust_key`)
58
+ * - gewijzigde naam → overal bijwerken
59
+ * - niet meer gebruikt → verwijderen zodra er geen voorraad en geen historie is,
60
+ * anders bewaren en markeren als "niet meer in gebruik".
61
+ */
62
+ export declare function syncFustTypes(connectionId: string | null): Promise<{
63
+ added: number;
64
+ updated: number;
65
+ removed: number;
66
+ }>;
67
+ /** Handmatige of automatische voorraadmutatie. Positief = bij, negatief = af. */
68
+ export declare function adjustFustStock(params: {
69
+ fustTypeId: string;
70
+ delta: number;
71
+ reason?: string;
72
+ note?: string | null;
73
+ tradeItemKey?: string | null;
74
+ }): Promise<void>;
75
+ export declare function updateFustType(id: string, values: Partial<Pick<FustType, "name" | "minimum_stock" | "notes" | "length_mm" | "width_mm" | "height_mm">>): Promise<void>;
76
+ export declare function fetchFustMutations(fustTypeId: string, limit?: number): Promise<FustMutation[]>;
77
+ /**
78
+ * Afschrijven bij verkoop. Het aantal verkochte stuks wordt via de primaire
79
+ * belading omgerekend naar fusten (stuks per fust, naar boven afgerond).
80
+ */
81
+ export declare function registerSaleFustUsage(params: {
82
+ connectionId: string | null;
83
+ tradeItemKey: string;
84
+ piecesSold: number;
85
+ note?: string;
86
+ }): Promise<void>;
87
+ //# sourceMappingURL=fusten.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fusten.d.ts","sourceRoot":"","sources":["../../src/lib/fusten.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,389 @@
1
+ /**
2
+ * Fustenadministratie.
3
+ *
4
+ * De fustenvoorraad is géén losstaand systeem: alle fusttypen worden afgeleid
5
+ * uit de beladingen (`packingConfigurations`) van de producten. Deze module
6
+ * verzorgt:
7
+ * - het scannen van alle producten + Floriday-cache op gebruikte fusten,
8
+ * - het automatisch registreren / hernoemen / opruimen van fusttypen,
9
+ * - handmatige voorraadmutaties,
10
+ * - automatische afschrijving bij verkoop op basis van de belading.
11
+ */
12
+ import { supabase } from "../integrations/supabase/client";
13
+ import { listFloricode } from "./floricode-client";
14
+ /** Afmetingen als "L × B × H mm", of null wanneer onbekend. */
15
+ export function formatFustDimensions(f) {
16
+ const parts = [f.length_mm, f.width_mm, f.height_mm];
17
+ if (parts.every((v) => v === null || v === undefined))
18
+ return null;
19
+ return `${parts.map((v) => (v == null ? "?" : v)).join(" × ")} mm`;
20
+ }
21
+ function isUuidLike(v) {
22
+ return typeof v === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-/i.test(v);
23
+ }
24
+ /** Stabiele sleutel per fusttype: VBN-code of custom package. */
25
+ export function fustKeyOf(p) {
26
+ const custom = p.packagingMaterialId;
27
+ const vbn = String(p.packagingType ?? "").trim();
28
+ if (p.source === "SUPPLIER" && isUuidLike(custom))
29
+ return `custom:${custom}`;
30
+ if (vbn)
31
+ return `vbn:${vbn}`;
32
+ if (isUuidLike(custom))
33
+ return `custom:${custom}`;
34
+ return null;
35
+ }
36
+ /** Zelfde sleutel, maar dan vanuit een rauwe Floriday-packing config. */
37
+ function fustKeyOfRaw(p) {
38
+ const vbn = p?.package?.vbnPackageCode ?? p?.vbnPackageCode ?? p?.packagingType;
39
+ const custom = p?.package?.customPackageId ?? p?.packagingMaterialId;
40
+ if (vbn !== undefined && vbn !== null && String(vbn).trim() !== "") {
41
+ return `vbn:${String(vbn).trim()}`;
42
+ }
43
+ if (isUuidLike(custom))
44
+ return `custom:${custom}`;
45
+ return null;
46
+ }
47
+ function packingsOf(payload) {
48
+ return Array.isArray(payload?.packingConfigurations) ? payload.packingConfigurations : [];
49
+ }
50
+ function asRecord(v) {
51
+ return v && typeof v === "object" && !Array.isArray(v) ? v : {};
52
+ }
53
+ /**
54
+ * Alle beladingen van alle producten (lokaal + Floriday-cache).
55
+ *
56
+ * De cache wordt ALTIJD meegenomen, ook zonder actieve koppeling: anders
57
+ * zouden fusten die alleen in Floriday-artikelen voorkomen ontbreken in de
58
+ * voorraad. Met een koppeling filteren we op die koppeling, zonder koppeling
59
+ * scannen we alle niet-verwijderde cache-rijen.
60
+ */
61
+ async function loadAllPackings(connectionId) {
62
+ const out = [];
63
+ const prodRes = await supabase
64
+ .from("products")
65
+ .select("id,trade_item_key,custom_fields")
66
+ .limit(2000);
67
+ for (const row of (prodRes.data ?? [])) {
68
+ const cf = asRecord(row.custom_fields);
69
+ // Beladingen kunnen in de payload staan, of (legacy) direct op custom_fields.
70
+ const packings = [...packingsOf(asRecord(cf.floriday_payload)), ...packingsOf(cf)];
71
+ if (packings.length > 0) {
72
+ out.push({ tradeItemKey: row.trade_item_key || `product:${row.id}`, packings });
73
+ }
74
+ }
75
+ let cacheQ = supabase
76
+ .from("floriday_trade_items_cache")
77
+ .select("floriday_id,data")
78
+ .eq("is_deleted", false)
79
+ .limit(2000);
80
+ if (connectionId)
81
+ cacheQ = cacheQ.eq("connection_id", connectionId);
82
+ const cacheRes = await cacheQ;
83
+ for (const row of (cacheRes.data ?? [])) {
84
+ const packings = packingsOf(row.data);
85
+ if (packings.length > 0)
86
+ out.push({ tradeItemKey: row.floriday_id, packings });
87
+ }
88
+ return out;
89
+ }
90
+ /** Lees een maat uit een catalogusrij; ondersteunt mm- en cm-varianten. */
91
+ function dimOf(data, keys) {
92
+ for (const k of Object.keys(data ?? {})) {
93
+ const lower = k.toLowerCase();
94
+ if (!keys.some((n) => lower === n || lower === `${n}mm` || lower === `${n}_mm`))
95
+ continue;
96
+ const num = Number(data[k]);
97
+ if (Number.isFinite(num) && num > 0)
98
+ return Math.round(num);
99
+ }
100
+ for (const k of Object.keys(data ?? {})) {
101
+ const lower = k.toLowerCase();
102
+ if (!keys.some((n) => lower === `${n}cm` || lower === `${n}_cm`))
103
+ continue;
104
+ const num = Number(data[k]);
105
+ if (Number.isFinite(num) && num > 0)
106
+ return Math.round(num * 10);
107
+ }
108
+ return null;
109
+ }
110
+ /** Namen én afmetingen van VBN-fusten uit de Floricode-catalogus. */
111
+ async function loadVbnCatalog() {
112
+ const map = new Map();
113
+ try {
114
+ const rows = await listFloricode("LoadCarrier", undefined, 1000);
115
+ for (const r of rows) {
116
+ const code = String(r.data?.code ?? r.data?.id ?? r.record_id);
117
+ const data = r.data ?? {};
118
+ map.set(code, {
119
+ name: String(data.name ?? data.description ?? code),
120
+ lengthMm: dimOf(data, ["length", "lengte"]),
121
+ widthMm: dimOf(data, ["width", "breedte"]),
122
+ heightMm: dimOf(data, ["height", "hoogte"]),
123
+ });
124
+ }
125
+ }
126
+ catch {
127
+ // Catalogus niet bereikbaar — we vallen terug op de code als naam.
128
+ }
129
+ return map;
130
+ }
131
+ /** Verzamel elk uniek fusttype dat in een belading wordt gebruikt. */
132
+ export async function collectFustUsage(connectionId) {
133
+ const [all, vbnCatalog] = await Promise.all([loadAllPackings(connectionId), loadVbnCatalog()]);
134
+ const byKey = new Map();
135
+ for (const entry of all) {
136
+ const seenForItem = new Set();
137
+ for (const p of entry.packings) {
138
+ const key = fustKeyOfRaw(p) ?? fustKeyOf(p);
139
+ if (!key)
140
+ continue;
141
+ const existing = byKey.get(key);
142
+ if (existing) {
143
+ if (!seenForItem.has(key))
144
+ existing.usedByCount += 1;
145
+ seenForItem.add(key);
146
+ continue;
147
+ }
148
+ const isCustom = key.startsWith("custom:");
149
+ const raw = key.slice(key.indexOf(":") + 1);
150
+ const catalog = isCustom ? undefined : vbnCatalog.get(raw);
151
+ const catalogName = catalog?.name;
152
+ const fallbackName = p?.packagingMaterialName || p?.packagingSupplierName || undefined;
153
+ byKey.set(key, {
154
+ fustKey: key,
155
+ name: catalogName ??
156
+ fallbackName ??
157
+ (isCustom ? `Eigen fust ${raw.slice(0, 8)}` : `VBN-fust ${raw}`),
158
+ source: isCustom ? "SUPPLIER" : "VBN",
159
+ vbnCode: isCustom ? null : Number.isFinite(Number(raw)) ? Number(raw) : null,
160
+ customPackageId: isCustom ? raw : null,
161
+ usedByCount: 1,
162
+ lengthMm: catalog?.lengthMm ?? null,
163
+ widthMm: catalog?.widthMm ?? null,
164
+ heightMm: catalog?.heightMm ?? null,
165
+ });
166
+ seenForItem.add(key);
167
+ }
168
+ }
169
+ return [...byKey.values()].sort((a, b) => a.name.localeCompare(b.name, "nl"));
170
+ }
171
+ /**
172
+ * Fusten zijn koppeling-onafhankelijk: één rij per fusttype. Anders ontstaan
173
+ * er dubbele rijen (één per koppeling) en lijkt de voorraad op 0 te staan.
174
+ */
175
+ export async function fetchFustTypes(_connectionId) {
176
+ const { data, error } = await supabase
177
+ .from("fust_types")
178
+ .select("*")
179
+ .is("connection_id", null)
180
+ .order("name");
181
+ if (error)
182
+ throw new Error(error.message);
183
+ return (data ?? []);
184
+ }
185
+ /**
186
+ * Synchroniseer de fustenvoorraad met de beladingen van alle producten.
187
+ * - nieuw fusttype → toevoegen (nooit dubbel: unieke `fust_key`)
188
+ * - gewijzigde naam → overal bijwerken
189
+ * - niet meer gebruikt → verwijderen zodra er geen voorraad en geen historie is,
190
+ * anders bewaren en markeren als "niet meer in gebruik".
191
+ */
192
+ export async function syncFustTypes(connectionId) {
193
+ const userRes = await supabase.auth.getUser();
194
+ const userId = userRes.data.user?.id;
195
+ if (!userId)
196
+ throw new Error("Niet ingelogd");
197
+ const [usage, existing] = await Promise.all([
198
+ collectFustUsage(connectionId),
199
+ fetchFustTypes(connectionId),
200
+ ]);
201
+ const existingByKey = new Map(existing.map((f) => [f.fust_key, f]));
202
+ const toInsert = [];
203
+ const updates = [];
204
+ let updated = 0;
205
+ for (const u of usage) {
206
+ const cur = existingByKey.get(u.fustKey);
207
+ if (!cur) {
208
+ toInsert.push({
209
+ user_id: userId,
210
+ connection_id: null,
211
+ fust_key: u.fustKey,
212
+ name: u.name,
213
+ source: u.source,
214
+ vbn_code: u.vbnCode,
215
+ custom_package_id: u.customPackageId,
216
+ in_use: true,
217
+ used_by_count: u.usedByCount,
218
+ length_mm: u.lengthMm,
219
+ width_mm: u.widthMm,
220
+ height_mm: u.heightMm,
221
+ });
222
+ continue;
223
+ }
224
+ const dimPatch = {};
225
+ if (cur.length_mm == null && u.lengthMm != null)
226
+ dimPatch.length_mm = u.lengthMm;
227
+ if (cur.width_mm == null && u.widthMm != null)
228
+ dimPatch.width_mm = u.widthMm;
229
+ if (cur.height_mm == null && u.heightMm != null)
230
+ dimPatch.height_mm = u.heightMm;
231
+ if (cur.name !== u.name ||
232
+ !cur.in_use ||
233
+ cur.used_by_count !== u.usedByCount ||
234
+ Object.keys(dimPatch).length > 0) {
235
+ updated++;
236
+ updates.push(supabase
237
+ .from("fust_types")
238
+ .update({ name: u.name, in_use: true, used_by_count: u.usedByCount, ...dimPatch })
239
+ .eq("id", cur.id));
240
+ }
241
+ }
242
+ const usedKeys = new Set(usage.map((u) => u.fustKey));
243
+ const orphans = existing.filter((f) => !usedKeys.has(f.fust_key));
244
+ let removed = 0;
245
+ if (orphans.length > 0) {
246
+ const { data: muts } = await supabase
247
+ .from("fust_stock_mutations")
248
+ .select("fust_type_id")
249
+ .in("fust_type_id", orphans.map((o) => o.id));
250
+ const withHistory = new Set((muts ?? []).map((m) => m.fust_type_id));
251
+ const deletable = orphans.filter((o) => o.stock === 0 && !withHistory.has(o.id));
252
+ const keep = orphans.filter((o) => !deletable.includes(o));
253
+ if (deletable.length > 0) {
254
+ removed = deletable.length;
255
+ updates.push(supabase
256
+ .from("fust_types")
257
+ .delete()
258
+ .in("id", deletable.map((d) => d.id)));
259
+ }
260
+ for (const k of keep) {
261
+ if (k.in_use || k.used_by_count !== 0) {
262
+ updates.push(supabase.from("fust_types").update({ in_use: false, used_by_count: 0 }).eq("id", k.id));
263
+ }
264
+ }
265
+ }
266
+ if (toInsert.length > 0) {
267
+ const { error } = await supabase.from("fust_types").insert(toInsert);
268
+ // Dubbele sleutel = fusttype bestaat al; dat is geen fout.
269
+ if (error && !/duplicate key/i.test(error.message))
270
+ throw new Error(error.message);
271
+ }
272
+ await Promise.all(updates);
273
+ return { added: toInsert.length, updated, removed };
274
+ }
275
+ /** Handmatige of automatische voorraadmutatie. Positief = bij, negatief = af. */
276
+ export async function adjustFustStock(params) {
277
+ if (!params.delta)
278
+ return;
279
+ const userRes = await supabase.auth.getUser();
280
+ const userId = userRes.data.user?.id;
281
+ if (!userId)
282
+ throw new Error("Niet ingelogd");
283
+ const { error } = await supabase.from("fust_stock_mutations").insert({
284
+ user_id: userId,
285
+ fust_type_id: params.fustTypeId,
286
+ delta: Math.trunc(params.delta),
287
+ reason: params.reason ?? "manual",
288
+ note: params.note ?? null,
289
+ trade_item_key: params.tradeItemKey ?? null,
290
+ });
291
+ if (error)
292
+ throw new Error(error.message);
293
+ }
294
+ export async function updateFustType(id, values) {
295
+ const { error } = await supabase.from("fust_types").update(values).eq("id", id);
296
+ if (error)
297
+ throw new Error(error.message);
298
+ }
299
+ export async function fetchFustMutations(fustTypeId, limit = 50) {
300
+ const { data, error } = await supabase
301
+ .from("fust_stock_mutations")
302
+ .select("*")
303
+ .eq("fust_type_id", fustTypeId)
304
+ .order("created_at", { ascending: false })
305
+ .limit(limit);
306
+ if (error)
307
+ throw new Error(error.message);
308
+ return (data ?? []);
309
+ }
310
+ /**
311
+ * Afschrijven bij verkoop. Het aantal verkochte stuks wordt via de primaire
312
+ * belading omgerekend naar fusten (stuks per fust, naar boven afgerond).
313
+ */
314
+ export async function registerSaleFustUsage(params) {
315
+ if (params.piecesSold <= 0)
316
+ return;
317
+ const packing = await findPrimaryPackingFor(params.connectionId, params.tradeItemKey);
318
+ if (!packing)
319
+ return;
320
+ const key = fustKeyOfRaw(packing) ?? fustKeyOf(packing);
321
+ if (!key)
322
+ return;
323
+ const perPackage = Math.max(1, Number(packing?.piecesPerPackage ?? packing?.unitsPerPackage ?? 1) || 1);
324
+ const packages = Math.ceil(params.piecesSold / perPackage);
325
+ if (packages <= 0)
326
+ return;
327
+ const { data } = await supabase
328
+ .from("fust_types")
329
+ .select("id")
330
+ .eq("fust_key", key)
331
+ .is("connection_id", null)
332
+ .limit(1);
333
+ let fustTypeId = (data ?? [])[0]?.id;
334
+ if (!fustTypeId) {
335
+ await syncFustTypes(params.connectionId);
336
+ const { data: retry } = await supabase
337
+ .from("fust_types")
338
+ .select("id")
339
+ .eq("fust_key", key)
340
+ .is("connection_id", null)
341
+ .limit(1);
342
+ fustTypeId = (retry ?? [])[0]?.id;
343
+ }
344
+ if (!fustTypeId)
345
+ return;
346
+ await adjustFustStock({
347
+ fustTypeId,
348
+ delta: -packages,
349
+ reason: "sale",
350
+ note: params.note ?? `Verkoop ${params.piecesSold} stuks`,
351
+ tradeItemKey: params.tradeItemKey,
352
+ });
353
+ }
354
+ async function findPrimaryPackingFor(connectionId, tradeItemKey) {
355
+ const productId = tradeItemKey.startsWith("product:")
356
+ ? tradeItemKey.slice("product:".length)
357
+ : null;
358
+ if (productId) {
359
+ const { data } = await supabase
360
+ .from("products")
361
+ .select("custom_fields")
362
+ .eq("id", productId)
363
+ .maybeSingle();
364
+ const packings = packingsOf(asRecord(asRecord(data?.custom_fields).floriday_payload));
365
+ if (packings.length > 0)
366
+ return packings.find((p) => p?.isPrimary || p?.primary) ?? packings[0];
367
+ return null;
368
+ }
369
+ const { data: prod } = await supabase
370
+ .from("products")
371
+ .select("custom_fields")
372
+ .eq("trade_item_key", tradeItemKey)
373
+ .maybeSingle();
374
+ const local = packingsOf(asRecord(asRecord(prod?.custom_fields).floriday_payload));
375
+ if (local.length > 0)
376
+ return local.find((p) => p?.isPrimary || p?.primary) ?? local[0];
377
+ if (connectionId) {
378
+ const { data } = await supabase
379
+ .from("floriday_trade_items_cache")
380
+ .select("data")
381
+ .eq("connection_id", connectionId)
382
+ .eq("floriday_id", tradeItemKey)
383
+ .maybeSingle();
384
+ const packings = packingsOf(data?.data);
385
+ if (packings.length > 0)
386
+ return packings.find((p) => p?.primary || p?.isPrimary) ?? packings[0];
387
+ }
388
+ return null;
389
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Gedeeld contract met de Logistics Engine.
3
+ *
4
+ * Alle communicatie loopt via de gedeelde Supabase-database
5
+ * (public.logistics_events / public.logistics_tasks). Geen HTTP, geen secrets.
6
+ *
7
+ * De Engine beslist over buffering, belading, picken, controle, printen en
8
+ * transport. Deze module voert alleen taken uit en beslist zelf niets meer.
9
+ */
10
+ export declare const OWNER_MODULE = "voorraad";
11
+ export declare const LOAD_CARRIER_TYPES: readonly ["trolley", "danish_container", "dense_container", "pallet", "box", "loose"];
12
+ export type LoadCarrierType = (typeof LOAD_CARRIER_TYPES)[number];
13
+ export declare const TASK_STATUSES: readonly ["planned", "ready", "in_progress", "done", "skipped", "cancelled", "failed"];
14
+ export type TaskStatus = (typeof TASK_STATUSES)[number];
15
+ export interface LoadCarrier {
16
+ type: LoadCarrierType | string;
17
+ count: number;
18
+ }
19
+ /** Rij uit public.logistics_tasks (alleen de velden die wij gebruiken). */
20
+ export interface LogisticsTaskRow {
21
+ id: string;
22
+ order_ref: string;
23
+ buffer_ref: string | null;
24
+ task_type: string;
25
+ sequence: number;
26
+ depends_on: string[];
27
+ planned_start: string | null;
28
+ planned_end: string | null;
29
+ due_at: string | null;
30
+ status: string;
31
+ shadow: boolean;
32
+ payload: any;
33
+ result: any;
34
+ rule_version: number | null;
35
+ }
36
+ export interface StockReserveLine {
37
+ tradeItemKey: string;
38
+ quantity: number;
39
+ label?: string | null;
40
+ }
41
+ /** Genormaliseerde weergave van een stock.reserve payload. */
42
+ export interface StockReservePlan {
43
+ lines: StockReserveLine[];
44
+ loadCarriers: LoadCarrier[];
45
+ }
46
+ export interface TaskExecutionResult {
47
+ taskId: string;
48
+ applied: boolean;
49
+ shadow: boolean;
50
+ /** Vergelijking huidige vs. door de Engine gevraagde reservering. */
51
+ diff: Array<{
52
+ tradeItemKey: string;
53
+ currentReserved: number;
54
+ engineReserved: number;
55
+ delta: number;
56
+ }>;
57
+ message: string;
58
+ }
59
+ export declare function normalizeLoadCarriers(input: any): LoadCarrier[];
60
+ /** Tolerant: de Engine mag regels anders benoemen. */
61
+ export declare function parseStockReservePlan(payload: any): StockReservePlan;
62
+ export declare function describeLoadCarriers(carriers: LoadCarrier[]): string;
63
+ //# sourceMappingURL=logistics-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logistics-engine.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,YAAY,aAAa,CAAC;AAEvC,eAAO,MAAM,kBAAkB,uFAOrB,CAAC;AACX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,aAAa,wFAQhB,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,EAAE,GAAG,CAAC;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,IAAI,EAAE,KAAK,CAAC;QACV,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,GAAG,GAAG,WAAW,EAAE,CAQ/D;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,GAAG,GAAG,gBAAgB,CAmBpE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAGpE"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Gedeeld contract met de Logistics Engine.
3
+ *
4
+ * Alle communicatie loopt via de gedeelde Supabase-database
5
+ * (public.logistics_events / public.logistics_tasks). Geen HTTP, geen secrets.
6
+ *
7
+ * De Engine beslist over buffering, belading, picken, controle, printen en
8
+ * transport. Deze module voert alleen taken uit en beslist zelf niets meer.
9
+ */
10
+ export const OWNER_MODULE = "voorraad";
11
+ export const LOAD_CARRIER_TYPES = [
12
+ "trolley",
13
+ "danish_container",
14
+ "dense_container",
15
+ "pallet",
16
+ "box",
17
+ "loose",
18
+ ];
19
+ export const TASK_STATUSES = [
20
+ "planned",
21
+ "ready",
22
+ "in_progress",
23
+ "done",
24
+ "skipped",
25
+ "cancelled",
26
+ "failed",
27
+ ];
28
+ export function normalizeLoadCarriers(input) {
29
+ if (!Array.isArray(input))
30
+ return [];
31
+ return input
32
+ .map((c) => ({
33
+ type: String(c?.type ?? "loose"),
34
+ count: Number(c?.count ?? c?.quantity ?? 0) || 0,
35
+ }))
36
+ .filter((c) => c.count > 0);
37
+ }
38
+ /** Tolerant: de Engine mag regels anders benoemen. */
39
+ export function parseStockReservePlan(payload) {
40
+ const rawLines = payload?.lines ?? payload?.items ?? payload?.reservations ?? [];
41
+ const lines = (Array.isArray(rawLines) ? rawLines : [])
42
+ .map((l) => ({
43
+ tradeItemKey: String(l?.tradeItemKey ?? l?.trade_item_key ?? l?.tradeItemId ?? l?.sku ?? ""),
44
+ quantity: Number(l?.quantity ?? l?.numberOfPieces ?? l?.pieces ?? 0) || 0,
45
+ label: l?.label ?? l?.name ?? null,
46
+ }))
47
+ .filter((l) => l.tradeItemKey);
48
+ return {
49
+ lines,
50
+ loadCarriers: normalizeLoadCarriers(payload?.loadCarriers ?? payload?.load_carriers),
51
+ };
52
+ }
53
+ export function describeLoadCarriers(carriers) {
54
+ if (carriers.length === 0)
55
+ return "geen ladingdragers";
56
+ return carriers.map((c) => `${c.count}× ${c.type}`).join(", ");
57
+ }
@@ -0,0 +1,18 @@
1
+ export interface LogisticsEventInput {
2
+ eventType: "order.received" | "order.changed" | "stock.changed" | string;
3
+ orderRef: string;
4
+ strategyId?: string | null;
5
+ shadow?: boolean;
6
+ payload?: Record<string, any>;
7
+ }
8
+ export declare function insertLogisticsEvent(input: LogisticsEventInput): Promise<void>;
9
+ interface StockChangedInput {
10
+ tradeItemKey: string;
11
+ physicalStock: number;
12
+ reservedStock: number;
13
+ orderRef?: string | null;
14
+ }
15
+ /** Fire-and-forget vanuit de voorraad-UI. */
16
+ export declare function emitStockChangedEvent(input: StockChangedInput): Promise<void>;
17
+ export {};
18
+ //# sourceMappingURL=logistics-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logistics-events.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-events.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,MAAM,CAAC;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,iBAUpE;AAED,UAAU,iBAAiB;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA6BD,6CAA6C;AAC7C,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,iBAAiB,iBAmBnE"}