@argusoft/medplat-product-management 1.0.0 → 1.0.2

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 (77) hide show
  1. package/dist/BrandsPage-CSA3meke.js +385 -0
  2. package/dist/BrandsPage-CSA3meke.js.map +1 -0
  3. package/dist/CategoriesPage-BrspzkJL.js +340 -0
  4. package/dist/CategoriesPage-BrspzkJL.js.map +1 -0
  5. package/dist/{DashboardPage-BNKUQj46.js → DashboardPage-CqaquOs2.js} +21 -22
  6. package/dist/{DashboardPage-BNKUQj46.js.map → DashboardPage-CqaquOs2.js.map} +1 -1
  7. package/dist/{FacilityPicker-Coi8I3yE.js → FacilityPicker-Dv_H95WW.js} +13 -13
  8. package/dist/{FacilityPicker-Coi8I3yE.js.map → FacilityPicker-Dv_H95WW.js.map} +1 -1
  9. package/dist/{InventoryPage-BR6jOzg2.js → InventoryPage-CPbzfDMR.js} +12 -12
  10. package/dist/{InventoryPage-BR6jOzg2.js.map → InventoryPage-CPbzfDMR.js.map} +1 -1
  11. package/dist/{MemberProfileCard-DbHZCS5n.js → MemberProfileCard-0vjO3Gzd.js} +2 -2
  12. package/dist/{MemberProfileCard-DbHZCS5n.js.map → MemberProfileCard-0vjO3Gzd.js.map} +1 -1
  13. package/dist/{PaymentCreatePage-Ci3ERnby.js → PaymentCreatePage-BYmDZAIB.js} +9 -10
  14. package/dist/{PaymentCreatePage-Ci3ERnby.js.map → PaymentCreatePage-BYmDZAIB.js.map} +1 -1
  15. package/dist/{PaymentsPage-sdg902zX.js → PaymentsPage-BXPvKon-.js} +16 -16
  16. package/dist/{PaymentsPage-sdg902zX.js.map → PaymentsPage-BXPvKon-.js.map} +1 -1
  17. package/dist/PickupCreatePage-CMGwoAXj.js +426 -0
  18. package/dist/PickupCreatePage-CMGwoAXj.js.map +1 -0
  19. package/dist/{PickupsPage-Ci2Ll0Kn.js → PickupsPage-D66_Wta4.js} +84 -39
  20. package/dist/PickupsPage-D66_Wta4.js.map +1 -0
  21. package/dist/PmTable-B9970kPY.js +375 -0
  22. package/dist/PmTable-B9970kPY.js.map +1 -0
  23. package/dist/{ProductEditPage-DL8gy85Y.js → ProductEditPage-B8qFTCW9.js} +226 -243
  24. package/dist/ProductEditPage-B8qFTCW9.js.map +1 -0
  25. package/dist/ProductsPage-C7_r9cKn.js +254 -0
  26. package/dist/ProductsPage-C7_r9cKn.js.map +1 -0
  27. package/dist/{PurchaseCreatePage-C3KMfBxJ.js → PurchaseCreatePage-CDfNDcKG.js} +150 -149
  28. package/dist/PurchaseCreatePage-CDfNDcKG.js.map +1 -0
  29. package/dist/{PurchaseRequestDetailPage-C0GYFJDl.js → PurchaseRequestDetailPage-C7aAwEqw.js} +15 -15
  30. package/dist/{PurchaseRequestDetailPage-C0GYFJDl.js.map → PurchaseRequestDetailPage-C7aAwEqw.js.map} +1 -1
  31. package/dist/{PurchaseRequestsPage-DvFH_tWe.js → PurchaseRequestsPage-BwCkbpaX.js} +14 -14
  32. package/dist/{PurchaseRequestsPage-DvFH_tWe.js.map → PurchaseRequestsPage-BwCkbpaX.js.map} +1 -1
  33. package/dist/{PurchasesPage-BCvLvSp4.js → PurchasesPage-CvaZouVy.js} +11 -12
  34. package/dist/{PurchasesPage-BCvLvSp4.js.map → PurchasesPage-CvaZouVy.js.map} +1 -1
  35. package/dist/{SaleCreatePage-DpkQMoTq.js → SaleCreatePage-DP1Qmltu.js} +30 -31
  36. package/dist/{SaleCreatePage-DpkQMoTq.js.map → SaleCreatePage-DP1Qmltu.js.map} +1 -1
  37. package/dist/{SalesPage-DUwgkEO-.js → SalesPage-CO3cdYX5.js} +18 -19
  38. package/dist/{SalesPage-DUwgkEO-.js.map → SalesPage-CO3cdYX5.js.map} +1 -1
  39. package/dist/{SettingsPage-CusC0jqJ.js → SettingsPage-DvVRwSFw.js} +104 -103
  40. package/dist/SettingsPage-DvVRwSFw.js.map +1 -0
  41. package/dist/StorageUnitsPage-B9Hr_RkW.js +470 -0
  42. package/dist/StorageUnitsPage-B9Hr_RkW.js.map +1 -0
  43. package/dist/{TransactionsPage-DDaK4VyY.js → TransactionsPage-HKIGwEZN.js} +127 -123
  44. package/dist/TransactionsPage-HKIGwEZN.js.map +1 -0
  45. package/dist/{TransferCreatePage-BxudLfhS.js → TransferCreatePage-DMuZqQ3c.js} +9 -9
  46. package/dist/{TransferCreatePage-BxudLfhS.js.map → TransferCreatePage-DMuZqQ3c.js.map} +1 -1
  47. package/dist/{TransfersPage-zgmt7teZ.js → TransfersPage-COIyouL4.js} +16 -16
  48. package/dist/{TransfersPage-zgmt7teZ.js.map → TransfersPage-COIyouL4.js.map} +1 -1
  49. package/dist/{VialsPage-CjqPfgpV.js → VialsPage-BNPUwGCU.js} +291 -224
  50. package/dist/VialsPage-BNPUwGCU.js.map +1 -0
  51. package/dist/{constants-BrA_hfAI.js → constants-5ZCZo-Zm.js} +5 -2
  52. package/dist/{constants-BrA_hfAI.js.map → constants-5ZCZo-Zm.js.map} +1 -1
  53. package/dist/{PmGuard-CjJBisaN.js → currency-ClRLIEg2.js} +186 -9
  54. package/dist/currency-ClRLIEg2.js.map +1 -0
  55. package/dist/index.js +1 -1
  56. package/package.json +1 -1
  57. package/dist/BrandsPage-DCMCVvDY.js +0 -334
  58. package/dist/BrandsPage-DCMCVvDY.js.map +0 -1
  59. package/dist/CategoriesPage-BpRhwfrc.js +0 -284
  60. package/dist/CategoriesPage-BpRhwfrc.js.map +0 -1
  61. package/dist/PickupCreatePage-CieMsgWV.js +0 -310
  62. package/dist/PickupCreatePage-CieMsgWV.js.map +0 -1
  63. package/dist/PickupsPage-Ci2Ll0Kn.js.map +0 -1
  64. package/dist/PmGuard-CjJBisaN.js.map +0 -1
  65. package/dist/PmTable-BuXZQZCU.js +0 -302
  66. package/dist/PmTable-BuXZQZCU.js.map +0 -1
  67. package/dist/ProductEditPage-DL8gy85Y.js.map +0 -1
  68. package/dist/ProductsPage-BANTKxLb.js +0 -221
  69. package/dist/ProductsPage-BANTKxLb.js.map +0 -1
  70. package/dist/PurchaseCreatePage-C3KMfBxJ.js.map +0 -1
  71. package/dist/SettingsPage-CusC0jqJ.js.map +0 -1
  72. package/dist/StorageUnitsPage-BlC2AgrJ.js +0 -469
  73. package/dist/StorageUnitsPage-BlC2AgrJ.js.map +0 -1
  74. package/dist/TransactionsPage-DDaK4VyY.js.map +0 -1
  75. package/dist/VialsPage-CjqPfgpV.js.map +0 -1
  76. package/dist/currency-BDDCDgeE.js +0 -169
  77. package/dist/currency-BDDCDgeE.js.map +0 -1
@@ -1,6 +1,5 @@
1
- import { W as e, a as t, c as n, i as r, n as i, r as a, t as ee } from "./PmGuard-CjJBisaN.js";
2
- import { t as te } from "./currency-BDDCDgeE.js";
3
- import { a as o, i as s, o as c, r as l, t as u } from "./PmTable-BuXZQZCU.js";
1
+ import { G as e, a as t, i as n, l as r, n as i, o as a, r as ee, t as te } from "./currency-ClRLIEg2.js";
2
+ import { a as o, c as s, i as c, s as l, t as u } from "./PmTable-B9970kPY.js";
4
3
  import { t as d } from "./format-UOyOu75i.js";
5
4
  import { useMemo as f, useState as p } from "react";
6
5
  import { jsx as m, jsxs as h } from "react/jsx-runtime";
@@ -15,7 +14,7 @@ import se from "@mui/icons-material/SearchRounded";
15
14
  //#region src/views/PurchasesPage.jsx
16
15
  var ce = 3;
