@flowselections/floriday-verkoop-module 1.0.7 → 1.0.9

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 (67) hide show
  1. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts +9 -0
  2. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts.map +1 -0
  3. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.js +26 -0
  4. package/dist-lib/components/verkoop/AnalyticsCards.js +2 -2
  5. package/dist-lib/components/verkoop/CreateOrderPage.d.ts +2 -0
  6. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -0
  7. package/dist-lib/components/verkoop/CreateOrderPage.js +218 -0
  8. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.d.ts +5 -0
  9. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.d.ts.map +1 -0
  10. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.js +40 -0
  11. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts +2 -0
  12. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts.map +1 -0
  13. package/dist-lib/components/verkoop/SalesOrderDetailPage.js +14 -0
  14. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts +6 -0
  15. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -0
  16. package/dist-lib/components/verkoop/SalesOrderDetailView.js +213 -0
  17. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.d.ts +9 -0
  18. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.d.ts.map +1 -0
  19. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.js +62 -0
  20. package/dist-lib/components/verkoop/SalesOrdersFilters.d.ts +13 -0
  21. package/dist-lib/components/verkoop/SalesOrdersFilters.d.ts.map +1 -1
  22. package/dist-lib/components/verkoop/SalesOrdersFilters.js +123 -23
  23. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts +3 -2
  24. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  25. package/dist-lib/components/verkoop/SalesOrdersTable.js +3 -3
  26. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
  27. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +125 -33
  28. package/dist-lib/components/verkoop/WeekCalendar.d.ts +6 -0
  29. package/dist-lib/components/verkoop/WeekCalendar.d.ts.map +1 -0
  30. package/dist-lib/components/verkoop/WeekCalendar.js +101 -0
  31. package/dist-lib/index.d.ts +2 -0
  32. package/dist-lib/index.d.ts.map +1 -1
  33. package/dist-lib/index.js +2 -0
  34. package/dist-lib/integrations/supabase/auth-middleware.d.ts +8509 -937
  35. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  36. package/dist-lib/integrations/supabase/client.d.ts +8509 -937
  37. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  38. package/dist-lib/integrations/supabase/client.server.d.ts +8509 -937
  39. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  40. package/dist-lib/integrations/supabase/types.d.ts +8753 -954
  41. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  42. package/dist-lib/integrations/supabase/types.js +20 -1
  43. package/dist-lib/lib/floriday-gateway.server.d.ts +42 -0
  44. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -0
  45. package/dist-lib/lib/floriday-gateway.server.js +222 -0
  46. package/dist-lib/lib/floriday-public.functions.d.ts +63971 -0
  47. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -0
  48. package/dist-lib/lib/floriday-public.functions.js +210 -0
  49. package/dist-lib/lib/floriday-sync.d.ts +32 -0
  50. package/dist-lib/lib/floriday-sync.d.ts.map +1 -0
  51. package/dist-lib/lib/floriday-sync.js +116 -0
  52. package/dist-lib/lib/floriday-writes.functions.d.ts +45678 -0
  53. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -0
  54. package/dist-lib/lib/floriday-writes.functions.js +110 -0
  55. package/dist-lib/lib/salesorders-mapping.d.ts +85 -0
  56. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -0
  57. package/dist-lib/lib/salesorders-mapping.js +198 -0
  58. package/dist-lib/lib/salesorders.functions.d.ts +27436 -0
  59. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -0
  60. package/dist-lib/lib/salesorders.functions.js +174 -0
  61. package/dist-lib/lib/tradeitems.functions.d.ts +9139 -0
  62. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -0
  63. package/dist-lib/lib/tradeitems.functions.js +60 -0
  64. package/dist-lib/lib/useSalesOrdersQuery.d.ts +6 -0
  65. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -0
  66. package/dist-lib/lib/useSalesOrdersQuery.js +129 -0
  67. package/package.json +16 -3
