///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition 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; version 3 of the License. // // This community edition 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 org.projectforge.fibu.kost.reporting; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.apache.commons.lang.ObjectUtils; import org.projectforge.fibu.KostFormatter; import org.projectforge.fibu.kost.AccountingConfig; import org.projectforge.fibu.kost.BuchungssatzDO; import org.projectforge.fibu.kost.BusinessAssessment; import org.projectforge.fibu.kost.BusinessAssessmentTable; import org.projectforge.user.PFUserContext; /** * Ein Report enthält unterliegende Buchungssätze, die gemäß Zeitraum und zugehörigem ReportObjective selektiert werden. * @author Kai Reinhard (k.reinhard@micromata.de) * */ public class Report implements Serializable { private static final long serialVersionUID = -5359861335173843043L; private transient List<BuchungssatzDO> buchungssaetze; private transient Set<BuchungssatzDO> buchungssatzSet; private transient ReportObjective reportObjective; private transient List<Report> childReports; private transient List<BuchungssatzDO> other; private transient List<BuchungssatzDO> duplicates; private boolean showChilds; private transient BusinessAssessment businessAssessment; private transient BusinessAssessmentTable businessAssessmentTable; private int fromYear; private int fromMonth; private int toYear; private int toMonth; private transient Report parent; public Report(final ReportObjective reportObjective) { this.reportObjective = reportObjective; } public Report(final ReportObjective reportObjective, final Report parent) { this(reportObjective, parent.fromYear, parent.fromMonth, parent.toYear, parent.toMonth); this.parent = parent; } public Report(final ReportObjective reportObjective, final int fromYear, final int fromMonth, final int toYear, final int toMonth) { this(reportObjective); this.fromYear = fromYear; this.fromMonth = fromMonth; this.toYear = toYear; this.toMonth = toMonth; } public void setFrom(final int year, final int month) { this.fromYear = year; this.fromMonth = month; } public void setTo(final int year, final int month) { this.toYear = year; this.toMonth = month; } public int getFromYear() { return fromYear; } public int getFromMonth() { return fromMonth; } public int getToYear() { return toYear; } public int getToMonth() { return toMonth; } public Report getParent() { return parent; } /** * Gibt den Reportpfad zurück, vom Root-Report bis zum direkten Eltern-Report. Der Report selber ist nicht im Pfad enthalten. * @return Liste oder null, wenn der Report keinen Elternreport hat. */ public List<Report> getPath() { if (this.parent == null) { return null; } final List<Report> path = new ArrayList<Report>(); this.parent.getPath(path); return path; } private void getPath(final List<Report> path) { if (this.parent != null) { this.parent.getPath(path); } path.add(this); } public ReportObjective getReportObjective() { return reportObjective; } public BusinessAssessment getBusinessAssessment() { if (this.businessAssessment == null) { this.businessAssessment = new BusinessAssessment(AccountingConfig.getInstance().getBusinessAssessmentConfig()); this.businessAssessment.setReference(this); this.businessAssessment.setStoreAccountRecordsInRows(true); this.businessAssessment.setAccountRecords(this.buchungssaetze); } return this.businessAssessment; } /** * Creates an array with all business assessment's of the child reports. * @param prependThisReport If true then the business assessment of this report will be prepend as first column. */ public BusinessAssessmentTable getChildBusinessAssessmentTable(final boolean prependThisReport) { if (businessAssessmentTable == null) { if (prependThisReport == false && hasChilds() == false) { return null; } businessAssessmentTable = new BusinessAssessmentTable(); if (prependThisReport == true) { businessAssessmentTable.addBusinessAssessment(this.getId(), this.getBusinessAssessment()); } if (hasChilds() == true) { for (final Report child : getChilds()) { businessAssessmentTable.addBusinessAssessment(child.getId(), child.getBusinessAssessment()); } } } return businessAssessmentTable; } /** * Wurde eine Selektion bereits durchgeführt? * @return true, wenn Buchungssätzeliste vorhanden ist (kann aber auf Grund der Selektion auch leer sein). */ public boolean isLoad() { return this.buchungssaetze != null; } public boolean isShowChilds() { return showChilds; } public void setShowChilds(final boolean showChilds) { this.showChilds = showChilds; } /** * @see ReportObjective#hasChilds() */ public boolean hasChilds() { return reportObjective.getHasChilds(); } /** * @see ReportObjective#getId() */ public String getId() { return reportObjective.getId(); } /** * @see ReportObjective#getTitle() */ public String getTitle() { return reportObjective.getTitle(); } public Report findById(final String id) { if (ObjectUtils.equals(this.reportObjective.getId(), id) == true) { return this; } if (hasChilds() == false) { return null; } for (final Report report : getChilds()) { if (ObjectUtils.equals(report.reportObjective.getId(), id) == true) { return report; } } for (final Report report : getChilds()) { final Report result = report.findById(id); if (result != null) { return result; } } return null; } /** * Creates and get the childs if the ReportObjective has childs. Iteriert über alle ChildReportObjectives und legt jeweils einen Report an * und selektiert gemäß Filter des ReportObjectives die Buchungssätze. Wenn Childs nicht implizit erzeugt werden sollen, so sollte die * Funktion hasChilds zur Abfrage genutzt werden. * @see #select(List) */ public List<Report> getChilds() { if (childReports == null && hasChilds() == true) { childReports = new ArrayList<Report>(); for (final ReportObjective child : reportObjective.getChildReportObjectives()) { final Report report = new Report(child, this); report.select(this.buchungssaetze); childReports.add(report); } if (this.buchungssaetze != null && (reportObjective.isSuppressOther() == false || reportObjective.isSuppressDuplicates() == false)) { for (final BuchungssatzDO satz : this.buchungssaetze) { int n = 0; for (final Report child : getChilds()) { if (child.contains(satz) == true) { n++; } } if (reportObjective.isSuppressOther() == false && n == 0) { // Kommt bei keinem Childreport vor: if (other == null) { other = new ArrayList<BuchungssatzDO>(); } other.add(satz); } else if (reportObjective.isSuppressDuplicates() == false && n > 1) { // Kommt bei mehreren Childs vor: if (duplicates == null) { duplicates = new ArrayList<BuchungssatzDO>(); } duplicates.add(satz); } } } if (reportObjective.isSuppressOther() == false && this.other != null) { final ReportObjective objective = new ReportObjective(); final String other = PFUserContext.getLocalizedString("fibu.reporting.other"); objective.setId(this.getId() + " - " + other); objective.setTitle(this.getTitle() + " - " + other); final Report report = new Report(objective, this); report.setBuchungssaetze(this.other); childReports.add(report); } if (reportObjective.isSuppressDuplicates() == false && this.duplicates != null) { final ReportObjective objective = new ReportObjective(); final String duplicates = PFUserContext.getLocalizedString("fibu.reporting.duplicates"); objective.setId(this.getId() + " - " + duplicates); objective.setTitle(this.getTitle() + " - " + duplicates); final Report report = new Report(objective, this); report.setBuchungssaetze(this.duplicates); childReports.add(report); } } return childReports; } public List<BuchungssatzDO> getBuchungssaetze() { return buchungssaetze; } /** * Bitte entweder diese Methode ODER select(...) benutzen. * @param buchungssaetze */ public void setBuchungssaetze(final List<BuchungssatzDO> buchungssaetze) { this.buchungssaetze = buchungssaetze; } /** * Gibt die Liste aller sonstigen Buchungssätze zurück, d. h. Buchungssätze, die zwar in diesem Report vorkommen aber in keinem der * Childreports vorkommen. * @return Liste oder null, wenn keine Einträge vorhanden sind. */ public List<BuchungssatzDO> getOther() { return other; } /** * Gibt die Liste aller doppelten Buchungssätze zurück, d. h. Buchungssätze, die in mehreren Childreports vorkommen. * @return Liste oder null, wenn keine Einträge vorhanden sind. */ public List<BuchungssatzDO> getDuplicates() { return duplicates; } public String getZeitraum() { return KostFormatter.formatZeitraum(fromYear, fromMonth, toYear, toMonth); } /** * Diese initiale Liste der Buchungsliste wird sofort bezüglich Exclude- und Include-Filter selektiert und das Ergebnis gesetzt. * @param buchungssaetze vor Selektion. */ public void select(final List<BuchungssatzDO> list) { final Predicate regExpPredicate = new Predicate() { public boolean evaluate(final Object obj) { final BuchungssatzDO satz = (BuchungssatzDO) obj; final String kost1 = KostFormatter.format(satz.getKost1()); final String kost2 = KostFormatter.format(satz.getKost2()); // 1st of all the Blacklists if (match(reportObjective.getKost1ExcludeRegExpList(), kost1, false) == true) { return false; } if (match(reportObjective.getKost2ExcludeRegExpList(), kost2, false) == true) { return false; } // 2nd the whitelists final boolean kost1Match = match(reportObjective.getKost1IncludeRegExpList(), kost1, true); final boolean kost2Match = match(reportObjective.getKost2IncludeRegExpList(), kost2, true); return kost1Match == true && kost2Match == true; } }; this.buchungssaetze = new ArrayList<BuchungssatzDO>(); this.buchungssatzSet = new HashSet<BuchungssatzDO>(); this.businessAssessment = null; this.businessAssessmentTable = null; this.childReports = null; this.duplicates = null; this.other = null; CollectionUtils.select(list, regExpPredicate, this.buchungssaetze); for (final BuchungssatzDO satz : this.buchungssaetze) { this.buchungssatzSet.add(satz); } } public boolean contains(final BuchungssatzDO satz) { if (buchungssatzSet == null) { return false; } return this.buchungssatzSet.contains(satz); } /** * In jedem regulärem Ausdruck werden alle Punkte gequoted und alle * durch ".*" ersetzt, bevor der Ausdruck durch * {@link Pattern#compile(String)} kompiliert wird.<br/> * Beispiele: * <ul> * <li>5.100.* -> 5\.100\..*</li> * <li>*.10.* -> .*\.10\..*</li> * </ul> * @param regExpList * @param kost * @param emptyListMatches * @return * @see String#matches(String)() * @see #modifyRegExp(String) */ public static boolean match(final List<String> regExpList, final String kost, final boolean emptyListMatches) { if (CollectionUtils.isNotEmpty(regExpList) == true) { for (final String str : regExpList) { final String regExp = modifyRegExp(str); if (kost.matches(regExp) == true) { return true; } } return false; } else { // List is empty: return emptyListMatches; } } /** * Alle Punkte werden gequoted und alle * durch ".*" ersetzt. Ausnahme: Der String beginnt mit einem einfachen Hochkomma, dann werden * keine Ersetzungen durchgeführt, sondern lediglich das Hochkomma selbst entfernt. * @param regExp */ public static String modifyRegExp(final String regExp) { if (regExp == null) { return null; } if (regExp.startsWith("'") == true) { return regExp.substring(1); } final String str = regExp.replace(".", "\\.").replace("*", ".*"); return str; } }