← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 15613: Link (attribute) categoy option to organisation unit (useful in approvals)

 

Merge authors:
  Jim Grace (jimgrace)
------------------------------------------------------------
revno: 15613 [merge]
committer: jimgrace@xxxxxxxxx
branch nick: dhis2
timestamp: Tue 2014-06-10 23:27:50 -0400
message:
  Link (attribute) categoy option to organisation unit (useful in approvals)
added:
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/ShowAddDataElementCategoryOptionAction.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryOption.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/main/resources/org/hisp/dhis/dataelement/hibernate/DataElementCategoryOption.hbm.xml
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java
  dhis-2/dhis-web/dhis-web-dataentry/src/main/webapp/dhis-web-dataentry/javascript/form.js
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryOptionAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/GetDataElementCategoryOptionAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/UpdateDataElementCategoryOptionAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/struts.xml
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/javascript/categoryOption.js
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/addDataElementCategoryOptionForm.vm
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/categoryOption.vm
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryOptionForm.vm
  dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetCategoryOptionGroupsAction.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/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/dataelement/DataElementCategoryOption.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryOption.java	2014-06-02 15:29:13 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryOption.java	2014-06-10 20:46:05 +0000
@@ -45,6 +45,8 @@
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+import org.hisp.dhis.common.view.ExportView;
+import org.hisp.dhis.organisationunit.OrganisationUnit;
 
 /**
  * @author Abyot Asalefew
@@ -64,6 +66,9 @@
 
     private Date endDate;
 
+    @Scanned
+    private Set<OrganisationUnit> organisationUnits = new HashSet<OrganisationUnit>();
+
     private Set<DataElementCategory> categories = new HashSet<DataElementCategory>();
 
     @Scanned
@@ -181,6 +186,21 @@
     }
 
     @JsonProperty
+    @JsonSerialize( contentAs = BaseIdentifiableObject.class )
+    @JsonView( { DetailedView.class, ExportView.class } )
+    @JacksonXmlElementWrapper( localName = "organisationUnits", namespace = DxfNamespaces.DXF_2_0 )
+    @JacksonXmlProperty( localName = "organisationUnit", namespace = DxfNamespaces.DXF_2_0 )
+    public Set<OrganisationUnit> getOrganisationUnits()
+    {
+        return organisationUnits;
+    }
+
+    public void setOrganisationUnits( Set<OrganisationUnit> organisationUnits )
+    {
+        this.organisationUnits = organisationUnits;
+    }
+
+    @JsonProperty
     @JsonSerialize(contentAs = BaseIdentifiableObject.class)
     @JsonView({ DetailedView.class })
     @JacksonXmlElementWrapper(localName = "categories", namespace = DxfNamespaces.DXF_2_0)

=== 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-06-06 19:29:39 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalSelection.java	2014-06-10 20:46:05 +0000
@@ -103,6 +103,8 @@
 
     private Map<CategoryOptionGroupSet, Set<CategoryOptionGroup>> selectionGroups = null;
 
+    private Set<CategoryOptionGroup> allSelectionGroups = new HashSet<CategoryOptionGroup>();
+
     private List<DataApprovalLevel> allApprovalLevels;
 
     private List<Set<CategoryOptionGroup>> categoryOptionGroupsByLevel;
@@ -498,7 +500,7 @@
     }
 
     /**
-     * Adds a category option group set and associated category option group
+     * Adds a category option group set and associated category option groups
      * to the set of these pairs referenced by the selected data.
      *
      * @param groupSet category option group set to add
@@ -516,6 +518,8 @@
         }
 
         groups.add( group );
+
+        allSelectionGroups.add( group );
     }
 
     /**
@@ -696,16 +700,7 @@
     {
         DataApproval da = null;
 
-        Set<CategoryOptionGroup> groups = null;
-
-        if ( index < lowerIndex )
-        {
-            groups = categoryOptionGroupsByLevel.get( index );
-        }
-        else if ( allApprovalLevels.get( index ).getCategoryOptionGroupSet() != null )
-        {
-            groups = new HashSet<CategoryOptionGroup>( allApprovalLevels.get( index ).getCategoryOptionGroupSet().getMembers() );
-        }
+        Set<CategoryOptionGroup> groups = categoryOptionGroupsByLevel.get( index );
 
         if ( groups == null || groups.isEmpty() )
         {
@@ -722,16 +717,9 @@
 
             log.debug( "getDataApproval( " + orgUnit.getName() + " ) = " + ( da != null ) + " (group: " + group.getName() + ")" );
 
-            if ( index < lowerIndex )
-            {
-                if ( da != null )
-                {
-                    return da;
-                }
-            }
-            else if ( da == null )
-            {
-                return null;
+            if ( da != null )
+            {
+                return da;
             }
         }
 
@@ -772,11 +760,24 @@
             {
                 log.debug( "isUnapprovedBelow() orgUnit level " + orgUnitLevel + " matches approval level." );
 
-                DataApproval da = getDataApproval( lowerIndex, orgUnit );
-
-                log.debug( "isUnapprovedBelow() returns " + ( da == null ) + " after looking for approval for this orgUnit." );
-
-                return ( da == null );
+                CategoryOptionGroupSet cogs = allApprovalLevels.get( lowerIndex ).getCategoryOptionGroupSet();
+
+                if ( cogs == null )
+                {
+                    DataApproval da = dataApprovalStore.getDataApproval( dataSet, period, orgUnit, null );
+
+                    log.debug( "isUnapprovedBelow() returns " + ( da == null ) + " after looking for approval for this orgUnit." );
+
+                    return ( da == null );
+                }
+                else
+                {
+                    boolean isUnapproved = isGroupSetUnapprovedBelow( orgUnit, cogs );
+
+                    log.debug( "isUnapprovedBelow() returns " + ( isUnapproved ) + " from group set." );
+
+                    return isUnapproved;
+                }
             }
         }
         else if ( dataSetAssignedAtOrBelowLevel )
@@ -809,4 +810,77 @@
 
         return false;
     }
+
+    /**
+     * Tests to see if any required category option group is unapproved below
+     * this level.
+     * <p>
+     * A category option group is unapproved if *all* of the following are true:
+     * <ul>
+     * <li>There is no approval object for this group within the selection.</li>
+     * <li>The group has category options that apply to this period and org unit.</li>
+     * <li>At least one of these options apply to the category option group
+     * (if any) at this level</li>
+     * </ul>
+     *
+     * @param orgUnit organisation unit to test.
+     * @param cogs category option group set containing category option
+     *             groups to test.
+     * @return true if some category option group is unapproved, else false.
+     */
+    private boolean isGroupSetUnapprovedBelow ( OrganisationUnit orgUnit, CategoryOptionGroupSet cogs )
+    {
+        Set<DataElementCategoryOption> selectedGroupsOptions = new HashSet<DataElementCategoryOption>();
+
+        for ( CategoryOptionGroup group : allSelectionGroups )
+        {
+            selectedGroupsOptions.addAll( group.getMembers() );
+        }
+
+        for ( CategoryOptionGroup group : cogs.getMembers() )
+        {
+            DataApproval da = dataApprovalStore.getDataApproval( dataSet, period, orgUnit, group );
+
+            if ( da == null )
+            {
+                for ( DataElementCategoryOption option : group.getMembers() )
+                {
+                    if ( selectedGroupsOptions.isEmpty() || selectedGroupsOptions.contains( option ) )
+                    {
+                        if ( ( option.getStartDate() == null || option.getStartDate().before( period.getEndDate() ) )
+                                && ( option.getEndDate() == null || option.getEndDate().after( period.getStartDate() ) ) )
+                        {
+                            if ( option.getOrganisationUnits().isEmpty() || orgUnit.isEqualOrChildOf( option.getOrganisationUnits() ) )
+                            {
+                                log.debug( "isGroupSetUnapprovedBelow( " + orgUnit.getName() + ", " + cogs.getName() + " ) returns true for group " + group.getName() + ", option " + option.getName() );
+
+                                return true;
+                            }
+                            else
+                            {
+                                log.debug( "isGroupSetUnapprovedBelow( " + orgUnit.getName() + ", " + cogs.getName() + " ) group " + group.getName() + ", option " + option.getName()
+                                        + " org unit " + option.getOrganisationUnits().iterator().next().getName() + " not valid in org unit " + orgUnit.getName() );
+                            }
+                        }
+                        else
+                        {
+                            log.debug( "isGroupSetUnapprovedBelow( " + orgUnit.getName() + ", " + cogs.getName() + " ) group " + group.getName() + ", option " + option.getName() + " not valid in period " + period.getName() );
+                        }
+                    }
+                    else
+                    {
+                        log.debug( "isGroupSetUnapprovedBelow( " + orgUnit.getName() + ", " + cogs.getName() + " ) selectedGroupsOptions does not contain option " + option.getName() );
+                    }
+                }
+            }
+            else
+            {
+                log.debug( "isGroupSetUnapprovedBelow( " + orgUnit.getName() + ", " + cogs.getName() + " ) found approval for group " + group.getName() );
+            }
+        }
+
+        log.debug( "isGroupSetUnapprovedBelow( " + orgUnit.getName() + ", " + cogs.getName() + " ) returns false" );
+
+        return false;
+    }
 }

