← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 18760: Get some approvals unit tests working again.

 

------------------------------------------------------------
revno: 18760
committer: jimgrace@xxxxxxxxx
branch nick: dhis2
timestamp: Mon 2015-03-30 20:55:04 -0400
message:
  Get some approvals unit tests working again.
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApproval.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissionsEvaluator.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/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java


--
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/dataapproval/DataApproval.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApproval.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApproval.java	2015-03-31 00:55:04 +0000
@@ -270,7 +270,7 @@
                 ", attributeOptionCombo='" + ( attributeOptionCombo == null ? "(null)" : attributeOptionCombo.getName() ) + "'" +
                 ", accepted=" + accepted +
                 ", created=" + created +
-                ", creator=" + ( dataApprovalLevel == null ? "(null)" : dataApprovalLevel.getName() ) +
+                ", creator=" + ( creator == null ? "(null)" : creator.getName() ) +
                 '}';
     }
 

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissionsEvaluator.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissionsEvaluator.java	2015-02-26 16:35:42 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissionsEvaluator.java	2015-03-31 00:55:04 +0000
@@ -36,6 +36,8 @@
 import com.google.common.cache.CacheLoader;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.hisp.dhis.organisationunit.OrganisationUnit;
+import org.hisp.dhis.organisationunit.OrganisationUnitService;
 import org.hisp.dhis.setting.SystemSettingManager;
 import org.hisp.dhis.user.CurrentUserService;
 import org.hisp.dhis.user.User;
@@ -59,6 +61,7 @@
     private final static Log log = LogFactory.getLog( DataApprovalPermissionsEvaluator.class );
 
     private DataApprovalLevelService dataApprovalLevelService;
+    private OrganisationUnitService organisationUnitService;
 
     private User user;
 
@@ -85,15 +88,18 @@
      * on one or more DataApproval objects.
      *
      * @param currentUserService Current user service
