Skip to main content

Data Models

All models are JPA @Entity classes that map to MySQL tables. With hbm2ddl.auto=update, tables are auto-created on application startup.

Entity Relationship Diagram​

┌──────────────┐       ┌──────────────────┐
│ Category │ │ Warehouse │
│──────────────│ │──────────────────│
│ categoryId │ │ warehouseId │
│ name │ │ name │
│ description │ │ │
│ active │ └────────┬─────────┘
│ createdDate │ │
└──────┬───────┘ │ 1:N
│ 1:N │
│ ┌───────┴──────────┐
┌──────┴───────┐ │ StockLocation │
│ Item │ │──────────────────│
│──────────────│ │ locationId │
│ itemId │ │ name │
│ name │ │ warehouseId (FK) │
│ sku │ │ description │
│ active │ │ active │
│ totalBalance │ └──────┬───────────┘
│ stockValue │ │
│ categoryId │◄── FK to Category
│ categoryName │ │
│ allowNeg... │ │
│ taxApplicable│ │
│ taxExempted │ │
│ createdDate │ │
└──────┬───────┘ │
│ │
│ 1:N │
│ │
┌──────┴───────┐ ┌─────────┴──────────────┐
│ ItemTax │ │ ItemStockLocation │
│──────────────│ │────────────────────────│
│ taxId (PK) │ │ id (PK) │
│ itemId (FK) │ │ itemId (FK) / itemName │
│ taxName │ │ stockLocationId (FK) │
│ taxPercentage│ │ locationName │
└──────────────┘ │ warehouseId / Name │
│ quantity │
│ categoryId / Name │
└────────────────────────┘
│
│ Referenced by ──────────────────────────
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────────┐
│ StockAdjustment │ │ StockPurchaseOrder │
│──────────────────│ │──────────────────────────│
│ adjustmentId │ │ poId │
│ itemId (FK) │ │ poNumber │
│ warehouseId (FK) │ │ supplierId │
│ adjustmentType │ │ supplierName │
│ quantity │ │ orderDate │
│ reason │ │ expectedDeliveryDate │
│ adjustedBy │ │ status │
│ adjustmentDate │ │ totalAmount / notes │
│ itemName │ │ createdBy / createdDate │
│ warehouseName │ └────────────┬─────────────┘
└──────────────────┘ │ 1:N
│
┌───────────────┴──────────────┐
│ StockPurchaseOrderItem │
│──────────────────────────────│
│ poItemId │
│ poId (FK) │
│ itemId │
│ itemName │
│ quantity / receivedQuantity │
│ unitPrice / subTotal │
└──────────────────────────────┘

┌──────────────────┐
│ RetailSale │
│──────────────────│
│ saleId │
│ saleNumber │
│ customerName │
│ customerPhone │
│ saleDate │
│ totalAmount │
│ discount │
│ paymentMethod │
│ status │
│ createdBy / notes│
└────────┬─────────┘
│ 1:N
│
┌────────┴──────────────┐
│ RetailSaleItem │
│───────────────────────│
│ saleItemId │
│ saleId (FK) │
│ itemId │
│ itemName │
│ quantity │
│ unitPrice / subTotal │
│ warehouseId │
└───────────────────────┘

Model Details​

Category​

Table: inventory_category

FieldTypeColumnNotes
categoryIdintCATEGORY_IDPK, auto-generated
nameStringNAMECategory name
descriptionStringDESCRIPTIONOptional
activebooleanactiveDefault true, used for soft-delete
createdDateDateCREATED_DATEAuto-set to current date

StockLocation​

Table: inventory_stock_location

FieldTypeColumnNotes
locationIdintLOCATION_IDPK, auto-generated
nameStringNAMELocation code (e.g. "A-01-03")
descriptionStringDESCRIPTIONOptional
activebooleanactiveDefault true
warehouseWarehouseWAREHOUSE_ID@ManyToOne FK
warehouseIdintWAREHOUSE_IDRead-only duplicate column

StockAdjustment​

Table: inventory_stock_adjustment

FieldTypeColumnNotes
adjustmentIdintADJUSTMENT_IDPK
itemItemITEM_ID@ManyToOne FK
itemIdintITEM_IDRead-only
warehouseWarehouseWAREHOUSE_ID@ManyToOne FK
warehouseIdintWAREHOUSE_IDRead-only
adjustmentTypeStringADJUSTMENT_TYPEINCREASE or DECREASE
quantityintQUANTITYAdjustment amount
reasonStringREASONFree-text explanation
adjustedByStringADJUSTED_BYUser who made the adjustment
adjustmentDateDateADJUSTMENT_DATEWhen the adjustment occurred
itemNameStringITEM_NAMEDenormalized for display
warehouseNameStringWAREHOUSE_NAMEDenormalized for display
stockLocationIdIntegerSTOCK_LOCATION_IDOptional FK to StockLocation
locationNameStringLOCATION_NAMEDenormalized for display
Side Effect

Creating an adjustment automatically updates Item.totalBalanceStock — increasing or decreasing based on adjustmentType. If stockLocationId is provided, the ItemStockLocation bridge table quantity is also updated.


ItemStockLocation​

Table: inventory_item_stock_location

Bridge table connecting items to specific stock locations with per-location quantity tracking.

