@flowselections/mailbox-orders 1.0.39 → 1.0.42

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.
@@ -14,7 +14,7 @@ import { EmailPreview } from "./EmailPreview.js";
14
14
  import { TablePagination } from "./TablePagination.js";
15
15
  import { MailboxFilterBar, usePersistedState, normalizeText, matchesPeriod } from "./MailboxFilterBar.js";
16
16
  import { supabase } from "../integrations/supabase/client.js";
17
- import { canOverrideLateSignup } from "../lib/verkoop-order.js";
17
+ import { canOverrideLateSignup, checkLinePrices } from "../lib/verkoop-order.js";
18
18
  const beladingKey = (b) => `${b.plants_per_crate}x${b.crates_per_layer}x${b.layers_per_cart}|${b.crate_code ?? ""}|${b.carrier_code ?? ""}`;
19
19
  const beladingLabel = (b) => `${b.plants_per_crate} planten/fust · ${b.crates_per_layer} fusten/laag · ${b.layers_per_cart} lagen`;
20
20
  // Extra regel onder het label: fusten/kar + fusttype + kartype
@@ -657,6 +657,10 @@ function ProposalEditor({ proposal, template, customers, products, allowBackorde
657
657
  useEffect(() => { setRefusal(null); setLateOpen(false); setLateReason(""); }, [deliveryVal, carrierVal, deliveryLocationVal]);
658
658
  const orderTotal = lines.reduce((sum, l) => sum + lineTotal(l.quantity ?? null, l.unit_price ?? null), 0);
659
659
  const linesWithoutPrice = lines.filter((l) => l.product_id && (l.unit_price === null || l.unit_price === undefined)).length;
660
+ const zeroPriceLines = lines.filter((l) => l.product_id && l.unit_price === 0).length;
661
+ const [zeroConfirmed, setZeroConfirmed] = useState(false);
662
+ useEffect(() => { if (zeroPriceLines === 0)
663
+ setZeroConfirmed(false); }, [zeroPriceLines]);
660
664
  const euro = (n) => n.toLocaleString("nl-NL", { style: "currency", currency: "EUR" });
661
665
  const [confirmOpen, setConfirmOpen] = useState(false);
662
666
  const [submitting, setSubmitting] = useState(false);
@@ -707,8 +711,9 @@ function ProposalEditor({ proposal, template, customers, products, allowBackorde
707
711
  return null;
708
712
  }
709
713
  }
710
- if (validLines.some((l) => l.unit_price === null || l.unit_price === undefined || l.unit_price <= 0)) {
711
- toast.error("Vul bij elke regel een prijs per stuk in; Verkoop maakt geen order zonder prijs.");
714
+ const priceCheck = checkLinePrices(validLines.map((l) => l.unit_price), zeroConfirmed);
715
+ if (!priceCheck.ok) {
716
+ toast.error(priceCheck.message);
712
717
  return null;
713
718
  }
714
719
  const cust = customers.find((c) => c.id === customerVal);
@@ -736,6 +741,7 @@ function ProposalEditor({ proposal, template, customers, products, allowBackorde
736
741
  customerReference: typeof values["reference"] === "string" ? values["reference"].trim() || null : null,
737
742
  momentKey: preview?.chosen?.momentKey ?? null,
738
743
  lateSignup: lateOverride ? { reason: lateReason.trim() } : null,
744
+ zeroPriceConfirmed: zeroConfirmed && priceCheck.zeroLines > 0,
739
745
  };
740
746
  };
741
747
  // Stap 1: controleren → bevestigingsdialoog. Pas na bevestigen wordt de
@@ -941,7 +947,7 @@ function ProposalEditor({ proposal, template, customers, products, allowBackorde
941
947
  next[idx] = { ...next[idx], needs_sticker: e.target.checked };
942
948
  setLines(next);
943
949
  } }), "Sticker"] })), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => setLines(lines.filter((_, i) => i !== idx)), children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }, idx));
