/* * 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.rnr.service; import org.apache.commons.collections.Transformer; import org.openlmis.core.domain.Money; import org.openlmis.core.domain.ProcessingPeriod; import org.openlmis.core.service.ProcessingScheduleService; import org.openlmis.rnr.domain.*; import org.openlmis.rnr.repository.RequisitionRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.apache.commons.collections.CollectionUtils.collect; /** * Exposes the services for calculating reference data for rnr and rnrLineItem. */ @Service public class CalculationService { public static final int MILLI_SECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000; public static final int MAX_NUMBER_OF_PERIODS_TO_TRACK = 5; @Autowired RequisitionRepository requisitionRepository; @Autowired ProcessingScheduleService processingScheduleService; public void perform(Rnr requisition, ProgramRnrTemplate template) { requisition.setFullSupplyItemsSubmittedCost(new Money("0")); requisition.setNonFullSupplyItemsSubmittedCost(new Money("0")); calculateForFullSupply(requisition, template); calculateForNonFullSupply(requisition); } public void fillReportingDays(Rnr requisition) { Date startDate = requisition.getPeriod().getStartDate(); Integer numberOfMonths = requisition.getPeriod().getNumberOfMonths(); List<ProcessingPeriod> twoPreviousPeriods = processingScheduleService.getNPreviousPeriodsInDescOrder(requisition.getPeriod(), 2); if (twoPreviousPeriods.size() != 0) { numberOfMonths = twoPreviousPeriods.get(0).getNumberOfMonths(); startDate = (numberOfMonths < 3 && twoPreviousPeriods.size() != 1) ? twoPreviousPeriods.get(1).getStartDate() : twoPreviousPeriods.get(0).getStartDate(); } for (RnrLineItem lineItem : requisition.getNonSkippedLineItems()) { Integer reportingDays = getReportingDaysBasedOnRequisition(requisition, lineItem.getProductCode(), startDate, numberOfMonths); lineItem.setReportingDays(reportingDays); } } public void fillFieldsForInitiatedRequisition(Rnr requisition, ProgramRnrTemplate rnrTemplate, RegimenTemplate regimenTemplate) { List<ProcessingPeriod> fivePreviousPeriods = processingScheduleService.getNPreviousPeriodsInDescOrder(requisition.getPeriod(), MAX_NUMBER_OF_PERIODS_TO_TRACK); Rnr previousRequisition = requisitionRepository .getLastRegularRequisitionToEnterThePostSubmitFlow(requisition.getFacility().getId(), requisition.getProgram().getId()); if (fivePreviousPeriods.size() == 0 || previousRequisition == null) { requisition.setFieldsAccordingToTemplateFrom(null, rnrTemplate, regimenTemplate); fillPreviousNCsInLineItems(requisition, requisition.getPeriod().getNumberOfMonths(), requisition.getPeriod().getStartDate()); return; } previousRequisition = requisitionRepository.getById(previousRequisition.getId()); requisition.setFieldsAccordingToTemplateFrom(previousRequisition, rnrTemplate, regimenTemplate); Integer numberOfMonths = fivePreviousPeriods.get(0).getNumberOfMonths(); Date trackingDate = fivePreviousPeriods.get(0).getStartDate(); int lastPeriodIndex = MAX_NUMBER_OF_PERIODS_TO_TRACK - 1; int secondPeriodIndex = 1; if (numberOfMonths == 1) { trackingDate = getStartDateForNthPreviousPeriod(fivePreviousPeriods, lastPeriodIndex); } else if (numberOfMonths == 2) { trackingDate = getStartDateForNthPreviousPeriod(fivePreviousPeriods, secondPeriodIndex); } fillPreviousNCsInLineItems(requisition, numberOfMonths, trackingDate); } public void copySkippedFieldFromPreviousPeriod(Rnr requisition) { List<ProcessingPeriod> fivePreviousPeriods = processingScheduleService.getNPreviousPeriodsInDescOrder(requisition.getPeriod(), 5); Rnr previousRequisition = requisitionRepository .getLastRegularRequisitionToEnterThePostSubmitFlow(requisition.getFacility().getId(), requisition.getProgram().getId()); if (fivePreviousPeriods.size() == 0 || previousRequisition == null) { if (requisition.getProgram().getHideSkippedProducts()) { for (RnrLineItem lineItem : requisition.getFullSupplyLineItems()) { lineItem.setSkipped(true); } } return; } previousRequisition = requisitionRepository.getLWById(previousRequisition.getId()); Map map = new HashMap<String, RnrLineItem>(); if (previousRequisition != null) { for (RnrLineItem lineItem : previousRequisition.getFullSupplyLineItems()) { map.put(lineItem.getProductCode(), lineItem); } for (RnrLineItem lineItem : requisition.getFullSupplyLineItems()) { RnrLineItem previous = (RnrLineItem) map.get(lineItem.getProductCode()); if (previous != null) { lineItem.setSkipped(previous.getSkipped()); } else if (requisition.getProgram().getHideSkippedProducts()) { lineItem.setSkipped(true); } } } } private Integer getReportingDaysBasedOnRequisition(Rnr requisition, String lineItemProductCode, Date startDate, Integer numberOfMonths) { Integer reportingDays = numberOfMonths * 30; if (requisition.isForVirtualFacility()) { Date calculationDate = requisitionRepository.getAuthorizedDateForPreviousLineItem(requisition, lineItemProductCode, startDate); reportingDays = getDaysForNC(requisition.getCreatedDate(), calculationDate); } else if (requisition.isEmergency()) { reportingDays = getDaysForNC(requisition.getCreatedDate(), requisition.getPeriod().getStartDate()); } return reportingDays; } private Integer getDaysForNC(Date requisitionCreatedDate, Date calculationDate) { if (calculationDate != null) { return (int) ((requisitionCreatedDate.getTime() - calculationDate.getTime()) / MILLI_SECONDS_IN_ONE_DAY); } return null; } private void fillPreviousNCsInLineItems(Rnr requisition, Integer numberOfMonths, Date trackingDate) { if (numberOfMonths >= 3 && !(requisition.isEmergency() || requisition.isForVirtualFacility())) { return; } for (RnrLineItem lineItem : requisition.getFullSupplyLineItems()) { List<RnrLineItem> previousLineItems = requisitionRepository.getAuthorizedRegularUnSkippedLineItems(lineItem.getProductCode(), requisition, getNumberOfPreviousNCToTrack(numberOfMonths), trackingDate); List<Integer> nNormalizedConsumptions = (List<Integer>) collect(previousLineItems, new Transformer() { @Override public Object transform(Object o) { return (o == null) ? 0 : ((RnrLineItem) o).getNormalizedConsumption(); } }); lineItem.setPreviousNormalizedConsumptions(nNormalizedConsumptions); } } private Integer getNumberOfPreviousNCToTrack(Integer m) { return (m == 1) ? 2 : 1; } private Date getStartDateForNthPreviousPeriod(List<ProcessingPeriod> fivePreviousPeriods, Integer index) { Integer numberOfPeriods = fivePreviousPeriods.size(); return numberOfPeriods <= index ? fivePreviousPeriods.get(numberOfPeriods - 1).getStartDate() : fivePreviousPeriods.get(index).getStartDate(); } private void calculateForNonFullSupply(Rnr requisition) { for (RnrLineItem lineItem : requisition.getNonFullSupplyLineItems()) { lineItem.validateNonFullSupply(); lineItem.calculatePacksToShip(); requisition.addToNonFullSupplyCost(lineItem.calculateCost()); } } private void calculateForFullSupply(Rnr requisition, ProgramRnrTemplate template) { List<LossesAndAdjustmentsType> lossesAndAdjustmentsTypes = requisitionRepository.getLossesAndAdjustmentsTypes(); Integer numberOfMonths = processingScheduleService.findM(requisition.getPeriod()); for (RnrLineItem lineItem : requisition.getNonSkippedLineItems()) { lineItem.validateMandatoryFields(template); lineItem.calculateForFullSupply(template, requisition.getStatus(), lossesAndAdjustmentsTypes, numberOfMonths); lineItem.validateCalculatedFields(template); requisition.addToFullSupplyCost(lineItem.calculateCost()); } } public void skipAllLineItems(Rnr requisition) { for (RnrLineItem lineItem : requisition.getFullSupplyLineItems()) { lineItem.setSkipped(true); } for (RegimenLineItem lineItem : requisition.getRegimenLineItems()) { lineItem.setSkipped(true); } } }