AbstractSupplier.java

package com.tradecloud.domain.supplier;

import com.tradecloud.common.base.HibernateUtils;
import com.tradecloud.domain.common.Currency;
import com.tradecloud.domain.common.Incoterm;
import com.tradecloud.domain.document.invoice.UnitPricePerItem;
import com.tradecloud.domain.event.Event;
import com.tradecloud.domain.event.SupplierEvent;
import com.tradecloud.domain.model.payment.ActualPaymentBasis;
import com.tradecloud.domain.model.payment.EstimatedPaymentBasis;
import com.tradecloud.domain.model.payment.PaymentMethod;
import com.tradecloud.domain.model.payment.PaymentTerm;
import com.tradecloud.domain.model.shipment.ShippingMode;
import com.tradecloud.domain.party.Bank;
import com.tradecloud.domain.party.ServiceProvider;
import com.tradecloud.domain.party.base.Address;
import com.tradecloud.domain.party.base.Company;
import com.tradecloud.domain.party.base.Contact;
import com.tradecloud.domain.place.NamedPlace;
import com.tradecloud.domain.place.PlaceOfLoading;
import com.tradecloud.domain.state.Stateful;
import com.tradecloud.domain.treasury.TreasuryBank;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.ForeignKey;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlTransient;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

@MappedSuperclass
public abstract class AbstractSupplier extends Company implements Stateful<SupplierState, SupplierEvent> {

    private final static Collection<SupplierState> NON_EDITABLE_STATES = Arrays.asList(SupplierState.LIQUIDATED,
            SupplierState.SUSPENDED);

    private static final long serialVersionUID = 1L;

    public enum Type {
        FULL, TEMPLATE, ELC
    }

    /**
     * The purchase order events, used to keep track of the history.
     */
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @Fetch(value = FetchMode.SUBSELECT)
    @XmlElementWrapper(name = "SupplierEvents", nillable = true)
    @XmlElement(name = "SupplierEvent")
    @OrderBy("createDateTime")
    private List<SupplierEvent> events = new ArrayList<>();

    @ManyToOne
    @XmlElement(name = "PaymentTerm", required = true)
    private PaymentTerm paymentTerm;

    @ManyToOne
    @XmlElement(name = "PaymentTermDays", required = true)
    private PaymentTerm paymentTermDays;

    @ManyToOne
    // @NotNull(message = "Payment method is required")
    @XmlElement(name = "PaymentMethod", required = true)
    private PaymentMethod paymentMethod;

    @ManyToOne
    // @NotNull(message = "Estimated payment basis is required")
    @XmlElement(name = "EstimatedPaymentBasis", required = true)
    private EstimatedPaymentBasis estimatedPaymentBasis;

    @ManyToOne
    // @NotNull(message = "Actual payment basis is required")
    @XmlElement(name = "ActualPaymentBasis", required = true)
    private ActualPaymentBasis actualPaymentBasis;

    @ManyToOne
    // @NotNull(message = "Currency is required")
    @XmlElement(name = "Currency", required = true)
    @ForeignKey(name = "fk_currency")
    private Currency currency;

    @ManyToOne
    @XmlElement(name = "Incoterm", required = true)
    // @NotNull(message = "Incoterm is required")
    private Incoterm incoterm;

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

    /**
     * The freight forwarder used by this supplier.
     */
    @XmlElement(name = "FreightForwarder")
    @ManyToOne
    @ForeignKey(name = "fk_freightforwarder")
    private ServiceProvider freightForwarder;

    /**
     * The agent who will be responsible for clearing this supplier's goods.
     */
    @XmlElement(name = "ClearingAgent")
    @ManyToOne
    @ForeignKey(name = "fk_clearingagent")
    private ServiceProvider clearingAgent;

    /**
     * The transporter who will transport this supplier's goods to the final
     * destination.
     */
    @XmlElement(name = "Transporter")
    @ManyToOne
    @ForeignKey(name = "fk_transporter")
    private ServiceProvider transporter;

    /**
     * Indicates if the supplier supports part shipments.
     */
    @XmlAttribute(required = true)
    // @NotNull
    private Boolean allowPartShipment;

    /**
     * Indicates if the supplier supports trans shipments.
     */
    @XmlAttribute(required = true)
    // @NotNull
    private Boolean allowTransShipment;

