LetterOfCredit.java

package com.tradecloud.domain.letterofcredit;

import com.tradecloud.common.base.PersistenceBase;
import com.tradecloud.domain.comment.AddedComment;
import com.tradecloud.domain.comment.CommentType;
import com.tradecloud.domain.comment.Commentable;
import com.tradecloud.domain.event.Event;
import com.tradecloud.domain.event.LetterOfCreditEvent;
import com.tradecloud.domain.model.ordermanagement.PurchaseOrder;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
import com.tradecloud.domain.party.Bank;
import com.tradecloud.domain.state.Stateful;
import com.tradecloud.domain.supplier.OrganisationalUnitSupplier;
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 org.springframework.stereotype.Component;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.xml.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * https://connect.devstream.net/display/Dev/Create+Letter+of+Credit.
 */
@Entity
@Component(value = "letterofcredit")
@Table(name = "letterofcredit")
@Access(AccessType.FIELD)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "LetterOfCredit")
public class LetterOfCredit extends PersistenceBase implements Stateful<LetterOfCreditState, LetterOfCreditEvent>,
        Commentable<AddedComment> {

    @NotNull(message = "Number is required")
    private String number;

    private String bankReference;

    @ManyToOne
    @ForeignKey(name = "fk_letterofcredittemplate")
    private LetterOfCreditTemplate letterOfCreditTemplate;

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

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

    @NotNull(message = "Created date is required")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date createdDate;

    @NotNull(message = "Bank charges are required")
    @Enumerated(EnumType.STRING)
    private LetterOfCreditBankCharges bankCharges = LetterOfCreditBankCharges.BENEFICIARY;

    private String bankChargesDescription;

    private int validityPeriod;

    @Transient
    private String instructionsToIssuingBank;

    private boolean transferable;

    private boolean confirm;

    private boolean irrevocable = true;

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

    @Transient
    private String specialConditions;

    @ElementCollection(fetch = FetchType.LAZY)
    @CollectionTable(name = "letterofcredit_freetextcomments", joinColumns = {@JoinColumn(name = "letterofcredit_id", unique = false)})
    @Column(name = "reason", unique = true)
    @ForeignKey(name = "fk_letterofcredit")
    @XmlElementWrapper(name = "FreeTextComments")
    @XmlElement(name = "FreeTextComment")
    @Fetch(value = FetchMode.SUBSELECT)
    private List<AddedComment> comments = new ArrayList<AddedComment>();

    @Transient
    private boolean active;

    private String shippingMark;

    @Transient
    private LetterOfCreditSyncStatus syncStatus;

    @NotNull(message = "Out of sync state is required")
    @Enumerated(EnumType.STRING)
    private LetterOfCreditOutOfSyncState outOfSyncState = LetterOfCreditOutOfSyncState.IN_SYNC;

    @Transient
    private LetterOfCreditDeliveryMethod deliveryMethod;

    @Column(length = 1300)
    private String documents;

    @Column(length = 1300)
    private String conditions;

    private String resolvedDocuments;

    private String resolvedConditions;

    @NotNull(message = "Notify By is required")
    private LetterOfCreditNotification notifyBy;

    @NotNull(message = "Beneficiary is required")
    @ManyToOne
    @ForeignKey(name = "fk_supplier")
    private OrganisationalUnitSupplier beneficiary;

    @NotNull(message = "Applicant is required")
    @ManyToOne
    @ForeignKey(name = "fk_organizationalunit")
    private OrganisationalUnit applicant;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<PurchaseOrder> purchaseOrders = new ArrayList<PurchaseOrder>();

    @NotNull(message = "State is required")
    @Enumerated(EnumType.STRING)
    private LetterOfCreditState state = LetterOfCreditState.UNFINALISED;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @Fetch(value = FetchMode.SUBSELECT)
    @XmlElementWrapper(name = "LetterOfCreditEvents")
    @XmlElement(name = "LetterOfCreditEvent")
    @OrderBy("createDateTime")
    private List<LetterOfCreditEvent> events = new ArrayList<LetterOfCreditEvent>();

    // This was on the purchase order on the original lc
    @NotNull(message = "Goods description is required")
    private String goodsDescription;

    @NotNull
    @XmlElement(name = "ShippingInfo")
    @OneToOne(cascade = CascadeType.ALL)
    @ForeignKey(name = "fk_shippingInfo")
    private LetterOfCreditShippingInfo shippingInfo = new LetterOfCreditShippingInfo();

    @NotNull
    @XmlElement(name = "Tenor")
    @OneToOne(cascade = CascadeType.ALL)
    @ForeignKey(name = "fk_tenor")
    private LetterOfCreditTenor tenor = new LetterOfCreditTenor();

    @NotNull(message = "Issuing bank is required")
    @ManyToOne
    private Bank issuingBank;

    // Not using int because it must be empty so the user has to type it in.
    @NotNull(message = "Presentation Period is required")
    private Integer presentationPeriod;

    //  Maximum of 35 swift characters
    @NotNull(message = "Proforma Reference is required")
    @Pattern(regexp = "([a-zA-Z_0-9\\s\\/\\-\\?\\:\\(\\)\\.\\,\\'\\+\\{\\}]*$){1,35}",
            message = "[ProForma Reference Can only contain:: (a-z) and (A-Z) and These special charactors (/ - ? : ( ) . , ' + { })]")
    private String proFormaReference;

    @NotNull(message = "Application Reference is required")
    private String applicationReference;

    /*Map that keep track of fields changed from the bank.
     * The original field and the changed field are stored here*/
    @OneToMany(mappedBy = "letterOfCredit", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @Fetch(value = FetchMode.SUBSELECT)
    private List<BankChangedField> bankChangedFields = new ArrayList<BankChangedField>();

    @Transient
    private String bankReason;

    private boolean sentToGateway;

    public LetterOfCredit() {
        this.expiryDate = new Date();
        this.submissionDate = new Date();
        this.createdDate = new Date();
        this.deliveryMethod = LetterOfCreditDeliveryMethod.COURIER;
        this.transferable = false;
        this.active = true;
    }

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

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

    public void setActive(boolean active) {
        this.active = active;
        //todo:set active on all related entities + clear out of sync on delete
    }

    public void addPurchaseOrder(PurchaseOrder purchaseOrder) {
        purchaseOrders.add(purchaseOrder);
        purchaseOrder.setLetterOfCredit(this);
    }

    public void addPurchaseOrders(List<PurchaseOrder> purchaseOrders) {
        for (PurchaseOrder purchaseOrder : purchaseOrders) {
            addPurchaseOrder(purchaseOrder);
        }
    }

    public void removePurchaseOrder(PurchaseOrder purchaseOrder) {
        purchaseOrders.remove(purchaseOrder);
        purchaseOrder.setLetterOfCredit(null);
    }

    public void removePurchaseOrders(Collection<PurchaseOrder> purchaseOrders) {
        for (PurchaseOrder order : purchaseOrders) {
            order.setOrderLetterOfCredit(null);
        }
        this.purchaseOrders.removeAll(purchaseOrders);
    }

    public LetterOfCreditSyncStatus getSyncStatus() {
        return syncStatus;
    }

    public void setSyncStatus(LetterOfCreditSyncStatus syncStatus) {
        this.syncStatus = syncStatus;
    }

    public LetterOfCreditDeliveryMethod getDeliveryMethod() {
        return deliveryMethod;
    }

    public void setDeliveryMethod(LetterOfCreditDeliveryMethod deliveryMethod) {
        this.deliveryMethod = deliveryMethod;
    }

    //  Maximum of 35 swift characters
    @Pattern(regexp = "([a-zA-Z_0-9\\s\\/\\-\\?\\:\\(\\)\\.\\,\\'\\+\\{\\}]*$){1,35}",
            message = "[Shipping Mark Can only contain:: (a-z) and (A-Z) and These special charactors (/ - ? : ( ) . , ' + { })]")
    public String getShippingMark() {
        return shippingMark;
    }

    public void setShippingMark(String shippingMark) {
        this.shippingMark = shippingMark;
    }

    public String getResolvedDocuments() {
        return resolvedDocuments;
    }

    public void setResolvedDocuments(String resolvedDocuments) {
        this.resolvedDocuments = resolvedDocuments;
    }

    public String getResolvedConditions() {
        return resolvedConditions;
    }

    public void setResolvedConditions(String resolvedConditions) {
        this.resolvedConditions = resolvedConditions;
    }

    public LetterOfCreditOutOfSyncState getOutOfSyncState() {
        return outOfSyncState;
    }

    public void setOutOfSyncState(LetterOfCreditOutOfSyncState outOfSyncState) {
        this.outOfSyncState = outOfSyncState;
    }

    public LetterOfCreditNotification getNotifyBy() {
        return notifyBy;
    }

    public void setNotifyBy(LetterOfCreditNotification notifyBy) {
        this.notifyBy = notifyBy;
    }

    public String getDocuments() {
        return documents;
    }

    public void setDocuments(String documents) {
        this.documents = documents;
    }

    public String getConditions() {
        return conditions;
    }

    public void setConditions(String conditions) {
        this.conditions = conditions;
    }

    public OrganisationalUnitSupplier getBeneficiary() {
        return beneficiary;
    }

    public void setBeneficiary(OrganisationalUnitSupplier beneficiary) {
        this.beneficiary = beneficiary;
    }

    public String getNumber() {
        return number;
    }

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

    public String getBankReference() {
        return bankReference;
    }

    public void setBankReference(String bankReference) {
        this.bankReference = bankReference;
    }

    public LetterOfCreditTemplate getLetterOfCreditTemplate() {
        return letterOfCreditTemplate;
    }

    public void setLetterOfCreditTemplate(LetterOfCreditTemplate letterOfCreditTemplate) {
        this.letterOfCreditTemplate = letterOfCreditTemplate;
    }

    public Date getEstablishmentDate() {
        return establishmentDate;
    }

    public void setEstablishmentDate(Date establishmentDate) {
        this.establishmentDate = establishmentDate;
    }

    public Date getSubmissionDate() {
        return submissionDate;
    }

    public void setSubmissionDate(Date submissionDate) {
        this.submissionDate = submissionDate;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public LetterOfCreditBankCharges getBankCharges() {
        return bankCharges;
    }

    public void setBankCharges(LetterOfCreditBankCharges bankCharges) {
        this.bankCharges = bankCharges;
    }

    public String getBankChargesDescription() {
        return bankChargesDescription;
    }

    public void setBankChargesDescription(String bankChargesDescription) {
        this.bankChargesDescription = bankChargesDescription;
    }

    public int getValidityPeriod() {
        return validityPeriod;
    }

    public void setValidityPeriod(int validityPeriod) {
        this.validityPeriod = validityPeriod;
    }

    public String getInstructionsToIssuingBank() {
        return instructionsToIssuingBank;
    }

    public void setInstructionsToIssuingBank(String instructionsToIssuingBank) {
        this.instructionsToIssuingBank = instructionsToIssuingBank;
    }

    public boolean isTransferable() {
        return transferable;
    }

    public void setTransferable(boolean transferable) {
        this.transferable = transferable;
    }

    public boolean isConfirm() {
        return confirm;
    }

    public void setConfirm(boolean confirm) {
        this.confirm = confirm;
    }

    public boolean isIrrevocable() {
        return irrevocable;
    }

    public void setIrrevocable(boolean irrevocable) {
        this.irrevocable = irrevocable;
    }

    public Date getExpiryDate() {
        return expiryDate;
    }

    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }

    public List<PurchaseOrder> getPurchaseOrders() {
        return purchaseOrders;
    }

    public void setPurchaseOrders(List<PurchaseOrder> purchaseOrders) {
        this.purchaseOrders = purchaseOrders;
    }

    public String getSpecialConditions() {
        return specialConditions;
    }

    public void setSpecialConditions(String specialConditions) {
        this.specialConditions = specialConditions;
    }

    public boolean isActive() {
        return active;
    }

    @Override
    public boolean inNonEditableState() {
        return state != LetterOfCreditState.UNFINALISED;
    }

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

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

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

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof LetterOfCredit)) {
            return false;
        }
        LetterOfCredit other = (LetterOfCredit) obj;
        return new EqualsBuilder()
                .appendSuper(super.equals(obj))
                .append(number, other.number)
                .isEquals();
    }

    //  Maximum of 35 swift characters
    @Pattern(regexp = "([a-zA-Z_0-9\\s\\/\\-\\?\\:\\(\\)\\.\\,\\'\\+\\{\\}]*$){1,35}",
            message = "[Goods Description Can only contain:: (a-z) and (A-Z) and These special charactors (/ - ? : ( ) . , ' + { })]")
    public String getGoodsDescription() {
        return goodsDescription;
    }

    public void setGoodsDescription(String goodsDescription) {
        this.goodsDescription = goodsDescription;
    }

    public LetterOfCreditShippingInfo getShippingInfo() {
        return shippingInfo;
    }

    public void setShippingInfo(LetterOfCreditShippingInfo shippingInfo) {
        this.shippingInfo = shippingInfo;
    }

    public Bank getIssuingBank() {
        return issuingBank;
    }

    public void setIssuingBank(Bank issuingBank) {
        this.issuingBank = issuingBank;
    }

    @Override
    public List<AddedComment> getComments() {
        return comments;
    }

    @Override
    public void setComments(List<AddedComment> comments) {
        this.comments = comments;
    }

    public LetterOfCreditTenor getTenor() {
        return tenor;
    }

    public void setTenor(LetterOfCreditTenor tenor) {
        this.tenor = tenor;
    }

    public int getPresentationPeriod() {
        return presentationPeriod;
    }

    public void setPresentationPeriod(int presentationPeriod) {
        this.presentationPeriod = presentationPeriod;
    }

    public List<BankChangedField> getBankChangedFields() {
        return bankChangedFields;
    }

    public void setBankChangedFields(List<BankChangedField> bankChangedFields) {
        this.bankChangedFields = bankChangedFields;
    }

    /**
     * @return the applicant
     */
    public OrganisationalUnit getApplicant() {
        return applicant;
    }

    /**
     * @param applicant the applicant to set
     */
    public void setApplicant(OrganisationalUnit applicant) {
        this.applicant = applicant;
    }

    public String getProFormaReference() {
        return proFormaReference;
    }

    public void setProFormaReference(String proFormaReference) {
        this.proFormaReference = proFormaReference;
    }

    public String getApplicationReference() {
        return applicationReference;
    }

    public void setApplicationReference(String applicationReference) {
        this.applicationReference = applicationReference;
    }

    public String getBankReason() {
        return bankReason;
    }

    public void setBankReason(String bankReason) {
        this.bankReason = bankReason;
    }

    public boolean isSentToGateway() {
        return sentToGateway;
    }

    public void setSentToGateway(boolean sentToGateway) {
        this.sentToGateway = sentToGateway;
    }

    @Override
    public CommentType getCommentType() {
        return CommentType.LETTER_OF_CREDIT;
    }
}