Subversion Repositories SmartDukaan

Rev

Rev 37592 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.spice.profitmandi.dao.entity.fofo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * Current investment snapshot for one partner, maintained by the 2-minute sweep.
 *
 * <p>All the fast-moving columns are written in a single pass so that coupled terms can never be
 * read at different vintages. Wallet and utilised are coupled (an advance payment swept to a loan
 * moves both); in-stock and aged-Apple are coupled (the aged units are a subset of stock). Mixing
 * vintages between any such pair double-counts the money that moved between them.
 *
 * <p>{@link #getBaseValue()} is the single number the credit-limit tier consumes. The sweep
 * compares it at 2dp against the stored value to decide whether a partner's limit needs
 * recomputing, so it must never carry raw double noise.
 */
@Entity
@Table(name = "fofo.partner_investment")
public class PartnerInvestment implements Serializable {

    private static final long serialVersionUID = 1L;

    /** Money is rounded to paise everywhere; base_value is a comparison key, not an accumulator. */
    public static final int MONEY_SCALE = 2;

    @Id
    @Column(name = "fofo_id")
    private int fofoId;

    @Column(name = "wallet_amount")
    private BigDecimal walletAmount = BigDecimal.ZERO;

    /** Gross stock value; the live-demo haircut is applied by {@link #getNetInStockAmount()}. */
    @Column(name = "in_stock_gross_amount")
    private BigDecimal inStockGrossAmount = BigDecimal.ZERO;

    @Column(name = "unbilled_amount")
    private BigDecimal unbilledAmount = BigDecimal.ZERO;

    @Column(name = "grn_pending_amount")
    private BigDecimal grnPendingAmount = BigDecimal.ZERO;

    @Column(name = "activated_stock_amount")
    private BigDecimal activatedStockAmount = BigDecimal.ZERO;

    @Column(name = "activated_grn_pending_amount")
    private BigDecimal activatedGrnPendingAmount = BigDecimal.ZERO;

    @Column(name = "return_in_transit_amount")
    private BigDecimal returnInTransitAmount = BigDecimal.ZERO;

    @Column(name = "utilized_amount")
    private BigDecimal utilizedAmount = BigDecimal.ZERO;

    @Column(name = "aged_apple_stock_amount")
    private BigDecimal agedAppleStockAmount = BigDecimal.ZERO;

    /** Unit count behind {@link #agedAppleStockAmount}; maintained on the same daily cycle. */
    @Column(name = "aged_apple_qty")
    private int agedAppleQty;

    @Column(name = "demo_excluded_amount")
    private BigDecimal demoExcludedAmount = BigDecimal.ZERO;

    @Column(name = "aged_refreshed_on")
    private LocalDate agedRefreshedOn;

    @Column(name = "total_investment")
    private BigDecimal totalInvestment = BigDecimal.ZERO;

    @Column(name = "base_value")
    private BigDecimal baseValue = BigDecimal.ZERO;

    @Column(name = "snapshot_timestamp")
    private LocalDateTime snapshotTimestamp;

    public PartnerInvestment() {
    }

    public PartnerInvestment(int fofoId) {
        this.fofoId = fofoId;
    }

    private static BigDecimal scaled(BigDecimal value) {
        return (value == null ? BigDecimal.ZERO : value).setScale(MONEY_SCALE, RoundingMode.HALF_UP);
    }

    /**
     * Recomputes {@code totalInvestment} and {@code baseValue} from the component columns.
     *
     * <p>Mirrors PartnerDailyInvestment.getTotalInvestment() less the aged-Apple haircut, then less
     * utilisation — the same chain PartnerLimitHelper.getSuggestedAmount() applies before the tier
     * multiply. Keeping it here means the limit never has to re-derive it from separate reads.
     */
    public void recalculate() {
        BigDecimal total = scaled(walletAmount)
                .add(getNetInStockAmount())
                .add(scaled(unbilledAmount))
                .add(scaled(grnPendingAmount))
                .add(scaled(returnInTransitAmount))
                .subtract(scaled(activatedStockAmount))
                .subtract(scaled(activatedGrnPendingAmount));
        this.totalInvestment = scaled(total);
        this.baseValue = scaled(this.totalInvestment
                .subtract(scaled(agedAppleStockAmount))
                .subtract(scaled(utilizedAmount)));
    }

    public int getFofoId() {
        return fofoId;
    }

    public void setFofoId(int fofoId) {
        this.fofoId = fofoId;
    }

    public BigDecimal getWalletAmount() {
        return walletAmount;
    }

    public void setWalletAmount(BigDecimal walletAmount) {
        this.walletAmount = scaled(walletAmount);
    }

    public BigDecimal getInStockGrossAmount() {
        return inStockGrossAmount;
    }

    public void setInStockGrossAmount(BigDecimal inStockGrossAmount) {
        this.inStockGrossAmount = scaled(inStockGrossAmount);
    }

    /**
     * Stock as investment counts it: gross less live-demo units held past 90 days.
     *
     * <p>This is what {@code InventoryService.getTotalAmountInStock()} returns and what both
     * original investment paths assigned to {@code PartnerDailyInvestment.inStockAmount} — which is
     * why {@code getTotalInvestment()} carries no separate demo term. Derived rather than stored so
     * that the daily demo refresh flows through to stock and base with nothing to keep in sync.
     */
    public BigDecimal getNetInStockAmount() {
        return scaled(scaled(inStockGrossAmount).subtract(scaled(demoExcludedAmount)));
    }

    public BigDecimal getUnbilledAmount() {
        return unbilledAmount;
    }

    public void setUnbilledAmount(BigDecimal unbilledAmount) {
        this.unbilledAmount = scaled(unbilledAmount);
    }

    public BigDecimal getGrnPendingAmount() {
        return grnPendingAmount;
    }

    public void setGrnPendingAmount(BigDecimal grnPendingAmount) {
        this.grnPendingAmount = scaled(grnPendingAmount);
    }

    public BigDecimal getActivatedStockAmount() {
        return activatedStockAmount;
    }

    public void setActivatedStockAmount(BigDecimal activatedStockAmount) {
        this.activatedStockAmount = scaled(activatedStockAmount);
    }

    public BigDecimal getActivatedGrnPendingAmount() {
        return activatedGrnPendingAmount;
    }

    public void setActivatedGrnPendingAmount(BigDecimal activatedGrnPendingAmount) {
        this.activatedGrnPendingAmount = scaled(activatedGrnPendingAmount);
    }

    public BigDecimal getReturnInTransitAmount() {
        return returnInTransitAmount;
    }

    public void setReturnInTransitAmount(BigDecimal returnInTransitAmount) {
        this.returnInTransitAmount = scaled(returnInTransitAmount);
    }

    public BigDecimal getUtilizedAmount() {
        return utilizedAmount;
    }

    public void setUtilizedAmount(BigDecimal utilizedAmount) {
        this.utilizedAmount = scaled(utilizedAmount);
    }

    public BigDecimal getAgedAppleStockAmount() {
        return agedAppleStockAmount;
    }

    public void setAgedAppleStockAmount(BigDecimal agedAppleStockAmount) {
        this.agedAppleStockAmount = scaled(agedAppleStockAmount);
    }

    public int getAgedAppleQty() {
        return agedAppleQty;
    }

    public void setAgedAppleQty(int agedAppleQty) {
        this.agedAppleQty = agedAppleQty;
    }

    public BigDecimal getDemoExcludedAmount() {
        return demoExcludedAmount;
    }

    public void setDemoExcludedAmount(BigDecimal demoExcludedAmount) {
        this.demoExcludedAmount = scaled(demoExcludedAmount);
    }

    public LocalDate getAgedRefreshedOn() {
        return agedRefreshedOn;
    }

    public void setAgedRefreshedOn(LocalDate agedRefreshedOn) {
        this.agedRefreshedOn = agedRefreshedOn;
    }

    public BigDecimal getTotalInvestment() {
        return totalInvestment;
    }

    public void setTotalInvestment(BigDecimal totalInvestment) {
        this.totalInvestment = scaled(totalInvestment);
    }

    public BigDecimal getBaseValue() {
        return baseValue;
    }

    public void setBaseValue(BigDecimal baseValue) {
        this.baseValue = scaled(baseValue);
    }

    public LocalDateTime getSnapshotTimestamp() {
        return snapshotTimestamp;
    }

    public void setSnapshotTimestamp(LocalDateTime snapshotTimestamp) {
        this.snapshotTimestamp = snapshotTimestamp;
    }

    @Override
    public String toString() {
        return "PartnerInvestment{fofoId=" + fofoId
                + ", wallet=" + walletAmount
                + ", inStockGross=" + inStockGrossAmount
                + ", inStockNet=" + getNetInStockAmount()
                + ", unbilled=" + unbilledAmount
                + ", grnPending=" + grnPendingAmount
                + ", activatedStock=" + activatedStockAmount
                + ", activatedGrnPending=" + activatedGrnPendingAmount
                + ", agedApple=" + agedAppleStockAmount
                + ", demoExcluded=" + demoExcludedAmount
                + ", utilised=" + utilizedAmount
                + ", total=" + totalInvestment
                + ", base=" + baseValue
                + ", at=" + snapshotTimestamp + '}';
    }
}