=== 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-06-06 19:29:39 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java	2014-06-10 20:46:05 +0000
@@ -321,7 +321,7 @@
         for ( Period period : periods )
         {
             DataApprovalStatus status = getDataApprovalStatus( da.getDataSet(), period, da.getOrganisationUnit(),
-                    org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
+                    da.getCategoryOptionGroup() == null ? null : org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
 
             if ( status.getDataApprovalState().isReady() && !status.getDataApprovalState().isApproved() )
             {
@@ -355,7 +355,7 @@
         for ( Period period : periods )
         {
             DataApprovalStatus status = getDataApprovalStatus( da.getDataSet(), period, da.getOrganisationUnit(),
-                    org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
+                    da.getCategoryOptionGroup() == null ? null : org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
 
             if ( status.getDataApprovalState().isApproved() )
             {
@@ -384,7 +384,7 @@
         for ( Period period : periods )
         {
             DataApprovalStatus status = getDataApprovalStatus( da.getDataSet(), period, da.getOrganisationUnit(),
-                    org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
+                    da.getCategoryOptionGroup() == null ? null : org.hisp.dhis.system.util.CollectionUtils.asSet( da.getCategoryOptionGroup() ), null );
 
             if ( status.getDataApprovalState().isApprovable() && status.getDataApprovalState().isAccepted() != accepted )
             {

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/dataelement/hibernate/DataElementCategoryOption.hbm.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/dataelement/hibernate/DataElementCategoryOption.hbm.xml	2014-06-02 15:29:13 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/dataelement/hibernate/DataElementCategoryOption.hbm.xml	2014-06-10 20:46:05 +0000
@@ -21,6 +21,13 @@
 
     <property name="endDate" type="timestamp" />
 
+    <set name="organisationUnits" table="cateogryoption_organisationunits">
+      <cache usage="read-write" />
+      <key column="categoryoptionid" foreign-key="fk_cateogryoption_organisationunits_datasetid" />
+      <many-to-many column="organisationunitid" class="org.hisp.dhis.organisationunit.OrganisationUnit"
+        foreign-key="fk_cateogryoption_organisationunits_organisationunit" />
+    </set>
+
     <set name="categoryOptionCombos" table="categoryoptioncombos_categoryoptions" inverse="true">
       <cache usage="read-write" />
       <key column="categoryoptionid" foreign-key="fk_categoryoptioncombos_categoryoptions_categoryoptionid" />

=== 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-06-06 19:29:39 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java	2014-06-10 20:46:05 +0000
@@ -61,6 +61,8 @@
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import static org.hisp.dhis.system.util.CollectionUtils.*;
+
 /**
  * @author Jim Grace
  */
@@ -114,6 +116,14 @@
 
     private Period periodD;
 
+    private Period periodJan;
+
+    private Period periodFeb;
+
+    private Period periodMar;
+
+    private Period periodQ1;
+
     private OrganisationUnit organisationUnitA;
 
     private OrganisationUnit organisationUnitB;
@@ -140,6 +150,8 @@
 
     private DataApprovalLevel level2ABCD;
 
+    private DataApprovalLevel level3ABCD;
+
     private User userA;
 
     private User userB;
@@ -237,11 +249,21 @@
         periodC = createPeriod( PeriodType.getPeriodTypeByName( "Yearly" ), getDay( 1 ), getDay( 365 ) );
         periodD = createPeriod( PeriodType.getPeriodTypeByName( "Weekly" ), getDay( 1 ), getDay( 365 ) );
 
+        periodJan = createPeriod( "201401" );
+        periodFeb = createPeriod( "201402" );
+        periodMar = createPeriod( "201403" );
+        periodQ1 = createPeriod( "2014Q1" );
+
         periodService.addPeriod( periodA );
         periodService.addPeriod( periodB );
         periodService.addPeriod( periodC );
         periodService.addPeriod( periodD );
 
+        periodService.addPeriod( periodJan );
+        periodService.addPeriod( periodFeb );
+        periodService.addPeriod( periodMar );
+        periodService.addPeriod( periodQ1 );
+
         //
         // Organisation unit hierarchy:
         //
@@ -379,6 +401,7 @@
         level1ABCD = new DataApprovalLevel( "level1ABCD", 1, groupSetABCD );
         level1EFGH = new DataApprovalLevel( "level1EFGH", 1, groupSetEFGH );
         level2ABCD = new DataApprovalLevel( "level2ABCD", 2, groupSetABCD );
+        level3ABCD = new DataApprovalLevel( "level3ABCD", 3, groupSetABCD );
     }
 
     // -------------------------------------------------------------------------
@@ -1065,6 +1088,67 @@
     }
 
     // ---------------------------------------------------------------------
+    // Test multi-period approval
+    // ---------------------------------------------------------------------
+
+    @Test
+    public void testMultiPeriodApproval() throws Exception
+    {
+        dataApprovalLevelService.addDataApprovalLevel( level2 );
+
+        dataSetA.setApproveData( true );
+
+        organisationUnitB.addDataSet( dataSetA );
+
+        Date date = new Date();
+
+        Set<OrganisationUnit> units = new HashSet<OrganisationUnit>();
+        units.add( organisationUnitA );
+        CurrentUserService currentUserService = new MockCurrentUserService( units, null, AUTH_APPR_LEVEL, DataApproval.AUTH_APPROVE, DataApproval.AUTH_APPROVE_LOWER_LEVELS, DataApproval.AUTH_ACCEPT_LOWER_LEVELS );
+        setDependency( dataApprovalService, "currentUserService", currentUserService, CurrentUserService.class );
+
+        DataApproval dataApprovalA = new DataApproval( level2, dataSetA, periodJan, organisationUnitB, NO_GROUP, NOT_ACCEPTED, date, userA );
+
+        dataApprovalService.addDataApproval( dataApprovalA );
+        dataApprovalService.accept( dataApprovalA );
+
+        assertEquals( DataApprovalState.ACCEPTED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodJan, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodFeb, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodMar, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.PARTIALLY_APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodQ1, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        DataApproval dataApprovalB = new DataApproval( level2, dataSetA, periodQ1, organisationUnitB, NO_GROUP, NOT_ACCEPTED, date, userA );
+
+        dataApprovalService.addDataApproval( dataApprovalB );
+
+        assertEquals( DataApprovalState.ACCEPTED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodJan, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodFeb, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodMar, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.PARTIALLY_ACCEPTED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodQ1, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.accept( dataApprovalB );
+
+        assertEquals( DataApprovalState.ACCEPTED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodJan, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.ACCEPTED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodFeb, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.ACCEPTED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodMar, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.ACCEPTED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodQ1, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.unaccept( dataApprovalB );
+
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodJan, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodFeb, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodMar, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodQ1, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.deleteDataApproval( dataApprovalB );
+
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodJan, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodFeb, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodMar, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodQ1, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+    }
+
+    // ---------------------------------------------------------------------
     // Test with Categories
     // ---------------------------------------------------------------------
 
@@ -1248,14 +1332,14 @@
 
         assertEquals( DataApprovalState.UNAPPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
         assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupASet, NO_OPTIONS ).getDataApprovalState() );
-        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupBSet, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupBSet, NO_OPTIONS ).getDataApprovalState() );
         assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupCSet, NO_OPTIONS ).getDataApprovalState() );
         assertEquals( DataApprovalState.APPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupXSet, NO_OPTIONS ).getDataApprovalState() );
         assertEquals( DataApprovalState.APPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, optionComboA ).getDataApprovalState() );
 
         assertEquals( null, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, NO_GROUPS, NO_OPTIONS ).getDataApprovalLevel() );
         assertEquals( level1ABCD, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupASet, NO_OPTIONS ).getDataApprovalLevel() );