FieldTypeColumnNotes
idintIDPK, auto-generated
itemItemITEM_ID@ManyToOne FK
itemIdIntegerITEM_IDRead-only
itemNameStringITEM_NAMEDenormalized
stockLocationStockLocationSTOCK_LOCATION_ID@ManyToOne FK
stockLocationIdIntegerSTOCK_LOCATION_IDRead-only
locationNameStringLOCATION_NAMEDenormalized
warehouseIdIntegerWAREHOUSE_IDParent warehouse
warehouseNameStringWAREHOUSE_NAMEDenormalized
quantityintQUANTITYUnits at this location
categoryIdIntegerCATEGORY_IDDenormalized
categoryNameStringCATEGORY_NAMEDenormalized
updatedDateDateUPDATED_DATELast modified
activebooleanACTIVEDefault true
Purpose

This bridge table answers: "Where exactly within a warehouse are my items stored, and how many are at each location?" The sum of all quantity values for an item should match Item.totalBalanceStock.


StockPurchaseOrder​

Table: inventory_purchase_order

FieldTypeColumnNotes
poIdintPO_IDPK
poNumberStringPO_NUMBERHuman-readable PO number
supplierIdStringSUPPLIER_IDFK to existing Supplier
supplierNameStringSUPPLIER_NAMEDenormalized
orderDateDateORDER_DATE
expectedDeliveryDateDateEXPECTED_DELIVERY_DATE
statusStringSTATUSDRAFT / ORDERED / PARTIAL / RECEIVED / CANCELLED
totalAmountStringTOTAL_AMOUNT
notesStringNOTESTEXT column
createdByStringCREATED_BY
createdDateDateCREATED_DATE
itemsList<StockPurchaseOrderItem>—@OneToMany cascade all

StockPurchaseOrderItem​

Table: inventory_purchase_order_item

FieldTypeColumnNotes
poItemIdintPO_ITEM_IDPK
purchaseOrderStockPurchaseOrderPO_ID@ManyToOne FK
itemIdintITEM_IDFK to Item
itemNameStringITEM_NAMEDenormalized
quantityintQUANTITYOrdered qty
receivedQuantityintRECEIVED_QUANTITYReceived so far
unitPriceStringUNIT_PRICEPrice per unit
subTotalStringSUB_TOTALquantity × unitPrice
categoryIdIntegerCATEGORY_IDFK to Category (denormalized)
categoryNameStringCATEGORY_NAMEDenormalized for display
stockLocationIdIntegerSTOCK_LOCATION_IDOptional — source bin/shelf
locationNameStringLOCATION_NAMEDenormalized for display

RetailSale​

Table: inventory_retail_sale

FieldTypeColumnNotes
saleIdintSALE_IDPK
saleNumberStringSALE_NUMBERSale/invoice number
customerNameStringCUSTOMER_NAME
customerPhoneStringCUSTOMER_PHONE
saleDateDateSALE_DATE
totalAmountStringTOTAL_AMOUNT
discountStringDISCOUNT
paymentMethodStringPAYMENT_METHODCASH / CARD / CREDIT / TRANSFER
statusStringSTATUSCOMPLETED / REFUNDED / VOID
createdByStringCREATED_BY
notesStringNOTESTEXT column
itemsList<RetailSaleItem>—@OneToMany cascade all

RetailSaleItem​

Table: inventory_retail_sale_item

FieldTypeColumnNotes
saleItemIdintSALE_ITEM_IDPK
saleRetailSaleSALE_ID@ManyToOne FK
itemIdintITEM_IDFK to Item
itemNameStringITEM_NAMEDenormalized
quantityintQUANTITYSold qty
unitPriceStringUNIT_PRICE
subTotalStringSUB_TOTAL
warehouseIdintWAREHOUSE_IDSource warehouse
categoryIdIntegerCATEGORY_IDFK to Category (denormalized)
categoryNameStringCATEGORY_NAMEDenormalized for display
stockLocationIdIntegerSTOCK_LOCATION_IDOptional — source bin/shelf
locationNameStringLOCATION_NAMEDenormalized for display

Existing Models (Pre-existing)​

These models existed before this module and are referenced by the new entities:

ModelTableKey Fields
Iteminventory_itemitemId, name, sku, totalBalanceStock, totalBalanceStockValue, purchaseCost, sellingCost, categoryId, categoryName, allowNegativeStock, taxApplicable, taxExempted
WarehousewarehousewarehouseId, name
InventoryTransactionsinventory_transactionstransactionId, item, warehouse, quantity, balanceStock
ItemPriceHistoryinventory_item_price_historyhistoryId, itemId, itemName, fieldName, oldValue, newValue, changedBy, changedDate
Item ↔ Category Relationship

Each Item now has a @ManyToOne relationship to Category via CATEGORY_ID. The categoryName field is denormalized for display convenience. When creating or updating an item, include categoryId and categoryName in the JSON payload.


ItemTax​

Table: inventory_item_tax

Stores multiple tax entries per item. Each item can have zero or more taxes with user-defined names and percentages.

FieldTypeColumnNotes
taxIdintTAX_IDPK, auto-generated
itemItemITEM_ID@ManyToOne FK, @JsonIgnore
taxNameStringTAX_NAMEe.g. "GST", "SST", "Service Tax"
taxPercentagedoubleTAX_PERCENTAGEe.g. 6.0 for 6%
Cascading

ItemTax entries are managed via Item.taxes (@OneToMany, cascade ALL, orphanRemoval). When editing an item, all old taxes are deleted and replaced with the new list from the JSON payload.