/*
* 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);
}
}
}