@burdenoff/microfe-movethewheels 2026.1017.18 → 2026.1026.1

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.
@@ -0,0 +1,9 @@
1
+ import { OrderItem, OrderPricing } from '../types';
2
+ /**
3
+ * Build the pricing payload used by delivery-job create/update forms.
4
+ * Item `value` is the per-unit declared value, so quantity is applied here.
5
+ * Delivery, handling, taxes, and insurance remain zero unless an existing
6
+ * supported value is being preserved during an edit; this form has no rate
7
+ * source and must not invent one.
8
+ */
9
+ export declare function calculateOrderPricing(items: OrderItem[], existing?: Partial<OrderPricing> | null): OrderPricing;
@@ -9,39 +9,40 @@ import { Users as te } from "../node_modules/lucide-react/dist/esm/icons/users.j
9
9
  import { buildCustomerCreatePath as ne, joinPath as o } from "../utils/navigation.js";
10
10
  import { useMoveTheWheels as re } from "../providers/MoveTheWheelsProvider.js";
11
11
  import { useRoute as ie, useRoutes as ae } from "../hooks/useRoutes.js";
12
- import { useOrder as s, useOrders as oe } from "../hooks/useOrders.js";
13
- import { useProducts as se } from "../hooks/useInventory.js";
12
+ import { useOrder as oe, useOrders as se } from "../hooks/useOrders.js";
13
+ import { useProducts as s } from "../hooks/useInventory.js";
14
14
  import { useCustomer as ce, useCustomers as le } from "../hooks/useCustomers.js";
15
15
  import { Badge as c, Button as l, Card as u, CardContent as d, CardDescription as f, CardHeader as p, CardTitle as m, Input as h, Label as g, Select as ue, SelectContent as de, SelectItem as _, SelectTrigger as fe, SelectValue as pe, Separator as me, Textarea as he } from "../components/ui.js";
16
16
  import { EmptyState as ge, PageLayout as _e } from "../components/PageLayout.js";
17
17
  import { FormError as ve } from "../components/FormError.js";
18
- import { EntityCombobox as ye } from "../components/EntityCombobox.js";
19
- import { CustomerPicker as be } from "../components/CustomerPicker.js";
20
- import { OrderItemRow as xe } from "../components/OrderItemRow.js";
21
- import { validateOrderItems as Se } from "../utils/orderValidation.js";
22
- import { useEffect as v, useState as y } from "react";
18
+ import { EntityCombobox as v } from "../components/EntityCombobox.js";
19
+ import { CustomerPicker as ye } from "../components/CustomerPicker.js";
20
+ import { OrderItemRow as be } from "../components/OrderItemRow.js";
21
+ import { validateOrderItems as xe } from "../utils/orderValidation.js";
22
+ import { calculateOrderPricing as Se } from "../utils/orderPricing.js";
23
+ import { useEffect as y, useState as b } from "react";
23
24
  import { useParams as Ce, useSearchParams as we } from "react-router";
24
- import { Fragment as Te, jsx as b, jsxs as x } from "react/jsx-runtime";
25
+ import { Fragment as Te, jsx as x, jsxs as S } from "react/jsx-runtime";
25
26
  import { EmphasisPanel as Ee, PagePurpose as De, Stepper as Oe } from "@burdenoff/fe-libs/ui";
26
- import { FormSection as S } from "@burdenoff/fe-libs/form";
27
+ import { FormSection as C } from "@burdenoff/fe-libs/form";
27
28
  //#region src/movethewheels/pages/CreateOrderPage.tsx
