CostingSummary.java

package com.tradecloud.domain.costing.clean;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.tradecloud.common.base.PersistenceBase;
import com.tradecloud.domain.CustomVat;
import com.tradecloud.domain.Vat;
import com.tradecloud.domain.common.Currency;
import com.tradecloud.domain.container.Container;
import com.tradecloud.domain.costing.*;
import com.tradecloud.domain.costing.utils.CostingUtils;
import com.tradecloud.domain.document.invoice.*;
import com.tradecloud.domain.exception.InvalidEntityException;
import com.tradecloud.domain.exchangerate.DefaultRateOfExchanges;
import com.tradecloud.domain.exchangerate.RateOfExchanges;
import com.tradecloud.domain.model.ordermanagement.Consignment;
import com.tradecloud.domain.model.ordermanagement.Order;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
import com.tradecloud.domain.party.ServiceProvider;
import com.tradecloud.domain.shipment.Shipment;
import org.hibernate.annotations.ForeignKey;

import javax.persistence.*;
import javax.xml.bind.annotation.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * Parent classes of all Costings. (Estimate and Actual).
 *
 * @param <C>  Costable
 * @param <OC> OriginalCostable
 */
@Entity
@Table(name = "costingsummary")
@Inheritance(strategy = InheritanceType.JOINED)
@Access(AccessType.FIELD)
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class CostingSummary<C extends Costable, OC extends Costable> extends PersistenceBase implements CostingResultLookup {

    private static final long serialVersionUID = 1L;

    long costingDurationMilliSeconds;

    @XmlElement
    private BigDecimal totalCostExVAT = BigDecimal.ZERO;

    @XmlElement
    private BigDecimal totalCostIncVAT = BigDecimal.ZERO;

    @XmlElement
    private BigDecimal totalCostExVatAndInternalProvisions = BigDecimal.ZERO;

    /**
     * Supply Costs.
     */
    @XmlElement
    private BigDecimal supplyCostTotal = BigDecimal.ZERO;

    /**
     * Remaining Logistics Costs.
     */
    @XmlElement
    private BigDecimal remainingLogisticsCosts = BigDecimal.ZERO;

    /**
     * Currencies.
     */
    @XmlElement
    @ManyToOne
    private Currency costingCurrency;

    @XmlElement
    @ManyToOne
    private Currency saleCurrency;

    /**
     * Current state of costing.
     */
    @XmlElement
    private CostingState costingState = CostingState.UNCOSTED;

    /**
     * Costing Dates.
     */
    @Temporal(TemporalType.TIMESTAMP)
    @XmlElement
    private Date costingEffectiveDate;

    @Temporal(TemporalType.TIMESTAMP)
    @XmlElement
    private Date costingLastRatesRefreshDate;

    @Temporal(TemporalType.TIMESTAMP)
    @XmlElement
    private Date costingLastCalculatedDate;

    @Temporal(TemporalType.TIMESTAMP)
    @XmlElement
    private Date costingLastSignedOffDate;

    @Temporal(TemporalType.TIMESTAMP)
    @XmlElement
    private Date costingLastUnSignedOffDate;

    @XmlElementWrapper(name = "CostLineSummaries")
    @XmlElement(name = "CostLineSummary")
    @OneToMany(cascade = CascadeType.ALL)
    @ForeignKey(name = "fk_costingsummary", inverseName = "fk_costlinesummary")
    @JoinTable(name = "costingsummary_costlinesummaries", joinColumns = {@JoinColumn(name = "costingsummary_id", unique = false)},
            inverseJoinColumns = {@JoinColumn(name = "costlinesummary_id", unique = false)})
    @JsonManagedReference
    private List<CostLineSummary> costLineSummaries = new ArrayList<CostLineSummary>();


    /**
     * Cache of CostLineName keys and their related CostLineSummary values.
     */
    @Transient
    @XmlTransient
    private final Map<String, CostLineSummary> costLineSummaryCache = new WeakHashMap<>();

    @XmlTransient
    @Transient
    @JsonIgnore
    private boolean applyPlannedSettlementRates = true;

    @XmlTransient
    @Transient
    private final List<CostLineSummary> costlineSummariesBasedOnTotalCost = new ArrayList<>();

    @XmlTransient
    @Transient
    private final List<CostLineSummary> costlineSummariesWithoutCostFormula = new ArrayList<>();

    @Transient
    @XmlTransient
    private final Map<String, Map<CostCalculationLevel, Map<CostAllocationMethod, BigDecimal>>> costProportionCache = new HashMap<>();

    @Transient
    @XmlTransient
    //for each cost group keep a map of costed  and  Settlement Date.
    private final Map<CostGroup, Map<Costed, Date>> costGroupOrderSettlementDate = new EnumMap<>(CostGroup.class);

    @Transient
    @XmlTransient
    @JsonIgnore
    private ExchangeRateCache exchangeRateCache = new ExchangeRateCache();
    @Transient
    @XmlTransient
    private CustomVat customVat;

    @Transient
    @XmlTransient
    private Map additionalInfo = new HashMap();

    @Transient
    @XmlTransient
    private Vat vat;

    @Transient
    @XmlTransient
    private boolean costCompare;

    public abstract C getCostable();

    public abstract void setCostable(C costable);

    public abstract void setOriginalCostable(OC costable);

    public abstract OC getOriginalCostable();

    /**
     * Return the relevant costed structure so visitor can be called on it.
     *
     * @return
     */
    public abstract Costed<? extends Costed, ? extends Costed> getCostedStructure();

    public BigDecimal getTotalCostExVAT() {
        return totalCostExVAT;
    }

    public BigDecimal getCostExcVatExcCostline() {
        BigDecimal value = getCostedStructure().getTotalsDistribution().get(TotalsDistributionType.COST_ZAR_EXCL_VAT_EXC_EXCULUDED_COSTLINE);
        return Optional.ofNullable(value).orElse(BigDecimal.ZERO);
    }

    public void setTotalCostExVAT(BigDecimal totalCostExVAT) {
        this.totalCostExVAT = totalCostExVAT;
    }

    public BigDecimal getTotalCostIncVAT() {
        return totalCostIncVAT;
    }

    public void setTotalCostIncVAT(BigDecimal totalCostIncVAT) {
        this.totalCostIncVAT = totalCostIncVAT;
    }

    public List<CostLineSummary> getCostLineSummaries() {
        return costLineSummaries;
    }

    public void setCostLineSummaries(List<CostLineSummary> costLineSummaries) {
        this.costLineSummaries = costLineSummaries;
        // clear the cache
        costLineSummaryCache.clear();
    }

    public BigDecimal getSupplyCostTotal() {
        return supplyCostTotal;
    }

    public void setSupplyCostTotal(BigDecimal supplyCostTotal) {
        this.supplyCostTotal = supplyCostTotal;
    }

    public BigDecimal getRemainingLogisticsCosts() {
        return remainingLogisticsCosts;
    }

    public void setRemainingLogisticsCosts(BigDecimal remainingLogisticsCosts) {
        this.remainingLogisticsCosts = remainingLogisticsCosts;
    }

    @Override
    public Currency getCostingCurrency() {
        return costingCurrency;
    }

    public void setCostingCurrency(Currency costingCurrency) {
        this.costingCurrency = costingCurrency;
    }

    public Currency getSaleCurrency() {
        return saleCurrency;
    }

    public void setSaleCurrency(Currency saleCurrency) {
        this.saleCurrency = saleCurrency;
    }

    public CostingState getCostingState() {
        return costingState;
    }

    public void setCostingState(CostingState costingState) {
        this.costingState = costingState;
    }

    public Date getCostingEffectiveDate() {
        return costingEffectiveDate;
    }

    public void setCostingEffectiveDate(Date costingEffectiveDate) {
        this.costingEffectiveDate = costingEffectiveDate;
    }

    public Date getCostingLastRatesRefreshDate() {
        return costingLastRatesRefreshDate;
    }

    public void setCostingLastRatesRefreshDate(Date costingLastRatesRefreshDate) {
        this.costingLastRatesRefreshDate = costingLastRatesRefreshDate;
    }

    public Date getCostingLastCalculatedDate() {
        return costingLastCalculatedDate;
    }

    public void setCostingLastCalculatedDate(Date costingLastCalculatedDate) {
        this.costingLastCalculatedDate = costingLastCalculatedDate;
    }

    public Date getCostingLastSignedOffDate() {
        return costingLastSignedOffDate;
    }

    public void setCostingLastSignedOffDate(Date costingLastSignedOffDate) {
        this.costingLastSignedOffDate = costingLastSignedOffDate;
    }

    public Date getCostingLastUnSignedOffDate() {
        return costingLastUnSignedOffDate;
    }

    public void setCostingLastUnSignedOffDate(Date costingLastUnSignedOffDate) {
        this.costingLastUnSignedOffDate = costingLastUnSignedOffDate;
    }

    public long getCostingDurationMilliSeconds() {
        return costingDurationMilliSeconds;
    }

    public void setCostingDurationMilliSeconds(long costingDurationMilliSeconds) {
        this.costingDurationMilliSeconds = costingDurationMilliSeconds;
    }

    public void addCostLineSummary(CostLineSummary costLineSummary) {
        costLineSummaries.add(costLineSummary);
        // add to the cache
        costLineSummaryCache.put(costLineSummary.getCostLine().getCostLineTemplate().getCode(), costLineSummary);
    }

    @Override
    public BigDecimal lookupCostingResult(String costLineCode) {
        CostLineSummary costLineSummary = lookupCosting(costLineCode);

        if (costLineSummary != null) {
            return costLineSummary.getCostingCurrencyTotal();
        }
        return null;
    }

    public BigDecimal lookupTransactionCurrencyTotal(String costLineCode) {
        CostLineSummary costLineSummary = lookupCosting(costLineCode);

        if (costLineSummary != null) {
            return costLineSummary.getTransactionCurrencyTotal();
        }
        return null;
    }

    public Currency lookupTransactionCurrency(String costLineCode) {
        CostLineSummary costLineSummary = lookupCosting(costLineCode);

        if (costLineSummary != null) {
            return costLineSummary.getTransactionCurrency();
        }
        return null;
    }

    /**
     * Uses the costLineSummaryCache to reduce search times for the costing lookups after the cache is built for the first lookup.
     *
     * @param costLineCode
     * @return
     */
    public CostLineSummary lookupCosting(String costLineCode) {
        if (!costLineSummaryCache.containsKey(costLineCode)) {
            // build the cache
            for (CostLineSummary costLineSummary : costLineSummaries) {
                costLineSummaryCache.put(costLineSummary.getCostLine().getCostLineTemplate().getCode(), costLineSummary);
            }
        }

        return costLineSummaryCache.get(costLineCode);
    }

    public BigDecimal getTotalCostExVatAndInternalProvisions() {
        return totalCostExVatAndInternalProvisions;
    }

    public void setTotalCostExVatAndInternalProvisions(BigDecimal totalCostExVatAndInternalProvisions) {
        this.totalCostExVatAndInternalProvisions = totalCostExVatAndInternalProvisions;
    }

    public List<CostLineSummary> getCostlineSummariesBasedOnTotalCost() {
        return costlineSummariesBasedOnTotalCost;
    }

    public List<CostLineSummary> getCostlineSummariesWithoutCostFormula() {
        return costlineSummariesWithoutCostFormula;
    }

    public void clearTotalsCache() {
        clearTotals(getCostedStructure());
        costlineSummariesBasedOnTotalCost.clear();
        costlineSummariesWithoutCostFormula.clear();
        costProportionCache.clear();
        costGroupOrderSettlementDate.clear();
    }

    private void clearTotals(Costed<? extends Costed, ? extends Costed> costed) {
        if (costed != null) {
            costed.getCostedTotals().clearTotals();
            for (Costed child : costed.getCostedChildren()) {
                clearTotals(child);
            }
        }
    }

    public BigDecimal lookupTotalCost(Costed costed, boolean includeVat, boolean spotRate, CostGroup... excludeFromTotal) {
        BigDecimal totalCost = lookupCost(costed, spotRate, false, null, excludeFromTotal);
        if (includeVat) {
            BigDecimal vat = costed.getCostedTotals().lookupTotalVat();
            if (vat != null) {
                totalCost = totalCost.add(vat);
            }
        }
        return totalCost;
    }

    public BigDecimal lookupTotalCost(PercentageLookUp percentageLookUp) {

        return lookupCosting(percentageLookUp);
    }

    private boolean applicableCostLine(CostLine costLine, PercentageLookUp percentageLookUp) {

        return hasMatchingPayerType(percentageLookUp, costLine) &&
                hasCostGroup(percentageLookUp, costLine) &&
                !hasMatchingCostApplicationBasis(costLine, percentageLookUp) &&
                !isSummaryCostline(percentageLookUp, costLine) &&
                !excludeCostLine(percentageLookUp, costLine) &&
                !inCostingCurrency(percentageLookUp, costLine) &&
                hasMatchingCostline(costLine, percentageLookUp);
    }

    private boolean hasMatchingCostline(CostLine costLine, PercentageLookUp percentageLookUp) {
        CostLineTemplate costLineTemplate = costLine.getCostLineTemplate();
        List<String> costlineNames = percentageLookUp.getCostlineNames();
        boolean allCostlinesMatch = costlineNames.isEmpty();
        return allCostlinesMatch || costlineNames.contains(costLineTemplate.getCode());
    }

    private boolean hasCostGroup(PercentageLookUp percentageLookUp, CostLine costLine) {
        CostLineTemplate costLineTemplate = costLine.getCostLineTemplate();
        return percentageLookUp.getCostGroups().contains(costLineTemplate.getCostGroup());
    }

    private boolean excludeCostLine(PercentageLookUp percentageLookUp, CostLine costLine) {
        CostLineTemplate costLineTemplate = costLine.getCostLineTemplate();
        return percentageLookUp.getCostlineToExclude().contains(costLineTemplate.getCode());
    }

    private CostLineSummary getCostlineSummary(CostLine costLine) {
        for (CostLineSummary costLineSummary : costLineSummaries) {
            if (costLineSummary.getCostLine().getCostLineTemplate().equals(costLine.getCostLineTemplate())) {
                return costLineSummary;
            }
        }
        return null;
    }

    private boolean isSummaryCostline(PercentageLookUp percentageLookUp, CostLine costLine) {
        CostLineSummary costLineSummary = percentageLookUp.getCostLineSummary();
        return costLineSummary.getCostLine().getCostLineTemplate().equals(costLine.getCostLineTemplate());
    }

    public BigDecimal lookupTotalCostInCostingCurrency(PercentageLookUp percentageLookUp) {
        return lookupCosting(percentageLookUp);
    }

    private BigDecimal lookupCosting(PercentageLookUp percentageLookUp) {
        BigDecimal totalCost = BigDecimal.ZERO;
        List<CostLineCostingCell> costLineCostingCells = percentageLookUp.getCosted().getCostLineCosting().getCostLineCostingCells();
        for (CostLineCostingCell costLineCostingCell : costLineCostingCells) {
            BigDecimal costingCellAmount = calculateCostingCellAmount(percentageLookUp, costLineCostingCell);
            totalCost = totalCost.add(costingCellAmount);
        }
        return totalCost;
    }

    private BigDecimal calculateCostingCellAmount(PercentageLookUp percentageLookUp, CostLineCostingCell costLineCostingCell) {
        CostLine costLine = costLineCostingCell.getCostLine();
        if (applicableCostLine(costLine, percentageLookUp)) {
            CostLineSummary costlineSummary = getCostlineSummary(costLine);
            if (costlineSummary != null) {
                BigDecimal rateOfExchange = getRateOfExchange(percentageLookUp, costlineSummary);

                if (costLine.getTransactionCurrency() == null) {
                    throw new InvalidEntityException("Missing currency on costline: " + costLine.getCostLineTemplate().getCode());
                }

                if (costLine.getTransactionCurrency().getCode().equals(costingCurrency.getCode())) {
                    return costLineCostingCell.getTransactionAmount();
                } else {
                    return costLineCostingCell.getTransactionAmount().multiply(rateOfExchange);
                }
            }
        }
        return BigDecimal.ZERO;
    }

    private BigDecimal getRateOfExchange(PercentageLookUp percentageLookUp, CostLineSummary costlineSummary) {
        return DefaultRateOfExchanges.getRateOfExchange(
                CostingUtils.findSpotAndForward(percentageLookUp.getCosted(), costlineSummary, false),
                percentageLookUp.getRateOfExchangeType());
    }

    private boolean inCostingCurrency(PercentageLookUp percentageLookUp, CostLine costLine) {
        return percentageLookUp.isCheckCurrency() && costLine.getTransactionCurrency().getCode().equals(costingCurrency.getCode());
    }

    private boolean hasMatchingPayerType(PercentageLookUp percentageLookUp, CostLine costLine) {
        CostLinePayerType costLinePayerType = percentageLookUp.getCostLinePayerType();
        return costLine.getCostLinePayerType().equals(costLinePayerType);
    }

    public boolean hasMatchingCostApplicationBasis(CostLine costLine, PercentageLookUp percentageLookUp) {
        CostApplicationBasis costApplicationBasis = percentageLookUp.getCostApplicationBasis();
        CostLineSummary costLineSummary = getCostlineSummary(costLine);
        if (costLineSummary != null) {
            CostFormula costFormula = costLineSummary.getCostFormula();
            if (costFormula != null && costFormula.getCostApplicationBasis() == costApplicationBasis) {
                return true;
            }
        }

        return false;
    }

    public BigDecimal lookupCostGroupTotal(Costed costed, boolean spotRate, CostGroup... costGroups) {
        return lookupCost(costed, spotRate, true, null, costGroups);
    }

    private BigDecimal lookupCost(Costed costed, boolean spotRate, boolean inclusiveCostGroupSearch, CostingContextType contextType,
                                  CostGroup... costGroups) {
        BigDecimal totalCost = BigDecimal.ZERO;
        List<CostGroup> costGroupsToInclude = Arrays.asList(costGroups);
        for (CostGroup costGroup : CostGroup.values()) {
            if (costGroup == CostGroup.BASE_SUPPLY) {
                continue;
            }
            if (inclusiveCostGroupSearch && costGroupsToInclude.contains(costGroup) ||
                    !inclusiveCostGroupSearch && !costGroupsToInclude.contains(costGroup)) {
                BigDecimal bigDecimal;
                if (contextType == null) {
                    //will sum both Export and Import
                    bigDecimal = costed.getCostedTotals().lookupTotalCostExVat(costGroup, spotRate);
                } else {
                    switch (contextType) {
                        case IMPORT:
                            bigDecimal = costed.getCostedTotals().lookupTotalCostExVat(costGroup, true, spotRate);
                            break;
                        case EXPORT:
                            bigDecimal = costed.getCostedTotals().lookupTotalCostExVat(costGroup, false, spotRate);
                            break;
                        default:
                            //will sum both Export and Import
                            bigDecimal = costed.getCostedTotals().lookupTotalCostExVat(costGroup, spotRate);
                    }
                }
                if (bigDecimal != null) {
                    totalCost = totalCost.add(bigDecimal);
                }

            }
        }
        return totalCost;
    }

    public BigDecimal lookupBasisCost(Costed costed, CostApplicationBasis costApplicationBasis, boolean spotRate) {
        if (costApplicationBasis.isBasedOnTotalCost()) {
            boolean totalCostInclVatBondedExclIntProv = costApplicationBasis == CostApplicationBasis.TOTAL_COST_INCVAT_BONDED_EXINTPROV;
            boolean incVat = CostApplicationBasis.TOTAL_COST_INCVAT.equals(costApplicationBasis) ||
                    totalCostInclVatBondedExclIntProv;
            CostGroup costGroup = CostApplicationBasis.TOTAL_COST_EXINTPROV.equals(costApplicationBasis) ||
                    totalCostInclVatBondedExclIntProv ? CostGroup.INTERNAL_PROVISIONS : null;
            BigDecimal lookupTotalCost = lookupTotalCost(costed, incVat, spotRate, costGroup);
            if (totalCostInclVatBondedExclIntProv) {
                lookupTotalCost = Optional.ofNullable(lookupTotalCost).orElse(BigDecimal.ZERO)
                        .add(costed.getCostedTotals().getBondedItemsCustomsTotals());
            }

            if (costApplicationBasis.equals(CostApplicationBasis.TOTAL_COST_EXVAT_EXCLUDED_COST_LINES)) {
//                BigDecimal totalCost = lookupTotalCost(costed, incVat, spotRate, costGroup);
                lookupTotalCost = spotRate ? lookupTotalCost.subtract(costed.getCostedTotals().getExculdedCostlineAmountAtSpot()) :
                        lookupTotalCost.subtract(costed.getCostedTotals().getExculdedCostlineAmountAtForward());
            }
            return lookupTotalCost;
        } else if (CostApplicationBasis.AGENT_COST.equals(costApplicationBasis)) {
            throw new RuntimeException("Cant determine AGENT_COST");
        }
        BigDecimal basis = lookupCostApplicationBasis(costed, costApplicationBasis);
        return basis != null ? basis : BigDecimal.ZERO;
    }

    public BigDecimal lookupDutiableCost(Costed costed, boolean spot) {
        BigDecimal dutiable = costed.getCostedTotals().lookupDutiable(spot);
        return dutiable != null ? dutiable : BigDecimal.ZERO;
    }

    public Set<? extends Container> getContainers() {
        return getOriginalCostable().getContainers();
    }

    public abstract Set<ActualConsignment> getActualConsignmentSet();

    public abstract Shipment getShipment();

    public Order getOrderFromActual(Costed costed) {
        Consignment consignment = getOriginalCostable(costed);
        switch (costed.getCostableType()) {
            case ORDER:
                return CostingStructureMatcher.getOrder(consignment, (ActualOrder) costed);
            case ORDER_LINE_ITEM:
                return CostingStructureMatcher.getOrder(consignment, ((ActualLineItem) costed).getActualOrder());
            default:
                return consignment.getOrdersList().get(0);
        }
    }

    public Consignment getOriginalCostable(Costed costed) {
        if (costed instanceof Actual) {
            ActualConsignment actualConsignment = getActualConsignment(costed);
            List<Consignment> activeConsignments = getShipment().getActiveConsignments();
            for (Consignment activeConsignment : activeConsignments) {
                //if (activeConsignment.match(actualConsignment)) {
                if (CostingStructureMatcher.match(activeConsignment, actualConsignment)) {
                    return activeConsignment;
                }
            }
            //if we come here something is wrong.
            return null;

        } else {
            return (Consignment) getOriginalCostable();
        }
    }

    public ActualConsignment getActualConsignment(Costed costed) {
        if (costed instanceof ActualConsignment) {
            return (ActualConsignment) costed;
        } else if (costed instanceof ActualOrder) {
            return (ActualConsignment) costed.getParent();
        } else if (costed instanceof ActualLineItem) {
            return (ActualConsignment) costed.getParent().getParent();
        }
        return getActualConsignmentSet().iterator().next();
    }

    public Currency getOrderCurrencyFromCostedItem(Costed costed) {
        Costed costedOrder = costed.getParent();

        if (costedOrder.isActual()) {
            ActualOrder actualOrder = (ActualOrder) costedOrder;
            return actualOrder.getCurrency();
        } else {
            CostedOrder order = (CostedOrder) costedOrder;
            return order.getCurrency();
        }
    }

    /**
     * only want to calculate planned settlements rates once per costing.
     *
     * @return
     */
    public boolean isApplyPlannedSettlementRates() {
        return applyPlannedSettlementRates;
    }

    public void setApplyPlannedSettlementRates(boolean applyPlannedSettlementRates) {
        this.applyPlannedSettlementRates = applyPlannedSettlementRates;
    }

    public BigDecimal lookupCostApplicationBasis(Costed costed, CostApplicationBasis costApplicationBasis) {
        return costed.getCostedTotals().lookupCostApplicationBasis(costApplicationBasis);
    }

    public void addAdditionalSupplyCosts(CostLineSummary costLineSummary, Costed costed, BigDecimal cost, RateOfExchanges rateOfExchanges,
                                         RateOfExchanges rateFeed) {
        boolean totalUnitPrice = isTotalUnitPrice(costed);
        costed.getCostedTotals().addAdditionalSupplyCosts(costLineSummary.getCostLine(), costed, cost, rateOfExchanges, totalUnitPrice, rateFeed);
    }

    public void addSupplyCostBasis(CostLineSummary costLineSummary, Costed costed, BigDecimal cost, RateOfExchanges rateOfExchanges,
                                   RateOfExchanges rateFeed) {
        addSupplyCostBasis(costLineSummary, costed, cost, rateOfExchanges, false, rateFeed);
    }

    public void addSupplyCostBasis(CostLineSummary costLineSummary, Costed costed, BigDecimal cost, RateOfExchanges rateOfExchanges,
                                   boolean sampleOrSpareItem, RateOfExchanges rateFeed) {
        costed.getCostedTotals().addSupplyCostBasis(costLineSummary.getCostLine(), costed, cost, rateOfExchanges, sampleOrSpareItem, rateFeed);
    }

    public abstract boolean isTotalUnitPrice(Costed costed);

    public void addDutiableCost(CostLineSummary costLineSummary,
                                Costed costed,
                                BigDecimal cost,
                                RateOfExchanges rateOfExchanges,
                                BigDecimal foreignToForeignRate) {
        boolean totalUnitPrice = isTotalUnitPrice(costed);
//        if (getSummaryCostType() == SummaryCostType.WORK_SHEET) {
//            totalUnitPrice = getShipment().get;
//        }
        costed.getCostedTotals().addDutiableCost(costLineSummary.getCostLine(), costed, cost, rateOfExchanges, totalUnitPrice, foreignToForeignRate);
    }

    public void addDutiableCostInit(CostLineSummary costLineSummary,
                                    Costed costed,
                                    BigDecimal cost,
                                    RateOfExchanges rateOfExchanges,
                                    BigDecimal foreignToForeignRate,
                                    boolean totalUnitPrice) {
//        if (getSummaryCostType() == SummaryCostType.WORK_SHEET) {
//            totalUnitPrice = false;
//        }
        costed.getCostedTotals().addDutiableCost(costLineSummary.getCostLine(), costed, cost, rateOfExchanges, totalUnitPrice, foreignToForeignRate);
    }

    public Map<String, Map<CostCalculationLevel, Map<CostAllocationMethod, BigDecimal>>> getCostProportionCache() {
        return costProportionCache;
    }

    public void addCostGroupCostedSetlementDate(CostGroup costGroup, Costed costed, Date settlementDate) {
        Map<Costed, Date> costedDateMap = costGroupOrderSettlementDate.get(costGroup);
        if (costedDateMap == null) {
            costedDateMap = new HashMap<>();
            costGroupOrderSettlementDate.put(costGroup, costedDateMap);
        }
        costedDateMap.put(costed, settlementDate);
    }

    public Date getSettlementDate(CostGroup costGroup, Costed costed) {
        Map<Costed, Date> costedDateMap = costGroupOrderSettlementDate.get(costGroup);
        if (costedDateMap == null) {
            return null;
        } else {
            return costedDateMap.get(costed);
        }
    }

    public ExchangeRateCache getExchangeRateCache() {
        return exchangeRateCache;
    }

    public void setExchangeRateCache(ExchangeRateCache exchangeRateCache) {
        this.exchangeRateCache = exchangeRateCache;
    }

    public BigDecimal lookupTotalCost(Costed costed, CostingContextType costingContextType) {
        return costed.getCostedTotals().getTotalCost(false, costingContextType);
    }

    public CustomVat getCustomVat() {
        return customVat;
    }

    public void setCustomVat(CustomVat customVat) {
        this.customVat = customVat;
    }

    public Map<String, Object> getAdditionalInfo() {
        return additionalInfo;
    }

    public void setAdditionalInfo(Map<String, Object> additionalInfo) {
        this.additionalInfo = additionalInfo;
    }

    public ServiceProvider getFreightForwarder() {
        return null;
    }

    public abstract OrganisationalUnit findStdRateOrgUnit();

    public SummaryCostType getSummaryCostType() {
        return null;
    }

    public boolean isDeclarationInvoice() {
        return false;
    }

    public boolean isSPIInvoice() {
        return false;
    }

    public boolean isInvoice() {
        return false;
    }

    public CostLineSummary getCostlineSummary(String code) {
        return costLineSummaries.stream().filter(cl -> cl.getCode().equals(code)).findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Cannot find costlinesummary " + code));
    }

    public abstract BigDecimal lookupMatchingItemAmount(ActualLineItem actual, String costLineCode);

    public Vat getVat() {
        return vat;
    }

    public void setVat(Vat vat) {
        this.vat = vat;
    }

    public boolean isCostCompare() {
        return costCompare;
    }

    public void setCostCompare(boolean costCompare) {
        this.costCompare = costCompare;
    }

    public void setPrimaryCosting(boolean b) {

    }

    public boolean isPrimaryCosting() {
        return true;
    }

    public CostSummaryAdditionalInfo getCostSummaryAdditionalInfo() {
        return null;
    }
}