@@ -0,0 +1,110 @@
1
+ // Write helpers — alle Floriday-schrijfacties lopen via deze functies.
2
+ // Niemand mag fetch met Authorization-header zelf bouwen.
3
+ import { createServerFn } from "@tanstack/react-start";
4
+ import { z } from "zod";
5
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
6
+ const EnvSchema = z.enum(["staging", "live"]);
7
+ async function callGateway(env, args, userId) {
8
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
9
+ const { florydayWrite } = await import("./floriday-gateway.server");
10
+ return florydayWrite(supabaseAdmin, env, args, userId);
11
+ }
12
+ // ─── Sales orders ─────────────────────────────────────────────────────────
13
+ const AckSchema = z.object({
14
+ environment: EnvSchema,
15
+ salesOrderId: z.string().min(1),
16
+ lines: z
17
+ .array(z.object({
18
+ salesOrderLineId: z.string().min(1),
19
+ acknowledgedQuantity: z.number().int().nonnegative(),
20
+ }))
21
+ .optional(),
22
+ });
23
+ export const acknowledgeSalesOrder = createServerFn({ method: "POST" })
24
+ .middleware([requireSupabaseAuth])
25
+ .inputValidator((d) => AckSchema.parse(d))
26
+ .handler(async ({ data, context }) => callGateway(data.environment, {
27
+ module: "sales",
28
+ action: "acknowledge-sales-order",
29
+ method: "POST",
30
+ path: `/sales-orders/${encodeURIComponent(data.salesOrderId)}/acknowledge`,
31
+ referenceId: data.salesOrderId,
32
+ body: data.lines ? { lines: data.lines } : {},
33
+ }, context.userId));
34
+ const RejectSchema = z.object({
35
+ environment: EnvSchema,
36
+ salesOrderId: z.string().min(1),
37
+ reason: z.string().min(1),
38
+ });
39
+ export const rejectSalesOrder = createServerFn({ method: "POST" })
40
+ .middleware([requireSupabaseAuth])
41
+ .inputValidator((d) => RejectSchema.parse(d))
42
+ .handler(async ({ data, context }) => callGateway(data.environment, {
43
+ module: "sales",
44
+ action: "reject-sales-order",
45
+ method: "POST",
46
+ path: `/sales-orders/${encodeURIComponent(data.salesOrderId)}/reject`,
47
+ referenceId: data.salesOrderId,
48
+ body: { reason: data.reason },
49
+ }, context.userId));
50
+ // ─── Fulfillment ──────────────────────────────────────────────────────────
51
+ const ConfirmFulSchema = z.object({
52
+ environment: EnvSchema,
53
+ fulfillmentOrderId: z.string().min(1),
54
+ lines: z
55
+ .array(z.object({
56
+ fulfillmentOrderLineId: z.string().min(1),
57
+ deliveredQuantity: z.number().int().nonnegative(),
58
+ }))
59
+ .optional(),
60
+ });
61
+ export const confirmFulfillmentOrder = createServerFn({ method: "POST" })
62
+ .middleware([requireSupabaseAuth])
63
+ .inputValidator((d) => ConfirmFulSchema.parse(d))
64
+ .handler(async ({ data, context }) => callGateway(data.environment, {
65
+ module: "warehouse",
66
+ action: "confirm-fulfillment-order",
67
+ method: "POST",
68
+ path: `/fulfillment-orders/${encodeURIComponent(data.fulfillmentOrderId)}/confirm`,
69
+ referenceId: data.fulfillmentOrderId,
70
+ body: data.lines ? { lines: data.lines } : {},
71
+ }, context.userId));
72
+ // ─── Catalog ──────────────────────────────────────────────────────────────
73
+ const TradeItemSchema = z.object({
74
+ environment: EnvSchema,
75
+ tradeItemId: z.string().min(1),
76
+ payload: z.record(z.string(), z.any()),
77
+ });
78
+ export const publishTradeItem = createServerFn({ method: "POST" })
79
+ .middleware([requireSupabaseAuth])
80
+ .inputValidator((d) => TradeItemSchema.parse(d))
81
+ .handler(async ({ data, context }) => callGateway(data.environment, {
82
+ module: "catalog",
83
+ action: "publish-trade-item",
84
+ method: "PUT",
85
+ path: `/trade-items/${encodeURIComponent(data.tradeItemId)}`,
86
+ referenceId: data.tradeItemId,
87
+ body: data.payload,
88
+ validateEnums: [{ field: "currency", listName: "Currency" }],
89
+ }, context.userId));
90
+ const SupplyLineSchema = z.object({
91
+ environment: EnvSchema,
92
+ supplyLineId: z.string().min(1),
93
+ payload: z.record(z.string(), z.any()),
94
+ });
95
+ export const upsertSupplyLine = createServerFn({ method: "POST" })
96
+ .middleware([requireSupabaseAuth])
97
+ .inputValidator((d) => SupplyLineSchema.parse(d))
98
+ .handler(async ({ data, context }) => callGateway(data.environment, {
99
+ module: "catalog",
100
+ action: "upsert-supply-line",
101
+ method: "PUT",
102
+ path: `/supply-lines/${encodeURIComponent(data.supplyLineId)}`,
103
+ referenceId: data.supplyLineId,
104
+ body: data.payload,
105
+ validateEnums: [
106
+ { field: "currency", listName: "Currency" },
107
+ { field: "salesUnit", listName: "SalesUnit" },
108
+ { field: "lines[].loadCarrierType", listName: "SupplyLoadCarrierType" },
109
+ ],
110
+ }, context.userId));
@@ -0,0 +1,85 @@
1
+ export interface SalesOrderDTO {
2
+ id: string;
3
+ orderNumber: string;
4
+ productName: string;
5
+ sku: string;
6
+ imageUrl: string | null;
7
+ orderedAt: string;
8
+ deliveryAt: string;
9
+ quantity: {
10
+ multiplier: number;
11
+ count: number;
12
+ unitPrice: number;
13
+ };
14
+ revenue: number;
15
+ fustCount: number;
16
+ isLocal?: boolean;
17
+ customer: {
18
+ name: string;
19
+ postalCode: string;
20
+ city: string;
21
+ logoUrl: string | null;
22
+ };
23
+ status: {
24
+ isCancelled: boolean;
25
+ isShipped: boolean;
26
+ isInvoiced: boolean;
27
+ isConfirmed: boolean;
28
+ };
29
+ raw: {
30
+ order: any;
31
+ customer: any | null;
32
+ tradeItem: any | null;
33
+ };
34
+ }
35
+ export interface CustomerLookupEntry {
36
+ name: string;
37
+ postalCode: string;
38
+ city: string;
39
+ logoUrl: string | null;
40
+ raw?: any;
41
+ }
42
+ export type CustomerLookup = Map<string, CustomerLookupEntry>;
43
+ export interface TradeItemLookupEntry {
44
+ name: string;
45
+ imageUrl: string | null;
46
+ sku: string | null;
47
+ raw?: any;
48
+ }
49
+ export type TradeItemLookup = Map<string, TradeItemLookupEntry>;
50
+ export declare function mapJsonbToSalesOrderDTO(o: any, customerLookup?: CustomerLookup, tradeItemLookup?: TradeItemLookup): SalesOrderDTO;
51
+ export interface SalesOrdersTotals {
52
+ revenue: number;
53
+ avgPrice: number;
54
+ fustCount: number;
55
+ }
56
+ export declare function computeTotals(orders: SalesOrderDTO[]): SalesOrdersTotals;
57
+ export interface SalesOrderFilters {
58
+ from?: string;
59
+ to?: string;
60
+ search?: string;
61
+ status?: string;
62
+ week?: number;
63
+ year?: number;
64
+ month?: number;
65
+ monthYear?: number;
66
+ day?: string;
67
+ customFrom?: string;
68
+ customTo?: string;
69
+ dateField?: "ordered" | "delivery";
70
+ period?: "week" | "month" | "day" | "last7" | "last30" | "custom";
71
+ }
72
+ export declare function isoWeekRange(week: number, year: number): {
73
+ start: Date;
74
+ end: Date;
75
+ };
76
+ export declare function monthRange(month: number, year: number): {
77
+ start: Date;
78
+ end: Date;
79
+ };
80
+ export declare function applyFilters(orders: SalesOrderDTO[], f: SalesOrderFilters): SalesOrderDTO[];
81
+ export declare function ordersToCsv(orders: SalesOrderDTO[]): {
82
+ csv: string;
83
+ filename: string;
84
+ };
85
+ //# sourceMappingURL=salesorders-mapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"salesorders-mapping.d.ts","sourceRoot":"","sources":["../../src/lib/salesorders-mapping.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,198 @@
1
+ function toNum(v, d = 0) {
2
+ if (typeof v === "number")
3
+ return v;
4
+ if (typeof v === "string") {
5
+ const n = parseFloat(v);
6
+ return Number.isFinite(n) ? n : d;
7
+ }
8
+ return d;
9
+ }
10
+ export function mapJsonbToSalesOrderDTO(o, customerLookup, tradeItemLookup) {
11
+ // Floriday suppliers-api-2026v1 levert een vlak sales-order object
12
+ // (geen salesOrderLines-array). Velden staan direct op het object.
13
+ const numberOfPieces = toNum(o?.numberOfPieces, 0);
14
+ const piecesPerPackage = Math.max(1, toNum(o?.packingConfiguration?.piecesPerPackage, 1));
15
+ const numberOfPackages = piecesPerPackage > 0 ? Math.ceil(numberOfPieces / piecesPerPackage) : 0;
16
+ const unitPrice = toNum(o?.pricePerPiece?.value);
17
+ const orderAmount = toNum(o?.calculatedFields?.orderAmount?.value);
18
+ const revenue = orderAmount > 0 ? orderAmount : unitPrice * numberOfPieces;
19
+ const orderedAt = o?.orderDateTime ?? o?.creationDateTime ?? "";
20
+ const deliveryAt = o?.delivery?.latestDeliveryDateTime ?? "";
21
+ const customerOrgId = o?.customerOrganizationId ?? "";
22
+ const found = customerOrgId ? customerLookup?.get(customerOrgId) : undefined;
23
+ const status = String(o?.status ?? "").toUpperCase();
24
+ const isCancelled = status === "CANCELLED" || !!o?.automaticallyCancelledOn;
25
+ const isShipped = status === "SHIPPED" || status === "DELIVERED";
26
+ const isInvoiced = o?.isPaid === true;
27
+ const isConfirmed = status === "COMMITTED" || status === "CONFIRMED";
28
+ const orderNumber = (typeof o?.customerOrderId === "string" && o.customerOrderId.trim().length > 0
29
+ ? o.customerOrderId
30
+ : null) ??
31
+ (o?.salesChannelOrderId ? String(o.salesChannelOrderId) : null) ??
32
+ String(o?.salesOrderId ?? "").slice(0, 16);
33
+ const tradeItemId = String(o?.tradeItemId ?? "");
34
+ const tradeItem = tradeItemId ? tradeItemLookup?.get(tradeItemId) : undefined;
35
+ return {
36
+ id: String(o?.salesOrderId ?? ""),
37
+ orderNumber: String(orderNumber ?? ""),
38
+ productName: tradeItem?.name ?? "Onbekend artikel",
39
+ sku: tradeItem?.sku ?? tradeItemId,
40
+ imageUrl: tradeItem?.imageUrl ?? null,
41
+ orderedAt,
42
+ deliveryAt,
43
+ quantity: { multiplier: numberOfPackages, count: piecesPerPackage, unitPrice },
44
+ revenue,
45
+ fustCount: numberOfPackages,
46
+ customer: {
47
+ name: found?.name ?? "Onbekende klant",
48
+ postalCode: found?.postalCode ?? "",
49
+ city: found?.city ?? "",
50
+ logoUrl: found?.logoUrl ?? null,
51
+ },
52
+ status: { isCancelled, isShipped, isInvoiced, isConfirmed },
53
+ raw: {
54
+ order: o ?? null,
55
+ customer: found?.raw ?? null,
56
+ tradeItem: tradeItem?.raw ?? null,
57
+ },
58
+ };
59
+ }
60
+ export function computeTotals(orders) {
61
+ const revenue = orders.reduce((s, o) => s + o.revenue, 0);
62
+ const fustCount = orders.reduce((s, o) => s + o.fustCount, 0);
63
+ const totalPieces = orders.reduce((s, o) => s + o.quantity.multiplier * o.quantity.count, 0);
64
+ const avgPrice = totalPieces > 0 ? revenue / totalPieces : 0;
65
+ return { revenue, avgPrice, fustCount };
66
+ }
67
+ // ISO-8601 weekbereik (maandag 00:00 t/m maandag erop 00:00, UTC).
68
+ export function isoWeekRange(week, year) {
69
+ // 4 januari valt altijd in week 1 (ISO).
70
+ const jan4 = new Date(Date.UTC(year, 0, 4));
71
+ const jan4Dow = (jan4.getUTCDay() + 6) % 7; // ma=0..zo=6
72
+ const week1Monday = new Date(jan4);
73
+ week1Monday.setUTCDate(jan4.getUTCDate() - jan4Dow);
74
+ const start = new Date(week1Monday);
75
+ start.setUTCDate(week1Monday.getUTCDate() + (week - 1) * 7);
76
+ const end = new Date(start);
77
+ end.setUTCDate(start.getUTCDate() + 7);
78
+ return { start, end };
79
+ }
80
+ export function monthRange(month, year) {
81
+ const start = new Date(Date.UTC(year, month, 1));
82
+ const end = new Date(Date.UTC(year, month + 1, 1));
83
+ return { start, end };
84
+ }
85
+ function ymdToUtc(ymd) {
86
+ const [y, m, d] = ymd.split("-").map(Number);
87
+ return new Date(Date.UTC(y, (m ?? 1) - 1, d ?? 1));
88
+ }
89
+ function startOfTodayUtc() {
90
+ const now = new Date();
91
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
92
+ }
93
+ export function applyFilters(orders, f) {
94
+ const dateField = f.dateField ?? "ordered";
95
+ let rangeStartIso = null;
96
+ let rangeEndIso = null;
97
+ if (f.period === "month" && f.month !== undefined && f.monthYear !== undefined) {
98
+ const { start, end } = monthRange(f.month, f.monthYear);
99
+ rangeStartIso = start.toISOString();
100
+ rangeEndIso = end.toISOString();
101
+ }
102
+ else if (f.period === "day" && f.day) {
103
+ const start = ymdToUtc(f.day);
104
+ const end = new Date(start);
105
+ end.setUTCDate(start.getUTCDate() + 1);
106
+ rangeStartIso = start.toISOString();
107
+ rangeEndIso = end.toISOString();
108
+ }
109
+ else if (f.period === "last7" || f.period === "last30") {
110
+ const days = f.period === "last7" ? 7 : 30;
111
+ const end = startOfTodayUtc();
112
+ end.setUTCDate(end.getUTCDate() + 1);
113
+ const start = new Date(end);
114
+ start.setUTCDate(end.getUTCDate() - days);
115
+ rangeStartIso = start.toISOString();
116
+ rangeEndIso = end.toISOString();
117
+ }
118
+ else if (f.period === "custom" && f.customFrom && f.customTo) {
119
+ const start = ymdToUtc(f.customFrom);
120
+ const end = ymdToUtc(f.customTo);
121
+ end.setUTCDate(end.getUTCDate() + 1);
122
+ rangeStartIso = start.toISOString();
123
+ rangeEndIso = end.toISOString();
124
+ }
125
+ else if (f.week && f.year) {
126
+ const { start, end } = isoWeekRange(f.week, f.year);
127
+ rangeStartIso = start.toISOString();
128
+ rangeEndIso = end.toISOString();
129
+ }
130
+ return orders.filter((o) => {
131
+ const refDate = dateField === "delivery" ? o.deliveryAt : o.orderedAt;
132
+ if (rangeStartIso && rangeEndIso) {
133
+ if (!refDate)
134
+ return false;
135
+ if (refDate < rangeStartIso || refDate >= rangeEndIso)
136
+ return false;
137
+ }
138
+ if (f.from && refDate < f.from)
139
+ return false;
140
+ if (f.to && refDate > f.to)
141
+ return false;
142
+ if (f.search) {
143
+ const q = f.search.toLowerCase();
144
+ const hay = `${o.productName} ${o.sku} ${o.orderNumber} ${o.customer.name}`.toLowerCase();
145
+ if (!hay.includes(q))
146
+ return false;
147
+ }
148
+ if (f.status === "cancelled" && !o.status.isCancelled)
149
+ return false;
150
+ if (f.status === "shipped" && !o.status.isShipped)
151
+ return false;
152
+ if (f.status === "invoiced" && !o.status.isInvoiced)
153
+ return false;
154
+ if (f.status === "open" && (o.status.isCancelled || o.status.isShipped))
155
+ return false;
156
+ return true;
157
+ });
158
+ }
159
+ export function ordersToCsv(orders) {
160
+ const header = [
161
+ "Ordernummer",
162
+ "Artikel",
163
+ "SKU",
164
+ "Bestelmoment",
165
+ "Aantal",
166
+ "Stuksprijs",
167
+ "Omzet",
168
+ "Klant",
169
+ "Postcode",
170
+ "Plaats",
171
+ "Levering",
172
+ "Status",
173
+ ];
174
+ const rows = orders.map((o) => [
175
+ o.orderNumber,
176
+ o.productName,
177
+ o.sku,
178
+ o.orderedAt,
179
+ `${o.quantity.multiplier}x${o.quantity.count}`,
180
+ o.quantity.unitPrice.toFixed(2),
181
+ o.revenue.toFixed(2),
182
+ o.customer.name,
183
+ o.customer.postalCode,
184
+ o.customer.city,
185
+ o.deliveryAt,
186
+ o.status.isCancelled
187
+ ? "Geannuleerd"
188
+ : o.status.isShipped
189
+ ? "Verzonden"
190
+ : o.status.isInvoiced
191
+ ? "Gefactureerd"
192
+ : "Open",
193
+ ]);
194
+ const escape = (v) => `"${String(v).replace(/"/g, '""')}"`;
195
+ const csv = [header, ...rows].map((r) => r.map(escape).join(";")).join("\n");
196
+ const filename = `verkooporders-${new Date().toISOString().slice(0, 10)}.csv`;
197
+ return { csv, filename };
198
+ }