@@ -327,7 +390,7 @@ export default function NewFreeIssuePage() {
{uoms.map((u) => (
- {u.name}
+ {formatUomName(u.name)}
))}
@@ -392,7 +455,7 @@ export default function NewFreeIssuePage() {
{uoms.map((u) => (
- {u.name}
+ {formatUomName(u.name)}
))}
diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx
index 94793fb..1f57856 100644
--- a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx
+++ b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx
@@ -11,8 +11,10 @@ import { itemsApi } from "@/lib/api/items"
import { uomsApi } from "@/lib/api/uoms"
import { warehousesApi } from "@/lib/api/warehouses"
import { errorMessage } from "@/lib/error-map"
+import { findUomLabel, formatUomName } from "@/lib/format-uom"
import { cn } from "@/lib/utils"
import { getSuggestedUnitPrice } from "@/lib/sales-line-utils"
+import { validateSalesDocument } from "@/lib/sales-validation"
import { Customer } from "@/types/customers"
import { ItemListItem, Uom, Warehouse } from "@/types/master-data"
import { CreateSalesInvoiceLineRequest, SalesInvoice, SalesInvoicePostingCheck, SalesInvoiceStatus, SalesInvoiceType } from "@/types/sales"
@@ -171,9 +173,15 @@ export default function SalesInvoiceDetailPage({ params }: { params: Promise<{ i
}
async function save() {
- if (!customerId || !warehouseId || !etag) return
- if (lines.some((line) => !line.itemId || !line.uomId || !line.warehouseId)) {
- setError("Select item, UOM and warehouse for every line.")
+ if (!etag) return
+ const validationError = validateSalesDocument({
+ customerId,
+ warehouseId,
+ lines,
+ lineLabel: "invoice line",
+ })
+ if (validationError) {
+ setError(validationError)
return
}
@@ -353,7 +361,7 @@ export default function SalesInvoiceDetailPage({ params }: { params: Promise<{ i
{line.description}
SKU {items.find((i) => i.itemId === line.itemId)?.sku ?? `#${line.itemId}`}
-
{uoms.find((u) => u.uomId === line.uomId)?.name ?? `#${line.uomId}`} |
+
{findUomLabel(uoms, line.uomId)} |
{line.qty.toFixed(0)} |
{line.freeQty > 0 ? line.freeQty.toFixed(0) : "—"} |
{money.format(line.unitPrice)} |
@@ -505,7 +513,7 @@ export default function SalesInvoiceDetailPage({ params }: { params: Promise<{ i
{uoms.map((u) => (
))}
diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx
index c86302d..f1ef563 100644
--- a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx
+++ b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx
@@ -10,6 +10,7 @@ import { itemsApi } from "@/lib/api/items"
import { uomsApi } from "@/lib/api/uoms"
import { warehousesApi } from "@/lib/api/warehouses"
import { errorMessage } from "@/lib/error-map"
+import { findUomLabel } from "@/lib/format-uom"
import { Button, buttonVariants } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
@@ -143,7 +144,7 @@ export default function SalesInvoicePrintPage({ params }: { params: { id: string
{line.description}
SKU {items.find((i) => i.itemId === line.itemId)?.sku ?? `#${line.itemId}`}
-
{uoms.find((u) => u.uomId === line.uomId)?.name ?? `#${line.uomId}`}
+
{findUomLabel(uoms, line.uomId)}
{line.qty.toFixed(2)}
{line.freeQty > 0 ? line.freeQty.toFixed(2) : "—"}
{line.unitPrice.toFixed(2)}
diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx
index ad3e323..fd667b2 100644
--- a/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx
+++ b/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx
@@ -11,8 +11,10 @@ import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Badge } from "@/components/ui/badge"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
+import { formatUomName } from "@/lib/format-uom"
import { cn } from "@/lib/utils"
import { getSuggestedUnitPrice } from "@/lib/sales-line-utils"
+import { validateSalesDocument } from "@/lib/sales-validation"
import { errorMessage } from "@/lib/error-map"
import { salesApi } from "@/lib/api/sales"
import { customersApi } from "@/lib/api/customers"
@@ -85,18 +87,9 @@ export default function NewSalesInvoicePage() {
setItems(itemRes.items)
setUoms(uomRes.items)
setWarehouses(whRes.items)
- const defaultWarehouseId = whRes.items[0]?.warehouseId ?? null
- setCustomerId(cust.items[0]?.customerId ?? null)
- setWarehouseId(defaultWarehouseId)
- setLines([
- {
- ...blankLine("line-1"),
- itemId: itemRes.items[0]?.itemId ?? 0,
- uomId: itemRes.items[0]?.baseUomId ?? uomRes.items[0]?.uomId ?? 0,
- warehouseId: defaultWarehouseId ?? 0,
- unitPrice: getSuggestedUnitPrice(itemRes.items, itemRes.items[0]?.itemId),
- },
- ])
+ setCustomerId(null)
+ setWarehouseId(null)
+ setLines([blankLine("line-1")])
setActiveFocSchemes(
freeIssueRes.items.flatMap((issue) => {
if (!issue.itemId) return []
@@ -132,6 +125,7 @@ export default function NewSalesInvoicePage() {
updateLine(key, {
itemId,
uomId: item?.baseUomId ?? 0,
+ warehouseId: warehouseId ?? 0,
unitPrice: getSuggestedUnitPrice(items, itemId),
})
}
@@ -178,10 +172,13 @@ export default function NewSalesInvoicePage() {
const payableTotal = netTotal + taxTotal
async function submit() {
- if (!customerId || !warehouseId) return setSubmitError("Select a customer and warehouse.")
- if (lines.some((line) => !line.itemId)) return setSubmitError("Select an item for every line.")
- if (lines.some((line) => !line.warehouseId)) return setSubmitError("Select a warehouse for every line.")
- if (lines.some((line) => !line.uomId)) return setSubmitError("Select a valid UOM for every line.")
+ const validationError = validateSalesDocument({
+ customerId,
+ warehouseId,
+ lines,
+ lineLabel: "invoice line",
+ })
+ if (validationError) return setSubmitError(validationError)
const payload: CreateSalesInvoiceRequest = {
customerId,
@@ -365,7 +362,7 @@ export default function NewSalesInvoicePage() {
{uoms.map((u) => (
- {u.name}
+ {formatUomName(u.name)}
))}
diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx
index d439ef7..9f09f1f 100644
--- a/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx
+++ b/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx
@@ -2,13 +2,15 @@
import { useEffect, useMemo, useState } from "react"
import Link from "next/link"
-import { ChevronLeft, ChevronRight, Eye, Filter, FileText, Plus, Printer } from "lucide-react"
+import { ChevronLeft, ChevronRight, Eye, FileText, Plus, Printer, Search } from "lucide-react"
import { salesApi } from "@/lib/api/sales"
import { errorMessage } from "@/lib/error-map"
import { Button, buttonVariants } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
+import { Card, CardContent, CardHeader } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Skeleton } from "@/components/ui/skeleton"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { PaginationMeta } from "@/types/common"
@@ -18,7 +20,6 @@ import { cn } from "@/lib/utils"
type StatusFilter = SalesInvoiceStatus | "All"
const PAGE_SIZE = 10
-const tabs = ["All", "Draft", "Posted", "Cancelled"] as const
function statusClass(status: SalesInvoiceStatus) {
switch (status) {
@@ -95,36 +96,32 @@ export default function SalesInvoicesPage() {