diff --git a/marlo-data/src/main/java/org/cgiar/ccafs/marlo/data/model/CrpProgramOutcome.java b/marlo-data/src/main/java/org/cgiar/ccafs/marlo/data/model/CrpProgramOutcome.java index 73e21affe9..0a54f138fd 100644 --- a/marlo-data/src/main/java/org/cgiar/ccafs/marlo/data/model/CrpProgramOutcome.java +++ b/marlo-data/src/main/java/org/cgiar/ccafs/marlo/data/model/CrpProgramOutcome.java @@ -57,8 +57,10 @@ public class CrpProgramOutcome extends MarloAuditableEntity implements java.io.S private Integer year; @Expose private FileDB file; - - + @Expose + private Integer orderIndex; + @Expose + private Integer afPhase; @Expose private BigDecimal value; @@ -160,6 +162,11 @@ public String getAcronym() { } + public Integer getAfPhase() { + return afPhase; + } + + public String getComposedName() { if (this.getCrpProgram() != null && this.getCrpProgram().getAcronym() != null && description != null) { return this.getCrpProgram().getAcronym() + " Outcome: " + description; @@ -168,7 +175,6 @@ public String getComposedName() { } } - public String getComposeID() { if (composeID != null) { return composeID; @@ -192,7 +198,6 @@ public Set getCrpOutcomeSubIdos() { return this.crpOutcomeSubIdos; } - public CrpProgram getCrpProgram() { return this.crpProgram; } @@ -201,7 +206,6 @@ public Set getCrpProgramOutcomeIndicators() { return crpProgramOutcomeIndicators; } - public Set getDeliverables() { return deliverables; } @@ -237,6 +241,9 @@ public List getMilestones() { return milestones; } + public Integer getOrderIndex() { + return orderIndex; + } public String getPAcronym() { return this.getCrpProgram().getAcronym(); @@ -274,16 +281,18 @@ public void setAcronym(String acronym) { this.acronym = acronym; } + public void setAfPhase(Integer afPhase) { + this.afPhase = afPhase; + } + public void setComposeID(String composeID) { this.composeID = composeID; } - public void setCrpClusterKeyOutputOutcomes(Set crpClusterKeyOutputOutcomes) { this.crpClusterKeyOutputOutcomes = crpClusterKeyOutputOutcomes; } - public void setCrpMilestones(Set crpMilestones) { this.crpMilestones = crpMilestones; } @@ -328,6 +337,10 @@ public void setMilestones(List milestones) { this.milestones = milestones; } + public void setOrderIndex(Integer orderIndex) { + this.orderIndex = orderIndex; + } + public void setPhase(Phase phase) { this.phase = phase; } diff --git a/marlo-data/src/main/resources/xmls/CrpProgramOutcomes.hbm.xml b/marlo-data/src/main/resources/xmls/CrpProgramOutcomes.hbm.xml index c4302f1a36..a9a07ca735 100644 --- a/marlo-data/src/main/resources/xmls/CrpProgramOutcomes.hbm.xml +++ b/marlo-data/src/main/resources/xmls/CrpProgramOutcomes.hbm.xml @@ -53,6 +53,12 @@ + + + + + + diff --git a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/BaseAction.java b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/BaseAction.java index 6fb2ac1afc..8c8ec4ca53 100644 --- a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/BaseAction.java +++ b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/BaseAction.java @@ -480,6 +480,17 @@ public class BaseAction extends ActionSupport implements Preparable, SessionAwar private HashMap completedeliverableListbyPhase = new HashMap(); + // OICR validation variables + private boolean isOicrGeneralInformationComplete = false; + + private boolean isOicrAllianceAlignmentComplete = false; + + + private boolean isOicrOneCgiarAlignmentComplete = false; + + + private boolean isOicrCommunicationsComplete = false; + public BaseAction() { this.saveable = true; @@ -506,6 +517,7 @@ public void addActionError(String anErrorMessage) { super.addActionError(anErrorMessage); } + /** * This function add a flag (--warn--) to the message in order to give a * different style to the success message using javascript once the html is @@ -517,6 +529,7 @@ public void addActionWarning(String message) { this.addActionMessage("--warn--" + message); } + public void addMessage(String message) { if (!StringUtils.stripToEmpty(message).isEmpty()) { this.validationMessage.append("

- "); @@ -573,6 +586,7 @@ public void addUsers() { } + public boolean canAccessSuperAdmin() { return this.securityContext.hasAllPermissions(Permission.FULL_PRIVILEGES); } @@ -594,6 +608,7 @@ public boolean canAcessCenterImpactPathway() { return this.securityContext.hasPermission(permission); } + public boolean canAcessCrp() { return this.canAcessPublications() || this.canAcessSynthesisMog(); } @@ -655,6 +670,7 @@ public boolean canAcessSumaries() { } } + public boolean canAcessSynthesisMog() { String permission = this.generatePermission(Permission.SYNTHESIS_BY_MOG_PERMISSION, this.getCrpSession()); return this.securityContext.hasPermission(permission); @@ -1647,7 +1663,6 @@ public boolean centerCanBeDeleted(long id, String className) { } - /** * ***********************CENTER METHOD******************** Check if the * capDev section is Active @@ -1784,6 +1799,7 @@ public PowbSynthesis createPowbSynthesis(long phaseID, long liaisonInstitutionID return synthesis; } + /** * Create a liaison institution Annual Report Synthesis in this phase * @@ -3551,7 +3567,6 @@ public List getDeliverableTypesByRule(String rule) { return rules; } - public List getDifferences() { return this.differences; } @@ -3970,7 +3985,6 @@ public List getexpectedCrpOutcomes(Long id) { return expectedStudies; } - public List getExpectedStudiesYears(Long expectedStudy) { List allYears = new ArrayList<>(); try { @@ -4080,6 +4094,7 @@ public Boolean getHasLp6ContributionDeliverable(long deliverableID, long phaseID } } + /** * Get the folder path according if the user navigate in center,crp or * platform sections. @@ -4180,6 +4195,7 @@ public List getInnovationProjectOutcomes(Long id) { return innovations; } + public SectionStatus getInnovationStatus(long innovationID) { ProjectInnovation innovation = this.projectInnovationManager.getProjectInnovationById(innovationID); @@ -4937,447 +4953,453 @@ public List getProjectRelationsImpact(Long id, String className) { * Green checks in Projects Menu */ public boolean getProjectSectionStatus(String section, long projectID) { - boolean returnValue = false; - SectionStatus sectionStatus; - Project project; + try { - if (section != null && !section.isEmpty()) { - if (ProjectSectionStatusEnum.value(section.toUpperCase()) == null) { - return false; - } - switch (ProjectSectionStatusEnum.value(section.toUpperCase())) { - case OUTCOMES: - project = this.projectManager.getProjectById(projectID); + boolean returnValue = false; + SectionStatus sectionStatus; + Project project; + + if (section != null && !section.isEmpty()) { + if (ProjectSectionStatusEnum.value(section.toUpperCase()) == null) { + return false; + } + switch (ProjectSectionStatusEnum.value(section.toUpperCase())) { + case OUTCOMES: + project = this.projectManager.getProjectById(projectID); - // Validate LP6 Contribution question - if (this.hasSpecificities(APConstants.CRP_LP6_ACTIVE) && this.isReportingActive()) { + // Validate LP6 Contribution question + if (this.hasSpecificities(APConstants.CRP_LP6_ACTIVE) && this.isReportingActive()) { - List projectLp6Contributions = project.getProjectLp6Contributions().stream() - .filter(pl -> pl.isActive() && pl.getPhase().equals(this.getActualPhase())).collect(Collectors.toList()); - if (projectLp6Contributions != null && !projectLp6Contributions.isEmpty()) { - ProjectLp6Contribution projectLp6Contribution = projectLp6Contributions.get(0); - if (projectLp6Contribution.getContribution() == null) { + List projectLp6Contributions = project.getProjectLp6Contributions().stream() + .filter(pl -> pl.isActive() && pl.getPhase().equals(this.getActualPhase())) + .collect(Collectors.toList()); + if (projectLp6Contributions != null && !projectLp6Contributions.isEmpty()) { + ProjectLp6Contribution projectLp6Contribution = projectLp6Contributions.get(0); + if (projectLp6Contribution.getContribution() == null) { + return false; + } + } else { return false; } - } else { - return false; - } - - } - - List projectOutcomes = project.getProjectOutcomes().stream() - .filter(c -> c.isActive() && c.getPhase() != null && c.getPhase().equals(this.getActualPhase())) - .collect(Collectors.toList()); - project.setOutcomes(projectOutcomes); - - if (!(project.getProjecInfoPhase(this.getActualPhase()).getAdministrative() != null - && project.getProjecInfoPhase(this.getActualPhase()).getAdministrative().booleanValue() == true)) { - if (project.getOutcomes().isEmpty()) { - return false; } - } else { - return true; - } - for (ProjectOutcome projectOutcome : project.getOutcomes()) { - sectionStatus = this.sectionStatusManager.getSectionStatusByProjectOutcome(projectOutcome.getId(), - this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); - if (sectionStatus == null) { - return false; - } - if (sectionStatus.getMissingFields().length() != 0) { - return false; - } - } + List projectOutcomes = project.getProjectOutcomes().stream() + .filter(c -> c.isActive() && c.getPhase() != null && c.getPhase().equals(this.getActualPhase())) + .collect(Collectors.toList()); - returnValue = true; - break; + project.setOutcomes(projectOutcomes); - case CASESTUDIES: - project = this.projectManager.getProjectById(projectID); - List caseStudies = - project.getCaseStudyProjects().stream().filter(d -> d.isActive()).collect(Collectors.toList()); - List caStudies = new ArrayList<>(); + if (!(project.getProjecInfoPhase(this.getActualPhase()).getAdministrative() != null + && project.getProjecInfoPhase(this.getActualPhase()).getAdministrative().booleanValue() == true)) { + if (project.getOutcomes().isEmpty()) { + return false; + } + } else { + return true; + } - for (CaseStudyProject caseStudyProject : caseStudies) { - if (caseStudyProject.isActive() - && caseStudyProject.getCaseStudy().getYear() == this.getCurrentCycleYear()) { - caStudies.add(caseStudyProject.getCaseStudy()); - sectionStatus = - this.sectionStatusManager.getSectionStatusByCaseStudy(caseStudyProject.getCaseStudy().getId(), - this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); + for (ProjectOutcome projectOutcome : project.getOutcomes()) { + sectionStatus = this.sectionStatusManager.getSectionStatusByProjectOutcome(projectOutcome.getId(), + this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); if (sectionStatus == null) { return false; - } - if (sectionStatus.getMissingFields().length() > 0) { + if (sectionStatus.getMissingFields().length() != 0) { return false; - } } - } - if (caStudies.isEmpty()) { - return true; - } - returnValue = true; - break; - case HIGHLIGHTS: - project = this.projectManager.getProjectById(projectID); - List highlights = project.getProjectHighligths().stream() - .filter(d -> d.getProjectHighlightInfo(this.getActualPhase()) != null && d.isActive() - && d.getProjectHighlightInfo(this.getActualPhase()).getYear().intValue() == this.getCurrentCycleYear()) - .collect(Collectors.toList()); - if (highlights.isEmpty()) { - return true; - } + returnValue = true; + break; - for (ProjectHighlight highlight : highlights) { + case CASESTUDIES: + project = this.projectManager.getProjectById(projectID); + List caseStudies = + project.getCaseStudyProjects().stream().filter(d -> d.isActive()).collect(Collectors.toList()); + List caStudies = new ArrayList<>(); + + for (CaseStudyProject caseStudyProject : caseStudies) { + if (caseStudyProject.isActive() + && caseStudyProject.getCaseStudy().getYear() == this.getCurrentCycleYear()) { + caStudies.add(caseStudyProject.getCaseStudy()); + sectionStatus = + this.sectionStatusManager.getSectionStatusByCaseStudy(caseStudyProject.getCaseStudy().getId(), + this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); + if (sectionStatus == null) { + return false; - sectionStatus = this.sectionStatusManager.getSectionStatusByProjectHighlight(highlight.getId(), - this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); - if (sectionStatus == null) { - return false; + } + if (sectionStatus.getMissingFields().length() > 0) { + return false; + } + } } - if (sectionStatus.getMissingFields().length() > 0) { - return false; - + if (caStudies.isEmpty()) { + return true; } + returnValue = true; + break; - } - returnValue = true; - break; - case DELIVERABLES: - project = this.projectManager.getProjectById(projectID); + case HIGHLIGHTS: + project = this.projectManager.getProjectById(projectID); + List highlights = project.getProjectHighligths().stream() + .filter(d -> d.getProjectHighlightInfo(this.getActualPhase()) != null && d.isActive() + && d.getProjectHighlightInfo(this.getActualPhase()).getYear().intValue() == this.getCurrentCycleYear()) + .collect(Collectors.toList()); + if (highlights.isEmpty()) { + return true; + } - Phase phase = this.getActualPhase(); - List deliverables = new ArrayList<>(); + for (ProjectHighlight highlight : highlights) { - if (project.getDeliverables() != null) { + sectionStatus = this.sectionStatusManager.getSectionStatusByProjectHighlight(highlight.getId(), + this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); + if (sectionStatus == null) { + return false; - List infos = - this.deliverableInfoManager.getDeliverablesInfoByProjectAndPhase(phase, project); + } + if (sectionStatus.getMissingFields().length() > 0) { + return false; - if (infos != null && !infos.isEmpty()) { - for (DeliverableInfo deliverableInfo : infos) { - Deliverable deliverable = deliverableInfo.getDeliverable(); - deliverable.setDeliverableInfo(deliverableInfo); - deliverables.add(deliverable); } + } - } + returnValue = true; + break; + case DELIVERABLES: + project = this.projectManager.getProjectById(projectID); - for (Deliverable deliverable : deliverables) { + Phase phase = this.getActualPhase(); + List deliverables = new ArrayList<>(); - if (!this.isDeliverableComplete(deliverable.getId(), this.getActualPhase().getId())) { - return false; - } + if (project.getDeliverables() != null) { - // sectionStatus = - // this.sectionStatusManager.getSectionStatusByDeliverable(deliverable.getId(), - // this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), - // section); - // if (sectionStatus == null) { - // - // return false; - // } else { - // if (deliverable.getDeliverableInfo(phase).getStatus() != null && - // deliverable.getDeliverableInfo(phase) - // .getStatus().intValue() == - // Integer.parseInt(ProjectStatusEnum.Ongoing.getStatusId())) { - // if (deliverable.getDeliverableInfo(phase).getYear() > - // this.getActualPhase().getYear()) { - // sectionStatus.setMissingFields(""); - // } - // } - // - // if (this.isPlanningActive() && !this.isUpKeepActive()) { - // if (deliverable.getDeliverableInfo(phase).getStatus() != null && - // deliverable.getDeliverableInfo(phase) - // .getStatus().intValue() == - // Integer.parseInt(ProjectStatusEnum.Complete.getStatusId())) { - // sectionStatus.setMissingFields(""); - // } - // } - // } - // - // if (sectionStatus.getMissingFields().length() != 0) { - // return false; - // } - } - returnValue = true; + List infos = + this.deliverableInfoManager.getDeliverablesInfoByProjectAndPhase(phase, project); - break; + if (infos != null && !infos.isEmpty()) { + for (DeliverableInfo deliverableInfo : infos) { + Deliverable deliverable = deliverableInfo.getDeliverable(); + deliverable.setDeliverableInfo(deliverableInfo); + deliverables.add(deliverable); + } + } + } - case ACTIVITIES: - project = this.projectManager.getProjectById(projectID); + for (Deliverable deliverable : deliverables) { - project.setProjectActivities(new ArrayList(project.getActivities().stream() - .filter(a -> a.isActive() && a.getPhase().equals(this.getActualPhase())).collect(Collectors.toList()))); + if (!this.isDeliverableComplete(deliverable.getId(), this.getActualPhase().getId())) { + return false; + } - if (project.getProjectActivities().isEmpty()) { - return true; - } + // sectionStatus = + // this.sectionStatusManager.getSectionStatusByDeliverable(deliverable.getId(), + // this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), + // section); + // if (sectionStatus == null) { + // + // return false; + // } else { + // if (deliverable.getDeliverableInfo(phase).getStatus() != null && + // deliverable.getDeliverableInfo(phase) + // .getStatus().intValue() == + // Integer.parseInt(ProjectStatusEnum.Ongoing.getStatusId())) { + // if (deliverable.getDeliverableInfo(phase).getYear() > + // this.getActualPhase().getYear()) { + // sectionStatus.setMissingFields(""); + // } + // } + // + // if (this.isPlanningActive() && !this.isUpKeepActive()) { + // if (deliverable.getDeliverableInfo(phase).getStatus() != null && + // deliverable.getDeliverableInfo(phase) + // .getStatus().intValue() == + // Integer.parseInt(ProjectStatusEnum.Complete.getStatusId())) { + // sectionStatus.setMissingFields(""); + // } + // } + // } + // + // if (sectionStatus.getMissingFields().length() != 0) { + // return false; + // } + } + returnValue = true; - sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), - this.getCurrentCycleYear(), this.isUpKeepActive(), section); + break; - if (this.isAiccra()) { - // Check if exist deliverables without activities - List deliverablesMissingActivity = new ArrayList<>(); - List prevMissingActivity = new ArrayList<>(); + case ACTIVITIES: + project = this.projectManager.getProjectById(projectID); - int quantityMissingDeliverables = 0; - try { - quantityMissingDeliverables = deliverableManager - .getQuantityDeliverablesWithActivities(this.getActualPhase().getId(), project.getId()); - } catch (Exception e) { - LOG.error("unable to get deliverables without activities Quantity", e); - prevMissingActivity = new ArrayList<>(); + project.setProjectActivities(new ArrayList(project.getActivities().stream() + .filter(a -> a.isActive() && a.getPhase().equals(this.getActualPhase())).collect(Collectors.toList()))); + + if (project.getProjectActivities().isEmpty()) { + return true; } - // cgamboa 22/04/2024 query is added to get quantity deliverables without activities, before to do the - // validations - if (quantityMissingDeliverables > 0) { - try { - prevMissingActivity = project.getCurrentDeliverables(this.getActualPhase()); + sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), + this.getCurrentCycleYear(), this.isUpKeepActive(), section); + if (this.isAiccra()) { + // Check if exist deliverables without activities + List deliverablesMissingActivity = new ArrayList<>(); + List prevMissingActivity = new ArrayList<>(); - if (prevMissingActivity != null && !prevMissingActivity.isEmpty()) { - prevMissingActivity = prevMissingActivity.stream() - .filter(d -> d != null && d.getDeliverableInfo(this.getActualPhase()).getStatus() != null - && d.getDeliverableInfo(this.getActualPhase()).getStatus() != 5) - .collect(Collectors.toList()); - } + int quantityMissingDeliverables = 0; + try { + quantityMissingDeliverables = deliverableManager + .getQuantityDeliverablesWithActivities(this.getActualPhase().getId(), project.getId()); } catch (Exception e) { - LOG.error("unable to get deliverables without activities", e); + LOG.error("unable to get deliverables without activities Quantity", e); prevMissingActivity = new ArrayList<>(); } - prevMissingActivity.stream() - .filter((deliverable) -> (deliverable.getDeliverableActivities().isEmpty() - || deliverable.getDeliverableActivities().stream().filter(da -> da.isActive()) - .collect(Collectors.toList()).isEmpty() - || deliverable.getDeliverableActivities().stream() - .filter(da -> da.getPhase().getId().equals(this.getActualPhase().getId()) - && da.getActivity().isActive() && da.isActive()) - .collect(Collectors.toList()).isEmpty())) - .forEachOrdered((_item) -> { - deliverablesMissingActivity.add(_item); - }); + // cgamboa 22/04/2024 query is added to get quantity deliverables without activities, before to do the + // validations + if (quantityMissingDeliverables > 0) { + try { + prevMissingActivity = project.getCurrentDeliverables(this.getActualPhase()); - } - if (deliverablesMissingActivity != null && !deliverablesMissingActivity.isEmpty()) { - // this.addMessage(this.getText("missingDeliverableActivity", "deliverable.missing.activity")); - // this.getInvalidFields().put("list-deliverable.missing.activity.alert", - // InvalidFieldsMessages.EMPTYFIELD); - /* - * SectionStatus status = null; - * status = new SectionStatus(); - * status.setCycle(this.getCurrentCycle()); - * status.setYear(this.getCurrentCycleYear()); - * status.setUpkeep(this.isUpKeepActive()); - * status.setProject(project); - * status.setSectionName(ProjectSectionStatusEnum.ACTIVITIES.getStatus()); - * status.setMissingFields("missingDeliverableActivity"); - * sectionStatusManager.saveSectionStatus(status); - */ + if (prevMissingActivity != null && !prevMissingActivity.isEmpty()) { + prevMissingActivity = prevMissingActivity.stream() + .filter(d -> d != null && d.getDeliverableInfo(this.getActualPhase()).getStatus() != null + && d.getDeliverableInfo(this.getActualPhase()).getStatus() != 5) + .collect(Collectors.toList()); + } + } catch (Exception e) { + LOG.error("unable to get deliverables without activities", e); + prevMissingActivity = new ArrayList<>(); + } - return false; + prevMissingActivity.stream() + .filter((deliverable) -> (deliverable.getDeliverableActivities().isEmpty() + || deliverable.getDeliverableActivities().stream().filter(da -> da.isActive()) + .collect(Collectors.toList()).isEmpty() + || deliverable.getDeliverableActivities().stream() + .filter(da -> da.getPhase().getId().equals(this.getActualPhase().getId()) + && da.getActivity().isActive() && da.isActive()) + .collect(Collectors.toList()).isEmpty())) + .forEachOrdered((_item) -> { + deliverablesMissingActivity.add(_item); + }); + + } + + if (deliverablesMissingActivity != null && !deliverablesMissingActivity.isEmpty()) { + // this.addMessage(this.getText("missingDeliverableActivity", "deliverable.missing.activity")); + // this.getInvalidFields().put("list-deliverable.missing.activity.alert", + // InvalidFieldsMessages.EMPTYFIELD); + /* + * SectionStatus status = null; + * status = new SectionStatus(); + * status.setCycle(this.getCurrentCycle()); + * status.setYear(this.getCurrentCycleYear()); + * status.setUpkeep(this.isUpKeepActive()); + * status.setProject(project); + * status.setSectionName(ProjectSectionStatusEnum.ACTIVITIES.getStatus()); + * status.setMissingFields("missingDeliverableActivity"); + * sectionStatusManager.saveSectionStatus(status); + */ + + return false; + } } - } - if (sectionStatus != null) { - if (sectionStatus.getMissingFields().length() == 0) { - return true; + if (sectionStatus != null) { + if (sectionStatus.getMissingFields().length() == 0) { + return true; + } } - } - break; + break; - case BUDGET: - if (this.isReportingActive()) { - return true; - } else { - if (!this.hasSpecificities(this.getCrpEnableBudgetExecution())) { + case BUDGET: + if (this.isReportingActive()) { return true; - } - project = this.projectManager.getProjectById(projectID); - if (project.getProjectBudgets().stream() - .filter(d -> d.isActive() && d.getPhase() != null && d.getPhase().equals(this.getActualPhase())) - .collect(Collectors.toList()).isEmpty()) { - return false; - } - if (this.isReportingActive() && this.hasSpecificities(this.getCrpEnableBudgetExecution())) { - if (project.getProjectBudgetExecutions().stream() + } else { + if (!this.hasSpecificities(this.getCrpEnableBudgetExecution())) { + return true; + } + project = this.projectManager.getProjectById(projectID); + if (project.getProjectBudgets().stream() .filter(d -> d.isActive() && d.getPhase() != null && d.getPhase().equals(this.getActualPhase())) .collect(Collectors.toList()).isEmpty()) { return false; } + if (this.isReportingActive() && this.hasSpecificities(this.getCrpEnableBudgetExecution())) { + if (project.getProjectBudgetExecutions().stream() + .filter(d -> d.isActive() && d.getPhase() != null && d.getPhase().equals(this.getActualPhase())) + .collect(Collectors.toList()).isEmpty()) { + return false; + } + } + + sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), + this.getCurrentCycleYear(), this.isUpKeepActive(), section); + if (sectionStatus != null) { + if (sectionStatus.getMissingFields().length() == 0) { + return true; + } + } } + break; + case DESCRIPTION: + case LOCATIONS: sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); if (sectionStatus != null) { if (sectionStatus.getMissingFields().length() == 0) { return true; } + } else { + if (this.isReportingActive()) { + return true; + } } - } - break; - case DESCRIPTION: - case LOCATIONS: - sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), - this.getCurrentCycleYear(), this.isUpKeepActive(), section); - if (sectionStatus != null) { - if (sectionStatus.getMissingFields().length() == 0) { - return true; - } - } else { - if (this.isReportingActive()) { - return true; + break; + + case EXPECTEDSTUDIES: + project = this.projectManager.getProjectById(projectID); + List allProjectStudies = new ArrayList(); + + // Load Studies + List studies = project.getProjectExpectedStudies().stream() + .filter(c -> c.isActive() && c.getProjectExpectedStudyInfo(this.getActualPhase()) != null) + .collect(Collectors.toList()); + if (studies != null && studies.size() > 0) { + allProjectStudies.addAll(studies); } - } - break; + List projectStudies = new ArrayList(); - case EXPECTEDSTUDIES: - project = this.projectManager.getProjectById(projectID); - List allProjectStudies = new ArrayList(); + if (allProjectStudies != null && allProjectStudies.size() > 0) { + // Editable project studies: Current cycle year-1 will be + // editable except Complete and Cancelled. + // Every study of the current cycle year will be editable + projectStudies = allProjectStudies.stream() + .filter(ps -> ps.getProjectExpectedStudyInfo().getYear() != null + && ps.getProjectExpectedStudyInfo().getStatus() != null + && ps.getProjectExpectedStudyInfo().getYear() >= this.getCurrentCycleYear()) + .collect(Collectors.toList()); + } - // Load Studies - List studies = project.getProjectExpectedStudies().stream() - .filter(c -> c.isActive() && c.getProjectExpectedStudyInfo(this.getActualPhase()) != null) - .collect(Collectors.toList()); - if (studies != null && studies.size() > 0) { - allProjectStudies.addAll(studies); - } + if (projectStudies.isEmpty()) { + return true; + } - List projectStudies = new ArrayList(); + for (ProjectExpectedStudy projectExpectedStudy : projectStudies) { + sectionStatus = + this.sectionStatusManager.getSectionStatusByProjectExpectedStudy(projectExpectedStudy.getId(), + this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); + if (sectionStatus != null) { + if (sectionStatus.getMissingFields() != null && sectionStatus.getMissingFields().length() != 0) { + return false; + } + } else { + return false; + } + } + returnValue = true; + break; - if (allProjectStudies != null && allProjectStudies.size() > 0) { - // Editable project studies: Current cycle year-1 will be - // editable except Complete and Cancelled. - // Every study of the current cycle year will be editable - projectStudies = allProjectStudies.stream() - .filter(ps -> ps.getProjectExpectedStudyInfo().getYear() != null - && ps.getProjectExpectedStudyInfo().getStatus() != null - && ps.getProjectExpectedStudyInfo().getYear() >= this.getCurrentCycleYear()) + case INNOVATIONS: + project = this.projectManager.getProjectById(projectID); + List innovations = project.getProjectInnovations().stream() + .filter(c -> c.getProjectInnovationInfo(this.getActualPhase()) != null && c.isActive() + && c.getProjectInnovationInfo(this.getActualPhase()).getYear().intValue() == this.getCurrentCycleYear()) .collect(Collectors.toList()); - } - if (projectStudies.isEmpty()) { - return true; - } + if (innovations.isEmpty()) { + return true; + } - for (ProjectExpectedStudy projectExpectedStudy : projectStudies) { - sectionStatus = - this.sectionStatusManager.getSectionStatusByProjectExpectedStudy(projectExpectedStudy.getId(), + for (ProjectInnovation projectInnovation : innovations) { + sectionStatus = this.sectionStatusManager.getSectionStatusByProjectInnovation(projectInnovation.getId(), this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); - if (sectionStatus != null) { - if (sectionStatus.getMissingFields() != null && sectionStatus.getMissingFields().length() != 0) { + if (sectionStatus != null) { + if (sectionStatus.getMissingFields().length() != 0) { + return false; + } + } else { return false; } - } else { - return false; } - } - returnValue = true; - break; + returnValue = true; - case INNOVATIONS: - project = this.projectManager.getProjectById(projectID); - List innovations = project.getProjectInnovations().stream() - .filter(c -> c.getProjectInnovationInfo(this.getActualPhase()) != null && c.isActive() - && c.getProjectInnovationInfo(this.getActualPhase()).getYear().intValue() == this.getCurrentCycleYear()) - .collect(Collectors.toList()); + break; - if (innovations.isEmpty()) { - return true; - } + case POLICIES: + project = this.projectManager.getProjectById(projectID); + List policies = project.getProjectPolicies().stream() + .filter(c -> c.getProjectPolicyInfo(this.getActualPhase()) != null && c.isActive() + && c.getProjectPolicyInfo(this.getActualPhase()).getYear().intValue() == this.getCurrentCycleYear()) + .collect(Collectors.toList()); - for (ProjectInnovation projectInnovation : innovations) { - sectionStatus = this.sectionStatusManager.getSectionStatusByProjectInnovation(projectInnovation.getId(), - this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); - if (sectionStatus != null) { - if (sectionStatus.getMissingFields().length() != 0) { + for (ProjectPolicy projectPolicy : policies) { + sectionStatus = this.sectionStatusManager.getSectionStatusByProjectPolicy(projectPolicy.getId(), + this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); + if (sectionStatus != null) { + if (sectionStatus.getMissingFields().length() != 0) { + return false; + } + } else { return false; } - } else { - return false; } - } - returnValue = true; - - break; + returnValue = true; - case POLICIES: - project = this.projectManager.getProjectById(projectID); - List policies = project.getProjectPolicies().stream() - .filter(c -> c.getProjectPolicyInfo(this.getActualPhase()) != null && c.isActive() - && c.getProjectPolicyInfo(this.getActualPhase()).getYear().intValue() == this.getCurrentCycleYear()) - .collect(Collectors.toList()); + break; - for (ProjectPolicy projectPolicy : policies) { - sectionStatus = this.sectionStatusManager.getSectionStatusByProjectPolicy(projectPolicy.getId(), - this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); + case LEVERAGES: + sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), + this.getCurrentCycleYear(), this.isUpKeepActive(), section); if (sectionStatus != null) { - if (sectionStatus.getMissingFields().length() != 0) { - return false; + if (sectionStatus.getMissingFields().length() == 0) { + return true; } } else { - return false; + return true; } - } - returnValue = true; + returnValue = false; - break; + break; - case LEVERAGES: - sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), - this.getCurrentCycleYear(), this.isUpKeepActive(), section); - if (sectionStatus != null) { - if (sectionStatus.getMissingFields().length() == 0) { + case BUDGETBYFLAGSHIP: + if (this.isAiccra()) { return true; + } else { + sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), + this.getCurrentCycleYear(), this.isUpKeepActive(), section); + if (sectionStatus != null) { + if (sectionStatus.getMissingFields().length() == 0) { + return true; + } + } } - } else { - return true; - } - returnValue = false; - - break; - - case BUDGETBYFLAGSHIP: - if (this.isAiccra()) { - return true; - } else { + break; + default: sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), this.getCurrentCycleYear(), this.isUpKeepActive(), section); if (sectionStatus != null) { if (sectionStatus.getMissingFields().length() == 0) { return true; } - } - } - break; - default: - sectionStatus = this.sectionStatusManager.getSectionStatusByProject(projectID, this.getCurrentCycle(), - this.getCurrentCycleYear(), this.isUpKeepActive(), section); - if (sectionStatus != null) { - if (sectionStatus.getMissingFields().length() == 0) { - return true; - } - break; + break; - } + } + } } + return returnValue; + } catch (Exception e) { + Log.error("Error getting project section status " + e); + return false; } - return returnValue; - } public List getProjectSubmissions(long projectID) { @@ -7272,7 +7294,6 @@ public Boolean isDeliverableComplete(Long deliverableID, Long phaseID) { } - /** * This method get the status of an specific deliverable depending of the * sectionStatuses and the year Previous deliverable will be marked as @@ -7344,7 +7365,6 @@ public Boolean isDeliverableNew(Long deliverableID) { } } - /** * Validate if a deliverable belongs to the phase * @@ -7374,12 +7394,10 @@ public Boolean isDeliverableNewDashboard(Long deliverableID) { } - public boolean isDraft() { return this.draft; } - public boolean isEditable() { return this.isEditable; } @@ -7406,6 +7424,7 @@ public boolean isEntityCRP() { return false; } + public boolean isEntityPlatform() { if (this.getCurrentCrp() != null) { if (this.getCurrentCrp().getGlobalUnitType().getId().intValue() == 3) { @@ -7442,6 +7461,7 @@ public Boolean isEvidenceNew(long evidenceId) { } + public boolean isExpectedDeliverablesReportAllYearsVisible() { // Specificity for show expected deliverable summary - all years selection - in summaries section Boolean isVisible = false; @@ -7459,6 +7479,7 @@ public boolean isExpectedDeliverablesReportAllYearsVisible() { return isVisible; } + /** * Findable * @@ -7497,6 +7518,7 @@ public Boolean isF(long deliverableID) { } + public boolean isFullEditable() { return this.fullEditable; } @@ -7656,6 +7678,22 @@ public boolean isNewCenterType(long projectID) { } + public boolean isOicrAllianceAlignmentComplete() { + return isOicrAllianceAlignmentComplete; + } + + public boolean isOicrCommunicationsComplete() { + return isOicrCommunicationsComplete; + } + + public boolean isOicrGeneralInformationComplete() { + return isOicrGeneralInformationComplete; + } + + public boolean isOicrOneCgiarAlignmentComplete() { + return isOicrOneCgiarAlignmentComplete; + } + public boolean isOtherUrl() { return this.otherUrl; } @@ -8779,6 +8817,22 @@ public void setNext(boolean next) { this.next = true; } + public void setOicrAllianceAlignmentComplete(boolean isOicrAllianceAlignmentComplete) { + this.isOicrAllianceAlignmentComplete = isOicrAllianceAlignmentComplete; + } + + public void setOicrCommunicationsComplete(boolean isOicrCommunicationsComplete) { + this.isOicrCommunicationsComplete = isOicrCommunicationsComplete; + } + + public void setOicrGeneralInformationComplete(boolean isOicrGeneralInformationComplete) { + this.isOicrGeneralInformationComplete = isOicrGeneralInformationComplete; + } + + public void setOicrOneCgiarAlignmentComplete(boolean isOicrOneCgiarAlignmentComplete) { + this.isOicrOneCgiarAlignmentComplete = isOicrOneCgiarAlignmentComplete; + } + public void setOtherUrl(boolean otherUrl) { this.otherUrl = otherUrl; } diff --git a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/json/project/ValidateProjectSectionAction.java b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/json/project/ValidateProjectSectionAction.java index c99acf3485..355f400c03 100644 --- a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/json/project/ValidateProjectSectionAction.java +++ b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/action/json/project/ValidateProjectSectionAction.java @@ -152,17 +152,17 @@ public String execute() throws Exception { break; case PARTNERS: this.projectSectionValidator.validateProjectParnters(this, this.getProjectID(), this.loggedCrp); - /* - * case BUDGET: - * if (this.isPlanningActive() || ((this.isReportingActive() || this.isUpKeepActive()) - * && this.hasSpecificities(this.getCrpEnableBudgetExecution()))) { - * this.projectSectionValidator.validateProjectBudgets(this, this.getProjectID()); - * } - * break; - * case BUDGETBYFLAGSHIP: - * this.projectSectionValidator.validateProjectBudgetsFlagship(this, this.getProjectID(), true); - * break; - */ + + case BUDGET: + if (this.isPlanningActive() || ((this.isReportingActive() || this.isUpKeepActive()) + && this.hasSpecificities(this.getCrpEnableBudgetExecution()))) { + this.projectSectionValidator.validateProjectBudgets(this, this.getProjectID()); + } + break; + case BUDGETBYFLAGSHIP: + this.projectSectionValidator.validateProjectBudgetsFlagship(this, this.getProjectID(), true); + break; + case DELIVERABLES: this.projectSectionValidator.validateProjectDeliverables(this, this.getProjectID()); break; diff --git a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/utils/Clone.java b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/utils/Clone.java index 3596209ee9..da02f4faee 100644 --- a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/utils/Clone.java +++ b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/utils/Clone.java @@ -26,16 +26,35 @@ public class Clone { + /** + * NOTE: This variable (projectRoot) should be changed according to the local project location on each developer's + * machine. + */ + public static String projectRoot = "D:\\MARLO-PROJECT - 4.5\\"; + public static String pathdao = - "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\dao"; + projectRoot + "MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\dao"; public static String pathmysqldao = - "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\dao\\mysql"; + projectRoot + "MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\dao\\mysql"; public static String pathmanager = - "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\manager"; + projectRoot + "MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\manager"; public static String pathmodel = - "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\model"; + projectRoot + "MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\model"; public static String pathmanagerimpl = - "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\manager\\impl"; + projectRoot + "MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\manager\\impl"; + + /* + * public static String pathdao = + * "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\dao"; + * public static String pathmysqldao = + * "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\dao\\mysql"; + * public static String pathmanager = + * "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\manager"; + * public static String pathmodel = + * "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\model"; + * public static String pathmanagerimpl = + * "D:\\MARLO-PROJECT - 4.5\\MARLO\\marlo-data\\src\\main\\java\\org\\cgiar\\ccafs\\marlo\\data\\manager\\impl"; + */ // Copy the source file to target file. // In case the dst file does not exist, it is created diff --git a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/validation/projects/ProjectSectionValidator.java b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/validation/projects/ProjectSectionValidator.java index 6fea7107d6..e702261cb2 100644 --- a/marlo-web/src/main/java/org/cgiar/ccafs/marlo/validation/projects/ProjectSectionValidator.java +++ b/marlo-web/src/main/java/org/cgiar/ccafs/marlo/validation/projects/ProjectSectionValidator.java @@ -58,6 +58,8 @@ import org.cgiar.ccafs.marlo.data.model.DeliverableLocation; import org.cgiar.ccafs.marlo.data.model.DeliverableParticipant; import org.cgiar.ccafs.marlo.data.model.DeliverableQualityCheck; +import org.cgiar.ccafs.marlo.data.model.DeliverableShfrmPriorityAction; +import org.cgiar.ccafs.marlo.data.model.DeliverableShfrmSubAction; import org.cgiar.ccafs.marlo.data.model.DeliverableUserPartnership; import org.cgiar.ccafs.marlo.data.model.DeliverableUserPartnershipPerson; import org.cgiar.ccafs.marlo.data.model.ExpectedStudyProject; @@ -108,6 +110,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -937,10 +940,11 @@ public void validateProjectBudgetsFlagship(BaseAction action, Long projectID, bo Project project = projectManager.getProjectById(projectID); project.setBudgetsFlagship(project.getProjectBudgetsFlagships().stream() .filter(c -> c.isActive() && c.getPhase().equals(action.getActualPhase())).collect(Collectors.toList())); - if (!(project.getProjectBudgetsFlagships().isEmpty() || project.getProjectBudgetsFlagships().size() == 1)) { - projectBudgetsFlagshipValidator.validate(action, project, false, sMessage); - } - + /* + * if (!(project.getProjectBudgetsFlagships().isEmpty() || project.getProjectBudgetsFlagships().size() == 1)) { + * } + */ + projectBudgetsFlagshipValidator.validate(action, project, false, sMessage); } @@ -1184,6 +1188,32 @@ public void validateProjectDeliverables(BaseAction action, Long projectID) { deliverable.setCrossCuttingMarkers(deliverableCrossCuttingMarkers); } + // Soil information + if (deliverable.getDeliverableShfrmPriorityAction() != null) { + deliverable.setShfrmPriorityActions(new ArrayList<>(deliverable.getDeliverableShfrmPriorityAction().stream() + .filter(o -> o.isActive() && o.getPhase().getId().equals(action.getActualPhase().getId())) + .sorted(Comparator.comparing(DeliverableShfrmPriorityAction::getId)).collect(Collectors.toList()))); + } + + if (deliverable.getShfrmPriorityActions() != null && !deliverable.getShfrmPriorityActions().isEmpty()) { + for (DeliverableShfrmPriorityAction deliverablePriorityAction : deliverable.getShfrmPriorityActions()) { + try { + + if (deliverablePriorityAction.getDeliverableShfrmSubAction() != null) { + deliverablePriorityAction + .setShfrmSubActions(new ArrayList<>(deliverablePriorityAction.getDeliverableShfrmSubAction().stream() + .filter(o -> o.isActive() && o.getPhase().getId().equals(action.getActualPhase().getId())) + .sorted(Comparator.comparing(DeliverableShfrmSubAction::getId)).collect(Collectors.toList()))); + } + + } catch (Exception e) { + logger.error("unable to get deliverableSubActions for deliverablePriorityAction: {}", + deliverablePriorityAction, e); + } + } + } + + if (action.isReportingActive() || action.isUpKeepActive()) { DeliverableQualityCheck deliverableQualityCheck = diff --git a/marlo-web/src/main/resources/database/migrations/V2_6_0_20241009_1121__AddCrpProgramOutcomesFieldsTable.sql b/marlo-web/src/main/resources/database/migrations/V2_6_0_20241009_1121__AddCrpProgramOutcomesFieldsTable.sql new file mode 100644 index 0000000000..ec034bc9eb --- /dev/null +++ b/marlo-web/src/main/resources/database/migrations/V2_6_0_20241009_1121__AddCrpProgramOutcomesFieldsTable.sql @@ -0,0 +1,2 @@ +ALTER TABLE crp_program_outcomes ADD order_index int(10) NULL; +ALTER TABLE crp_program_outcomes ADD af_phase int(10) NULL; diff --git a/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/menu-projects.ftl b/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/menu-projects.ftl index b016268358..4751026503 100644 --- a/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/menu-projects.ftl +++ b/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/menu-projects.ftl @@ -65,8 +65,8 @@ }, { 'title': 'Budget', 'show': true, 'items': [ - { 'slug': 'budgetByPartners', 'name': 'projects.menu.budgetByPartners', 'action': 'budgetByPartners', 'active': true, 'show':true, "showCheck": isGlobalUnitProject }, - { 'slug': 'budgetByFlagships', 'name': 'projects.menu.budgetByFlagships', 'action': 'budgetByFlagship', 'active': true, 'show': action.getCountProjectFlagships(project.id) && !reportingActive && isCrpProject, "showCheck": isGlobalUnitProject}, + { 'slug': 'budgetByPartners', 'name': 'projects.menu.budgetByPartners', 'action': 'budgetByPartners', 'active': true, 'show':true, "showCheck": false }, + { 'slug': 'budgetByFlagships', 'name': 'projects.menu.budgetByFlagships', 'action': 'budgetByFlagship', 'active': true, 'show': action.getCountProjectFlagships(project.id) && !reportingActive && isCrpProject, "showCheck": false}, { 'slug': 'leverages', 'name': 'Leverages', 'action': 'leverages', 'active': true, 'show': reportingActive && action.hasSpecificities("crp_leverages_module") && isCrpProject, "showCheck": isGlobalUnitProject} ] }, @@ -110,7 +110,12 @@

    [#list menu.items as item] [#if (item.showCheck)!true] - [#assign submitStatus = (action.getProjectSectionStatus(item.action, projectID))!false /] + [#assign submitStatus = false /] + [#if item.action?has_content && projectID?has_content] + [#assign submitStatus = (action.getProjectSectionStatus(item.action, projectID))!false /] + [#else] + [#assign submitStatus = false /] + [/#if] [/#if] [#assign hasDraft = (action.getAutoSaveFilePath(project.class.simpleName, item.action, project.id))!false /] [#if (item.show)!true ] diff --git a/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/projectDeliverable.ftl b/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/projectDeliverable.ftl index b2a7b4e99e..54dcf84f62 100644 --- a/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/projectDeliverable.ftl +++ b/marlo-web/src/main/webapp/WEB-INF/crp/views/projects/projectDeliverable.ftl @@ -97,11 +97,11 @@ - - - - - + + + + +