-        assertEquals( null, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupBSet, NO_OPTIONS ).getDataApprovalLevel() );
+        assertEquals( level1ABCD, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupBSet, NO_OPTIONS ).getDataApprovalLevel() );
         assertEquals( level1EFGH, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupCSet, NO_OPTIONS ).getDataApprovalLevel() );
         assertEquals( level1ABCD, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, groupXSet, NO_OPTIONS ).getDataApprovalLevel() );
         assertEquals( level1ABCD, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, optionComboA ).getDataApprovalLevel() );
@@ -1305,4 +1389,144 @@
         assertEquals( level1, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, groupXSet, NO_OPTIONS ).getDataApprovalLevel() );
         assertEquals( level1, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, optionComboA ).getDataApprovalLevel() );
     }
+
+    @Test
+    public void testCategoriesWithOrgUnits_2Levels() throws Exception
+    {
+        setUpCategories();
+
+        dataApprovalLevelService.addDataApprovalLevel( level2 );
+        dataApprovalLevelService.addDataApprovalLevel( level3ABCD );
+
+        dataSetA.setApproveData( true );
+
+        organisationUnitC.addDataSet( dataSetA );
+        organisationUnitE.addDataSet( dataSetA );
+
+        Date date = new Date();
+
+        Set<OrganisationUnit> units = asSet( organisationUnitA );
+        CurrentUserService currentUserService = new MockCurrentUserService( units, null, AUTH_APPR_LEVEL, DataApproval.AUTH_APPROVE, DataApproval.AUTH_APPROVE_LOWER_LEVELS, DataApproval.AUTH_ACCEPT_LOWER_LEVELS );
+        setDependency( dataApprovalService, "currentUserService", currentUserService, CurrentUserService.class );
+
+        optionA.setOrganisationUnits( asSet( organisationUnitC ) );
+        optionB.setOrganisationUnits( asSet( organisationUnitE ) );
+        optionC.setOrganisationUnits( asSet( organisationUnitE ) );
+        optionD.setOrganisationUnits( asSet( organisationUnitE ) );
+
+        categoryService.updateDataElementCategoryOption( optionA );
+        categoryService.updateDataElementCategoryOption( optionB );
+        categoryService.updateDataElementCategoryOption( optionC );
+        categoryService.updateDataElementCategoryOption( optionD );
+
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.addDataApproval( new DataApproval( level3ABCD, dataSetA, periodA, organisationUnitC, groupAB, NOT_ACCEPTED, date, userA ) );
+
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.addDataApproval( new DataApproval( level3ABCD, dataSetA, periodA, organisationUnitE, groupAB, NOT_ACCEPTED, date, userA ) );
+
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.addDataApproval( new DataApproval( level3ABCD, dataSetA, periodA, organisationUnitE, groupCD, NOT_ACCEPTED, date, userA ) );
+
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+    }
+
+    @Test
+    public void testCategoriesWithOrgUnits_3Levels() throws Exception
+    {
+        setUpCategories();
+
+        dataApprovalLevelService.addDataApprovalLevel( level2 );
+        dataApprovalLevelService.addDataApprovalLevel( level3 );
+        dataApprovalLevelService.addDataApprovalLevel( level3ABCD );
+
+        dataSetA.setApproveData( true );
+
+        organisationUnitC.addDataSet( dataSetA );
+        organisationUnitE.addDataSet( dataSetA );
+
+        Date date = new Date();
+
+        Set<OrganisationUnit> units = asSet( organisationUnitA );
+        CurrentUserService currentUserService = new MockCurrentUserService( units, null, AUTH_APPR_LEVEL, DataApproval.AUTH_APPROVE, DataApproval.AUTH_APPROVE_LOWER_LEVELS, DataApproval.AUTH_ACCEPT_LOWER_LEVELS );
+        setDependency( dataApprovalService, "currentUserService", currentUserService, CurrentUserService.class );
+
+        optionA.setOrganisationUnits( asSet( organisationUnitC ) );
+        optionB.setOrganisationUnits( asSet( organisationUnitE ) );
+        optionC.setOrganisationUnits( asSet( organisationUnitE ) );
+        optionD.setOrganisationUnits( asSet( organisationUnitE ) );
+
+        categoryService.updateDataElementCategoryOption( optionA );
+        categoryService.updateDataElementCategoryOption( optionB );
+        categoryService.updateDataElementCategoryOption( optionC );
+        categoryService.updateDataElementCategoryOption( optionD );
+
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.addDataApproval( new DataApproval( level3ABCD, dataSetA, periodA, organisationUnitC, groupAB, NOT_ACCEPTED, date, userA ) );
+
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.addDataApproval( new DataApproval( level3ABCD, dataSetA, periodA, organisationUnitE, groupAB, NOT_ACCEPTED, date, userA ) );
+
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.addDataApproval( new DataApproval( level3ABCD, dataSetA, periodA, organisationUnitE, groupCD, NOT_ACCEPTED, date, userA ) );
+
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+
+        dataApprovalService.addDataApproval( new DataApproval( level3, dataSetA, periodA, organisationUnitC, NO_GROUP, NOT_ACCEPTED, date, userA ) );
+        dataApprovalService.addDataApproval( new DataApproval( level3, dataSetA, periodA, organisationUnitE, NO_GROUP, NOT_ACCEPTED, date, userA ) );
+
+        assertEquals( DataApprovalState.APPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupAB ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_ELSEWHERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, asSet( groupCD ), NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, NO_GROUPS, NO_OPTIONS ).getDataApprovalState() );
+    }
 }