    /**
     * Indicates that a LC can be drawn for X% more than the face value of the LC.
     * Should not be less than 0 and more than 10.
     */
    @XmlAttribute
    private BigDecimal lcToleranceAbove;

    /**
     * Indicates that a LC can be drawn for X% less than the face value of the LC.
     * Should not be less than 0 or more than 10.
     */
    @XmlAttribute
    private BigDecimal lcToleranceBelow;

    @XmlAttribute
    private Integer presentationDays;

    /**
     * ERPTA-70. This was PlaceOfExpiry but business requested to make it a String
     */
    @XmlElement(name = "PlaceOfExpiry")
    private String placeOfExpiry;

    @ManyToOne
    @XmlElement(name = "Bank")
    private Bank advisingBank;

    @ManyToOne
    @JoinColumn(name = "nominatedBank_code")
    private TreasuryBank nominatedBank;

    /**
     * SupplierState INITIALISED after object initialisation. (This is an internal
     * state only)
     */
    @NotNull
    @XmlAttribute
    @Enumerated(EnumType.STRING)
    private SupplierState state = SupplierState.ACTIVE;

    /**
     * Whether or not the supplier was created through an integration.
     */
    @XmlAttribute
    @NotNull
    private Boolean integrated = Boolean.FALSE;

    /**
     * Whether or not all the mandatory fields have been populated. These fields are
     * configurable in the Client Config.
     */
    @XmlAttribute
    @NotNull
    private Boolean complete = Boolean.FALSE;

    @Transient
    private Boolean update = Boolean.FALSE;

    private Boolean splitPayment = Boolean.FALSE;

    @ManyToOne
    @XmlElement(name = "EstimatedPaymentBasis2", required = true)
    private EstimatedPaymentBasis estimatedPaymentBasis2;

    @ManyToOne
    @JoinColumn(name = "placeofloading_code")
    @ForeignKey(name = "fk_placeofloading")
    @XmlElement(name = "PlaceOfLoading", required = true)
    private PlaceOfLoading placeOfLoading;

    @XmlAttribute
    @Enumerated(value = EnumType.STRING)
    private ShippingMode shippingMode;

    private BigDecimal paymentBasisPercentage;

    private BigDecimal paymentBasis2Percentage;

    @XmlAttribute
    private BigDecimal buyingCommission;

    @XmlAttribute
    private BigDecimal earlySettlementDisc;

    @ManyToOne
    private NamedPlace namedPlace;

