@flowselections/floriday-verkoop-module 1.0.10 → 1.0.12

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 (37) hide show
  1. package/dist-lib/components/verkoop/AnalyticsCards.d.ts +1 -1
  2. package/dist-lib/components/verkoop/AnalyticsCards.d.ts.map +1 -1
  3. package/dist-lib/components/verkoop/AnalyticsCards.js +1 -7
  4. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
  5. package/dist-lib/components/verkoop/CreateOrderPage.js +217 -189
  6. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
  7. package/dist-lib/components/verkoop/SalesOrderDetailView.js +1 -1
  8. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  9. package/dist-lib/components/verkoop/SalesOrdersTable.js +19 -40
  10. package/dist-lib/components/verkoop/SearchableSelect.d.ts +19 -0
  11. package/dist-lib/components/verkoop/SearchableSelect.d.ts.map +1 -0
  12. package/dist-lib/components/verkoop/SearchableSelect.js +21 -0
  13. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1486 -35
  14. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  15. package/dist-lib/integrations/supabase/client.d.ts +1486 -35
  16. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  17. package/dist-lib/integrations/supabase/client.server.d.ts +1486 -35
  18. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  19. package/dist-lib/integrations/supabase/types.d.ts +1515 -34
  20. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  21. package/dist-lib/lib/create-order.functions.d.ts +75223 -0
  22. package/dist-lib/lib/create-order.functions.d.ts.map +1 -0
  23. package/dist-lib/lib/create-order.functions.js +188 -0
  24. package/dist-lib/lib/floriday-public.functions.d.ts +10157 -0
  25. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/floriday-writes.functions.d.ts +7255 -0
  27. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/salesorders-mapping.d.ts +4 -0
  29. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
  30. package/dist-lib/lib/salesorders-mapping.js +14 -3
  31. package/dist-lib/lib/salesorders.functions.d.ts +4614 -261
  32. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/tradeitems.functions.d.ts +1486 -35
  34. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
  35. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
  36. package/dist-lib/lib/useSalesOrdersQuery.js +166 -26
  37. package/package.json +1 -1