944
- }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setLines([...lines, { product_id: null, raw_product_text: "", quantity: 1, unit: "stuks", belading: null }]), children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Regel toevoegen"] }), _jsxs("div", { className: "flex justify-between border-t pt-2 text-sm", children: [_jsx("span", { className: "text-muted-foreground", children: "Totaal" }), _jsx("span", { className: "font-medium", children: euro(orderTotal) })] }), linesWithoutPrice > 0 && (_jsxs("div", { className: "rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800", children: ["Geen prijs bekend voor ", linesWithoutPrice, " regel(s). Vul een prijs in; zonder prijs kan de bestelling niet worden aangemaakt."] }))] }), refusal && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm space-y-2", children: [_jsx("div", { className: "font-medium text-destructive", children: "Bestelling niet aangemaakt \u2014 het voorstel blijft open." }), _jsx("div", { className: "text-destructive", children: refusal.message }), _jsx("div", { className: "flex flex-wrap gap-2", children: refusal.nextDeliveryDate && refusal.nextDeliveryDate !== deliveryVal && (_jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVal("delivery_date", refusal.nextDeliveryDate), children: ["Volgend haalbaar moment: ", fmtDate(refusal.nextDeliveryDate)] })) }), refusal.kind === "late_signup" && (_jsx(LateSignupChoice, { open: lateOpen, setOpen: setLateOpen, reason: lateReason, setReason: setLateReason }))] })), _jsxs("div", { className: "flex gap-2 pt-2", children: [_jsxs(Button, { className: "flex-1", onClick: handleApprove, children: [_jsx(Check, { className: "h-4 w-4 mr-1" }), " Controleren & bevestigen"] }), _jsxs(Button, { variant: "destructive", onClick: () => onReject(notesVal || ""), children: [_jsx(X, { className: "h-4 w-4 mr-1" }), " Afwijzen"] })] }), _jsx(Dialog, { open: confirmOpen, onOpenChange: (o) => {
950
+ }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setLines([...lines, { product_id: null, raw_product_text: "", quantity: 1, unit: "stuks", belading: null }]), children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Regel toevoegen"] }), _jsxs("div", { className: "flex justify-between border-t pt-2 text-sm", children: [_jsx("span", { className: "text-muted-foreground", children: "Totaal" }), _jsx("span", { className: "font-medium", children: euro(orderTotal) })] }), linesWithoutPrice > 0 && (_jsxs("div", { className: "rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800", children: ["Geen prijs bekend voor ", linesWithoutPrice, " regel(s). Vul een prijs in; zonder prijs kan de bestelling niet worden aangemaakt."] })), zeroPriceLines > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-xs text-destructive space-y-1", children: [_jsxs("div", { children: [zeroPriceLines, " regel(s) met prijs \u20AC 0. Vul de prijs in, of bevestig dat \u20AC 0 klopt."] }), _jsxs("label", { className: "flex items-center gap-2 text-foreground", children: [_jsx("input", { type: "checkbox", className: "h-4 w-4 accent-primary", checked: zeroConfirmed, onChange: (e) => setZeroConfirmed(e.target.checked) }), _jsx("span", { children: "Prijs \u20AC 0 klopt" }), "Prijs \u20AC 0 klopt"] })] }))] }), refusal && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm space-y-2", children: [_jsx("div", { className: "font-medium text-destructive", children: "Bestelling niet aangemaakt \u2014 het voorstel blijft open." }), _jsx("div", { className: "text-destructive", children: refusal.message }), _jsx("div", { className: "flex flex-wrap gap-2", children: refusal.nextDeliveryDate && refusal.nextDeliveryDate !== deliveryVal && (_jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVal("delivery_date", refusal.nextDeliveryDate), children: ["Volgend haalbaar moment: ", fmtDate(refusal.nextDeliveryDate)] })) }), refusal.kind === "late_signup" && (_jsx(LateSignupChoice, { open: lateOpen, setOpen: setLateOpen, reason: lateReason, setReason: setLateReason }))] })), _jsxs("div", { className: "flex gap-2 pt-2", children: [_jsxs(Button, { className: "flex-1", onClick: handleApprove, children: [_jsx(Check, { className: "h-4 w-4 mr-1" }), " Controleren & bevestigen"] }), _jsxs(Button, { variant: "destructive", onClick: () => onReject(notesVal || ""), children: [_jsx(X, { className: "h-4 w-4 mr-1" }), " Afwijzen"] })] }), _jsx(Dialog, { open: confirmOpen, onOpenChange: (o) => {
945
951
  if (submitting)
946
952
  return;
947
953
  setConfirmOpen(o);