+     * @param organisationUnitService OrganisationUnit service
      * @param systemSettingManager System setting manager
      * @param dataApprovalLevelService Data approval level service
      * @return context for determining user permissions
      */
     static DataApprovalPermissionsEvaluator makePermissionsEvaluator( CurrentUserService currentUserService,
-            SystemSettingManager systemSettingManager, DataApprovalLevelService dataApprovalLevelService )
+            OrganisationUnitService organisationUnitService, SystemSettingManager systemSettingManager,
+            DataApprovalLevelService dataApprovalLevelService )
     {
         DataApprovalPermissionsEvaluator ev = new DataApprovalPermissionsEvaluator();
 
+        ev.organisationUnitService = organisationUnitService;
         ev.dataApprovalLevelService = dataApprovalLevelService;
 
         ev.user = currentUserService.getCurrentUser();
@@ -125,9 +131,9 @@
      * @param status the data approval status (if any)
      * @return the data approval permissions for the object
      */
-    DataApprovalPermissions getPermissions( DataApprovalStatus status )
+    DataApprovalPermissions getPermissions( DataApprovalStatus status, OrganisationUnit orgUnit )
     {
-        final DataApproval da = status.getDataApproval();
+        DataApproval da = status.getDataApproval();
 
         DataApprovalState s = status.getState();
 
@@ -137,18 +143,91 @@
         {
             log.debug( "getPermissions da " + da + ( da != null ? ( " " + da.getOrganisationUnit() ) : "" ) );
 
-            return permissions; // No permissions are set.
-        }
-
+            permissions.setMayReadData( organisationUnitService.isInUserHierarchy( orgUnit ) );
+
+            return permissions; // No approval permissions set.
+        }
+
+        DataApprovalLevel userApprovalLevel = getUserApprovalLevelWithCache( da );
+
+        if ( userApprovalLevel == null )
+        {
+            log.debug( "getPermissions userApprovalLevel null for user " + ( user == null ? "(null)" : user.getUsername() ) + " orgUnit " + da.getOrganisationUnit().getName() );
+
+            permissions.setMayReadData( organisationUnitService.isInUserHierarchy( orgUnit ) );
+
+            return permissions; // Can't find user approval level, so no approval permissions are set.
+        }
+
+        int userLevel = userApprovalLevel.getLevel();
+        int userOrgUnitLevel = userApprovalLevel.getOrgUnitLevel();
+
+        int dataLevel = ( da.getDataApprovalLevel() != null ? da.getDataApprovalLevel().getLevel() : maxApprovalLevel );
+        int dataOrgUnitLevel = ( da.getDataApprovalLevel() != null ? da.getDataApprovalLevel().getOrgUnitLevel() : 9999999 );
+
+        boolean mayApprove = false;
+        boolean mayUnapprove = false;
+        boolean mayAccept = false;
+        boolean mayUnaccept = false;
+
+        if ( ( authorizedToApprove && userLevel == dataLevel ) || ( authorizedToApproveAtLowerLevels && userLevel < dataLevel ) )
+        {
+            mayApprove = s.isApprovable();
+            mayUnapprove = s.isUnapprovable();
+        }
+
+        if ( authorizedToApprove && userLevel == dataLevel - 1 && userOrgUnitLevel == dataOrgUnitLevel &&
+            ( s.isAccepted() || ( s.isApproved() && !acceptanceRequiredForApproval ) ) )
+        {
+            mayApprove = true;
+            mayUnapprove = true;
+        }
+
+        if ( authorizedToAcceptAtLowerLevels && userLevel == dataLevel - 1 )
+        {
+            mayAccept =  s.isAcceptable();
+            mayUnaccept = s.isUnacceptable();
+
+            if ( s.isUnapprovable() )
+            {
+                mayUnapprove = true;
+            }
+        }
+
+        boolean mayReadData = mayApprove || mayUnapprove || mayAccept || mayUnaccept || userLevel == dataLevel ||
+                ( organisationUnitService.isInUserHierarchy( orgUnit ) && userLevel == dataLevel || ( authorizedToViewUnapprovedData || !hideUnapprovedData ) );
+
+        log.debug( "getPermissions orgUnit " + ( da.getOrganisationUnit() == null ? "(null)" : da.getOrganisationUnit().getName() )
+                + " combo " + da.getAttributeOptionCombo().getName() + " state " + s.name()
+                + " isApproved " + s.isApproved() + " isApprovable " + s.isApprovable() + " isUnapprovable " + s.isUnapprovable()
+                + " isAccepted " + s.isAccepted() + " isAcceptable " + s.isAcceptable() + " isUnacceptable " + s.isUnacceptable()
+                + " userLevel " + userLevel + " dataLevel " + dataLevel
+                + " mayApprove " + mayApprove + " mayUnapprove " + mayUnapprove
+                + " mayAccept " + mayAccept + " mayUnaccept " + mayUnaccept
+                + " mayReadData " + mayReadData );
+
+        permissions.setMayApprove( mayApprove );
+        permissions.setMayUnapprove( mayUnapprove );
+        permissions.setMayAccept( mayAccept );
+        permissions.setMayUnaccept( mayUnaccept );
+        permissions.setMayReadData( mayReadData );
+
+        return permissions;
+    }
+
+    private DataApprovalLevel getUserApprovalLevelWithCache( DataApproval da )
+    {
         DataApprovalLevel userApprovalLevel;
 
+        final DataApproval dataApproval = da;
+
         try
         {
             userApprovalLevel = ( USER_APPROVAL_LEVEL_CACHE.get( user.getId() + "-" + da.getOrganisationUnit().getId(), new Callable<DataApprovalLevel>()
             {
                 public DataApprovalLevel call() throws ExecutionException
                 {
-                    return dataApprovalLevelService.getUserApprovalLevel( user, da.getOrganisationUnit() );
+                    return dataApprovalLevelService.getUserApprovalLevel( user, dataApproval.getOrganisationUnit() );
                 }
             } ) );
         }
@@ -161,58 +240,6 @@
             throw new RuntimeException( ex );
         }
 
