/*
* 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.misc.op;
import eu.ggnet.dwoss.rules.DocumentType;
import eu.ggnet.dwoss.rules.PositionType;
import eu.ggnet.dwoss.progress.SubMonitor;
import eu.ggnet.dwoss.progress.MonitorFactory;
import eu.ggnet.dwoss.redtape.entity.Position;
import eu.ggnet.dwoss.redtape.assist.RedTapes;
import eu.ggnet.dwoss.redtape.entity.Document;
import eu.ggnet.dwoss.redtape.entity.Dossier;
import eu.ggnet.dwoss.stock.eao.StockUnitEao;
import eu.ggnet.dwoss.stock.entity.LogicTransaction;
import eu.ggnet.dwoss.stock.eao.LogicTransactionEao;
import eu.ggnet.dwoss.stock.entity.StockUnit;
import eu.ggnet.dwoss.uniqueunit.assist.UniqueUnits;
import eu.ggnet.lucidcalc.CCalcDocument;
import eu.ggnet.lucidcalc.LucidCalc;
import eu.ggnet.lucidcalc.CSheet;
import eu.ggnet.lucidcalc.CBorder;
import eu.ggnet.lucidcalc.STable;
import eu.ggnet.lucidcalc.CFormat;
import eu.ggnet.lucidcalc.TempCalcDocument;
import eu.ggnet.lucidcalc.STableModelList;
import eu.ggnet.lucidcalc.STableColumn;
import java.io.File;
import java.util.*;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.swing.JOptionPane;
import javax.validation.*;
import eu.ggnet.dwoss.redtape.eao.DossierEao;
import eu.ggnet.dwoss.stock.assist.Stocks;
import eu.ggnet.dwoss.uniqueunit.eao.UniqueUnitEao;
import eu.ggnet.dwoss.uniqueunit.entity.UniqueUnit;
import eu.ggnet.dwoss.util.FileJacket;
import eu.ggnet.dwoss.util.validation.ConstraintViolationFormater;
import lombok.*;
import static eu.ggnet.lucidcalc.CFormat.FontStyle.BOLD_ITALIC;
import static eu.ggnet.lucidcalc.CFormat.HorizontalAlignment.CENTER;
import static java.awt.Color.*;
@Stateless
public class PersistenceValidatorOperation implements PersistenceValidator {
@Data
public static class Vm {
@RequiredArgsConstructor
private static enum Level {
ERROR("Fehler"), WARNING("Warnung");
@Getter
private final String name;
}
private final Level level;
private final String message;
}
@Inject
@RedTapes
private EntityManager redTapeEm;
@Inject
@UniqueUnits
private EntityManager uuEm;
@Inject
@Stocks
private EntityManager stockEm;
@Inject
private MonitorFactory monitorFactory;
private final static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
/**
* This Method Validate all Databases.
* It's validate:
* - RedTape
* - UniqueUnit
* - Sopo
* - Stock
* <p/>
* @return a Filejacket where a xls from the JExcel api is, that contains all Errors.
*/
@Override
public FileJacket validateDatabase() {
List<Vm> vms = new ArrayList<>();
UniqueUnitEao uuEao = new UniqueUnitEao(uuEm);
DossierEao dossierEao = new DossierEao(redTapeEm);
LogicTransactionEao logicEao = new LogicTransactionEao(stockEm);
StockUnitEao stockUnitEao = new StockUnitEao(stockEm);
int uuMax = uuEao.count();
int dossierMax = dossierEao.count();
int logicMax = logicEao.count();
int stockUnitMax = stockUnitEao.count();
SubMonitor m = monitorFactory.newSubMonitor("DatenbankValidation", 100);
//All Listen
List<Dossier> dossiers;
List<UniqueUnit> uniqueUnits;
List<LogicTransaction> logicTransactions;
List<StockUnit> stockUnits;
m.message("Hole alle Unique Units. " + uuMax);
uniqueUnits = uuEao.findAll();
m.worked(10, "Hole Alle Dossiers. " + dossierMax);
dossiers = dossierEao.findAll();
m.worked(10, "Hole Alle LogicTransaction. " + logicMax);
logicTransactions = logicEao.findAll();
m.worked(10, "Hole Alle StockUnit. " + stockUnitMax);
stockUnits = stockUnitEao.findAll();
m.worked(10, "Validieren.");
validateRedTape(vms, dossiers, convertUnitListToMap(uniqueUnits), m.newChild(10));
validateUniqueUnit(vms, uniqueUnits, m.newChild(10));
validateLogicTransaction(vms, logicTransactions, dossiers, m.newChild(10));
// TODO: split:
// 1. Sopo Validation <-> RedTape
// 2. Sopo Only.
m.finish();
return createFileJacket(vms);
}
// HINT: See validate in RedTapeUpdateWorkflow.
/**
* This method checks given Dossiers.
* It checks followed things:
* - It is only one Document of every existing Type active but it must one Document active.
* - Every Position from type UNIT have a existing UniqueUnit.
* - It exist a SopoAuftrag to a Dossier. <---- TODO: It must a status of a Dossier where a SopoAuftrag exist, not Canceled ---->
* - and a BothSide check if the Positions of a Dossier are in the SopoAuftrag and vice vesa.
* <p/>
* @param dossiers
* @param units Map<uniqueUnitId, uniqueUnits&rt;
* @param auftrags
* @param m
* @return
*/
private void validateRedTape(List<Vm> vms, List<Dossier> dossiers, Map<Integer, UniqueUnit> units, SubMonitor m) {
m.setWorkRemaining(dossiers.size());
m.start();
for (Dossier dossier : dossiers) {
m.worked(1, "Validate: Dossier:" + dossier.getId());
Set<ConstraintViolation<Dossier>> validateError = validator.validate(dossier);
if ( !validateError.isEmpty() ) {
error(vms, ConstraintViolationFormater.toSingleLine(validateError));
continue;
}
for (DocumentType type : DocumentType.values()) {
Set<Document> documents = dossier.getDocuments();
List<Document> existingDocumentWithType = new ArrayList<>();
for (Document document : documents) {
if ( document.getType() == type ) existingDocumentWithType.add(document);
}
if ( !existingDocumentWithType.isEmpty() && !dossier.getActiveDocuments(type).isEmpty()
&& dossier.getActiveDocuments(type).size() > 1 )
error(vms, "Dossier(id=" + dossier.getId() + ",customerId=" + dossier.getCustomerId() + "): Es ist mehr als ein Dokument vom Typ " + type.getName() + " ist active.");
}
for (Document document : dossier.getDocuments()) {
for (Position position : document.getPositions(PositionType.UNIT).values()) {
if ( !units.containsKey(position.getUniqueUnitId()) ) {
error(vms, "Dossier(id=" + dossier.getId() + ",customerId=" + dossier.getCustomerId() + ") enhält Position(type=Unit,uniqueUnitId=" + position.getUniqueUnitId() + "): UniqueUnit existiert nicht.");
}
}
}
if ( !dossier.getActiveDocuments(DocumentType.ORDER).isEmpty()
&& dossier.getActiveDocuments(DocumentType.ORDER).get(0).getConditions().contains(Document.Condition.CANCELED) ) {
continue;
} else if ( dossier.isClosed() ) {
continue;
}
}
m.finish();
}
/**
* This Method Check the given UniqueUnit list.
* First check is if a UniqueUnit has a Product and the Productdescription isn't null.
* If the Description isn't null it checks if it contains a invalid Character.
* <p/>
* @param units
* @param m
* @return
*/
private void validateUniqueUnit(List<Vm> vms, List<UniqueUnit> units, SubMonitor m) {
m.setWorkRemaining(units.size());
m.start();
for (UniqueUnit uniqueUnit : units) {
m.worked(1, "Validate: UniqueUnit:" + uniqueUnit.getId());
Set<ConstraintViolation<UniqueUnit>> validateError = validator.validate(uniqueUnit);
if ( !validateError.isEmpty() ) {
error(vms, ConstraintViolationFormater.toSingleLine(validateError));
}
if ( uniqueUnit.getProduct() == null ) {
warn(vms, "UniqueUnit(id=" + uniqueUnit.getId() + ",refurbishId=" + uniqueUnit.getRefurbishId() + ").product == null");
} else if ( uniqueUnit.getProduct().getDescription() == null ) {
warn(vms, "UniqueUnit(id=" + uniqueUnit.getId() + ",refurbishId=" + uniqueUnit.getRefurbishId() + ").product(id=" + uniqueUnit.getProduct().getId() + ",partNo=" + uniqueUnit.getProduct().getPartNo() + ").description == null");
}
}
m.finish();
}
/**
*
* This Method Validate all LogicTransaction that will be given.
* First it checks if all UUIds from the Document are in the Logictransaction.
* Then check its in the opposite way.
* <p/>
* @param transactions
* @param dossiers
* @param m
* @return
*/
private void validateLogicTransaction(List<Vm> vms, List<LogicTransaction> transactions, List<Dossier> dossiers, SubMonitor m) {
Map<Long, Dossier> dossierMap = new HashMap<>();
m.setWorkRemaining(transactions.size());
for (Dossier dossier : dossiers) {
dossierMap.put(dossier.getId(), dossier);
}
for (LogicTransaction logicTransaction : transactions) {
m.worked(1, "Validate: LogicTransaction:" + logicTransaction.getId());
Dossier dossier = dossierMap.get(logicTransaction.getDossierId());
DocumentType type = getMostImportandDocument(dossierMap.get(logicTransaction.getDossierId())).getType();
// TODO: Here you discard cases, not good.
if ( type != DocumentType.INVOICE && type != DocumentType.ORDER ) {
continue;
}
List<Integer> stockUuIds = toUniqueUnitIds(logicTransaction);
if ( !stockUuIds.containsAll(dossier.getRelevantUniqueUnitIds()) ) {
error(vms, "Stock asynchron zu Dossier. LogicTransaction(id=" + logicTransaction.getId() + ", UniqueUnits="
+ logicTransaction.getUnits() + ") ->" + "Dossier( id=" + dossier.getId() + ",customerId=" + dossier.getCustomerId() + ", relevant UniqueUnits="
+ dossier.getRelevantUniqueUnitIds() + ")");
}
if ( !dossier.getRelevantUniqueUnitIds().containsAll(stockUuIds) ) {
error(vms, "Dossier asynchron zu Stock." + "Dossier(id=" + dossier.getId() + ",customerId=" + dossier.getCustomerId() + ",relevant UniqueUnits="
+ dossier.getRelevantUniqueUnitIds() + ") -> LogicTransaction(id=" + logicTransaction.getId() + ", UniqueUnits="
+ logicTransaction.getUnits() + ")");
}
for (StockUnit stockUnit : logicTransaction.getUnits()) {
Set<ConstraintViolation<StockUnit>> validateError = validator.validate(stockUnit);
if ( !validateError.isEmpty() ) {
error(vms, ConstraintViolationFormater.toSingleLine(validateError));
}
}
}
m.finish();
}
/** TODO: SHOULD BE TESTED IF ITS WORKS CORRECTLY!
* MID / VIP
* <p/>
* @param dossier
* @return
*/
private Document getMostImportandDocument(Dossier dossier) {
List<Document> activeDocuments = dossier.getActiveDocuments();
Document activeDocument = null;
for (Document document : activeDocuments) {
if ( activeDocument == null ) {
activeDocument = document;
continue;
}
if ( activeDocument.getType().compareTo(document.getType()) > 0 )
activeDocument = document;
}
return activeDocument;
}
/**
* Convert a List of SopoUnits to a map with UniqueUnit id as Key.
* <p/>
* @param units
* @return
*/
private Map<Integer, StockUnit> stockToUniqueUnitId(Collection<StockUnit> units) {
Map<Integer, StockUnit> map = new HashMap<>();
for (StockUnit stockUnit : units) {
map.put(stockUnit.getUniqueUnitId(), stockUnit);
}
return map;
}
/**
* Make out of List of units a Map with UnitId as Key
* <p/>
* @param units
* @return
*/
private Map<Integer, UniqueUnit> convertUnitListToMap(Collection<UniqueUnit> units) {
Map<Integer, UniqueUnit> map = new HashMap<>();
for (UniqueUnit uniqueUnit : units) {
map.put(uniqueUnit.getId(), uniqueUnit);
}
return map;
}
/**
* This method make a List of UniqueUnitIds out of the given LogicTransaction.
* <p/>
* @param logicTransaction
* @return
*/
private List<Integer> toUniqueUnitIds(LogicTransaction logicTransaction) {
List<Integer> stockUuIds = new ArrayList<>(logicTransaction.getUnits().size());
for (StockUnit stockUnit : logicTransaction.getUnits()) {
stockUuIds.add(stockUnit.getUniqueUnitId());
}
return stockUuIds;
}
/**
* This Method create a XLS Document with the given list of errors.
* First it sort the erros by his level and put them into different sheets.
* <p/>
* @param errors
* @return
*/
private FileJacket createFileJacket(List<Vm> errors) {
if ( errors.isEmpty() ) {
JOptionPane.showMessageDialog(null, "Kein Fehler gefunden");
return null;
}
List<Object[]> rows = new ArrayList<>();
for (Vm vm : errors) {
rows.add(new Object[]{vm.getLevel(), vm.getMessage()});
}
CSheet sheet = new CSheet("Fehler");
STable table = new STable();
table.setHeadlineFormat(new CFormat(BOLD_ITALIC, BLACK, RED, CENTER, new CBorder(BLACK)));
table.add(new STableColumn("Level", 12, new CFormat(RED, BLUE))).add(new STableColumn("Nachricht", 60));
table.setModel(new STableModelList(rows));
sheet.addBelow(table);
CCalcDocument document = new TempCalcDocument();
document.add(sheet);
File file = LucidCalc.createWriter(LucidCalc.Backend.XLS).write(document);
FileJacket result = new FileJacket("Datenbank_Errors", ".xls", file);
return result;
}
private void error(List<Vm> vms, String msg) {
if ( msg == null ) return;
if ( msg.trim().equals("") ) return;
vms.add(new Vm(Vm.Level.ERROR, msg));
}
private void warn(List<Vm> vms, String msg) {
if ( msg == null ) return;
if ( msg.trim().equals("") ) return;
vms.add(new Vm(Vm.Level.WARNING, msg));
}
}