PurchaseOrder.java

package com.tradecloud.domain.model.ordermanagement;

import com.tradecloud.domain.agent.OrganisationalUnitAgent;
import com.tradecloud.domain.common.Currency;
import com.tradecloud.domain.common.Incoterm;
import com.tradecloud.domain.dms.DocumentGroupState;
import com.tradecloud.domain.document.PaymentState;
import com.tradecloud.domain.document.invoice.UnitPricePerItem;
import com.tradecloud.domain.item.LineItem;
import com.tradecloud.domain.letterofcredit.LetterOfCredit;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
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.party.Employee;
import com.tradecloud.domain.party.ServiceProvider;
import com.tradecloud.domain.party.base.Address;
import com.tradecloud.domain.settlement.PlannedSettlementType;
import com.tradecloud.domain.shipment.ShippingInformation;
import com.tradecloud.domain.supplier.OrganisationalUnitSupplier;
import org.hibernate.annotations.ForeignKey;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Set;

/**
 * A purchase order is an order in the imports context.
 *
 * A buyer is the person responsible for arranging the import/purchase order.
 * The supplier is the party that fulfils the order.
 */
@Entity
@DiscriminatorValue("PURCHASE_ORDER")
@Table(name = "purchaseorder")
@Access(AccessType.FIELD)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "PurchaseOrder")
@NamedQueries({@NamedQuery(name = "PurchaseOrder.findAllUnlinkedWithItems",
        query = "from PurchaseOrder o where o.state not in ('DELETED') and o.consignment is null and o.lineItems.size > 0 and elc='f'"),
        @NamedQuery(name = "PurchaseOrder.findAllUnlinkedWithItemsForOrganisationalUnit",
                query = "from PurchaseOrder o where o.state not in ('DELETED') and o.consignment is null and o.lineItems.size > 0 and elc='f'" +
                        " and o.organisationalUnit = :organisationalUnit"),
        @NamedQuery(name = "PurchaseOrder.findAllUnlinkedWithItemsForOrganisationalUnitStructure",
                query = "from PurchaseOrder o where o.state not in ('DELETED') and o.consignment is null and o.lineItems.size > 0 and elc='f'" +
                        " and o.organisationalUnit in :organisationalUnit")})

@NamedEntityGraph(name = "graph.PurchaseOrderConsignment", attributeNodes = @NamedAttributeNode(value = "consignment"))
public class PurchaseOrder extends Order implements Exposure {

    private static final long serialVersionUID = 3325149164036096080L;

    /**
     * The person in charge of buying this order.
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @ForeignKey(name = "fk_buyer")
    @NotNull(message = "Buyer is required")
    @XmlElement(name = "Buyer", required = true)
    private Employee buyer;

    /**
     * The party, linked to the organisational unit, that is supplying this order.
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @ForeignKey(name = "fk_supplier")
    @NotNull(message = "Supplier is required")
    @XmlElement(name = "Supplier", required = true)
    private OrganisationalUnitSupplier supplier;

    /**
     * The party, linked to the organisational unit, that is the Merchandising Agent for this order.
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @ForeignKey(name = "fk_merchandisingagent")
    @XmlElement(name = "MerchandisingAgent", required = true)
    private OrganisationalUnitAgent merchandisingAgent;

    /**
     * The party, linked to the organisational unit, that is the Foreign Agent for this order.
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @ForeignKey(name = "fk_foreignagent")
    @XmlElement(name = "ForeignAgent", required = true)
    private OrganisationalUnitAgent foreignAgent;

    /**
     * The party, linked to the organisational unit, that is the Sourcing Agent for this order.
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @ForeignKey(name = "fk_sourcingagent")
    @XmlElement(name = "SourcingAgent", required = true)
    private OrganisationalUnitAgent sourcingAgent;

    /**
     * The party, linked to the organisational unit, that is the Warehousing Agent for this order.
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @ForeignKey(name = "fk_warehousingagent")
    @XmlElement(name = "WarehousingAgent", required = true)
    private OrganisationalUnitAgent warehousingAgent;

    /**
     * A reference to the letter of credit. mostly needed so to keep easy track of the rule that an order can only be linked to one lc
     * at a time.
     * Need the updatable and insertable as false because this is not the owning side of the relationship
     */
    @ManyToOne(fetch = FetchType.LAZY)
//    @JoinTable(name = "letterofcredit_purchaseorder",
//            joinColumns = @JoinColumn(name = "purchaseorders_id", updatable = false, insertable = false),
//            inverseJoinColumns = @JoinColumn(name = "letterofcredit_id", updatable = false, insertable = false))
    private LetterOfCredit letterOfCredit;