-        if ( userApprovalLevel == null )
-        {
-            log.debug( "getPermissions userApprovalLevel null for user " + ( user == null ? "(null)" : user.getUsername() ) + " orgUnit " +  da.getOrganisationUnit().getName() );
-
-            return permissions; // Can't find user approval level, so no permissions are set.
-        }
-
-        int userLevel = userApprovalLevel.getLevel();
-
-        int dataLevel = ( da.getDataApprovalLevel() != null ? da.getDataApprovalLevel().getLevel() : maxApprovalLevel );
-
-        int nextApproveDataLevel = s.isApproved() ? dataLevel - 1 : dataLevel;
-
-        boolean mayApproveOrUnapproveAtLevel = ( authorizedToApprove && userLevel == dataLevel && !da.isAccepted() ) ||
-            ( authorizedToApproveAtLowerLevels && userLevel < dataLevel );
-
-        boolean mayApproveAtNextLevel = ( s == DataApprovalState.ACCEPTED_HERE || ( s == DataApprovalState.APPROVED_HERE && ! acceptanceRequiredForApproval ) ) && 
-            ( ( authorizedToApprove && userLevel == nextApproveDataLevel ) || ( authorizedToApproveAtLowerLevels && userLevel < nextApproveDataLevel ) );
-
-        // TODO More testing needed
-
-        // boolean mayAcceptOrUnacceptAtLevel = authorizedToAcceptAtLowerLevels && ( userLevel <= dataLevel );
-
-        boolean mayAcceptOrUnacceptAtLevel = authorizedToAcceptAtLowerLevels && ( userLevel == dataLevel - 1 || ( userLevel < dataLevel && authorizedToApproveAtLowerLevels ) );
-        
-        boolean mayApprove = ( s.isApprovable() && mayApproveOrUnapproveAtLevel ) || mayApproveAtNextLevel;
-
-        boolean mayUnapprove = s.isUnapprovable() && ( ( mayApproveOrUnapproveAtLevel && !da.isAccepted() ) || mayAcceptOrUnacceptAtLevel );
-
-        boolean mayAccept = s.isAcceptable() && mayAcceptOrUnacceptAtLevel;
-
-        boolean mayUnaccept = s.isUnacceptable() && mayAcceptOrUnacceptAtLevel;
-
-        boolean mayReadData = authorizedToViewUnapprovedData || !hideUnapprovedData || mayApprove
-                || userLevel >= dataLevel;
-
-        log.debug( "getPermissions orgUnit " + ( da.getOrganisationUnit() == null ? "(null)" : da.getOrganisationUnit().getName() )
-            + " combo " + da.getAttributeOptionCombo().getName() + " state " + s.name()
-            + " isApproved " + s.isApproved() + " isApprovable " + s.isApprovable() + " isUnapprovable " + s.isUnapprovable()
-            + " isAccepted " + s.isAccepted() + " isAcceptable " + s.isAcceptable() + " isUnacceptable " + s.isUnacceptable()
-            + " userLevel " + userLevel + " dataLevel " + dataLevel
-            + " mayApproveOrUnapproveAtLevel " + mayApproveOrUnapproveAtLevel + " mayAcceptOrUnacceptAtLevel " + mayAcceptOrUnacceptAtLevel
-            + " mayApprove " + mayApprove + " mayUnapprove " + mayUnapprove
-            + " mayAccept " + mayAccept + " mayUnaccept " + mayUnaccept
-            + " mayReadData " + mayReadData );
-
-        permissions.setMayApprove( mayApprove );
-        permissions.setMayUnapprove( mayUnapprove );
-        permissions.setMayAccept( mayAccept );
-        permissions.setMayUnaccept( mayUnaccept );
-        permissions.setMayReadData( mayReadData );
-
-        return permissions;
+        return userApprovalLevel;
     }
 }

=== 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	2015-03-02 12:21:12 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java	2015-03-31 00:55:04 +0000
@@ -47,11 +47,11 @@
 import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
 import org.hisp.dhis.dataset.DataSet;
 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.setting.SystemSettingManager;
 import org.hisp.dhis.system.util.CollectionUtils;
-import org.hisp.dhis.system.util.TextUtils;
 import org.hisp.dhis.user.CurrentUserService;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -89,6 +89,13 @@
         this.currentUserService = currentUserService;
     }
 
+    private OrganisationUnitService organisationUnitService;
+
+    public void setOrganisationUnitService( OrganisationUnitService organisationUnitService )
+    {
+        this.organisationUnitService = organisationUnitService;
+    }
+
     private PeriodService periodService;
 
     public void setPeriodService( PeriodService periodService )
@@ -144,7 +151,7 @@
             if ( status == null || !status.getPermissions().isMayApprove() )
             {
                 log.warn( "approveData: data may not be approved, state " +
-                    ( status == null ? TextUtils.EMPTY : status.getState().name() ) + " " + da );
+                    ( status == null ? "(null)" : status.getState().name() ) + " " + da );
 
                 throw new DataMayNotBeApprovedException();
             }
@@ -246,7 +253,7 @@
 
             if ( status == null || !status.getPermissions().isMayAccept() )
             {
-                log.warn( "acceptData: data may not be accepted, state " + ( status == null ? TextUtils.EMPTY : status.getState().name() ) + " " + da );
+                log.warn( "acceptData: data may not be accepted, state " + ( status == null ? "(null)" : status.getState().name() ) + " " + da );
 
                 throw new DataMayNotBeAcceptedException();
             }
@@ -343,7 +350,7 @@
         log.debug( "getDataApprovalStatus( " + dataSet.getName() + ", "
             + period.getPeriodType().getName() + " " + period.getName() + " " + period + ", "
             + organisationUnit.getName() + ", "
-            + ( attributeOptionCombo == null ? TextUtils.EMPTY : attributeOptionCombo.getName() ) + " )" );
+            + ( attributeOptionCombo == null ? "(null)" : attributeOptionCombo.getName() ) + " )" );
 
         List<DataApprovalStatus> statuses = dataApprovalStore.getDataApprovals( CollectionUtils.asSet( dataSet ),
             periodService.reloadPeriod( period ), organisationUnit, attributeOptionCombo );
