CostedOrder.java

package com.tradecloud.domain.costing.clean;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.tradecloud.common.base.HibernateUtils;
import com.tradecloud.domain.common.Currency;
import com.tradecloud.domain.costing.CostableType;
import com.tradecloud.domain.costing.CostingOrder;
import com.tradecloud.domain.document.invoice.UnitPricePerItem;
import com.tradecloud.domain.model.ordermanagement.OrderFinance;
import com.tradecloud.domain.model.ordermanagement.TradeFinance;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.*;
import org.hibernate.annotations.ForeignKey;

import javax.persistence.*;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Entity
@Table(name = "costedorder")
@Access(AccessType.FIELD)
@XmlAccessorType(XmlAccessType.FIELD)
public class CostedOrder extends CostedBase<CostedConsignment, CostedLineItem> implements CostingOrder, OrderFinance {

    private static final long serialVersionUID = 1L;

    /**
     * The bidirectional link back to the parent {@link CostedConsignment}.
     */
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @ForeignKey(name = "fk_costedconsignment")
    @XmlIDREF
    @JsonManagedReference
    private CostedConsignment costedConsignment;

    @XmlElementWrapper(name = "CostedLineItems")
    @XmlElement(name = "CostedLineItem")
    @OneToMany(mappedBy = "costedOrder", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    @Fetch(value = FetchMode.SELECT)
    @OrderBy("addedToOrderDate, code")
    @JsonBackReference
    private List<CostedLineItem> costedLineItems = new ArrayList<CostedLineItem>();

    @NotNull(message = "Number should not be null")
    @XmlID
    // XML id used for setting parent order in child lineitem
    @NaturalId
    private String number;

    @NotNull(message = "Reference should not be null")
    @XmlID
    // XML id used for setting parent order in child lineitem
    //@NaturalId
    private String reference;

    @Transient
    @XmlTransient
    private BigDecimal cachedOrderWeight;

    @XmlAttribute
    @Temporal(TemporalType.TIMESTAMP)
    private Date addedToConsignmentDate;

    @Enumerated(value = EnumType.STRING)
    @XmlAttribute
    private UnitPricePerItem unitPricePerItem;

    @ManyToOne
    @XmlElement(name = "Currency", required = true)
    private Currency currency;

    private String shippingReference;

    @XmlElement
    protected BigDecimal customsRateOfExchange;

    @Transient
    private BigDecimal rate;

    @Transient
    @XmlTransient
    private boolean refreshCosting;

    private String summaryReference;

    @OneToMany(cascade = {CascadeType.ALL}, orphanRemoval = true, fetch = FetchType.LAZY)
    @JsonIgnore
    private List<TradeFinance> tradeFinance;

    @Transient
    @XmlTransient
    BigDecimal unitQuantityExclHanger;

    @Basic(fetch = FetchType.LAZY)
    @Formula(value = "(select sum(c.unitquantity) from costedlineitem c  where c.costedorder_id=id ) ")
    private BigDecimal unitquantity;

    public CostedOrder() {
    }

    public CostedOrder(String number, String reference) {
        this.number = number;
        this.reference = reference;
    }

    public void removeCostedLineItem(CostedLineItem costedLineItem) {
        if (costedLineItem == null) {
            return;
        }

        costedLineItems.remove(costedLineItem);
        costedLineItem.setCostedOrder(null);
    }

    public List<CostedLineItem> getCostedLineItems() {
        return costedLineItems;
    }

    public void getCostedLineItems(List<CostedLineItem> costedLineItems) {
        costedLineItems.addAll(getCostedLineItems());
    }

    public void setCostedLineItems(List<CostedLineItem> costedLineItems) {
        this.costedLineItems = costedLineItems;
    }

    public void addCostedLineItem(CostedLineItem costedLineItem) {
        this.getCostedLineItems().add(costedLineItem);
        costedLineItem.setCostedOrder(this);
    }

    public List<CostedLineItem> getCostedLineItemsList() {
        return new ArrayList<>(costedLineItems);
    }

    @Override
    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String getReference() {
        return reference;
    }

    public void setReference(String reference) {
        this.reference = reference;
    }

    public CostedConsignment getCostedConsignment() {
        return costedConsignment;
    }

    public void setCostedConsignment(CostedConsignment costedConsignment) {
        this.costedConsignment = costedConsignment;
    }

    @Override
    public void accept(CostingVisitor costingVisitor) {
        for (CostedLineItem costedLineItem : costedLineItems) {
            costedLineItem.accept(costingVisitor);
        }
        costingVisitor.visit(this);
    }

    @Override
    public void acceptVisitParentFirst(CostingVisitor costingVisitor) {
        costingVisitor.visit(this);
        for (CostedLineItem costedLineItem : costedLineItems) {
            costedLineItem.acceptVisitParentFirst(costingVisitor);
        }
    }

    @Override
    public String toString() {
        return "CostedOrder [number=" + number + ", reference=" + reference + "]";
    }

    @Override
    public CostableType getCostableType() {
        return CostableType.ORDER;
    }

    @Override
    public CostedConsignment getParent() {
        return costedConsignment;
    }

    @Override
    public List<CostedLineItem> getCostedChildren() {
        List<CostedLineItem> results = new ArrayList<CostedLineItem>();
        for (CostedLineItem costedLineItem : getCostedLineItems()) {
            results.add(costedLineItem);
        }
        return results;
    }

    @Override
    public CostedConsignment getConsignment() {
        return costedConsignment;
    }

    public BigDecimal getUnitQuantity() {
        if (unitquantity == null) {
            BigDecimal sumAllItems = BigDecimal.ZERO;
            for (CostedLineItem costedLineItem : costedLineItems) {
                sumAllItems = sumAllItems.add(costedLineItem.getUnitQuantity());
            }
            return sumAllItems;
        } else {
            return unitquantity;
        }
    }

    public BigDecimal getSumUnitSellingPrice() {
        BigDecimal sumAllItems = BigDecimal.ZERO;
        for (CostedLineItem costedLineItem : costedLineItems) {
            if (costedLineItem.getLineItem().getSellPriceExclusiveAmount() != null) {
                sumAllItems = sumAllItems.add(costedLineItem.getLineItem().getSellPriceExclusiveAmount());
            }
        }
        return sumAllItems;
    }

    public BigDecimal getSumUnitSellingPriceInclVAT() {
        BigDecimal sumAllItems = BigDecimal.ZERO;
        for (CostedLineItem costedLineItem : costedLineItems) {
            if (costedLineItem.getLineItem().getSellPriceInclusiveAmount() != null) {
                sumAllItems = sumAllItems.add(costedLineItem.getLineItem().getSellPriceInclusiveAmount());
            }
        }
        return sumAllItems;
    }

    public BigDecimal getAllItemTotals() {
        BigDecimal sumAllItems = BigDecimal.ZERO;
        for (CostedLineItem costedLineItem : costedLineItems) {
            sumAllItems = sumAllItems.add(costedLineItem.getTotalPrice());
        }
        return sumAllItems;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(number)
                .append(reference)
                .append(summaryReference)
                .toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        // Do not use class.equals. This can return false for proxy objects
        if (!HibernateUtils.proxyClassEquals(this, obj)) {
            return false;
        }

        CostedOrder other = (CostedOrder) obj;

        return new EqualsBuilder()
                .append(number, other.getNumber())
                .append(reference, other.getReference())
                .append(summaryReference, other.getSummaryReference())
                .isEquals();
    }

    public void setCachedOrderWeight(BigDecimal orderWeight) {
        cachedOrderWeight = orderWeight;
    }

    public BigDecimal getCachedOrderWeight() {
        return cachedOrderWeight;
    }

    @Override
    public String getKey() {
        return new StringBuilder(getClass().getCanonicalName()).append(toString()).append(hashCode()).toString();
    }

    /**
     * @return the addedToConsignmentDate
     */
    public Date getAddedToConsignmentDate() {
        return addedToConsignmentDate;
    }

    /**
     * @param addedToConsignmentDate the addedToConsignmentDate to set
     */
    public void setAddedToConsignmentDate(Date addedToConsignmentDate) {
        this.addedToConsignmentDate = addedToConsignmentDate;
    }

    @Override
    public boolean isOrder() {
        return true;
    }

    public UnitPricePerItem getUnitPricePerItem() {
        return unitPricePerItem;
    }

    public void setUnitPricePerItem(UnitPricePerItem unitPricePerItem) {
        this.unitPricePerItem = unitPricePerItem;
    }

    public Currency getCurrency() {
        return currency;
    }

    public void setCurrency(Currency currency) {
        this.currency = currency;
    }

    public String getShippingReference() {
        return shippingReference;
    }

    public void setShippingReference(String shippingReference) {
        this.shippingReference = shippingReference;
    }

    @Override
    public String getReferenceWithShippingRef() {
        return StringUtils.isEmpty(shippingReference) ? getReference() : getReference() + " - " + shippingReference;
    }

    public BigDecimal getCustomsRateOfExchange() {
        return customsRateOfExchange;
    }

    public void setCustomsRateOfExchange(BigDecimal customsRateOfExchange) {
        this.customsRateOfExchange = customsRateOfExchange;
    }

    public BigDecimal getRate() {
        return rate;
    }

    public void setRate(BigDecimal rate) {
        this.rate = rate;
    }

    public boolean getRefreshCosting() {
        return refreshCosting;
    }

    public void setRefreshCosting(boolean refreshCosting) {
        this.refreshCosting = refreshCosting;
    }

    public String getSummaryReference() {
        return summaryReference;
    }

    public void setSummaryReference(String summaryReference) {
        this.summaryReference = summaryReference;
    }

    @Override
    public boolean isCustomsRateOfExchangeOverridden() {
        return false;
    }

    @Override
    public String getOrderReference() {
        return reference;
    }

    public List<TradeFinance> getTradeFinance() {
        return tradeFinance;
    }

    public void setTradeFinance(List<TradeFinance> tradeFinance) {
        this.tradeFinance = tradeFinance;
    }

    public BigDecimal getQuantityExclHanger() {
        if (unitQuantityExclHanger == null) {
            BigDecimal unitQuantity = getCostLineCosting().getCostingCell(CostLineNames.BASE_SUPPLY_COSTS_QUANTITY).getTransactionAmount();
            BigDecimal hangerQty = getHangerItemQuantities();
            unitQuantityExclHanger = unitQuantity.subtract(hangerQty);
        }
        return unitQuantityExclHanger;
    }

    private BigDecimal getHangerItemQuantities() {
        return getCostedLineItems().stream().filter(c -> c.getUnitQuantity() != null &&
                        c.getLineItem().getHangerType() != null)
                .map(CostedLineItem::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}