dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #30657
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 15578: Allow data approvals for composite periods spanning multiple data set periods.
------------------------------------------------------------
revno: 15578
committer: jimgrace@xxxxxxxxx
branch nick: dhis2
timestamp: Fri 2014-06-06 15:29:39 -0400
message:
Allow data approvals for composite periods spanning multiple data set periods.
added:
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPeriodAggregator.java
modified:
dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/MapMap.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApproval.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalBaseState.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalState.java
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalSelection.java
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java
dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java
dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CollectionUtils.java
dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java
dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetDataApprovalOptionsAction.java
dhis-2/dhis-web/dhis-web-reporting/src/main/resources/org/hisp/dhis/reporting/i18n_module.properties
dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/dataApprovalForm.vm
dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/javascript/dataApproval.js
--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk
Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/MapMap.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/MapMap.java 2014-05-18 00:49:40 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/MapMap.java 2014-06-06 19:29:39 +0000
@@ -28,6 +28,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
@@ -57,4 +58,16 @@
{
return this.get( key ) == null ? null : this.get( key ).get( valueKey );
}
+
+ public static <T, U, V> MapMap<T, U, V> asMapMap( AbstractMap.SimpleEntry<T, Map<U, V>>... entries )
+ {
+ MapMap<T, U, V> map = new MapMap<T, U, V>();
+
+ for ( AbstractMap.SimpleEntry<T, Map<U, V>> entry : entries )
+ {
+ map.put( entry.getKey(), entry.getValue() );
+ }
+
+ return map;
+ }
}
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApproval.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApproval.java 2014-04-07 04:27:17 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApproval.java 2014-06-06 19:29:39 +0000
@@ -122,6 +122,18 @@
this.creator = creator;
}
+ public DataApproval( DataApproval da )
+ {
+ this.dataApprovalLevel = da.dataApprovalLevel;
+ this.dataSet = da.dataSet;
+ this.period = da.period;
+ this.organisationUnit = da.organisationUnit;
+ this.categoryOptionGroup = da.categoryOptionGroup;
+ this.accepted = da.accepted;
+ this.created = da.created;
+ this.creator = da.creator;
+ }
+
// -------------------------------------------------------------------------
// Getters and setters
// -------------------------------------------------------------------------
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalBaseState.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalBaseState.java 2014-04-14 06:52:39 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalBaseState.java 2014-06-06 19:29:39 +0000
@@ -52,11 +52,23 @@
UNAPPROVED_READY,
/**
+ * Data is approved for some but not all periods inside this longer period
+ * and is ready for approval in all periods inside this containing period.
+ */
+ PARTIALLY_APPROVED,
+
+ /**
* Data is approved (either here or elsewhere).
*/
APPROVED,
/**
+ * Data is accepted for some but not all periods inside this longer period
+ * and is ready for accepting in all periods inside this containing period.
+ */
+ PARTIALLY_ACCEPTED,
+
+ /**
* Data is approved and accepted (either here or elsewhere).
*/
ACCEPTED;
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalState.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalState.java 2014-04-14 06:52:39 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalState.java 2014-06-06 19:29:39 +0000
@@ -59,17 +59,28 @@
UNAPPROVED_READY ( DataApprovalBaseState.UNAPPROVED_READY, false, true, true, false, true ),
/**
+ * Some periods within this multi-period selection are approved here
+ * and some are not approved (but ready for approval here.)
+ */
+ PARTIALLY_APPROVED_HERE( DataApprovalBaseState.PARTIALLY_APPROVED, false, false, true, false, true ),
+
+ /**
* Data is approved, and was approved here (so could be unapproved here.)
*/
APPROVED_HERE ( DataApprovalBaseState.APPROVED, true, false, true, false, false ),
/**
+ * Some periods within this multi-period selection are approved elsewhere
+ * and some are not approved elsewhere (not approvable here.)
+ */
+ PARTIALLY_APPROVED_ELSEWHERE ( DataApprovalBaseState.PARTIALLY_APPROVED, false, false, false, false, false ),
+
+ /**
* Data is approved, but was not approved here (so cannot be unapproved here.)
* This covers the following cases:
* <ul>
* <li>Data is approved at a higher level.</li>
* <li>Data is approved for wider scope of category options.</li>
- * <li>Data is approved for all sub-periods in selected period.</li>
* </ul>
* In the first two cases, there is a single data approval object
* that covers the selection. In the third case there is not.
@@ -77,11 +88,23 @@
APPROVED_ELSEWHERE( DataApprovalBaseState.APPROVED, true, false, false, false, false ),
/**
+ * Some periods within this multi-period selection are accepted here
+ * and some are not approved elsewhere (not approvable here.)
+ */
+ PARTIALLY_ACCEPTED_HERE( DataApprovalBaseState.PARTIALLY_ACCEPTED, true, false, true, false, false ),
+
+ /**
* Data is approved and accepted here (so could be unapproved here.)
*/
ACCEPTED_HERE ( DataApprovalBaseState.ACCEPTED, true, false, true, true, false ),
/**
+ * Some periods within this multi-period selection are accepted elsewhere
+ * and some are approved elsewhere (not approvable here.)
+ */
+ PARTIALLY_ACCEPTED_ELSEWHERE ( DataApprovalBaseState.PARTIALLY_APPROVED, false, false, false, false, false ),
+
+ /**
* Data is approved and accepted, but elsewhere.
*/
ACCEPTED_ELSEWHERE ( DataApprovalBaseState.ACCEPTED, true, false, false, true, false );
=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPeriodAggregator.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPeriodAggregator.java 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPeriodAggregator.java 2014-06-06 19:29:39 +0000
@@ -0,0 +1,223 @@
+package org.hisp.dhis.dataapproval;
+
+/*
+ * Copyright (c) 2004-2014, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hisp.dhis.common.MapMap;
+
+import static org.hisp.dhis.common.MapMap.*;
+import static org.hisp.dhis.dataapproval.DataApprovalState.*;
+import static org.hisp.dhis.system.util.CollectionUtils.*;
+
+/**
+ * This package-private class is used by the data approval service to
+ * form a composite data approval state for a period spanning more than
+ * one data approval period.
+ *
+ * @author Jim Grace
+ * @version $Id$
+ */
+class DataApprovalPeriodAggregator
+{
+ /**
+ * Represents the data approval state transitions from a current state
+ * representing the combined state of all periods tested so far, combined
+ * with the state of a new period, resulting in the next current state.
+ * <p>
+ * When checking states for a number of periods where all other selection
+ * criteria are the same, the states will always be in one of three
+ * mutually exclusive categories:
+ * <ul>
+ * <li>UNAPPROVABLE</li>
+ * <li>Approvable elsewhere states:
+ * UNAPPROVED_ELSEWHERE,
+ * PARTIALLY_APPROVED_ELSEWHERE,
+ * APPROVED_ELSEWHERE,
+ * ACCEPTED_ELSEWHERE,
+ * PARTIALLY_ACCEPTED_ELSEWHERE
+ * </li>
+ * <li>Approvable here states:
+ * UNAPPROVED_WAITING,
+ * UNAPPROVED_READY,
+ * PARTIALLY_APPROVED_HERE,
+ * APPROVED_HERE,
+ * PARTIALLY_ACCEPTED_HERE,
+ * ACCEPTED_HERE
+ * </li>
+ * </ul>
+ * We don't have to worry about state transitions between these
+ * categories; they will not exist. We need only consider state
+ * transitions within each of the categories. (And we don't have to
+ * consider state transitions within the first category since there
+ * is only one state.)
+ * <p>
+ * The state transitions are coded in a MapMap. Conceptually, they
+ * form a triangular matrix (minus the diagonal) like the following,
+ * where "*" shows the entries:
+ * <pre>
+ *
+ * current A B C D E
+ * new A . * * * *
+ * B . . * * *
+ * C . . . * *
+ * D . . . . *
+ *
+ * </pre>
+ * The diagonal is not required because when the current and new states
+ * are the same, the next current state will be the same as both.
+ * The lower triangle of the matrix is not required because the
+ * matrix is tested both ways: current (columns) - new (rows), and also
+ * current (rows) - new (columns) (The matrix dimensions are commutative.)
+ */
+ static private final MapMap<DataApprovalState, DataApprovalState, DataApprovalState> transitionMap = asMapMap(
+
+ // ---------------------------------------------------------------------
+ // States where data can be approved, but not here
+ // ---------------------------------------------------------------------
+
+ //
+ // Data is unapproved, and is waiting for approval somewhere else.
+ //
+ asEntry( UNAPPROVED_ELSEWHERE, asMap(
+ asEntry( PARTIALLY_APPROVED_ELSEWHERE, PARTIALLY_APPROVED_ELSEWHERE ),
+ asEntry( APPROVED_ELSEWHERE, PARTIALLY_APPROVED_ELSEWHERE ),
+ asEntry( PARTIALLY_ACCEPTED_ELSEWHERE, PARTIALLY_APPROVED_ELSEWHERE ),
+ asEntry( ACCEPTED_ELSEWHERE, PARTIALLY_APPROVED_ELSEWHERE ) ) ),
+
+ //
+ // Some periods within this selection are approved elsewhere and
+ // some are unapproved elsewhere.
+ //
+ asEntry( PARTIALLY_APPROVED_ELSEWHERE, asMap(
+ asEntry( APPROVED_ELSEWHERE, PARTIALLY_APPROVED_ELSEWHERE ),
+ asEntry( PARTIALLY_ACCEPTED_ELSEWHERE, PARTIALLY_APPROVED_ELSEWHERE ),
+ asEntry( ACCEPTED_ELSEWHERE, PARTIALLY_APPROVED_ELSEWHERE ) ) ),
+
+ //
+ // Data is unapproved, and is waiting for approval somewhere else.
+ //
+ asEntry( APPROVED_ELSEWHERE, asMap(
+ asEntry( PARTIALLY_ACCEPTED_ELSEWHERE, PARTIALLY_ACCEPTED_ELSEWHERE ),
+ asEntry( ACCEPTED_ELSEWHERE, PARTIALLY_ACCEPTED_ELSEWHERE ) ) ),
+
+ //
+ // Data is approved somewhere else.
+ //
+ asEntry( PARTIALLY_ACCEPTED_ELSEWHERE, asMap(
+ asEntry( ACCEPTED_ELSEWHERE, PARTIALLY_ACCEPTED_ELSEWHERE ) ) ),
+
+ // ---------------------------------------------------------------------
+ // States where data can be approved here
+ // ---------------------------------------------------------------------
+
+ //
+ // Data is unapproved, and is waiting for some lower-level approval.
+ //
+ asEntry( UNAPPROVED_WAITING, asMap(
+ asEntry( UNAPPROVED_READY, UNAPPROVED_WAITING ),
+ asEntry( PARTIALLY_APPROVED_HERE, UNAPPROVED_WAITING ),
+ asEntry( APPROVED_HERE, UNAPPROVED_WAITING ),
+ asEntry( PARTIALLY_ACCEPTED_HERE, UNAPPROVED_WAITING ),
+ asEntry( ACCEPTED_HERE, UNAPPROVED_WAITING ) ) ),
+
+ //
+ // Data is unapproved, and is ready to be approved here.
+ //
+ asEntry( UNAPPROVED_READY, asMap(
+ asEntry( PARTIALLY_APPROVED_HERE, PARTIALLY_APPROVED_HERE ),
+ asEntry( APPROVED_HERE, PARTIALLY_APPROVED_HERE ),
+ asEntry( PARTIALLY_ACCEPTED_HERE, PARTIALLY_APPROVED_HERE ),
+ asEntry( ACCEPTED_HERE, PARTIALLY_APPROVED_HERE ) ) ),
+
+ //
+ // Data is approved for some but not all periods inside this longer period
+ // and is ready for approval in all periods inside this containing period.
+ //
+ asEntry( PARTIALLY_APPROVED_HERE, asMap(
+ asEntry( APPROVED_HERE, PARTIALLY_APPROVED_HERE ),
+ asEntry( PARTIALLY_ACCEPTED_HERE, PARTIALLY_APPROVED_HERE ),
+ asEntry( ACCEPTED_HERE, PARTIALLY_APPROVED_HERE ) ) ),
+
+ //
+ // Data is approved, and was approved here.
+ //
+ asEntry( APPROVED_HERE, asMap(
+ asEntry( PARTIALLY_ACCEPTED_HERE, PARTIALLY_ACCEPTED_HERE ),
+ asEntry( ACCEPTED_HERE, PARTIALLY_ACCEPTED_HERE ) ) ),
+
+ //
+ // Data is accepted for some but not all periods inside this longer period
+ // and is ready to be accepted in all periods inside this containing period.
+ //
+ asEntry( PARTIALLY_ACCEPTED_HERE, asMap(
+ asEntry( ACCEPTED_HERE, PARTIALLY_ACCEPTED_HERE ) ) )
+ );
+
+ /**
+ * Finds the next data approval state for the multi-period selection by
+ * considering the current aggregate state of all periods so far, and the
+ * state of a new, additional period.
+ * <p>
+ * Note that that arguments to this function have the commutative property.
+ * It is unimportant as to which is the current composite state and
+ * which is the new state for a period within the data selection.
+ *
+ * @param s1 current aggregate state (or new state)
+ * @param s2 new period state (or current state)
+ * @return the next current state
+ */
+ static DataApprovalState nextState( DataApprovalState s1, DataApprovalState s2 )
+ {
+ return firstNonNull(
+ transitionMap.getValue( s1, s2 ),
+ transitionMap.getValue( s2, s1 ),
+ s1,
+ s2 );
+ }
+
+ /**
+ * Returns the first non-null argument. This simulates a method found in
+ * org.apache.commons.lang3.ObjectUtils, and can be replaced some day
+ * by that or a comparable method.
+ *
+ * @param values values to check
+ * @param <T> type of items
+ * @return the first non-null item
+ */
+ private static <T> T firstNonNull(T ...values)
+ {
+ for ( T value : values )
+ {
+ if ( value != null )
+ {
+ return value;
+ }
+ }
+ return null;
+ }
+}
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalSelection.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalSelection.java 2014-05-04 21:36:18 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalSelection.java 2014-06-06 19:29:39 +0000
@@ -59,6 +59,7 @@
* between methods.
*
* @author Jim Grace
+ * @version $Id$
*/
class DataApprovalSelection
{
@@ -189,7 +190,7 @@
{
if ( period.getPeriodType().getFrequencyOrder() > dataSet.getPeriodType().getFrequencyOrder() )
{
- findStatusForLongerPeriodType();
+ findStatusForCompositePeriod();
}
else
{
@@ -250,19 +251,8 @@
* Handles the case where the selected period type is longer than the
* data set period type. The selected period is broken down into data
* set type periods. The approval status of the selected period is
- * constructed by logic that combines the approval statuses of the
- * constituent periods.
- * <p>
- * If the data is unapproved for any time segment, returns
- * UNAPPROVED_ELSEWHERE.
- * <p>
- * If the data is accepted for all time segments, returns
- * ACCEPTED_ELSEWHERE.
- * <p>
- * If the data is approved for all time segments (and maybe accepted for
- * some but not all), returns APPROVED_ELSEWHERE.
- * <p>
- * Note that the dataApproval object always returns null.
+ * constructed by state transition logic that combines the approval
+ * statuses of the constituent periods.
* <p>
* If data is accepted and/or approved in all time periods, the
* dataApprovalLevel object reference points to the lowest level of
@@ -272,8 +262,10 @@
*
* @return status status of the longer period
*/
- private void findStatusForLongerPeriodType()
+ private void findStatusForCompositePeriod()
{
+ Period longerPeriod = period;
+
Collection<Period> testPeriods = periodService.getPeriodsBetweenDates( dataSet.getPeriodType(), period.getStartDate(), period.getEndDate() );
DataApprovalLevel lowestApprovalLevel = null;
@@ -282,65 +274,44 @@
{
period = testPeriod;
- DataApprovalState s = getState();
+ state = DataApprovalPeriodAggregator.nextState( state, getState() );
- switch ( s )
+ switch ( state )
{
+ case PARTIALLY_APPROVED_HERE:
case APPROVED_HERE:
case APPROVED_ELSEWHERE:
-
- state = DataApprovalState.APPROVED_ELSEWHERE;
-
- dataApproval = null;
-
- if ( lowestApprovalLevel == null || dataApprovalLevel.getLevel() > lowestApprovalLevel.getLevel() )
- {
- lowestApprovalLevel = dataApprovalLevel;
- }
-
- break;
-
+ case PARTIALLY_ACCEPTED_HERE:
case ACCEPTED_HERE:
case ACCEPTED_ELSEWHERE:
-
- if ( state == null )
- {
- state = DataApprovalState.ACCEPTED_ELSEWHERE;
- }
-
- dataApproval = null;
-
- if ( lowestApprovalLevel == null || dataApprovalLevel.getLevel() > lowestApprovalLevel.getLevel() )
- {
- lowestApprovalLevel = dataApprovalLevel;
- }
-
- break;
-
case UNAPPROVED_READY:
+
+ if ( lowestApprovalLevel == null || ( dataApprovalLevel != null
+ && dataApprovalLevel.getLevel() > lowestApprovalLevel.getLevel() ) )
+ {
+ lowestApprovalLevel = dataApprovalLevel;
+ }
+
+ break;
+
case UNAPPROVED_WAITING:
case UNAPPROVED_ELSEWHERE:
-
- dataApproval = null;
- dataApprovalLevel = null;
-
- state = DataApprovalState.UNAPPROVED_ELSEWHERE;
-
- return;
-
case UNAPPROVABLE:
default: // (Not expected)
- state = s;
-
dataApproval = null;
dataApprovalLevel = null;
- return;
+ return; // No further state transitions are possible from these three states.
}
}
dataApprovalLevel = lowestApprovalLevel;
+ if ( dataApproval != null )
+ {
+ dataApproval = new DataApproval( dataApproval ); // (clone, so we don't modify a Hibernate object.)
+ dataApproval.setPeriod( longerPeriod );
+ }
}
/**
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java 2014-04-28 15:43:02 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java 2014-06-06 19:29:39 +0000
@@ -28,6 +28,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+import java.util.Collection;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
@@ -42,6 +43,7 @@
import org.hisp.dhis.organisationunit.OrganisationUnitService;
import org.hisp.dhis.period.Period;
import org.hisp.dhis.period.PeriodService;
+import org.hisp.dhis.period.PeriodType;
import org.hisp.dhis.security.SecurityService;
import org.hisp.dhis.user.CurrentUserService;
import org.hisp.dhis.user.User;
@@ -118,7 +120,22 @@
if ( ( dataApproval.getCategoryOptionGroup() == null || securityService.canRead( dataApproval.getCategoryOptionGroup() ) )
&& mayApprove( dataApproval.getOrganisationUnit() ) )
{
- dataApprovalStore.addDataApproval( dataApproval );
+ PeriodType selectionPeriodType = dataApproval.getPeriod().getPeriodType();
+ PeriodType dataSetPeriodType = dataApproval.getDataSet().getPeriodType();
+
+ if ( selectionPeriodType.equals( dataSetPeriodType ) )
+ {
+ dataApprovalStore.addDataApproval( dataApproval );
+ }
+ else if ( selectionPeriodType.getFrequencyOrder() <= dataSetPeriodType.getFrequencyOrder() )
+ {
+ log.warn( "Attempted data approval for period " + dataApproval.getPeriod().getIsoDate()
+ + " is incompatible with data set period type " + dataSetPeriodType.getName() + "." );
+ }
+ else
+ {
+ approveCompositePeriod( dataApproval );
+ }
}
else
{
@@ -131,18 +148,33 @@
if ( ( dataApproval.getCategoryOptionGroup() == null || securityService.canRead( dataApproval.getCategoryOptionGroup() ) )
&& mayUnapprove( dataApproval.getOrganisationUnit(), dataApproval.isAccepted() ) )
{
- dataApprovalStore.deleteDataApproval( dataApproval );
+ PeriodType selectionPeriodType = dataApproval.getPeriod().getPeriodType();
+ PeriodType dataSetPeriodType = dataApproval.getDataSet().getPeriodType();
- for ( OrganisationUnit ancestor : dataApproval.getOrganisationUnit().getAncestors() )
+ if ( selectionPeriodType.equals( dataSetPeriodType ) )
{
- DataApproval ancestorApproval = dataApprovalStore.getDataApproval(
- dataApproval.getDataSet(), dataApproval.getPeriod(), ancestor, dataApproval.getCategoryOptionGroup() );
+ dataApprovalStore.deleteDataApproval( dataApproval );
- if ( ancestorApproval != null )
+ for ( OrganisationUnit ancestor : dataApproval.getOrganisationUnit().getAncestors() )
{
- dataApprovalStore.deleteDataApproval ( ancestorApproval );
+ DataApproval ancestorApproval = dataApprovalStore.getDataApproval(
+ dataApproval.getDataSet(), dataApproval.getPeriod(), ancestor, dataApproval.getCategoryOptionGroup() );
+
+ if ( ancestorApproval != null )
+ {
+ dataApprovalStore.deleteDataApproval ( ancestorApproval );
+ }
}
}
+ else if ( selectionPeriodType.getFrequencyOrder() <= dataSetPeriodType.getFrequencyOrder() )
+ {
+ log.warn( "Attempted data unapproval for period " + dataApproval.getPeriod().getIsoDate()
+ + " is incompatible with data set period type " + dataSetPeriodType.getName() + "." );
+ }
+ else
+ {
+ unapproveCompositePeriod( dataApproval );
+ }
}
else
{
@@ -198,16 +230,18 @@
&& ( dataApprovalLevel.getCategoryOptionGroupSet() == null || securityService.canRead( dataApprovalLevel.getCategoryOptionGroupSet() ))
&& canReadOneCategoryOptionGroup( categoryOptionGroups ) )
{
- boolean accepted = false;
+ boolean unacceptPermissionNeededToUnapprove = false;
switch ( status.getDataApprovalState() )
{
+ case PARTIALLY_ACCEPTED_HERE:
+ case ACCEPTED_HERE:
+ unacceptPermissionNeededToUnapprove = true;
+ case PARTIALLY_APPROVED_HERE:
case APPROVED_HERE:
- case ACCEPTED_HERE:
- accepted = status.getDataApproval().isAccepted();
case UNAPPROVED_READY:
permissions.setMayApprove( mayApprove( organisationUnit ) );
- permissions.setMayUnapprove( mayUnapprove( organisationUnit, accepted ) );
+ permissions.setMayUnapprove( mayUnapprove( organisationUnit, unacceptPermissionNeededToUnapprove ) );
permissions.setMayAccept( mayAcceptOrUnaccept( organisationUnit ) );
permissions.setMayUnaccept( permissions.isMayAccept() );
break;
@@ -226,38 +260,12 @@
public void accept( DataApproval dataApproval )
{
- if ( ( dataApproval.getCategoryOptionGroup() == null || securityService.canRead( dataApproval.getCategoryOptionGroup() ) )
- && mayAcceptOrUnaccept( dataApproval.getOrganisationUnit() ) )
- {
- if ( !dataApproval.isAccepted() )
- {
- dataApproval.setAccepted( true );
-
- dataApprovalStore.updateDataApproval( dataApproval );
- }
- }
- else
- {
- warnNotPermitted( dataApproval, "accept", mayAcceptOrUnaccept( dataApproval.getOrganisationUnit() ) );
- }
+ acceptOrUnaccept( dataApproval, true );
}
public void unaccept( DataApproval dataApproval )
{
- if ( ( dataApproval.getCategoryOptionGroup() == null || securityService.canRead( dataApproval.getCategoryOptionGroup() ) )
- && mayAcceptOrUnaccept( dataApproval.getOrganisationUnit() ) )
- {
- if ( dataApproval.isAccepted() )
- {
- dataApproval.setAccepted( false );
-
- dataApprovalStore.updateDataApproval( dataApproval );
- }
- }
- else
- {
- warnNotPermitted( dataApproval, "unaccept", mayAcceptOrUnaccept( dataApproval.getOrganisationUnit() ) );
- }
+ acceptOrUnaccept( dataApproval, false );
}
// -------------------------------------------------------------------------
@@ -265,6 +273,128 @@
// -------------------------------------------------------------------------
/**
+ * Accept or unaccept a data approval.
+ *
+ * @param dataApproval the data approval object.
+ * @param accepted true to accept, false to unaccept.
+ */
+ public void acceptOrUnaccept ( DataApproval dataApproval, boolean accepted )
+ {
+ if ( ( dataApproval.getCategoryOptionGroup() == null || securityService.canRead( dataApproval.getCategoryOptionGroup() ) )
+ && mayAcceptOrUnaccept( dataApproval.getOrganisationUnit() ) )
+ {
+ PeriodType selectionPeriodType = dataApproval.getPeriod().getPeriodType();
+ PeriodType dataSetPeriodType = dataApproval.getDataSet().getPeriodType();
+
+ if ( selectionPeriodType.equals( dataSetPeriodType ) )
+ {
+ dataApproval.setAccepted( accepted );
+ dataApprovalStore.updateDataApproval( dataApproval );
+ } else if ( selectionPeriodType.getFrequencyOrder() <= dataSetPeriodType.getFrequencyOrder() )
+ {
+ log.warn( "Attempted data approval for period " + dataApproval.getPeriod().getIsoDate()
+ + " is incompatible with data set period type " + dataSetPeriodType.getName() + "." );
+ } else
+ {
+ acceptOrUnacceptCompositePeriod( dataApproval, accepted );
+ }
+ } else
+ {
+ warnNotPermitted( dataApproval, accepted ? "accept" : "unaccept", mayAcceptOrUnaccept( dataApproval.getOrganisationUnit() ) );
+ }
+ }
+
+ /**
+ * Approves data for a longer period that contains multiple data approval
+ * periods. When individual periods are already approved, no action is
+ * necessary. (It's possible that they could be accepted as well.)
+ *
+ * @param da data approval object describing the longer period.
+ */
+ private void approveCompositePeriod( DataApproval da )
+ {
+ Collection<Period> periods = periodService.getPeriodsBetweenDates(
+ da.getDataSet().getPeriodType(),
+ da.getPeriod().getStartDate(),
+ da.getPeriod().getEndDate() );
+
+ for ( Period period : periods )
+ {
+ DataApprovalStatus status = getDataApprovalStatus( da.getDataSet(), period, da.getOrganisationUnit(),
+ org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
+
+ if ( status.getDataApprovalState().isReady() && !status.getDataApprovalState().isApproved() )
+ {
+ DataApproval dataApproval = new DataApproval( da );
+ dataApproval.setPeriod( period );
+
+ dataApprovalStore.addDataApproval( dataApproval );
+ }
+ }
+ }
+
+ /**
+ * Unapproves data for a longer period that contains multiple data approval
+ * periods. When individual periods are already unapproved, no action is
+ * necessary.
+ * <p>
+ * Note that when we delete approval for a period, we also need to make
+ * sure that approval is removed for any ancestors at higher levels of
+ * approval. For this reason, we go back through the main deleteDataApproval
+ * method. (It won't call back here, becuase it's only for one period.)
+ *
+ * @param da data approval object describing the longer period.
+ */
+ void unapproveCompositePeriod( DataApproval da )
+ {
+ Collection<Period> periods = periodService.getPeriodsBetweenDates(
+ da.getDataSet().getPeriodType(),
+ da.getPeriod().getStartDate(),
+ da.getPeriod().getEndDate() );
+
+ for ( Period period : periods )
+ {
+ DataApprovalStatus status = getDataApprovalStatus( da.getDataSet(), period, da.getOrganisationUnit(),
+ org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
+
+ if ( status.getDataApprovalState().isApproved() )
+ {
+ deleteDataApproval( status.getDataApproval() );
+ }
+ }
+ }
+
+ /**
+ * Accepts or unaccepts data for a longer period that contains multiple
+ * data approval periods. When individual periods are already at the
+ * desired accptance state, no action is necessary.
+ *
+ * @param da data approval object describing the longer period.
+ * @param accepted true to accept, false to unaccept.
+ */
+ private void acceptOrUnacceptCompositePeriod( DataApproval da, boolean accepted )
+ {
+ Collection<Period> periods = periodService.getPeriodsBetweenDates(
+ da.getDataSet().getPeriodType(),
+ da.getPeriod().getStartDate(),
+ da.getPeriod().getEndDate() );
+
+ DataApprovalLevel lowestApprovalLevel = null;
+
+ for ( Period period : periods )
+ {
+ DataApprovalStatus status = getDataApprovalStatus( da.getDataSet(), period, da.getOrganisationUnit(),
+ org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
+
+ if ( status.getDataApprovalState().isApprovable() && status.getDataApprovalState().isAccepted() != accepted )
+ {
+ status.getDataApproval().setAccepted( accepted );
+ dataApprovalStore.updateDataApproval( status.getDataApproval() );
+ }
+ }
+ }
+
+ /**
* Return true if there are no category option groups, or if there is
* one and the user can read it.
*
@@ -342,14 +472,15 @@
* data set, and the user is not authorized to remove that approval as well.
*
* @param organisationUnit The data approval status to check for permission.
- * @param accepted Whether selection is accepted.
+ * @param unacceptPermissionNeededToUnapprove Whether *unaccept* permission
+ * is also needed to unapprove this data.
* @return true if the user may unapprove, otherwise false
*/
- private boolean mayUnapprove( OrganisationUnit organisationUnit, boolean accepted )
+ private boolean mayUnapprove( OrganisationUnit organisationUnit, boolean unacceptPermissionNeededToUnapprove )
{
if ( isAuthorizedToUnapprove( organisationUnit ) )
{
- if ( !accepted || mayAcceptOrUnaccept( organisationUnit ) )
+ if ( !unacceptPermissionNeededToUnapprove || mayAcceptOrUnaccept( organisationUnit ) )
{
log.debug( "mayUnapprove = true for organisation unit " + organisationUnit.getName() );
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java 2014-04-28 21:42:50 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java 2014-06-06 19:29:39 +0000
@@ -723,8 +723,8 @@
assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitD, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodB, organisationUnitA, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
- assertEquals( DataApprovalState.UNAPPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodC, organisationUnitA, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
- assertEquals( DataApprovalState.UNAPPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodC, organisationUnitD, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+ assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodC, organisationUnitA, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+ assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodC, organisationUnitD, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatus( dataSetA, periodD, organisationUnitD, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
}
=== modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CollectionUtils.java'
--- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CollectionUtils.java 2014-04-11 07:50:58 +0000
+++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/CollectionUtils.java 2014-06-06 19:29:39 +0000
@@ -28,9 +28,12 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+import java.util.AbstractMap;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
import org.hisp.dhis.system.util.functional.Function1;
@@ -95,4 +98,34 @@
return set;
}
+
+ /**
+ * Constructs a Map Entry (key, value). Used to construct a Map with asMap.
+ *
+ * @param key map entry key
+ * @param value map entry value
+ * @return entry with the key and value
+ */
+ public static <K, V> AbstractMap.SimpleEntry<K, V> asEntry( K key, V value )
+ {
+ return new AbstractMap.SimpleEntry<K, V>( key, value );
+ }
+
+ /**
+ * Constructs a Map from Entries, each containing a (key, value) pair.
+ *
+ * @param entries any number of (key, value) pairs
+ * @return Map of the entries
+ */
+ public static <K, V> Map<K, V> asMap( AbstractMap.SimpleEntry<K, V>... entries )
+ {
+ Map<K, V> map = new HashMap<K, V>();
+
+ for ( AbstractMap.SimpleEntry<K, V> entry : entries )
+ {
+ map.put( entry.getKey(), entry.getValue() );
+ }
+
+ return map;
+ }
}
=== modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java 2014-05-22 12:40:24 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java 2014-06-06 19:29:39 +0000
@@ -213,10 +213,13 @@
}
DataApprovalPermissions permissions = dataApprovalService.getDataApprovalPermissions( dataSet, period, organisationUnit, categoryOptionGroups, null );
-
- if ( !DataApprovalState.UNAPPROVED_READY.equals( permissions.getDataApprovalStatus().getDataApprovalState() ) )
+
+ DataApprovalState state = permissions.getDataApprovalStatus().getDataApprovalState();
+
+ if ( state != DataApprovalState.UNAPPROVED_READY &&
+ state != DataApprovalState.PARTIALLY_APPROVED_HERE )
{
- ContextUtils.conflictResponse( response, "Data is not ready for approval, current state is: " + permissions.getDataApprovalStatus().getDataApprovalState().name() );
+ ContextUtils.conflictResponse( response, "Data is not ready for approval here, current state is: " + state.name() );
return;
}
@@ -289,10 +292,12 @@
DataApprovalState state = permissions.getDataApprovalStatus().getDataApprovalState();
- if ( !DataApprovalState.APPROVED_HERE.equals( state )
- && !DataApprovalState.ACCEPTED_HERE.equals(state ) )
+ if ( state != DataApprovalState.APPROVED_HERE &&
+ state != DataApprovalState.ACCEPTED_HERE &&
+ state != DataApprovalState.PARTIALLY_APPROVED_HERE &&
+ state != DataApprovalState.PARTIALLY_ACCEPTED_HERE )
{
- ContextUtils.conflictResponse( response, "Data is not approved here and cannot be unapproved" );
+ ContextUtils.conflictResponse( response, "Data is not approved here, current state is: " + state.name() );
return;
}
@@ -355,9 +360,12 @@
DataApprovalPermissions permissions = dataApprovalService.getDataApprovalPermissions( dataSet, period, organisationUnit, categoryOptionGroups, null );
- if ( !DataApprovalState.APPROVED_HERE.equals( permissions.getDataApprovalStatus().getDataApprovalState() ) )
+ DataApprovalState state = permissions.getDataApprovalStatus().getDataApprovalState();
+
+ if ( state != DataApprovalState.APPROVED_HERE &&
+ state != DataApprovalState.PARTIALLY_ACCEPTED_HERE )
{
- ContextUtils.conflictResponse( response, "Data is not approved here, current state is: " + permissions.getDataApprovalStatus().getDataApprovalState().name() );
+ ContextUtils.conflictResponse( response, "Data is not ready for accepting here, current state is: " + state.name() );
return;
}
@@ -421,9 +429,12 @@
DataApprovalPermissions permissions = dataApprovalService.getDataApprovalPermissions( dataSet, period, organisationUnit, categoryOptionGroups, null );
- if ( !DataApprovalState.ACCEPTED_HERE.equals( permissions.getDataApprovalStatus().getDataApprovalState() ) )
+ DataApprovalState state = permissions.getDataApprovalStatus().getDataApprovalState();
+
+ if ( state != DataApprovalState.ACCEPTED_HERE &&
+ state != DataApprovalState.PARTIALLY_ACCEPTED_HERE )
{
- ContextUtils.conflictResponse( response, "Data is not approved here, current state is: " + permissions.getDataApprovalStatus().getDataApprovalState().name() );
+ ContextUtils.conflictResponse( response, "Data is not accepted here, current state is: " + state.name() );
return;
}
=== modified file 'dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetDataApprovalOptionsAction.java'
--- dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetDataApprovalOptionsAction.java 2014-04-02 12:33:53 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetDataApprovalOptionsAction.java 2014-06-06 19:29:39 +0000
@@ -37,12 +37,15 @@
import org.hisp.dhis.dataelement.DataElementCategoryService;
import org.hisp.dhis.dataset.DataSet;
import org.hisp.dhis.dataset.DataSetService;
+import org.hisp.dhis.period.PeriodType;
import org.hisp.dhis.system.util.Filter;
import org.hisp.dhis.system.util.FilterUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.opensymphony.xwork2.Action;
+import static org.hisp.dhis.period.PeriodType.getAvailablePeriodTypes;
+
public class GetDataApprovalOptionsAction
implements Action
{
@@ -69,7 +72,14 @@
{
return dataSets;
}
-
+
+ private List<PeriodType> periodTypes;
+
+ public List<PeriodType> getPeriodTypes()
+ {
+ return periodTypes;
+ }
+
// -------------------------------------------------------------------------
// Action implementation
// -------------------------------------------------------------------------
@@ -79,8 +89,9 @@
throws Exception
{
categoryOptionGroups = new ArrayList<CategoryOptionGroup>( categoryService.getAllCategoryOptionGroups() );
- dataSets = new ArrayList<DataSet>( dataSetService.getAllDataSets() );
-
+ dataSets = new ArrayList<DataSet>( dataSetService.getAllDataSets() );
+ periodTypes = getAvailablePeriodTypes();
+
FilterUtils.filter( dataSets, new DataSetApproveDataFilter() );
Collections.sort( categoryOptionGroups, IdentifiableObjectNameComparator.INSTANCE );
=== modified file 'dhis-2/dhis-web/dhis-web-reporting/src/main/resources/org/hisp/dhis/reporting/i18n_module.properties'
--- dhis-2/dhis-web/dhis-web-reporting/src/main/resources/org/hisp/dhis/reporting/i18n_module.properties 2014-05-26 10:46:17 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/resources/org/hisp/dhis/reporting/i18n_module.properties 2014-06-06 19:29:39 +0000
@@ -261,6 +261,10 @@
waiting_for_approval_elsewhere=Waiting for approval elsewhere
approved_elsewhere=Approved elsewhere
accepted_elsewhere=Accepted elsewhere
+approved_for_part_of_this_period=Approved for part of this period
+accepted_for_part_of_this_period=Accepted for part of this period
+approved_elsewhere_for_part_of_this_period=Approved elsewhere for part of this period
+accepted_elsewhere_for_part_of_this_period=Accepted elsewhere for part of this period
confirm_approval=Are you sure you want to approve this data set?
confirm_unapproval=Are you sure you want to unapprove this data set?
confirm_accept=Are you sure you want to accept this data set approval?
=== modified file 'dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/dataApprovalForm.vm'
--- dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/dataApprovalForm.vm 2014-05-26 10:46:17 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/dataApprovalForm.vm 2014-06-06 19:29:39 +0000
@@ -1,8 +1,18 @@
<script type="text/javascript">
+dhis2.appr.metaData = {
+ "periodTypes": [
+ #set( $size1 = $periodTypes.size() )
+ #foreach( $type in $periodTypes )
+ "${type.name}"#if( $velocityCount < $size1 ),#end
+ #end
+ ]
+};
+
selection.setListenerFunction( dhis2.appr.orgUnitSelected );
var i18n_select_data_set = '$encoder.jsEscape( $i18n.getString( "select_data_set" ), "'")';
+var i18n_select_period_type = '$encoder.jsEscape( $i18n.getString( "select_period_type" ), "'")';
var i18n_select_period = '$encoder.jsEscape( $i18n.getString( "select_period" ), "'")';
var i18n_select_organisation_unit = '$encoder.jsEscape( $i18n.getString( "select_organisation_unit" ), "'")';
var i18n_generating_report = '$encoder.jsEscape( $i18n.getString( "generating_report" ), "'")';
@@ -15,6 +25,10 @@
var i18n_approved_elsewhere = '$encoder.jsEscape( $i18n.getString( "approved_elsewhere" ) , "'")';
var i18n_accepted_elsewhere = '$encoder.jsEscape( $i18n.getString( "accepted_elsewhere" ) , "'")';
var i18n_approved_and_accepted = '$encoder.jsEscape( $i18n.getString( "approved_and_accepted" ) , "'")';
+var i18n_approved_for_part_of_this_period = '$encoder.jsEscape( $i18n.getString( "approved_for_part_of_this_period" ) , "'")';
+var i18n_accepted_for_part_of_this_period = '$encoder.jsEscape( $i18n.getString( "accepted_for_part_of_this_period" ) , "'")';
+var i18n_approved_elsewhere_for_part_of_this_period = '$encoder.jsEscape( $i18n.getString( "approved_elsewhere_for_part_of_this_period" ) , "'")';
+var i18n_accepted_elsewhere_for_part_of_this_period = '$encoder.jsEscape( $i18n.getString( "accepted_elsewhere_for_part_of_this_period" ) , "'")';
var i18n_confirm_approval = '$encoder.jsEscape( $i18n.getString( "confirm_approval" ) , "'")';
var i18n_confirm_unapproval = '$encoder.jsEscape( $i18n.getString( "confirm_unapproval" ) , "'")';
var i18n_confirm_accept = '$encoder.jsEscape( $i18n.getString( "confirm_accept" ) , "'")';
@@ -55,7 +69,7 @@
<input type="button" id="approveButton" value="$i18n.getString( 'approve' )" onclick="dhis2.appr.approveData()" class="approveButton" style="width:120px">
<input type="button" id="unapproveButton" value="$i18n.getString( 'unapprove' )" onclick="dhis2.appr.unapproveData()" class="approveButton" style="width:120px">
<input type="button" id="acceptButton" value="$i18n.getString( 'accept' )" onclick="dhis2.appr.acceptData()" class="approveButton" style="width:120px">
-<input type="button" id="unacceptButton" value="$i18n.getString( 'unaccept' )" onclick="dhis2.appr.unacceptData()" class="approveButton" style="width:120px">
+<input type="button" id="unacceptButton" value="$i18n.getString( 'unaccept' )" onclick="dhis2.appr.unacceptData()" class="approveButton" style="width:120px">
</div>
<div id="criteria" class="inputCriteria" style="width:360px;">
@@ -78,6 +92,7 @@
<div class="inputSection">
<label>$i18n.getString( "report_period" )</label><br>
+<select id="periodType" name="periodType" style="width:174px" disabled="disabled" onchange="dhis2.appr.displayPeriods()"></select>
<input type="button" style="width:75px" value="$i18n.getString( 'prev_year' )" onclick="dhis2.appr.displayPreviousPeriods()" />
<input type="button" style="width:75px" value="$i18n.getString( 'next_year' )" onclick="dhis2.appr.displayNextPeriods()" /><br>
<select id="periodId" name="periodId" style="width:330px" disabled="disabled">
=== modified file 'dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/javascript/dataApproval.js'
--- dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/javascript/dataApproval.js 2014-05-26 10:46:17 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/javascript/dataApproval.js 2014-06-06 19:29:39 +0000
@@ -12,9 +12,33 @@
// Report
//------------------------------------------------------------------------------
+/**
+ * Callback for changes in data set. Displays a list of period types starting
+ * with the data set's period as the shortest type, and including all longer
+ * types so that approvals can be made for multiple periods. If there is a
+ * current period type selection, and it is still on the new list of period
+ * types, keep it. Otherwise choose the period type for the data set.
+ */
dhis2.appr.dataSetSelected = function()
{
- dhis2.appr.displayPeriods();
+ var dataSetPeriodType = $( "#dataSetId :selected" ).data( "pt" );
+ var periodTypeToSelect = $( "#periodType" ).val() || dataSetPeriodType;
+ var foundDataSetPeriodType = false;
+ var html = "<option value=''>[ " + i18n_select_period_type + " ]</option>";
+
+ $.each( dhis2.appr.metaData.periodTypes, function() {
+ if ( foundDataSetPeriodType || this == dataSetPeriodType ) {
+ var selected = ( this == periodTypeToSelect ) ? " selected" : "";
+ html += "<option value='" + this + "'" + selected + ">" + this + "</option>";
+ foundDataSetPeriodType = true;
+ } else if ( this == periodTypeToSelect ) {
+ periodTypeToSelect = dataSetPeriodType;
+ }
+ } );
+
+ $( "#periodType" ).html( html );
+ $( "#periodType" ).removeAttr( "disabled" );
+ dhis2.appr.displayPeriods();
}
dhis2.appr.orgUnitSelected = function( orgUnits, orgUnitNames, children )
@@ -24,8 +48,8 @@
dhis2.appr.displayPeriods = function()
{
- var pt = $( '#dataSetId :selected' ).data( "pt" );
- dhis2.dsr.displayPeriodsInternal( pt, dhis2.appr.currentPeriodOffset );
+ var periodType = $( "#periodType" ).val();
+ dhis2.dsr.displayPeriodsInternal( periodType, dhis2.appr.currentPeriodOffset );
}
dhis2.appr.displayNextPeriods = function()
@@ -164,7 +188,22 @@
}
break;
-
+
+ case "PARTIALLY_APPROVED_HERE":
+ $( "#approvalNotification" ).html( i18n_approved_for_part_of_this_period );
+
+ if ( json.mayApprove ) {
+ $( "#approvalDiv" ).show();
+ $( "#approveButton" ).show();
+ }
+
+ if ( json.mayUnapprove ) {
+ $( "#approvalDiv" ).show();
+ $( "#unapproveButton" ).show();
+ }
+
+ break;
+
case "APPROVED_HERE":
$( "#approvalNotification" ).html( i18n_approved );
@@ -179,11 +218,35 @@
}
break;
-
+
+ case "PARTIALLY_APPROVED_ELSEWHERE":
+ $( "#approvalNotification" ).html( i18n_approved_elsewhere_for_part_of_this_period );
+ break;
+
case "APPROVED_ELSEWHERE":
$( "#approvalNotification" ).html( i18n_approved_elsewhere );
break;
-
+
+ case "PARTIALLY_ACCEPTED_HERE":
+ $( "#approvalNotification" ).html( i18n_accepted_for_part_of_this_period );
+
+ if ( json.mayUnapprove ) {
+ $( "#approvalDiv" ).show();
+ $( "#unapproveButton" ).show();
+ }
+
+ if ( json.mayAccept ) {
+ $( "#approvalDiv" ).show();
+ $( "#acceptButton" ).show();
+ }
+
+ if ( json.mayUnaccept ) {
+ $( "#approvalDiv" ).show();
+ $( "#unacceptButton" ).show();
+ }
+
+ break;
+
case "ACCEPTED_HERE":
$( "#approvalNotification" ).html( i18n_approved_and_accepted );
@@ -192,13 +255,17 @@
$( "#unapproveButton" ).show();
}
- if ( json.mayUnccept ) {
+ if ( json.mayUnaccept ) {
$( "#approvalDiv" ).show();
$( "#unacceptButton" ).show();
}
break;
+ case "PARTIALLY_ACCEPTED_ELSEWHERE":
+ $( "#approvalNotification" ).html( i18n_accepted_elsewhere_for_part_of_this_period );
+ break;
+
case "ACCEPTED_ELSEWHERE":
$( "#approvalNotification" ).html( i18n_accepted_elsewhere );
break;
@@ -217,18 +284,8 @@
url: dhis2.appr.getApprovalUrl(),
type: "post",
success: function() {
- $( "#approvalNotification" ).show().html( i18n_approved );
- $( "#approvalDiv" ).hide();
- $( "#approveButton" ).hide();
- if ( dhis2.appr.permissions.mayUnapprove ) {
- $( "#approvalDiv" ).show();
- $( "#unapproveButton" ).show();
- }
- if ( dhis2.appr.permissions.mayAccept ) {
- $( "#approvalDiv" ).show();
- $( "#acceptButton" ).show();
- }
- },
+ dhis2.appr.setApprovalState();
+ },
error: function( xhr, status, error ) {
alert( xhr.responseText );
}
@@ -245,17 +302,8 @@
url: dhis2.appr.getApprovalUrl(),
type: "delete",
success: function() {
- $( "#approvalNotification" ).show().html( i18n_ready_for_approval );
- $( "#approvalDiv" ).hide();
- $( "#unapproveButton" ).hide();
- $( "#acceptButton" ).hide();
- $( "#unacceptButton" ).hide();
-
- if ( dhis2.appr.permissions.mayApprove ) {
- $( "#approvalDiv" ).show();
- $( "#approveButton" ).show();
- }
- },
+ dhis2.appr.setApprovalState();
+ },
error: function( xhr, status, error ) {
alert( xhr.responseText );
}
@@ -272,19 +320,7 @@
url: dhis2.appr.getAcceptanceUrl(),
type: "post",
success: function() {
- $( "#approvalNotification" ).show().html( i18n_approved_and_accepted );
- $( "#approvalDiv" ).hide();
- $( "#acceptButton" ).hide();
-
- if ( dhis2.appr.permissions.mayUnapprove ) {
- $( "#approvalDiv" ).show();
- $( "#unapproveButton" ).show();
- }
-
- if ( dhis2.appr.permissions.mayUnaccept ) {
- $( "#approvalDiv" ).show();
- $( "#unacceptButton" ).show();
- }
+ dhis2.appr.setApprovalState();
},
error: function( xhr, status, error ) {
alert( xhr.responseText );
@@ -302,17 +338,7 @@
url: dhis2.appr.getAcceptanceUrl(),
type: "delete",
success: function() {
- $( "#approvalNotification" ).show().html( i18n_approved );
- $( "#approvalDiv" ).hide();
- $( "#unacceptButton" ).hide();
- if ( dhis2.appr.permissions.mayUnapprove ) {
- $( "#approvalDiv" ).show();
- $( "#unapproveButton" ).show();
- }
- if ( dhis2.appr.permissions.mayAccept ) {
- $( "#approvalDiv" ).show();
- $( "#acceptButton" ).show();
- }
+ dhis2.appr.setApprovalState();
},
error: function( xhr, status, error ) {
alert( xhr.responseText );