28
- function C(e) {
29
+ function w(e) {
29
30
  if (!e || Number.isNaN(e.getTime())) return "";
30
31
  let t = (e) => String(e).padStart(2, "0");
31
32
  return `${e.getFullYear()}-${t(e.getMonth() + 1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`;
32
33
  }
33
- function w() {
34
- let { orderId: w } = Ce(), [T] = we(), E = T.get("customerId")?.trim() || void 0, { basePath: D, navigate: O } = re(), { order: k } = s(w), { createOrder: ke, updateOrder: Ae } = oe(), [je, Me] = y(""), { customers: A, createCustomer: Ne, isLoading: j, isLoadingMore: Pe, error: M, refetch: Fe, loadMore: Ie, hasMore: Le, totalCustomers: Re } = le({
34
+ function T() {
35
+ let { orderId: T } = Ce(), [ke] = we(), E = ke.get("customerId")?.trim() || void 0, { basePath: D, navigate: O } = re(), { order: k } = oe(T), { createOrder: Ae, updateOrder: je } = se(), [Me, Ne] = b(""), { customers: A, createCustomer: Pe, isLoading: j, isLoadingMore: Fe, error: M, refetch: Ie, loadMore: Le, hasMore: Re, totalCustomers: ze } = le({
35
36
  limit: 20,
36
- filters: { search: je || void 0 }
37
- }), { customer: N } = ce(E ?? k?.customerId), [ze, Be] = y(""), { routes: P, isLoading: F, error: I, hasMore: L, totalRoutes: Ve, loadMore: He, isLoadingMore: Ue } = ae({
37
+ filters: { search: Me || void 0 }
38
+ }), { customer: N } = ce(E ?? k?.customerId), [Be, Ve] = b(""), { routes: P, isLoading: F, error: I, hasMore: He, totalRoutes: Ue, loadMore: L, isLoadingMore: We } = ae({
38
39
  status: "planned",
39
- search: ze || void 0,
40
+ search: Be || void 0,
40
41
  limit: 20
41
- }), [R, We] = y(""), { products: Ge, isLoading: Ke, isLoadingMore: qe, hasMore: Je, totalProducts: Ye, loadMore: Xe } = se({
42
+ }), [R, Ge] = b(""), { products: Ke, isLoading: qe, isLoadingMore: Je, hasMore: Ye, totalProducts: Xe, loadMore: Ze } = s({
42
43
  search: R || void 0,
43
44
  limit: 20
44
- }), z = !!w && !!k, [B, V] = y(null), [H, U] = y({
45
+ }), z = !!T && !!k, [B, V] = b(null), [H, U] = b({
45
46
  customerId: E || k?.customerId || "",
46
47
  priority: k?.priority || "normal",
47
48
  routeId: k?.routeId || "",
@@ -61,15 +62,15 @@ function w() {
61
62
  },
62
63
  items: k?.items || [],
63
64
  trackingNumber: k?.trackingNumber || "",
64
- scheduledDelivery: C(k?.scheduledDelivery),
65
+ scheduledDelivery: w(k?.scheduledDelivery),
65
66
  specialInstructions: k?.specialInstructions || ""
66
- }), { route: Ze, isLoading: Qe } = ie(H.routeId || void 0), [W, G] = y(!1), [$e, K] = y(null), [q, J] = y({});
67
- v(() => {
67
+ }), { route: Qe, isLoading: $e } = ie(H.routeId || void 0), [W, G] = b(!1), [et, K] = b(null), [q, J] = b({});
68
+ y(() => {
68
69
  N && (V((e) => e?.id === N.id ? e : N), U((e) => e.customerId ? e : {
69
70
  ...e,
70
71
  customerId: N.id
71
72
  }));
72
- }, [N]), v(() => {
73
+ }, [N]), y(() => {
73
74
  k && U({
74
75
  customerId: k.customerId || "",
75
76
  priority: k.priority || "normal",
@@ -90,7 +91,7 @@ function w() {
90
91
  },
91
92
  items: k.items || [],
92
93
  trackingNumber: k.trackingNumber || "",
93
- scheduledDelivery: C(k.scheduledDelivery),
94
+ scheduledDelivery: w(k.scheduledDelivery),
94
95
  specialInstructions: k.specialInstructions || ""
95
96
  });
96
97
  }, [k]);
@@ -102,16 +103,16 @@ function w() {
102
103
  }
103
104
  O(o(D, "orders"));
104
105
  }
105
- }, X = () => B ?? A.find((e) => e.id === H.customerId), et = () => {
106
+ }, X = () => B ?? A.find((e) => e.id === H.customerId), tt = () => {
106
107
  if (!H.customerId || !X()) return M ? "Customers could not be loaded. Retry or create a customer before continuing." : "Select a customer for this order.";
107
108
  if (!H.pickupAddress.street.trim()) return "Enter a pickup street address.";
108
109
  if (!H.deliveryAddress.street.trim()) return "Enter a delivery street address.";
109
110
  if (H.items.length === 0) return "Add at least one item to the order.";
110
- let e = Se(H.items);
111
+ let e = xe(H.items);
111
112
  return J(e), Object.keys(e).length > 0 ? "Fix the highlighted shipment item fields before continuing." : H.scheduledDelivery && Number.isNaN(Date.parse(H.scheduledDelivery)) ? "Enter a valid scheduled delivery date and time." : null;
112
- }, tt = async (e) => {
113
+ }, nt = async (e) => {
113
114
  e.preventDefault(), K(null);
114
- let t = et();
115
+ let t = tt();
115
116
  if (t) {
116
117
  K(t);
117
118
  return;
@@ -135,14 +136,15 @@ function w() {
135
136
  pickupAddress: H.pickupAddress,
136
137
  deliveryAddress: H.deliveryAddress,
137
138
  items: H.items,
139
+ pricing: Se(H.items, z ? k?.pricing : void 0),
138
140
  trackingNumber: H.trackingNumber.trim() || void 0,
139
141
  scheduledDelivery: H.scheduledDelivery ? new Date(H.scheduledDelivery) : null,
140
142
  routeId: H.routeId || void 0,
141
143
  specialInstructions: H.specialInstructions
142
144
  };
143
- if (z && w) await Ae(w, e), O && O(o(D, `orders/${w}`));
145
+ if (z && T) await je(T, e), O && O(o(D, `orders/${T}`));
144
146
  else {
145
- let t = await ke(e);
147
+ let t = await Ae(e);
146
148
  O ? O(o(D, `orders/${t.id}`)) : Y();
147
149
  }
148
150
  } catch (e) {
@@ -163,7 +165,7 @@ function w() {
163
165
  [t]: n
164
166
  }
165
167
  }));
166
- }, nt = (e) => {
168
+ }, rt = (e) => {
167
169
  if (!e) {
168
170
  Z("routeId", "");
169
171
  return;
@@ -202,7 +204,7 @@ function w() {
202
204
  sku: ""
203
205
  }]
204
206
  }));
205
- }, rt = (e) => {
207
+ }, it = (e) => {
206
208
  let t = H.items[e]?.id;
207
209
  t && J((e) => {
208
210
  let n = { ...e };
@@ -211,7 +213,7 @@ function w() {
211
213
  ...t,
212
214
  items: t.items.filter((t, n) => n !== e)
213
215
  }));
214
- }, it = (e, t, n) => {
216
+ }, at = (e, t, n) => {
215
217
  let r = H.items[e]?.id;
216
218
  r && q[r] && J((e) => {
217
219
  let t = { ...e };
@@ -223,35 +225,35 @@ function w() {
223
225
  [t]: n
224
226
  } : r)
225
227
  }));