=== modified file 'dhis-2/dhis-web/dhis-web-dataentry/src/main/webapp/dhis-web-dataentry/javascript/form.js'
--- dhis-2/dhis-web/dhis-web-dataentry/src/main/webapp/dhis-web-dataentry/javascript/form.js	2014-06-06 13:29:45 +0000
+++ dhis-2/dhis-web/dhis-web-dataentry/src/main/webapp/dhis-web-dataentry/javascript/form.js	2014-06-10 20:46:05 +0000
@@ -58,6 +58,9 @@
 // Current offset, next or previous corresponding to increasing or decreasing value
 dhis2.de.currentPeriodOffset = 0;
 
+// Associative array with currently-displayed period choices, keyed by iso
+dhis2.de.periodChoices = [];
+
 // Username of user who marked the current data set as complete if any
 dhis2.de.currentCompletedByUser = null;
 
@@ -689,21 +692,6 @@
     return split;
 }
 
-/**
- * Creates an associative array with values of the current period.
- */
-function currentPeriod()
-{
-    var period = {};
-    var part = ( $( '#selectedPeriodId' ).val() || "" ).split( ',' );
-
-    period.iso = part[0];
-    period.startDate = part[1];
-    period.endDate = part[2];
-
-    return period;
-}
-
 function refreshZebraStripes( $tbody )
 {
     $tbody.find( 'tr:not([colspan]):visible:even' ).find( 'td:first-child' ).removeClass( 'reg alt' ).addClass( 'alt' );
@@ -772,7 +760,7 @@
     $( '#selectedDataSetId' ).removeAttr( 'disabled' );
 
     var dataSetId = $( '#selectedDataSetId' ).val();
-    var periodId = currentPeriod().iso;
+    var periodId = $( '#selectedPeriodId').val();
 
     clearListById( 'selectedDataSetId' );
     addOptionById( 'selectedDataSetId', '-1', '[ ' + i18n_select_data_set + ' ]' );
@@ -912,7 +900,6 @@
     var x = dhis2.de.currentDataSetId;
     
     var dataSetId = $( '#selectedDataSetId' ).val();
-    var periodVal = $( '#selectedPeriodId' ).val();
 
     var previousDataSetValid = ( dhis2.de.currentDataSetId && dhis2.de.currentDataSetId != -1 );    
     var previousPeriodType = previousDataSetValid ? dhis2.de.dataSets[dhis2.de.currentDataSetId].periodType : null;
@@ -1042,11 +1029,13 @@
     {
     	addOptionById( 'selectedPeriodId', "", i18n_no_periods_click_prev_year_button );
     }
-    
+
+    dhis2.de.periodChoices = [];
+
     $.safeEach( periods, function( idx, item ) 
     {
-        var periodInfo = item.iso + ',' + item.startDate + ',' + item.endDate;
-        addOptionById( 'selectedPeriodId', periodInfo, item.name );
+        addOptionById( 'selectedPeriodId', item.iso, item.name );
+        dhis2.de.periodChoices[ item.iso ] = item;
     } );
 }
 