    @ManyToOne
    @ForeignKey(name = "fk_localCustomsAuthority")
    @XmlElement(name = "LocalCustomsAuthority")
    private ServiceProvider localCustomsAuthority;

    @ManyToOne
    @ForeignKey(name = "fk_localPortAuthority")
    @XmlElement(name = "LocalPortAuthority")
    private ServiceProvider localPortAuthority;

    /**
     * Indicates whether the letter of credit is required for this order or not.
     */
    @XmlAttribute
    private boolean lcRequired;

    private String fcrNumber;

    private Long liteConsignmentId;

    private String liteConsignmentReference;

    @Temporal(javax.persistence.TemporalType.DATE)
    private Date signedOffDate;

    @Transient
    private boolean toBeDeleted;

    @Enumerated(EnumType.STRING)
    private DocumentGroupState documentGroupStatus;

    private BigDecimal integratedTotalVolume;

    private BigDecimal integratedTotalWeight;
    //used mainly in ultralight as there multiple way to get volume or weight.We need to remember
    // how integratedTotalVolume and integratedTotalWeight are calculated , order we keep loosing values.
    @Enumerated(EnumType.STRING)
    private VolumeOrWeightSource totalVolumeOrWeightSource = VolumeOrWeightSource.ITEM;

    private String description;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @XmlElement(name = "PhysicalAddress")
    private Address physicalAddress;

    public PurchaseOrder() {
    }

    public PurchaseOrder(String number, String reference) {
        setNumber(number);
        setOrderReference(reference);
    }

    public static PurchaseOrderBuilder createBuilder() {
        return new PurchaseOrderBuilder();
    }

    /**
     * Constructor. Do not use directly. Use the Builder instead.
     */
    private PurchaseOrder(String number, String orderReference, OrganisationalUnit organisationalUnit, OrganisationalUnitSupplier supplier,
                          Employee buyer, Set<LineItem> items, OrderDates orderDates, ShippingInformation shippingInformation, Currency currency,
                          PaymentMethod paymentMethod, PaymentTerm paymentTerm, Incoterm incoterm,
                          OrganisationalUnitAgent merchandisingAgent, OrganisationalUnitAgent foreignAgent, OrganisationalUnitAgent sourcingAgent,
                          OrganisationalUnitAgent warehousingAgent) {
        super(number, orderReference, organisationalUnit, items, orderDates, shippingInformation, currency, paymentMethod, paymentTerm,
                incoterm);
        this.supplier = supplier;
        this.buyer = buyer;
        this.merchandisingAgent = merchandisingAgent;
        this.foreignAgent = foreignAgent;
        this.sourcingAgent = sourcingAgent;
        this.warehousingAgent = warehousingAgent;
        this.shippingInformation = shippingInformation;
        this.orderDates = orderDates;
    }

    public Employee getBuyer() {
        return buyer;
    }

    public void setBuyer(Employee buyer) {
        this.buyer = buyer;
    }

    @Override
    public OrderType getType() {
        return OrderType.PURCHASE_ORDER;
    }

    public OrganisationalUnitSupplier getSupplier() {
        return supplier;
    }

    public void setSupplier(OrganisationalUnitSupplier supplier) {
        this.supplier = supplier;
    }

    public OrganisationalUnitAgent getMerchandisingAgent() {
        return merchandisingAgent;
    }

    public void setMerchandisingAgent(OrganisationalUnitAgent merchandisingAgent) {
        this.merchandisingAgent = merchandisingAgent;
    }

    public OrganisationalUnitAgent getForeignAgent() {
        return foreignAgent;
    }

    public void setForeignAgent(OrganisationalUnitAgent foreignAgent) {
        this.foreignAgent = foreignAgent;
    }

    public OrganisationalUnitAgent getSourcingAgent() {
        return sourcingAgent;
    }

