/*
* 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.report.assist;
import eu.ggnet.dwoss.rules.DocumentType;
import eu.ggnet.dwoss.rules.TradeName;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.*;
import eu.ggnet.dwoss.report.entity.Report.YearSplit;
import eu.ggnet.dwoss.report.entity.ReportLine.SingleReferenceType;
import eu.ggnet.dwoss.report.entity.ReportLine.WorkflowStatus;
import eu.ggnet.dwoss.report.entity.ReportLine;
import lombok.Value;
import static eu.ggnet.dwoss.report.entity.ReportLine.SingleReferenceType.WARRANTY;
import static eu.ggnet.dwoss.rules.DocumentType.*;
/**
*
* @author bastian.venz
*/
public class ReportUtil {
private final static Logger L = LoggerFactory.getLogger(ReportUtil.class);
/**
* Returns all Lines of the Report for Category Invoiced.
* This consists of:
* <ul>
* <li>Position of Type Capital Asset</li>
* <li>Position of Type Invoice, with no References</li>
* <li>Position of Type UNIT_ANNEX in DocumentType CREDIT_MEMO/ANNULATIION_INVOICE and a Referencing Invoice in the same report.</li>
* </ul>
* <p>
* It's not allowed to have a null value in the collection.
* <p>
* @param lines
* @return all Lines of the Report for Category Invoiced.
*/
//TODO: We could also substract the value of a unitannex from the invoice and not return the unit annex at all.
//But consider the impact in the ui, especially if we allow selection of such a "combined" line.
public static NavigableSet<ReportLine> filterInvoiced(Collection<ReportLine> lines) {
NavigableSet<ReportLine> result = new TreeSet<>();
for (ReportLine line : lines) {
if ( line.getDocumentType() == CAPITAL_ASSET ) result.add(line);
if ( line.getDocumentType() == INVOICE && !line.isFullRepayedIn(lines) ) result.add(line);
if ( line.isPartialRepayment() && !line.isFullRepayedIn(lines) ) {
ReportLine invoiceRef = line.getSingleReference(INVOICE);
if ( invoiceRef == null ) /* No Invoice exists, probably before 2014 */ result.add(line);
else if ( lines.contains(invoiceRef) ) result.add(line);
}
}
return result;
}
/**
* Returns all Lines of the Report for Category Invoiced, split by mfgDate - startOfReport < 1 year and the rest.
* This consists of:
* <ul>
* <li>Position of Type Invoice, with no References</li>
* <li>Position of Type UNIT_ANNEX in DocumentType CREDIT_MEMO/ANNULATIION_INVOICE and a Referencing Invoice in the same report.</li>
* </ul>
* <p>
* It's not allowed to have a null value in the collection.
* <p>
* @param lines
* @param startingDate
* @return all Lines of the Report for Category Invoiced.
*/
public static YearSplit filterInvoicedSplit(Collection<ReportLine> lines, Date startingDate) {
NavigableSet<ReportLine> pastSplit = new TreeSet<>();
NavigableSet<ReportLine> preSplit = new TreeSet<>();
Date splitter = DateUtils.addYears(startingDate, -1);
for (ReportLine line : filterInvoiced(lines)) {
if ( splitter.before(line.getMfgDate()) ) {
preSplit.add(line);
} else {
pastSplit.add(line);
}
}
return new YearSplit(startingDate, preSplit, pastSplit);
}
/**
* Returns all Lines of the Report which represent Positions of CreditMemos and Annulation Invoices but the associated Invoices have been reported before.
* This consists of:
* <ul>
* <li>Position of Type UNIT in DocumentType CREDIT_MEMO/ANNULATIION_INVOICE and a Referencing Invoice is not in same report.</li>
* <li>Position of Type UNIT_ANNEX in DocumentType CREDIT_MEMO/ANNULATIION_INVOICE and a Referencing Invoice is not in same report and no UNIT also in this
* report</li>
* </ul>
* <p>
* It's not allowed to have a null value in the collection.
* <p>
* @param lines
* @return all Lines of the Report which represent Positions of CreditMemos and Annulation Invoices but the associated Invoices have been reported before.
*/
public static NavigableSet<ReportLine> filterRepayed(Collection<ReportLine> lines) {
NavigableSet<ReportLine> result = new TreeSet<>();
for (ReportLine line : lines) {
if ( line.isFullRepayment() ) {
ReportLine invoiceRef = line.getSingleReference(DocumentType.INVOICE);
if ( invoiceRef == null ) /* No Invoice exists, probably before 2014 */ result.add(line);
else if ( !lines.contains(invoiceRef) ) result.add(line);
}
if ( line.isPartialRepayment() && !line.isFullRepayedIn(lines) ) {
ReportLine invoiceRef = line.getSingleReference(INVOICE);
if ( invoiceRef == null ) /* No Invoice exists, probably before 2014 */ result.add(line);
else if ( !lines.contains(invoiceRef) ) result.add(line);
}
}
return result;
}
/**
* Returns all Reportlines, that don't have an impact on the result, but have only informational character.
* <p>
* It's not allowed to have a null value in the collection.
* <p>
* @param lines
* @return all Reportlines, that don't have an impact on the result, but have only informational character.
*/
public static NavigableSet<ReportLine> filterReportInfo(Collection<ReportLine> lines) {
NavigableSet<ReportLine> result = new TreeSet<>(lines);
result.removeAll(filterInvoiced(lines));
result.removeAll(filterRepayed(lines));
return result;
}
/**
* Removes all Lines, that only represent active Info (open Complaints).
* <ol>
* <li>Sammle alle only Invoice Positions raus → Report</li>
* <li>Sammle alle Repayment Positions raus → Report</li>
* <li>Sammle alle Complaint Positionen die mit den Repayment Positionen zusammenhängen raus → Report</li>
* <li>Sammle alle Compleints die DISCHARDED sind → Report</li>
* <li>Alles was übrig ist, sollten (offene) Complaints sein → Active Info</li>
* </ol>
* <p>
* It's not allowed to have a null value in the collection.
* <p>
* @param allLines all lines.
* @param reportType the report type
* @return
*/
public static PrepareReportPartition partition(Collection<ReportLine> allLines, TradeName reportType) {
L.debug("filter {}", allLines);
NavigableSet<ReportLine> reportAble = new TreeSet<>();
for (ReportLine line : allLines) {
L.debug("filter processing {}", line.toSimple());
if ( !(line.getDocumentType() == DocumentType.ANNULATION_INVOICE
|| line.getDocumentType() == DocumentType.CREDIT_MEMO
|| line.getDocumentType() == DocumentType.CAPITAL_ASSET
|| (line.getDocumentType() == DocumentType.COMPLAINT && line.getWorkflowStatus() == WorkflowStatus.DISCHARGED)
|| (line.getDocumentType() == DocumentType.INVOICE && line.hasNoRepayments()) && line.hasNoOpenComplaints()) )
continue;
L.debug("filter processing, add to reportAble {}", line.toSimple());
reportAble.add(line);
Date tomorrow = DateUtils.addDays(line.getReportingDate(), 1);
for (ReportLine ref : line.getRefrences()) {
if ( ref.getDocumentType() == DocumentType.COMPLAINT && !ref.isInReport(reportType) ) {
L.debug("filter processing referencing complaints, add to reportAble {}", ref.toSimple());
reportAble.add(ref);
} else if ( ref.getDocumentType() == DocumentType.INVOICE && !ref.isInReport(reportType) && ref.getReportingDate().before(tomorrow) ) {
L.debug("filter processing referencing invoices, add to reportAble {}", ref.toSimple());
reportAble.add(ref);
}
}
}
NavigableSet<ReportLine> activeInfo = new TreeSet<>(allLines);
activeInfo.removeAll(reportAble);
return new PrepareReportPartition(reportAble, activeInfo);
}
@Value
public static class PrepareReportPartition {
private final NavigableSet<ReportLine> reportAble;
private final NavigableSet<ReportLine> activeInfo;
}
/**
* Returns a set containing only non reportable lines that are not of the RETURNS type.
* It's not allowed to have a null value in the collection.
* <p>
* @param allLines
* @param reportAble
* @return
*/
public static NavigableSet<ReportLine> filterActiveInfo(Collection<ReportLine> allLines, Collection<ReportLine> reportAble) {
TreeSet<ReportLine> treeSet = new TreeSet<>(allLines);
treeSet.removeAll(reportAble);
for (Iterator<ReportLine> it = treeSet.iterator(); it.hasNext();) {
ReportLine reportLine = it.next();
if ( reportLine.getDocumentType() == DocumentType.RETURNS ) it.remove();
}
return treeSet;
}
/**
* Returns a Set of all Warrenty Positions in the Collection that is given to the method.
* <p>
* A Warranty is in the Set of Reportable Warranty if
* <ul>
* <li>SingleRefence from Type {@link SingleReferenceType#WARRANTY} is not null</li>
* <li>SingleReferenced Unit is in the reportable Amount of ReportLines</li>
* <li>Reporting Date is after the from Parameter and before the till Parameter</li>
* </ul>
* <p>
* @param warrentyLines all unreported Reportlines that represent warrenty.
* @param unitLines all ReportLine's that are already in amount of Reportlines which should be reported.
* @return all Warrentys which can be reported in this report.
*/
public static NavigableSet<ReportLine> filterWarrenty(Collection<ReportLine> warrentyLines, Collection<ReportLine> unitLines) {
L.info("Warranties in filter: {}", warrentyLines);
return warrentyLines.stream()
.filter((t) -> t != null
&& t.getReference(WARRANTY) != null
&& (unitLines.contains(t.getReference(WARRANTY)) || !t.getReference(WARRANTY).getReports().isEmpty()))
.collect(Collectors.toCollection(() -> new TreeSet<ReportLine>()));
}
}