@@ -1195,8 +1184,8 @@
  */
 dhis2.de.optionValidWithinPeriod = function( option, period )
 {
-    return ( option.startDate == null || option.startDate <= period.endDate )
-        && ( option.endDate == null || option.endDate >= period.startDate )
+    return ( option.startDate == null || option.startDate <= dhis2.periodChoices[ period ].endDate )
+        && ( option.endDate == null || option.endDate >= dhis2.periodChoices[ period ].startDate )
 }
 
 /**
@@ -1207,7 +1196,7 @@
 {
 	var html = '';
 
-    var period = currentPeriod();
+    var period = $( '#selectedPeriodId' ).val();
 
     var options = dhis2.de.getCurrentCategoryOptions();
 
@@ -1277,7 +1266,7 @@
 dhis2.de.inputSelected = function()
 {
     var dataSetId = $( '#selectedDataSetId' ).val();
-    var periodId = currentPeriod().iso;
+    var periodId = $( '#selectedPeriodId').val();
 
 	if (
 	    dhis2.de.currentOrganisationUnitId &&
@@ -1304,7 +1293,7 @@
 
 function getAndInsertDataValues()
 {
-    var periodId = currentPeriod().iso;
+    var periodId = $( '#selectedPeriodId').val();
     var dataSetId = $( '#selectedDataSetId' ).val();
 
     // Clear existing values and colors, grey disabled fields
@@ -1890,7 +1879,7 @@
 
 function viewHist( dataElementId, optionComboId )
 {
-    var periodId = currentPeriod().iso;
+    var periodId = $( '#selectedPeriodId').val();
 
 	if ( dataElementId && optionComboId && periodId && periodId != -1 )
 	{
@@ -2443,7 +2432,7 @@
     {
         var params = {
             'ds': $( '#selectedDataSetId' ).val(),
-            'pe': currentPeriod().iso,
+            'pe': $( '#selectedPeriodId').val(),
             'ou': getCurrentOrganisationUnit(),
             'multiOu': dhis2.de.multiOrganisationUnit
         };

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryOptionAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryOptionAction.java	2014-06-02 15:29:13 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryOptionAction.java	2014-06-10 20:46:05 +0000
@@ -35,6 +35,7 @@
 import org.hisp.dhis.dataelement.DataElementCategoryService;
 
 import com.opensymphony.xwork2.Action;
+import org.hisp.dhis.ouwt.manager.OrganisationUnitSelectionManager;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import java.util.Date;
@@ -59,6 +60,9 @@
     @Autowired
     private CalendarService calendarService;
 
+    @Autowired
+    private OrganisationUnitSelectionManager selectionManager;
+
     // -------------------------------------------------------------------------
     // Input
     // -------------------------------------------------------------------------
@@ -129,6 +133,7 @@
         dataElementCategoryOption.setCode( code );
         dataElementCategoryOption.setStartDate( sDate );
         dataElementCategoryOption.setEndDate( eDate );
+        dataElementCategoryOption.getOrganisationUnits().addAll ( selectionManager.getSelectedOrganisationUnits() );
 
         dataElementCategoryService.addDataElementCategoryOption( dataElementCategoryOption );
 

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/GetDataElementCategoryOptionAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/GetDataElementCategoryOptionAction.java	2014-03-18 08:10:10 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/GetDataElementCategoryOptionAction.java	2014-06-10 20:46:05 +0000
@@ -33,7 +33,9 @@
 import org.hisp.dhis.concept.Concept;
 import org.hisp.dhis.dataelement.DataElementCategoryOption;
 import org.hisp.dhis.dataelement.DataElementCategoryService;
+import org.hisp.dhis.ouwt.manager.OrganisationUnitSelectionManager;
 import org.hisp.dhis.paging.ActionPagingSupport;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * @author Chau Thu Tran
@@ -54,8 +56,11 @@
         this.dataElementCategoryService = dataElementCategoryService;
     }
 
+    @Autowired
+    private OrganisationUnitSelectionManager selectionManager;
+
     // -------------------------------------------------------------------------
-    // Getters & Setters
+    // Input
     // -------------------------------------------------------------------------
 
     private Integer id;
@@ -65,6 +70,10 @@
         this.id = id;
     }
 
+    // -------------------------------------------------------------------------
+    // Output
+    // -------------------------------------------------------------------------
+
     private DataElementCategoryOption dataElementCategoryOption;
 
     public DataElementCategoryOption getDataElementCategoryOption()
@@ -72,6 +81,13 @@
         return dataElementCategoryOption;
     }
 
+    private boolean moreOptionsPresent;
+
+    public boolean isMoreOptionsPresent()
+    {
+        return moreOptionsPresent;
+    }
+
     private List<Concept> concepts;
 
     public List<Concept> getConcepts()
@@ -87,6 +103,12 @@
     {
         dataElementCategoryOption = dataElementCategoryService.getDataElementCategoryOption( id );
 
+        selectionManager.setSelectedOrganisationUnits( dataElementCategoryOption.getOrganisationUnits() );
+
+        moreOptionsPresent = dataElementCategoryOption.getStartDate() != null
+                || dataElementCategoryOption.getEndDate() != null
+                || !dataElementCategoryOption.getOrganisationUnits().isEmpty();
+
         return SUCCESS;
     }
 }

=== added file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/ShowAddDataElementCategoryOptionAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/ShowAddDataElementCategoryOptionAction.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/ShowAddDataElementCategoryOptionAction.java	2014-06-10 20:46:05 +0000
@@ -0,0 +1,64 @@
+package org.hisp.dhis.dd.action.category;
+
+/*
+ * 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 com.opensymphony.xwork2.Action;
+import org.hisp.dhis.organisationunit.OrganisationUnit;
+import org.hisp.dhis.ouwt.manager.OrganisationUnitSelectionManager;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.ArrayList;
+
+/**
+ * @author Jim Grace
+ * @version $Id$
+ */
+public class ShowAddDataElementCategoryOptionAction
+    implements Action
+{
+    // -------------------------------------------------------------------------
+    // Dependencies
+    // -------------------------------------------------------------------------
+
+    @Autowired
+    private OrganisationUnitSelectionManager selectionManager;
+
+    // -------------------------------------------------------------------------
+    // Action implementation
+    // -------------------------------------------------------------------------
+
+    @Override
+    public String execute()
+            throws Exception
+    {
+        selectionManager.setSelectedOrganisationUnits( new ArrayList<OrganisationUnit>() );
+
+        return SUCCESS;
+    }
+}

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/UpdateDataElementCategoryOptionAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/UpdateDataElementCategoryOptionAction.java	2014-06-02 15:29:13 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/UpdateDataElementCategoryOptionAction.java	2014-06-10 20:46:05 +0000
@@ -33,6 +33,7 @@
 import org.hisp.dhis.calendar.DateUnit;
 import org.hisp.dhis.dataelement.DataElementCategoryOption;
 import org.hisp.dhis.dataelement.DataElementCategoryService;
+import org.hisp.dhis.ouwt.manager.OrganisationUnitSelectionManager;
 
 import com.opensymphony.xwork2.Action;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -60,6 +61,9 @@
     @Autowired
     private CalendarService calendarService;
 
+    @Autowired
+    private OrganisationUnitSelectionManager selectionManager;
+
     // -------------------------------------------------------------------------
     // Input
     // -------------------------------------------------------------------------
@@ -138,6 +142,8 @@
         categoryOption.setCode( code );
         categoryOption.setStartDate( sDate );
         categoryOption.setEndDate( eDate );
+        categoryOption.getOrganisationUnits().clear();
+        categoryOption.getOrganisationUnits().addAll ( selectionManager.getSelectedOrganisationUnits() );
 
         dataElementCategoryService.updateDataElementCategoryOption( categoryOption );
 

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties	2014-02-16 14:24:41 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties	2014-06-10 20:46:05 +0000
@@ -232,4 +232,6 @@
 create_new_category_option_group_set = Create new category option group set
 edit_category_option_group_set = Edit category option group set
 available_category_option_groups = Available category option groups