@@ -0,0 +1,188 @@
1
+ // Server functions voor de handmatige "Nieuwe bestelling" flow.
2
+ // Deelt DB-queries met de Mailbox-module zodat beide manieren van
3
+ // orderinvoer identieke lijsten, validaties en berekeningen gebruiken.
4
+ import { createServerFn } from "@tanstack/react-start";
5
+ import { z } from "zod";
6
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
7
+ // ─── Klanten ────────────────────────────────────────────────────────────────
8
+ export const listCustomersForCreateOrder = createServerFn({ method: "GET" })
9
+ .middleware([requireSupabaseAuth])
10
+ .handler(async ({ context }) => {
11
+ const { data } = await context.supabase
12
+ .from("customers")
13
+ .select("id, company_name, email")
14
+ .eq("is_active", true)
15
+ .order("company_name")
16
+ .range(0, 4999);
17
+ return { items: data ?? [] };
18
+ });
19
+ // ─── Producten (alleen met voorraad — zelfde filter als Mailbox) ────────────
20
+ export const listProductsForCreateOrder = createServerFn({ method: "GET" })
21
+ .middleware([requireSupabaseAuth])
22
+ .handler(async ({ context }) => {
23
+ const { data: stockRows } = await context.supabase
24
+ .from("trade_item_stock")
25
+ .select("trade_item_key, physical_stock, reserved_stock");
26
+ const availableKeys = new Set();
27
+ for (const s of Array.isArray(stockRows) ? stockRows : []) {
28
+ const avail = Number(s.physical_stock ?? 0) - Number(s.reserved_stock ?? 0);
29
+ if (avail > 0 && typeof s.trade_item_key === "string") {
30
+ availableKeys.add(s.trade_item_key);
31
+ }
32
+ }
33
+ if (availableKeys.size === 0)
34
+ return { items: [] };
35
+ const { data } = await context.supabase
36
+ .from("products")
37
+ .select("id, product, unit, sale_price, plants_per_crate, crates_per_layer, layers_per_cart, custom_fields")
38
+ .order("product")
39
+ .range(0, 4999);
40
+ const items = (Array.isArray(data) ? data : []).filter((p) => {
41
+ const fid = p?.custom_fields?.floriday_id;
42
+ return typeof fid === "string" && availableKeys.has(fid);
43
+ });
44
+ return { items };
45
+ });
46
+ // ─── Afleverlocaties van geselecteerde klant ────────────────────────────────
47
+ export const listDeliveryLocationsForCustomerCO = createServerFn({ method: "POST" })
48
+ .middleware([requireSupabaseAuth])
49
+ .inputValidator((d) => z.object({ customerId: z.string().uuid() }).parse(d))
50
+ .handler(async ({ data, context }) => {
51
+ if (!data.customerId)
52
+ return { items: [] };
53
+ const { data: rows } = await context.supabase
54
+ .from("customer_delivery_locations")
55
+ .select("id, name, address_line, street, house_number, postal_code, city, province, country, is_primary, carrier_id, carriers(id, name)")
56
+ .eq("customer_id", data.customerId)
57
+ .order("is_primary", { ascending: false })
58
+ .order("name");
59
+ return { items: Array.isArray(rows) ? rows : [] };
60
+ });
61
+ // ─── Afhaallocaties (eigen tuinen) — Floriday warehouses ────────────────────
62
+ export const listPickupLocationsCO = createServerFn({ method: "GET" })
63
+ .middleware([requireSupabaseAuth])
64
+ .handler(async ({ context }) => {
65
+ const { data } = await context.supabase
66
+ .from("floriday_warehouses_cache")
67
+ .select("floriday_id, name, data")
68
+ .eq("is_deleted", false);
69
+ const rows = Array.isArray(data) ? data : [];
70
+ const items = rows
71
+ .filter((r) => {
72
+ const d = r?.data ?? {};
73
+ return d?.hasAccess === true && d?.isExternal === false;
74
+ })
75
+ .map((r) => {
76
+ const addr = r?.data?.location?.address?.addressLine;
77
+ const city = r?.data?.location?.address?.city;
78
+ const nameRaw = typeof r.name === "string" ? r.name.trim() : "";
79
+ const label = nameRaw.length > 0
80
+ ? nameRaw
81
+ : [addr, city].filter(Boolean).join(", ") || "(naamloos)";
82
+ return { id: r.floriday_id, name: label };
83
+ })
84
+ .sort((a, b) => a.name.localeCompare(b.name, "nl"));
85
+ return { items };
86
+ });
87
+ // ─── Vervoerders ────────────────────────────────────────────────────────────
88
+ export const listCarriersForCreateOrder = createServerFn({ method: "GET" })
89
+ .middleware([requireSupabaseAuth])
90
+ .handler(async ({ context }) => {
91
+ const { data } = await context.supabase
92
+ .from("carriers")
93
+ .select("id, name, is_default, sort_order, delivery_days")
94
+ .order("sort_order", { ascending: true, nullsFirst: false })
95
+ .order("name");
96
+ return { items: data ?? [] };
97
+ });
98
+ // ─── Orderpicktijd (dagen) ──────────────────────────────────────────────────
99
+ export const getOrderPickDaysCO = createServerFn({ method: "GET" })
100
+ .middleware([requireSupabaseAuth])
101
+ .handler(async ({ context }) => {
102
+ const { data } = await context.supabase
103
+ .from("afhandeling_settings")
104
+ .select("pick_days_before_transport")
105
+ .order("updated_at", { ascending: false })
106
+ .limit(1)
107
+ .maybeSingle();
108
+ return { pickDays: Math.max(0, Number(data?.pick_days_before_transport ?? 0)) };
109
+ });
110
+ // ─── Handmatige bestelling aanmaken ────────────────────────────────────────
111
+ const LineInput = z.object({
112
+ product_id: z.string().uuid(),
113
+ product_name: z.string().min(1).max(255),
114
+ quantity: z.number().int().positive(), // aantal PLANTEN (== fusten * plants_per_crate)
115
+ unit: z.string().max(50).nullable().optional(),
116
+ unit_price: z.number().nonnegative().optional(),
117
+ });
118
+ const CreateManualOrderInput = z.object({
119
+ customerId: z.string().uuid(),
120
+ customerName: z.string().min(1).max(255),
121
+ deliveryLocationId: z.string().uuid(),
122
+ pickupLocationId: z.string().min(1),
123
+ carrierId: z.string().uuid(),
124
+ deliveryDate: z.string().min(1).max(50),
125
+ notes: z.string().max(2000).nullable().optional(),
126
+ lines: z.array(LineInput).min(1),
127
+ });
128
+ function addCalendarDays(base, n) {
129
+ const x = new Date(base);
130
+ x.setDate(x.getDate() + n);
131
+ return x;
132
+ }
133
+ function toISODate(d) {
134
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
135
+ }
136
+ export const createManualOrder = createServerFn({ method: "POST" })
137
+ .middleware([requireSupabaseAuth])
138
+ .inputValidator((input) => CreateManualOrderInput.parse(input))
139
+ .handler(async ({ data, context }) => {
140
+ // Validatie eerst mogelijke leverdatum — identiek aan approveProposal
141
+ const { data: c } = await context.supabase
142
+ .from("carriers").select("delivery_days").eq("id", data.carrierId).maybeSingle();
143
+ const carrierDeliveryDays = Math.max(0, Number(c?.delivery_days ?? 0));
144
+ const { data: settings } = await context.supabase
145
+ .from("afhandeling_settings")
146
+ .select("pick_days_before_transport")
147
+ .order("updated_at", { ascending: false })
148
+ .limit(1).maybeSingle();
149
+ const pickDays = Math.max(0, Number(settings?.pick_days_before_transport ?? 0));
150
+ const earliestISO = toISODate(addCalendarDays(new Date(), pickDays + carrierDeliveryDays));
151
+ if (data.deliveryDate < earliestISO) {
152
+ throw new Error(`Leverdatum (${data.deliveryDate}) ligt voor de eerst mogelijke datum (${earliestISO}) o.b.v. orderpicktijd (${pickDays}d) + levertijd vervoerder (${carrierDeliveryDays}d).`);
153
+ }
154
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
155
+ const total = data.lines.reduce((s, l) => s + (Number(l.unit_price ?? 0) * Number(l.quantity)), 0);
156
+ const orderNumber = `LOC-${Date.now()}`;
157
+ const { data: order, error: orderErr } = await supabaseAdmin.from("orders").insert({
158
+ order_number: orderNumber,
159
+ customer_name: data.customerName,
160
+ customer_id: data.customerId,
161
+ order_date: new Date().toISOString().slice(0, 10),
162
+ delivery_date: data.deliveryDate,
163
+ status: "nieuw",
164
+ total: String(total.toFixed(2)),
165
+ notes: data.notes ?? null,
166
+ custom_fields: {
167
+ source: "manual",
168
+ delivery_location: data.deliveryLocationId,
169
+ pickup_location: data.pickupLocationId,
170
+ carrier: data.carrierId,
171
+ },
172
+ }).select("id").single();
173
+ if (orderErr)
174
+ throw new Error(orderErr.message);
175
+ const items = data.lines.map((l) => ({
176
+ order_id: order.id,
177
+ product_id: l.product_id,
178
+ product_name: l.product_name,
179
+ quantity: Math.max(1, Math.round(l.quantity)),
180
+ unit: l.unit ?? "stuks",
181
+ unit_price: l.unit_price ?? undefined,
182
+ }));
183
+ const { error: itemsErr } = await supabaseAdmin.from("order_items").insert(items);
184
+ if (itemsErr)
185
+ throw new Error(itemsErr.message);
186
+ void context; // reserveren indien later audit-log nodig is
187
+ return { ok: true, orderId: order.id };
188
+ });