17
16
  function j() {
18
- let j = r().has("update.manage.productManagement.purchase"), M = n(), N = te(M?.currencyCode), P = M?.managePurchaseAmounts !== !1, F = ae(), I = A(), [L, R] = p([]), [z, B] = p(!0), [V, H] = p(""), [U, W] = p(""), [G, K] = p(""), [q, J] = p("");
17
+ let j = t().has("update.manage.productManagement.purchase"), M = r(), N = te(M?.currencyCode), P = M?.managePurchaseAmounts !== !1, F = ae(), I = A(), [L, R] = p([]), [z, B] = p(!0), [V, H] = p(""), [U, W] = p(""), [G, K] = p(""), [q, J] = p("");
19
18
  async function Y() {
20
19
  B(!0), H("");
21
20
  try {
@@ -30,7 +29,7 @@ function j() {
30
29
  B(!1);
31
30
  }
32
31
  }
33
- a("view.manage.productManagement.purchase", () => {
32
+ n("view.manage.productManagement.purchase", () => {
34
33
  Y();
35
34
  }, []);
36
35
  let X = f(() => {
@@ -61,9 +60,9 @@ function j() {
61
60
  W(""), K(""), J("");
62
61
  }
63
62
  let $ = P ? 7 : 5;
64
- return /* @__PURE__ */ h(g, { children: [/* @__PURE__ */ m(i, {}), /* @__PURE__ */ m(ee, {
63
+ return /* @__PURE__ */ h(g, { children: [/* @__PURE__ */ m(ee, {}), /* @__PURE__ */ m(i, {
65
64
  perm: "view.manage.productManagement.purchase",
66
- children: /* @__PURE__ */ h(t, {
65
+ children: /* @__PURE__ */ h(a, {
67
66
  title: "Completed Purchases",
68
67
  subtitle: "Completed purchases and approved requests. Each shows the items and cost.",
69
68
  actions: /* @__PURE__ */ h(x, {
@@ -85,7 +84,7 @@ function j() {
85
84
  severity: "error",
86
85
  sx: { mb: 2 },
87
86
  children: V
88
- }) : null, /* @__PURE__ */ h(c, {
87
+ }) : null, /* @__PURE__ */ h(s, {
89
88
  eyebrow: "Procurement",
90
89
  title: "Recent purchases",
91
90
  subtitle: z ? "Loading…" : P ? `${X.length} ${X.length === 1 ? "purchase" : "purchases"} · total spend ${N}${le.toFixed(2)}` : `${X.length} ${X.length === 1 ? "purchase" : "purchases"}`,
@@ -134,7 +133,7 @@ function j() {
134
133
  children: "Clear"
135
134
  })
136
135
  ]
137
- }), /* @__PURE__ */ h(o, {
136
+ }), /* @__PURE__ */ h(l, {
138
137
  dense: !0,
139
138
  children: [/* @__PURE__ */ m(w, { children: /* @__PURE__ */ h(T, { children: [
140
139
  /* @__PURE__ */ m(C, { children: "Ref" }),
@@ -169,7 +168,7 @@ function j() {
169
168
  return /* @__PURE__ */ h(T, {
170
169
  hover: !0,
171
170
  children: [
172
- /* @__PURE__ */ m(C, { children: /* @__PURE__ */ m(s, { id: e.serialId }) }),
171
+ /* @__PURE__ */ m(C, { children: /* @__PURE__ */ m(o, { id: e.serialId }) }),
173
172
  /* @__PURE__ */ m(C, { children: /* @__PURE__ */ m(D, {
174
173
  variant: "body2",
175
174
  sx: { fontWeight: 700 },
@@ -261,7 +260,7 @@ function j() {
261
260
  }) }) : null,
262
261
  P ? /* @__PURE__ */ m(C, {
263
262
  align: "right",
264
- children: /* @__PURE__ */ m(l, { value: e.totalAmount })
263
+ children: /* @__PURE__ */ m(c, { value: e.totalAmount })
265
264
  }) : null,
266
265
  /* @__PURE__ */ m(C, {
267
266
  sx: { maxWidth: 320 },
@@ -292,4 +291,4 @@ function j() {
292
291
  //#endregion
293
292
  export { j as default };
294
293
 
295
- //# sourceMappingURL=PurchasesPage-BCvLvSp4.js.map
294
+ //# sourceMappingURL=PurchasesPage-CvaZouVy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PurchasesPage-BCvLvSp4.js","names":[],"sources":["../src/views/PurchasesPage.jsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Alert,\n Box,\n Button,\n Chip,\n InputAdornment,\n Skeleton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport { alpha, useTheme } from '@mui/material/styles';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';\nimport SearchRoundedIcon from '@mui/icons-material/SearchRounded';\nimport ShoppingCartRoundedIcon from '@mui/icons-material/ShoppingCartRounded';\nimport { showToast } from '@/common/toaster/toaster';\nimport PmPageShell from '@pm/components/PmPageShell';\nimport PmNav from '@pm/components/PmNav';\nimport PmGuard from '@pm/components/PmGuard';\nimport { usePermissionedEffect, usePmPermissions } from '@pm/utils/permissions';\nimport {\n PmEmptyRow,\n PmMoney,\n PmRef,\n PmTable,\n PmTableCard,\n} from '@pm/components/PmTable';\nimport { listPurchases } from '@pm/services/pmApi';\nimport { formatDate } from '@pm/utils/format';\nimport { currencySymbol } from '@pm/utils/currency';\nimport { usePmSettings } from '@pm/utils/settingsStore';\n\n// Compact one-liner of items+qty for the table. We render up to MAX_ITEMS_INLINE\n// pills inline; anything beyond gets a \"+N more\" pill so the row height stays\n// stable regardless of how many lines a purchase has.\nconst MAX_ITEMS_INLINE = 3;\n\nexport default function PurchasesPage() {\n const perms = usePmPermissions();\n const canCreate = perms.has('update.manage.productManagement.purchase');\n const settings = usePmSettings();\n const cur = currencySymbol(settings?.currencyCode);\n const manageAmounts = settings?.managePurchaseAmounts !== false;\n const navigate = useNavigate();\n const theme = useTheme();\n const [items, setItems] = useState([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState('');\n const [search, setSearch] = useState('');\n const [fromDate, setFromDate] = useState('');\n const [toDate, setToDate] = useState('');\n\n async function load() {\n setLoading(true);\n setError('');\n try {\n setItems(await listPurchases(0, 50));\n } catch (err) {\n const message = err?.response?.data?.message ?? err?.message ?? 'Failed to load purchases';\n setError(message);\n showToast({ message, type: 'error' });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect('view.manage.productManagement.purchase', () => {\n void load();\n }, []);\n\n // Search hits notes + every batch's productName + lotNumber. Date filters\n // bracket purchaseDate inclusively on either side; either bound is optional.\n const filteredItems = useMemo(() => {\n const q = search.trim().toLowerCase();\n const from = fromDate ? new Date(fromDate) : null;\n const to = toDate ? new Date(toDate) : null;\n return items.filter((p) => {\n if (q) {\n const hayItems = (p.batches ?? [])\n .map((b) => `${b.productName ?? ''} ${b.lotNumber ?? ''}`)\n .join(' ');\n const hay = `${p.notes ?? ''} ${hayItems}`.toLowerCase();\n if (!hay.includes(q)) return false;\n }\n if (from || to) {\n if (!p.purchaseDate) return false;\n const d = new Date(p.purchaseDate);\n if (from && d < from) return false;\n if (to) {\n // Date inputs give midnight; include the entire `to` day.\n const toEnd = new Date(to);\n toEnd.setHours(23, 59, 59, 999);\n if (d > toEnd) return false;\n }\n }\n return true;\n });\n }, [items, search, fromDate, toDate]);\n\n const totalSpend = useMemo(\n () => filteredItems.reduce((s, x) => s + Number(x.totalAmount || 0), 0),\n [filteredItems],\n );\n\n const hasFilter = search.trim() !== '' || fromDate !== '' || toDate !== '';\n\n function clearFilters() {\n setSearch('');\n setFromDate('');\n setToDate('');\n }\n\n // Match the table column count when manageAmounts swaps Payment + Total in\n // and out. Owner + Items columns are always present.\n const columnCount = manageAmounts ? 7 : 5;\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={'view.manage.productManagement.purchase'}>\n <PmPageShell\n title=\"Completed Purchases\"\n subtitle=\"Completed purchases and approved requests. Each shows the items and cost.\"\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\" startIcon={<RefreshRoundedIcon />} onClick={() => void load()}>Refresh</Button>\n {canCreate ? (\n <Button variant=\"contained\" startIcon={<AddRoundedIcon />} onClick={() => navigate('/ui/medplat/manage/pm/purchases/new')}>\n New Purchase\n </Button>\n ) : null}\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n <PmTableCard\n eyebrow=\"Procurement\"\n title=\"Recent purchases\"\n subtitle={loading\n ? 'Loading…'\n : manageAmounts\n ? `${filteredItems.length} ${filteredItems.length === 1 ? 'purchase' : 'purchases'} · total spend ${cur}${totalSpend.toFixed(2)}`\n : `${filteredItems.length} ${filteredItems.length === 1 ? 'purchase' : 'purchases'}`}\n >\n <Stack direction={{ xs: 'column', md: 'row' }} spacing={1.5} sx={{ mb: 2 }}>\n <TextField\n size=\"small\"\n fullWidth\n placeholder=\"Search notes or items…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchRoundedIcon fontSize=\"small\" />\n </InputAdornment>\n ),\n }}\n />\n <TextField\n size=\"small\"\n type=\"date\"\n label=\"From\"\n InputLabelProps={{ shrink: true }}\n value={fromDate}\n onChange={(e) => setFromDate(e.target.value)}\n sx={{ minWidth: 160 }}\n />\n <TextField\n size=\"small\"\n type=\"date\"\n label=\"To\"\n InputLabelProps={{ shrink: true }}\n value={toDate}\n onChange={(e) => setToDate(e.target.value)}\n sx={{ minWidth: 160 }}\n />\n <Button\n variant=\"text\"\n onClick={clearFilters}\n disabled={!hasFilter}\n sx={{ alignSelf: { md: 'center' } }}\n >\n Clear\n </Button>\n </Stack>\n\n <PmTable dense>\n <TableHead>\n <TableRow>\n <TableCell>Ref</TableCell>\n <TableCell>Date</TableCell>\n <TableCell>Owner</TableCell>\n <TableCell>Items</TableCell>\n {manageAmounts ? <TableCell>Payment</TableCell> : null}\n {manageAmounts ? <TableCell align=\"right\">Total</TableCell> : null}\n <TableCell>Notes</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n Array.from({ length: 5 }).map((_, i) => (\n <TableRow key={i}>\n {Array.from({ length: columnCount }).map((__, j) => (\n <TableCell key={j}><Skeleton height={22} /></TableCell>\n ))}\n </TableRow>\n ))\n ) : filteredItems.length === 0 ? (\n <PmEmptyRow\n colSpan={columnCount}\n icon={ShoppingCartRoundedIcon}\n title={hasFilter ? 'No purchases match your filters' : 'No purchases yet'}\n hint={hasFilter\n ? 'Adjust your search or filters.'\n : 'Record a purchase to add items to your stock.'}\n action={hasFilter\n ? <Button variant=\"text\" size=\"small\" onClick={clearFilters}>Clear filters</Button>\n : !canCreate ? null : (\n <Button variant=\"contained\" size=\"small\" startIcon={<AddRoundedIcon />} onClick={() => navigate('/ui/medplat/manage/pm/purchases/new')}>\n Record first purchase\n </Button>\n )}\n />\n ) : filteredItems.map((p) => {\n const batches = p.batches ?? [];\n const visibleBatches = batches.slice(0, MAX_ITEMS_INLINE);\n const overflow = batches.length - visibleBatches.length;\n return (\n <TableRow key={p.id} hover>\n <TableCell><PmRef id={p.serialId} /></TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>{formatDate(p.purchaseDate)}</Typography>\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>\n {p.ownerName || (p.ownerType === 'USER' ? 'Personal' : 'Facility')}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {p.ownerType}\n </Typography>\n </TableCell>\n <TableCell sx={{ maxWidth: 360 }}>\n {batches.length === 0 ? (\n <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>\n ) : (\n <Stack direction=\"row\" spacing={0.5} flexWrap=\"wrap\" useFlexGap>\n {visibleBatches.map((b) => (\n <Chip\n key={b.id}\n size=\"small\"\n label={\n <Box component=\"span\" sx={{ display: 'inline-flex', alignItems: 'baseline', gap: 0.5 }}>\n <Box component=\"span\" sx={{ fontWeight: 700 }}>{b.productName ?? '—'}</Box>\n <Box component=\"span\" sx={{ fontWeight: 700, fontVariantNumeric: 'tabular-nums', color: 'text.secondary' }}>\n ×{b.qtyPurchased ?? 0}\n </Box>\n </Box>\n }\n sx={{\n height: 22,\n borderRadius: 1.5,\n bgcolor: alpha(theme.palette.primary.main, 0.06),\n color: 'text.primary',\n border: `1px solid ${alpha(theme.palette.primary.main, 0.16)}`,\n '& .MuiChip-label': { px: 0.85 },\n }}\n />\n ))}\n {overflow > 0 ? (\n <Chip\n size=\"small\"\n label={`+${overflow} more`}\n sx={{\n height: 22,\n borderRadius: 1.5,\n bgcolor: 'transparent',\n color: 'text.secondary',\n border: `1px dashed ${theme.palette.divider}`,\n '& .MuiChip-label': { px: 0.85, fontWeight: 700 },\n }}\n />\n ) : null}\n </Stack>\n )}\n </TableCell>\n {manageAmounts ? (\n <TableCell>\n {p.paymentMode ? (\n <Chip\n size=\"small\"\n label={p.paymentMode}\n sx={{\n fontWeight: 700, borderRadius: 1.5, height: 22,\n bgcolor: alpha(theme.palette.primary.main, 0.08),\n color: 'primary.dark',\n border: `1px solid ${alpha(theme.palette.primary.main, 0.18)}`,\n }}\n />\n ) : <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>}\n </TableCell>\n ) : null}\n {manageAmounts ? (\n <TableCell align=\"right\"><PmMoney value={p.totalAmount} /></TableCell>\n ) : null}\n <TableCell sx={{ maxWidth: 320 }}>\n {p.notes ? (\n <Typography variant=\"caption\" color=\"text.secondary\"\n sx={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>\n {p.notes}\n </Typography>\n ) : <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA0CA,IAAM,KAAmB;AAEzB,SAAwB,IAAgB;CAEtC,IAAM,IADQ,EACI,EAAM,IAAI,0CAA0C,GAChE,IAAW,EAAc,GACzB,IAAM,GAAe,GAAU,YAAY,GAC3C,IAAgB,GAAU,0BAA0B,IACpD,IAAW,GAAY,GACvB,IAAQ,EAAS,GACjB,CAAC,GAAO,KAAY,EAAS,CAAC,CAAC,GAC/B,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,CAAC,GAAU,KAAe,EAAS,EAAE,GACrC,CAAC,GAAQ,KAAa,EAAS,EAAE;CAEvC,eAAe,IAAO;EAEpB,AADA,EAAW,EAAI,GACf,EAAS,EAAE;EACX,IAAI;GACF,EAAS,MAAM,EAAc,GAAG,EAAE,CAAC;EACrC,SAAS,GAAK;GACZ,IAAM,IAAU,GAAK,UAAU,MAAM,WAAW,GAAK,WAAW;GAEhE,AADA,EAAS,CAAO,GAChB,GAAU;IAAE;IAAS,MAAM;GAAQ,CAAC;EACtC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,EAAsB,gDAAgD;EACpE,EAAU;CACZ,GAAG,CAAC,CAAC;CAIL,IAAM,IAAgB,QAAc;EAClC,IAAM,IAAI,EAAO,KAAK,EAAE,YAAY,GAC9B,IAAO,IAAW,IAAI,KAAK,CAAQ,IAAI,MACvC,IAAK,IAAS,IAAI,KAAK,CAAM,IAAI;EACvC,OAAO,EAAM,QAAQ,MAAM;GACzB,IAAI,GAAG;IACL,IAAM,KAAY,EAAE,WAAW,CAAC,GAC7B,KAAK,MAAM,GAAG,EAAE,eAAe,GAAG,GAAG,EAAE,aAAa,IAAI,EACxD,KAAK,GAAG;IAEX,IAAI,CADQ,GAAG,EAAE,SAAS,GAAG,GAAG,IAAW,YACtC,EAAI,SAAS,CAAC,GAAG,OAAO;GAC/B;GACA,IAAI,KAAQ,GAAI;IACd,IAAI,CAAC,EAAE,cAAc,OAAO;IAC5B,IAAM,IAAI,IAAI,KAAK,EAAE,YAAY;IACjC,IAAI,KAAQ,IAAI,GAAM,OAAO;IAC7B,IAAI,GAAI;KAEN,IAAM,IAAQ,IAAI,KAAK,CAAE;KAEzB,IADA,EAAM,SAAS,IAAI,IAAI,IAAI,GAAG,GAC1B,IAAI,GAAO,OAAO;IACxB;GACF;GACA,OAAO;EACT,CAAC;CACH,GAAG;EAAC;EAAO;EAAQ;EAAU;CAAM,CAAC,GAE9B,KAAa,QACX,EAAc,QAAQ,GAAG,MAAM,IAAI,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,GACtE,CAAC,CAAa,CAChB,GAEM,IAAY,EAAO,KAAK,MAAM,MAAM,MAAa,MAAM,MAAW;CAExE,SAAS,IAAe;EAGtB,AAFA,EAAU,EAAE,GACZ,EAAY,EAAE,GACd,EAAU,EAAE;CACd;CAIA,IAAM,IAAc,IAAgB,IAAI;CAExC,OACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,CAAQ,CAAA,GACR,kBAAC,IAAD;EAAS,MAAM;YACf,kBAAC,GAAD;GACE,OAAM;GACN,UAAS;GACT,SACE,kBAAC,GAAD;IAAO,WAAU;IAAM,SAAS;cAAhC,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAW,WAAW,kBAAC,IAAD,CAAqB,CAAA;KAAG,eAAe,KAAK,EAAK;eAAG;IAAe,CAAA,GACxG,IACC,kBAAC,GAAD;KAAQ,SAAQ;KAAY,WAAW,kBAAC,GAAD,CAAiB,CAAA;KAAG,eAAe,EAAS,qCAAqC;eAAG;IAEnH,CAAA,IACN,IACC;;aAXX,CAcG,IAAQ,kBAAC,IAAD;IAAO,UAAS;IAAQ,IAAI,EAAE,IAAI,EAAE;cAAI;GAAa,CAAA,IAAI,MAElE,kBAAC,GAAD;IACE,SAAQ;IACR,OAAM;IACN,UAAU,IACN,aACA,IACE,GAAG,EAAc,OAAO,GAAG,EAAc,WAAW,IAAI,aAAa,YAAY,iBAAiB,IAAM,GAAW,QAAQ,CAAC,MAC5H,GAAG,EAAc,OAAO,GAAG,EAAc,WAAW,IAAI,aAAa;cAP7E,CASE,kBAAC,GAAD;KAAO,WAAW;MAAE,IAAI;MAAU,IAAI;KAAM;KAAG,SAAS;KAAK,IAAI,EAAE,IAAI,EAAE;eAAzE;MACE,kBAAC,GAAD;OACE,MAAK;OACL,WAAA;OACA,aAAY;OACZ,OAAO;OACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;OACzC,YAAY,EACV,gBACE,kBAAC,GAAD;QAAgB,UAAS;kBACvB,kBAAC,IAAD,EAAmB,UAAS,QAAS,CAAA;OACvB,CAAA,EAEpB;MACD,CAAA;MACD,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,OAAM;OACN,iBAAiB,EAAE,QAAQ,GAAK;OAChC,OAAO;OACP,WAAW,MAAM,EAAY,EAAE,OAAO,KAAK;OAC3C,IAAI,EAAE,UAAU,IAAI;MACrB,CAAA;MACD,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,OAAM;OACN,iBAAiB,EAAE,QAAQ,GAAK;OAChC,OAAO;OACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;OACzC,IAAI,EAAE,UAAU,IAAI;MACrB,CAAA;MACD,kBAAC,GAAD;OACE,SAAQ;OACR,SAAS;OACT,UAAU,CAAC;OACX,IAAI,EAAE,WAAW,EAAE,IAAI,SAAS,EAAE;iBACnC;MAEO,CAAA;KACH;QAEP,kBAAC,GAAD;KAAS,OAAA;eAAT,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;MACE,kBAAC,GAAD,EAAA,UAAW,MAAc,CAAA;MACzB,kBAAC,GAAD,EAAA,UAAW,OAAe,CAAA;MAC1B,kBAAC,GAAD,EAAA,UAAW,QAAgB,CAAA;MAC3B,kBAAC,GAAD,EAAA,UAAW,QAAgB,CAAA;MAC1B,IAAgB,kBAAC,GAAD,EAAA,UAAW,UAAkB,CAAA,IAAI;MACjD,IAAgB,kBAAC,GAAD;OAAW,OAAM;iBAAQ;MAAgB,CAAA,IAAI;MAC9D,kBAAC,GAAD,EAAA,UAAW,QAAgB,CAAA;KACnB,EAAA,CAAA,EACD,CAAA,GACX,kBAAC,GAAD,EAAA,UACG,IACC,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAG,MAChC,kBAAC,GAAD,EAAA,UACG,MAAM,KAAK,EAAE,QAAQ,EAAY,CAAC,EAAE,KAAK,GAAI,MAC5C,kBAAC,GAAD,EAAA,UAAmB,kBAAC,GAAD,EAAU,QAAQ,GAAK,CAAA,EAAY,GAAtC,CAAsC,CACvD,EACO,GAJK,CAIL,CACX,IACC,EAAc,WAAW,IAC3B,kBAAC,GAAD;MACE,SAAS;MACT,MAAM;MACN,OAAO,IAAY,oCAAoC;MACvD,MAAM,IACF,mCACA;MACJ,QAAQ,IACJ,kBAAC,GAAD;OAAQ,SAAQ;OAAO,MAAK;OAAQ,SAAS;iBAAc;MAAqB,CAAA,IAC/E,IACD,kBAAC,GAAD;OAAQ,SAAQ;OAAY,MAAK;OAAQ,WAAW,kBAAC,GAAD,CAAiB,CAAA;OAAG,eAAe,EAAS,qCAAqC;iBAAG;MAEhI,CAAA,IAHK;KAKlB,CAAA,IACC,EAAc,KAAK,MAAM;MAC3B,IAAM,IAAU,EAAE,WAAW,CAAC,GACxB,IAAiB,EAAQ,MAAM,GAAG,EAAgB,GAClD,IAAW,EAAQ,SAAS,EAAe;MACjD,OACE,kBAAC,GAAD;OAAqB,OAAA;iBAArB;QACE,kBAAC,GAAD,EAAA,UAAW,kBAAC,GAAD,EAAO,IAAI,EAAE,SAAW,CAAA,EAAY,CAAA;QAC/C,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;SAAY,SAAQ;SAAQ,IAAI,EAAE,YAAY,IAAI;mBAAI,EAAW,EAAE,YAAY;QAAc,CAAA,EACpF,CAAA;QACX,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAY,SAAQ;SAAQ,IAAI,EAAE,YAAY,IAAI;mBAC/C,EAAE,cAAc,EAAE,cAAc,SAAS,aAAa;QAC7C,CAAA,GACZ,kBAAC,GAAD;SAAY,SAAQ;SAAU,OAAM;mBACjC,EAAE;QACO,CAAA,CACH,EAAA,CAAA;QACX,kBAAC,GAAD;SAAW,IAAI,EAAE,UAAU,IAAI;mBAC5B,EAAQ,WAAW,IAClB,kBAAC,GAAD;UAAY,SAAQ;UAAU,OAAM;oBAAgB;SAAa,CAAA,IAEjE,kBAAC,GAAD;UAAO,WAAU;UAAM,SAAS;UAAK,UAAS;UAAO,YAAA;oBAArD,CACG,EAAe,KAAK,MACnB,kBAAC,GAAD;WAEE,MAAK;WACL,OACE,kBAAC,GAAD;YAAK,WAAU;YAAO,IAAI;aAAE,SAAS;aAAe,YAAY;aAAY,KAAK;YAAI;sBAArF,CACE,kBAAC,GAAD;aAAK,WAAU;aAAO,IAAI,EAAE,YAAY,IAAI;uBAAI,EAAE,eAAe;YAAS,CAAA,GAC1E,kBAAC,GAAD;aAAK,WAAU;aAAO,IAAI;cAAE,YAAY;cAAK,oBAAoB;cAAgB,OAAO;aAAiB;uBAAzG,CAA4G,KACxG,EAAE,gBAAgB,CACjB;cACF;;WAEP,IAAI;YACF,QAAQ;YACR,cAAc;YACd,SAAS,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;YAC/C,OAAO;YACP,QAAQ,aAAa,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;YAC3D,oBAAoB,EAAE,IAAI,IAAK;WACjC;UACD,GAlBM,EAAE,EAkBR,CACF,GACA,IAAW,IACV,kBAAC,GAAD;WACE,MAAK;WACL,OAAO,IAAI,EAAS;WACpB,IAAI;YACF,QAAQ;YACR,cAAc;YACd,SAAS;YACT,OAAO;YACP,QAAQ,cAAc,EAAM,QAAQ;YACpC,oBAAoB;aAAE,IAAI;aAAM,YAAY;YAAI;WAClD;UACD,CAAA,IACC,IACC;;QAEA,CAAA;QACV,IACC,kBAAC,GAAD,EAAA,UACG,EAAE,cACD,kBAAC,GAAD;SACE,MAAK;SACL,OAAO,EAAE;SACT,IAAI;UACF,YAAY;UAAK,cAAc;UAAK,QAAQ;UAC5C,SAAS,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;UAC/C,OAAO;UACP,QAAQ,aAAa,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;SAC7D;QACD,CAAA,IACC,kBAAC,GAAD;SAAY,SAAQ;SAAU,OAAM;mBAAgB;QAAa,CAAA,EAC5D,CAAA,IACT;QACH,IACC,kBAAC,GAAD;SAAW,OAAM;mBAAQ,kBAAC,GAAD,EAAS,OAAO,EAAE,YAAc,CAAA;QAAY,CAAA,IACnE;QACJ,kBAAC,GAAD;SAAW,IAAI,EAAE,UAAU,IAAI;mBAC5B,EAAE,QACD,kBAAC,GAAD;UAAY,SAAQ;UAAU,OAAM;UAClC,IAAI;WAAE,SAAS;WAAS,UAAU;WAAU,cAAc;WAAY,YAAY;UAAS;oBAC1F,EAAE;SACO,CAAA,IACV,kBAAC,GAAD;UAAY,SAAQ;UAAU,OAAM;oBAAgB;SAAa,CAAA;QAC5D,CAAA;OACH;SApFK,EAAE,EAoFP;KAEd,CAAC,EACQ,CAAA,CACJ;MACE;KACF;;CACJ,CAAA,CACN,EAAA,CAAA;AAET"}
1
+ {"version":3,"file":"PurchasesPage-CvaZouVy.js","names":[],"sources":["../src/views/PurchasesPage.jsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Alert,\n Box,\n Button,\n Chip,\n InputAdornment,\n Skeleton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport { alpha, useTheme } from '@mui/material/styles';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';\nimport SearchRoundedIcon from '@mui/icons-material/SearchRounded';\nimport ShoppingCartRoundedIcon from '@mui/icons-material/ShoppingCartRounded';\nimport { showToast } from '@/common/toaster/toaster';\nimport PmPageShell from '@pm/components/PmPageShell';\nimport PmNav from '@pm/components/PmNav';\nimport PmGuard from '@pm/components/PmGuard';\nimport { usePermissionedEffect, usePmPermissions } from '@pm/utils/permissions';\nimport {\n PmEmptyRow,\n PmMoney,\n PmRef,\n PmTable,\n PmTableCard,\n} from '@pm/components/PmTable';\nimport { listPurchases } from '@pm/services/pmApi';\nimport { formatDate } from '@pm/utils/format';\nimport { currencySymbol } from '@pm/utils/currency';\nimport { usePmSettings } from '@pm/utils/settingsStore';\n\n// Compact one-liner of items+qty for the table. We render up to MAX_ITEMS_INLINE\n// pills inline; anything beyond gets a \"+N more\" pill so the row height stays\n// stable regardless of how many lines a purchase has.\nconst MAX_ITEMS_INLINE = 3;\n\nexport default function PurchasesPage() {\n const perms = usePmPermissions();\n const canCreate = perms.has('update.manage.productManagement.purchase');\n const settings = usePmSettings();\n const cur = currencySymbol(settings?.currencyCode);\n const manageAmounts = settings?.managePurchaseAmounts !== false;\n const navigate = useNavigate();\n const theme = useTheme();\n const [items, setItems] = useState([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState('');\n const [search, setSearch] = useState('');\n const [fromDate, setFromDate] = useState('');\n const [toDate, setToDate] = useState('');\n\n async function load() {\n setLoading(true);\n setError('');\n try {\n setItems(await listPurchases(0, 50));\n } catch (err) {\n const message = err?.response?.data?.message ?? err?.message ?? 'Failed to load purchases';\n setError(message);\n showToast({ message, type: 'error' });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect('view.manage.productManagement.purchase', () => {\n void load();\n }, []);\n\n // Search hits notes + every batch's productName + lotNumber. Date filters\n // bracket purchaseDate inclusively on either side; either bound is optional.\n const filteredItems = useMemo(() => {\n const q = search.trim().toLowerCase();\n const from = fromDate ? new Date(fromDate) : null;\n const to = toDate ? new Date(toDate) : null;\n return items.filter((p) => {\n if (q) {\n const hayItems = (p.batches ?? [])\n .map((b) => `${b.productName ?? ''} ${b.lotNumber ?? ''}`)\n .join(' ');\n const hay = `${p.notes ?? ''} ${hayItems}`.toLowerCase();\n if (!hay.includes(q)) return false;\n }\n if (from || to) {\n if (!p.purchaseDate) return false;\n const d = new Date(p.purchaseDate);\n if (from && d < from) return false;\n if (to) {\n // Date inputs give midnight; include the entire `to` day.\n const toEnd = new Date(to);\n toEnd.setHours(23, 59, 59, 999);\n if (d > toEnd) return false;\n }\n }\n return true;\n });\n }, [items, search, fromDate, toDate]);\n\n const totalSpend = useMemo(\n () => filteredItems.reduce((s, x) => s + Number(x.totalAmount || 0), 0),\n [filteredItems],\n );\n\n const hasFilter = search.trim() !== '' || fromDate !== '' || toDate !== '';\n\n function clearFilters() {\n setSearch('');\n setFromDate('');\n setToDate('');\n }\n\n // Match the table column count when manageAmounts swaps Payment + Total in\n // and out. Owner + Items columns are always present.\n const columnCount = manageAmounts ? 7 : 5;\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={'view.manage.productManagement.purchase'}>\n <PmPageShell\n title=\"Completed Purchases\"\n subtitle=\"Completed purchases and approved requests. Each shows the items and cost.\"\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\" startIcon={<RefreshRoundedIcon />} onClick={() => void load()}>Refresh</Button>\n {canCreate ? (\n <Button variant=\"contained\" startIcon={<AddRoundedIcon />} onClick={() => navigate('/ui/medplat/manage/pm/purchases/new')}>\n New Purchase\n </Button>\n ) : null}\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n <PmTableCard\n eyebrow=\"Procurement\"\n title=\"Recent purchases\"\n subtitle={loading\n ? 'Loading…'\n : manageAmounts\n ? `${filteredItems.length} ${filteredItems.length === 1 ? 'purchase' : 'purchases'} · total spend ${cur}${totalSpend.toFixed(2)}`\n : `${filteredItems.length} ${filteredItems.length === 1 ? 'purchase' : 'purchases'}`}\n >\n <Stack direction={{ xs: 'column', md: 'row' }} spacing={1.5} sx={{ mb: 2 }}>\n <TextField\n size=\"small\"\n fullWidth\n placeholder=\"Search notes or items…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchRoundedIcon fontSize=\"small\" />\n </InputAdornment>\n ),\n }}\n />\n <TextField\n size=\"small\"\n type=\"date\"\n label=\"From\"\n InputLabelProps={{ shrink: true }}\n value={fromDate}\n onChange={(e) => setFromDate(e.target.value)}\n sx={{ minWidth: 160 }}\n />\n <TextField\n size=\"small\"\n type=\"date\"\n label=\"To\"\n InputLabelProps={{ shrink: true }}\n value={toDate}\n onChange={(e) => setToDate(e.target.value)}\n sx={{ minWidth: 160 }}\n />\n <Button\n variant=\"text\"\n onClick={clearFilters}\n disabled={!hasFilter}\n sx={{ alignSelf: { md: 'center' } }}\n >\n Clear\n </Button>\n </Stack>\n\n <PmTable dense>\n <TableHead>\n <TableRow>\n <TableCell>Ref</TableCell>\n <TableCell>Date</TableCell>\n <TableCell>Owner</TableCell>\n <TableCell>Items</TableCell>\n {manageAmounts ? <TableCell>Payment</TableCell> : null}\n {manageAmounts ? <TableCell align=\"right\">Total</TableCell> : null}\n <TableCell>Notes</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n Array.from({ length: 5 }).map((_, i) => (\n <TableRow key={i}>\n {Array.from({ length: columnCount }).map((__, j) => (\n <TableCell key={j}><Skeleton height={22} /></TableCell>\n ))}\n </TableRow>\n ))\n ) : filteredItems.length === 0 ? (\n <PmEmptyRow\n colSpan={columnCount}\n icon={ShoppingCartRoundedIcon}\n title={hasFilter ? 'No purchases match your filters' : 'No purchases yet'}\n hint={hasFilter\n ? 'Adjust your search or filters.'\n : 'Record a purchase to add items to your stock.'}\n action={hasFilter\n ? <Button variant=\"text\" size=\"small\" onClick={clearFilters}>Clear filters</Button>\n : !canCreate ? null : (\n <Button variant=\"contained\" size=\"small\" startIcon={<AddRoundedIcon />} onClick={() => navigate('/ui/medplat/manage/pm/purchases/new')}>\n Record first purchase\n </Button>\n )}\n />\n ) : filteredItems.map((p) => {\n const batches = p.batches ?? [];\n const visibleBatches = batches.slice(0, MAX_ITEMS_INLINE);\n const overflow = batches.length - visibleBatches.length;\n return (\n <TableRow key={p.id} hover>\n <TableCell><PmRef id={p.serialId} /></TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>{formatDate(p.purchaseDate)}</Typography>\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>\n {p.ownerName || (p.ownerType === 'USER' ? 'Personal' : 'Facility')}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {p.ownerType}\n </Typography>\n </TableCell>\n <TableCell sx={{ maxWidth: 360 }}>\n {batches.length === 0 ? (\n <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>\n ) : (\n <Stack direction=\"row\" spacing={0.5} flexWrap=\"wrap\" useFlexGap>\n {visibleBatches.map((b) => (\n <Chip\n key={b.id}\n size=\"small\"\n label={\n <Box component=\"span\" sx={{ display: 'inline-flex', alignItems: 'baseline', gap: 0.5 }}>\n <Box component=\"span\" sx={{ fontWeight: 700 }}>{b.productName ?? '—'}</Box>\n <Box component=\"span\" sx={{ fontWeight: 700, fontVariantNumeric: 'tabular-nums', color: 'text.secondary' }}>\n ×{b.qtyPurchased ?? 0}\n </Box>\n </Box>\n }\n sx={{\n height: 22,\n borderRadius: 1.5,\n bgcolor: alpha(theme.palette.primary.main, 0.06),\n color: 'text.primary',\n border: `1px solid ${alpha(theme.palette.primary.main, 0.16)}`,\n '& .MuiChip-label': { px: 0.85 },\n }}\n />\n ))}\n {overflow > 0 ? (\n <Chip\n size=\"small\"\n label={`+${overflow} more`}\n sx={{\n height: 22,\n borderRadius: 1.5,\n bgcolor: 'transparent',\n color: 'text.secondary',\n border: `1px dashed ${theme.palette.divider}`,\n '& .MuiChip-label': { px: 0.85, fontWeight: 700 },\n }}\n />\n ) : null}\n </Stack>\n )}\n </TableCell>\n {manageAmounts ? (\n <TableCell>\n {p.paymentMode ? (\n <Chip\n size=\"small\"\n label={p.paymentMode}\n sx={{\n fontWeight: 700, borderRadius: 1.5, height: 22,\n bgcolor: alpha(theme.palette.primary.main, 0.08),\n color: 'primary.dark',\n border: `1px solid ${alpha(theme.palette.primary.main, 0.18)}`,\n }}\n />\n ) : <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>}\n </TableCell>\n ) : null}\n {manageAmounts ? (\n <TableCell align=\"right\"><PmMoney value={p.totalAmount} /></TableCell>\n ) : null}\n <TableCell sx={{ maxWidth: 320 }}>\n {p.notes ? (\n <Typography variant=\"caption\" color=\"text.secondary\"\n sx={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>\n {p.notes}\n </Typography>\n ) : <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1,11 +1,10 @@
1
- import { A as e, E as t, P as n, a as r, c as i, i as a, n as ee, r as te, t as ne, y as re } from "./PmGuard-CjJBisaN.js";
2
- import { t as ie } from "./currency-BDDCDgeE.js";
3
- import { n as o } from "./PmTable-BuXZQZCU.js";
4
- import { d as s, m as c, o as ae, t as l } from "./validation-Ba7puFIO.js";
5
- import { t as oe } from "./useFieldErrors-hDG8ox_x.js";
6
- import { i as se, r as u } from "./format-UOyOu75i.js";
7
- import { n as ce } from "./constants-BrA_hfAI.js";
8
- import { t as le } from "./MemberProfileCard-DbHZCS5n.js";
1
+ import { D as e, F as t, a as n, b as r, i, j as ee, l as te, n as ne, o as re, r as ie, t as ae } from "./currency-ClRLIEg2.js";
2
+ import { r as a } from "./PmTable-B9970kPY.js";
3
+ import { d as o, m as s, o as oe, t as c } from "./validation-Ba7puFIO.js";
4
+ import { t as se } from "./useFieldErrors-hDG8ox_x.js";
5
+ import { i as l, r as u } from "./format-UOyOu75i.js";
6
+ import { n as ce } from "./constants-5ZCZo-Zm.js";
7
+ import { t as le } from "./MemberProfileCard-0vjO3Gzd.js";
9
8
  import { useMemo as d, useState as f } from "react";
10
9
  import { Fragment as ue, jsx as p, jsxs as m } from "react/jsx-runtime";
11
10
  import { v4 as h } from "uuid";
@@ -25,7 +24,7 @@ function A() {
25
24
  };
26
25
  }
27
26
  function j() {
28
- let j = ge(), { memberId: M } = _e(), N = ie(i()?.currencyCode), [P, be] = f([]), [F, xe] = f([]), [Se, Ce] = f({}), [I, we] = f(""), [L, R] = f(""), Te = a().has("view.manage.productManagement.dues"), z = oe(), Ee = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10), [B, V] = f({
27
+ let j = ge(), { memberId: M } = _e(), N = ae(te()?.currencyCode), [P, be] = f([]), [F, xe] = f([]), [Se, Ce] = f({}), [I, we] = f(""), [L, R] = f(""), Te = n().has("view.manage.productManagement.dues"), z = se(), Ee = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10), [B, V] = f({
29
28
  id: h(),
30
29
  saleDate: Ee,
31
30
  paymentMode: "CASH",
@@ -33,24 +32,24 @@ function j() {
33
32
  notes: "",
34
33
  items: [A()]
35
34
  });
36
- te("update.manage.productManagement.sale", () => {
37
- async function r() {
35
+ i("update.manage.productManagement.sale", () => {
36
+ async function n() {
38
37
  try {
39
- let [r, i, a] = await Promise.all([
40
- n(),
41
- e().catch(() => []),
42
- Te ? t().catch(() => []) : Promise.resolve([])
38
+ let [n, r, i] = await Promise.all([
39
+ t(),
40
+ ee().catch(() => []),
41
+ Te ? e().catch(() => []) : Promise.resolve([])
43
42
  ]);
44
- be(r), xe(i), Ce(Object.fromEntries((a ?? []).map((e) => [e.memberId, Number(e.totalDue)])));
43
+ be(n), xe(r), Ce(Object.fromEntries((i ?? []).map((e) => [e.memberId, Number(e.totalDue)])));
45
44
  } catch (e) {
46
45
  R(e?.response?.data?.message ?? e?.message ?? "Failed to load reference data");
47
46
  }
48
47
  }
49
- r();
48
+ n();
50
49
  }, []);
51
50
  let H = (e) => P.find((t) => t.id === e), U = d(() => {
52
51
  let e = new Set(F.filter((e) => e.notForSale === !0).map((e) => e.id));
53
- return P.filter((t) => !(t.qtyRemaining <= 0 || t.isQuarantined || e.has(t.productId) || se(t.expiryDate)));
52
+ return P.filter((t) => !(t.qtyRemaining <= 0 || t.isQuarantined || e.has(t.productId) || l(t.expiryDate)));
54
53
  }, [P, F]);
55
54
  function W(e, t, n) {
56
55
  V((r) => {
@@ -65,30 +64,30 @@ function j() {
65
64
  });
66
65
  }
67
66
  let G = d(() => B.items.reduce((e, t) => !t.salePrice || !t.quantity ? e : e + Number(t.salePrice) * Number(t.quantity), 0), [B.items]), K = Number(M ? Se[M] ?? 0 : 0), De = Number(B.collectingNow || 0), q = Math.min(Math.max(De, 0), G), J = Math.max(G - q, 0), Y = J + K, X = {
68
- saleDate: c(B.saleDate, "Sale date"),
67
+ saleDate: s(B.saleDate, "Sale date"),
69
68
  member: M ? "" : "No member selected"
70
- }, Z = B.collectingNow === "" ? "" : s(B.collectingNow);
69
+ }, Z = B.collectingNow === "" ? "" : o(B.collectingNow);
71
70
  !Z && B.collectingNow !== "" && Number(B.collectingNow) > G ? X.collectingNow = `Cannot collect more than the sale amount (${u(G)}). Settle old dues via Payments.` : Z && (X.collectingNow = Z);
72
71
  let Oe = new Set(U.map((e) => e.id)), Q = B.items.map((e) => {
73
- let t = H(e.batchId), n = ae(e.quantity, "Quantity", {
72
+ let t = H(e.batchId), n = oe(e.quantity, "Quantity", {
74
73
  required: !0,
75
74
  min: 1
76
75
  });
77
76
  !n && t && Number(e.quantity) > t.qtyRemaining && (n = `Only ${t.qtyRemaining} available in selected batch`);
78
- let r = c(e.batchId, "Batch");
77
+ let r = s(e.batchId, "Batch");
79
78
  return !r && e.batchId && !Oe.has(e.batchId) && (r = "Batch is not available for sale (expired, quarantined, out of stock, or not-for-sale)."), {
80
79
  batchId: r,
81
80
  quantity: n,
82
- salePrice: s(e.salePrice) || c(e.salePrice, "Unit price")
81
+ salePrice: o(e.salePrice) || s(e.salePrice, "Unit price")
83
82
  };
84
- }), $ = l(X) && Q.every(l) && B.items.length > 0 && G > 0;
83
+ }), $ = c(X) && Q.every(c) && B.items.length > 0 && G > 0;
85
84
  async function ke() {
86
85
  if (!$) {
87
86
  z.markAllTouched(), R("Please fix the highlighted fields and try again.");
88
87
  return;
89
88
  }
90
89
  try {
91
- await re({
90
+ await r({
92
91
  id: B.id,
93
92
  saleDate: B.saleDate,
94
93
  memberId: M,
@@ -114,10 +113,10 @@ function j() {
114
113
  R(e?.response?.data?.message ?? e?.message ?? "Failed to record sale");
115
114
  }
116
115
  }
117
- return /* @__PURE__ */ m(de, { children: [/* @__PURE__ */ p(ee, {}), /* @__PURE__ */ p(ne, {
116
+ return /* @__PURE__ */ m(de, { children: [/* @__PURE__ */ p(ie, {}), /* @__PURE__ */ p(ne, {
118
117
  perm: "update.manage.productManagement.sale",
119
118
  title: "You don’t have permission to record sales",
120
- children: /* @__PURE__ */ m(r, {
119
+ children: /* @__PURE__ */ m(re, {
121
120
  title: "Record Sale",
122
121
  actions: /* @__PURE__ */ m(D, {
123
122
  direction: "row",
@@ -150,7 +149,7 @@ function j() {
150
149
  },
151
150
  children: /* @__PURE__ */ m(y, {
152
151
  sx: { p: 2.5 },
153
- children: [/* @__PURE__ */ p(o, {
152
+ children: [/* @__PURE__ */ p(a, {
154
153
  eyebrow: "Step 1",
155
154
  title: "Customer",
156
155
  hint: "The customer is set from the link you opened. Their balance shows below.",
@@ -182,7 +181,7 @@ function j() {
182
181
  justifyContent: "space-between",
183
182
  alignItems: "flex-start",
184
183
  sx: { mb: 2 },
185
- children: [/* @__PURE__ */ p(o, {
184
+ children: [/* @__PURE__ */ p(a, {
186
185
  eyebrow: "Step 2",
187
186
  title: "Line items",
188
187
  hint: "Each item comes from a batch. Buy price is shown as a cost reference."
@@ -344,7 +343,7 @@ function j() {
344
343
  children: /* @__PURE__ */ m(y, {
345
344
  sx: { p: 2.5 },
346
345
  children: [
347
- /* @__PURE__ */ p(o, {
346
+ /* @__PURE__ */ p(a, {
348
347
  eyebrow: "Step 3",
349
348
  title: "Bill summary",
350
349
  hint: "Shows totals, previous balance, and what you're collecting. Any remainder becomes a note of what they owe.",
@@ -545,4 +544,4 @@ function j() {
545
544
  //#endregion
546
545
  export { j as default };
547
546
 
548
- //# sourceMappingURL=SaleCreatePage-DpkQMoTq.js.map
547
+ //# sourceMappingURL=SaleCreatePage-DP1Qmltu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SaleCreatePage-DpkQMoTq.js","names":[],"sources":["../src/views/SaleCreatePage.jsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Divider,\n FormControl,\n FormHelperText,\n Grid,\n IconButton,\n InputAdornment,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n TextField,\n Typography,\n} from \"@mui/material\";\nimport AddRoundedIcon from \"@mui/icons-material/AddRounded\";\nimport DeleteOutlineRoundedIcon from \"@mui/icons-material/DeleteOutlineRounded\";\nimport SaveRoundedIcon from \"@mui/icons-material/SaveRounded\";\nimport { showToast } from \"@/common/toaster/toaster\";\nimport PmPageShell from \"@pm/components/PmPageShell\";\nimport PmNav from \"@pm/components/PmNav\";\nimport PmGuard from \"@pm/components/PmGuard\";\nimport { usePermissionedEffect, usePmPermissions } from \"@pm/utils/permissions\";\nimport {\n createSale,\n getMemberOutstandingDues,\n getProducts,\n listBatches,\n} from \"@pm/services/pmApi\";\nimport MemberProfileCard from \"@pm/components/MemberProfileCard\";\nimport { PAYMENT_MODES } from \"@pm/utils/constants\";\nimport { formatNumber, isPastExpiry } from \"@pm/utils/format\";\nimport { currencySymbol } from \"@pm/utils/currency\";\nimport { usePmSettings } from \"@pm/utils/settingsStore\";\nimport { PmFormSection } from \"@pm/components/PmTable\";\nimport {\n isClean,\n validateInteger,\n validatePrice,\n validateRequired,\n} from \"@pm/utils/validation\";\nimport { useFieldErrors } from \"@pm/utils/useFieldErrors\";\n\nfunction emptyLine() {\n return { key: uuidv4(), batchId: \"\", quantity: 1, salePrice: \"\" };\n}\n\nexport default function SaleCreatePage() {\n const navigate = useNavigate();\n const { memberId } = useParams();\n const settings = usePmSettings();\n const cur = currencySymbol(settings?.currencyCode);\n const [batches, setBatches] = useState([]);\n const [products, setProducts] = useState([]);\n // Map of memberId -> previousDue. The dues endpoint only returns members\n // with non-zero dues so the map stays small. Looked up by route memberId.\n const [duesByMember, setDuesByMember] = useState({});\n // Cached after MemberProfileCard fetches — used by the warning Alert that\n // references the member by name when remaining dues will be recorded.\n const [memberName, setMemberName] = useState(\"\");\n const [error, setError] = useState(\"\");\n const perms = usePmPermissions();\n // When the operator can't see dues, previousDue gracefully degrades to 0\n // instead of populating duesByMember from a 403'ing call.\n const canSeeDues = perms.has(\"view.manage.productManagement.dues\");\n const fieldErrors = useFieldErrors();\n const today = new Date().toISOString().substring(0, 10);\n const [form, setForm] = useState({\n id: uuidv4(),\n saleDate: today,\n paymentMode: \"CASH\",\n collectingNow: \"\",\n notes: \"\",\n items: [emptyLine()],\n });\n\n usePermissionedEffect(\n \"update.manage.productManagement.sale\",\n () => {\n async function load() {\n try {\n // Members are picked via async search now — no upfront load. Dues\n // come from the (aggregated) backend query and get attached to a\n // picked member by id.\n const [b, p, dues] = await Promise.all([\n listBatches(),\n getProducts().catch(() => []),\n // Silent degrade when the operator lacks the dues permission —\n // previousDue will stay 0 and the sale itself still works.\n canSeeDues\n ? getMemberOutstandingDues().catch(() => [])\n : Promise.resolve([]),\n ]);\n setBatches(b);\n setProducts(p);\n setDuesByMember(\n Object.fromEntries(\n (dues ?? []).map((d) => [d.memberId, Number(d.totalDue)]),\n ),\n );\n } catch (err) {\n setError(\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to load reference data\",\n );\n }\n }\n void load();\n },\n [],\n );\n\n const batchById = (id) => batches.find((b) => b.id === id);\n\n // Batches that can be sold: stock on hand, not quarantined, not past expiry, and\n // the underlying product is not flagged as not-for-sale. Computed once per render.\n const sellableBatches = useMemo(() => {\n const notForSaleProductIds = new Set(\n products.filter((p) => p.notForSale === true).map((p) => p.id),\n );\n return batches.filter((b) => {\n if (b.qtyRemaining <= 0) return false;\n if (b.isQuarantined) return false;\n if (notForSaleProductIds.has(b.productId)) return false;\n // A batch is sellable on the day of its printed expiry; only the\n // following day counts as expired (matches the unit packaging rule).\n if (isPastExpiry(b.expiryDate)) return false;\n return true;\n });\n }, [batches, products]);\n\n function patchLine(idx, k, v) {\n setForm((f) => {\n const items = f.items.slice();\n items[idx] = { ...items[idx], [k]: v };\n return { ...f, items };\n });\n }\n\n // -------- Money math --------\n const saleAmount = useMemo(\n () =>\n form.items.reduce((sum, line) => {\n if (!line.salePrice || !line.quantity) return sum;\n return sum + Number(line.salePrice) * Number(line.quantity);\n }, 0),\n [form.items],\n );\n\n const previousDue = Number(memberId ? (duesByMember[memberId] ?? 0) : 0);\n const collectedRaw = Number(form.collectingNow || 0);\n // Clip the collected amount at the current sale's total — anything more would\n // need to be applied to the member's old dues, which is a separate Payment flow.\n const collected = Math.min(Math.max(collectedRaw, 0), saleAmount);\n const saleDue = Math.max(saleAmount - collected, 0);\n const remainingDue = saleDue + previousDue;\n\n // -------- Validation --------\n const headerErrors = {\n saleDate: validateRequired(form.saleDate, \"Sale date\"),\n member: memberId ? \"\" : \"No member selected\",\n };\n // Collecting Now: optional; if filled, must be a non-negative number ≤ sale amount.\n const collectingErr =\n form.collectingNow === \"\" ? \"\" : validatePrice(form.collectingNow);\n if (\n !collectingErr &&\n form.collectingNow !== \"\" &&\n Number(form.collectingNow) > saleAmount\n ) {\n headerErrors.collectingNow = `Cannot collect more than the sale amount (${formatNumber(saleAmount)}). Settle old dues via Payments.`;\n } else if (collectingErr) {\n headerErrors.collectingNow = collectingErr;\n }\n\n const sellableBatchIds = new Set(sellableBatches.map((b) => b.id));\n const lineErrors = form.items.map((line) => {\n const batch = batchById(line.batchId);\n const qty = validateInteger(line.quantity, \"Quantity\", {\n required: true,\n min: 1,\n });\n let qtyErr = qty;\n if (!qtyErr && batch && Number(line.quantity) > batch.qtyRemaining) {\n qtyErr = `Only ${batch.qtyRemaining} available in selected batch`;\n }\n let batchErr = validateRequired(line.batchId, \"Batch\");\n if (!batchErr && line.batchId && !sellableBatchIds.has(line.batchId)) {\n batchErr =\n \"Batch is not available for sale (expired, quarantined, out of stock, or not-for-sale).\";\n }\n return {\n batchId: batchErr,\n quantity: qtyErr,\n salePrice:\n validatePrice(line.salePrice) ||\n validateRequired(line.salePrice, \"Unit price\"),\n };\n });\n const formIsValid =\n isClean(headerErrors) &&\n lineErrors.every(isClean) &&\n form.items.length > 0 &&\n saleAmount > 0;\n\n async function save() {\n if (!formIsValid) {\n fieldErrors.markAllTouched();\n setError(\"Please fix the highlighted fields and try again.\");\n return;\n }\n try {\n await createSale({\n id: form.id,\n saleDate: form.saleDate,\n memberId,\n paymentMode: form.paymentMode || null,\n totalAmount: Number(saleAmount.toFixed(2)),\n paidAmount: Number(collected.toFixed(2)),\n dueAmount: Number(saleDue.toFixed(2)),\n notes: form.notes || null,\n items: form.items.map((l) => {\n const batch = batchById(l.batchId);\n return {\n batchId: l.batchId,\n productId: batch?.productId,\n quantity: Number(l.quantity),\n salePrice: Number(l.salePrice),\n };\n }),\n });\n showToast({ message: \"Sale recorded.\", type: \"success\" });\n navigate(\"/ui/medplat/manage/pm/sales\");\n } catch (err) {\n setError(\n err?.response?.data?.message ?? err?.message ?? \"Failed to record sale\",\n );\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard\n perm={\"update.manage.productManagement.sale\"}\n title=\"You don’t have permission to record sales\"\n >\n <PmPageShell\n title=\"Record Sale\"\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"outlined\"\n onClick={() => navigate(\"/ui/medplat/manage/pm/sales\")}\n >\n Cancel\n </Button>\n <Button\n variant=\"contained\"\n startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}\n >\n Save\n </Button>\n </Stack>\n }\n >\n {error ? (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {error}\n </Alert>\n ) : null}\n\n {/* ---------- Member ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n mb: 2,\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 1\"\n title=\"Customer\"\n hint=\"The customer is set from the link you opened. Their balance shows below.\"\n sx={{ mb: 2 }}\n />\n {memberId ? (\n <MemberProfileCard\n memberId={memberId}\n previousDue={previousDue}\n currencySymbol={cur}\n onLoad={(p) => setMemberName(p?.name ?? \"\")}\n />\n ) : (\n <Alert severity=\"warning\">No member selected</Alert>\n )}\n </CardContent>\n </Card>\n\n {/* ---------- Line items ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n mb: 2,\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"flex-start\"\n sx={{ mb: 2 }}\n >\n <PmFormSection\n eyebrow=\"Step 2\"\n title=\"Line items\"\n hint=\"Each item comes from a batch. Buy price is shown as a cost reference.\"\n />\n <Button\n variant=\"outlined\"\n startIcon={<AddRoundedIcon />}\n onClick={() =>\n setForm({ ...form, items: [...form.items, emptyLine()] })\n }\n >\n Add Line\n </Button>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Stack spacing={2}>\n {form.items.map((line, idx) => {\n const batch = batchById(line.batchId);\n const lerr = lineErrors[idx] || {};\n const subtotal =\n line.salePrice && line.quantity\n ? Number(line.salePrice) * Number(line.quantity)\n : 0;\n return (\n <Card\n key={line.key}\n variant=\"outlined\"\n sx={{ p: 2, borderRadius: 3 }}\n >\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs={12} md={5}>\n <FormControl\n required\n fullWidth\n size=\"small\"\n error={\n fieldErrors.show(`items.${idx}.batchId`) &&\n !!lerr.batchId\n }\n >\n <InputLabel>Batch</InputLabel>\n <Select\n label=\"Batch\"\n value={line.batchId}\n onChange={(e) =>\n patchLine(idx, \"batchId\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(\n `items.${idx}.batchId`,\n )}\n >\n {sellableBatches.map((b) => (\n <MenuItem key={b.id} value={b.id}>\n {b.productName} · lot {b.lotNumber || \"—\"} ·{\" \"}\n {b.qtyRemaining} left\n </MenuItem>\n ))}\n </Select>\n <FormHelperText>\n {fieldErrors.show(`items.${idx}.batchId`) &&\n lerr.batchId\n ? lerr.batchId\n : batch?.buyPrice != null\n ? `Buy price: ${cur}${formatNumber(batch.buyPrice)} / unit`\n : \" \"}\n </FormHelperText>\n </FormControl>\n </Grid>\n <Grid item xs={6} md={2}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"number\"\n label=\"Qty\"\n inputProps={{ min: 1, max: batch?.qtyRemaining }}\n value={line.quantity}\n onChange={(e) =>\n patchLine(idx, \"quantity\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(`items.${idx}.quantity`)}\n error={\n fieldErrors.show(`items.${idx}.quantity`) &&\n !!lerr.quantity\n }\n helperText={\n fieldErrors.show(`items.${idx}.quantity`)\n ? lerr.quantity\n : \"\"\n }\n />\n </Grid>\n <Grid item xs={6} md={2}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"number\"\n inputProps={{ step: \"0.01\", min: 0 }}\n label=\"Unit Price\"\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n {cur}\n </InputAdornment>\n ),\n }}\n value={line.salePrice}\n onChange={(e) =>\n patchLine(idx, \"salePrice\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(\n `items.${idx}.salePrice`,\n )}\n error={\n fieldErrors.show(`items.${idx}.salePrice`) &&\n !!lerr.salePrice\n }\n helperText={\n fieldErrors.show(`items.${idx}.salePrice`)\n ? lerr.salePrice\n : \"\"\n }\n />\n </Grid>\n <Grid item xs={6} md={2} sx={{ textAlign: \"right\" }}>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: \"block\" }}\n >\n Subtotal\n </Typography>\n <Typography\n variant=\"body1\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n {cur}\n {formatNumber(subtotal)}\n </Typography>\n </Grid>\n <Grid item xs={6} md={1} sx={{ textAlign: \"right\" }}>\n <IconButton\n color=\"error\"\n onClick={() =>\n setForm({\n ...form,\n items: form.items.filter((_, i) => i !== idx),\n })\n }\n disabled={form.items.length === 1}\n >\n <DeleteOutlineRoundedIcon />\n </IconButton>\n </Grid>\n </Grid>\n </Card>\n );\n })}\n </Stack>\n </CardContent>\n </Card>\n\n {/* ---------- Bill Summary footer ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n bgcolor: \"grey.50\",\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 3\"\n title=\"Bill summary\"\n hint=\"Shows totals, previous balance, and what you're collecting. Any remainder becomes a note of what they owe.\"\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n <Grid item xs={12} md={4}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"date\"\n label=\"Date\"\n InputLabelProps={{ shrink: true }}\n value={form.saleDate}\n onChange={(e) =>\n setForm({ ...form, saleDate: e.target.value })\n }\n onBlur={fieldErrors.onBlur(\"saleDate\")}\n error={\n fieldErrors.show(\"saleDate\") && !!headerErrors.saleDate\n }\n helperText={\n fieldErrors.show(\"saleDate\") ? headerErrors.saleDate : \"\"\n }\n />\n </Grid>\n <Grid item xs={12} md={4}>\n <FormControl fullWidth size=\"small\">\n <InputLabel>Payment Mode</InputLabel>\n <Select\n label=\"Payment Mode\"\n value={form.paymentMode || \"\"}\n onChange={(e) =>\n setForm({ ...form, paymentMode: e.target.value })\n }\n >\n <MenuItem value=\"\">\n <em>—</em>\n </MenuItem>\n {PAYMENT_MODES.map((m) => (\n <MenuItem key={m.value} value={m.value}>\n {m.label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Grid>\n <Grid item xs={12} md={4}>\n <TextField\n fullWidth\n size=\"small\"\n label=\"Notes\"\n value={form.notes}\n onChange={(e) =>\n setForm({ ...form, notes: e.target.value })\n }\n />\n </Grid>\n </Grid>\n\n <Divider sx={{ my: 2 }} />\n\n <Stack spacing={1.25}>\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Sale Amount ({form.items.length}{\" \"}\n {form.items.length === 1 ? \"line\" : \"lines\"})\n </Typography>\n <Typography\n variant=\"h5\"\n sx={{ fontWeight: 800, fontVariantNumeric: \"tabular-nums\" }}\n >\n {cur}\n {formatNumber(saleAmount)}\n </Typography>\n </Stack>\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Previous Due\n </Typography>\n <Typography\n variant=\"body1\"\n sx={{\n fontWeight: 700,\n fontVariantNumeric: \"tabular-nums\",\n color: previousDue > 0 ? \"warning.dark\" : \"text.primary\",\n }}\n >\n {cur}\n {formatNumber(previousDue)}\n </Typography>\n </Stack>\n\n <Divider sx={{ my: 0.5 }} />\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n spacing={2}\n >\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ flexShrink: 0 }}\n >\n Amount Collecting Now\n </Typography>\n <TextField\n size=\"small\"\n type=\"number\"\n inputProps={{\n step: \"0.01\",\n min: 0,\n max: saleAmount,\n style: { textAlign: \"right\", fontWeight: 700 },\n }}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">{cur}</InputAdornment>\n ),\n }}\n sx={{ width: 200 }}\n value={form.collectingNow}\n placeholder=\"0.00\"\n onChange={(e) =>\n setForm({ ...form, collectingNow: e.target.value })\n }\n onBlur={fieldErrors.onBlur(\"collectingNow\")}\n error={\n fieldErrors.show(\"collectingNow\") &&\n !!headerErrors.collectingNow\n }\n helperText={\n fieldErrors.show(\"collectingNow\")\n ? headerErrors.collectingNow\n : \"\"\n }\n />\n </Stack>\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Remaining Due (after this sale)\n </Typography>\n <Typography\n variant=\"h5\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n color: remainingDue > 0 ? \"warning.dark\" : \"success.dark\",\n }}\n >\n {cur}\n {formatNumber(remainingDue)}\n </Typography>\n </Stack>\n </Stack>\n\n {memberId && remainingDue > 0 ? (\n <Alert severity=\"warning\" sx={{ mt: 2 }}>\n {cur}\n {formatNumber(remainingDue)} will be recorded as pending dues\n {memberName ? (\n <>\n {\" \"}\n for <strong>{memberName}</strong>\n </>\n ) : null}\n {saleDue > 0 && previousDue > 0\n ? ` (${cur}${formatNumber(saleDue)} from this sale + ${cur}${formatNumber(previousDue)} previously outstanding).`\n : saleDue > 0\n ? \" from this sale.\"\n : \" from previously outstanding dues.\"}\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SaleCreatePage-DP1Qmltu.js","names":[],"sources":["../src/views/SaleCreatePage.jsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Divider,\n FormControl,\n FormHelperText,\n Grid,\n IconButton,\n InputAdornment,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n TextField,\n Typography,\n} from \"@mui/material\";\nimport AddRoundedIcon from \"@mui/icons-material/AddRounded\";\nimport DeleteOutlineRoundedIcon from \"@mui/icons-material/DeleteOutlineRounded\";\nimport SaveRoundedIcon from \"@mui/icons-material/SaveRounded\";\nimport { showToast } from \"@/common/toaster/toaster\";\nimport PmPageShell from \"@pm/components/PmPageShell\";\nimport PmNav from \"@pm/components/PmNav\";\nimport PmGuard from \"@pm/components/PmGuard\";\nimport { usePermissionedEffect, usePmPermissions } from \"@pm/utils/permissions\";\nimport {\n createSale,\n getMemberOutstandingDues,\n getProducts,\n listBatches,\n} from \"@pm/services/pmApi\";\nimport MemberProfileCard from \"@pm/components/MemberProfileCard\";\nimport { PAYMENT_MODES } from \"@pm/utils/constants\";\nimport { formatNumber, isPastExpiry } from \"@pm/utils/format\";\nimport { currencySymbol } from \"@pm/utils/currency\";\nimport { usePmSettings } from \"@pm/utils/settingsStore\";\nimport { PmFormSection } from \"@pm/components/PmTable\";\nimport {\n isClean,\n validateInteger,\n validatePrice,\n validateRequired,\n} from \"@pm/utils/validation\";\nimport { useFieldErrors } from \"@pm/utils/useFieldErrors\";\n\nfunction emptyLine() {\n return { key: uuidv4(), batchId: \"\", quantity: 1, salePrice: \"\" };\n}\n\nexport default function SaleCreatePage() {\n const navigate = useNavigate();\n const { memberId } = useParams();\n const settings = usePmSettings();\n const cur = currencySymbol(settings?.currencyCode);\n const [batches, setBatches] = useState([]);\n const [products, setProducts] = useState([]);\n // Map of memberId -> previousDue. The dues endpoint only returns members\n // with non-zero dues so the map stays small. Looked up by route memberId.\n const [duesByMember, setDuesByMember] = useState({});\n // Cached after MemberProfileCard fetches — used by the warning Alert that\n // references the member by name when remaining dues will be recorded.\n const [memberName, setMemberName] = useState(\"\");\n const [error, setError] = useState(\"\");\n const perms = usePmPermissions();\n // When the operator can't see dues, previousDue gracefully degrades to 0\n // instead of populating duesByMember from a 403'ing call.\n const canSeeDues = perms.has(\"view.manage.productManagement.dues\");\n const fieldErrors = useFieldErrors();\n const today = new Date().toISOString().substring(0, 10);\n const [form, setForm] = useState({\n id: uuidv4(),\n saleDate: today,\n paymentMode: \"CASH\",\n collectingNow: \"\",\n notes: \"\",\n items: [emptyLine()],\n });\n\n usePermissionedEffect(\n \"update.manage.productManagement.sale\",\n () => {\n async function load() {\n try {\n // Members are picked via async search now — no upfront load. Dues\n // come from the (aggregated) backend query and get attached to a\n // picked member by id.\n const [b, p, dues] = await Promise.all([\n listBatches(),\n getProducts().catch(() => []),\n // Silent degrade when the operator lacks the dues permission —\n // previousDue will stay 0 and the sale itself still works.\n canSeeDues\n ? getMemberOutstandingDues().catch(() => [])\n : Promise.resolve([]),\n ]);\n setBatches(b);\n setProducts(p);\n setDuesByMember(\n Object.fromEntries(\n (dues ?? []).map((d) => [d.memberId, Number(d.totalDue)]),\n ),\n );\n } catch (err) {\n setError(\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to load reference data\",\n );\n }\n }\n void load();\n },\n [],\n );\n\n const batchById = (id) => batches.find((b) => b.id === id);\n\n // Batches that can be sold: stock on hand, not quarantined, not past expiry, and\n // the underlying product is not flagged as not-for-sale. Computed once per render.\n const sellableBatches = useMemo(() => {\n const notForSaleProductIds = new Set(\n products.filter((p) => p.notForSale === true).map((p) => p.id),\n );\n return batches.filter((b) => {\n if (b.qtyRemaining <= 0) return false;\n if (b.isQuarantined) return false;\n if (notForSaleProductIds.has(b.productId)) return false;\n // A batch is sellable on the day of its printed expiry; only the\n // following day counts as expired (matches the unit packaging rule).\n if (isPastExpiry(b.expiryDate)) return false;\n return true;\n });\n }, [batches, products]);\n\n function patchLine(idx, k, v) {\n setForm((f) => {\n const items = f.items.slice();\n items[idx] = { ...items[idx], [k]: v };\n return { ...f, items };\n });\n }\n\n // -------- Money math --------\n const saleAmount = useMemo(\n () =>\n form.items.reduce((sum, line) => {\n if (!line.salePrice || !line.quantity) return sum;\n return sum + Number(line.salePrice) * Number(line.quantity);\n }, 0),\n [form.items],\n );\n\n const previousDue = Number(memberId ? (duesByMember[memberId] ?? 0) : 0);\n const collectedRaw = Number(form.collectingNow || 0);\n // Clip the collected amount at the current sale's total — anything more would\n // need to be applied to the member's old dues, which is a separate Payment flow.\n const collected = Math.min(Math.max(collectedRaw, 0), saleAmount);\n const saleDue = Math.max(saleAmount - collected, 0);\n const remainingDue = saleDue + previousDue;\n\n // -------- Validation --------\n const headerErrors = {\n saleDate: validateRequired(form.saleDate, \"Sale date\"),\n member: memberId ? \"\" : \"No member selected\",\n };\n // Collecting Now: optional; if filled, must be a non-negative number ≤ sale amount.\n const collectingErr =\n form.collectingNow === \"\" ? \"\" : validatePrice(form.collectingNow);\n if (\n !collectingErr &&\n form.collectingNow !== \"\" &&\n Number(form.collectingNow) > saleAmount\n ) {\n headerErrors.collectingNow = `Cannot collect more than the sale amount (${formatNumber(saleAmount)}). Settle old dues via Payments.`;\n } else if (collectingErr) {\n headerErrors.collectingNow = collectingErr;\n }\n\n const sellableBatchIds = new Set(sellableBatches.map((b) => b.id));\n const lineErrors = form.items.map((line) => {\n const batch = batchById(line.batchId);\n const qty = validateInteger(line.quantity, \"Quantity\", {\n required: true,\n min: 1,\n });\n let qtyErr = qty;\n if (!qtyErr && batch && Number(line.quantity) > batch.qtyRemaining) {\n qtyErr = `Only ${batch.qtyRemaining} available in selected batch`;\n }\n let batchErr = validateRequired(line.batchId, \"Batch\");\n if (!batchErr && line.batchId && !sellableBatchIds.has(line.batchId)) {\n batchErr =\n \"Batch is not available for sale (expired, quarantined, out of stock, or not-for-sale).\";\n }\n return {\n batchId: batchErr,\n quantity: qtyErr,\n salePrice:\n validatePrice(line.salePrice) ||\n validateRequired(line.salePrice, \"Unit price\"),\n };\n });\n const formIsValid =\n isClean(headerErrors) &&\n lineErrors.every(isClean) &&\n form.items.length > 0 &&\n saleAmount > 0;\n\n async function save() {\n if (!formIsValid) {\n fieldErrors.markAllTouched();\n setError(\"Please fix the highlighted fields and try again.\");\n return;\n }\n try {\n await createSale({\n id: form.id,\n saleDate: form.saleDate,\n memberId,\n paymentMode: form.paymentMode || null,\n totalAmount: Number(saleAmount.toFixed(2)),\n paidAmount: Number(collected.toFixed(2)),\n dueAmount: Number(saleDue.toFixed(2)),\n notes: form.notes || null,\n items: form.items.map((l) => {\n const batch = batchById(l.batchId);\n return {\n batchId: l.batchId,\n productId: batch?.productId,\n quantity: Number(l.quantity),\n salePrice: Number(l.salePrice),\n };\n }),\n });\n showToast({ message: \"Sale recorded.\", type: \"success\" });\n navigate(\"/ui/medplat/manage/pm/sales\");\n } catch (err) {\n setError(\n err?.response?.data?.message ?? err?.message ?? \"Failed to record sale\",\n );\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard\n perm={\"update.manage.productManagement.sale\"}\n title=\"You don’t have permission to record sales\"\n >\n <PmPageShell\n title=\"Record Sale\"\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"outlined\"\n onClick={() => navigate(\"/ui/medplat/manage/pm/sales\")}\n >\n Cancel\n </Button>\n <Button\n variant=\"contained\"\n startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}\n >\n Save\n </Button>\n </Stack>\n }\n >\n {error ? (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {error}\n </Alert>\n ) : null}\n\n {/* ---------- Member ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n mb: 2,\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 1\"\n title=\"Customer\"\n hint=\"The customer is set from the link you opened. Their balance shows below.\"\n sx={{ mb: 2 }}\n />\n {memberId ? (\n <MemberProfileCard\n memberId={memberId}\n previousDue={previousDue}\n currencySymbol={cur}\n onLoad={(p) => setMemberName(p?.name ?? \"\")}\n />\n ) : (\n <Alert severity=\"warning\">No member selected</Alert>\n )}\n </CardContent>\n </Card>\n\n {/* ---------- Line items ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n mb: 2,\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"flex-start\"\n sx={{ mb: 2 }}\n >\n <PmFormSection\n eyebrow=\"Step 2\"\n title=\"Line items\"\n hint=\"Each item comes from a batch. Buy price is shown as a cost reference.\"\n />\n <Button\n variant=\"outlined\"\n startIcon={<AddRoundedIcon />}\n onClick={() =>\n setForm({ ...form, items: [...form.items, emptyLine()] })\n }\n >\n Add Line\n </Button>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Stack spacing={2}>\n {form.items.map((line, idx) => {\n const batch = batchById(line.batchId);\n const lerr = lineErrors[idx] || {};\n const subtotal =\n line.salePrice && line.quantity\n ? Number(line.salePrice) * Number(line.quantity)\n : 0;\n return (\n <Card\n key={line.key}\n variant=\"outlined\"\n sx={{ p: 2, borderRadius: 3 }}\n >\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs={12} md={5}>\n <FormControl\n required\n fullWidth\n size=\"small\"\n error={\n fieldErrors.show(`items.${idx}.batchId`) &&\n !!lerr.batchId\n }\n >\n <InputLabel>Batch</InputLabel>\n <Select\n label=\"Batch\"\n value={line.batchId}\n onChange={(e) =>\n patchLine(idx, \"batchId\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(\n `items.${idx}.batchId`,\n )}\n >\n {sellableBatches.map((b) => (\n <MenuItem key={b.id} value={b.id}>\n {b.productName} · lot {b.lotNumber || \"—\"} ·{\" \"}\n {b.qtyRemaining} left\n </MenuItem>\n ))}\n </Select>\n <FormHelperText>\n {fieldErrors.show(`items.${idx}.batchId`) &&\n lerr.batchId\n ? lerr.batchId\n : batch?.buyPrice != null\n ? `Buy price: ${cur}${formatNumber(batch.buyPrice)} / unit`\n : \" \"}\n </FormHelperText>\n </FormControl>\n </Grid>\n <Grid item xs={6} md={2}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"number\"\n label=\"Qty\"\n inputProps={{ min: 1, max: batch?.qtyRemaining }}\n value={line.quantity}\n onChange={(e) =>\n patchLine(idx, \"quantity\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(`items.${idx}.quantity`)}\n error={\n fieldErrors.show(`items.${idx}.quantity`) &&\n !!lerr.quantity\n }\n helperText={\n fieldErrors.show(`items.${idx}.quantity`)\n ? lerr.quantity\n : \"\"\n }\n />\n </Grid>\n <Grid item xs={6} md={2}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"number\"\n inputProps={{ step: \"0.01\", min: 0 }}\n label=\"Unit Price\"\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n {cur}\n </InputAdornment>\n ),\n }}\n value={line.salePrice}\n onChange={(e) =>\n patchLine(idx, \"salePrice\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(\n `items.${idx}.salePrice`,\n )}\n error={\n fieldErrors.show(`items.${idx}.salePrice`) &&\n !!lerr.salePrice\n }\n helperText={\n fieldErrors.show(`items.${idx}.salePrice`)\n ? lerr.salePrice\n : \"\"\n }\n />\n </Grid>\n <Grid item xs={6} md={2} sx={{ textAlign: \"right\" }}>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: \"block\" }}\n >\n Subtotal\n </Typography>\n <Typography\n variant=\"body1\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n {cur}\n {formatNumber(subtotal)}\n </Typography>\n </Grid>\n <Grid item xs={6} md={1} sx={{ textAlign: \"right\" }}>\n <IconButton\n color=\"error\"\n onClick={() =>\n setForm({\n ...form,\n items: form.items.filter((_, i) => i !== idx),\n })\n }\n disabled={form.items.length === 1}\n >\n <DeleteOutlineRoundedIcon />\n </IconButton>\n </Grid>\n </Grid>\n </Card>\n );\n })}\n </Stack>\n </CardContent>\n </Card>\n\n {/* ---------- Bill Summary footer ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n bgcolor: \"grey.50\",\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 3\"\n title=\"Bill summary\"\n hint=\"Shows totals, previous balance, and what you're collecting. Any remainder becomes a note of what they owe.\"\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n <Grid item xs={12} md={4}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"date\"\n label=\"Date\"\n InputLabelProps={{ shrink: true }}\n value={form.saleDate}\n onChange={(e) =>\n setForm({ ...form, saleDate: e.target.value })\n }\n onBlur={fieldErrors.onBlur(\"saleDate\")}\n error={\n fieldErrors.show(\"saleDate\") && !!headerErrors.saleDate\n }\n helperText={\n fieldErrors.show(\"saleDate\") ? headerErrors.saleDate : \"\"\n }\n />\n </Grid>\n <Grid item xs={12} md={4}>\n <FormControl fullWidth size=\"small\">\n <InputLabel>Payment Mode</InputLabel>\n <Select\n label=\"Payment Mode\"\n value={form.paymentMode || \"\"}\n onChange={(e) =>\n setForm({ ...form, paymentMode: e.target.value })\n }\n >\n <MenuItem value=\"\">\n <em>—</em>\n </MenuItem>\n {PAYMENT_MODES.map((m) => (\n <MenuItem key={m.value} value={m.value}>\n {m.label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Grid>\n <Grid item xs={12} md={4}>\n <TextField\n fullWidth\n size=\"small\"\n label=\"Notes\"\n value={form.notes}\n onChange={(e) =>\n setForm({ ...form, notes: e.target.value })\n }\n />\n </Grid>\n </Grid>\n\n <Divider sx={{ my: 2 }} />\n\n <Stack spacing={1.25}>\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Sale Amount ({form.items.length}{\" \"}\n {form.items.length === 1 ? \"line\" : \"lines\"})\n </Typography>\n <Typography\n variant=\"h5\"\n sx={{ fontWeight: 800, fontVariantNumeric: \"tabular-nums\" }}\n >\n {cur}\n {formatNumber(saleAmount)}\n </Typography>\n </Stack>\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Previous Due\n </Typography>\n <Typography\n variant=\"body1\"\n sx={{\n fontWeight: 700,\n fontVariantNumeric: \"tabular-nums\",\n color: previousDue > 0 ? \"warning.dark\" : \"text.primary\",\n }}\n >\n {cur}\n {formatNumber(previousDue)}\n </Typography>\n </Stack>\n\n <Divider sx={{ my: 0.5 }} />\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n spacing={2}\n >\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ flexShrink: 0 }}\n >\n Amount Collecting Now\n </Typography>\n <TextField\n size=\"small\"\n type=\"number\"\n inputProps={{\n step: \"0.01\",\n min: 0,\n max: saleAmount,\n style: { textAlign: \"right\", fontWeight: 700 },\n }}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">{cur}</InputAdornment>\n ),\n }}\n sx={{ width: 200 }}\n value={form.collectingNow}\n placeholder=\"0.00\"\n onChange={(e) =>\n setForm({ ...form, collectingNow: e.target.value })\n }\n onBlur={fieldErrors.onBlur(\"collectingNow\")}\n error={\n fieldErrors.show(\"collectingNow\") &&\n !!headerErrors.collectingNow\n }\n helperText={\n fieldErrors.show(\"collectingNow\")\n ? headerErrors.collectingNow\n : \"\"\n }\n />\n </Stack>\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Remaining Due (after this sale)\n </Typography>\n <Typography\n variant=\"h5\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n color: remainingDue > 0 ? \"warning.dark\" : \"success.dark\",\n }}\n >\n {cur}\n {formatNumber(remainingDue)}\n </Typography>\n </Stack>\n </Stack>\n\n {memberId && remainingDue > 0 ? (\n <Alert severity=\"warning\" sx={{ mt: 2 }}>\n {cur}\n {formatNumber(remainingDue)} will be recorded as pending dues\n {memberName ? (\n <>\n {\" \"}\n for <strong>{memberName}</strong>\n </>\n ) : null}\n {saleDue > 0 && previousDue > 0\n ? ` (${cur}${formatNumber(saleDue)} from this sale + ${cur}${formatNumber(previousDue)} previously outstanding).`\n : saleDue > 0\n ? \" from this sale.\"\n : \" from previously outstanding dues.\"}\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
- import { G as e, O as t, a as n, c as r, n as i, r as a, t as o } from "./PmGuard-CjJBisaN.js";
2
- import { t as s } from "./currency-BDDCDgeE.js";
3
- import { a as ee, i as c, o as l, r as u, t as d } from "./PmTable-BuXZQZCU.js";
1
+ import { K as e, i as t, k as n, l as r, n as i, o as a, r as o, t as s } from "./currency-ClRLIEg2.js";
2
+ import { a as ee, c, i as l, s as u, t as d } from "./PmTable-B9970kPY.js";
4
3
  import { r as f, t as p } from "./format-UOyOu75i.js";
5
4
  import { useMemo as m, useState as h } from "react";
6
5
  import { jsx as g, jsxs as _ } from "react/jsx-runtime";
@@ -79,15 +78,15 @@ function V() {
79
78
  async function Q() {
80
79
  Y(!0), Z("");
81
80
  try {
82
- let n = await e(0, 50);
83
- G(n);
84
- let r = Array.from(new Set(n.map((e) => e.memberId).filter(Boolean)));
81
+ let t = await e(0, 50);
82
+ G(t);
83
+ let r = Array.from(new Set(t.map((e) => e.memberId).filter(Boolean)));
85
84
  if (r.length === 0) q({});
86
85
  else try {
87
- let e = await t(r), n = {};
86
+ let e = await n(r), t = {};
88
87
  e.forEach((e) => {
89
- n[e.id] = e.name;
90
- }), q(n);
88
+ t[e.id] = e.name;
89
+ }), q(t);
91
90
  } catch {
92
91
  q({});
93
92
  }
@@ -101,7 +100,7 @@ function V() {
101
100
  Y(!1);
102
101
  }
103
102
  }
104
- a("view.manage.productManagement.sale", () => {
103
+ t("view.manage.productManagement.sale", () => {
105
104
  Q();
106
105
  }, []);
107
106
  let $ = m(() => ({
@@ -110,9 +109,9 @@ function V() {
110
109
  openSales: W.filter((e) => Number(e.dueAmount) > 0).length,
111
110
  count: W.length
112
111
  }), [W]);
113
- return /* @__PURE__ */ _(b, { children: [/* @__PURE__ */ g(i, {}), /* @__PURE__ */ g(o, {
112
+ return /* @__PURE__ */ _(b, { children: [/* @__PURE__ */ g(o, {}), /* @__PURE__ */ g(i, {
114
113
  perm: "view.manage.productManagement.sale",
115
- children: /* @__PURE__ */ _(n, {
114
+ children: /* @__PURE__ */ _(a, {
116
115
  title: "Sales",
117
116
  subtitle: "Customer sales and payments. Each sale is tracked with the batches used.",
118
117
  actions: /* @__PURE__ */ _(w, {
@@ -169,11 +168,11 @@ function V() {
169
168
  sx: { mb: 2 },
170
169
  children: X
171
170
  }) : null,
172
- /* @__PURE__ */ g(l, {
171
+ /* @__PURE__ */ g(c, {
173
172
  eyebrow: "Recent activity",
174
173
  title: "Sales transactions",
175
174
  subtitle: J ? "Loading…" : `${$.count} ${$.count === 1 ? "sale" : "sales"} in the last window`,
176
- children: /* @__PURE__ */ _(ee, {
175
+ children: /* @__PURE__ */ _(u, {
177
176
  dense: !0,
178
177
  children: [/* @__PURE__ */ g(te, { children: /* @__PURE__ */ _(D, { children: [
179
178
  /* @__PURE__ */ g(E, { children: "Ref" }),
@@ -209,7 +208,7 @@ function V() {
209
208
  return /* @__PURE__ */ _(D, {
210
209
  hover: !0,
211
210
  children: [
212
- /* @__PURE__ */ g(E, { children: /* @__PURE__ */ g(c, { id: e.serialId }) }),
211
+ /* @__PURE__ */ g(E, { children: /* @__PURE__ */ g(ee, { id: e.serialId }) }),
213
212
  /* @__PURE__ */ g(E, { children: /* @__PURE__ */ g(O, {
214
213
  variant: "body2",
215
214
  sx: { fontWeight: 700 },
@@ -274,11 +273,11 @@ function V() {
274
273
  }) }),
275
274
  /* @__PURE__ */ g(E, {
276
275
  align: "right",
277
- children: /* @__PURE__ */ g(u, { value: e.totalAmount })
276
+ children: /* @__PURE__ */ g(l, { value: e.totalAmount })
278
277
  }),
279
278
  /* @__PURE__ */ g(E, {
280
279
  align: "right",
281
- children: /* @__PURE__ */ g(u, {
280
+ children: /* @__PURE__ */ g(l, {
282
281
  value: e.paidAmount,
283
282
  tone: n ? "success" : "muted"
284
283
  })
@@ -294,7 +293,7 @@ function V() {
294
293
  borderRadius: 1.5,
295
294
  height: 22
296
295
  }
297
- }) : /* @__PURE__ */ g(u, {
296
+ }) : /* @__PURE__ */ g(l, {
298
297
  value: t,
299
298
  tone: "warning",
300
299
  size: "big"
@@ -312,4 +311,4 @@ function V() {
312
311
  //#endregion
313
312
  export { V as default };
314
313
 
315
- //# sourceMappingURL=SalesPage-DUwgkEO-.js.map
314
+ //# sourceMappingURL=SalesPage-CO3cdYX5.js.map