-confirm_delete_category_option_group_set = Are you sure you want to delete this category option group set?
\ No newline at end of file
+confirm_delete_category_option_group_set = Are you sure you want to delete this category option group set?
+show_more_options=Show more options
+show_less_options=Show less options
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/struts.xml'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/struts.xml	2014-03-26 14:28:00 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/struts.xml	2014-06-10 20:46:05 +0000
@@ -540,10 +540,10 @@
       <param name="anyAuthorities">F_CATEGORY_OPTION_PUBLIC_ADD, F_CATEGORY_OPTION_PRIVATE_ADD</param>
     </action>
 
-    <action name="showAddDataElementCategoryOptionForm" class="org.hisp.dhis.dd.action.NoAction">
+    <action name="showAddDataElementCategoryOptionForm" class="org.hisp.dhis.dd.action.category.ShowAddDataElementCategoryOptionAction">
       <result name="success" type="velocity">/main.vm</result>
       <param name="page">/dhis-web-maintenance-datadictionary/multidimensional/addDataElementCategoryOptionForm.vm</param>
-      <param name="javascripts">javascript/categoryOption.js</param>
+      <param name="javascripts">../dhis-web-commons/ouwt/ouwt.js,javascript/categoryOption.js</param>
       <param name="anyAuthorities">F_CATEGORY_OPTION_PUBLIC_ADD, F_CATEGORY_OPTION_PRIVATE_ADD</param>
     </action>
 
@@ -555,7 +555,7 @@
     <action name="showUpdateDataElementCategoryOptionForm" class="org.hisp.dhis.dd.action.category.GetDataElementCategoryOptionAction">
       <result name="success" type="velocity">/main.vm</result>
       <param name="page">/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryOptionForm.vm</param>
-      <param name="javascripts">javascript/category.js</param>
+      <param name="javascripts">../dhis-web-commons/ouwt/ouwt.js,javascript/categoryOption.js</param>
       <param name="anyAuthorities">F_CATEGORY_OPTION_PUBLIC_ADD, F_CATEGORY_OPTION_PRIVATE_ADD</param>
     </action>
 

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/javascript/categoryOption.js'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/javascript/categoryOption.js	2013-02-22 15:07:05 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/javascript/categoryOption.js	2014-06-10 20:46:05 +0000
@@ -56,3 +56,10 @@
 		addOptionById( 'categoryOptions', json.dataElementCategoryOption.id, name );
 	} );
 }
+
+function showMoreOrFewerOptions()
+{
+    $( ".showMoreOptions" ).toggle();
+    $( ".moreOptions" ).toggle();
+}
+

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/addDataElementCategoryOptionForm.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/addDataElementCategoryOptionForm.vm	2014-06-02 15:29:13 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/addDataElementCategoryOptionForm.vm	2014-06-10 20:46:05 +0000
@@ -10,6 +10,11 @@
         $('#startDate').calendarsPicker('option', 'maxDate', dhis2.period.calendar.add(dhis2.period.calendar.today(), 100, 'y'));
         $('#endDate').calendarsPicker('option', 'maxDate', dhis2.period.calendar.add(dhis2.period.calendar.today(), 100, 'y'));
 
+        selection.setMultipleSelectionAllowed( true );
+        selection.setUnselectAllowed( true );
+        selection.setAutoSelectRoot( false );
+        selection.clearSelected();
+
         checkValueIsExist( "name", "validateDataElementCategoryOption.action" );
         checkValueIsExist( "code", "validateDataElementCategoryOption.action" );
 	});
@@ -19,6 +24,14 @@
     var i18n_must_include_category_option = '$encoder.jsEscape( $i18n.getString( "must_include_category_option" ) , "'")';
 </script>
 
+<style type="text/css">
+    div#orgUnitTree
+    {
+        width: 495px;
+        border: 1px solid #ccc;
+    }
+</style>
+
 <h3>$i18n.getString( "create_new_data_element_category_option" )</h3>
 
 <form id="addDataElementCategoryOptionForm" name="addDataElementCategoryOptionForm" action="addDataElementCategoryOption.action" method="post" class="inputForm">
@@ -34,28 +47,51 @@
 	  <td><label>$i18n.getString( "code" )</td>
 	  <td colspan="3"><input type="text" id="code" name="code"/></td>
     </tr>
-      <tr>
-        <td>
-          <label for="startDate">$i18n.getString( 'start_date' )</label>
-        </td>
-        <td>
-          <input type="text" id="startDate" name="startDate" style="width:20em">
-        </td>
-      </tr>
-      <tr>
-        <td>
-          <label for="endDate">$i18n.getString( 'end_date' )</label>
-        </td>
-        <td>
-          <input type="text" id="endDate" name="endDate" style="width:20em">
-        </td>
-      </tr>
-	<tr>
-	  <td></td>
-	  <td colspan="3">
-		<input type="submit"  value="$i18n.getString( 'add' )" style="width:100px">
-		<input type="button" onclick="dhis2.commons.redirectCurrentPage( 'categoryOption.action' )" value="$i18n.getString( 'cancel' )" style="width:100px"/></p>
-      </td>
-	</tr>
+    <tr class="showMoreOptions">
+      <td colspan="4"><a href="javascript:showMoreOrFewerOptions()">$i18n.getString( "show_more_options" )</a></td>
+    </tr>
+    <tr class="moreOptions" style="display:none">
+      <td>
+        <label for="startDate">$i18n.getString( 'start_date' )</label>
+      </td>
+      <td>
+        <input type="text" id="startDate" name="startDate" style="width:20em">
+      </td>
+    </tr>
+    <tr class="moreOptions" style="display:none">
+      <td>
+        <label for="endDate">$i18n.getString( 'end_date' )</label>
+      </td>
+      <td>
+        <input type="text" id="endDate" name="endDate" style="width:20em">
+      </td>
+    </tr>
+  </table>
+
+  <div class="moreOptions" style="display:none">
+
+  <table style="margin-bottom: 20px;">
+    <tr>
+      <th>$i18n.getString( "organisation_units" )</th>
+    </tr>
+    <tr>
+      <td>
+        #parse( "/dhis-web-commons/ouwt/orgunittree.vm" )
+      </td>
+    </tr>
+  </table>
+
+  <div id="showLessOptions" style="margin-bottom: 20px;"><a href="javascript:showMoreOrFewerOptions()">$i18n.getString( "show_less_options" )</a></div>
+
+  </div>
+
+  <table>
+    <tr>
+      <td></td>
+      <td>
+        <input type="submit"  value="$i18n.getString( 'add' )" style="width:100px">
+        <input type="button" onclick="dhis2.commons.redirectCurrentPage( 'categoryOption.action' )" value="$i18n.getString( 'cancel' )" style="width:100px"/></p>
+      </td>
+    </tr>
   </table>
 </form>

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/categoryOption.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/categoryOption.vm	2014-06-02 15:29:13 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/categoryOption.vm	2014-06-10 20:46:05 +0000
@@ -20,8 +20,11 @@
 				setInnerHTML( 'startDateField', json.dataElementCategoryOption.startDate );
 				setInnerHTML( 'endDateField', json.dataElementCategoryOption.endDate );
 				setInnerHTML( 'conceptField', json.dataElementCategoryOption.concept );
