/* * 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; import java.util.*; import javax.ejb.Stateless; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.persistence.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.*; import eu.ggnet.dwoss.common.log.AutoLogger; import eu.ggnet.dwoss.report.api.MarginCalculator; import eu.ggnet.dwoss.report.assist.ReportUtil.PrepareReportPartition; import eu.ggnet.dwoss.report.assist.Reports; import eu.ggnet.dwoss.report.eao.ReportLineEao; import eu.ggnet.dwoss.report.entity.Report; import eu.ggnet.dwoss.report.entity.Report.YearSplit; import eu.ggnet.dwoss.report.entity.ReportLine; import eu.ggnet.dwoss.report.entity.partial.SimpleReportLine; import eu.ggnet.dwoss.rules.*; import eu.ggnet.dwoss.util.persistence.AbstractAgentBean; import static eu.ggnet.dwoss.rules.PositionType.*; import static eu.ggnet.dwoss.report.ReportAgent.ViewReportResult.Type.*; import static eu.ggnet.dwoss.report.assist.ReportUtil.*; import static eu.ggnet.dwoss.report.entity.Report.ViewMode.*; /** * * @author oliver.guenther */ @Stateless public class ReportAgentBean extends AbstractAgentBean implements ReportAgent { private final static Logger L = LoggerFactory.getLogger(ReportAgentBean.class); @Inject @Reports private EntityManager reportEm; @Inject private ReportLineEao reportLineEao; @Inject private Instance<MarginCalculator> marginCalculator; @Override protected EntityManager getEntityManager() { return reportEm; } /** * Returns all ReportLines, which are at the given Customer id from to till the given Dates * <p/> * @param type the document type * @param till the date as upper border * @param from the date as lower border * @return the matching report lines. */ @Override public List<ReportLine> findReportLinesByDocumentType(DocumentType type, Date from, Date till) { return reportLineEao.findbyDocumentTypeFromTill(type, from, till); } /** * Returns all ReportLines, limited by first and max ordered by reportDate descending. * <p> * @param firstResult the first result to return * @param maxResults the maximum results to return * @return all ReportLines, limitied by first and max ordered by reportDate descending. */ @Override public List<ReportLine> findAllReportLinesReverse(int firstResult, int maxResults) { return reportLineEao.findAllReverse(firstResult, maxResults); } /** * Stores a new report, persisting the report and merging the lines. * <p/> * @param report the report to persist. * @param storables the lines to merge, only the id is considered and a new instance is used from the EntityManager. * @return the persisted report. */ @Override @AutoLogger public Report store(Report report, Collection<ReportLine.Storeable> storables) { for (ReportLine.Storeable storable : storables) { ReportLine line = reportEm.find(ReportLine.class, storable.getId()); line.setMarginPercentage(storable.getMarginPercentage()); line.setPurchasePrice(storable.getPurchasePrice()); report.add(line); L.debug("Report Line {} was anded to report. ", line); } reportEm.persist(report); return optionalFetchEager(report); } @Override public ViewReportResult findReportResult(long reportId) { Report r = findById(Report.class, reportId); return r == null ? null : ViewReportResult.fromReport(r); } /** * Attaches "dangling" {@link ReportLine}<code>s</code> of {@link DocumentType#COMPLAINT} to existing Reports of contractor. A ReportLine is considered * "dangling" if: * <ul> * <li>It is of {@link PositionType#UNIT}</li> * <li>It is not in a report of the type</li> * <li>It references a {@link ReportLine} of {@link DocumentType#ANNULATION_INVOICE} or {@link DocumentType#CREDIT_MEMO} which is already in a report of the * type.</li> * <li></li> * </ul> * These "dangling" {@link ReportLine}<code>s</code> are added to the Report that contains the referencing {@link ReportLine} of * {@link DocumentType#ANNULATION_INVOICE} or {@link DocumentType#CREDIT_MEMO}. * <p> * @param type the contractor as report identifier. * @param till the upper limit. * @return the discovered and attached ReportLines. */ @Override public Set<ReportLine> attachDanglingComplaints(TradeName type, Date till) { L.info("attachDanglingComplaints(contractor={},till={}", type, till); //TODO: There might be a case their first a CreditMemo for a Unit_Annex is create and than a complaint for the full unit. For now, this is ignored. //TODO: The Allreport will fail here. Should be covered before List<ReportLine> unrepored = reportLineEao.findUnreported(type, null, till, UNIT, UNIT_ANNEX, PRODUCT_BATCH); L.debug("found unreported: {}", unrepored); Map<ReportLine, Report> danglingComplaints = filterDanglingComplaints(unrepored, type); L.debug("filted dangling: {}", danglingComplaints); for (Map.Entry<ReportLine, Report> entry : danglingComplaints.entrySet()) { entry.getValue().add(entry.getKey()); } L.info("attachDanglingComplaints complete"); return new HashSet<>(danglingComplaints.keySet()); } private Map<ReportLine, Report> filterDanglingComplaints(List<ReportLine> unrepored, TradeName type) { Map<ReportLine, Report> filtered2 = new HashMap<>(); for (ReportLine possibleDangling : unrepored) { if ( possibleDangling.getDocumentType() != DocumentType.COMPLAINT ) continue; for (ReportLine ref : possibleDangling.getRefrences()) { Report report = inReport(ref, type); if ( (ref.getDocumentType() == DocumentType.ANNULATION_INVOICE || ref.getDocumentType() == DocumentType.CREDIT_MEMO) && report != null ) { filtered2.put(possibleDangling, report); } } } return filtered2; } private Report inReport(ReportLine line, TradeName type) { for (Report report : line.getReports()) { if ( report.getType() == type ) return report; } return null; } @Override public List<SimpleReportLine> findSimple(SearchParameter search, int firstResult, int maxResults) { StringBuilder sb = new StringBuilder("Select l from SimpleReportLine l"); if ( !StringUtils.isBlank(search.getRefurbishId()) ) sb.append(" where l.refurbishId = :refurbishId"); L.debug("Using created SearchQuery:{}", sb); TypedQuery<SimpleReportLine> q = reportEm.createQuery(sb.toString(), SimpleReportLine.class); if ( !StringUtils.isBlank(search.getRefurbishId()) ) q.setParameter("refurbishId", search.getRefurbishId().trim()); q.setFirstResult(firstResult); q.setMaxResults(maxResults); return q.getResultList(); } @Override public List<ReportLine> find(SearchParameter search, int firstResult, int maxResults) { StringBuilder sb = new StringBuilder("Select l from ReportLine l"); if ( !StringUtils.isBlank(search.getRefurbishId()) ) sb.append(" where l.refurbishId = :refurbishId"); L.debug("Using created SearchQuery:{}", sb); TypedQuery<ReportLine> q = reportEm.createQuery(sb.toString(), ReportLine.class); if ( !StringUtils.isBlank(search.getRefurbishId()) ) q.setParameter("refurbishId", search.getRefurbishId().trim()); q.setFirstResult(firstResult); q.setMaxResults(maxResults); return q.getResultList(); } @Override public long count(SearchParameter search) { StringBuilder sb = new StringBuilder("Select Count(l) from ReportLine l"); if ( !StringUtils.isBlank(search.getRefurbishId()) ) sb.append(" where l.refurbishId = :refurbishId"); L.debug("Using created SearchQuery:{}", sb); TypedQuery<Long> q = reportEm.createQuery(sb.toString(), Long.class); if ( !StringUtils.isBlank(search.getRefurbishId()) ) q.setParameter("refurbishId", search.getRefurbishId().trim()); return q.getSingleResult(); } @Override public ViewReportResult prepareReport(ReportParameter p, boolean loadUnreported) { attachDanglingComplaints(p.getContractor(), p.getEnd()); List<ReportLine> findUnreportedUnits = reportLineEao.findUnreportedUnits(p.getContractor(), (loadUnreported) ? null : p.getStart(), p.getEnd()); EnumMap<ViewReportResult.Type, NavigableSet<ReportLine>> lines = new EnumMap<>(ViewReportResult.Type.class); PrepareReportPartition unitPartition = partition(findUnreportedUnits, p.getContractor()); lines.put(ACTIVE_INFO, unitPartition.getActiveInfo()); lines.put(REPORT_INFO, filterReportInfo(unitPartition.getReportAble())); lines.put(REPAYMENTS, filterRepayed(unitPartition.getReportAble())); switch (p.getViewMode()) { case DEFAULT: lines.put(INVOICED, filterInvoiced(unitPartition.getReportAble())); break; case YEARSPLITT_AND_WARRANTIES: YearSplit filterInvoicedSplit = filterInvoicedSplit(unitPartition.getReportAble(), p.getStart()); lines.put(PAST_ONE_YEAR, filterInvoicedSplit.getAfter()); lines.put(UNDER_ONE_YEAR, filterInvoicedSplit.getBefore()); PrepareReportPartition warrantyPartition = partition(filterWarrenty( reportLineEao.findUnreportedWarrentys(), unitPartition.getReportAble()), p.getContractor()); lines.put(WARRENTY, filterInvoiced(warrantyPartition.getReportAble())); lines.get(ACTIVE_INFO).addAll(warrantyPartition.getActiveInfo()); lines.get(REPAYMENTS).addAll(filterRepayed(warrantyPartition.getReportAble())); lines.get(REPORT_INFO).addAll(filterReportInfo(warrantyPartition.getReportAble())); break; } ViewReportResult viewReportResult = new ViewReportResult(lines, p); viewReportResult.getAllLines().stream().forEach((allLine) -> reportEm.detach(allLine)); if ( !marginCalculator.isUnsatisfied() ) marginCalculator.get().recalc(viewReportResult); return viewReportResult; } }