/* * 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.domain; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.apache.commons.collections.Predicate; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.openlmis.core.domain.*; import org.openlmis.core.exception.DataException; import javax.persistence.Transient; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; import static org.apache.commons.collections.CollectionUtils.find; import static org.apache.commons.collections.CollectionUtils.selectRejected; import static com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion.NON_NULL; import static org.openlmis.rnr.domain.ProgramRnrTemplate.BEGINNING_BALANCE; import static org.openlmis.rnr.domain.RnrStatus.*; /** * This class represents a Requisition and Reporting entity and contains rnrLineItems, regimenLineItems, status, period, program * and facility associated with that requisition and report(Rnr). */ @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = NON_NULL) @EqualsAndHashCode(callSuper = false) public class Rnr extends BaseModel { public static final String RNR_VALIDATION_ERROR = "error.rnr.validation"; private boolean emergency; private Facility facility; private Program program; private ProcessingPeriod period; private RnrStatus status; private Money fullSupplyItemsSubmittedCost = new Money("0"); private Money nonFullSupplyItemsSubmittedCost = new Money("0"); private List<RnrLineItem> fullSupplyLineItems = new ArrayList<>(); private List<RnrLineItem> nonFullSupplyLineItems = new ArrayList<>(); private List<RegimenLineItem> regimenLineItems = new ArrayList<>(); private List<EquipmentLineItem> equipmentLineItems = new ArrayList<>(); private List<PatientQuantificationLineItem> patientQuantifications = new ArrayList<>(); private BigDecimal allocatedBudget; @Transient @JsonIgnore private List<RnrLineItem> allLineItems = new ArrayList<>(); private Facility supplyingDepot; private Long supplyingDepotId; private Long supervisoryNodeId; private Date submittedDate; private Date clientSubmittedTime; private String clientSubmittedNotes; private String sourceApplication = "WEB_UI"; private List<Comment> comments = new ArrayList<>(); private List<Signature> rnrSignatures; public Rnr(Facility facility, Program program, ProcessingPeriod period, Boolean emergency, Long modifiedBy, Long createdBy) { this.facility = facility; this.program = program; this.period = period; this.emergency = emergency; this.modifiedBy = modifiedBy; this.createdBy = createdBy; } public Rnr(Facility facility, Program program, ProcessingPeriod period, Boolean emergency, List<FacilityTypeApprovedProduct> facilityTypeApprovedProducts, List<Regimen> regimens, Long modifiedBy) { this(facility, program, period, emergency, modifiedBy, modifiedBy); fillLineItems(facilityTypeApprovedProducts); fillActiveRegimenLineItems(regimens); } public Rnr(Facility facility, Program program, ProcessingPeriod period) { this.facility = facility; this.program = program; this.period = period; } public Rnr(Long id) { this.id = id; } private void fillActiveRegimenLineItems(List<Regimen> regimens) { for (Regimen regimen : regimens) { if (regimen.getActive()) { RegimenLineItem regimenLineItem = new RegimenLineItem(regimen.getId(), regimen.getCategory(), createdBy, modifiedBy); regimenLineItem.setCode(regimen.getCode()); regimenLineItem.setName(regimen.getName()); regimenLineItem.setRegimenDisplayOrder(regimen.getDisplayOrder()); regimenLineItems.add(regimenLineItem); } } } public void add(RnrLineItem rnrLineItem, Boolean fullSupply) { if (fullSupply) { fullSupplyLineItems.add(rnrLineItem); } else { nonFullSupplyLineItems.add(rnrLineItem); } } public void calculateForApproval() { for (RnrLineItem lineItem : fullSupplyLineItems) { lineItem.calculatePacksToShip(); } for (RnrLineItem lineItem : nonFullSupplyLineItems) { lineItem.calculatePacksToShip(); } this.fullSupplyItemsSubmittedCost = calculateCost(fullSupplyLineItems); this.nonFullSupplyItemsSubmittedCost = calculateCost(nonFullSupplyLineItems); } private Money calculateCost(List<RnrLineItem> lineItems) { Money totalFullSupplyCost = new Money("0"); for (RnrLineItem lineItem : lineItems) { totalFullSupplyCost = totalFullSupplyCost.add(lineItem.calculateCost()); } return totalFullSupplyCost; } public void fillLineItems(List<FacilityTypeApprovedProduct> facilityTypeApprovedProducts) { for (FacilityTypeApprovedProduct facilityTypeApprovedProduct : facilityTypeApprovedProducts) { RnrLineItem requisitionLineItem = new RnrLineItem(null, facilityTypeApprovedProduct, modifiedBy, createdBy); add(requisitionLineItem, true); } } private void setBeginningBalances(Rnr previousRequisition, boolean beginningBalanceVisible) { if (previousRequisition == null || previousRequisition.status == INITIATED || previousRequisition.status == SUBMITTED) { if (!beginningBalanceVisible) { resetBeginningBalances(); } return; } for (RnrLineItem currentLineItem : this.fullSupplyLineItems) { RnrLineItem previousLineItem = previousRequisition.findCorrespondingLineItem(currentLineItem); currentLineItem.setBeginningBalanceWhenPreviousStockInHandAvailable(previousLineItem, beginningBalanceVisible); currentLineItem.setSkippedValueWhenPreviousLineItemIsAvailable(previousLineItem); } } private void resetBeginningBalances() { for (RnrLineItem lineItem : fullSupplyLineItems) { lineItem.setBeginningBalance(0); } } public void setFieldsForApproval() { for (RnrLineItem item : fullSupplyLineItems) { item.setFieldsForApproval(); } for (RnrLineItem item : nonFullSupplyLineItems) { item.setFieldsForApproval(); } } public List<RnrLineItem> getAllLineItems() { if (this.allLineItems.isEmpty()) { this.allLineItems.addAll(this.getFullSupplyLineItems()); this.allLineItems.addAll(this.getNonFullSupplyLineItems()); } return allLineItems; } public void fillBasicInformation(Facility facility, Program program, ProcessingPeriod period) { this.program = program; this.period = period; this.facility = facility.basicInformation(); } public RnrLineItem findCorrespondingLineItem(final RnrLineItem item) { return (RnrLineItem) find(this.getAllLineItems(), new Predicate() { @Override public boolean evaluate(Object o) { RnrLineItem lineItem = (RnrLineItem) o; return lineItem.getProductCode().equalsIgnoreCase(item.getProductCode()); } }); } public RegimenLineItem findCorrespondingRegimenLineItem(final RegimenLineItem regimenLineItem) { return (RegimenLineItem) find(this.regimenLineItems, new Predicate() { @Override public boolean evaluate(Object o) { RegimenLineItem regimenLineItem1 = (RegimenLineItem) o; return regimenLineItem1.getCode().equalsIgnoreCase(regimenLineItem.getCode()); } }); } public void setFieldsAccordingToTemplateFrom(Rnr previousRequisition, ProgramRnrTemplate template, RegimenTemplate regimenTemplate) { this.setBeginningBalances(previousRequisition, template.columnsVisible(BEGINNING_BALANCE)); for (RnrLineItem lineItem : this.fullSupplyLineItems) { lineItem.setLineItemFieldsAccordingToTemplate(template); } if (regimenTemplate.getColumns().isEmpty()) return; for (RegimenLineItem regimenLineItem : this.regimenLineItems) { regimenLineItem.setRegimenFieldsAccordingToTemplate(regimenTemplate); } } public void convertToOrder(Long userId) { this.status = RELEASED; this.modifiedBy = userId; } public void fillFullSupplyCost() { this.fullSupplyItemsSubmittedCost = calculateCost(this.fullSupplyLineItems); } public void fillNonFullSupplyCost() { this.nonFullSupplyItemsSubmittedCost = calculateCost(this.nonFullSupplyLineItems); } public void validateForApproval() { validateLineItemsForApproval(fullSupplyLineItems); validateLineItemsForApproval(nonFullSupplyLineItems); } private void validateLineItemsForApproval(List<RnrLineItem> lineItems) { for (RnrLineItem lineItem : lineItems) { lineItem.validateForApproval(); } } public void copyCreatorEditableFields(Rnr rnr, ProgramRnrTemplate rnrTemplate, RegimenTemplate regimenTemplate, List<ProgramProduct> programProducts) { this.modifiedBy = rnr.getModifiedBy(); copyCreatorEditableFieldsForFullSupply(rnr, rnrTemplate); copyCreatorEditableFieldsForNonFullSupply(rnr, rnrTemplate, programProducts); copyCreatorEditableFieldsForRegimen(rnr, regimenTemplate); } private void copyCreatorEditableFieldsForRegimen(Rnr rnr, RegimenTemplate regimenTemplate) { for (RegimenLineItem regimenLineItem : rnr.regimenLineItems) { RegimenLineItem savedRegimenLineItem = this.findCorrespondingRegimenLineItem(regimenLineItem); if (savedRegimenLineItem != null) { savedRegimenLineItem.copyCreatorEditableFieldsForRegimen(regimenLineItem, regimenTemplate); savedRegimenLineItem.setModifiedBy(rnr.getModifiedBy()); } } } private void copyCreatorEditableFieldsForNonFullSupply(Rnr rnr, ProgramRnrTemplate template, List<ProgramProduct> programProducts) { this.allLineItems.clear(); for (final RnrLineItem lineItem : rnr.nonFullSupplyLineItems) { RnrLineItem savedLineItem = this.findCorrespondingLineItem(lineItem); if (savedLineItem == null) { ProgramProduct programProduct = (ProgramProduct) find(programProducts, new Predicate() { @Override public boolean evaluate(Object o) { ProgramProduct programProduct = (ProgramProduct) o; return programProduct.getProduct().getCode().equalsIgnoreCase(lineItem.getProductCode()); } }); if (programProduct != null) { lineItem.setModifiedBy(rnr.getModifiedBy()); this.nonFullSupplyLineItems.add(lineItem); } } else { savedLineItem.setModifiedBy(rnr.getModifiedBy()); savedLineItem.copyCreatorEditableFieldsForNonFullSupply(lineItem, template); } } } private void copyCreatorEditableFieldsForFullSupply(Rnr rnr, ProgramRnrTemplate template) { for (RnrLineItem lineItem : rnr.fullSupplyLineItems) { RnrLineItem savedLineItem = this.findCorrespondingLineItem(lineItem); if (savedLineItem == null) throw new DataException("product.code.invalid"); savedLineItem.copyCreatorEditableFieldsForFullSupply(lineItem, template); savedLineItem.setModifiedBy(rnr.getModifiedBy()); } } public void copyApproverEditableFields(Rnr rnr, ProgramRnrTemplate template) { this.modifiedBy = rnr.modifiedBy; copyApproverEditableFieldsToLineItems(rnr, template, rnr.fullSupplyLineItems); copyApproverEditableFieldsToLineItems(rnr, template, rnr.nonFullSupplyLineItems); } private void copyApproverEditableFieldsToLineItems(Rnr rnr, ProgramRnrTemplate template, List<RnrLineItem> lineItems) { for (RnrLineItem lineItem : lineItems) { RnrLineItem savedLineItem = this.findCorrespondingLineItem(lineItem); if (savedLineItem == null) throw new DataException("product.code.invalid"); savedLineItem.setModifiedBy(rnr.modifiedBy); savedLineItem.copyApproverEditableFields(lineItem, template); } } public void setAuditFieldsForRequisition(Long modifiedBy, RnrStatus status) { this.status = status; this.modifiedBy = modifiedBy; } public void prepareForFinalApproval() { this.status = APPROVED; } public void approveAndAssignToNextSupervisoryNode(SupervisoryNode parent) { status = IN_APPROVAL; supervisoryNodeId = parent.getId(); } public boolean isApprovable() { return status == AUTHORIZED || status == IN_APPROVAL; } public boolean preAuthorize() { return status == INITIATED || status == SUBMITTED; } public void addToNonFullSupplyCost(Money cost) { this.nonFullSupplyItemsSubmittedCost = this.nonFullSupplyItemsSubmittedCost.add(cost); } public void addToFullSupplyCost(Money cost) { this.fullSupplyItemsSubmittedCost = this.fullSupplyItemsSubmittedCost.add(cost); } public boolean isForVirtualFacility() { return this.facility.getVirtualFacility(); } public List<RnrLineItem> getNonSkippedLineItems() { return (List<RnrLineItem>) selectRejected(this.fullSupplyLineItems, new Predicate() { @Override public boolean evaluate(Object o) { RnrLineItem rnrLineItem = (RnrLineItem) o; return rnrLineItem.getSkipped(); } }); } public void validateRegimenLineItems(RegimenTemplate regimenTemplate) { for (RegimenLineItem regimenLineItem : this.regimenLineItems) { try { if(!regimenLineItem.getSkipped()) { regimenLineItem.validate(regimenTemplate); } } catch (NoSuchFieldException | IllegalAccessException e) { throw new DataException(RNR_VALIDATION_ERROR); } } } @JsonIgnore public boolean isBudgetingApplicable() { return !this.emergency && this.program.getBudgetingApplies(); } }