-					  
-				showDetails();
+
+                var organisationUnits = joinNameableObjects(json.dataElementCategoryOption.organisationUnits);
+                setInnerHTML( 'orgUnitField', organisationUnits ? organisationUnits : '[' + i18n_none + ']');
+
+                showDetails();
 		});
 	}
 	
@@ -90,6 +93,7 @@
           <p><label>$i18n.getString( "code" ):</label><br/><span id="codeField"></span></p>
           <p><label>$i18n.getString( "start_date" ):</label><br/><span id="startDateField"></span></p>
           <p><label>$i18n.getString( "end_date" ):</label><br/><span id="endDateField"></span></p>
+          <p><label>$i18n.getString( "organisation_units" ):</label><br/><span id="orgunitField"></span></p>
         <p><label>$i18n.getString( "concept" ):</label><br/><span id="conceptField"></span></p>
       </div>
 

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryOptionForm.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryOptionForm.vm	2014-06-02 15:29:13 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryOptionForm.vm	2014-06-10 20:46:05 +0000
@@ -11,8 +11,14 @@
         $('#startDate').calendarsPicker('option', 'maxDate', dhis2.period.calendar.add(dhis2.period.calendar.today(), 100, 'y'));
         $('#endDate').calendarsPicker('option', 'maxDate', dhis2.period.calendar.add(dhis2.period.calendar.today(), 100, 'y'));
 
+        selection.clearSelected(); // Sync ouwt from server
+        selection.setMultipleSelectionAllowed( true );
+        selection.setUnselectAllowed( true );
+        selection.setAutoSelectRoot( false );
+
+        #if ( $moreOptionsPresent ) showMoreOrFewerOptions(); #end
+
         checkValueIsExist( "name", "validateDataElementCategoryOption.action", {id: $dataElementCategoryOption.id} );
-//Error:        checkValueIsExist( "code", "validateDataElementCategoryOption.action", {id: $dataElementCategoryOption.code} );
 	});
 
     var i18n_confirm_delete = '$encoder.jsEscape( $i18n.getString( "confirm_delete_data_element_category_option" ) , "'")';
@@ -21,6 +27,14 @@
 	var i18n_option_rename_successfully = '$encoder.jsEscape( $i18n.getString( "option_rename_successfully" ) , "'")';
 </script>
 
+<style type="text/css">
+    div#orgUnitTree
+    {
+        width: 495px;
+        border: 1px solid #ccc;
+    }
+</style>
+
 <h3>$i18n.getString( "edit_data_element_category_option" )</h3>
 
 <form id="editDataElementCategoryOptionForm" name="editDataElementCategoryOptionForm" action="updateDataElementCategoryOption.action" method="post" class="inputForm">
@@ -40,7 +54,10 @@
 	  <td><label>$i18n.getString( "code" )</label></td>
 	  <td colspan="3"><input type="text" id="code" name="code" style="width:25em" value="$!encoder.htmlEncode( $dataElementCategoryOption.code )" /></td>
     </tr>
-    <tr>
+    <tr class="showMoreOptions">
+      <td colspan="4"><a href="javascript:showMoreOrFewerOptions()">$i18n.getString( "show_more_options" )</a></td>
+    </tr>
+    <tr class="moreOptions" style="display:none">
       <td>
         <label for="startDate">$i18n.getString( 'start_date' )</label>
       </td>
@@ -48,7 +65,7 @@
         <input type="text" id="startDate" name="startDate" style="width:20em" value="$!format.formatDate( $!dataElementCategoryOption.startDate )">
           </td>
       </tr>
-      <tr>
+      <tr class="moreOptions" style="display:none">
         <td>
           <label for="endDate">$i18n.getString( 'end_date' )</label>
         </td>
@@ -56,6 +73,26 @@
           <input type="text" id="endDate" name="endDate" style="width:20em" value="$!format.formatDate( $!dataElementCategoryOption.endDate )">
       </td>
     </tr>
+  </table>
+
+  <div class="moreOptions" style="display:none">
+
+  <table style="margin-bottom: 20px;">
+    <tr>
+      <th>$i18n.getString( "organisation_units" )</th>
+    </tr>
+    <tr>
+      <td>
+        #parse( "/dhis-web-commons/ouwt/orgunittree.vm" )
+      </td>
+    </tr>
+  </table>
+
+  <div id="showLessOptions" style="margin-bottom: 20px;"><a href="javascript:showMoreOrFewerOptions()">$i18n.getString( "show_less_options" )</a></div>
+
+  </div>
+
+  <table>
     <tr>
       <td></td>
       <td colspan="3">

=== modified file 'dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetCategoryOptionGroupsAction.java'
--- dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetCategoryOptionGroupsAction.java	2014-04-28 18:23:49 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetCategoryOptionGroupsAction.java	2014-06-10 20:46:05 +0000
@@ -38,10 +38,13 @@
 import org.hisp.dhis.dataapproval.DataApprovalLevelService;
 import org.hisp.dhis.dataelement.CategoryOptionGroup;
 import org.hisp.dhis.dataelement.CategoryOptionGroupSet;
+import org.hisp.dhis.dataelement.DataElementCategoryOption;
 import org.hisp.dhis.dataelement.DataElementCategoryService;
 import org.hisp.dhis.i18n.I18n;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
 import org.hisp.dhis.organisationunit.OrganisationUnitService;
+import org.hisp.dhis.period.Period;
+import org.hisp.dhis.period.PeriodService;
 import org.hisp.dhis.system.util.Filter;
 import org.hisp.dhis.system.util.FilterUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -56,7 +59,10 @@
 
     @Autowired
     private OrganisationUnitService organisationUnitService;
-    
+
+    @Autowired
+    private PeriodService periodService;
+
     @Autowired
     private DataElementCategoryService categoryService;
 
@@ -72,12 +78,19 @@
     // -------------------------------------------------------------------------
 
     private String ou;
-    
+
     public void setOu( String ou )
     {
         this.ou = ou;
     }
 
