@argusoft/medplat-product-management 1.0.0

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 (65) hide show
  1. package/dist/BrandsPage-DCMCVvDY.js +334 -0
  2. package/dist/BrandsPage-DCMCVvDY.js.map +1 -0
  3. package/dist/CategoriesPage-BpRhwfrc.js +284 -0
  4. package/dist/CategoriesPage-BpRhwfrc.js.map +1 -0
  5. package/dist/DashboardPage-BNKUQj46.js +436 -0
  6. package/dist/DashboardPage-BNKUQj46.js.map +1 -0
  7. package/dist/FacilityPicker-Coi8I3yE.js +79 -0
  8. package/dist/FacilityPicker-Coi8I3yE.js.map +1 -0
  9. package/dist/InventoryPage-BR6jOzg2.js +517 -0
  10. package/dist/InventoryPage-BR6jOzg2.js.map +1 -0
  11. package/dist/MemberProfileCard-DbHZCS5n.js +272 -0
  12. package/dist/MemberProfileCard-DbHZCS5n.js.map +1 -0
  13. package/dist/PaymentCreatePage-Ci3ERnby.js +487 -0
  14. package/dist/PaymentCreatePage-Ci3ERnby.js.map +1 -0
  15. package/dist/PaymentsPage-sdg902zX.js +157 -0
  16. package/dist/PaymentsPage-sdg902zX.js.map +1 -0
  17. package/dist/PickupCreatePage-CieMsgWV.js +310 -0
  18. package/dist/PickupCreatePage-CieMsgWV.js.map +1 -0
  19. package/dist/PickupsPage-Ci2Ll0Kn.js +566 -0
  20. package/dist/PickupsPage-Ci2Ll0Kn.js.map +1 -0
  21. package/dist/PmGuard-CjJBisaN.js +1195 -0
  22. package/dist/PmGuard-CjJBisaN.js.map +1 -0
  23. package/dist/PmTable-BuXZQZCU.js +302 -0
  24. package/dist/PmTable-BuXZQZCU.js.map +1 -0
  25. package/dist/ProductEditPage-DL8gy85Y.js +666 -0
  26. package/dist/ProductEditPage-DL8gy85Y.js.map +1 -0
  27. package/dist/ProductsPage-BANTKxLb.js +221 -0
  28. package/dist/ProductsPage-BANTKxLb.js.map +1 -0
  29. package/dist/PurchaseCreatePage-C3KMfBxJ.js +626 -0
  30. package/dist/PurchaseCreatePage-C3KMfBxJ.js.map +1 -0
  31. package/dist/PurchaseRequestDetailPage-C0GYFJDl.js +586 -0
  32. package/dist/PurchaseRequestDetailPage-C0GYFJDl.js.map +1 -0
  33. package/dist/PurchaseRequestsPage-DvFH_tWe.js +265 -0
  34. package/dist/PurchaseRequestsPage-DvFH_tWe.js.map +1 -0
  35. package/dist/PurchasesPage-BCvLvSp4.js +295 -0
  36. package/dist/PurchasesPage-BCvLvSp4.js.map +1 -0
  37. package/dist/SaleCreatePage-DpkQMoTq.js +548 -0
  38. package/dist/SaleCreatePage-DpkQMoTq.js.map +1 -0
  39. package/dist/SalesPage-DUwgkEO-.js +315 -0
  40. package/dist/SalesPage-DUwgkEO-.js.map +1 -0
  41. package/dist/SettingsPage-CusC0jqJ.js +710 -0
  42. package/dist/SettingsPage-CusC0jqJ.js.map +1 -0
  43. package/dist/StorageUnitsPage-BlC2AgrJ.js +469 -0
  44. package/dist/StorageUnitsPage-BlC2AgrJ.js.map +1 -0
  45. package/dist/TransactionsPage-DDaK4VyY.js +714 -0
  46. package/dist/TransactionsPage-DDaK4VyY.js.map +1 -0
  47. package/dist/TransferCreatePage-BxudLfhS.js +363 -0
  48. package/dist/TransferCreatePage-BxudLfhS.js.map +1 -0
  49. package/dist/TransfersPage-zgmt7teZ.js +480 -0
  50. package/dist/TransfersPage-zgmt7teZ.js.map +1 -0
  51. package/dist/VialsPage-CjqPfgpV.js +855 -0
  52. package/dist/VialsPage-CjqPfgpV.js.map +1 -0
  53. package/dist/constants-BrA_hfAI.js +254 -0
  54. package/dist/constants-BrA_hfAI.js.map +1 -0
  55. package/dist/currency-BDDCDgeE.js +169 -0
  56. package/dist/currency-BDDCDgeE.js.map +1 -0
  57. package/dist/format-UOyOu75i.js +52 -0
  58. package/dist/format-UOyOu75i.js.map +1 -0
  59. package/dist/index.js +45 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/useFieldErrors-hDG8ox_x.js +29 -0
  62. package/dist/useFieldErrors-hDG8ox_x.js.map +1 -0
  63. package/dist/validation-Ba7puFIO.js +109 -0
  64. package/dist/validation-Ba7puFIO.js.map +1 -0
  65. package/package.json +36 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PurchaseCreatePage-C3KMfBxJ.js","names":[],"sources":["../src/views/PurchaseCreatePage.jsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Chip,\n Divider,\n FormControl,\n FormHelperText,\n Grid,\n IconButton,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport DeleteOutlineRoundedIcon from '@mui/icons-material/DeleteOutlineRounded';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport SaveRoundedIcon from '@mui/icons-material/SaveRounded';\nimport Inventory2RoundedIcon from '@mui/icons-material/Inventory2Rounded';\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 {\n createPurchase,\n getProducts,\n getStorageUnits,\n listBatches,\n submitPurchaseRequest,\n} from '@pm/services/pmApi';\nimport FacilityPicker from '@pm/components/FacilityPicker';\nimport { BATCH_STATUSES, PAYMENT_MODES, labelOf } from '@pm/utils/constants';\nimport {\n isClean,\n validateFutureDate,\n validatePastOrTodayDate,\n validatePrice,\n validateQuantity,\n validateRequired,\n} from '@pm/utils/validation';\nimport { formatDate, formatNumber, statusColor } from '@pm/utils/format';\nimport { PmFormSection } from '@pm/components/PmTable';\nimport { useFieldErrors } from '@pm/utils/useFieldErrors';\nimport { usePermissionedEffect } from '@pm/utils/permissions';\nimport { usePmSettings } from '@pm/utils/settingsStore';\n\n// Purchase type → backend owner_type mapping. \"Personal Use\" puts the stock\n// against the acting user; \"Facility Use\" puts it against a facility the user\n// has access to. The Personal option is filtered out when the deployment-level\n// allowPersonalPurchase setting is off (see PURCHASE_TYPES filter below).\nconst ALL_PURCHASE_TYPES = [\n { value: 'USER', label: 'Personal Use' },\n { value: 'FACILITY', label: 'Facility Use' },\n];\n\nfunction emptyLine() {\n return {\n key: uuidv4(),\n batchId: uuidv4(),\n productId: '',\n quantity: 1,\n buyPrice: '',\n expiryDate: '',\n lotNumber: '',\n manufactureDate: '',\n storageUnitId: '',\n };\n}\n\nexport default function PurchaseCreatePage() {\n const navigate = useNavigate();\n const settings = usePmSettings();\n const manageAmounts = settings?.managePurchaseAmounts !== false;\n const allowPersonalPurchase = settings?.allowPersonalPurchase !== false;\n // When confirmation is on, the form submits to the request endpoint and\n // the user lands on the request detail page instead of the purchases list.\n const requireConfirmation = settings?.requirePurchaseConfirmation === true;\n // Personal Use is hidden entirely when the deployment doesn't allow it; the\n // dropdown then has a single option and the form defaults to FACILITY.\n const PURCHASE_TYPES = useMemo(\n () => (allowPersonalPurchase ? ALL_PURCHASE_TYPES : ALL_PURCHASE_TYPES.filter((t) => t.value !== 'USER')),\n [allowPersonalPurchase],\n );\n const [products, setProducts] = useState([]);\n const [storageUnits, setStorageUnits] = useState([]);\n const [batches, setBatches] = useState([]);\n const [error, setError] = useState('');\n const fieldErrors = useFieldErrors();\n const today = new Date().toISOString().substring(0, 10);\n const [form, setForm] = useState({\n id: uuidv4(),\n ownerType: allowPersonalPurchase ? 'USER' : 'FACILITY',\n ownerId: '',\n purchaseDate: today,\n paymentMode: 'CASH',\n notes: '',\n items: [emptyLine()],\n });\n\n // When the deployment setting flips from allowing personal purchases to not\n // allowing them (or vice-versa) after the form has rendered, snap the\n // ownerType back to a valid choice so the user can't end up submitting USER\n // against a deployment that doesn't accept it.\n useEffect(() => {\n if (!allowPersonalPurchase && form.ownerType === 'USER') {\n setForm((f) => ({ ...f, ownerType: 'FACILITY', ownerId: '' }));\n }\n }, [allowPersonalPurchase, form.ownerType]);\n\n usePermissionedEffect('update.manage.productManagement.purchase', () => {\n async function load() {\n try {\n const [p, su, b] = await Promise.all([\n getProducts(),\n getStorageUnits({ activeOnly: true }),\n // Pulled here so the existing-inventory panel renders alongside the\n // purchase form — the operator can see what they / the facility\n // already hold before deciding what to buy.\n listBatches().catch(() => []),\n ]);\n setProducts(p);\n setStorageUnits(su);\n setBatches(b);\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to load reference data');\n }\n }\n void load();\n }, []);\n\n // Existing-stock view for the currently-selected owner. Personal purchases\n // show the operator's USER-owned batches; facility purchases show batches\n // owned by the picked facility. Exhausted/expired rows are skipped to keep\n // the panel decision-relevant.\n const existingInventory = useMemo(() => {\n if (form.ownerType === 'USER') {\n return batches.filter((b) => b.ownerType === 'USER' && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n if (form.ownerType === 'FACILITY' && form.ownerId) {\n return batches.filter((b) => b.ownerType === 'FACILITY' && b.ownerId === form.ownerId\n && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n return [];\n }, [batches, form.ownerType, form.ownerId]);\n\n // Storage units filtered by the selected facility (when applicable).\n // Personal purchases don't reference a storage unit at all.\n const eligibleStorageUnits = useMemo(() => {\n if (form.ownerType !== 'FACILITY' || !form.ownerId) return [];\n return storageUnits.filter((u) => u.ownerId === form.ownerId);\n }, [storageUnits, form.ownerType, form.ownerId]);\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 function removeLine(idx) {\n setForm((f) => ({ ...f, items: f.items.filter((_, i) => i !== idx) }));\n }\n\n function addLine() {\n setForm((f) => ({ ...f, items: [...f.items, emptyLine()] }));\n }\n\n // Sum of (qty * buy price) for lines that have a price. Used to validate\n // the optional Total Amount header field.\n const computedLineTotal = useMemo(() => {\n let priced = false;\n const sum = form.items.reduce((acc, line) => {\n if (line.buyPrice === '' || line.quantity === '' || line.quantity == null) return acc;\n const qty = Number(line.quantity);\n const price = Number(line.buyPrice);\n if (Number.isNaN(qty) || Number.isNaN(price)) return acc;\n priced = true;\n return acc + qty * price;\n }, 0);\n return { sum, priced };\n }, [form.items]);\n\n // Header + per-line errors. The total is auto-derived from the line items so it\n // never needs its own validation message.\n const headerErrors = {\n ownerType: validateRequired(form.ownerType, 'Purchase type'),\n ownerId: form.ownerType === 'FACILITY' ? validateRequired(form.ownerId, 'Facility') : '',\n purchaseDate: validateRequired(form.purchaseDate, 'Purchase date'),\n };\n const lineErrors = form.items.map((line) => {\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n // Storage units only apply to facility-owned stock — personal purchases keep\n // the field disabled, so the requirement is only enforced for facility flows.\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n let storageErr = storageRequired ? validateRequired(line.storageUnitId, 'Storage unit') : '';\n // Cold-chain check: if the product has a vaccine config with a required\n // storage range, the selected storage unit's operating range must fit\n // inside it. Mirror the backend STORAGE_UNIT_TEMP_MISMATCH rule.\n if (!storageErr && line.storageUnitId && product?.vaccineConfig) {\n const cfg = product.vaccineConfig;\n const unit = storageUnits.find((u) => u.id === line.storageUnitId);\n if (cfg.storageTempMinCelsius != null && cfg.storageTempMaxCelsius != null && unit) {\n const uMin = unit.operatingTempMinCelsius;\n const uMax = unit.operatingTempMaxCelsius;\n // Unit must be able to reach every temp the vaccine tolerates: its\n // operating range must contain the vaccine's required range.\n if (uMin == null || uMax == null\n || Number(uMin) > Number(cfg.storageTempMinCelsius)\n || Number(uMax) < Number(cfg.storageTempMaxCelsius)) {\n storageErr = `Unit operates ${uMin ?? '?'}°C → ${uMax ?? '?'}°C, which cannot cover this product's required ${cfg.storageTempMinCelsius}°C → ${cfg.storageTempMaxCelsius}°C.`;\n }\n }\n }\n return {\n productId: validateRequired(line.productId, 'Product'),\n quantity: validateQuantity(line.quantity),\n buyPrice: validatePrice(line.buyPrice),\n // In request mode the approver fills expiry at confirmation, so we\n // don't require it from the requester even when the product carries\n // hasExpiry. The approver gets to enter the actual expiry off the\n // packaging on PurchaseRequestDetailPage.\n expiryDate: requireConfirmation\n ? ''\n : expiryRequired\n ? (validateRequired(line.expiryDate, 'Expiry date')\n || validateFutureDate(line.expiryDate, { label: 'Expiry' }))\n : validateFutureDate(line.expiryDate, { label: 'Expiry' }),\n manufactureDate: validatePastOrTodayDate(line.manufactureDate, { label: 'Manufacture date' }),\n storageUnitId: storageErr,\n };\n });\n const formIsValid = isClean(headerErrors) && lineErrors.every(isClean) && form.items.length > 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 // Payload shape is the same for direct purchase and request submission —\n // the difference is the endpoint and what we navigate to afterwards.\n const payload = {\n id: form.id,\n ownerType: form.ownerType,\n ownerId: form.ownerType === 'FACILITY' ? form.ownerId : null,\n purchaseDate: form.purchaseDate,\n paymentMode: manageAmounts ? (form.paymentMode || null) : null,\n // Amount tracking is a per-deployment toggle. When it's off, scrub money\n // values to null so the backend's validatePurchaseTotal stays a no-op\n // and inventory rows don't carry stale prices that aren't surfaced anywhere.\n totalAmount: manageAmounts && computedLineTotal.priced ? Number(computedLineTotal.sum.toFixed(2)) : null,\n notes: form.notes || null,\n items: form.items.map((line) => ({\n batchId: line.batchId,\n productId: line.productId,\n quantity: Number(line.quantity),\n buyPrice: manageAmounts && line.buyPrice !== '' ? Number(line.buyPrice) : null,\n expiryDate: line.expiryDate || null,\n lotNumber: line.lotNumber || null,\n manufactureDate: line.manufactureDate || null,\n storageUnitId: line.storageUnitId || null,\n })),\n };\n try {\n if (requireConfirmation) {\n const submitted = await submitPurchaseRequest(payload);\n showToast({ message: 'Purchase request submitted for approval.', type: 'success' });\n navigate(`/ui/medplat/manage/pm/purchase-requests/${submitted?.id ?? form.id}`);\n } else {\n await createPurchase(payload);\n showToast({ message: 'Purchase recorded.', type: 'success' });\n navigate('/ui/medplat/manage/pm/purchases');\n }\n } catch (err) {\n setError(err?.response?.data?.message\n ?? err?.message\n ?? (requireConfirmation ? 'Failed to submit purchase request' : 'Failed to record purchase'));\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard\n perm={'update.manage.productManagement.purchase'}\n title={requireConfirmation\n ? \"You don’t have permission to submit purchase requests\"\n : \"You don’t have permission to record purchases\"}>\n <PmPageShell\n title={requireConfirmation ? 'Submit Purchase Request' : 'Record Purchase'}\n subtitle={requireConfirmation\n ? 'Purchases need approval before stock is added. Submit the request and wait for a manager to confirm.'\n : 'Each item creates a batch that you can track and a record in your ledger.'}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\"\n onClick={() => navigate(requireConfirmation\n ? '/ui/medplat/manage/pm/purchase-requests'\n : '/ui/medplat/manage/pm/purchases')}>Cancel</Button>\n <Button variant=\"contained\" startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}>{requireConfirmation ? 'Submit Request' : 'Save'}</Button>\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n {/* ---------- Ownership ---------- */}\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 1\"\n title=\"Purchase for\"\n hint=\"Choose who will own this stock: yourself or a facility.\"\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n <Grid item xs={12} md={4}>\n <FormControl required fullWidth size=\"small\"\n error={fieldErrors.show('ownerType') && !!headerErrors.ownerType}>\n <InputLabel>Purchase Type</InputLabel>\n <Select label=\"Purchase Type\" value={form.ownerType}\n onChange={(e) => setForm({ ...form, ownerType: e.target.value, ownerId: '' })}\n onBlur={fieldErrors.onBlur('ownerType')}>\n {PURCHASE_TYPES.map((t) => (\n <MenuItem key={t.value} value={t.value}>{t.label}</MenuItem>\n ))}\n </Select>\n <FormHelperText>\n {fieldErrors.show('ownerType') && headerErrors.ownerType\n ? headerErrors.ownerType\n : 'Personal stock vs. facility stock'}\n </FormHelperText>\n </FormControl>\n </Grid>\n {form.ownerType === 'FACILITY' ? (\n <Grid item xs={12} md={4}>\n <FacilityPicker\n required\n value={form.ownerId}\n onChange={(id) => setForm({ ...form, ownerId: id })}\n onBlur={fieldErrors.onBlur('ownerId')}\n error={fieldErrors.show('ownerId') && !!headerErrors.ownerId}\n helperText={\n fieldErrors.show('ownerId') && headerErrors.ownerId\n ? headerErrors.ownerId\n : 'Search for the facility.'\n }\n />\n </Grid>\n ) : null}\n </Grid>\n </CardContent>\n </Card>\n\n {/* ---------- Existing inventory for the picked owner ---------- */}\n {(form.ownerType === 'USER' || (form.ownerType === 'FACILITY' && form.ownerId)) ? (\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Context\"\n title=\"Existing inventory\"\n hint={form.ownerType === 'USER'\n ? 'Your current stock.'\n : 'What this facility already has, including where it is stored.'}\n sx={{ mb: 1.5 }}\n />\n {existingInventory.length === 0 ? (\n <Stack direction=\"row\" spacing={1.25} alignItems=\"center\" sx={{ py: 1.5, color: 'text.secondary' }}>\n <Inventory2RoundedIcon fontSize=\"small\" />\n <Typography variant=\"body2\">No active stock yet for this owner.</Typography>\n </Stack>\n ) : (\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell align=\"right\">Remaining</TableCell>\n <TableCell>Expiry</TableCell>\n {form.ownerType === 'FACILITY' ? <TableCell>Storage</TableCell> : null}\n <TableCell>Status</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {existingInventory.map((b) => (\n <TableRow key={b.id}>\n <TableCell sx={{ fontWeight: 700 }}>{b.productName}</TableCell>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: '0.78rem' }}>{b.lotNumber || '—'}</TableCell>\n <TableCell align=\"right\" sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 700 }}>\n {b.qtyRemaining}\n </TableCell>\n <TableCell>{b.expiryDate ? formatDate(b.expiryDate) : '—'}</TableCell>\n {form.ownerType === 'FACILITY' ? (\n <TableCell>{b.storageUnitName || '—'}</TableCell>\n ) : null}\n <TableCell>\n <Chip size=\"small\" label={labelOf(BATCH_STATUSES, b.status)} color={statusColor(b.status)} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n ) : null}\n\n {/* ---------- Lines ---------- */}\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" sx={{ mb: 2 }}>\n <PmFormSection\n eyebrow=\"Step 2\"\n title=\"Line items\"\n hint=\"Each item creates one batch in your inventory and a receipt record.\"\n />\n <Button variant=\"outlined\" startIcon={<AddRoundedIcon />} onClick={addLine}>Add Line</Button>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Stack spacing={2}>\n {form.items.map((line, idx) => {\n const lerr = lineErrors[idx] || {};\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n return (\n <Card key={line.key} variant=\"outlined\" sx={{ p: 2, borderRadius: 3 }}>\n <Grid container spacing={2}>\n <Grid item xs={12} md={3}>\n <FormControl required fullWidth size=\"small\"\n error={fieldErrors.show(`items.${idx}.productId`) && !!lerr.productId}>\n <InputLabel>Product</InputLabel>\n <Select label=\"Product\" value={line.productId}\n onChange={(e) => patchLine(idx, 'productId', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.productId`)}>\n {products.map((p) => <MenuItem key={p.id} value={p.id}>{p.name}</MenuItem>)}\n </Select>\n {fieldErrors.show(`items.${idx}.productId`) && lerr.productId\n ? <FormHelperText>{lerr.productId}</FormHelperText> : null}\n </FormControl>\n </Grid>\n <Grid item xs={6} md={1}>\n <TextField required fullWidth size=\"small\" type=\"number\" label=\"Qty\"\n inputProps={{ min: 1 }}\n value={line.quantity}\n onChange={(e) => patchLine(idx, 'quantity', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.quantity`)}\n error={fieldErrors.show(`items.${idx}.quantity`) && !!lerr.quantity}\n helperText={fieldErrors.show(`items.${idx}.quantity`) ? lerr.quantity : ''} />\n </Grid>\n {manageAmounts ? (\n <Grid item xs={6} md={2}>\n <TextField fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.01', min: 0 }}\n label=\"Buy Price\"\n value={line.buyPrice}\n onChange={(e) => patchLine(idx, 'buyPrice', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.buyPrice`)}\n error={fieldErrors.show(`items.${idx}.buyPrice`) && !!lerr.buyPrice}\n helperText={fieldErrors.show(`items.${idx}.buyPrice`) ? lerr.buyPrice : ''} />\n </Grid>\n ) : null}\n {/* Expiry is captured at confirmation time alongside Lot —\n the requester is asking for stock they don't yet have,\n so they can't read the expiry off the packaging. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField required={expiryRequired} fullWidth size=\"small\" type=\"date\" label=\"Expiry\"\n InputLabelProps={{ shrink: true }}\n inputProps={{ min: today }}\n value={line.expiryDate}\n onChange={(e) => patchLine(idx, 'expiryDate', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.expiryDate`)}\n error={fieldErrors.show(`items.${idx}.expiryDate`) && !!lerr.expiryDate}\n helperText={\n fieldErrors.show(`items.${idx}.expiryDate`) && lerr.expiryDate\n ? lerr.expiryDate\n : (expiryRequired ? 'Required for this product' : '')\n } />\n </Grid>\n )}\n {/* Lot # is captured at confirmation time by the approver,\n who physically receives the goods. Hidden when this\n form is in request-mode so the requester doesn't waste\n time guessing a lot they don't have yet. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField fullWidth size=\"small\" label=\"Lot #\"\n value={line.lotNumber}\n onChange={(e) => patchLine(idx, 'lotNumber', e.target.value)} />\n </Grid>\n )}\n <Grid item xs={10} md={1}>\n <FormControl fullWidth size=\"small\"\n required={storageRequired}\n disabled={form.ownerType !== 'FACILITY' || !form.ownerId}\n error={fieldErrors.show(`items.${idx}.storageUnitId`) && !!lerr.storageUnitId}>\n <InputLabel>Storage</InputLabel>\n <Select label=\"Storage\" value={line.storageUnitId || ''}\n onChange={(e) => patchLine(idx, 'storageUnitId', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.storageUnitId`)}>\n <MenuItem value=\"\"><em>—</em></MenuItem>\n {eligibleStorageUnits.map((u) => <MenuItem key={u.id} value={u.id}>{u.unitName}</MenuItem>)}\n </Select>\n {fieldErrors.show(`items.${idx}.storageUnitId`) && lerr.storageUnitId ? (\n <FormHelperText>{lerr.storageUnitId}</FormHelperText>\n ) : storageRequired ? (\n <FormHelperText>Required for this product</FormHelperText>\n ) : null}\n </FormControl>\n </Grid>\n <Grid item xs={2} md={1} sx={{ textAlign: 'right' }}>\n <IconButton color=\"error\" onClick={() => removeLine(idx)} disabled={form.items.length === 1}>\n <DeleteOutlineRoundedIcon />\n </IconButton>\n </Grid>\n </Grid>\n </Card>\n );\n })}\n </Stack>\n </CardContent>\n </Card>\n\n {/* ---------- Summary footer (like a bill) ---------- */}\n <Card 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 <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 3\"\n title={manageAmounts ? 'Bill summary' : 'Receipt details'}\n hint={manageAmounts\n ? 'The total is calculated from items. Add payment mode and notes. The date is set automatically.'\n : 'Add notes about this receipt. The date is set automatically.'}\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n {manageAmounts ? (\n <Grid item xs={12} md={6}>\n <FormControl fullWidth size=\"small\">\n <InputLabel>Payment Mode</InputLabel>\n <Select label=\"Payment Mode\" value={form.paymentMode || ''}\n onChange={(e) => setForm({ ...form, paymentMode: e.target.value })}>\n <MenuItem value=\"\"><em>—</em></MenuItem>\n {PAYMENT_MODES.map((m) => <MenuItem key={m.value} value={m.value}>{m.label}</MenuItem>)}\n </Select>\n </FormControl>\n </Grid>\n ) : null}\n <Grid item xs={12} md={manageAmounts ? 6 : 12}>\n <TextField fullWidth size=\"small\" label=\"Notes\" value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })} />\n </Grid>\n </Grid>\n\n {manageAmounts ? (\n <>\n <Divider sx={{ my: 2 }} />\n\n <Stack direction=\"row\" justifyContent=\"flex-end\" alignItems=\"baseline\" spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Total ({form.items.length} {form.items.length === 1 ? 'line' : 'lines'})\n </Typography>\n <Typography variant=\"h4\" sx={{ fontWeight: 800, fontVariantNumeric: 'tabular-nums' }}>\n {computedLineTotal.priced ? formatNumber(computedLineTotal.sum) : '—'}\n </Typography>\n </Stack>\n {!computedLineTotal.priced ? (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', textAlign: 'right' }}>\n Add a Buy Price on at least one line to compute the total.\n </Typography>\n ) : null}\n </>\n ) : null}\n </CardContent>\n </Card>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,586 @@
