Build inventory frontend and stock management features

- Full inventory UI: product list with search/filter, product detail with
  tabs (details, units, suppliers, stock receipts, price history)
- Product filters: category, type (serialized/rental/repair), low stock,
  active/inactive — all server-side with URL-synced state
- Product-supplier junction: link products to multiple suppliers with
  preferred flag, joined supplier details in UI
- Stock receipts: record incoming stock with supplier, qty, cost per unit,
  invoice number; auto-increments qty_on_hand for non-serialized products
- Price history tab on product detail page
- categories/all endpoint to avoid pagination limit on dropdown fetches
- categoryId filter on product list endpoint
- Repair parts and additional inventory items in music store seed data
- isDualUseRepair corrected: instruments set to false, strings/parts true
- Product-supplier links and stock receipts in seed data
- Price history seed data simulating cost increases over past year
- 37 API tests covering categories, suppliers, products, units,
  product-suppliers, and stock receipts
- alert-dialog and checkbox UI components
- sync-and-deploy.sh script for rsync + remote deploy
This commit is contained in:
Ryan Moon
2026-03-30 20:12:07 -05:00
parent ec09e319ed
commit 5f5ba9e4a2
24 changed files with 4023 additions and 187 deletions

View File

@@ -55,6 +55,9 @@ export {
ProductSearchSchema,
InventoryUnitCreateSchema,
InventoryUnitUpdateSchema,
ProductSupplierCreateSchema,
ProductSupplierUpdateSchema,
StockReceiptCreateSchema,
} from './inventory.schema.js'
export type {
CategoryCreateInput,
@@ -65,6 +68,9 @@ export type {
ProductUpdateInput,
InventoryUnitCreateInput,
InventoryUnitUpdateInput,
ProductSupplierCreateInput,
ProductSupplierUpdateInput,
StockReceiptCreateInput,
} from './inventory.schema.js'
export {

View File

@@ -90,3 +90,24 @@ export type InventoryUnitCreateInput = z.infer<typeof InventoryUnitCreateSchema>
export const InventoryUnitUpdateSchema = InventoryUnitCreateSchema.omit({ productId: true }).partial()
export type InventoryUnitUpdateInput = z.infer<typeof InventoryUnitUpdateSchema>
export const ProductSupplierCreateSchema = z.object({
supplierId: z.string().uuid(),
supplierSku: opt(z.string().max(100)),
isPreferred: z.boolean().default(false),
})
export type ProductSupplierCreateInput = z.infer<typeof ProductSupplierCreateSchema>
export const ProductSupplierUpdateSchema = ProductSupplierCreateSchema.omit({ supplierId: true }).partial()
export type ProductSupplierUpdateInput = z.infer<typeof ProductSupplierUpdateSchema>
export const StockReceiptCreateSchema = z.object({
supplierId: opt(z.string().uuid()),
inventoryUnitId: opt(z.string().uuid()),
qty: z.number().int().min(1).default(1),
costPerUnit: z.number().min(0),
receivedDate: z.string().date(),
invoiceNumber: opt(z.string().max(100)),
notes: opt(z.string()),
})
export type StockReceiptCreateInput = z.infer<typeof StockReceiptCreateSchema>