StockHistory.java
package com.smartsupplypro.inventory.model;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.smartsupplypro.inventory.enums.StockChangeReason;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Stock history entity for immutable audit trail of inventory movements.
* Maps to STOCK_HISTORY table with indexed columns for analytics.
*
* <p><strong>Purpose</strong>: Captures stock changes (receive, sell, scrap, adjust) with who, what, when, why, and price context.
*
* <p><strong>Indexes</strong>: IX_SH_ITEM_TS (item_id, created_at), IX_SH_TS (created_at), IX_SH_SUPPLIER_TS (supplier_id, created_at).
*
* <p><strong>Usage</strong>: Analytics, compliance, debugging, WAC calculations.
*
* @see InventoryItem
* @see Supplier
* @see StockChangeReason
* @see <a href="../../../../../docs/architecture/patterns/model-patterns.md">Model Patterns</a>
*/
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(
name = "STOCK_HISTORY",
indexes = {
// itemId + timestamp lookups (common for item history views)
@Index(name = "IX_SH_ITEM_TS", columnList = "ITEM_ID, CREATED_AT"),
// timestamp range scans (recent activity)
@Index(name = "IX_SH_TS", columnList = "CREATED_AT"),
// supplier + timestamp analytics
@Index(name = "IX_SH_SUPPLIER_TS", columnList = "SUPPLIER_ID, CREATED_AT")
}
)
public class StockHistory {
/** Unique identifier for stock history event (UUID). */
@Id
@Column(name="ID")
private String id;
/** Foreign key reference to affected inventory item. */
@Column(name = "ITEM_ID", nullable = false)
private String itemId;
/** Denormalized supplier reference for fast analytics (populated by service layer). */
@Column(name = "SUPPLIER_ID")
private String supplierId;
/** Quantity delta (positive for increases, negative for decreases). */
@Column(name = "QUANTITY_CHANGE", nullable = false)
private int change;
/** Reason for stock change (stored as STRING). */
@Enumerated(EnumType.STRING)
@Column(name = "REASON", nullable = false)
private StockChangeReason reason;
/** User who initiated the stock change. */
@Column(name = "CREATED_BY", nullable = false)
private String createdBy;
/** Timestamp of stock change event. */
@Column(name = "CREATED_AT", nullable = false)
private LocalDateTime timestamp;
/** Price at time of change (for valuation analytics). */
@Column(name = "PRICE_AT_CHANGE", precision = 12, scale = 2)
private BigDecimal priceAtChange;
/**
* Sets timestamp before persist if not already set.
*/
@PrePersist
public void prePersist() {
if (timestamp == null) {
timestamp = LocalDateTime.now();
}
}
/**
* Inventory item entity reference (read-only, for joins).
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ITEM_ID", insertable = false, updatable = false)
private InventoryItem inventoryItem;
/**
* Supplier entity reference (read-only, for joins).
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SUPPLIER_ID", insertable = false, updatable = false)
private Supplier supplier;
}