@flowselections/floriday-voorraad 1.0.87 → 1.0.90
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/FloridayTakeoverPanel.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayTakeoverPanel.js +21 -8
- package/dist-lib/components/voorraad/InventoryStatusBadge.d.ts.map +1 -1
- package/dist-lib/components/voorraad/InventoryStatusBadge.js +7 -0
- package/dist-lib/hooks/useInventorySettings.d.ts +3 -0
- package/dist-lib/hooks/useInventorySettings.d.ts.map +1 -1
- package/dist-lib/hooks/useInventorySettings.js +1 -1
- package/dist-lib/hooks-entry.d.ts +1 -0
- package/dist-lib/hooks-entry.d.ts.map +1 -1
- package/dist-lib/hooks-entry.js +1 -0
- package/dist-lib/integrations/supabase/types.d.ts +214 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/floriday-customer-offers.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/floriday-customer-offers.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/floriday-customer-offers.test.js +36 -0
- package/dist-lib/lib/floriday-customer-offers.d.ts +25 -0
- package/dist-lib/lib/floriday-customer-offers.d.ts.map +1 -0
- package/dist-lib/lib/floriday-customer-offers.js +49 -0
- package/dist-lib/lib/floriday-takeover-core.d.ts +71 -0
- package/dist-lib/lib/floriday-takeover-core.d.ts.map +1 -0
- package/dist-lib/lib/floriday-takeover-core.js +196 -0
- package/dist-lib/lib/floriday-takeover.functions.d.ts +5 -49
- package/dist-lib/lib/floriday-takeover.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-takeover.functions.js +3 -219
- package/dist-lib/server-hooks/floriday-follow.d.ts +3 -0
- package/dist-lib/server-hooks/floriday-follow.d.ts.map +1 -0
- package/dist-lib/server-hooks/floriday-follow.js +46 -0
- package/dist-lib/server-hooks/guard.d.ts.map +1 -1
- package/dist-lib/server-hooks/guard.js +6 -1
- package/package.json +16 -2
- package/sql/AGENTS.md +3 -0
- package/sql/floriday-voorraad-schema.sql +8188 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-customer-offers.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/floriday-customer-offers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { customerOffersByTradeItem } from "../floriday-customer-offers.js";
|
|
3
|
+
const NOW = new Date("2026-10-20T10:00:00Z");
|
|
4
|
+
const TID = "AAAAAAAA-1111-2222-3333-444444444444";
|
|
5
|
+
const line = (over = {}) => ({
|
|
6
|
+
tradeItemId: TID,
|
|
7
|
+
pricePerPiece: { value: 0.85, currency: "EUR" },
|
|
8
|
+
numberOfPieces: 500,
|
|
9
|
+
tradePeriod: { startDateTime: "2026-10-01T00:00:00Z", endDateTime: "2026-11-01T00:00:00Z" },
|
|
10
|
+
isDeleted: false,
|
|
11
|
+
...over,
|
|
12
|
+
});
|
|
13
|
+
const offer = (over = {}, lines = [line()]) => ({
|
|
14
|
+
floriday_id: "o1",
|
|
15
|
+
data: { customerOfferId: "o1", customerOfferType: "SUPPLY_REQUEST", isDraft: false, isDeleted: false,
|
|
16
|
+
customerOrganizationId: "CUST-1", customerOfferLines: lines, ...over },
|
|
17
|
+
});
|
|
18
|
+
describe("klantaanbiedingen koppelen op customerOfferLines[].tradeItemId", () => {
|
|
19
|
+
it("koppelt een actuele regel op artikel-id, met klantnaam", () => {
|
|
20
|
+
const m = customerOffersByTradeItem([offer()], new Map([["cust-1", "Plantor"]]), NOW);
|
|
21
|
+
const v = m.get(TID.toLowerCase());
|
|
22
|
+
expect(v).toHaveLength(1);
|
|
23
|
+
expect(v[0].price).toBe(0.85);
|
|
24
|
+
expect(v[0].pieces).toBe(500);
|
|
25
|
+
expect(v[0].customer).toBe("Plantor");
|
|
26
|
+
});
|
|
27
|
+
it("slaat verlopen regels over", () => {
|
|
28
|
+
const m = customerOffersByTradeItem([offer({}, [line({ tradePeriod: { endDateTime: "2024-12-28T00:00:00Z" } })])], new Map(), NOW);
|
|
29
|
+
expect(m.size).toBe(0);
|
|
30
|
+
});
|
|
31
|
+
it("slaat concepten, verwijderde aanbiedingen en verwijderde regels over", () => {
|
|
32
|
+
expect(customerOffersByTradeItem([offer({ isDraft: true })], new Map(), NOW).size).toBe(0);
|
|
33
|
+
expect(customerOffersByTradeItem([offer({ isDeleted: true })], new Map(), NOW).size).toBe(0);
|
|
34
|
+
expect(customerOffersByTradeItem([offer({}, [line({ isDeleted: true })])], new Map(), NOW).size).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Klantaanbiedingen (Floriday CustomerOffer) koppelen aan artikelen.
|
|
3
|
+
* Vorm in de cache (data): { customerOfferId, customerOfferType, isDraft, isDeleted, title,
|
|
4
|
+
* agreementReference, customerOrganizationId?, customerOfferLines: [{ tradeItemId,
|
|
5
|
+
* pricePerPiece { value, currency }, numberOfPieces, tradePeriod { startDateTime, endDateTime },
|
|
6
|
+
* deliveryPeriod, salesUnit, warehouseId, isDeleted }] }.
|
|
7
|
+
* Alleen actuele regels: regel en aanbieding niet verwijderd, geen concept, tradePeriod.endDateTime >= nu.
|
|
8
|
+
*/
|
|
9
|
+
export type CustomerOfferView = {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string | null;
|
|
12
|
+
type: string | null;
|
|
13
|
+
customer: string | null;
|
|
14
|
+
price: number | null;
|
|
15
|
+
currency: string | null;
|
|
16
|
+
pieces: number | null;
|
|
17
|
+
from: string | null;
|
|
18
|
+
to: string | null;
|
|
19
|
+
};
|
|
20
|
+
/** Map artikel-id (kleine letters) → actuele klantaanbiedingsregels. */
|
|
21
|
+
export declare function customerOffersByTradeItem(offers: Array<{
|
|
22
|
+
floriday_id: string;
|
|
23
|
+
data: any;
|
|
24
|
+
}>, customerNames: Map<string, string>, now?: Date): Map<string, CustomerOfferView[]>;
|
|
25
|
+
//# sourceMappingURL=floriday-customer-offers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-customer-offers.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-customer-offers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB,CAAC;AAaF,wEAAwE;AACxE,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,CAAC,EACjD,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,GAAG,GAAE,IAAiB,GACrB,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CA+BlC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
function customerIds(d) {
|
|
2
|
+
const out = new Set();
|
|
3
|
+
const add = (v) => { if (typeof v === "string" && v)
|
|
4
|
+
out.add(v.toLowerCase()); };
|
|
5
|
+
add(d?.customerOrganizationId);
|
|
6
|
+
add(d?.buyerOrganizationId);
|
|
7
|
+
add(d?.customer?.organizationId);
|
|
8
|
+
for (const v of Array.isArray(d?.customerOrganizationIds) ? d.customerOrganizationIds : [])
|
|
9
|
+
add(v);
|
|
10
|
+
for (const c of Array.isArray(d?.customers) ? d.customers : [])
|
|
11
|
+
add(c?.organizationId ?? c?.customerOrganizationId);
|
|
12
|
+
return [...out];
|
|
13
|
+
}
|
|
14
|
+
/** Map artikel-id (kleine letters) → actuele klantaanbiedingsregels. */
|
|
15
|
+
export function customerOffersByTradeItem(offers, customerNames, now = new Date()) {
|
|
16
|
+
const by = new Map();
|
|
17
|
+
const nowMs = now.getTime();
|
|
18
|
+
for (const o of offers) {
|
|
19
|
+
const d = o.data ?? {};
|
|
20
|
+
if (d.isDeleted === true || d.isDraft === true)
|
|
21
|
+
continue;
|
|
22
|
+
const ids = customerIds(d);
|
|
23
|
+
const customer = ids.length ? ids.map((id) => customerNames.get(id) || id).join(", ") : (d.customerOrganizationName ?? null);
|
|
24
|
+
for (const l of Array.isArray(d.customerOfferLines) ? d.customerOfferLines : []) {
|
|
25
|
+
if (!l?.tradeItemId || l.isDeleted === true)
|
|
26
|
+
continue;
|
|
27
|
+
const end = l.tradePeriod?.endDateTime;
|
|
28
|
+
if (!end || !(Date.parse(end) >= nowMs))
|
|
29
|
+
continue;
|
|
30
|
+
const tid = String(l.tradeItemId).toLowerCase();
|
|
31
|
+
const pr = l.pricePerPiece;
|
|
32
|
+
const price = pr?.value != null ? Number(pr.value) : null;
|
|
33
|
+
const arr = by.get(tid) ?? [];
|
|
34
|
+
arr.push({
|
|
35
|
+
id: String(d.customerOfferId ?? o.floriday_id),
|
|
36
|
+
title: d.title ?? null,
|
|
37
|
+
type: d.customerOfferType ?? null,
|
|
38
|
+
customer,
|
|
39
|
+
price: price != null && Number.isFinite(price) ? price : null,
|
|
40
|
+
currency: pr?.currency ?? null,
|
|
41
|
+
pieces: l.numberOfPieces != null ? Number(l.numberOfPieces) : null,
|
|
42
|
+
from: l.tradePeriod?.startDateTime ?? null,
|
|
43
|
+
to: end,
|
|
44
|
+
});
|
|
45
|
+
by.set(tid, arr);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return by;
|
|
49
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proefrapport "Overnemen uit Floriday". Leest alleen: niets wordt geschreven
|
|
3
|
+
* en niets gaat naar Floriday. Het daadwerkelijk overnemen gebeurt via de
|
|
4
|
+
* databasefunctie `floriday_takeover_apply` (één transactie, terug te draaien).
|
|
5
|
+
*
|
|
6
|
+
* Floriday (Suppliers API 2026v1), alleen lezen via de leesroute van de
|
|
7
|
+
* Floriday-authenticatiemodule:
|
|
8
|
+
* - GET /trade-items/base-supply?year&week → WeeklyBaseSupply { tradeItemId, numberOfPieces, isAvailable, basePricePerPiece { currency, value } }
|
|
9
|
+
* Daarnaast de lokaal bijgehouden Floriday-gegevens van deze koppeling:
|
|
10
|
+
* - trade items (TradeItemBase: tradeItemId, supplierArticleCode, articleGtin, vbnProductCode, tradeItemName)
|
|
11
|
+
* - doorlopende voorraad (GET /trade-items/{id}/continuous-stock, door de ophaaltaak bewaard)
|
|
12
|
+
* - catalogusprijzen en aanbodregels (supply lines), incl. klantspecifieke aanbiedingen (alleen tonen)
|
|
13
|
+
*/
|
|
14
|
+
import { type FetchState } from "./floriday-fetch-status.js";
|
|
15
|
+
export type TakeoverRow = {
|
|
16
|
+
tradeItemId: string;
|
|
17
|
+
name: string;
|
|
18
|
+
articleCode: string | null;
|
|
19
|
+
gtin: string | null;
|
|
20
|
+
vbnCode: string | null;
|
|
21
|
+
productId: string | null;
|
|
22
|
+
productName: string | null;
|
|
23
|
+
action: "bijwerken" | "aanmaken" | "niet";
|
|
24
|
+
reason: string | null;
|
|
25
|
+
suggestion: string | null;
|
|
26
|
+
erpStock: number | null;
|
|
27
|
+
erpPrice: number | null;
|
|
28
|
+
floridayStock: number | null;
|
|
29
|
+
stockSource: string | null;
|
|
30
|
+
supplyLinePrice: number | null;
|
|
31
|
+
catalogPrice: number | null;
|
|
32
|
+
floridayPrice: number | null;
|
|
33
|
+
priceSource: string | null;
|
|
34
|
+
priceDiffers: boolean;
|
|
35
|
+
forms: {
|
|
36
|
+
continuous: boolean;
|
|
37
|
+
continuousAvailable: boolean | null;
|
|
38
|
+
weekly: boolean;
|
|
39
|
+
weeks: Array<{
|
|
40
|
+
year: number;
|
|
41
|
+
week: number;
|
|
42
|
+
pieces: number;
|
|
43
|
+
available: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
supply_lines: number;
|
|
46
|
+
customer_offers: number;
|
|
47
|
+
};
|
|
48
|
+
customerOffers: CustomerOfferView[];
|
|
49
|
+
};
|
|
50
|
+
export type { CustomerOfferView } from "./floriday-customer-offers.js";
|
|
51
|
+
import type { CustomerOfferView } from "./floriday-customer-offers.js";
|
|
52
|
+
/** Bouwt het proefrapport (alleen lezen). Ook gebruikt door de uurtaak "Volgt Floriday". */
|
|
53
|
+
export declare function buildTakeoverPreview(admin: any, conn: {
|
|
54
|
+
id: string;
|
|
55
|
+
environment?: string | null;
|
|
56
|
+
}): Promise<{
|
|
57
|
+
connectionId: string;
|
|
58
|
+
environment: any;
|
|
59
|
+
weeks: {
|
|
60
|
+
year: number;
|
|
61
|
+
week: number;
|
|
62
|
+
}[];
|
|
63
|
+
weekError: string | null;
|
|
64
|
+
fetchStatus: {
|
|
65
|
+
tradeItems: FetchState;
|
|
66
|
+
catalogPrices: FetchState;
|
|
67
|
+
customerOffers: FetchState;
|
|
68
|
+
};
|
|
69
|
+
rows: TakeoverRow[];
|
|
70
|
+
}>;
|
|
71
|
+
//# sourceMappingURL=floriday-takeover-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-takeover-core.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-takeover-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGtE,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IAC1C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE;QACL,UAAU,EAAE,OAAO,CAAC;QACpB,mBAAmB,EAAE,OAAO,GAAG,IAAI,CAAC;QACpC,MAAM,EAAE,OAAO,CAAC;QAChB,KAAK,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QACjF,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,cAAc,EAAE,iBAAiB,EAAE,CAAC;CACrC,CAAC;AAEF,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAmBpE,4FAA4F;AAC5F,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;;;;;;;;;oBAsErE,UAAU;uBAAiB,UAAU;wBAAkB,UAAU;;;GAgFnG"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proefrapport "Overnemen uit Floriday". Leest alleen: niets wordt geschreven
|
|
3
|
+
* en niets gaat naar Floriday. Het daadwerkelijk overnemen gebeurt via de
|
|
4
|
+
* databasefunctie `floriday_takeover_apply` (één transactie, terug te draaien).
|
|
5
|
+
*
|
|
6
|
+
* Floriday (Suppliers API 2026v1), alleen lezen via de leesroute van de
|
|
7
|
+
* Floriday-authenticatiemodule:
|
|
8
|
+
* - GET /trade-items/base-supply?year&week → WeeklyBaseSupply { tradeItemId, numberOfPieces, isAvailable, basePricePerPiece { currency, value } }
|
|
9
|
+
* Daarnaast de lokaal bijgehouden Floriday-gegevens van deze koppeling:
|
|
10
|
+
* - trade items (TradeItemBase: tradeItemId, supplierArticleCode, articleGtin, vbnProductCode, tradeItemName)
|
|
11
|
+
* - doorlopende voorraad (GET /trade-items/{id}/continuous-stock, door de ophaaltaak bewaard)
|
|
12
|
+
* - catalogusprijzen en aanbodregels (supply lines), incl. klantspecifieke aanbiedingen (alleen tonen)
|
|
13
|
+
*/
|
|
14
|
+
import { fetchState } from "./floriday-fetch-status.js";
|
|
15
|
+
import { customerOffersByTradeItem } from "./floriday-customer-offers.js";
|
|
16
|
+
function isoWeek(d) {
|
|
17
|
+
const t = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
|
18
|
+
const day = t.getUTCDay() || 7;
|
|
19
|
+
t.setUTCDate(t.getUTCDate() + 4 - day);
|
|
20
|
+
const y = t.getUTCFullYear();
|
|
21
|
+
const w = Math.ceil(((t.getTime() - Date.UTC(y, 0, 1)) / 86400000 + 1) / 7);
|
|
22
|
+
return { year: y, week: w };
|
|
23
|
+
}
|
|
24
|
+
function nameOf(data) {
|
|
25
|
+
const n = data?.tradeItemName;
|
|
26
|
+
if (typeof n === "string")
|
|
27
|
+
return n;
|
|
28
|
+
return String(n?.nl ?? n?.en ?? Object.values(n ?? {})[0] ?? "").trim();
|
|
29
|
+
}
|
|
30
|
+
const norm = (v) => String(v ?? "").trim().toLowerCase();
|
|
31
|
+
/** Bouwt het proefrapport (alleen lezen). Ook gebruikt door de uurtaak "Volgt Floriday". */
|
|
32
|
+
export async function buildTakeoverPreview(admin, conn) {
|
|
33
|
+
const nowIso = new Date().toISOString();
|
|
34
|
+
const [items, products, stock, cont, catalog, lines, syncRows, offers, custs] = await Promise.all([
|
|
35
|
+
admin.from("floriday_trade_items_cache").select("floriday_id, data").eq("connection_id", conn.id).eq("is_deleted", false).limit(5000),
|
|
36
|
+
admin.from("products").select("id, product, barcode, article_number, floriday_trade_item_id, sale_price").limit(10000),
|
|
37
|
+
admin.from("trade_item_stock").select("trade_item_key, physical_stock").eq("connection_id", conn.id).limit(20000),
|
|
38
|
+
admin.from("floriday_continuous_stock_cache").select("trade_item_id, number_of_pieces").eq("connection_id", conn.id).limit(5000),
|
|
39
|
+
admin.from("floriday_catalog_prices_cache").select("trade_item_id, price, updated_at").eq("connection_id", conn.id).eq("is_deleted", false).or(`valid_to.is.null,valid_to.gte.${nowIso}`).order("updated_at", { ascending: false }).limit(20000),
|
|
40
|
+
admin.from("trade_item_supply_lines").select("trade_item_id, supply_type, price_per_piece, updated_at").eq("connection_id", conn.id).order("updated_at", { ascending: false }).limit(20000),
|
|
41
|
+
admin.from("floriday_sync_state").select("entity_type, backfill_target_sequence, backfill_done_at, last_success_at").eq("connection_id", conn.id).in("entity_type", ["trade-items", "catalog-prices", "customer-offers"]),
|
|
42
|
+
admin.from("floriday_customer_offers_cache").select("floriday_id, data").eq("connection_id", conn.id).eq("is_deleted", false).limit(5000),
|
|
43
|
+
admin.from("floriday_customers_cache").select("organization_id, name").eq("connection_id", conn.id).limit(10000),
|
|
44
|
+
]);
|
|
45
|
+
for (const r of [items, products, stock, cont, catalog, lines, syncRows, offers, custs])
|
|
46
|
+
if (r.error)
|
|
47
|
+
throw new Error(r.error.message);
|
|
48
|
+
// Weekaanbod: huidige en twee komende weken, live gelezen (1 per seconde).
|
|
49
|
+
const now = new Date();
|
|
50
|
+
const weeks = [0, 1, 2].map((i) => isoWeek(new Date(now.getTime() + i * 7 * 86400000)));
|
|
51
|
+
let weekError = null;
|
|
52
|
+
const weekly = new Map();
|
|
53
|
+
try {
|
|
54
|
+
const { readBaseSupplyWeeks } = await import("./base-supply-reader.server.js");
|
|
55
|
+
const out = await readBaseSupplyWeeks(weeks);
|
|
56
|
+
if (out.error)
|
|
57
|
+
weekError = out.error;
|
|
58
|
+
for (const w of out.results) {
|
|
59
|
+
if (!w.ok) {
|
|
60
|
+
weekError = weekError ?? w.error;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
for (const r of w.rows) {
|
|
64
|
+
const arr = weekly.get(r.tradeItemId) ?? [];
|
|
65
|
+
arr.push({ year: w.year, week: w.week, pieces: r.numberOfPieces, available: r.isAvailable, price: r.basePricePerPiece });
|
|
66
|
+
weekly.set(r.tradeItemId, arr);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
weekError = e?.message ?? String(e);
|
|
72
|
+
}
|
|
73
|
+
const byFid = new Map();
|
|
74
|
+
const unlinked = [];
|
|
75
|
+
for (const p of products.data ?? []) {
|
|
76
|
+
if (p.floriday_trade_item_id)
|
|
77
|
+
byFid.set(String(p.floriday_trade_item_id).toLowerCase(), p);
|
|
78
|
+
else
|
|
79
|
+
unlinked.push(p);
|
|
80
|
+
}
|
|
81
|
+
const stockBy = new Map();
|
|
82
|
+
for (const s of stock.data ?? [])
|
|
83
|
+
stockBy.set(String(s.trade_item_key), Number(s.physical_stock ?? 0));
|
|
84
|
+
const contBy = new Map();
|
|
85
|
+
for (const c of cont.data ?? [])
|
|
86
|
+
contBy.set(String(c.trade_item_id), Number(c.number_of_pieces ?? 0));
|
|
87
|
+
const catBy = new Map();
|
|
88
|
+
for (const c of catalog.data ?? []) {
|
|
89
|
+
const k = String(c.trade_item_id);
|
|
90
|
+
if (!catBy.has(k) && Number(c.price) > 0)
|
|
91
|
+
catBy.set(k, Number(c.price));
|
|
92
|
+
}
|
|
93
|
+
const lineBy = new Map();
|
|
94
|
+
for (const l of lines.data ?? []) {
|
|
95
|
+
const k = String(l.trade_item_id);
|
|
96
|
+
const cur = lineBy.get(k) ?? { price: null, count: 0, customer: 0 };
|
|
97
|
+
const isCustomer = /customer/i.test(String(l.supply_type ?? ""));
|
|
98
|
+
if (isCustomer)
|
|
99
|
+
cur.customer++;
|
|
100
|
+
else {
|
|
101
|
+
cur.count++;
|
|
102
|
+
if (cur.price == null && Number(l.price_per_piece) > 0)
|
|
103
|
+
cur.price = Number(l.price_per_piece);
|
|
104
|
+
}
|
|
105
|
+
lineBy.set(k, cur);
|
|
106
|
+
}
|
|
107
|
+
const syncBy = new Map();
|
|
108
|
+
for (const r of syncRows.data ?? [])
|
|
109
|
+
syncBy.set(String(r.entity_type), r);
|
|
110
|
+
const custNames = new Map();
|
|
111
|
+
for (const c of custs.data ?? [])
|
|
112
|
+
if (c.organization_id && c.name)
|
|
113
|
+
custNames.set(String(c.organization_id).toLowerCase(), String(c.name));
|
|
114
|
+
const offersBy = customerOffersByTradeItem(offers.data ?? [], custNames);
|
|
115
|
+
const fetchStatus = {
|
|
116
|
+
tradeItems: fetchState(syncBy.get("trade-items") ?? null, (items.data ?? []).length),
|
|
117
|
+
catalogPrices: fetchState(syncBy.get("catalog-prices") ?? null, (catalog.data ?? []).length),
|
|
118
|
+
customerOffers: fetchState(syncBy.get("customer-offers") ?? null, (offers.data ?? []).length),
|
|
119
|
+
};
|
|
120
|
+
const cur = weeks[0];
|
|
121
|
+
const rows = (items.data ?? []).map((it) => {
|
|
122
|
+
const fid = String(it.floriday_id).toLowerCase();
|
|
123
|
+
const d = it.data ?? {};
|
|
124
|
+
const p = byFid.get(fid) ?? null;
|
|
125
|
+
const w = (weekly.get(fid) ?? []).sort((a, b) => a.year - b.year || a.week - b.week);
|
|
126
|
+
const contRaw = contBy.has(fid) ? contBy.get(fid) : null;
|
|
127
|
+
const line = lineBy.get(fid) ?? { price: null, count: 0, customer: 0 };
|
|
128
|
+
// Aanbodvorm uit Floriday zelf: staat er een doorlopende-voorraadrecord, dan is dat de vorm; anders weekaanbod huidige week.
|
|
129
|
+
let floridayStock = null;
|
|
130
|
+
let stockSource = null;
|
|
131
|
+
if (contRaw != null) {
|
|
132
|
+
floridayStock = contRaw;
|
|
133
|
+
stockSource = "Doorlopende voorraad";
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const now = w.find((x) => x.year === cur.year && x.week === cur.week);
|
|
137
|
+
if (now) {
|
|
138
|
+
floridayStock = now.pieces;
|
|
139
|
+
stockSource = `Weekaanbod week ${now.week}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Prijs: aanbodregel gaat voor op catalogusprijs.
|
|
143
|
+
const weekPrice = w.find((x) => x.price != null && x.price > 0)?.price ?? null;
|
|
144
|
+
const supplyLinePrice = line.price ?? weekPrice;
|
|
145
|
+
const catalogPrice = catBy.get(fid) ?? null;
|
|
146
|
+
const floridayPrice = supplyLinePrice ?? catalogPrice;
|
|
147
|
+
const priceSource = supplyLinePrice != null ? "Aanbodregel" : catalogPrice != null ? "Catalogusprijs" : null;
|
|
148
|
+
let suggestion = null;
|
|
149
|
+
if (!p) {
|
|
150
|
+
const gtin = norm(d.articleGtin), code = norm(d.supplierArticleCode), nm = norm(nameOf(d));
|
|
151
|
+
const hit = unlinked.find((u) => (gtin && norm(u.barcode) === gtin) || (code && norm(u.article_number) === code) || (nm && norm(u.product) === nm));
|
|
152
|
+
if (hit)
|
|
153
|
+
suggestion = `Lijkt op bestaand artikel "${hit.product}" (niet automatisch gekoppeld)`;
|
|
154
|
+
}
|
|
155
|
+
const erpStock = p
|
|
156
|
+
? stockBy.get(fid) ?? stockBy.get(`product:${p.id}`) ?? stockBy.get(String(p.id)) ?? null
|
|
157
|
+
: null;
|
|
158
|
+
const name = nameOf(d) || String(d.supplierArticleCode ?? fid);
|
|
159
|
+
let action = p ? "bijwerken" : "aanmaken";
|
|
160
|
+
let reason = null;
|
|
161
|
+
if (floridayStock == null && floridayPrice == null) {
|
|
162
|
+
action = p ? "niet" : "aanmaken";
|
|
163
|
+
reason = "Geen aantal en geen prijs gevonden in Floriday";
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
tradeItemId: fid, name,
|
|
167
|
+
articleCode: d.supplierArticleCode ?? null,
|
|
168
|
+
gtin: d.articleGtin ?? null,
|
|
169
|
+
vbnCode: d.vbnProductCode != null ? String(d.vbnProductCode) : null,
|
|
170
|
+
productId: p?.id ?? null, productName: p?.product ?? null,
|
|
171
|
+
action, reason, suggestion,
|
|
172
|
+
erpStock, erpPrice: p?.sale_price != null ? Number(p.sale_price) : null,
|
|
173
|
+
floridayStock, stockSource,
|
|
174
|
+
supplyLinePrice, catalogPrice, floridayPrice, priceSource,
|
|
175
|
+
priceDiffers: supplyLinePrice != null && catalogPrice != null && Math.abs(supplyLinePrice - catalogPrice) > 0.0001,
|
|
176
|
+
forms: {
|
|
177
|
+
continuous: contRaw != null && contRaw > 0,
|
|
178
|
+
continuousAvailable: contRaw == null ? null : contRaw > 0,
|
|
179
|
+
weekly: w.length > 0,
|
|
180
|
+
weeks: w.map(({ year, week, pieces, available }) => ({ year, week, pieces, available })),
|
|
181
|
+
supply_lines: line.count,
|
|
182
|
+
customer_offers: (offersBy.get(fid) ?? []).length,
|
|
183
|
+
},
|
|
184
|
+
customerOffers: offersBy.get(fid) ?? [],
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
rows.sort((a, b) => a.name.localeCompare(b.name, "nl"));
|
|
188
|
+
return {
|
|
189
|
+
connectionId: conn.id,
|
|
190
|
+
environment: conn.environment ?? null,
|
|
191
|
+
weeks,
|
|
192
|
+
weekError,
|
|
193
|
+
fetchStatus,
|
|
194
|
+
rows,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -1,48 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export type TakeoverRow = {
|
|
3
|
-
tradeItemId: string;
|
|
4
|
-
name: string;
|
|
5
|
-
articleCode: string | null;
|
|
6
|
-
gtin: string | null;
|
|
7
|
-
vbnCode: string | null;
|
|
8
|
-
productId: string | null;
|
|
9
|
-
productName: string | null;
|
|
10
|
-
action: "bijwerken" | "aanmaken" | "niet";
|
|
11
|
-
reason: string | null;
|
|
12
|
-
suggestion: string | null;
|
|
13
|
-
erpStock: number | null;
|
|
14
|
-
erpPrice: number | null;
|
|
15
|
-
floridayStock: number | null;
|
|
16
|
-
stockSource: string | null;
|
|
17
|
-
supplyLinePrice: number | null;
|
|
18
|
-
catalogPrice: number | null;
|
|
19
|
-
floridayPrice: number | null;
|
|
20
|
-
priceSource: string | null;
|
|
21
|
-
priceDiffers: boolean;
|
|
22
|
-
forms: {
|
|
23
|
-
continuous: boolean;
|
|
24
|
-
continuousAvailable: boolean | null;
|
|
25
|
-
weekly: boolean;
|
|
26
|
-
weeks: Array<{
|
|
27
|
-
year: number;
|
|
28
|
-
week: number;
|
|
29
|
-
pieces: number;
|
|
30
|
-
available: boolean;
|
|
31
|
-
}>;
|
|
32
|
-
supply_lines: number;
|
|
33
|
-
customer_offers: number;
|
|
34
|
-
};
|
|
35
|
-
customerOffers: CustomerOfferView[];
|
|
36
|
-
};
|
|
37
|
-
export type CustomerOfferView = {
|
|
38
|
-
id: string;
|
|
39
|
-
customer: string | null;
|
|
40
|
-
price: number | null;
|
|
41
|
-
currency: string | null;
|
|
42
|
-
pieces: number | null;
|
|
43
|
-
from: string | null;
|
|
44
|
-
to: string | null;
|
|
45
|
-
};
|
|
1
|
+
export type { TakeoverRow, CustomerOfferView } from "./floriday-takeover-core.js";
|
|
46
2
|
export declare const getFloridayTakeoverPreview: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
47
3
|
supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
|
|
48
4
|
userId: string;
|
|
@@ -56,10 +12,10 @@ export declare const getFloridayTakeoverPreview: import("@tanstack/start-client-
|
|
|
56
12
|
}[];
|
|
57
13
|
weekError: string | null;
|
|
58
14
|
fetchStatus: {
|
|
59
|
-
tradeItems: FetchState;
|
|
60
|
-
catalogPrices: FetchState;
|
|
61
|
-
customerOffers: FetchState;
|
|
15
|
+
tradeItems: import("./floriday-fetch-status.js").FetchState;
|
|
16
|
+
catalogPrices: import("./floriday-fetch-status.js").FetchState;
|
|
17
|
+
customerOffers: import("./floriday-fetch-status.js").FetchState;
|
|
62
18
|
};
|
|
63
|
-
rows: TakeoverRow[];
|
|
19
|
+
rows: import("./floriday-takeover-core.js").TakeoverRow[];
|
|
64
20
|
}>>;
|
|
65
21
|
//# sourceMappingURL=floriday-takeover.functions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floriday-takeover.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-takeover.functions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"floriday-takeover.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-takeover.functions.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE/E,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;GASnC,CAAC"}
|