    public void setSourcingAgent(OrganisationalUnitAgent sourcingAgent) {
        this.sourcingAgent = sourcingAgent;
    }

    public OrganisationalUnitAgent getWarehousingAgent() {
        return warehousingAgent;
    }

    public void setWarehousingAgent(OrganisationalUnitAgent warehousingAgent) {
        this.warehousingAgent = warehousingAgent;
    }

    public boolean getAllowPartShipment() {
        if (supplier != null) {
            return supplier.getAllowPartShipment();
        }
        return false;
    }

    public boolean getAllowTransShipment() {
        if (supplier != null) {
            return supplier.getAllowTransShipment();
        }
        return false;
    }

    public boolean isLcRequired() {
        return lcRequired;
    }

    public void setLcRequired(boolean lcRequired) {
        this.lcRequired = lcRequired;
    }

    public LetterOfCredit getLetterOfCredit() {
        return letterOfCredit;
    }

    public void setLetterOfCredit(LetterOfCredit letterOfCredit) {
        this.letterOfCredit = letterOfCredit;
    }

    @Override
    public Employee getResponsibleEmployee() {
        return getBuyer();
    }

    @Override
    public void setResponsibleEmployee(Employee employee) {
        setBuyer(employee);
    }

    public Long getLiteConsignmentId() {
        return liteConsignmentId;
    }

    public void setLiteConsignmentId(Long liteConsignmentId) {
        this.liteConsignmentId = liteConsignmentId;
    }

    public String getLiteConsignmentReference() {
        return liteConsignmentReference;
    }

    public void setLiteConsignmentReference(String liteConsignmentReference) {
        this.liteConsignmentReference = liteConsignmentReference;
    }

    public Date getSignedOffDate() {
        return signedOffDate;
    }

    public void setSignedOffDate(Date signedOffDate) {
        this.signedOffDate = signedOffDate;
    }

    public boolean isToBeDeleted() {
        return toBeDeleted;
    }

    public void setToBeDeleted(boolean toBeDeleted) {
        this.toBeDeleted = toBeDeleted;
    }

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

    /**
     * Purchase Order Builder.
     */
    public static final class PurchaseOrderBuilder {

        private String number;
        private String orderReference;
        private OrganisationalUnit organisationalUnit;
        private Currency currency;
        private PaymentMethod paymentMethod;
        private PaymentTerm paymentTerm;
        private Incoterm incoterm;
        private Set<LineItem> items;
        private OrderDates orderDates;
        private ShippingInformation shippingInformation;
        private OrganisationalUnitSupplier supplier;
        private Employee buyer;
        private OrganisationalUnitAgent merchandisingAgent;
        private OrganisationalUnitAgent foreignAgent;
        private OrganisationalUnitAgent sourcingAgent;
        private OrganisationalUnitAgent warehousingAgent;
        private UnitPricePerItem unitPricePerItem;

        /**
         * Private constructor to prevent instantiation.
         */
        private PurchaseOrderBuilder() {
        }

        public PurchaseOrder build() {
            validate();
            return new PurchaseOrder(number, orderReference, organisationalUnit, supplier, buyer, items, orderDates, shippingInformation, currency,
                    paymentMethod, paymentTerm, incoterm, merchandisingAgent, foreignAgent, sourcingAgent, warehousingAgent);
        }

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

        public PurchaseOrderBuilder setOrderReference(String orderReference) {
            this.orderReference = orderReference;
            return this;
        }

        public PurchaseOrderBuilder setOrganisationalUnit(OrganisationalUnit organisationalUnit) {
            this.organisationalUnit = organisationalUnit;
            return this;
        }

        public PurchaseOrderBuilder setItems(Set<LineItem> items) {
            this.items = items;
            return this;
        }

        public PurchaseOrderBuilder setOrderDates(OrderDates orderDates) {
            this.orderDates = orderDates;
            return this;
        }

        public PurchaseOrderBuilder setShippingInformation(ShippingInformation shippingInformation) {
            this.shippingInformation = shippingInformation;
            return this;
        }

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

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

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

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

        public PurchaseOrderBuilder setSupplier(OrganisationalUnitSupplier supplier) {
            this.supplier = supplier;
            return this;
        }

        public PurchaseOrderBuilder setMerchandisingAgent(OrganisationalUnitAgent merchandisingAgent) {
            this.merchandisingAgent = merchandisingAgent;
            return this;
        }

