CustomsDeclaration.java
package com.tradecloud.domain.shipment.clearing;
import com.tradecloud.domain.common.Incoterm;
import com.tradecloud.domain.configuration.clearing.za.*;
import com.tradecloud.domain.dms.DocumentManagementHardCoding;
import com.tradecloud.domain.exception.DuplicateEntityException;
import com.tradecloud.domain.export.ChangeAcknowledgementIndicator;
import com.tradecloud.domain.export.EdifactStatus;
import com.tradecloud.domain.export.ExportParty;
import com.tradecloud.domain.model.ordermanagement.SalesOrder;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
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.Employee;
import com.tradecloud.domain.party.ServiceProvider;
import com.tradecloud.domain.party.base.Company;
import com.tradecloud.domain.place.*;
import com.tradecloud.domain.sars.SARSShipment;
import com.tradecloud.domain.sars.Status;
import com.tradecloud.domain.shipment.AirShipment;
import com.tradecloud.domain.shipment.MultiModalShipment;
import com.tradecloud.domain.shipment.SeaShipment;
import com.tradecloud.domain.useraudit.UserAudit;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.JoinFormula;
import javax.persistence.*;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@Entity
@Getter
@Setter
public class CustomsDeclaration extends BaseClearingInstruction implements SARSShipment, Cloneable {
private static final List<Status> EDITABLE_STATES = Arrays.asList(Status.NEW, Status.REJECTED, Status.REOPENED, Status.OUT_OF_SYNC,
Status.FAILED);
@ManyToOne(fetch = FetchType.LAZY)
private PlaceOfCustom clearingTerminal;
@Transient
private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
@XmlElementWrapper(name = "ClearingEvents")
@XmlElement(name = "ClearingEvent")
@OrderBy("createDateTime")
protected List<ClearingEvent> events = new LinkedList<>();
@ManyToOne(fetch = FetchType.EAGER)
@JoinFormula("(SELECT MAX(CustomsDeclaration_clearingevent.events_id) " +
"FROM CustomsDeclaration_clearingevent left join ClearingEvent on CustomsDeclaration_clearingevent.events_id = ClearingEvent.id " +
"WHERE CustomsDeclaration_clearingevent.customsdeclaration_id = id)")
//TODO: replace with: @Column(name = "formula_col", insertable = false, updatable = false)
//@ColumnTransformer(read = "(FORMULA SQL)")
protected ClearingEvent lastEvent;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
@XmlElementWrapper(name = "ClearingEvents")
@XmlElement(name = "ClearingEvent")
@OrderBy("createDateTime")
@JoinTable(name = "customsdeclaration_sarsevent",
joinColumns = {@JoinColumn(name = "customsdeclaration_id", unique = false)},
inverseJoinColumns = {@JoinColumn(name = "events_id", unique = true)})
private List<SarsEvent> sarsEvents = new LinkedList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinFormula("(SELECT MAX(customsdeclaration_sarsevent.events_id) " +
"FROM customsdeclaration_sarsevent left join SarsEvent on customsdeclaration_sarsevent.events_id = SarsEvent.id " +
"WHERE customsdeclaration_sarsevent.customsdeclaration_id = id)")
private SarsEvent lastSarsEvent;
@ManyToOne
private PlaceOfCustom placeOfCustomsEntry;
@ManyToOne
private Country warehouseCountryOfDestination;
@Enumerated(value = EnumType.STRING)
private ContainerCargoState containerCargoState;
@Enumerated(EnumType.STRING)
private EdifactStatus edifactStatus;
@Enumerated(value = EnumType.STRING)
protected PurposeCode purposeCode;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@Fetch(value = FetchMode.SUBSELECT)
@OrderBy("created")
protected List<CustomsDocument> customsDocuments = new LinkedList<>();
private int numberOfPackages;
private int totalNumberOfPackages;
@Size(max = 350)
private String marksAndNumbers;
private Date declarationDate;
protected BigInteger number;
private String caseNumber;
private String mrnNumber;
private String lrnNumber;
private Date assessmentDate;
private String billOfEntryNumber;
private Long originalDeclarationID;
private boolean pendingOutOfSync;
private boolean pendingCancellation;
private boolean vatInvoice;
private boolean vatRefundRequired;
private BigDecimal expectedPayment;
private Date reopenedTime;
private int reopenedCount;
private String ucrNumber;
private String documentsProduced;
private BigDecimal customsOverriddenTotal;
private String customsFinancialAccountNumber;
@Enumerated(EnumType.STRING)
private CustomsFinancialAccountOwner customsFinancialAccountOwner;
@Size(max = 255)
private String amendReason;
@Enumerated(value = EnumType.STRING)
private DeclarationType declarationType = DeclarationType.RDC_REGULAR_COMPLETE;
@OneToMany(fetch = FetchType.LAZY)
@Fetch(value = FetchMode.SUBSELECT)
@OrderBy("created")
protected List<UserAudit> userAudits = new LinkedList<>();
@Enumerated(value = EnumType.STRING)
private ChangeAcknowledgementIndicator changeAcknowledgementIndicator;
@ManyToOne
@org.hibernate.annotations.ForeignKey(name = "receivingbank_id")
private Bank receivingBank;
@ManyToOne
@org.hibernate.annotations.ForeignKey(name = "export_paymentterm_fk")
@XmlElement(name = "PaymentTerm")
private PaymentTerm paymentTerm;
//refund fields
@Enumerated(value = EnumType.STRING)
private VOCCostResponsible vocCostResponsible;
@Enumerated(value = EnumType.STRING)
private RefundType refundType;
@Size(max = 255)
private String refundAdditional;
@ManyToOne(fetch = FetchType.LAZY)
private ServiceProvider refundApplicant;
@ManyToOne(fetch = FetchType.EAGER)
private Employee refundApplicantRepresentative;
@ManyToOne(fetch = FetchType.LAZY)
private City refundPlace;
private Date refundDate;
@Size(max = 255)
private String refundReason;
private BigDecimal refundAmount = BigDecimal.ZERO;
private BigDecimal currentAmountDue = BigDecimal.ZERO;
protected BigInteger positionInShipment;
private String extendedCustomsProcedurePrevious;
private int lastBillOfEntryLineNumber = 0;
private transient List<String> comments;
private boolean noHouseBill;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@Fetch(value = FetchMode.SUBSELECT)
@OrderBy("created")
private List<CustomsDeclarationCostedValues> costedValues;
@Enumerated(EnumType.STRING)
private ClearingPaymentTerm clearingPaymentTerm;
private String printIndicator;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private Set<AdditionalClearingInfo> additionalClearingInfo;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Set<AdvancedPayment> advancedPayments;
public ContainerCargoState getContainerCargoState() {
return containerCargoState;
}
public void setContainerCargoState(ContainerCargoState containerCargoState) {
this.containerCargoState = containerCargoState;
}
public SarsEvent getLastSarsEvent() {
return lastSarsEvent;
}
public void setPlaceOfCustomsEntry(PlaceOfCustom placeOfCustomsEntry) {
this.placeOfCustomsEntry = placeOfCustomsEntry;
}
@Override
public PlaceOfCustom getPlaceOfCustomsEntry() {
return placeOfCustomsEntry;
}
@Override
public Country getCountryOfDestination() {
return warehouseCountryOfDestination;
}
public List<SarsEvent> getSarsEvents() {
return sarsEvents;
}
public void setSarsEvents(List<SarsEvent> sarsEvents) {
this.sarsEvents = sarsEvents;
}
public Country getWarehouseCountryOfDestination() {
return warehouseCountryOfDestination;
}
public void setWarehouseCountryOfDestination(Country warehouseCountryOfDestination) {
this.warehouseCountryOfDestination = warehouseCountryOfDestination;
}
public int getNumberOfPackages() {
return numberOfPackages;
}
public void setNumberOfPackages(int numberOfPackages) {
this.numberOfPackages = numberOfPackages;
}
public int getTotalNumberOfPackages() {
return totalNumberOfPackages;
}
public void setTotalNumberOfPackages(int totalNumberOfPackages) {
this.totalNumberOfPackages = totalNumberOfPackages;
}
public Date getActualDepartureDate() {
return getShipment().getArrivalDateAtPlaceOfDischarge() != null ? getShipment().getArrivalDateAtPlaceOfDischarge() :
getShipment().getEstimatedArrivalDateAtPlaceOfDischarge();
}
@Override
public PlaceOfCustom getPortOfExit() {
return null;
}
@Override
public OrganisationalUnit getParty() {
return getImporter();
}
@Override
public String findChangeAcknowledgementIndicatorNumberStr(String s) {
ChangeAcknowledgementIndicator ack = getChangeAcknowledgementIndicator();
return (ack != null && ack.getStatusNumber() != 0) ? ack.getStatusNumber().toString() : s;
}
public ChangeAcknowledgementIndicator getChangeAcknowledgementIndicator() {
return changeAcknowledgementIndicator;
}
public void setChangeAcknowledgementIndicator(ChangeAcknowledgementIndicator changeAcknowledgementIndicator) {
this.changeAcknowledgementIndicator = changeAcknowledgementIndicator;
}
@Override
public boolean isVatInvoice() {
return vatInvoice;
}
public void setVatInvoice(boolean vatInvoice) {
this.vatInvoice = vatInvoice;
}
@Override
public String getTransportDocumentNumber() {
return switch (shipment.getShippingMode()) {
case SEA -> ((SeaShipment) shipment).getMasterBillOfLadingReference();
case AIR -> ((AirShipment) shipment).getMasterAirwayBillNumber();
default -> null;
};
}
@Override
public Date getTransportDocumentDate() {
return switch (shipment.getShippingMode()) {
case SEA -> ((SeaShipment) shipment).getMasterBillOfLadingDate();
case AIR -> ((AirShipment) shipment).getMasterAirwayBillIssueDate();
default -> null;
};
}
@Override
public EdifactStatus getEdifactStatus() {
if (edifactStatus == null) {
edifactStatus = EdifactStatus.BLANK;
}
return edifactStatus;
}
public PlaceOfLoading getPlaceOfLoading() {
return getShipment().getShippingInfo().getPlaceOfLoading();
}
public PlaceOfDischarge getPlaceOfDischarge() {
return getShipment().getShippingInfo().getPlaceOfDischarge();
}
@Override
public PaymentTerm getPaymentTerm() {
return paymentTerm;
}
@Override
public void setPaymentTerm(PaymentTerm paymentTerm) {
this.paymentTerm = paymentTerm;
}
@Override
public void setEdifactStatus(EdifactStatus edifactStatus) {
this.edifactStatus = edifactStatus;
}
@Override
public Date getBillOfEntryDate() {
//JSA202107265191106
if (mrnNumber != null) {
try {
return sdf.parse(getMrnNumber().substring(3, 11));
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public String getUcrNumber() {
return ucrNumber;
}
@Override
public String getVehicleRegistration() {
return null;
}
@Override
public Company getAgent() {
if (shipment.isImports())
return shipment.getOrders().get(0).getSupplier().getCompany();
else
return ((SalesOrder) shipment.getOrders().get(0)).getExportParty();
}
@Override
public String getExtendedCustomsProcedurePrevious() {
return extendedCustomsProcedurePrevious != null ? extendedCustomsProcedurePrevious : "00";
}
public void setExtendedCustomsProcedurePrevious(String code) {
extendedCustomsProcedurePrevious = code;
}
@Override
public String getExtendedCustomsProcedureRequested() {
return "" + getPurposeCode().getCpcCode();
}
@Override
public Long getPreviousCuscarId() {
return null;
}
@Override
public ServiceProvider getTransporterServiceProvider() {
return null;
}
@Override
public ExportParty getExportConsignee() {
if (!shipment.isImports()) {
if (shipment.getShippingInfo().getShipToParty() == null)
return ((SalesOrder) shipment.getOrders().get(0)).getExportParty();
else
return shipment.getShippingInfo().getShipToParty();
}
return null;
}
@Override
public Country getCountryOfExport() {
return shipment.getShippingInfo().getCountryOfExport();
}
@Override
public String getLrnNumber() {
if (lrnNumber == null) {
StringBuilder sb = new StringBuilder();
String customsCode = getClearingAgent() != null ? getClearingAgent().getCustomsCode() : null;
sb.append(customsCode);
String code = getPlaceOfCustomsEntry() != null ? getPlaceOfCustomsEntry().getCode() : null;
sb.append(code);
sb.append(getDeclarationDate() != null ? sdf.format(getDeclarationDate()) : "");
sb.append(ObjectUtils.firstNonNull(StringUtils.leftPad("" + number, 6, "0"), "000000"));
//If any of the required fields are null, do not return genrated lrn
if (ObjectUtils.anyNull(customsCode, code, getDeclarationDate(), number))
return null;
return sb.toString();
}
return lrnNumber;
}
@Override
public Employee getDriver() {
return null;
}
@Override
public Employee getCoDriver() {
return null;
}
@Override
public String getTrailerRegistration2() {
return null;
}
@Override
public String getTrailerRegistration1() {
return null;
}
@Override
public void setCusdecId(Long id) {
}
@Override
public void setUcrNumber(String ucrNumber) {
this.ucrNumber = ucrNumber;
}
@Override
public String getExportersReference() {
return null;
}
@Override
public Long getCusdecId() {
return null;
}
@Override
public String getManifestNumber() {
StringBuilder sb = new StringBuilder();
switch (shipment.getShippingMode()) {
case SEA:
sb.append(((SeaShipment) shipment).getMasterBillOfLadingReference());
sb.append(sdf.format(((SeaShipment) shipment).getMasterBillOfLadingDate()));
sb.append(shipment.getBillPlaceOfIssue().getCode());
break;
case AIR:
sb = new StringBuilder();
sb.append(((AirShipment) shipment).getMasterAirwayBillNumber());
sb.append(sdf.format(((AirShipment) shipment).getMasterAirwayBillIssueDate()));
sb.append(shipment.getBillPlaceOfIssue().getCode());
break;
}
return sb.toString();
}
public City getPlaceOfIssue() {
return switch (shipment.getShippingMode()) {
case SEA -> shipment.getBillPlaceOfIssue();
case AIR -> shipment.getBillPlaceOfIssue();
default -> null;
};
}
public String getMarksAndNumbers() {
return marksAndNumbers;
}
public void setMarksAndNumbers(String marksAndNumbers) {
this.marksAndNumbers = marksAndNumbers;
}
@Override
public PlaceOfCustom getClearingTerminal() {
return clearingTerminal;
}
public void setClearingTerminal(PlaceOfCustom clearingTerminal) {
this.clearingTerminal = clearingTerminal;
}
public Date getDeclarationDate() {
return declarationDate;
}
public void setDeclarationDate(Date declarationDate) {
this.declarationDate = declarationDate;
}
@Override
public Date getEstimatedArrivalDate() {
return shipment.getEstimatedArrivalDateAtPlaceOfDischarge();
}
@Override
public Date getPresentationOfGoodsDate() {
return assessmentDate != null ? assessmentDate : declarationDate;
//return declarationDate;
}
@Override
public String getShipmentNumber() {
return getShipment().getReference();
}
@Override
public Bank getReceivingBank() {
return receivingBank;
}
public void setReceivingBank(Bank receivingBank) {
this.receivingBank = receivingBank;
}
public BigInteger getNumber() {
return number;
}
public void setNumber(BigInteger number) {
this.number = number;
}
public ClearingEvent getLastStatus() {
return lastEvent;
}
public List<ClearingEvent> getEvents() {
if (events == null) {
events = new LinkedList<>();
}
return events;
}
@Override
public ClearingEvent getLastEvent() {
return lastEvent;
}
public void setEvents(List<ClearingEvent> events) {
this.events = events;
}
public void addClearingEvent(ClearingEvent clearingEvent) {
this.getEvents().add(clearingEvent);
}
public void setCaseNumber(String caseNumber) {
this.caseNumber = caseNumber;
}
@Override
public String getCaseNumber() {
return caseNumber;
}
@Override
public String getMrnNumber() {
return mrnNumber;
}
public void setMrnNumber(String mrnNumber) {
this.mrnNumber = mrnNumber;
}
public List<CustomsDocument> getCustomsDocuments() {
if (customsDocuments == null) {
customsDocuments = new ArrayList<>();
}
return customsDocuments;
}
public void addCustomsDocument(CustomsDocument customsDocument) {
customsDocument.setCustomsDeclaration(this);
getCustomsDocuments().add(customsDocument);
}
@Override
public boolean inNonEditableState() {
return !EDITABLE_STATES.contains(this.status);
}
public List<UserAudit> getUserAudits() {
if (userAudits == null) {
userAudits = new ArrayList<>();
}
return userAudits;
}
public void setPositionInShipment(BigInteger positionInShipment) {
this.positionInShipment = positionInShipment;
}
public void setLrnNumber(String lrnNumber) {
//only set it once, no changes allowed. the getLrn will generate new until mrn is received from sars
if (this.lrnNumber == null) {
this.lrnNumber = lrnNumber;
}
}
/*This allows the LRN to be set for a manual declaration*/
public void allowManualLrnUpdate(String lrnNumber) {
this.lrnNumber = lrnNumber;
}
public BigDecimal getRefundAmount() {
CustomsDeclarationCostedValues before = getCostedValues(CustomsDeclarationCostedValues.Type.BEFORE_CORRECTION, 0);
//YFG-242: if this is a second or greater number VOC, use the after values
if (reopenedCount > 1)
before = getCostedValues(CustomsDeclarationCostedValues.Type.AFTER_CORRECTION, getReopenedCount());
if (before != null && before.getAmountDue() != null && currentAmountDue != null) {
refundAmount = before.getAmountDue().subtract(currentAmountDue);
}
if (refundAmount == null)
refundAmount = BigDecimal.ZERO;
return refundAmount;
}
public void setRefundAmount(BigDecimal refundAmount) {
//this.refundAmount = refundAmount;
}
public boolean isCustomsDeclaration() {
return true;
}
@Override
public String getDocumentGroupName() {
return DocumentManagementHardCoding.CUSTOMS_DECLARATION.getDocumentManagementHardCodedName();
}
public SarsEvent getSarsEvent(EdifactStatus edifactStatus) {
List<SarsEvent> events = getSarsEvents();
if (events == null)
return null;
for (SarsEvent event : events) {
if (event.getEventType() == edifactStatus) {
return event;
}
}
return null;
}
public boolean getSarsEventNewerThan(EdifactStatus newer, EdifactStatus older) {
List<SarsEvent> events = getSarsEvents();
if (events == null)
return false;
Collections.reverse(events);
boolean foundNewer = false;
for (SarsEvent event : events) {
if (event.getEventType() == newer) {
foundNewer = true;
}
if (foundNewer && event.getEventType() == older)
return true;
}
return false;
}
public SarsEvent getSarsEventBefore(EdifactStatus eventFrom) {
List<SarsEvent> events = getSarsEvents();
if (events == null)
return null;
Collections.reverse(events);
int index = 0;
for (SarsEvent event : events) {
if (event.getEventType() == eventFrom) {
if (index <= events.size() - 2)
return events.get(index + 1);
}
index++;
}
return null;
}
public List<CustomsDeclarationCostedValues> getCostedValues() {
if (costedValues == null)
costedValues = new ArrayList<>();
return costedValues;
}
public void setCostedValues(List<CustomsDeclarationCostedValues> costedValues) {
this.costedValues = costedValues;
}
public void addCostedValues(CustomsDeclarationCostedValues costedValues) {
if (getCostedValues(costedValues.getType(), getReopenedCount()) != null) {
throw new DuplicateEntityException("Values for type already exist: " + costedValues.getType().name());
}
getCostedValues().add(costedValues);
}
public CustomsDeclarationCostedValues getCostedValues(CustomsDeclarationCostedValues.Type type, Integer version) {
return getCostedValues().stream().filter(p -> p.getType().equals(type) && (p.getVersion() == null ||
(p.getVersion() != null && version != null &&
p.getVersion().intValue() == version.intValue()))).findFirst().orElse(null);
}
@Override
public ShippingMode getShippingMode() {
return getShipment().getShippingMode();
}
@Override
public String getInvoiceNumber() {
return getShipment().getActiveCommercialInvoices().get(0).getReference();
}
@Override
public Incoterm getIncoterm() {
return getShipment().getIncoterm();
}
public Set<AdditionalClearingInfo> getAdditionalClearingInfo() {
return additionalClearingInfo;
}
public void setAdditionalClearingInfo(Set<AdditionalClearingInfo> additionalClearingInfo) {
if (this.additionalClearingInfo == null)
this.additionalClearingInfo = new HashSet<>();
if (additionalClearingInfo != null) {
this.additionalClearingInfo.clear();
this.additionalClearingInfo.addAll(additionalClearingInfo);
}
}
public Set<AdvancedPayment> getAdvancedPayments() {
return advancedPayments;
}
public void setAdvancedPayments(Set<AdvancedPayment> advancedPayments) {
if (this.advancedPayments == null)
this.advancedPayments = new HashSet<>();
if (advancedPayments != null) {
this.advancedPayments.clear();
this.advancedPayments.addAll(advancedPayments.stream().limit(5).collect(Collectors.toSet()));
}
}
public void addAdvancedPaymentDTO(AdvancedPayment dto) {
if (getAdvancedPayments() == null)
advancedPayments = new HashSet<>();
advancedPayments.add(dto);
}
public void addAdditionalClearingInfoDTO(AdditionalClearingInfo dto) {
if (additionalClearingInfo == null)
additionalClearingInfo = new HashSet<>();
additionalClearingInfo.add(dto);
}
public void setDocumentsProduced(String documentsProduced) {
this.documentsProduced = documentsProduced;
}
public MultiModalShipment getMultiModalShipment() {
return null; //TODO implement
}
public void setCustomsOverriddenTotal(BigDecimal customsOverriddenTotal) {
if (customsOverriddenTotal != null)
this.customsOverriddenTotal = customsOverriddenTotal;
}
}