/* * 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.restapi.service; import com.google.common.base.Function; import com.google.common.collect.FluentIterable; import lombok.NoArgsConstructor; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.Transformer; import org.apache.log4j.Logger; import org.openlmis.core.domain.*; import org.openlmis.core.exception.DataException; import org.openlmis.core.service.FacilityApprovedProductService; import org.openlmis.core.service.FacilityService; import org.openlmis.core.service.ProcessingPeriodService; import org.openlmis.core.service.ProgramService; import org.openlmis.order.service.OrderService; import org.openlmis.restapi.domain.ReplenishmentDTO; import org.openlmis.restapi.domain.Report; import org.openlmis.rnr.domain.*; import org.openlmis.rnr.search.criteria.RequisitionSearchCriteria; import org.openlmis.rnr.service.RequisitionService; import org.openlmis.rnr.service.RnrTemplateService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static org.apache.commons.collections.CollectionUtils.find; import static org.openlmis.restapi.domain.ReplenishmentDTO.prepareForREST; /** * This service exposes methods for creating, approving a requisition. */ @Service @NoArgsConstructor public class RestRequisitionService { public static final boolean EMERGENCY = false; private static final String SOURCE_APPLICATION_ELMIS_FE = "ELMIS_FE"; private static final String SOURCE_APPLICATION_OTHER = "OTHER"; private static final Logger logger = Logger.getLogger(RestRequisitionService.class); @Autowired private RequisitionService requisitionService; @Autowired private OrderService orderService; @Autowired private FacilityService facilityService; @Autowired private ProgramService programService; @Autowired private RnrTemplateService rnrTemplateService; @Autowired private RestRequisitionCalculator restRequisitionCalculator; @Autowired private ProcessingPeriodService processingPeriodService; @Autowired private FacilityApprovedProductService facilityApprovedProductService; private List<FacilityTypeApprovedProduct> fullSupplyFacilityTypeApprovedProducts; private List<FacilityTypeApprovedProduct> nonFullSupplyFacilityApprovedProducts; @Transactional public Rnr submitReport(Report report, Long userId) { report.validate(); Facility reportingFacility = facilityService.getOperativeFacilityByCode(report.getAgentCode()); Program reportingProgram = programService.getValidatedProgramByCode(report.getProgramCode()); restRequisitionCalculator.validatePeriod(reportingFacility, reportingProgram); Rnr rnr = requisitionService.initiate(reportingFacility, reportingProgram, userId, EMERGENCY, null, SOURCE_APPLICATION_OTHER); restRequisitionCalculator.validateProducts(report.getProducts(), rnr); markSkippedLineItems(rnr, report); if (reportingFacility.getVirtualFacility()) restRequisitionCalculator.setDefaultValues(rnr); copyRegimens(rnr, report); requisitionService.save(rnr); updateClientFields(report, rnr); insertPatientQuantificationLineItems(report, rnr); insertRnrSignatures(report, rnr, userId); rnr = requisitionService.submit(rnr); return requisitionService.authorize(rnr); } private void updateClientFields(Report report, Rnr rnr) { Date clientSubmittedTime = report.getClientSubmittedTime(); rnr.setClientSubmittedTime(clientSubmittedTime); String clientSubmittedNotes = report.getClientSubmittedNotes(); rnr.setClientSubmittedNotes(clientSubmittedNotes); requisitionService.updateClientFields(rnr); } @Transactional public Rnr submitSdpReport(Report report, Long userId) { report.validate(); Facility reportingFacility = facilityService.getOperativeFacilityByCode(report.getAgentCode()); Program reportingProgram = programService.getValidatedProgramByCode(report.getProgramCode()); ProcessingPeriod period = processingPeriodService.getById(report.getPeriodId()); Rnr rnr; List<Rnr> rnrs = null; RequisitionSearchCriteria searchCriteria = new RequisitionSearchCriteria(); searchCriteria.setProgramId(reportingProgram.getId()); searchCriteria.setFacilityId(reportingFacility.getId()); searchCriteria.setWithoutLineItems(true); searchCriteria.setUserId(userId); if(report.getPeriodId() != null) { //check if the requisition has already been initiated / submitted / authorized. restRequisitionCalculator.validateCustomPeriod(reportingFacility, reportingProgram, period, userId); rnrs = requisitionService.getRequisitionsFor(searchCriteria, asList(period)); } if(rnrs != null && rnrs.size() > 0){ rnr = requisitionService.getFullRequisitionById( rnrs.get(0).getId() ); }else{ rnr = requisitionService.initiate(reportingFacility, reportingProgram, userId, report.getEmergency(), period, SOURCE_APPLICATION_ELMIS_FE); } List<RnrLineItem> fullSupplyProducts = new ArrayList<>(); List<RnrLineItem> nonFullSupplyProducts = new ArrayList<>(); fullSupplyFacilityTypeApprovedProducts = facilityApprovedProductService.getFullSupplyFacilityApprovedProductByFacilityAndProgram( reportingFacility.getId(), reportingProgram.getId() ); nonFullSupplyFacilityApprovedProducts = facilityApprovedProductService.getNonFullSupplyFacilityApprovedProductByFacilityAndProgram( reportingFacility.getId(), reportingProgram.getId() ); Collection<String> fullSupplyProductCodes = ( Collection<String> ) CollectionUtils.collect(fullSupplyFacilityTypeApprovedProducts, input -> ((FacilityTypeApprovedProduct) input).getProgramProduct().getProduct().getCode()); Collection<String> nonFullSupplyProductCodes = ( Collection<String> ) CollectionUtils.collect(nonFullSupplyFacilityApprovedProducts, input -> ((FacilityTypeApprovedProduct) input).getProgramProduct().getProduct().getCode()); fullSupplyProducts = report.getProducts().stream() .filter(p-> fullSupplyProductCodes.contains(p.getProductCode()) ) .collect(Collectors.toList()); nonFullSupplyProducts = report.getProducts().stream() .filter(p-> nonFullSupplyProductCodes.contains(p.getProductCode()) ) .collect(Collectors.toList()); for(RnrLineItem li : nonFullSupplyProducts){ setNonFullSupplyCreatorFields(li); } report.setProducts(fullSupplyProducts); report.setNonFullSupplyProducts(nonFullSupplyProducts); restRequisitionCalculator.validateProducts(report.getProducts(), rnr); markSkippedLineItems(rnr, report); copyRegimens(rnr, report); // if you have come this far, then do it, it is your day. make the submission. // i cannot believe we do all of these three at the same time. // but then this is what zambia specifically asked. requisitionService.save(rnr); rnr = requisitionService.submit(rnr); return requisitionService.authorize(rnr); } private void setNonFullSupplyCreatorFields(final RnrLineItem lineItem) { FacilityTypeApprovedProduct facilityTypeApprovedProduct = (FacilityTypeApprovedProduct) find(nonFullSupplyFacilityApprovedProducts, new Predicate() { @Override public boolean evaluate(Object product) { return ((FacilityTypeApprovedProduct) product).getProgramProduct().getProduct().getCode().equals(lineItem.getProductCode()); } }); if(facilityTypeApprovedProduct == null){ return; } lineItem.populateFromProduct(facilityTypeApprovedProduct.getProgramProduct()); lineItem.setMaxMonthsOfStock(facilityTypeApprovedProduct.getMaxMonthsOfStock()); } private void copyRegimens(Rnr rnr, Report report) { if (report.getRegimens() != null) { for (RegimenLineItem regimenLineItem : report.getRegimens()) { RegimenLineItem correspondingRegimenLineItem = rnr.findCorrespondingRegimenLineItem(regimenLineItem); if (correspondingRegimenLineItem == null) throw new DataException("error.invalid.regimen"); correspondingRegimenLineItem.populate(regimenLineItem); } } } private void insertPatientQuantificationLineItems(Report report, Rnr rnr) { if (report.getPatientQuantifications() != null) { rnr.setPatientQuantifications(report.getPatientQuantifications()); requisitionService.insertPatientQuantificationLineItems(rnr); } } private void insertRnrSignatures(Report report, Rnr rnr, final Long userId) { if (report.getRnrSignatures() != null) { List<Signature> rnrSignatures = new ArrayList(CollectionUtils.collect(report.getRnrSignatures(), new Transformer() { @Override public Object transform(Object input) { ((Signature)input).setCreatedBy(userId); ((Signature)input).setModifiedBy(userId); return input; } })); rnr.setRnrSignatures(rnrSignatures); requisitionService.insertRnrSignatures(rnr); } } @Transactional public void approve(Report report, Long requisitionId, Long userId) { Rnr requisition = report.getRequisition(requisitionId, userId); Rnr savedRequisition = requisitionService.getFullRequisitionById(requisition.getId()); if (!savedRequisition.getFacility().getVirtualFacility()) { throw new DataException("error.approval.not.allowed"); } if (savedRequisition.getNonSkippedLineItems().size() != report.getProducts().size()) { throw new DataException("error.number.of.line.items.mismatch"); } restRequisitionCalculator.validateProducts(report.getProducts(), savedRequisition); requisitionService.save(requisition); requisitionService.approve(requisition, report.getApproverName()); } public ReplenishmentDTO getReplenishmentDetails(Long id) { Rnr requisition = requisitionService.getFullRequisitionById(id); return prepareForREST(requisition, orderService.getOrder(id)); } private void markSkippedLineItems(Rnr rnr, Report report) { ProgramRnrTemplate rnrTemplate = rnrTemplateService.fetchProgramTemplateForRequisition(rnr.getProgram().getId()); List<RnrLineItem> savedLineItems = rnr.getFullSupplyLineItems(); List<RnrLineItem> reportedProducts = report.getProducts(); for (final RnrLineItem savedLineItem : savedLineItems) { RnrLineItem reportedLineItem = (RnrLineItem) find(reportedProducts, new Predicate() { @Override public boolean evaluate(Object product) { return ((RnrLineItem) product).getProductCode().equals(savedLineItem.getProductCode()); } }); copyInto(savedLineItem, reportedLineItem, rnrTemplate); } savedLineItems = rnr.getNonFullSupplyLineItems(); reportedProducts = report.getNonFullSupplyProducts(); if(reportedProducts != null) { for (final RnrLineItem reportedLineItem : reportedProducts) { RnrLineItem savedLineItem = (RnrLineItem) find(savedLineItems, new Predicate() { @Override public boolean evaluate(Object product) { return ((RnrLineItem) product).getProductCode().equals(reportedLineItem.getProductCode()); } }); if (savedLineItem == null && reportedLineItem != null) { rnr.getNonFullSupplyLineItems().add(reportedLineItem); } else { copyInto(savedLineItem, reportedLineItem, rnrTemplate); } } } } private void copyInto(RnrLineItem savedLineItem, RnrLineItem reportedLineItem, ProgramRnrTemplate rnrTemplate) { if (reportedLineItem == null) { savedLineItem.setSkipped(true); return; } for (Column column : rnrTemplate.getColumns()) { if (!column.getVisible() || !rnrTemplate.columnsUserInput(column.getName())) continue; try { Field field = RnrLineItem.class.getDeclaredField(column.getName()); field.setAccessible(true); Object reportedValue = field.get(reportedLineItem); Object toBeSavedValue = (reportedValue != null ? reportedValue : field.get(savedLineItem)); field.set(savedLineItem, toBeSavedValue); } catch (Exception e) { logger.error("could not copy field: " + column.getName()); } } } public List<Report> getRequisitionsByFacility(String facilityCode) { Facility facility = facilityService.getFacilityByCode(facilityCode); if (facility == null) { throw new DataException("error.facility.unknown"); } List<Rnr> rnrList = requisitionService.getRequisitionsByFacility(facility); return FluentIterable.from(rnrList).transform(new Function<Rnr, Report>() { @Override public Report apply(Rnr input) { return Report.prepareForREST(input); } }).toList(); } }