    @XmlElementWrapper(name = "NamedPlaces")
    @XmlElement(name = "NamedPlace")
    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "supplier_namedplace",
            joinColumns = {@JoinColumn(name = "supplier_id")},
            inverseJoinColumns = {@JoinColumn(name = "namedplace_code")})
    private List<NamedPlace> factoryList;

    @XmlTransient
    @Enumerated(EnumType.STRING)
    private Type type = Type.FULL;

    private boolean canContact;

    public AbstractSupplier() {
        super();
        allowPartShipment = Boolean.FALSE;
        allowTransShipment = Boolean.FALSE;
    }

    public AbstractSupplier(String name) {
        super(name);
        allowPartShipment = Boolean.FALSE;
        allowTransShipment = Boolean.FALSE;
        this.type = Type.FULL;
    }

    public AbstractSupplier(Address physicalAddress, Address postalAddress, List<Contact> contacts, String name,
                            String externalReference, String salesTaxRegistrationNumber, String vatRegistrationNumber,
                            String companyRegistrationNumber, String warehouseCode) {
        super(physicalAddress, postalAddress, contacts, name, externalReference, salesTaxRegistrationNumber,
                vatRegistrationNumber, companyRegistrationNumber, warehouseCode);
        allowPartShipment = Boolean.FALSE;
        allowTransShipment = Boolean.FALSE;
    }

    public AbstractSupplier(Address physicalAddress, Address postalAddress, List<Contact> contacts, String name,
                            String externalReference, String salesTaxRegistrationNumber, String vatRegistrationNumber,
                            String companyRegistrationNumber, String warehouseCode, SwiftCompliantAddress swiftCompliantAddress) {
        super(physicalAddress, postalAddress, contacts, name, externalReference, salesTaxRegistrationNumber,
                vatRegistrationNumber, companyRegistrationNumber, warehouseCode, swiftCompliantAddress);
        allowPartShipment = Boolean.FALSE;
        allowTransShipment = Boolean.FALSE;
    }

    public PaymentTerm getPaymentTerm() {
        return paymentTerm;
    }

    public void setPaymentTerm(PaymentTerm paymentTerm) {
        this.paymentTerm = paymentTerm;
    }

    public PaymentMethod getPaymentMethod() {
        return paymentMethod;
    }

    public void setPaymentMethod(PaymentMethod paymentMethod) {
        this.paymentMethod = paymentMethod;
    }

    public EstimatedPaymentBasis getEstimatedPaymentBasis() {
        return estimatedPaymentBasis;
    }

    public void setEstimatedPaymentBasis(EstimatedPaymentBasis estimatedPaymentBasis) {
        this.estimatedPaymentBasis = estimatedPaymentBasis;
    }

    public ActualPaymentBasis getActualPaymentBasis() {
        return actualPaymentBasis;
    }

    public void setActualPaymentBasis(ActualPaymentBasis actualPaymentBasis) {
        this.actualPaymentBasis = actualPaymentBasis;
    }

    public Incoterm getIncoterm() {
        return incoterm;
    }

    public void setIncoterm(Incoterm incoterm) {
        this.incoterm = incoterm;
    }

    public Currency getCurrency() {
        return currency;
    }

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

    public UnitPricePerItem getUnitPricePerItem() {
        return unitPricePerItem;
    }

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

    public ServiceProvider getFreightForwarder() {
        return freightForwarder;
    }

    public void setFreightForwarder(ServiceProvider freightForwarder) {
        this.freightForwarder = freightForwarder;
    }

    public ServiceProvider getClearingAgent() {
        return clearingAgent;
    }

    public void setClearingAgent(ServiceProvider clearingAgent) {
        this.clearingAgent = clearingAgent;
    }

    public ServiceProvider getTransporter() {
        return transporter;
    }

    public void setTransporter(ServiceProvider transporter) {
        this.transporter = transporter;
    }

    public Boolean getAllowPartShipment() {
        return allowPartShipment;
    }

    public void setAllowPartShipment(Boolean allowPartShipment) {
        this.allowPartShipment = allowPartShipment;
    }

    public Boolean getAllowTransShipment() {
        return allowTransShipment;
    }

    public void setAllowTransShipment(Boolean allowTransShipmentParam) {
        this.allowTransShipment = allowTransShipmentParam;
    }

    public BigDecimal getLcToleranceAbove() {
        return lcToleranceAbove;
    }

    public void setLcToleranceAbove(BigDecimal lcToleranceAboveParam) {
        this.lcToleranceAbove = lcToleranceAboveParam;
    }

    public BigDecimal getLcToleranceBelow() {
        return lcToleranceBelow;
    }

    public void setLcToleranceBelow(BigDecimal lcToleranceBelowParam) {
        this.lcToleranceBelow = lcToleranceBelowParam;
    }

    public Integer getPresentationDays() {
        return presentationDays;
    }

    public void setPresentationDays(Integer presentationDaysParam) {
        this.presentationDays = presentationDaysParam;
    }

    public String getPlaceOfExpiry() {
        return placeOfExpiry;
    }

    public void setPlaceOfExpiry(String placeOfExpiryParam) {
        this.placeOfExpiry = placeOfExpiryParam;
    }

    public Bank getAdvisingBank() {
        return advisingBank;
    }

    public void setAdvisingBank(Bank advisingBankParam) {
        this.advisingBank = advisingBankParam;
    }

    @Override
    public SupplierState getState() {
        return state;
    }

    @Override
    public void setState(SupplierState state) {
        this.state = state;
    }

    public Boolean isIntegrated() {
        return integrated;
    }

    public void setIntegrated(Boolean integrated) {
        this.integrated = integrated;
    }

    @Override
    public List<SupplierEvent> getEvents() {
        return events;
    }

    public void setEvents(List<SupplierEvent> events) {
        this.events = events;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().appendSuper(super.hashCode()).append(shippingMode).append(externalReference)
                .toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!HibernateUtils.proxyClassEquals(this, obj)) {
            return false;
        }

        AbstractSupplier other = (AbstractSupplier) obj;
        return new EqualsBuilder().appendSuper(super.equals(obj))
                .append(externalReference, other.getExternalReference()).append(shippingMode, other.getShippingMode())
                .isEquals();
    }

    public Boolean getIntegrated() {
        return integrated;
    }

    @Override
    public SupplierEvent getLastEvent() {
        return Event.getLastEvent(events);
    }

    @Override
    public boolean inNonEditableState() {
        return NON_EDITABLE_STATES.contains(state);
    }

    public Boolean isComplete() {
        return complete;
    }

    public Boolean getComplete() {
        return complete;
    }

    public void setComplete(Boolean complete) {
        this.complete = complete;
    }

    public Boolean isUpdate() {
        return update;
    }

    public void setUpdate(Boolean update) {
        this.update = update;
    }

    /**
     * Using the state instead of the active flag on the database.
     *
     * @return
     */
    @Override
    public Boolean getActive() {
        return state == SupplierState.ACTIVE;
    }

    public Boolean isActive() {
        return state == SupplierState.ACTIVE;
    }

    /**
     * We don't use the active boolean, instead a State of ACTIVE decides if the
     * supplier is active. kkk - why would you do that?
     *
     * @param active
     */
    @Override
    public void setActive(Boolean active) {
        if (!integrated) {
            if (active) {
                setState(SupplierState.ACTIVE);
            } else {
                setState(SupplierState.INACTIVE);
            }
        }
        // don't want to do this for integrated suppliers. (They'll just use states)
    }

    public Boolean getSplitPayment() {
        return splitPayment;
    }

    public boolean splitPayment() {
        return getSplitPayment();
    }

    public void setSplitPayment(Boolean splitPayment) {
        this.splitPayment = splitPayment;
    }

    public EstimatedPaymentBasis getEstimatedPaymentBasis2() {
        return estimatedPaymentBasis2;
    }

    public void setEstimatedPaymentBasis2(EstimatedPaymentBasis estimatedPaymentBasis2) {
        this.estimatedPaymentBasis2 = estimatedPaymentBasis2;
    }

    public PaymentTerm getPaymentTermDays() {
        return paymentTermDays;
    }

    public void setPaymentTermDays(PaymentTerm paymentTermDays) {
        this.paymentTermDays = paymentTermDays;
    }

    public BigDecimal getPaymentBasisPercentage() {
        return paymentBasisPercentage;
    }

    public void setPaymentBasisPercentage(BigDecimal paymentBasisPercentage) {
        this.paymentBasisPercentage = paymentBasisPercentage;
    }

    public BigDecimal getPaymentBasis2Percentage() {
        return paymentBasis2Percentage;
    }

    public void setPaymentBasis2Percentage(BigDecimal paymentBasis2Percentage) {
        this.paymentBasis2Percentage = paymentBasis2Percentage;
    }

    public PlaceOfLoading getPlaceOfLoading() {
        return placeOfLoading;
    }

    public void setPlaceOfLoading(PlaceOfLoading placeOfLoading) {
        this.placeOfLoading = placeOfLoading;
    }

    public ShippingMode getShippingMode() {
        return shippingMode;
    }

    public void setShippingMode(ShippingMode shippingMode) {
        this.shippingMode = shippingMode;
    }

    public TreasuryBank getNominatedBank() {
        return nominatedBank;
    }

    public void setNominatedBank(TreasuryBank nominatedBank) {
        this.nominatedBank = nominatedBank;
    }

    public NamedPlace getNamedPlace() {
        return namedPlace;
    }

    public void setNamedPlace(NamedPlace namedPlace) {
        this.namedPlace = namedPlace;
    }

    public Type getType() {
        if (type == null) {
            type = Type.FULL;
        }
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public BigDecimal getBuyingCommission() {
        return buyingCommission;
    }

    public void setBuyingCommission(BigDecimal buyingCommission) {
        this.buyingCommission = buyingCommission;
    }

    public boolean isCanContact() {
        return canContact;
    }

    public void setCanContact(boolean canContact) {
        this.canContact = canContact;
    }

    public BigDecimal getEarlySettlementDisc() {
        return earlySettlementDisc;
    }

    public void setEarlySettlementDisc(BigDecimal earlySettlementDisc) {
        this.earlySettlementDisc = earlySettlementDisc;
    }

    public List<NamedPlace> getFactoryList() {
        return factoryList;
    }

    public void setFactoryList(List<NamedPlace> factoryList) {
        this.factoryList = factoryList;
    }
}