+    private String pe;
+
+    public void setPe( String pe )
+    {
+        this.pe = pe;
+    }
+
     // -------------------------------------------------------------------------
     // Output
     // -------------------------------------------------------------------------
@@ -102,9 +115,11 @@
     public String execute()
         throws Exception
     {
-        if ( ou != null )
+        if ( ou != null && pe != null )
         {
             OrganisationUnit unit = organisationUnitService.getOrganisationUnit( ou );
+
+            Period period = periodService.getPeriod( pe );
             
             int orgUnitLevel = organisationUnitService.getLevelOfOrganisationUnit( unit.getId() );
             
@@ -117,7 +132,9 @@
             categoryOptionGroups = new ArrayList<CategoryOptionGroup>( categoryService.getAllCategoryOptionGroups() );
             
             FilterUtils.filter( categoryOptionGroups, new CategoryOptionGroupGroupSetFilter( groupSets ) );
-            
+
+            FilterUtils.filter( categoryOptionGroups, new CategoryOptionPeriodOrganisationUnitFilter( period, unit ) );
+
             addNoneGroupIfNoGroupSet( approvalLevels, categoryOptionGroups );
         }
         
@@ -218,4 +235,39 @@
             return groupSets != null && groupSets.contains( group.getGroupSet() );
         }
     }
+
+    /**
+     * Filter for group set on period and organisation unit, based on whether
+     * any member option groups match the period and organisation unit.
+     */
+    class CategoryOptionPeriodOrganisationUnitFilter
+        implements Filter<CategoryOptionGroup>
+    {
+        private Period period;
+        private OrganisationUnit organisationUnit;
+
+        public CategoryOptionPeriodOrganisationUnitFilter( Period period, OrganisationUnit organisationUnit )
+        {
+            this.period = period;
+            this.organisationUnit = organisationUnit;
+        }
+
+        @Override
+        public boolean retain( CategoryOptionGroup group )
+        {
+            for ( DataElementCategoryOption option : group.getMembers() )
+            {
+                if ( ( option.getStartDate() == null || option.getStartDate().before( period.getEndDate() ) )
+                        && ( option.getEndDate() == null || option.getEndDate().after( period.getStartDate() ) ) )
+                {
+                    if ( option.getOrganisationUnits().isEmpty() || organisationUnit.isEqualOrChildOf( option.getOrganisationUnits() ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+    }
 }

=== 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-06-06 19:29:39 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/java/org/hisp/dhis/reporting/dataapproval/action/GetDataApprovalOptionsAction.java	2014-06-10 20:46:05 +0000
@@ -59,13 +59,6 @@
     // Output
     // -------------------------------------------------------------------------
 
-    private List<CategoryOptionGroup> categoryOptionGroups;
-    
-    public List<CategoryOptionGroup> getCategoryOptionGroups()
-    {
-        return categoryOptionGroups;
-    }
-
     private List<DataSet> dataSets;
 
     public List<DataSet> getDataSets()
@@ -88,13 +81,11 @@
     public String execute()
         throws Exception
     {
-        categoryOptionGroups = new ArrayList<CategoryOptionGroup>( categoryService.getAllCategoryOptionGroups() );
         dataSets = new ArrayList<DataSet>( dataSetService.getAllDataSets() );
         periodTypes = getAvailablePeriodTypes();
 
         FilterUtils.filter( dataSets, new DataSetApproveDataFilter() );
         
-        Collections.sort( categoryOptionGroups, IdentifiableObjectNameComparator.INSTANCE );
         Collections.sort( dataSets, IdentifiableObjectNameComparator.INSTANCE );
         
         return SUCCESS;

=== 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-06-06 19:29:39 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/dataApprovalForm.vm	2014-06-10 20:46:05 +0000
@@ -95,7 +95,7 @@
 <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">
+<select id="periodId" name="periodId" style="width:330px" disabled="disabled" onchange="dhis2.appr.periodSelected()">
 </select>
 </div>
 

=== 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-06-06 19:29:39 +0000
+++ dhis-2/dhis-web/dhis-web-reporting/src/main/webapp/dhis-web-reporting/javascript/dataApproval.js	2014-06-10 20:46:05 +0000
@@ -22,23 +22,26 @@
 dhis2.appr.dataSetSelected = function()
 {
     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();
+
+    if ( $( "#periodType" ).val() != dataSetPeriodType ) {
+        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 )
@@ -50,6 +53,7 @@
 {
     var periodType = $( "#periodType" ).val();
     dhis2.dsr.displayPeriodsInternal( periodType, dhis2.appr.currentPeriodOffset );
+    dhis2.appr.displayCategoryOptionGroups();
 }
 
 dhis2.appr.displayNextPeriods = function()
@@ -67,18 +71,24 @@
     dhis2.appr.displayPeriods();
 }
 
+dhis2.appr.periodSelected = function()
+{
+    dhis2.appr.displayCategoryOptionGroups();
+}
+
 dhis2.appr.displayCategoryOptionGroups = function()
 {
 	var ou = selection.getSelected()[0];
+	var pe = $( "#periodId" ).val();
 	
-	if ( !ou ) {
+	if ( !ou || !pe ) {
 		return;
 	}
 	
 	var url = "getCategoryOptionGroups.action";
 	
-	$.getJSON( url, {ou:ou}, function( json ) {
-		if ( json.categoryOptionGroups && json.categoryOptionGroups.length ) {
+	$.getJSON( url, {ou:ou, pe:pe}, function( json ) {
+		if ( json.categoryOptionGroups && json.categoryOptionGroups.length > 1 ) {
 			var html = "";
 			$.each( json.categoryOptionGroups, function( index, group ) {
 				html += "<option value=\"" + group.uid + "\" data-dimension=\"" + group.groupSet + "\">" + group.name + "</option>";
@@ -349,13 +359,13 @@
 dhis2.appr.getApprovalUrl = function()
 {
 	var data = dhis2.appr.getDataReport();
-	var url = "../api/dataApprovals?ds=" + data.ds + "&pe=" + data.pe + "&ou=" + data.ou + "&cog=" + data.cog;	
+	var url = "../api/dataApprovals?ds=" + data.ds + "&pe=" + data.pe + "&ou=" + data.ou + "&cog=" + data.cog;
 	return url;
 }
 
 dhis2.appr.getAcceptanceUrl = function()
 {
 	var data = dhis2.appr.getDataReport();
-	var url = "../api/dataApprovals/acceptances?ds=" + data.ds + "&pe=" + data.pe + "&ou=" + data.ou + "&cog=" + data.cog;	
+	var url = "../api/dataApprovals/acceptances?ds=" + data.ds + "&pe=" + data.pe + "&ou=" + data.ou + "&cog=" + data.cog;
 	return url;
 }