@@ -374,7 +381,7 @@
     {
         DataApprovalStatus status = getDataApprovalStatus( dataSet, period, organisationUnit, attributeOptionCombo );
 
-        status.setPermissions( makePermissionsEvaluator().getPermissions( status ) );
+        status.setPermissions( makePermissionsEvaluator().getPermissions( status, organisationUnit ) );
 
         return status;
     }
@@ -388,7 +395,7 @@
         
         for ( DataApprovalStatus status : statusList )
         {
-            status.setPermissions( permissionsEvaluator.getPermissions( status ) );
+            status.setPermissions( permissionsEvaluator.getPermissions( status, orgUnit ) );
         }
 
         return statusList;
@@ -472,7 +479,7 @@
 
             for ( DataApprovalStatus status : statuses )
             {
-                status.setPermissions( evaluator.getPermissions( status ) );
+                status.setPermissions( evaluator.getPermissions( status, da0.getOrganisationUnit() ) );
 
                 DataApproval da = status.getDataApproval();
 
@@ -511,6 +518,6 @@
     private DataApprovalPermissionsEvaluator makePermissionsEvaluator()
     {
         return DataApprovalPermissionsEvaluator.makePermissionsEvaluator(
-            currentUserService, systemSettingManager, dataApprovalLevelService );
+            currentUserService, organisationUnitService, systemSettingManager, dataApprovalLevelService );
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java	2015-03-26 21:20:21 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java	2015-03-31 00:55:04 +0000
@@ -31,6 +31,7 @@
 import static org.hisp.dhis.dataapproval.DataApprovalState.ACCEPTED_HERE;
 import static org.hisp.dhis.dataapproval.DataApprovalState.APPROVED_ABOVE;
 import static org.hisp.dhis.dataapproval.DataApprovalState.APPROVED_HERE;
+import static org.hisp.dhis.dataapproval.DataApprovalState.UNAPPROVABLE;
 import static org.hisp.dhis.dataapproval.DataApprovalState.UNAPPROVED_ABOVE;
 import static org.hisp.dhis.dataapproval.DataApprovalState.UNAPPROVED_READY;
 import static org.hisp.dhis.dataapproval.DataApprovalState.UNAPPROVED_WAITING;
@@ -194,15 +195,24 @@
         
         final User user = currentUserService.getCurrentUser();
 
-        if ( CollectionUtils.isEmpty( dataSets ) )
-        {
-            log.warn( " No data sets specified for getting approvals, period " + period.getName()
+        Set<DataSet> validDataSets = new HashSet<DataSet>();
+        for ( DataSet set : dataSets )
+        {
+            if ( set.isApproveData() )
+            {
+                validDataSets.add( set );
+            }
+        }
+
+        if ( CollectionUtils.isEmpty( validDataSets ) )
+        {
+            log.warn( " No valid data sets specified for getting approvals, period " + period.getName()
                 + " user " + ( user == null ? "(null)" : user.getUsername() ) );
 
             return new ArrayList<>();
         }
 
-        PeriodType dataSetPeriodType = dataSets.iterator().next().getPeriodType();
+        PeriodType dataSetPeriodType = validDataSets.iterator().next().getPeriodType();
 
         List<Period> periods;
 
@@ -218,7 +228,7 @@
         else
         {
             log.warn( "Selected period type " + period.getPeriodType().getName() + " is incompatible with data set '"
-                + dataSets.iterator().next().getName() + "' period type " + dataSetPeriodType.getName()
+                + validDataSets.iterator().next().getName() + "' period type " + dataSetPeriodType.getName()
                 + " user " + ( user == null ? "(null)" : user.getUsername() ) );
 
             return new ArrayList<>();
@@ -233,7 +243,7 @@
 
         DataElementCategoryCombo defaultCategoryCombo = categoryService.getDefaultDataElementCategoryCombo();
 
-        final String dataSetIds = getCommaDelimitedString( getIdentifiers( DataSet.class, dataSets ) );
+        final String dataSetIds = getCommaDelimitedString( getIdentifiers( DataSet.class, validDataSets ) );
 
         boolean isDefaultCombo = categoryService.getDefaultDataElementCategoryOptionCombo().equals( attributeOptionCombo );
 
@@ -247,7 +257,7 @@
         {
             Set<Integer> catComboIds = new HashSet<>();
 
-            for ( DataSet ds : dataSets )
+            for ( DataSet ds : validDataSets )
             {
                 if ( ds.isApproveData() && ( maySeeDefaultCategoryCombo || ds.getCategoryCombo() != defaultCategoryCombo ) )
                 {
@@ -305,7 +315,14 @@
         int orgUnitLevelAbove = 0;
 
         List<DataApprovalLevel> approvalLevels = dataApprovalLevelService.getAllDataApprovalLevels();
-        
+
+        if ( CollectionUtils.isEmpty( approvalLevels ) )
+        {
+            log.warn( " No approval levels configured." );
+
+            return new ArrayList<>(); // Unapprovable.
+        }
+
         for ( DataApprovalLevel dal : approvalLevels )
         {
             if ( dal.getOrgUnitLevel() < orgUnitLevel )
@@ -323,13 +340,15 @@
                 boolean acceptanceRequiredForApproval = (Boolean) systemSettingManager.getSystemSetting( KEY_ACCEPTANCE_REQUIRED_FOR_APPROVAL, false );
 
                 readyBelowSubquery = "not exists (select 1 from _orgunitstructure ous " +
-                    "left join dataapproval da on da.organisationunitid = ous.organisationunitid " +
-                    "and da.dataapprovallevelid = " + dal.getId() + " and da.periodid in (" + periodIds + ") " +
-                    "and da.datasetid in (" + dataSetIds + ") " +
-                    "where ous.idlevel" + orgUnitLevel + " = o.organisationunitid " +
-                    "and da.attributeoptioncomboid = cocco.categoryoptioncomboid " +
-                    "and ous.level = " + dal.getOrgUnitLevel() + " " +
-                    "and ( da.dataapprovalid is null " + ( acceptanceRequiredForApproval ? "or not da.accepted " : "" ) + ") )";
+                    "where not exists (select 1 from dataapproval da " +
+                        "where da.organisationunitid = ous.organisationunitid " +
+                        "and da.dataapprovallevelid = " + dal.getId() + " and da.periodid in (" + periodIds + ") " +
+                        "and da.datasetid in (" + dataSetIds + ") " +
+                        "and da.attributeoptioncomboid = cocco.categoryoptioncomboid " +
+                        ( acceptanceRequiredForApproval ? "and da.accepted " : "" ) +
+                    ") " +
+                    "and ous.idlevel" + orgUnitLevel + " = o.organisationunitid " +
+                    "and ous.level = " + dal.getOrgUnitLevel() + ") ";
                 break;
             }
         }
@@ -385,7 +404,7 @@
         
         List<DataApprovalStatus> statusList = new ArrayList<>();
 
-        DataSet dataSet = ( dataSets.size() == 1 ? dataSets.iterator().next() : null );
+        DataSet dataSet = ( validDataSets.size() == 1 ? validDataSets.iterator().next() : null );
 
         while ( rowSet.next() )
         {
@@ -424,7 +443,9 @@
                 DataApprovalState state = (
                     statusLevel == null ?
                         lowestApprovalLevelForOrgUnit == null ?
-                            UNAPPROVED_ABOVE :
+                            orgUnitLevelAbove == 0 ?
+                                UNAPPROVABLE :
+                                UNAPPROVED_ABOVE :
                             readyBelow ?
                                 UNAPPROVED_READY :
                                 UNAPPROVED_WAITING :

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2015-03-26 14:13:53 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2015-03-31 00:55:04 +0000
@@ -430,6 +430,7 @@
     <property name="dataApprovalStore" ref="org.hisp.dhis.dataapproval.DataApprovalStore" />
     <property name="dataApprovalLevelService" ref="org.hisp.dhis.dataapproval.DataApprovalLevelService" />
     <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
+    <property name="organisationUnitService" ref="org.hisp.dhis.organisationunit.OrganisationUnitService" />
     <property name="periodService" ref="org.hisp.dhis.period.PeriodService" />
     <property name="systemSettingManager" ref="org.hisp.dhis.setting.SystemSettingManager" />
   </bean>

=== 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	2015-03-05 18:09:55 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataapproval/DataApprovalServiceTest.java	2015-03-31 00:55:04 +0000
@@ -423,10 +423,9 @@
 
     @Test
     public void test()
-    {        
+    {
     }
-    
-    @Ignore
+
     @Test
     public void testAddAllAndGetDataApproval() throws Exception
     {
@@ -515,11 +514,9 @@
         assertEquals( DataApprovalState.UNAPPROVED_READY, status.getState() );
         assertNotNull( status.getDataApproval() );
         level = status.getDataApprovalLevel();
-        assertNotNull( level );
-        assertEquals( level2, level );
+        assertNull( level );
     }
 
-    @Ignore
     @Test
     public void testAddDuplicateDataApproval() throws Exception
     {
@@ -543,7 +540,6 @@
         dataApprovalService.approveData( asList( dataApprovalB ) ); // Redundant, so call is ignored.
     }
 
-    @Ignore
     @Test
     public void testDeleteDataApproval() throws Exception
     {
@@ -584,7 +580,6 @@
         assertFalse( dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, defaultCombo ).getState().isApproved() );
     }
 
-    @Ignore
     @Test
     public void testGetDataApprovalState() throws Exception
     {
@@ -702,7 +697,6 @@
         assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitF, defaultCombo ).getState() );
     }
     
-    @Ignore
     @Test
     public void testGetDataApprovalStateWithMultipleChildren() throws Exception
     {
@@ -763,7 +757,6 @@
         assertEquals( DataApprovalState.APPROVED_ABOVE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitF, defaultCombo ).getState() );
     }
 
-    @Ignore
     @Test
     public void testGetDataApprovalStateOtherPeriodTypes() throws Exception
     {
@@ -792,7 +785,6 @@
         assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatus( dataSetA, periodW, organisationUnitD, defaultCombo ).getState() );
     }
 
-    @Ignore
     @Test
     public void testMayApproveSameLevel() throws Exception
     {
@@ -826,16 +818,15 @@
         assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ).getPermissions().isMayApprove());
 
         // Level 3 (organisationUnitC) and Level 4 (organisationUnitF) ready
-        //todo: figure out why these have to be commented out.
-//        try
-//        {
-//            dataApprovalService.approveData( asList( new DataApproval( level4, dataSetA, periodA, organisationUnitD, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
-//            fail( "User should not have permission to approve org unit C." );
-//        }
-//        catch ( DataMayNotBeApprovedException ex )
-//        {
-//            Expected error, so add the data through dataApprovalStore:
-//        }
+        try
+        {
+            dataApprovalService.approveData( asList( new DataApproval( level4, dataSetA, periodA, organisationUnitD, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
+            fail( "User should not have permission to approve org unit C." );
+        }
+        catch ( DataMayNotBeApprovedException ex )
+        {
+            // Expected error, so add the data through dataApprovalStore:
+        }
         dataApprovalStore.addDataApproval( new DataApproval( level4, dataSetA, periodA, organisationUnitD, defaultCombo, NOT_ACCEPTED, date, userA ) );
 
         assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, defaultCombo ).getPermissions().isMayApprove());
@@ -845,53 +836,52 @@
         assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ).getPermissions().isMayApprove());
 
         // Level 2 (organisationUnitB) ready
-//        try
-//        {
-//            dataApprovalService.approveData( asList( new DataApproval( level4, dataSetA, periodA, organisationUnitF, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
-//            fail( "User should not have permission to approve org unit F." );
-//        }
-//        catch ( DataMayNotBeApprovedException ex )
-//        {
-//            // Expected error, so add the data through dataApprovalStore:
-//        }
+        try
+        {
+            dataApprovalService.approveData( asList( new DataApproval( level4, dataSetA, periodA, organisationUnitF, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
+            fail( "User should not have permission to approve org unit F." );
+        }
+        catch ( DataMayNotBeApprovedException ex )
+        {
+            // Expected error, so add the data through dataApprovalStore:
+        }
         dataApprovalStore.addDataApproval( new DataApproval( level4, dataSetA, periodA, organisationUnitF, defaultCombo, NOT_ACCEPTED, date, userA ) );
 
-//        try
-//        {
-//            dataApprovalService.approveData( asList( new DataApproval( level3, dataSetA, periodA, organisationUnitE, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
-//            fail( "User should not have permission to approve org unit E." );
-//        }
-//        catch ( DataMayNotBeApprovedException ex )
-//        {
-//            // Expected error, so add the data through dataApprovalStore:
-//
-//        }
+        try
+        {
+            dataApprovalService.approveData( asList( new DataApproval( level3, dataSetA, periodA, organisationUnitE, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
+            fail( "User should not have permission to approve org unit E." );
+        }
+        catch ( DataMayNotBeApprovedException ex )
+        {
+            // Expected error, so add the data through dataApprovalStore:
+        }
         dataApprovalStore.addDataApproval( new DataApproval( level3, dataSetA, periodA, organisationUnitE, defaultCombo, NOT_ACCEPTED, date, userA ) );
 
-//        try
-//        {
-//            dataApprovalService.approveData( asList( new DataApproval( level3, dataSetA, periodA, organisationUnitC, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
-//            fail( "User should not have permission to approve org unit C." );
-//        }
-//        catch ( DataMayNotBeApprovedException ex )
-//        {
-//            Expected error, so add the data through dataApprovalStore:
-//        }
+        try
+        {
+            dataApprovalService.approveData( asList( new DataApproval( level3, dataSetA, periodA, organisationUnitC, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
+            fail( "User should not have permission to approve org unit C." );
+        }
+        catch ( DataMayNotBeApprovedException ex )
+        {
+            // Expected error, so add the data through dataApprovalStore:
+        }
         dataApprovalStore.addDataApproval( new DataApproval( level3, dataSetA, periodA, organisationUnitC, defaultCombo, NOT_ACCEPTED, date, userA ) );
 
-        assertEquals( true, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitD, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitE, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ).getPermissions().isMayApprove());
+        assertEquals( "UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, defaultCombo) );
+        assertEquals( "APPROVED_HERE level=3 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, defaultCombo) );
+        assertEquals( "APPROVED_ABOVE level=4 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitD, defaultCombo) );
+        assertEquals( "APPROVED_HERE level=3 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitE, defaultCombo) );
+        assertEquals( "APPROVED_ABOVE level=4 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo) );
 
         // Level 1 (organisationUnitA) ready
         dataApprovalService.approveData( asList( new DataApproval( level2, dataSetA, periodA, organisationUnitB, defaultCombo, NOT_ACCEPTED, date, userA ) ) );
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitD, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitE, defaultCombo ).getPermissions().isMayApprove());
-        assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ).getPermissions().isMayApprove());
+        assertEquals( "APPROVED_HERE level=2 approve=F unapprove=T accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, defaultCombo) );
+        assertEquals( "APPROVED_ABOVE level=3 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, defaultCombo) );
+        assertEquals( "APPROVED_ABOVE level=4 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitD, defaultCombo) );
+        assertEquals( "APPROVED_ABOVE level=3 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitE, defaultCombo) );
+        assertEquals( "APPROVED_ABOVE level=4 approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ) );
 
         // Level 1 (organisationUnitA) try to approve
         try
@@ -905,7 +895,6 @@
         }
     }
 
-    @Ignore
     @Test
     public void testMayApproveLowerLevels() throws Exception
     {
@@ -986,7 +975,6 @@
         }
     }
 
-    @Ignore
     @Test
     public void testMayApproveSameAndLowerLevels() throws Exception
     {
@@ -1057,7 +1045,6 @@
         }
     }
 
-    @Ignore
     @Test
     public void testMayApproveNoAuthority() throws Exception
     {
@@ -1095,7 +1082,6 @@
         assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, defaultCombo ).getPermissions().isMayApprove());
     }
 
-    @Ignore
     @Test
     public void testMayUnapproveSameLevel() throws Exception
     {
@@ -1168,7 +1154,6 @@
         assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ).getPermissions().isMayUnapprove());
     }
 
-    @Ignore
     @Test
     public void testMayUnapproveLowerLevels() throws Exception
     {
@@ -1240,7 +1225,6 @@
         assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ).getPermissions().isMayUnapprove());
     }
 
-    @Ignore
     @Test
     public void testMayUnapproveWithAcceptAuthority() throws Exception
     {
@@ -1312,7 +1296,6 @@
         assertEquals( false, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitF, defaultCombo ).getPermissions().isMayUnapprove());
     }
 
-    @Ignore
     @Test
     public void testMayUnapproveNoAuthority() throws Exception
     {
@@ -1387,7 +1370,7 @@
     // Test multi-period approval
     // ---------------------------------------------------------------------
 
-    @Ignore
+    @Ignore // Multi-period approval is no longer supported.
     @Test
     public void testMultiPeriodApproval() throws Exception
     {
@@ -1456,17 +1439,23 @@
     // Test with Categories
     // ---------------------------------------------------------------------
 
-    @Ignore
+    @Ignore //todo: Get this test working.
     @Test
     public void testApprovalStateWithCategories() throws Exception
     {
         setUpCategories();
 
+        dataApprovalLevelService.addDataApprovalLevel( level1ABCD, 1 );
+        dataApprovalLevelService.addDataApprovalLevel( level1EFGH, 2 );
+        dataApprovalLevelService.addDataApprovalLevel( level2ABCD, 3 );
+        dataApprovalLevelService.addDataApprovalLevel( level3ABCD, 4 );
+
         Set<OrganisationUnit> units = asSet( organisationUnitA );
 
         CurrentUserService currentUserService = new MockCurrentUserService( units, null, DataApproval.AUTH_APPROVE, DataApproval.AUTH_APPROVE_LOWER_LEVELS, AUTH_APPR_LEVEL );
         setDependency( dataApprovalService, "currentUserService", currentUserService, CurrentUserService.class );
         setDependency( dataApprovalLevelService, "currentUserService", currentUserService, CurrentUserService.class );
+        setDependency( organisationUnitService, "currentUserService", currentUserService, CurrentUserService.class );
 
         dataSetA.setApproveData( true );
 
@@ -1491,56 +1480,106 @@
         // Option combo J -> Options C,F -> Groups B,D
         // Option combo K -> Options C,G -> Groups B,D
 
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        dataApprovalLevelService.addDataApprovalLevel( level2ABCD ); // Groups AB, CD. Options A,B,C,D.
-
-        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, null ) );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, null ) );
+        assertEquals("UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, null ) );
+
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAE ) );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAE ) );
+        assertEquals("UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAE ) );
+
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAF ) );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAF ) );
+        assertEquals("UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAF ) );
+
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAG ) );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAG ) );
+        assertEquals("UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAG ) );
+
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCE ) );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCE ) );
+        assertEquals("UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCE ) );
+
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCF ) );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCF ) );
+        assertEquals("UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCF ) );
+
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCG ) );
+        assertEquals("UNAPPROVED_WAITING level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCG ) );
+        assertEquals("UNAPPROVED_READY level=null approve=T unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCG ) );
+
+        try
+        {
+            dataApprovalService.approveData( asList( new DataApproval( level2ABCD, dataSetA, periodA, organisationUnitB, optionComboAE, NOT_ACCEPTED, date, userA ) ) );
+            fail( "organisationUnitB should not be ready for data approval." );
+        }
+        catch ( DataMayNotBeApprovedException ex )
+        {
+            // Can't add this approval before approving at a lower level.
+        }
+
+        dataApprovalService.approveData( asList( new DataApproval( level3ABCD, dataSetA, periodA, organisationUnitC, optionComboAE, NOT_ACCEPTED, date, userA ) ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, null ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, null ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, null ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAE ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAF ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAG ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCE ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCF ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCG ) );
 
         dataApprovalService.approveData( asList( new DataApproval( level2ABCD, dataSetA, periodA, organisationUnitB, optionComboAE, NOT_ACCEPTED, date, userA ) ) );
 
-        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.APPROVED_ABOVE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.APPROVED_ABOVE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVED_WAITING, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitA, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitB, null ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatusAndPermissions( dataSetA, periodA, organisationUnitC, null ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, optionComboAE ).getState() );
-        assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, optionComboAF ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, optionComboAG ).getState() );
-
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitA, optionComboCE ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, optionComboCF ).getState() );
-        assertEquals( DataApprovalState.UNAPPROVABLE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitC, optionComboCG ).getState() );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, null ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, null ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, null ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAE ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAF ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboAG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboAG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboAG ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCE ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCE ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCF ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCF ) );
+
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitA, optionComboCG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitB, optionComboCG ) );
+        assertEquals("UNAPPROVABLE level=null approve=F unapprove=F accept=F unaccept=F read=T", statusAndPermissions( dataSetA, periodA, organisationUnitC, optionComboCG ) );
     }
 