1
+ import { a as e, b as t, c as n, i as r, j as i, n as a, p as ee, r as te, t as ne } from "./PmGuard-CjJBisaN.js";
2
+ import { i as re, n as ie, r as o } from "./PmTable-BuXZQZCU.js";
3
+ import { n as s, t as c } from "./format-UOyOu75i.js";
4
+ import { useMemo as l, useState as u } from "react";
5
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
6
+ import { Alert as m, Box as h, Button as g, Card as _, CardContent as v, Chip as ae, Divider as y, Stack as b, Table as oe, TableBody as se, TableCell as x, TableHead as ce, TableRow as S, TextField as C, Typography as w } from "@mui/material";
7
+ import le from "@mui/icons-material/RefreshRounded";
8
+ import { showToast as T } from "@/common/toaster/toaster";
9
+ import { useNavigate as ue, useParams as de } from "react-router-dom";
10
+ import { alpha as fe } from "@mui/material/styles";
11
+ import pe from "@mui/icons-material/ArrowBackRounded";
12
+ import me from "@mui/icons-material/CheckCircleRounded";
13
+ import he from "@mui/icons-material/CancelRounded";
14
+ import ge from "@mui/icons-material/DoNotDisturbRounded";
15
+ //#region src/views/PurchaseRequestDetailPage.jsx
16
+ function _e(e) {
17
+ switch (e) {
18
+ case "PENDING": return "info";
19
+ case "APPROVED": return "success";
20
+ case "PARTIALLY_APPROVED": return "warning";
21
+ case "REJECTED": return "error";
22
+ case "CANCELLED": return "default";
23
+ default: return "default";
24
+ }
25
+ }
26
+ function E(e) {
27
+ return e ? e.toLowerCase().split("_").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" ") : "";
28
+ }
29
+ function D() {
30
+ let D = ue(), { requestId: k } = de(), A = r().has("approve.manage.productManagement.purchase"), j = n()?.managePurchaseAmounts !== !1, [M, N] = u(null), [ve, P] = u(!0), [F, I] = u(!1), [L, R] = u(""), [z, B] = u({}), [V, H] = u({}), [U, W] = u({}), [G, K] = u("");
31
+ async function q() {
32
+ P(!0), R("");
33
+ try {
34
+ let e = await i(k);
35
+ N(e);
36
+ let t = {}, n = {}, r = {};
37
+ (e?.items ?? []).forEach((e) => {
38
+ t[e.id] = String(e.approvedQuantity == null ? e.requestedQuantity : e.approvedQuantity), n[e.id] = e.lotNumber ?? "", r[e.id] = e.expiryDate ? new Date(e.expiryDate).toISOString().substring(0, 10) : "";
39
+ }), B(t), H(n), W(r), K(e?.decisionNotes ?? "");
40
+ } catch (e) {
41
+ R(e?.response?.data?.message ?? e?.message ?? "Failed to load purchase request");
42
+ } finally {
43
+ P(!1);
44
+ }
45
+ }
46
+ te(["view.manage.productManagement.purchase", "approve.manage.productManagement.purchase"], () => {
47
+ q();
48
+ }, [k]);
49
+ let J = M?.status === "PENDING", Y = l(() => {
50
+ if (!M || !j) return null;
51
+ let e = 0, t = !1;
52
+ return (M.items ?? []).forEach((n) => {
53
+ let r = Number(z[n.id] ?? 0);
54
+ !Number.isFinite(r) || r <= 0 || n.buyPrice != null && (e += Number(n.buyPrice) * r, t = !0);
55
+ }), t ? e : null;
56
+ }, [
57
+ M,
58
+ z,
59
+ j
60
+ ]);
61
+ function ye(e, t) {
62
+ B((n) => ({
63
+ ...n,
64
+ [e]: t
65
+ }));
66
+ }
67
+ function be(e, t) {
68
+ H((n) => ({
69
+ ...n,
70
+ [e]: t
71
+ }));
72
+ }
73
+ function xe(e, t) {
74
+ W((n) => ({
75
+ ...n,
76
+ [e]: t
77
+ }));
78
+ }
79
+ function Se() {
80
+ if (!M) return;
81
+ let e = {};
82
+ M.items.forEach((t) => {
83
+ e[t.id] = String(t.requestedQuantity);
84
+ }), B(e);
85
+ }
86
+ function X() {
87
+ if (!M) return;
88
+ let e = {};
89
+ M.items.forEach((t) => {
90
+ e[t.id] = "0";
91
+ }), B(e);
92
+ }
93
+ let Z = l(() => {
94
+ if (!M) return {};
95
+ let e = {};
96
+ return (M.items ?? []).forEach((t) => {
97
+ let n = z[t.id];
98
+ if (n == null || n === "") {
99
+ e[t.id] = "Required";
100
+ return;
101
+ }
102
+ let r = Number(n);
103
+ !Number.isInteger(r) || r < 0 ? e[t.id] = "Must be 0 or a positive whole number" : r > t.requestedQuantity && (e[t.id] = `At most ${t.requestedQuantity}`);
104
+ }), e;
105
+ }, [M, z]), Q = l(() => {
106
+ if (!M) return {};
107
+ let e = {};
108
+ return (M.items ?? []).forEach((t) => {
109
+ let n = Number(z[t.id] ?? 0);
110
+ !Number.isFinite(n) || n <= 0 || (V[t.id] ?? "").trim() || (e[t.id] = "Lot is required when approving this line");
111
+ }), e;
112
+ }, [
113
+ M,
114
+ z,
115
+ V
116
+ ]), Ce = l(() => {
117
+ if (!M) return {};
118
+ let e = {}, t = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10);
119
+ return (M.items ?? []).forEach((n) => {
120
+ let r = Number(z[n.id] ?? 0);
121
+ if (!Number.isFinite(r) || r <= 0) return;
122
+ let i = (U[n.id] ?? "").trim();
123
+ i ? i < t && (e[n.id] = "Expiry must be today or later") : e[n.id] = "Expiry is required when approving this line";
124
+ }), e;
125
+ }, [
126
+ M,
127
+ z,
128
+ U
129
+ ]), we = Object.keys(Z).length === 0 && Object.keys(Q).length === 0 && Object.keys(Ce).length === 0;
130
+ async function Te() {
131
+ if (!(!we || !M)) {
132
+ I(!0), R("");
133
+ try {
134
+ let e = await t(M.id, {
135
+ decisionNotes: G || null,
136
+ items: M.items.map((e) => {
137
+ let t = (V[e.id] ?? "").trim(), n = (U[e.id] ?? "").trim();
138
+ return {
139
+ itemId: e.id,
140
+ approvedQuantity: Number(z[e.id]),
141
+ lotNumber: t || null,
142
+ expiryDate: n || null
143
+ };
144
+ })
145
+ });
146
+ N(e), T({
147
+ message: `Request ${E(e.status).toLowerCase()}.`,
148
+ type: "success"
149
+ });
150
+ } catch (e) {
151
+ R(e?.response?.data?.message ?? e?.message ?? "Failed to submit decision");
152
+ } finally {
153
+ I(!1);
154
+ }
155
+ }
156
+ }
157
+ async function Ee() {
158
+ if (M) {
159
+ I(!0), R("");
160
+ try {
161
+ N(await ee(M.id)), T({
162
+ message: "Request cancelled.",
163
+ type: "success"
164
+ });
165
+ } catch (e) {
166
+ R(e?.response?.data?.message ?? e?.message ?? "Failed to cancel request");
167
+ } finally {
168
+ I(!1);
169
+ }
170
+ }
171
+ }
172
+ let $ = J && A;
173
+ return /* @__PURE__ */ p(h, { children: [/* @__PURE__ */ f(a, {}), /* @__PURE__ */ f(ne, {
174
+ anyOf: ["view.manage.productManagement.purchase", "approve.manage.productManagement.purchase"],
175
+ children: /* @__PURE__ */ p(e, {
176
+ title: M?.serialId ? `Purchase Request #${M.serialId}` : "Purchase Request",
177
+ subtitle: "Approve or reject each item. Your decision closes the entire request.",
178
+ chips: M?.status ? [E(M.status)] : [],
179
+ actions: /* @__PURE__ */ p(b, {
180
+ direction: "row",
181
+ spacing: 1,
182
+ children: [/* @__PURE__ */ f(g, {
183
+ variant: "outlined",
184
+ startIcon: /* @__PURE__ */ f(pe, {}),
185
+ onClick: () => D("/ui/medplat/manage/pm/purchase-requests"),
186
+ children: "Back"
187
+ }), /* @__PURE__ */ f(g, {
188
+ variant: "outlined",
189
+ startIcon: /* @__PURE__ */ f(le, {}),
190
+ onClick: () => void q(),
191
+ children: "Refresh"
192
+ })]
193
+ }),
194
+ children: [L ? /* @__PURE__ */ f(m, {
195
+ severity: "error",
196
+ sx: { mb: 2 },
197
+ children: L
198
+ }) : null, !ve && M ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(_, {
199
+ sx: {
200
+ borderRadius: 4,
201
+ mb: 2,
202
+ border: "1px solid",
203
+ borderColor: "divider",
204
+ boxShadow: "0 12px 32px rgba(15, 23, 42, 0.045)"
205
+ },
206
+ children: /* @__PURE__ */ p(v, {
207
+ sx: { p: 2.5 },
208
+ children: [
209
+ /* @__PURE__ */ p(b, {
210
+ direction: {
211
+ xs: "column",
212
+ md: "row"
213
+ },
214
+ spacing: 2,
215
+ alignItems: { md: "flex-start" },
216
+ children: [/* @__PURE__ */ p(h, {
217
+ sx: {
218
+ flexGrow: 1,
219
+ minWidth: 0
220
+ },
221
+ children: [
222
+ /* @__PURE__ */ p(b, {
223
+ direction: "row",
224
+ alignItems: "center",
225
+ spacing: 1.5,
226
+ sx: { mb: .5 },
227
+ children: [
228
+ /* @__PURE__ */ f(re, { id: M.serialId }),
229
+ /* @__PURE__ */ f(ae, {
230
+ size: "small",
231
+ label: E(M.status),
232
+ color: _e(M.status)
233
+ }),
234
+ /* @__PURE__ */ f(w, {
235
+ variant: "caption",
236
+ color: "text.secondary",
237
+ children: M.ownerType
238
+ })
239
+ ]
240
+ }),
241
+ /* @__PURE__ */ f(w, {
242
+ variant: "h6",
243
+ sx: { fontWeight: 800 },
244
+ children: M.ownerName || (M.ownerType === "USER" ? "Personal" : "Facility")
245
+ }),
246
+ /* @__PURE__ */ p(w, {
247
+ variant: "caption",
248
+ color: "text.secondary",
249
+ sx: { display: "block" },
250
+ children: ["For purchase date ", c(M.purchaseDate)]
251
+ })
252
+ ]
253
+ }), /* @__PURE__ */ p(b, {
254
+ spacing: .25,
255
+ sx: { minWidth: 260 },
256
+ children: [
257
+ /* @__PURE__ */ f(O, {
258
+ label: "Requested by",
259
+ children: M.requestedByName || `#${M.requestedBy}`
260
+ }),
261
+ /* @__PURE__ */ f(O, {
262
+ label: "Requested on",
263
+ children: s(M.requestedOn)
264
+ }),
265
+ M.decidedBy ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(O, {
266
+ label: "Decided by",
267
+ children: M.decidedByName || `#${M.decidedBy}`
268
+ }), /* @__PURE__ */ f(O, {
269
+ label: "Decided on",
270
+ children: s(M.decidedOn)
271
+ })] }) : null,
272
+ M.paymentMode ? /* @__PURE__ */ f(O, {
273
+ label: "Payment",
274
+ children: M.paymentMode
275
+ }) : null
276
+ ]
277
+ })]
278
+ }),
279
+ M.notes ? /* @__PURE__ */ p(d, { children: [
280
+ /* @__PURE__ */ f(y, { sx: { my: 1.5 } }),
281
+ /* @__PURE__ */ f(w, {
282
+ variant: "caption",
283
+ color: "text.secondary",
284
+ sx: {
285
+ display: "block",
286
+ mb: .5,
287
+ fontWeight: 700,
288
+ textTransform: "uppercase",
289
+ letterSpacing: .6
290
+ },
291
+ children: "Requester notes"
292
+ }),
293
+ /* @__PURE__ */ f(w, {
294
+ variant: "body2",
295
+ children: M.notes
296
+ })
297
+ ] }) : null,
298
+ M.decisionNotes ? /* @__PURE__ */ p(d, { children: [
299
+ /* @__PURE__ */ f(y, { sx: { my: 1.5 } }),
300
+ /* @__PURE__ */ f(w, {
301
+ variant: "caption",
302
+ color: "text.secondary",
303
+ sx: {
304
+ display: "block",
305
+ mb: .5,
306
+ fontWeight: 700,
307
+ textTransform: "uppercase",
308
+ letterSpacing: .6
309
+ },
310
+ children: "Decision notes"
311
+ }),
312
+ /* @__PURE__ */ f(w, {
313
+ variant: "body2",
314
+ children: M.decisionNotes
315
+ })
316
+ ] }) : null
317
+ ]
318
+ })
319
+ }), /* @__PURE__ */ f(_, {
320
+ sx: {
321
+ borderRadius: 4,
322
+ mb: 2,
323
+ border: "1px solid",
324
+ borderColor: "divider",
325
+ boxShadow: "0 12px 32px rgba(15, 23, 42, 0.045)"
326
+ },
327
+ children: /* @__PURE__ */ p(v, {
328
+ sx: { p: 2.5 },
329
+ children: [
330
+ /* @__PURE__ */ p(b, {
331
+ direction: {
332
+ xs: "column",
333
+ md: "row"
334
+ },
335
+ alignItems: { md: "flex-end" },
336
+ justifyContent: "space-between",
337
+ sx: { mb: 1.5 },
338
+ children: [/* @__PURE__ */ f(ie, {
339
+ eyebrow: "Lines",
340
+ title: $ ? "Decide per-line quantities" : "Requested items",
341
+ hint: $ ? "Enter 0 to reject an item, or less than requested to partially approve." : null
342
+ }), $ ? /* @__PURE__ */ p(b, {
343
+ direction: "row",
344
+ spacing: 1,
345
+ children: [/* @__PURE__ */ f(g, {
346
+ size: "small",
347
+ variant: "outlined",
348
+ onClick: Se,
349
+ children: "Approve all"
350
+ }), /* @__PURE__ */ f(g, {
351
+ size: "small",
352
+ variant: "outlined",
353
+ color: "error",
354
+ onClick: X,
355
+ children: "Reject all"
356
+ })]
357
+ }) : null]
358
+ }),
359
+ /* @__PURE__ */ p(oe, {
360
+ size: "small",
361
+ children: [/* @__PURE__ */ f(ce, { children: /* @__PURE__ */ p(S, { children: [
362
+ /* @__PURE__ */ f(x, { children: "Product" }),
363
+ /* @__PURE__ */ f(x, { children: "Lot" }),
364
+ /* @__PURE__ */ f(x, { children: "Expiry" }),
365
+ j ? /* @__PURE__ */ f(x, {
366
+ align: "right",
367
+ children: "Buy Price"
368
+ }) : null,
369
+ /* @__PURE__ */ f(x, {
370
+ align: "right",
371
+ children: "Requested"
372
+ }),
373
+ /* @__PURE__ */ f(x, {
374
+ align: "right",
375
+ children: $ ? "Approve" : "Approved"
376
+ }),
377
+ M.ownerType === "FACILITY" ? /* @__PURE__ */ f(x, { children: "Storage" }) : null
378
+ ] }) }), /* @__PURE__ */ f(se, { children: M.items.map((e) => {
379
+ let t = Z[e.id], n = Q[e.id], r = Ce[e.id], i = Number(z[e.id] ?? 0), a = $ && Number.isFinite(i) && i > 0;
380
+ return /* @__PURE__ */ p(S, { children: [
381
+ /* @__PURE__ */ f(x, {
382
+ sx: { fontWeight: 700 },
383
+ children: e.productName
384
+ }),
385
+ /* @__PURE__ */ f(x, {
386
+ sx: {
387
+ fontFamily: "monospace",
388
+ fontSize: "0.78rem"
389
+ },
390
+ children: $ ? /* @__PURE__ */ f(C, {
391
+ size: "small",
392
+ placeholder: "Enter lot",
393
+ value: V[e.id] ?? "",
394
+ onChange: (t) => be(e.id, t.target.value),
395
+ disabled: !a,
396
+ error: !!n,
397
+ helperText: n || (a ? "" : "Approve qty > 0 to enter lot"),
398
+ inputProps: { style: {
399
+ fontFamily: "monospace",
400
+ fontSize: "0.78rem"
401
+ } },
402
+ sx: { minWidth: 140 }
403
+ }) : e.lotNumber || "—"
404
+ }),
405
+ /* @__PURE__ */ f(x, { children: $ ? /* @__PURE__ */ f(C, {
406
+ size: "small",
407
+ type: "date",
408
+ InputLabelProps: { shrink: !0 },
409
+ value: U[e.id] ?? "",
410
+ onChange: (t) => xe(e.id, t.target.value),
411
+ disabled: !a,
412
+ error: !!r,
413
+ helperText: r || "",
414
+ sx: { minWidth: 150 }
415
+ }) : e.expiryDate ? c(e.expiryDate) : "—" }),
416
+ j ? /* @__PURE__ */ f(x, {
417
+ align: "right",
418
+ children: e.buyPrice == null ? /* @__PURE__ */ f(w, {
419
+ variant: "caption",
420
+ color: "text.disabled",
421
+ children: "—"
422
+ }) : /* @__PURE__ */ f(o, {
423
+ value: e.buyPrice,
424
+ bold: !1
425
+ })
426
+ }) : null,
427
+ /* @__PURE__ */ f(x, {
428
+ align: "right",
429
+ sx: {
430
+ fontVariantNumeric: "tabular-nums",
431
+ fontWeight: 700
432
+ },
433
+ children: e.requestedQuantity
434
+ }),
435
+ /* @__PURE__ */ f(x, {
436
+ align: "right",
437
+ children: $ ? /* @__PURE__ */ f(C, {
438
+ size: "small",
439
+ type: "number",
440
+ inputProps: {
441
+ min: 0,
442
+ max: e.requestedQuantity,
443
+ style: { textAlign: "right" }
444
+ },
445
+ value: z[e.id] ?? "",
446
+ onChange: (t) => ye(e.id, t.target.value),
447
+ error: !!t,
448
+ helperText: t || "",
449
+ sx: { maxWidth: 110 }
450
+ }) : /* @__PURE__ */ f(w, {
451
+ sx: {
452
+ fontVariantNumeric: "tabular-nums",
453
+ fontWeight: 700
454
+ },
455
+ children: e.approvedQuantity == null ? "—" : e.approvedQuantity
456
+ })
457
+ }),
458
+ M.ownerType === "FACILITY" ? /* @__PURE__ */ f(x, { children: e.storageUnitName || "—" }) : null
459
+ ] }, e.id);
460
+ }) })]
461
+ }),
462
+ $ ? /* @__PURE__ */ p(d, { children: [
463
+ /* @__PURE__ */ f(y, { sx: { my: 2 } }),
464
+ /* @__PURE__ */ p(b, {
465
+ direction: {
466
+ xs: "column",
467
+ md: "row"
468
+ },
469
+ spacing: 2,
470
+ alignItems: { md: "flex-start" },
471
+ children: [/* @__PURE__ */ f(C, {
472
+ fullWidth: !0,
473
+ size: "small",
474
+ label: "Decision notes (optional)",
475
+ placeholder: "Rationale shown to the requester",
476
+ value: G,
477
+ onChange: (e) => K(e.target.value),
478
+ multiline: !0,
479
+ minRows: 2
480
+ }), j && Y != null ? /* @__PURE__ */ p(h, {
481
+ sx: (e) => ({
482
+ minWidth: 220,
483
+ p: 1.5,
484
+ borderRadius: 3,
485
+ border: "1px solid",
486
+ borderColor: "divider",
487
+ bgcolor: fe(e.palette.primary.main, .04),
488
+ textAlign: "right"
489
+ }),
490
+ children: [/* @__PURE__ */ f(w, {
491
+ variant: "caption",
492
+ color: "text.secondary",
493
+ sx: {
494
+ display: "block",
495
+ fontWeight: 800,
496
+ textTransform: "uppercase",
497
+ letterSpacing: .6
498
+ },
499
+ children: "Total if approved"
500
+ }), /* @__PURE__ */ f(o, {
501
+ value: Number(Y.toFixed(2)),
502
+ size: "big"
503
+ })]
504
+ }) : null]
505
+ }),
506
+ /* @__PURE__ */ p(b, {
507
+ direction: "row",
508
+ spacing: 1,
509
+ justifyContent: "flex-end",
510
+ sx: { mt: 2 },
511
+ children: [/* @__PURE__ */ f(g, {
512
+ variant: "outlined",
513
+ color: "error",
514
+ startIcon: /* @__PURE__ */ f(he, {}),
515
+ disabled: F,
516
+ onClick: () => {
517
+ X();
518
+ },
519
+ children: "Set all to 0"
520
+ }), /* @__PURE__ */ f(g, {
521
+ variant: "contained",
522
+ startIcon: /* @__PURE__ */ f(me, {}),
523
+ disabled: F || !we,
524
+ onClick: () => void Te(),
525
+ children: F ? "Submitting…" : "Submit Decision"
526
+ })]
527
+ })
528
+ ] }) : null,
529
+ J && !A ? /* @__PURE__ */ f(b, {
530
+ direction: "row",
531
+ spacing: 1,
532
+ justifyContent: "flex-end",
533
+ sx: { mt: 2 },
534
+ children: /* @__PURE__ */ f(g, {
535
+ variant: "outlined",
536
+ color: "error",
537
+ startIcon: /* @__PURE__ */ f(ge, {}),
538
+ disabled: F,
539
+ onClick: () => void Ee(),
540
+ children: F ? "Cancelling…" : "Cancel Request"
541
+ })
542
+ }) : null,
543
+ M.approvedPurchaseId ? /* @__PURE__ */ p(m, {
544
+ severity: "success",
545
+ sx: { mt: 2 },
546
+ children: [
547
+ "Stock was added to inventory under purchase id ",
548
+ /* @__PURE__ */ f("strong", { children: M.approvedPurchaseId }),
549
+ "."
550
+ ]
551
+ }) : null
552
+ ]
553
+ })
554
+ })] }) : null]
555
+ })
556
+ })] });
557
+ }
558
+ function O({ label: e, children: t }) {
559
+ return /* @__PURE__ */ p(b, {
560
+ direction: "row",
561
+ spacing: 1,
562
+ sx: { minWidth: 0 },
563
+ children: [/* @__PURE__ */ f(w, {
564
+ variant: "caption",
565
+ color: "text.secondary",
566
+ sx: {
567
+ minWidth: 100,
568
+ fontWeight: 700,
569
+ textTransform: "uppercase",
570
+ letterSpacing: .5
571
+ },
572
+ children: e
573
+ }), /* @__PURE__ */ f(w, {
574
+ variant: "caption",
575
+ sx: {
576
+ minWidth: 0,
577
+ fontWeight: 700
578
+ },
579
+ children: t
580
+ })]
581
+ });
582
+ }
583
+ //#endregion
584
+ export { D as default };
585
+
586
+ //# sourceMappingURL=PurchaseRequestDetailPage-C0GYFJDl.js.map