@flowselections/floriday-voorraad 1.0.28 → 1.0.30
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/voorraad/ArtikelWizard.d.ts +2 -1
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +555 -316
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts +2 -1
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/EditArtikelPage.js +3 -3
- package/dist-lib/components/voorraad/FustenPage.d.ts +2 -0
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -0
- package/dist-lib/components/voorraad/FustenPage.js +97 -0
- package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts +2 -0
- package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/LogistiekTakenCard.js +54 -0
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts +2 -0
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PublicatiesPage.js +60 -0
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts +12 -0
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PublishProgressCard.js +30 -0
- package/dist-lib/components/voorraad/PublishReadinessCard.d.ts +10 -0
- package/dist-lib/components/voorraad/PublishReadinessCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PublishReadinessCard.js +12 -0
- package/dist-lib/components/voorraad/StockSyncStatus.d.ts +2 -0
- package/dist-lib/components/voorraad/StockSyncStatus.d.ts.map +1 -0
- package/dist-lib/components/voorraad/StockSyncStatus.js +23 -0
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +5 -1
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +1 -1
- package/dist-lib/hooks/useFusten.d.ts +17 -0
- package/dist-lib/hooks/useFusten.d.ts.map +1 -0
- package/dist-lib/hooks/useFusten.js +81 -0
- package/dist-lib/hooks/useLocalTradeItems.d.ts +14 -4
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +83 -5
- package/dist-lib/hooks/useLogisticsTasks.d.ts +13 -0
- package/dist-lib/hooks/useLogisticsTasks.d.ts.map +1 -0
- package/dist-lib/hooks/useLogisticsTasks.js +155 -0
- package/dist-lib/hooks/usePublishJob.d.ts +13 -0
- package/dist-lib/hooks/usePublishJob.d.ts.map +1 -0
- package/dist-lib/hooks/usePublishJob.js +67 -0
- package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts +2 -0
- package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts.map +1 -0
- package/dist-lib/hooks/useRegisterModuleBaseUrl.js +31 -0
- package/dist-lib/hooks/useStockSyncHealth.d.ts +8 -0
- package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -0
- package/dist-lib/hooks/useStockSyncHealth.js +42 -0
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +37 -1
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +5 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1849 -50
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +1849 -50
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1849 -50
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +1870 -41
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floricode-required-features.functions.d.ts +1849 -50
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-client.d.ts +1 -1
- package/dist-lib/lib/floriday-client.d.ts.map +1 -1
- package/dist-lib/lib/floriday-client.js +1 -2
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +1849 -50
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-customer-offer.functions.js +14 -19
- package/dist-lib/lib/floriday-env.functions.d.ts +35712 -0
- package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-env.functions.js +16 -0
- package/dist-lib/lib/floriday-env.server.d.ts +12 -0
- package/dist-lib/lib/floriday-env.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-env.server.js +15 -2
- package/dist-lib/lib/floriday-gateway.functions.d.ts +26934 -82
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.js +12 -3
- package/dist-lib/lib/floriday-readiness.server.d.ts +11 -0
- package/dist-lib/lib/floriday-readiness.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-readiness.server.js +43 -0
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts +3698 -100
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +5442 -45
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-warehouse.functions.js +3 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts +3698 -100
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/fusten.d.ts +87 -0
- package/dist-lib/lib/fusten.d.ts.map +1 -0
- package/dist-lib/lib/fusten.js +389 -0
- package/dist-lib/lib/logistics-engine.d.ts +63 -0
- package/dist-lib/lib/logistics-engine.d.ts.map +1 -0
- package/dist-lib/lib/logistics-engine.js +57 -0
- package/dist-lib/lib/logistics-events.d.ts +18 -0
- package/dist-lib/lib/logistics-events.d.ts.map +1 -0
- package/dist-lib/lib/logistics-events.js +61 -0
- package/dist-lib/lib/module-runtime-config.functions.d.ts +14 -0
- package/dist-lib/lib/module-runtime-config.functions.d.ts.map +1 -0
- package/dist-lib/lib/module-runtime-config.functions.js +107 -0
- package/dist-lib/lib/publish-errors.d.ts +37 -0
- package/dist-lib/lib/publish-errors.d.ts.map +1 -0
- package/dist-lib/lib/publish-errors.js +183 -0
- package/dist-lib/lib/publish-pipeline.d.ts +91 -0
- package/dist-lib/lib/publish-pipeline.d.ts.map +1 -0
- package/dist-lib/lib/publish-pipeline.js +212 -0
- package/dist-lib/lib/publish-readiness.d.ts +42 -0
- package/dist-lib/lib/publish-readiness.d.ts.map +1 -0
- package/dist-lib/lib/publish-readiness.js +102 -0
- 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"}
|