-    @Ignore
+    @Ignore //todo: get this test working
     @Test
     public void testApprovalLevelWithCategories() throws Exception
     {
@@ -1685,7 +1724,7 @@
         assertEquals( level1, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, optionComboAE ).getDataApprovalLevel() );
     }
 
-    @Ignore
+    @Ignore //todo: get this test working
     @Test
     public void testCategoriesWithOrgUnitLevels() throws Exception
     {
@@ -1747,4 +1786,35 @@
         assertEquals( DataApprovalState.APPROVED_HERE, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitE, null ).getState() );
         assertEquals( DataApprovalState.UNAPPROVED_READY, dataApprovalService.getDataApprovalStatus( dataSetA, periodA, organisationUnitB, defaultCombo ).getState() );
     }
+
+    /**
+     * Returns approval status and permissions information as a string.
+     * This allows a test to compare the result against a string and test
+     * several things at once. More importantly, it shows in the log
+     * all of the ways in which the test status and permissions differs from
+     * expected, instead of showing only one different value. This can
+     * save time in understanding the difference between the expected value
+     * and the test result.
+     *
+     * @param dataSet Approval data set
+     * @param period Approval period
+     * @param organisationUnit Approval orgaisation unit
+     * @param attributeOptionCombo Approval attribute option combination
+     * @return A String representing the state, level, and allowed user actions
+     */
+    private String statusAndPermissions( DataSet dataSet, Period period, OrganisationUnit organisationUnit,
+                                         DataElementCategoryOptionCombo attributeOptionCombo )
+    {
+        DataApprovalStatus status = dataApprovalService.getDataApprovalStatusAndPermissions( dataSet, period, organisationUnit, attributeOptionCombo );
+
+        DataApprovalPermissions p = status.getPermissions();
+
+        return status.getState().toString()
+                + " level=" + ( status.getDataApprovalLevel() == null ? "null" : status.getDataApprovalLevel().getLevel() )
+                + " approve=" + ( p.isMayApprove() ? "T" : "F" )
+                + " unapprove=" + ( p.isMayUnapprove() ? "T" : "F" )
+                + " accept=" + ( p.isMayAccept() ? "T" : "F" )
+                + " unaccept=" + ( p.isMayUnaccept() ? "T" : "F" )
+                + " read=" + ( p.isMayReadData() ? "T" : "F" );
+    }
 }