Product.java

package com.stocks.stockease.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * Domain entity representing a Product in the inventory system.
 * 
 * Persisted to the "product" table with auto-increment ID. Maintains quantity and price
 * with automatic total value recalculation. All fields are required (nullable=false).
 * 
 * @author Team StockEase
 * @version 1.0
 * @since 2025-01-01
 */
@Data
@Entity
@Table(name = "product")
@NoArgsConstructor
@AllArgsConstructor
public class Product {

    /**
     * Unique product identifier. Auto-generated by database (auto-increment).
     * Used as primary key for database relationships and API lookups.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * Product display name. Required, unique constraint enforced at database level.
     * Used for user-facing displays and search operations.
     */
    @Column(nullable = false)
    private String name;

    /**
     * Current stock quantity (integer count of units).
     * Cannot be null or negative. Auto-recalculates totalValue when changed.
     */
    @Column(nullable = false)
    private Integer quantity;

    /**
     * Unit price in currency (double precision).
     * Cannot be null or negative. Auto-recalculates totalValue when changed.
     */
    @Column(nullable = false)
    private Double price;

    /**
     * Computed total stock value. Automatically calculated as (quantity * price).
     * Updated via custom setters to ensure consistency. Never manually set.
     * Used for financial reporting and inventory valuation.
     */
    @Column(nullable = false)
    private Double totalValue;

    /**
     * Updates product quantity and recalculates total stock value.
     * 
     * Overrides Lombok's default setter to maintain data consistency.
     * Automatically recalculates totalValue = quantity * price.
     * 
     * @param quantity new quantity in stock (must be non-negative)
     */
    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
        // Automatically recalculate to keep totalValue consistent with quantity/price changes
        updateTotalValue();
    }

    /**
     * Updates product unit price and recalculates total stock value.
     * 
     * Overrides Lombok's default setter to maintain data consistency.
     * Automatically recalculates totalValue = quantity * price.
     * 
     * @param price new unit price (must be positive)
     */
    public void setPrice(Double price) {
        this.price = price;
        // Automatically recalculate to keep totalValue consistent with quantity/price changes
        updateTotalValue();
    }

    /**
     * Internal helper to compute total stock value.
     * 
     * Formula: totalValue = quantity * price
     * Handles null cases (e.g., during object construction) by returning 0.0.
     * Called by setQuantity() and setPrice() to maintain consistency.
     */
    private void updateTotalValue() {
        // Prevent null arithmetic; use 0 if either quantity or price is null
        this.totalValue = (this.quantity != null && this.price != null) ? this.quantity * this.price : 0.0;
    }

    /**
     * Constructs product entity for creation (without auto-generated ID).
     * 
     * Used for new product insertion. ID is omitted; database generates it via auto-increment.
     * Automatically calculates totalValue from provided quantity and price.
     * 
     * @param name product name (required)
     * @param quantity stock quantity (required, non-negative)
     * @param price unit price (required, positive)
     */
    public Product(String name, Integer quantity, Double price) {
        this.name = name;
        this.quantity = quantity;
        this.price = price;
        // Pre-calculate totalValue during construction for consistency
        this.totalValue = (quantity != null && price != null) ? quantity * price : 0.0;
    }
}