/* * This program is part of the OpenLMIS logistics management information system platform software. * Copyright © 2013 VillageReach * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  */ package org.openlmis.web.view.pdf.requisition; import com.itextpdf.text.*; import com.itextpdf.text.pdf.PdfPCell; import com.itextpdf.text.pdf.PdfPTable; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.collections.Predicate; import org.apache.commons.lang.ArrayUtils; import org.openlmis.core.domain.Facility; import org.openlmis.core.domain.GeographicZone; import org.openlmis.core.domain.Money; import org.openlmis.core.service.ConfigurationSettingService; import org.openlmis.core.service.MessageService; import org.openlmis.rnr.domain.*; import org.openlmis.web.model.PrintRnrLineItem; import java.io.IOException; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import static org.apache.commons.collections.CollectionUtils.find; import static org.openlmis.rnr.domain.RnrStatus.AUTHORIZED; import static org.openlmis.rnr.domain.RnrStatus.SUBMITTED; import static org.openlmis.web.controller.RequisitionController.*; import static org.openlmis.web.view.pdf.requisition.RequisitionCellFactory.*; /** * This entity is used to encapsulate rnr attributes, report formatting attributes to be used in requisition report. */ @Data @NoArgsConstructor public class RequisitionPdfModel { public static final String LABEL_CURRENCY_SYMBOL = "label.currency.symbol"; public static final float PARAGRAPH_SPACING = 30.0f; public static final BaseColor ROW_GREY_BACKGROUND = new BaseColor(235, 235, 235); public static final Font H1_FONT = FontFactory.getFont(FontFactory.TIMES, 30, Font.BOLD, BaseColor.BLACK); public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy"); public static final int TABLE_SPACING = 25; private List<RequisitionStatusChange> statusChanges; private List<? extends Column> rnrColumnList; private List<? extends Column> regimenColumnList; private Integer numberOfMonths; private Rnr requisition; private List<LossesAndAdjustmentsType> lossesAndAdjustmentsTypes; private MessageService messageService; private ConfigurationSettingService configService; public RequisitionPdfModel(Map<String, Object> model, MessageService messageService, ConfigurationSettingService configService) { this.statusChanges = (List<RequisitionStatusChange>) model.get(STATUS_CHANGES); this.rnrColumnList = (List<RnrColumn>) model.get(RNR_TEMPLATE); this.regimenColumnList = (List<RegimenColumn>) model.get(REGIMEN_TEMPLATE); this.requisition = (Rnr) model.get(RNR); this.lossesAndAdjustmentsTypes = (List<LossesAndAdjustmentsType>) model.get(LOSSES_AND_ADJUSTMENT_TYPES); this.numberOfMonths = (Integer) model.get(NUMBER_OF_MONTHS); this.messageService = messageService; this.configService = configService; } public Paragraph getFullSupplyHeader() { return new Paragraph(messageService.message("label.full.supply.products"), H2_FONT); } public Paragraph getNonFullSupplyHeader() { return new Paragraph(messageService.message("label.non.full.supply.products"), H2_FONT); } public PdfPTable getFullSupplyTable() throws DocumentException, NoSuchFieldException, IllegalAccessException, IOException { return getTableFor(requisition.getFullSupplyLineItems(), true, rnrColumnList); } public PdfPTable getRegimenTable() throws DocumentException, NoSuchFieldException, IllegalAccessException, IOException { List<RegimenLineItem> regimenLineItems = requisition.getRegimenLineItems(); if (regimenLineItems.size() == 0) return null; return getTableFor(regimenLineItems, null, regimenColumnList); } private PdfPTable getTableFor(List<? extends LineItem> lineItems, Boolean fullSupply, List<? extends Column> columnList) throws DocumentException, NoSuchFieldException, IllegalAccessException, IOException { Template template = Template.getInstance(columnList); List<? extends Column> visibleColumns = template.getPrintableColumns(fullSupply); PdfPTable table = prepareTable(visibleColumns); boolean odd = true; LineItem previousLineItem = null; for (LineItem lineItem : lineItems) { if (previousLineItem == null || !lineItem.compareCategory(previousLineItem)) { table.addCell(categoryRow(visibleColumns.size(), lineItem)); previousLineItem = lineItem; } if (lineItem.isRnrLineItem()) { PrintRnrLineItem printRnrLineItem = new PrintRnrLineItem((RnrLineItem) lineItem); printRnrLineItem.calculate(rnrColumnList, lossesAndAdjustmentsTypes, numberOfMonths, requisition.getStatus()); } String currencySymbol = messageService.message(LABEL_CURRENCY_SYMBOL); if(!configService.getBoolValue("RNR_PRINT_REPEAT_CURRENCY_SYMBOL")){ currencySymbol = ""; } List<PdfPCell> cells = getCells(visibleColumns, lineItem, currencySymbol); odd = !odd; for (PdfPCell cell : cells) { cell.setBackgroundColor(odd ? BaseColor.WHITE : ROW_GREY_BACKGROUND); table.addCell(cell); } } return table; } private void setTableHeader(PdfPTable table, List<? extends Column> visibleColumns) { for (Column column : visibleColumns) { table.addCell(column.getLabel()); } } private PdfPTable prepareTable(List<? extends Column> visibleColumns) throws DocumentException { java.util.List<Integer> widths = new ArrayList<>(); for (Column column : visibleColumns) { widths.add(column.getColumnWidth()); } PdfPTable table = new PdfPTable(widths.size()); table.setWidths(ArrayUtils.toPrimitive(widths.toArray(new Integer[widths.size()]))); table.getDefaultCell().setBackgroundColor(HEADER_BACKGROUND); table.getDefaultCell().setPadding(CELL_PADDING); table.setWidthPercentage(WIDTH_PERCENTAGE); table.setSpacingBefore(TABLE_SPACING); table.setHeaderRows(2); table.setFooterRows(1); setTableHeader(table, visibleColumns); setBlankFooter(table, visibleColumns.size()); return table; } private void setBlankFooter(PdfPTable table, Integer visibleColumnsSize) { PdfPCell cell = new PdfPCell(new Phrase(" ")); cell.setBorder(0); cell.setColspan(visibleColumnsSize); cell.setBackgroundColor(BaseColor.WHITE); table.addCell(cell); } public PdfPTable getNonFullSupplyTable() throws DocumentException, NoSuchFieldException, IllegalAccessException, IOException { List<RnrLineItem> nonFullSupplyLineItems = requisition.getNonFullSupplyLineItems(); Collections.sort(nonFullSupplyLineItems, new LineItemComparator()); if (nonFullSupplyLineItems.size() == 0) return null; return getTableFor(nonFullSupplyLineItems, false, rnrColumnList); } public PdfPTable getRequisitionHeader() throws DocumentException { PdfPTable table = prepareRequisitionHeaderTable(); addHeading(table); Facility facility = requisition.getFacility(); addFirstLine(facility, table); addSecondLine(facility, table, requisition.isEmergency()); table.setSpacingAfter(PARAGRAPH_SPACING); return table; } private void addHeading(PdfPTable table) throws DocumentException { Chunk chunk = new Chunk(String.format(messageService.message("label.requisition") + ": %s (%s)", this.requisition.getProgram().getName(), this.requisition.getFacility().getFacilityType().getName()), H1_FONT); PdfPCell cell = new PdfPCell(new Phrase(chunk)); cell.setColspan(5); cell.setPadding(10); cell.setBorder(0); table.addCell(cell); } private void addFirstLine(Facility facility, PdfPTable table) { String text = String.format(messageService.message("label.facility") + ": %s", facility.getName()); insertCell(table, text, 1); text = String.format(messageService.message("create.facility.operatedBy") + ": %s", facility.getOperatedBy().getText()); insertCell(table, text, 1); text = String.format(messageService.message("label.facility.maximumStock") + ": %s", facility.getFacilityType().getNominalMaxMonth()); insertCell(table, text, 1); text = String.format(messageService.message("label.emergency.order.point") + ": %s", facility.getFacilityType().getNominalEop()); insertCell(table, text, 1); insertCell(table, "", 1); } private void insertCell(PdfPTable table, String text, int colSpan) { Chunk chunk; chunk = new Chunk(text); PdfPCell cell = table.getDefaultCell(); cell.setPhrase(new Phrase(chunk)); cell.setColspan(colSpan); table.addCell(cell); } private void addSecondLine(Facility facility, PdfPTable table, Boolean emergency) { GeographicZone geographicZone = facility.getGeographicZone(); GeographicZone parent = geographicZone.getParent(); String text = String.format(messageService.message("header.facility.code") + ": %s", facility.getCode()); insertCell(table,text, 1); StringBuilder builder = new StringBuilder(); builder.append(geographicZone.getLevel().getName()).append(": ").append(geographicZone.getName()); insertCell(table, builder.toString(), 1); builder = new StringBuilder(); builder.append(parent.getLevel().getName()).append(": ").append(parent.getName()); insertCell(table, builder.toString(), 1); builder = new StringBuilder(); builder.append(messageService.message("label.facility.reportingPeriod")).append(": ").append(DATE_FORMAT.format( requisition.getPeriod().getStartDate())).append(" - ").append(DATE_FORMAT.format(requisition.getPeriod().getEndDate())); insertCell(table, builder.toString(), 1); String label = emergency ? "requisition.type.emergency" : "requisition.type.regular"; builder = new StringBuilder(); builder.append(messageService.message("label.requisition.type")).append(": ").append(messageService.message(label)); insertCell(table, builder.toString(), 1); } private PdfPTable prepareRequisitionHeaderTable() throws DocumentException { int[] columnWidths = {160, 160, 160, 160, 160}; PdfPTable table = new PdfPTable(columnWidths.length); table.setWidths(columnWidths); table.getDefaultCell().setBackgroundColor(HEADER_BACKGROUND); table.getDefaultCell().setPadding(10); table.getDefaultCell().setBorder(0); table.setWidthPercentage(WIDTH_PERCENTAGE); table.setSpacingBefore(TABLE_SPACING); table.setHeaderRows(1); return table; } public PdfPTable getSummary() throws DocumentException { this.requisition.fillFullSupplyCost(); this.requisition.fillNonFullSupplyCost(); DecimalFormat formatter = new DecimalFormat("#,##0.00"); PdfPTable summaryTable = new PdfPTable(2); summaryTable.setWidths(new int[]{30, 20}); summaryTable.setSpacingBefore(TABLE_SPACING); summaryTable.setWidthPercentage(40); summaryTable.setHorizontalAlignment(0); PdfPCell summaryHeaderCell = headingCell(messageService.message("label.summary")); summaryHeaderCell.setColspan(2); summaryHeaderCell.setPadding(10); summaryHeaderCell.setBorder(0); summaryTable.addCell(summaryHeaderCell); boolean showBudget = !requisition.isEmergency() && requisition.getProgram().getBudgetingApplies(); if (showBudget) { summaryTable.addCell(summaryCell(textCell(messageService.message("label.allocated.budget")))); PdfPCell allocatedBudgetCell = requisition.getAllocatedBudget() != null ? numberCell(messageService.message(LABEL_CURRENCY_SYMBOL) + formatter.format( new Money(requisition.getAllocatedBudget()).toDecimal())) : numberCell(messageService.message("msg.budget.not.allocated")); summaryTable.addCell(summaryCell(allocatedBudgetCell)); } summaryTable.addCell(summaryCell(textCell(messageService.message("label.total.cost.full.supply.items")))); summaryTable.addCell(summaryCell(numberCell(messageService.message(LABEL_CURRENCY_SYMBOL) + formatter.format(requisition.getFullSupplyItemsSubmittedCost().toDecimal()))) ); summaryTable.addCell(summaryCell(textCell(messageService.message("label.total.cost.non.full.supply.items")))); summaryTable.addCell(summaryCell(numberCell(messageService.message(LABEL_CURRENCY_SYMBOL) + formatter.format(requisition.getNonFullSupplyItemsSubmittedCost().toDecimal())))); summaryTable.addCell(summaryCell(textCell(messageService.message("label.total.cost")))); summaryTable.addCell(summaryCell(numberCell(messageService.message(LABEL_CURRENCY_SYMBOL) + formatter.format(this.getTotalCost( requisition).toDecimal()).toString()))); if (showBudget && requisition.getAllocatedBudget() != null && (requisition.getAllocatedBudget().compareTo(this.getTotalCost(requisition).getValue()) == -1)) { summaryTable.addCell(summaryCell(textCell(messageService.message("msg.cost.exceeds.budget")))); summaryTable.addCell(summaryCell(textCell(" "))); } summaryTable.addCell(summaryCell(textCell(" "))); summaryTable.addCell(summaryCell(textCell(" "))); summaryTable.addCell(summaryCell(textCell(" "))); summaryTable.addCell(summaryCell(textCell(" "))); fillAuditFields(summaryTable); return summaryTable; } private void fillAuditFields(PdfPTable summaryTable) { RequisitionStatusChange submittedStatusChange = getStatusChangeFor(SUBMITTED); RequisitionStatusChange authorizedStatusChange = getStatusChangeFor(AUTHORIZED); String submittedDate = submittedStatusChange != null ? DATE_FORMAT.format(submittedStatusChange.getCreatedDate()) : ""; String submittedBy = submittedStatusChange != null ? submittedStatusChange.getCreatedBy().getFirstName() + " " + submittedStatusChange.getCreatedBy().getLastName() : ""; String authorizedDate = authorizedStatusChange != null ? DATE_FORMAT.format(authorizedStatusChange.getCreatedDate()) : ""; String authorizedBy = authorizedStatusChange != null ? authorizedStatusChange.getCreatedBy().getFirstName() + " " + authorizedStatusChange.getCreatedBy().getLastName() : ""; summaryTable.addCell(summaryCell(textCell(messageService.message("label.submitted.by") + ": " + submittedBy))); summaryTable.addCell(summaryCell(textCell(messageService.message("label.date") + ": " + submittedDate))); summaryTable.addCell(summaryCell(textCell(" "))); summaryTable.addCell(summaryCell(textCell(" "))); summaryTable.addCell(summaryCell(textCell(messageService.message("label.authorized.by") + ": " + authorizedBy))); summaryTable.addCell(summaryCell(textCell(messageService.message("label.date") + ": " + authorizedDate))); } private RequisitionStatusChange getStatusChangeFor(final RnrStatus status) { return (RequisitionStatusChange) find(statusChanges, new Predicate() { @Override public boolean evaluate(Object o) { return ((RequisitionStatusChange) o).getStatus().equals(status); } }); } private PdfPCell summaryCell(PdfPCell cell) { cell.setPadding(15); cell.setBorder(0); return cell; } public Money getTotalCost(Rnr requisition) { return new Money(requisition.getFullSupplyItemsSubmittedCost().getValue().add(requisition.getNonFullSupplyItemsSubmittedCost().getValue())); } public Paragraph getRegimenHeader() { return new Paragraph(messageService.message("label.regimens"), H2_FONT); } }