feat: add purchase invoice batch print page and related components

- Implemented PurchaseInvoiceBatchPrintPage for printing purchase invoices.
- Created InvoicePrintHeader and InvoicePrintFooter components for invoice layout.
- Added invoice layout presets for different styles (Classic, Compact, Modern).
- Developed PurchaseInvoicePaymentDialog for handling payments against invoices.
- Introduced API mocks for company profile and invoice template configuration.
- Added validation functions for company profile and purchase invoices.
- Defined types for company profile, invoice template configuration, and purchase invoices.
This commit is contained in:
2026-09-01 15:25:32 +05:30
parent 160c687db3
commit bebec10eb3
70 changed files with 12446 additions and 589 deletions
@@ -0,0 +1,22 @@
// Client-side UX validation only — same thin convention as lib/validations/sales.ts.
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
export function validateCompanyProfile(input: {
legalName: string
addressLine1: string
city: string
country: string
phone: string
email: string
}): Record<string, string> {
const errors: Record<string, string> = {}
if (!input.legalName.trim()) errors.legalName = "Enter the company's legal name"
if (!input.addressLine1.trim()) errors.addressLine1 = "Enter an address"
if (!input.city.trim()) errors.city = "Enter a city"
if (!input.country.trim()) errors.country = "Enter a country"
if (!input.phone.trim()) errors.phone = "Enter a phone number"
if (!input.email.trim()) errors.email = "Enter an email address"
else if (!EMAIL_RE.test(input.email.trim())) errors.email = "Enter a valid email address"
return errors
}
@@ -0,0 +1,21 @@
// Client-side UX validation only — required fields, format/range checks the browser can
// already see. Server-authoritative rules (GRN eligibility, vendor match, duplicate vendor
// invoice numbers) are never re-implemented here. Same pattern as lib/validations/sales.ts.
export function validatePurchaseInvoiceHeader(input: {
vendorId: number | null
vendorInvoiceNo: string
vendorInvoiceDate: string
grnIds: number[]
}): Record<string, string> {
const errors: Record<string, string> = {}
if (!input.vendorId) errors.vendorId = "Select a vendor"
if (!input.vendorInvoiceNo.trim()) errors.vendorInvoiceNo = "Enter the vendor's invoice number"
if (!input.vendorInvoiceDate) {
errors.vendorInvoiceDate = "Enter the vendor's invoice date"
} else if (input.vendorInvoiceDate > new Date().toISOString().slice(0, 10)) {
errors.vendorInvoiceDate = "Invoice date cannot be in the future"
}
if (input.grnIds.length === 0) errors.grnIds = "Select at least one GRN to consolidate"
return errors
}