        public PurchaseOrderBuilder setForeignAgent(OrganisationalUnitAgent foreignAgent) {
            this.foreignAgent = foreignAgent;
            return this;
        }

        public PurchaseOrderBuilder setSourcingAgent(OrganisationalUnitAgent sourcingAgent) {
            this.sourcingAgent = sourcingAgent;
            return this;
        }

        public PurchaseOrderBuilder setWarehousingAgent(OrganisationalUnitAgent warehousingAgent) {
            this.warehousingAgent = warehousingAgent;
            return this;
        }

        public PurchaseOrderBuilder setBuyer(Employee buyer) {
            this.buyer = buyer;
            return this;
        }

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

        private void ensureNotNull(Object object, String name) {
            // if (object == null) {
            // throw new IllegalArgumentException("A valid PurchaseOrder requires " + name + " to be set.");
            // }
        }

        private void validate() {
            ensureNotNull(number, "number");
            ensureNotNull(orderReference, "orderReference");
            ensureNotNull(organisationalUnit, "organisationalUnit");
            ensureNotNull(items, "items");
            ensureNotNull(currency, "currency");
            ensureNotNull(paymentMethod, "paymentMethod");
            ensureNotNull(paymentTerm, "paymentTerm");
            ensureNotNull(buyer, "buyer");
            ensureNotNull(supplier, "supplier");
        }
    }

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

    @Override
    public OrganisationalUnitSupplier getSupplierOrCustomer() {
        return supplier;
    }

    @Override
    public EstimatedPaymentBasis getEstimatedPaymentBasis() {
        return supplier.getEstimatedPaymentBasis();
    }

    @Override
    public ActualPaymentBasis getActualPaymentBasis() {
        return supplier.getActualPaymentBasis();
    }

    @Override
    public EstimatedPaymentBasis getEstimatedPaymentBasis2() {
        return supplier.getSupplier().getEstimatedPaymentBasis2();
    }

    @Override
    public PlannedSettlementType getPlannedSettlementType() {
        return PlannedSettlementType.CONSIGNED_ORDER;
    }

    @Override
    public PaymentState getPaymentState() {
        return null;
    }

    @Override
    public BigDecimal getTotalSalesValue() {
        return null;
    }

    public String getFcrNumber() {
        return fcrNumber;
    }

    public void setFcrNumber(String fcrNumber) {
        this.fcrNumber = fcrNumber;
    }

    public ServiceProvider getLocalCustomsAuthority() {
        return localCustomsAuthority;
    }

    public void setLocalCustomsAuthority(ServiceProvider localCustomsAuthority) {
        this.localCustomsAuthority = localCustomsAuthority;
    }

    public ServiceProvider getLocalPortAuthority() {
        return localPortAuthority;
    }

    public void setLocalPortAuthority(ServiceProvider localPortAuthority) {
        this.localPortAuthority = localPortAuthority;
    }

    public DocumentGroupState getDocumentGroupStatus() {
        return documentGroupStatus;
    }

    public void setDocumentGroupStatus(DocumentGroupState documentGroupStatus) {
        this.documentGroupStatus = documentGroupStatus;
    }

    public BigDecimal getIntegratedTotalVolume() {
        return integratedTotalVolume;
    }

    public void setIntegratedTotalVolume(BigDecimal integratedTotalVolume) {
        this.integratedTotalVolume = integratedTotalVolume;
    }

    public BigDecimal getIntegratedTotalWeight() {
        return integratedTotalWeight;
    }

    public void setIntegratedTotalWeight(BigDecimal integratedTotalWeight) {
        this.integratedTotalWeight = integratedTotalWeight;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Address getPhysicalAddress() {
        return physicalAddress;
    }

    public void setPhysicalAddress(Address physicalAddress) {
        this.physicalAddress = physicalAddress;
    }

    @Override
    public String toString() {
        return super.toString();
    }

    public VolumeOrWeightSource getTotalVolumeOrWeightSource() {
        return totalVolumeOrWeightSource;
    }

    public void setTotalVolumeOrWeightSource(VolumeOrWeightSource totalVolumeOrWeightSource) {
        this.totalVolumeOrWeightSource = totalVolumeOrWeightSource;
    }

}