Document.java
package com.tradecloud.domain.document;
import com.tradecloud.common.base.HibernateUtils;
import com.tradecloud.common.base.PersistenceBase;
import com.tradecloud.domain.comment.Comment;
import com.tradecloud.domain.event.DocumentEvent;
import com.tradecloud.domain.event.DocumentEventType;
import com.tradecloud.domain.event.Event;
import com.tradecloud.domain.shipment.Shipment;
import com.tradecloud.domain.state.Stateful;
import com.tradecloud.domain.supplier.Creditor;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.log4j.Logger;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlID;
import java.util.*;
/**
* Generic Document.
* Note. This could also be called CostingDocument. Slightly confusing, as there is also a document in DMS.
*/
@MappedSuperclass
public abstract class Document extends PersistenceBase implements Comparable<Document>, Stateful<DocumentState, DocumentEvent> {
protected final transient static Logger log = Logger.getLogger(Document.class);
private static final long serialVersionUID = 1L;
public final static Collection<DocumentState> NON_EDITABLE_STATES = Arrays.asList(DocumentState.SIGNED_OFF, DocumentState.SENT_TO_ERP,
DocumentState.SETTLED, DocumentState.AWAITING_TREASURY_RATES, DocumentState.DOCUMENT_TOLERANCE_EXCEEDED, DocumentState.AWAITING_CREDIT,
DocumentState.AWAITING_FINANCIAL_SUPERVISOR_SIGN_OFF, DocumentState.COMPLETE, DocumentState.PAYMENT_INITIATED, DocumentState.DELETED,
DocumentState.PENDING_SIGN_OFF, DocumentState.OVER_PAID);
/**
* Business refer to this as "Document Date".
*
* @see https://jira.devstream.net/browse/BTM-1135
*/
@NotNull(message = "Date created is required")
@XmlAttribute
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
protected Date dateCreated;
@NotNull(message = "Date received is required")
@XmlAttribute
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
protected Date dateReceived;
@Temporal(TemporalType.DATE)
@XmlAttribute
protected Date actualSettlementDate;
/**
* The reference is not unique, users can choose the same one for a different document if they like.
* There is client-config that can be enabled to show a warning when this happens.
*/
@NotNull(message = "reference is required")
@Size(max = 255, message = "invoice reference exceeded 255 character")
@XmlID
// XML id used for setting parent costsinvoice in child actualconsignment
@XmlAttribute
protected String reference;
@XmlElementWrapper(name = "Comments")
@XmlElement(name = "Comment")
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "costsinvoice_comment", joinColumns = {@JoinColumn(name = "costsinvoice_id")},
inverseJoinColumns = {@JoinColumn(name = "comments_id")})
protected List<Comment> documentsComments;
/**
* DocumentState INITIALISED after object initialisation. (This is an
* internal state only)
*/
@NotNull(message = "State is required")
@XmlAttribute
@Enumerated(EnumType.STRING)
private DocumentState state = DocumentState.INITIALISED;
/**
* The document events, used to keep track of the history.
*/
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@Fetch(value = FetchMode.SELECT)
@XmlElementWrapper(name = "DocumentEvents")
@XmlElement(name = "DocumentEvent")
@OrderBy("createDateTime")
private List<DocumentEvent> events = new LinkedList<>();
private transient String orderReferences;
public Document() {
super();
}
public abstract DocumentType getDocumentType();
public abstract Creditor getCreditor();
@Override
public Boolean getActive() {
return state != DocumentState.DELETED;
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
public Date getDateReceived() {
return dateReceived;
}
public void setDateReceived(Date dateReceived) {
this.dateReceived = dateReceived;
}
public String getReference() {
return reference;
}
public void setReference(String reference) {
this.reference = reference;
}
public List<Comment> getDocumentsComments() {
return documentsComments;
}
public void setDocumentsComments(List<Comment> documentsComments) {
this.documentsComments = documentsComments;
}
@Override
public DocumentState getState() {
return state;
}
@Override
public void setState(DocumentState state) {
this.state = state;
}
@Override
public List<DocumentEvent> getEvents() {
return events;
}
public void setEvents(List<DocumentEvent> events) {
this.events = events;
}
@Override
public int compareTo(Document other) {
if (getDocumentType().equals(other.getDocumentType())) {
return getDateCreated().compareTo(other.getDateCreated());
}
return getDocumentType().compareTo(other.getDocumentType());
}
@Override
public String toString() {
return new ToStringBuilder(this).append(dateCreated).append(dateReceived).append(reference).append(state).toString();
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(dateCreated)
.append(dateReceived)
.append(reference)
.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;
}
Document other = (Document) obj;
return new EqualsBuilder()
.append(dateCreated, other.getDateCreated())
.append(dateReceived, other.getDateReceived())
.append(reference, other.getReference())
.isEquals();
}
@Override
public DocumentEvent getLastEvent() {
return Event.getLastEvent(events);
}
@Deprecated
public boolean editDisabled() {
return inNonEditableState();
}
@Override
public boolean inNonEditableState() {
return NON_EDITABLE_STATES.contains(state);
}
public Date getActualSettlementDate() {
return actualSettlementDate;
}
public void setActualSettlementDate(Date actualSettlementDate) {
this.actualSettlementDate = actualSettlementDate;
}
public abstract Shipment getShipment();
public DocumentEvent getLastEvent(DocumentEventType documentEventType) {
List<DocumentEvent> copyEvents = new ArrayList<>(events);
Collections.reverse(copyEvents);
return Event.getFirstEvent(copyEvents, documentEventType);
}
public boolean isCostsInvoice() {
return false;
}
public boolean isSettled() {
return state == DocumentState.SETTLED;
}
public boolean isPostSignOffState() {
return this.state == DocumentState.SIGNED_OFF || state == DocumentState.SETTLED || state == DocumentState.OVER_PAID;
}
public DocumentEvent getDocumentEventSignOffEvent() {
// 1) Prefer the latest SIGNED_OFF if it exists
DocumentEvent signedOff = getLastEvent(DocumentEventType.SIGNED_OFF);
if (signedOff != null) {
return signedOff;
}
// 2) Look at the truly last event, but guard for null
DocumentEvent last = getLastEvent();
if (last == null) {
return null;
}
// 3) Only if the last event is NOT PENDING_SIGN_OFF,
// return the latest PENDING_SIGN_OFF (may still be null)
if (last.getEventType() != DocumentEventType.PENDING_SIGN_OFF) {
return getLastEvent(DocumentEventType.PENDING_SIGN_OFF);
}
return null;
}
public String getOrderReferences() {
return orderReferences;
}
public void setOrderReferences(String orderReferences) {
this.orderReferences = orderReferences;
}
}