226
- }, at = () => {
227
- O && O(o(D, ne(z && w ? `orders/${w}/edit` : "orders/new")));
228
- }, ot = H.customerId ? !H.pickupAddress.street || !H.deliveryAddress.street ? 1 : H.items.length === 0 ? 2 : 3 : 0;
229
- return /* @__PURE__ */ x(_e, {
228
+ }, ot = () => {
229
+ O && O(o(D, ne(z && T ? `orders/${T}/edit` : "orders/new")));
230
+ }, st = H.customerId ? !H.pickupAddress.street || !H.deliveryAddress.street ? 1 : H.items.length === 0 ? 2 : 3 : 0;
231
+ return /* @__PURE__ */ S(_e, {
230
232
  title: z ? "Edit Order" : "Create Order",
231
233
  subtitle: z ? `Editing ${k?.orderNumber}` : "Create a new delivery order",
232
- icon: /* @__PURE__ */ b(i, { size: 24 }),
234
+ icon: /* @__PURE__ */ x(i, { size: 24 }),
233
235
  maxWidth: "2xl",
234
- headerActions: /* @__PURE__ */ x("div", {
236
+ headerActions: /* @__PURE__ */ S("div", {
235
237
  className: "flex w-full flex-col-reverse gap-2 sm:w-auto sm:flex-row",
236
- children: [/* @__PURE__ */ x(l, {
238
+ children: [/* @__PURE__ */ S(l, {
237
239
  type: "button",
238
240
  variant: "outline",
239
241
  onClick: Y,
240
242
  disabled: W,
241
- children: [/* @__PURE__ */ b(e, { size: 16 }), "Cancel"]
242
- }), /* @__PURE__ */ x(l, {
243
+ children: [/* @__PURE__ */ x(e, { size: 16 }), "Cancel"]
244
+ }), /* @__PURE__ */ S(l, {
243
245
  type: "submit",
244
246
  form: "order-form",
245
247
  disabled: W || j,
246
- children: [/* @__PURE__ */ b(ee, { size: 16 }), W ? "Saving..." : z ? "Save Changes" : "Create Order"]
248
+ children: [/* @__PURE__ */ x(ee, { size: 16 }), W ? "Saving..." : z ? "Save Changes" : "Create Order"]
247
249
  })]
248
250
  }),
249
251
  children: [
250
- /* @__PURE__ */ b(De, {
251
- icon: /* @__PURE__ */ b(i, { size: 18 }),
252
+ /* @__PURE__ */ x(De, {
253
+ icon: /* @__PURE__ */ x(i, { size: 18 }),
252
254
  children: z ? "Update the customer, delivery plan, shipment contents, or handling details before saving." : "Set the customer and delivery plan, add what is being shipped, then choose any timing or handling preferences."
253
255
  }),
254
- /* @__PURE__ */ b(Oe, {
256
+ /* @__PURE__ */ x(Oe, {
255
257
  steps: [
256
258
  {
257
259
  key: "customer",
@@ -274,7 +276,7 @@ function w() {
274
276
  description: "Add optional delivery instructions."
275
277
  }
276
278
  ],
277
- active: ot,
279
+ active: st,
278
280
  onStepClick: (e) => {
279
281
  let t = document.getElementById([
280
282
  "customer",
@@ -293,41 +295,41 @@ function w() {
293
295
  },
294
296
  allowSkipAhead: !0
295
297
  }),
296
- /* @__PURE__ */ x("form", {
298
+ /* @__PURE__ */ S("form", {
297
299
  id: "order-form",
298
- onSubmit: tt,
300
+ onSubmit: nt,
299
301
  className: "space-y-6",
300
302
  children: [
301
- /* @__PURE__ */ b(ve, { message: $e }),
302
- /* @__PURE__ */ b("section", {
303
+ /* @__PURE__ */ x(ve, { message: et }),
304
+ /* @__PURE__ */ x("section", {
303
305
  "aria-labelledby": "order-details-title",
304
- children: /* @__PURE__ */ x(Ee, {
306
+ children: /* @__PURE__ */ S(Ee, {
305
307
  className: "space-y-5",
306
- children: [/* @__PURE__ */ x("div", {
308
+ children: [/* @__PURE__ */ S("div", {
307
309
  className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",
308
- children: [/* @__PURE__ */ x("div", {
310
+ children: [/* @__PURE__ */ S("div", {
309
311
  className: "flex items-start gap-3",
310
- children: [/* @__PURE__ */ b("div", {
312
+ children: [/* @__PURE__ */ x("div", {
311
313
  className: "rounded-xl bg-action-primary-bg/10 p-2 text-action-primary-bg",
312
- children: /* @__PURE__ */ b(te, {
314
+ children: /* @__PURE__ */ x(te, {
313
315
  size: 18,
314
316
  "aria-hidden": "true"
315
317
  })
316
- }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
318
+ }), /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("h2", {
317
319
  id: "order-details-title",
318
320
  className: "text-base font-semibold text-text-primary",
319
321
  children: "Customer & priority"
320
- }), /* @__PURE__ */ b("p", {
322
+ }), /* @__PURE__ */ x("p", {
321
323
  className: "mt-1 text-sm text-text-secondary",
322
324
  children: "Choose who this delivery is for and how urgently it should be handled."
323
325
  })] })]
324
- }), /* @__PURE__ */ b(c, {
326
+ }), /* @__PURE__ */ x(c, {
325
327
  variant: "outline",
326
328
  children: "Required"
327
329
  })]
328
- }), /* @__PURE__ */ x("div", {
330
+ }), /* @__PURE__ */ S("div", {
329
331
  className: "grid grid-cols-1 gap-4 md:grid-cols-2",
330
- children: [/* @__PURE__ */ b(be, {
332
+ children: [/* @__PURE__ */ x(ye, {
331
333
  customers: A,
332
334
  value: H.customerId,
333
335
  onValueChange: (e) => {
@@ -336,50 +338,50 @@ function w() {
336
338
  onCreated: (e) => {
337
339
  V(e), Z("customerId", e.id), K(null);
338
340
  },
339
- createCustomer: Ne,
341
+ createCustomer: Pe,
340
342
  isLoading: j,
341
343
  error: M,
342
- onRetry: Fe,
343
- onOpenCustomers: at,
344
+ onRetry: Ie,
345
+ onOpenCustomers: ot,
344
346
  selectedCustomer: B,
345
- onSearchQueryChange: Me,
346
- hasMore: Le,
347
- totalCount: Re,
348
- onLoadMore: Ie,
349
- isLoadingMore: Pe
350
- }), /* @__PURE__ */ x("div", {
347
+ onSearchQueryChange: Ne,
348
+ hasMore: Re,
349
+ totalCount: ze,
350
+ onLoadMore: Le,
351
+ isLoadingMore: Fe
352
+ }), /* @__PURE__ */ S("div", {
351
353
  className: "space-y-2",
352
354
  children: [
353
- /* @__PURE__ */ b(g, {
355
+ /* @__PURE__ */ x(g, {
354
356
  htmlFor: "priority",
355
357
  children: "Priority"
356
358
  }),
357
- /* @__PURE__ */ x(ue, {
359
+ /* @__PURE__ */ S(ue, {
358
360
  value: H.priority,
359
361
  onValueChange: (e) => Z("priority", e),
360
- children: [/* @__PURE__ */ b(fe, {
362
+ children: [/* @__PURE__ */ x(fe, {
361
363
  id: "priority",
362
- children: /* @__PURE__ */ b(pe, {})
363
- }), /* @__PURE__ */ x(de, { children: [
364
- /* @__PURE__ */ b(_, {
364
+ children: /* @__PURE__ */ x(pe, {})
365
+ }), /* @__PURE__ */ S(de, { children: [
366
+ /* @__PURE__ */ x(_, {
365
367
  value: "low",
366
368
  children: "Low"
367
369
  }),
368
- /* @__PURE__ */ b(_, {
370
+ /* @__PURE__ */ x(_, {
369
371
  value: "normal",
370
372
  children: "Normal"
371
373
  }),
372
- /* @__PURE__ */ b(_, {
374
+ /* @__PURE__ */ x(_, {
373
375
  value: "high",
374
376
  children: "High"
375
377
  }),
376
- /* @__PURE__ */ b(_, {
378
+ /* @__PURE__ */ x(_, {
377
379
  value: "urgent",
378
380
  children: "Urgent"
379
381
  })
380
382
  ] })]
381
383
  }),
382
- /* @__PURE__ */ b("p", {
384
+ /* @__PURE__ */ x("p", {
383
385
  className: "text-xs text-text-secondary",
384
386
  children: "Normal is selected by default."
385
387
  })
@@ -388,106 +390,106 @@ function w() {
388
390
  })]
389
391
  })
390
392
  }),
391
- /* @__PURE__ */ b("div", {
393
+ /* @__PURE__ */ x("div", {
392
394
  id: "locations-section",
393
- children: /* @__PURE__ */ x(S, {
394
- title: /* @__PURE__ */ x("span", {
395
+ children: /* @__PURE__ */ S(C, {
396
+ title: /* @__PURE__ */ S("span", {
395
397
  className: "flex items-center gap-2",
396
- children: [/* @__PURE__ */ b(r, {
398
+ children: [/* @__PURE__ */ x(r, {
397
399
  size: 18,
398
400
  "aria-hidden": "true"
399
401
  }), "Route & addresses"]
400
402
  }),
401
403
  description: "Choose an optional saved route, then review or enter the collection and destination details.",
402
404
  children: [
403
- /* @__PURE__ */ x("div", {
405
+ /* @__PURE__ */ S("div", {
404
406
  className: "rounded-card border border-border-subtle bg-bg-sunken/40 p-4",
405
407
  children: [
406
- /* @__PURE__ */ x("div", {
408
+ /* @__PURE__ */ S("div", {
407
409
  className: "mb-3 flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between",
408
- children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h3", {
410
+ children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("h3", {
409
411
  className: "text-sm font-semibold text-text-primary",
410
412
  children: "Use a saved route"
411
- }), /* @__PURE__ */ b("p", {
413
+ }), /* @__PURE__ */ x("p", {
412
414
  className: "mt-1 text-xs text-text-secondary",
413
415
  children: "Optional — fills only blank address fields; you can always edit them below."
414
- })] }), /* @__PURE__ */ b(c, {
416
+ })] }), /* @__PURE__ */ x(c, {
415
417
  variant: "secondary",
416
418
  children: "Optional"
417
419
  })]
418
420
  }),
419
- /* @__PURE__ */ b(ye, {
421
+ /* @__PURE__ */ x(v, {
420
422
  id: "saved-route",
421
423
  value: H.routeId,
422
- onChange: nt,
424
+ onChange: rt,
423
425
  options: P,
424
426
  loading: F,
425
427
  getId: (e) => e.id,
426
428
  getLabel: (e) => `${e.name} · ${e.stops.length} stops`,
427
- renderOption: (e) => /* @__PURE__ */ x(Te, { children: [
429
+ renderOption: (e) => /* @__PURE__ */ S(Te, { children: [
428
430
  e.name,
429
431
  " · ",
430
432
  e.stops.length,
431
433
  " stops"
432
434
  ] }),
433
- selectedOption: Ze,
434
- selectedLoading: Qe,
435
+ selectedOption: Qe,
436
+ selectedLoading: $e,
435
437
  placeholder: "Choose a saved route",
436
438
  searchPlaceholder: "Search saved routes",
437
439
  emptyText: "No saved routes found.",
438
440
  noneLabel: "No saved route",
439
441
  error: I ? "Saved routes are unavailable" : void 0,
440
- onSearchChange: Be,
441
- hasMore: L,
442
- totalCount: Ve,
443
- onLoadMore: He,
444
- loadingMore: Ue
442
+ onSearchChange: Ve,
443
+ hasMore: He,
444
+ totalCount: Ue,
445
+ onLoadMore: L,
446
+ loadingMore: We
445
447
  }),
446
- /* @__PURE__ */ b("p", {
448
+ /* @__PURE__ */ x("p", {
447
449
  className: "mt-2 text-xs text-text-secondary",
448
450
  role: "status",
449
451
  children: F ? "Loading planned routes…" : I ? "Saved routes are unavailable; enter pickup and delivery addresses manually." : "Manual values always remain authoritative when a route is selected."
450
452
  })
451
453
  ]
452
454
  }),
453
- /* @__PURE__ */ b(me, {}),
454
- /* @__PURE__ */ x("div", {
455
+ /* @__PURE__ */ x(me, {}),
456
+ /* @__PURE__ */ S("div", {
455
457
  className: "grid grid-cols-1 gap-4 lg:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] lg:items-stretch",
456
458
  children: [
457
- /* @__PURE__ */ x(u, {
459
+ /* @__PURE__ */ S(u, {
458
460
  className: "border-border-seam shadow-none",
459
- children: [/* @__PURE__ */ x(p, { children: [/* @__PURE__ */ x(m, {
461
+ children: [/* @__PURE__ */ S(p, { children: [/* @__PURE__ */ S(m, {
460
462
  className: "flex items-center gap-2 text-base",
461
463
  children: [
462
- /* @__PURE__ */ b(r, {
464
+ /* @__PURE__ */ x(r, {
463
465
  size: 17,
464
466
  className: "text-text-secondary",
465
467
  "aria-hidden": "true"
466
468
  }),
467
469
  "Pickup address ",
468
- /* @__PURE__ */ b("span", {
470
+ /* @__PURE__ */ x("span", {
469
471
  className: "text-status-error-text",
470
472
  children: "*"
471
473
  })
472
474
  ]
473
- }), /* @__PURE__ */ b(f, { children: "Where the shipment is collected." })] }), /* @__PURE__ */ b(d, {
475
+ }), /* @__PURE__ */ x(f, { children: "Where the shipment is collected." })] }), /* @__PURE__ */ x(d, {
474
476
  className: "space-y-4",
475
- children: /* @__PURE__ */ x("fieldset", {
477
+ children: /* @__PURE__ */ S("fieldset", {
476
478
  className: "space-y-4",
477
479
  children: [
478
- /* @__PURE__ */ b("legend", {
480
+ /* @__PURE__ */ x("legend", {
479
481
  className: "sr-only",
480
482
  children: "Pickup address details"
481
483
  }),
482
- /* @__PURE__ */ x("div", {
484
+ /* @__PURE__ */ S("div", {
483
485
  className: "space-y-2",
484
- children: [/* @__PURE__ */ x(g, {
486
+ children: [/* @__PURE__ */ S(g, {
485
487
  htmlFor: "pickupStreet",
486
- children: ["Street address ", /* @__PURE__ */ b("span", {
488
+ children: ["Street address ", /* @__PURE__ */ x("span", {
487
489
  className: "text-status-error-text",
488
490
  children: "*"
489
491
  })]
490
- }), /* @__PURE__ */ b(h, {
492
+ }), /* @__PURE__ */ x(h, {
491
493
  id: "pickupStreet",
492
494
  value: H.pickupAddress.street,
493
495
  onChange: (e) => Q("pickupAddress", "street", e.target.value),
@@ -495,25 +497,25 @@ function w() {
495
497
  "aria-required": "true"
496
498
  })]
497
499
  }),
498
- /* @__PURE__ */ x("div", {
500
+ /* @__PURE__ */ S("div", {
499
501
  className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
500
- children: [/* @__PURE__ */ x("div", {
502
+ children: [/* @__PURE__ */ S("div", {
501
503
  className: "space-y-2",
502
- children: [/* @__PURE__ */ b(g, {
504
+ children: [/* @__PURE__ */ x(g, {
503
505
  htmlFor: "pickupCity",
504
506
  children: "City"
505
- }), /* @__PURE__ */ b(h, {
507
+ }), /* @__PURE__ */ x(h, {
506
508
  id: "pickupCity",
507
509
  value: H.pickupAddress.city,
508
510
  onChange: (e) => Q("pickupAddress", "city", e.target.value),
509
511
  placeholder: "San Francisco"
510
512
  })]
511
- }), /* @__PURE__ */ x("div", {
513
+ }), /* @__PURE__ */ S("div", {
512
514
  className: "space-y-2",
513
- children: [/* @__PURE__ */ b(g, {
515
+ children: [/* @__PURE__ */ x(g, {
514
516
  htmlFor: "pickupState",
515
517
  children: "State"
516
- }), /* @__PURE__ */ b(h, {
518
+ }), /* @__PURE__ */ x(h, {
517
519
  id: "pickupState",
518
520
  value: H.pickupAddress.state,
519
521
  onChange: (e) => Q("pickupAddress", "state", e.target.value),
@@ -521,25 +523,25 @@ function w() {
521
523
  })]
522
524
  })]
523
525
  }),
524
- /* @__PURE__ */ x("div", {
526
+ /* @__PURE__ */ S("div", {
525
527
  className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
526
- children: [/* @__PURE__ */ x("div", {
528
+ children: [/* @__PURE__ */ S("div", {
527
529
  className: "space-y-2",
528
- children: [/* @__PURE__ */ b(g, {
530
+ children: [/* @__PURE__ */ x(g, {
529
531
  htmlFor: "pickupPostal",
530
532
  children: "Postal Code"
531
- }), /* @__PURE__ */ b(h, {
533
+ }), /* @__PURE__ */ x(h, {
532
534
  id: "pickupPostal",
533
535
  value: H.pickupAddress.postalCode,
534
536
  onChange: (e) => Q("pickupAddress", "postalCode", e.target.value),
535
537
  placeholder: "94103"
536
538
  })]
537
- }), /* @__PURE__ */ x("div", {
539
+ }), /* @__PURE__ */ S("div", {
538
540
  className: "space-y-2",
539
- children: [/* @__PURE__ */ b(g, {
541
+ children: [/* @__PURE__ */ x(g, {
540
542
  htmlFor: "pickupCountry",
541
543
  children: "Country"
542
- }), /* @__PURE__ */ b(h, {
544
+ }), /* @__PURE__ */ x(h, {
543
545
  id: "pickupCountry",
544
546
  value: H.pickupAddress.country,
545
547
  onChange: (e) => Q("pickupAddress", "country", e.target.value),
@@ -551,48 +553,48 @@ function w() {
551
553
  })
552
554
  })]
553
555
  }),
554
- /* @__PURE__ */ b("div", {
556
+ /* @__PURE__ */ x("div", {
555
557
  className: "hidden items-center justify-center lg:flex",
556
558
  "aria-hidden": "true",
557
- children: /* @__PURE__ */ b(t, {
559
+ children: /* @__PURE__ */ x(t, {
558
560
  size: 18,
559
561
  className: "text-text-muted"
560
562
  })
561
563
  }),
562
- /* @__PURE__ */ x(u, {
564
+ /* @__PURE__ */ S(u, {
563
565
  className: "border-border-seam shadow-none",
564
- children: [/* @__PURE__ */ x(p, { children: [/* @__PURE__ */ x(m, {
566
+ children: [/* @__PURE__ */ S(p, { children: [/* @__PURE__ */ S(m, {
565
567
  className: "flex items-center gap-2 text-base",
566
568
  children: [
567
- /* @__PURE__ */ b(r, {
569
+ /* @__PURE__ */ x(r, {
568
570
  size: 17,
569
571
  className: "text-text-secondary",
570
572
  "aria-hidden": "true"
571
573
  }),
572
574
  "Delivery address ",
573
- /* @__PURE__ */ b("span", {
575
+ /* @__PURE__ */ x("span", {
574
576
  className: "text-status-error-text",
575
577
  children: "*"
576
578
  })
577
579
  ]
578
- }), /* @__PURE__ */ b(f, { children: "Where the shipment is delivered." })] }), /* @__PURE__ */ b(d, {
580
+ }), /* @__PURE__ */ x(f, { children: "Where the shipment is delivered." })] }), /* @__PURE__ */ x(d, {
579
581
  className: "space-y-4",
580
- children: /* @__PURE__ */ x("fieldset", {
582
+ children: /* @__PURE__ */ S("fieldset", {
581
583
  className: "space-y-4",
582
584
  children: [
583
- /* @__PURE__ */ b("legend", {
585
+ /* @__PURE__ */ x("legend", {
584
586
  className: "sr-only",
585
587
  children: "Delivery address details"
586
588
  }),
587
- /* @__PURE__ */ x("div", {
589
+ /* @__PURE__ */ S("div", {
588
590
  className: "space-y-2",
589
- children: [/* @__PURE__ */ x(g, {
591
+ children: [/* @__PURE__ */ S(g, {
590
592
  htmlFor: "deliveryStreet",
591
- children: ["Street address ", /* @__PURE__ */ b("span", {
593
+ children: ["Street address ", /* @__PURE__ */ x("span", {
592
594
  className: "text-status-error-text",
593
595
  children: "*"
594
596
  })]
595
- }), /* @__PURE__ */ b(h, {
597
+ }), /* @__PURE__ */ x(h, {
596
598
  id: "deliveryStreet",
597
599
  value: H.deliveryAddress.street,
598
600
  onChange: (e) => Q("deliveryAddress", "street", e.target.value),
@@ -600,25 +602,25 @@ function w() {
600
602
  "aria-required": "true"
601
603
  })]
602
604
  }),
603
- /* @__PURE__ */ x("div", {
605
+ /* @__PURE__ */ S("div", {
604
606
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
605
- children: [/* @__PURE__ */ x("div", {
607
+ children: [/* @__PURE__ */ S("div", {
606
608
  className: "space-y-2",
607
- children: [/* @__PURE__ */ b(g, {
609
+ children: [/* @__PURE__ */ x(g, {
608
610
  htmlFor: "deliveryCity",
609
611
  children: "City"
610
- }), /* @__PURE__ */ b(h, {
612
+ }), /* @__PURE__ */ x(h, {
611
613
  id: "deliveryCity",
612
614
  value: H.deliveryAddress.city,
613
615
  onChange: (e) => Q("deliveryAddress", "city", e.target.value),
614
616
  placeholder: "Los Angeles"
615
617
  })]
616
- }), /* @__PURE__ */ x("div", {
618
+ }), /* @__PURE__ */ S("div", {
617
619
  className: "space-y-2",
618
- children: [/* @__PURE__ */ b(g, {
620
+ children: [/* @__PURE__ */ x(g, {
619
621
  htmlFor: "deliveryState",
620
622
  children: "State"
621
- }), /* @__PURE__ */ b(h, {
623
+ }), /* @__PURE__ */ x(h, {
622
624
  id: "deliveryState",
623
625
  value: H.deliveryAddress.state,
624
626
  onChange: (e) => Q("deliveryAddress", "state", e.target.value),
@@ -626,25 +628,25 @@ function w() {
626
628
  })]
627
629
  })]
628
630
  }),
629
- /* @__PURE__ */ x("div", {
631
+ /* @__PURE__ */ S("div", {
630
632
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
631
- children: [/* @__PURE__ */ x("div", {
633
+ children: [/* @__PURE__ */ S("div", {
632
634
  className: "space-y-2",
633
- children: [/* @__PURE__ */ b(g, {
635
+ children: [/* @__PURE__ */ x(g, {
634
636
  htmlFor: "deliveryPostal",
635
637
  children: "Postal code"
636
- }), /* @__PURE__ */ b(h, {
638
+ }), /* @__PURE__ */ x(h, {
637
639
  id: "deliveryPostal",
638
640
  value: H.deliveryAddress.postalCode,
639
641
  onChange: (e) => Q("deliveryAddress", "postalCode", e.target.value),
640
642
  placeholder: "90210"
641
643
  })]
642
- }), /* @__PURE__ */ x("div", {
644
+ }), /* @__PURE__ */ S("div", {
643
645
  className: "space-y-2",
644
- children: [/* @__PURE__ */ b(g, {
646
+ children: [/* @__PURE__ */ x(g, {
645
647
  htmlFor: "deliveryCountry",
646
648
  children: "Country"
647
- }), /* @__PURE__ */ b(h, {
649
+ }), /* @__PURE__ */ x(h, {
648
650
  id: "deliveryCountry",
649
651
  value: H.deliveryAddress.country,
650
652
  onChange: (e) => Q("deliveryAddress", "country", e.target.value),
@@ -661,141 +663,141 @@ function w() {
661
663
  ]
662
664
  })
663
665
  }),
664
- /* @__PURE__ */ b("div", {
666
+ /* @__PURE__ */ x("div", {
665
667
  id: "items-section",
666
- children: /* @__PURE__ */ b(S, {
667
- title: /* @__PURE__ */ x("span", {
668
+ children: /* @__PURE__ */ x(C, {
669
+ title: /* @__PURE__ */ S("span", {
668
670
  className: "flex items-center gap-2",
669
671
  children: [
670
- /* @__PURE__ */ b(i, {
672
+ /* @__PURE__ */ x(i, {
671
673
  size: 18,
672
674
  "aria-hidden": "true"
673
675
  }),
674
676
  "Shipment contents ",
675
- /* @__PURE__ */ b(c, {
677
+ /* @__PURE__ */ x(c, {
676
678
  variant: "secondary",
677
679
  children: H.items.length
678
680
  })
679
681
  ]
680
682
  }),
681
683
  description: "Add each item being shipped. Use the catalog to prefill details or enter a one-off item manually.",
682
- actions: /* @__PURE__ */ x(l, {
684
+ actions: /* @__PURE__ */ S(l, {
683
685
  type: "button",
684
686
  variant: "outline",
685
687
  size: "sm",
686
688
  onClick: $,
687
- children: [/* @__PURE__ */ b(a, { size: 16 }), "Add item"]
689
+ children: [/* @__PURE__ */ x(a, { size: 16 }), "Add item"]
688
690
  }),
689
- children: H.items.length === 0 ? /* @__PURE__ */ b(ge, {
691
+ children: H.items.length === 0 ? /* @__PURE__ */ x(ge, {
690
692
  illustration: "empty-files",
691
693
  title: "No items added yet",
692
694
  description: "Add the products this order will ship — each with a name, SKU, quantity and value.",
693
- action: /* @__PURE__ */ x(l, {
695
+ action: /* @__PURE__ */ S(l, {
694
696
  type: "button",
695
697
  variant: "outline",
696
698
  onClick: $,
697
- children: [/* @__PURE__ */ b(a, {
699
+ children: [/* @__PURE__ */ x(a, {
698
700
  size: 16,
699
701
  className: "mr-1.5"
700
702
  }), "Add your first item"]
701
703
  })
702
- }) : /* @__PURE__ */ x("div", {
704
+ }) : /* @__PURE__ */ S("div", {
703
705
  className: "space-y-4",
704
- children: [/* @__PURE__ */ x("div", {
706
+ children: [/* @__PURE__ */ S("div", {
705
707
  className: "rounded-card border border-border-subtle bg-bg-sunken/40 p-4",
706
708
  children: [
707
- /* @__PURE__ */ b(g, {
709
+ /* @__PURE__ */ x(g, {
708
710
  htmlFor: "item-product-search",
709
711
  children: "Search product catalog"
710
712
  }),
711
- /* @__PURE__ */ b(h, {
713
+ /* @__PURE__ */ x(h, {
712
714
  id: "item-product-search",
713
715
  value: R,
714
- onChange: (e) => We(e.target.value),
716
+ onChange: (e) => Ge(e.target.value),
715
717
  placeholder: "Search by name or SKU",
716
718
  className: "mt-2"
717
719
  }),
718
- /* @__PURE__ */ b("p", {
720
+ /* @__PURE__ */ x("p", {
719
721
  className: "mt-2 text-xs text-text-secondary",
720
722
  children: "This filters the catalog picker for every item below. You can still edit any autofilled value or enter item details manually."
721
723
  })
722
724
  ]
723
- }), /* @__PURE__ */ b("ol", {
725
+ }), /* @__PURE__ */ x("ol", {
724
726
  className: "space-y-4",
725
727
  "aria-label": "Shipment items",
726
- children: H.items.map((e, t) => /* @__PURE__ */ b("li", { children: /* @__PURE__ */ b(xe, {
728
+ children: H.items.map((e, t) => /* @__PURE__ */ x("li", { children: /* @__PURE__ */ x(be, {
727
729
  item: e,
728
730
  index: t,
729
- products: Ge,
730
- productsLoading: Ke,
731
- productsLoadingMore: qe,
732
- productsHasMore: Je,
733
- productsTotalCount: Ye,
734
- onLoadMoreProducts: Xe,
731
+ products: Ke,
732
+ productsLoading: qe,
733
+ productsLoadingMore: Je,
734
+ productsHasMore: Ye,
735
+ productsTotalCount: Xe,
736
+ onLoadMoreProducts: Ze,
735
737
  errors: q[e.id],
736
- onUpdate: it,
737
- onRemove: rt
738
+ onUpdate: at,
739
+ onRemove: it
738
740
  }) }, e.id))
739
741
  })]
740
742
  })
741
743
  })
742
744
  }),
743
- /* @__PURE__ */ b(S, {
744
- title: /* @__PURE__ */ x("span", {
745
+ /* @__PURE__ */ x(C, {
746
+ title: /* @__PURE__ */ S("span", {
745
747
  className: "flex items-center gap-2",
746
- children: [/* @__PURE__ */ b(n, {
748
+ children: [/* @__PURE__ */ x(n, {
747
749
  size: 18,
748
750
  "aria-hidden": "true"
749
751
  }), "Timing & handling"]
750
752
  }),
751
753
  description: "Optional delivery timing and instructions for the carrier.",
752
- children: /* @__PURE__ */ x("div", {
754
+ children: /* @__PURE__ */ S("div", {
753
755
  className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
754
756
  children: [
755
- /* @__PURE__ */ x("div", {
757
+ /* @__PURE__ */ S("div", {
756
758
  className: "space-y-2",
757
759
  children: [
758
- /* @__PURE__ */ b(g, {
760
+ /* @__PURE__ */ x(g, {
759
761
  htmlFor: "trackingNumber",
760
762
  children: "Tracking / reference number"
761
763
  }),
762
- /* @__PURE__ */ b(h, {
764
+ /* @__PURE__ */ x(h, {
763
765
  id: "trackingNumber",
764
766
  value: H.trackingNumber,
765
767
  onChange: (e) => Z("trackingNumber", e.target.value),
766
768
  placeholder: "Optional carrier reference"
767
769
  }),
768
- /* @__PURE__ */ b("p", {
770
+ /* @__PURE__ */ x("p", {
769
771
  className: "text-xs text-text-secondary",
770
772
  children: "Use this when a carrier or customer already supplied a tracking reference."
771
773
  })
772
774
  ]
773
775
  }),
774
- /* @__PURE__ */ x("div", {
776
+ /* @__PURE__ */ S("div", {
775
777
  className: "space-y-2",
776
778
  children: [
777
- /* @__PURE__ */ b(g, {
779
+ /* @__PURE__ */ x(g, {
778
780
  htmlFor: "scheduledDelivery",
779
781
  children: "Scheduled delivery"
780
782
  }),
781
- /* @__PURE__ */ b(h, {
783
+ /* @__PURE__ */ x(h, {
782
784
  id: "scheduledDelivery",
783
785
  type: "datetime-local",
784
786
  value: H.scheduledDelivery,
785
787
  onChange: (e) => Z("scheduledDelivery", e.target.value)
786
788
  }),
787
- /* @__PURE__ */ b("p", {
789
+ /* @__PURE__ */ x("p", {
788
790
  className: "text-xs text-text-secondary",
789
791
  children: "Leave blank to dispatch as soon as a driver is available."
790
792
  })
791
793
  ]
792
794
  }),
793
- /* @__PURE__ */ x("div", {
795
+ /* @__PURE__ */ S("div", {
794
796
  className: "space-y-2 lg:col-span-1",
795
- children: [/* @__PURE__ */ b(g, {
797
+ children: [/* @__PURE__ */ x(g, {
796
798
  htmlFor: "specialInstructions",
797
799
  children: "Special instructions"
798
- }), /* @__PURE__ */ b(he, {
800
+ }), /* @__PURE__ */ x(he, {
799
801
  id: "specialInstructions",
800
802
  value: H.specialInstructions,
801
803
  onChange: (e) => Z("specialInstructions", e.target.value),
@@ -812,4 +814,4 @@ function w() {
812
814
  });
813
815
  }
814
816
  //#endregion
815
- export { w as default };
817
+ export { T as default };
@@ -0,0 +1,20 @@
1
+ //#region src/movethewheels/utils/orderPricing.ts
2
+ var e = (e) => Math.round(e * 100) / 100, t = (e) => typeof e == "number" && Number.isFinite(e) && e >= 0 ? e : 0;
3
+ function n(n, r) {
4
+ let i = e(n.reduce((e, n) => e + t(n.quantity) * t(n.value), 0)), a = t(r?.taxes), o = t(r?.fees?.delivery), s = t(r?.fees?.handling), c = t(r?.fees?.insurance);
5
+ return {
6
+ subtotal: i,
7
+ taxes: a,
8
+ fees: {
9
+ delivery: o,
10
+ handling: s,
11
+ ...c > 0 ? { insurance: c } : {}
12
+ },
13
+ total: e(i + a + o + s + c),
14
+ currency: r?.currency || "USD",
15
+ paymentMethod: r?.paymentMethod || "unknown",
16
+ paymentStatus: r?.paymentStatus || "pending"
17
+ };
18
+ }
19
+ //#endregion
20
+ export { n as calculateOrderPricing };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-movethewheels",
3
- "version": "2026.1017.18",
3
+ "version": "2026.1026.1",
4
4
  "description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -49,6 +49,7 @@ import { EntityCombobox } from '../components/EntityCombobox';
49
49
  import { OrderItemRow } from '../components/OrderItemRow';
50
50
  import type { Customer } from '../types';
51
51
  import { validateOrderItems, type OrderItemFieldErrors } from '../utils/orderValidation';
52
+ import { calculateOrderPricing } from '../utils/orderPricing';
52
53
 
53
54
  /**
54
55
  * `<input type="datetime-local">` needs a `YYYY-MM-DDTHH:mm` string in LOCAL
@@ -272,6 +273,7 @@ export default function CreateOrderPage() {
272
273
  pickupAddress: formData.pickupAddress,
273
274
  deliveryAddress: formData.deliveryAddress,
274
275
  items: formData.items,
276
+ pricing: calculateOrderPricing(formData.items, isEditing ? order?.pricing : undefined),
275
277
  // `null` clears an existing slot on edit; the service field is nullable.
276
278
  trackingNumber: formData.trackingNumber.trim() || undefined,
277
279
  scheduledDelivery: formData.scheduledDelivery ? new Date(formData.scheduledDelivery) : null,
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { calculateOrderPricing } from './orderPricing';
3
+ import type { OrderItem } from '../types';
4
+
5
+ const item = (quantity: number, value: number): OrderItem => ({
6
+ id: `${quantity}-${value}`,
7
+ productId: '',
8
+ name: 'Declared cargo',
9
+ quantity,
10
+ weight: 1,
11
+ dimensions: { length: 1, width: 1, height: 1, unit: 'cm' },
12
+ value,
13
+ sku: 'CARGO-1',
14
+ });
15
+
16
+ describe('calculateOrderPricing', () => {
17
+ it('calculates subtotal as quantity multiplied by each item value', () => {
18
+ expect(calculateOrderPricing([item(2, 12.5), item(3, 0.1)])).toEqual({
19
+ subtotal: 25.3,
20
+ taxes: 0,
21
+ fees: { delivery: 0, handling: 0 },
22
+ total: 25.3,
23
+ currency: 'USD',
24
+ paymentMethod: 'unknown',
25
+ paymentStatus: 'pending',
26
+ });
27
+ });
28
+
29
+ it('preserves existing supported charges during edit without inventing rates', () => {
30
+ expect(
31
+ calculateOrderPricing([item(2, 10)], {
32
+ taxes: 1.25,
33
+ fees: { delivery: 4, handling: 2, insurance: 0.5 },
34
+ currency: 'EUR',
35
+ paymentMethod: 'invoice',
36
+ paymentStatus: 'paid',
37
+ })
38
+ ).toEqual({
39
+ subtotal: 20,
40
+ taxes: 1.25,
41
+ fees: { delivery: 4, handling: 2, insurance: 0.5 },
42
+ total: 27.75,
43
+ currency: 'EUR',
44
+ paymentMethod: 'invoice',
45
+ paymentStatus: 'paid',
46
+ });
47
+ });
48
+ });
@@ -0,0 +1,40 @@
1
+ import type { OrderItem, OrderPricing } from '../types';
2
+
3
+ const roundCurrency = (amount: number): number => Math.round(amount * 100) / 100;
4
+
5
+ const nonNegativeFinite = (value: unknown): number =>
6
+ typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
7
+
8
+ /**
9
+ * Build the pricing payload used by delivery-job create/update forms.
10
+ * Item `value` is the per-unit declared value, so quantity is applied here.
11
+ * Delivery, handling, taxes, and insurance remain zero unless an existing
12
+ * supported value is being preserved during an edit; this form has no rate
13
+ * source and must not invent one.
14
+ */
15
+ export function calculateOrderPricing(
16
+ items: OrderItem[],
17
+ existing?: Partial<OrderPricing> | null
18
+ ): OrderPricing {
19
+ const subtotal = roundCurrency(
20
+ items.reduce(
21
+ (sum, item) => sum + nonNegativeFinite(item.quantity) * nonNegativeFinite(item.value),
22
+ 0
23
+ )
24
+ );
25
+ const taxes = nonNegativeFinite(existing?.taxes);
26
+ const delivery = nonNegativeFinite(existing?.fees?.delivery);
27
+ const handling = nonNegativeFinite(existing?.fees?.handling);
28
+ const insurance = nonNegativeFinite(existing?.fees?.insurance);
29
+ const fees = { delivery, handling, ...(insurance > 0 ? { insurance } : {}) };
30
+
31
+ return {
32
+ subtotal,
33
+ taxes,
34
+ fees,
35
+ total: roundCurrency(subtotal + taxes + delivery + handling + insurance),
36
+ currency: existing?.currency || 'USD',
37
+ paymentMethod: existing?.paymentMethod || 'unknown',
38
+ paymentStatus: existing?.paymentStatus || 'pending',
39
+ };
40
+ }