/*
* Copyright (C) 2014 GG-Net GmbH - Oliver Günther
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.ggnet.dwoss.stock.entity;
import java.io.Serializable;
import java.util.*;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import eu.ggnet.dwoss.util.persistence.EagerAble;
import lombok.*;
import static eu.ggnet.dwoss.stock.entity.StockTransactionStatusType.*;
/**
* This class represents the transaction between stocks or in special cases the in and out of stocks.
* See {@link StockTransactionType} for more details about the course of events.
* <p/>
* @has 1 - 2 Stock
* @has 1 - n StockTransactionStatus
* @has 1 - n StockTransactionPosition
* @has n - 1 StockTransactionType
*/
//TODO: The StockTransaction must have a validationViolation():String method to check the validity of a transaction
@Entity
@EqualsAndHashCode(of = "id")
@NamedQueries({
@NamedQuery(name = "StockTransaction.byTypeAndStatus", query = "select s from StockTransaction s where s.type = ?1 and s.status.type = ?2 ORDER BY s.id DESC"),
@NamedQuery(name = "StockTransaction.bySourceTypesComment", query = "select s from StockTransaction s where s.source.id = ?1 and s.type = ?2 and s.status.type = ?3 and s.comment = ?4"),
@NamedQuery(name = "StockTransaction.byDestinationTypesComment", query = "select s from StockTransaction s where s.destination.id = ?1 and s.type = ?2 and s.status.type = ?3 and s.comment = ?4"),
@NamedQuery(name = "StockTransaction.byDestinationTypes", query = "select s from StockTransaction s where s.destination.id = ?1 and s.type = ?2 and s.status.type = ?3")
})
public class StockTransaction implements Serializable, EagerAble {
private static final List<List<StockTransactionStatusType>> POSSIBLE_STATUS_TYPES = new ArrayList<>();
static {
List<StockTransactionStatusType> possibleStatusType = new ArrayList<>();
possibleStatusType.add(PREPARED);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
possibleStatusType = new ArrayList<>();
possibleStatusType.add(PREPARED);
possibleStatusType.add(CANCELLED);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
possibleStatusType = new ArrayList<>();
possibleStatusType.add(PREPARED);
possibleStatusType.add(COMPLETED);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
possibleStatusType = new ArrayList<>();
possibleStatusType.add(PREPARED);
possibleStatusType.add(COMMISSIONED);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
possibleStatusType = new ArrayList<>();
possibleStatusType.add(PREPARED);
possibleStatusType.add(COMMISSIONED);
possibleStatusType.add(IN_TRANSFER);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
possibleStatusType = new ArrayList<>();
possibleStatusType.add(PREPARED);
possibleStatusType.add(COMMISSIONED);
possibleStatusType.add(IN_TRANSFER);
possibleStatusType.add(RECEIVED);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
possibleStatusType = new ArrayList<>();
possibleStatusType.add(PREPARED);
possibleStatusType.add(COMMISSIONED);
possibleStatusType.add(IN_TRANSFER);
possibleStatusType.add(RECEIVED);
possibleStatusType.add(COMPLETED);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
possibleStatusType = new ArrayList<>();
possibleStatusType.add(COMPLETED);
POSSIBLE_STATUS_TYPES.add(possibleStatusType);
}
@Getter
@Id
@GeneratedValue
private int id;
@Version
private short optLock;
/**
* The source of the transaction
*/
@Getter
@Setter
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.EAGER)
private Stock source;
/**
* The destination of the transaction
*/
@Getter
@Setter
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.EAGER)
private Stock destination;
/**
* The Type of transaction
*/
@Getter
@Setter
@NotNull
@Basic(optional = false)
private StockTransactionType type;
@Getter
@Setter
@Lob
@Column(length = 65536)
private String comment;
@OneToMany(cascade = CascadeType.ALL)
private List<StockTransactionStatus> statusHistory = new ArrayList<>();
/**
* the actual/last status
*/
@Getter
@NotNull
@OneToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, optional = false)
private StockTransactionStatus status;
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private List<StockTransaction> alternativeTransactions = new ArrayList<>();
/**
* Positions of the Transaction
*/
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, mappedBy = "transaction")
List<StockTransactionPosition> positions = new ArrayList<>();
public StockTransaction() {
}
public StockTransaction(StockTransactionType type) {
this.type = type;
}
public List<StockTransaction> getAlternativeTransactions() {
return alternativeTransactions;
}
public void setAlternativeTransactions(List<StockTransaction> alternativeTransactions) {
this.alternativeTransactions = alternativeTransactions;
}
public void addUnit(StockUnit stockUnit) {
addPosition(new StockTransactionPosition(stockUnit));
}
public void addPosition(StockTransactionPosition position) {
if ( position == null ) return;
position.setTransaction(this);
}
public void removePosition(StockTransactionPosition position) {
if ( position == null ) return;
position.setTransaction(null);
}
/**
* Returns all positions
* <p/>
* @return all positions
*/
public List<StockTransactionPosition> getPositions() {
return Collections.unmodifiableList(positions);
}
/**
* Returns all stock units in this transaction.
* This method iterates over all positions and collects only {@link StockTransactionPosition#getStockUnit()} != null
*
* @return all stock units in this transaction.
*/
public List<StockUnit> getUnits() {
List<StockUnit> result = new ArrayList<>();
for (StockTransactionPosition position : positions) {
if ( position.getStockUnit() == null ) continue;
result.add(position.getStockUnit());
}
return result;
}
public List<StockTransactionStatus> getStatusHistory() {
return statusHistory;
}
public Date addStatus(StockTransactionStatus status) {
statusHistory.add(status);
this.status = status;
return status.getOccurence();
}
public Date addStatus(StockTransactionStatusType statusType,
StockTransactionParticipationType participationType1,
String participant1,
StockTransactionParticipationType participationType2,
String participant2) {
StockTransactionStatus newStatus = new StockTransactionStatus(statusType, new Date());
newStatus.addParticipation(new StockTransactionParticipation(participationType1, participant1, true));
newStatus.addParticipation(new StockTransactionParticipation(participationType2, participant2, true));
return addStatus(newStatus);
}
public Date addStatus(StockTransactionStatusType statusType,
StockTransactionParticipationType participationType1,
String participant1) {
StockTransactionStatus newStatus = new StockTransactionStatus(statusType, new Date());
newStatus.addParticipation(new StockTransactionParticipation(participationType1, participant1, true));
return addStatus(newStatus);
}
public Date addStatus(Date ocourence, StockTransactionStatusType statusType,
StockTransactionParticipationType participationType1,
String participant1) {
StockTransactionStatus newStatus = new StockTransactionStatus(statusType, ocourence);
newStatus.addParticipation(new StockTransactionParticipation(participationType1, participant1, true));
return addStatus(newStatus);
}
public List<StockTransactionParticipation> getParticipations() {
List<StockTransactionParticipation> result = new ArrayList<>();
for (StockTransactionStatus s : statusHistory) {
for (StockTransactionParticipation stp : s.getParticipations()) {
result.add(stp);
}
}
return result;
}
@Override
public void fetchEager() {
status.getParticipations().size();
for (StockTransactionPosition position : positions) {
if ( position.getStockUnit() != null ) position.getStockUnit().getId();
}
for (StockTransactionStatus stockTransactionStatus : getStatusHistory()) stockTransactionStatus.getParticipations().size();
}
@Null
public String getValidationViolations() {
List<StockTransactionStatus> sortedList = new ArrayList<>(getStatusHistory());
Collections.sort(sortedList);
List<StockTransactionStatusType> sortedTypeList = new ArrayList<>();
for (StockTransactionStatus stockTransactionStatus : sortedList) {
sortedTypeList.add(stockTransactionStatus.getType());
}
if ( POSSIBLE_STATUS_TYPES.contains(sortedTypeList) ) return null;
return "TransactionStatusHistory is in invalid: " + sortedTypeList;
}
@Override
public String toString() {
return "StockTransaction{id=" + id + ",source=" + source
+ ",destination=" + destination
+ ",type=" + type + ",status=" + status + ",positions=" + positions + '}';
}
public String toSimpleLine() {
return "StockTransaction{id=" + id + ",source=" + source
+ ",destination=" + destination
+ ",type=" + type + '}';
}
}