← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 18134: Propagate fix from DHIS 2.16 (Rev. 16548 and 16585)

 

------------------------------------------------------------
revno: 18134
committer: sherylyn.marie
branch nick: dhis2
timestamp: Wed 2015-01-28 18:50:56 +0800
message:
  Propagate fix from DHIS 2.16 (Rev. 16548 and 16585)
added:
  dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/comparator/TrackedEntityAttributeValueSortOrderComparator.java
modified:
  dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/ActivityReportingService.java
  dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Patient.java
  dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Program.java
  dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/MobileOrgUnitLinks.java
  dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/ActivityReportingServiceImpl.java
  dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/DefaultProgramService.java
  dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitTest.java
  dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitsTest.java
  dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java
  dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileClientController.java
  dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileOrganisationUnitController.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-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/ActivityReportingService.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/ActivityReportingService.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/ActivityReportingService.java	2015-01-28 10:50:56 +0000
@@ -94,6 +94,9 @@
     Patient savePatient( Patient patient, int orgUnitId, String programId )
         throws NotAllowedException;
 
+    Patient updatePatient( Patient patient, int orgUnitId, String programId )
+        throws NotAllowedException;
+
     String findLostToFollowUp( int orgUnitId, String programId )
         throws NotAllowedException;
 
@@ -135,4 +138,7 @@
 
     String postInterpretationComment( String data )
         throws NotAllowedException;
+    
+    Patient registerRelative( Patient patient, int orgUnitId, String programId )
+        throws NotAllowedException;
 }

=== modified file 'dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Patient.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Patient.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Patient.java	2015-01-28 10:50:56 +0000
@@ -60,6 +60,12 @@
 
     private List<Relationship> relationships = new ArrayList<>();
 
+    private int idToAddRelative;
+    
+    private int relTypeIdToAdd;
+    
+    private Relationship enrollmentRelationship;
+
 
     // -------------------------------------------------------------------------
     // Getters and setters
@@ -145,6 +151,36 @@
         this.completedPrograms = completedPrograms;
     }
 
+    public int getIdToAddRelative()
+    {
+        return idToAddRelative;
+    }
+
+    public void setIdToAddRelative( int idToAddRelative )
+    {
+        this.idToAddRelative = idToAddRelative;
+    }
+    
+    public int getRelTypeIdToAdd()
+    {
+        return relTypeIdToAdd;
+    }
+    
+    public void setRelTypeIdToAdd( int relTypeIdToAdd )
+    {
+        this.relTypeIdToAdd = relTypeIdToAdd;
+    }
+    
+    public Relationship getEnrollmentRelationship()
+    {
+        return enrollmentRelationship;
+    }
+    
+    public void setEnrollmentRelationship( Relationship enrollmentRelationship )
+    {
+        this.enrollmentRelationship = enrollmentRelationship;
+    }
+
     // -------------------------------------------------------------------------
     // Override Methods
     // -------------------------------------------------------------------------
@@ -187,6 +223,17 @@
         {
             each.serialize( dout );
         }
+        dout.writeInt( this.getIdToAddRelative() );
+        dout.writeInt( this.getRelTypeIdToAdd() );
+        if(enrollmentRelationship!=null)
+        {
+            dout.writeInt( 1 );
+            enrollmentRelationship.serialize( dout );
+        }
+        else
+        {
+            dout.writeInt( 0 );
+        }
 
 //        bout.flush();
 //        bout.writeTo( out );
@@ -251,6 +298,16 @@
                 this.relationships.add( relationship );
             }
         }
+        this.setIdToAddRelative( din.readInt() );
+        this.setRelTypeIdToAdd( din.readInt() );
+        
+        int numEnrollmentRelationships = din.readInt();
+        if(numEnrollmentRelationships > 0)
+        {
+            Relationship enrollmentRelationship = new Relationship();
+            enrollmentRelationship.deSerialize( din );
+            this.setEnrollmentRelationship( enrollmentRelationship );
+        }
     }
 
     @Override

=== modified file 'dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Program.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Program.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/LWUITmodel/Program.java	2015-01-28 10:50:56 +0000
@@ -64,6 +64,12 @@
 
     private List<PatientAttribute> programAttributes = new ArrayList<>();
 
+    private String relationshipText;
+
+    private int relatedProgramId;
+
+    private int relationshipType;
+
     public List<ProgramStage> getProgramStages()
     {
         return programStages;
@@ -146,6 +152,36 @@
         this.programAttributes = programAttributes;
     }
 
+    public String getRelationshipText()
+    {
+        return relationshipText;
+    }
+
+    public void setRelationshipText( String relationshipText )
+    {
+        this.relationshipText = relationshipText;
+    }
+
+    public int getRelatedProgramId()
+    {
+        return relatedProgramId;
+    }
+
+    public void setRelatedProgramId( int relatedProgramId )
+    {
+        this.relatedProgramId = relatedProgramId;
+    }
+
+    public int getRelationshipType()
+    {
+        return relationshipType;
+    }
+    
+    public void setRelationshipType( int relationshipType )
+    {
+        this.relationshipType = relationshipType;
+    }
+
     @Override
     public void serialize( DataOutputStream dout )
         throws IOException
@@ -170,6 +206,18 @@
         {
             pa.serialize( dout );
         }
+        
+        String relationshipText = getRelationshipText();
+        if(relationshipText == null)
+        {
+            dout.writeUTF( "" );
+        }
+        else
+        {
+            dout.writeUTF( getRelationshipText() );
+        }
+        dout.writeInt( getRelatedProgramId() );
+        dout.writeInt( relationshipType );
 
     }
 
@@ -207,5 +255,9 @@
                 programAttributes.add( pa );
             }
         }
+
+        this.setRelationshipText( dataInputStream.readUTF() );
+        this.setRelatedProgramId( dataInputStream.readInt() );
+        this.setRelationshipType( dataInputStream.readInt() );
     }
 }

=== modified file 'dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/MobileOrgUnitLinks.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/MobileOrgUnitLinks.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/MobileOrgUnitLinks.java	2015-01-28 10:50:56 +0000
@@ -116,6 +116,8 @@
     private String uploadSingleEventWithoutRegistration;
 
     private String completeProgramInstanceUrl;
+    
+    private String registerRelativeUrl;
 
     @XmlAttribute
     public int getId()
@@ -501,6 +503,16 @@
         this.completeProgramInstanceUrl = completeProgramInstanceUrl;
     }
 
+    public String getRegisterRelativeUrl()
+    {
+        return registerRelativeUrl;
+    }
+
+    public void setRegisterRelativeUrl( String registerRelativeUrl )
+    {
+        this.registerRelativeUrl = registerRelativeUrl;
+    }
+
     @Override
     public void serialize( DataOutputStream dataOutputStream )
         throws IOException
@@ -542,6 +554,7 @@
         dataOutputStream.writeUTF( generateRepeatableEventUrl );
         dataOutputStream.writeUTF( uploadSingleEventWithoutRegistration );
         dataOutputStream.writeUTF( completeProgramInstanceUrl );
+        dataOutputStream.writeUTF( registerRelativeUrl );
 
     }
 
@@ -586,6 +599,7 @@
         generateRepeatableEventUrl = dataInputStream.readUTF();
         uploadSingleEventWithoutRegistration = dataInputStream.readUTF();
         completeProgramInstanceUrl = dataInputStream.readUTF();
+        registerRelativeUrl = dataInputStream.readUTF();
 
     }
 
@@ -673,5 +687,6 @@
         dataOutputStream.writeUTF( generateRepeatableEventUrl );
         dataOutputStream.writeUTF( uploadSingleEventWithoutRegistration );
         dataOutputStream.writeUTF( completeProgramInstanceUrl );
+        dataOutputStream.writeUTF( registerRelativeUrl );
     }
 }

=== added file 'dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/comparator/TrackedEntityAttributeValueSortOrderComparator.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/comparator/TrackedEntityAttributeValueSortOrderComparator.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/api/mobile/model/comparator/TrackedEntityAttributeValueSortOrderComparator.java	2015-01-28 10:50:56 +0000
@@ -0,0 +1,29 @@
+package org.hisp.dhis.api.mobile.model.comparator;
+
+import java.util.Comparator;
+
+import org.hisp.dhis.trackedentityattributevalue.TrackedEntityAttributeValue;
+
+public class TrackedEntityAttributeValueSortOrderComparator implements Comparator<TrackedEntityAttributeValue>
+{
+
+        public int compare( TrackedEntityAttributeValue value0, TrackedEntityAttributeValue value1 )
+        {
+            if ( value0 == null || value1 == null || value0.getAttribute() == null || value1.getAttribute() == null )
+            {
+                return 0;
+            }
+            if ( value0.getAttribute().getSortOrderInListNoProgram() == null || value0.getAttribute().getSortOrderInListNoProgram() == 0 )
+            {
+                return value0.getAttribute().getName().compareTo( value1.getAttribute().getName() );
+            }
+
+            if ( value1.getAttribute().getSortOrderInListNoProgram() == null || value1.getAttribute().getSortOrderInListNoProgram() == 0 )
+            {
+                return value0.getAttribute().getName().compareTo( value1.getAttribute().getName() );
+            }
+
+            return value0.getAttribute().getSortOrderInListNoProgram() - value1.getAttribute().getSortOrderInListNoProgram();
+        }
+
+}

=== modified file 'dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/ActivityReportingServiceImpl.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/ActivityReportingServiceImpl.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/ActivityReportingServiceImpl.java	2015-01-28 10:50:56 +0000
@@ -60,6 +60,7 @@
 import org.hisp.dhis.api.mobile.model.LWUITmodel.PatientList;
 import org.hisp.dhis.api.mobile.model.LWUITmodel.Section;
 import org.hisp.dhis.api.mobile.model.comparator.ActivityComparator;
+import org.hisp.dhis.api.mobile.model.comparator.TrackedEntityAttributeValueSortOrderComparator;
 import org.hisp.dhis.chart.Chart;
 import org.hisp.dhis.chart.ChartService;
 import org.hisp.dhis.common.Grid;
@@ -106,6 +107,7 @@
 import org.hisp.dhis.trackedentity.TrackedEntityInstanceQueryParams;
 import org.hisp.dhis.trackedentity.TrackedEntityInstanceReminder;
 import org.hisp.dhis.trackedentity.TrackedEntityInstanceReminderService;
+import org.hisp.dhis.trackedentity.TrackedEntityInstanceStore;
 import org.hisp.dhis.trackedentity.TrackedEntityInstanceService;
 import org.hisp.dhis.trackedentity.TrackedEntityService;
 import org.hisp.dhis.trackedentityattributevalue.TrackedEntityAttributeValue;
@@ -529,6 +531,16 @@
             List<org.hisp.dhis.api.mobile.model.LWUITmodel.ProgramStageDataElement> dataElements = mobileProgramStage
                 .getDataElements();
 
+            try
+            {
+                OrganisationUnit organisationUnit = organisationUnitService.getOrganisationUnit( orgUnitId );
+                programStageInstance.setOrganisationUnit( organisationUnit );
+            }
+            catch ( Exception e )
+            {
+                programStageInstance.setOrganisationUnit( null );
+            }
+
             for ( org.hisp.dhis.api.mobile.model.LWUITmodel.ProgramStageDataElement dataElement1 : dataElements )
             {
                 DataElement dataElement = dataElementService.getDataElement( dataElement1.getId() );
@@ -578,8 +590,10 @@
             }
             else
             {
-                // programStageInstance.setCompleted(
-                // mobileProgramStage.isCompleted() );
+                if ( mobileProgramStage.isCompleted() )
+                {
+                    programStageInstance.setStatus( EventStatus.COMPLETED );
+                }
                 programStageInstanceService.updateProgramStageInstance( programStageInstance );
 
                 // check if all belonged program stage are completed
@@ -686,7 +700,14 @@
                     if ( mobileProgramStage != null && mobileProgramStage.getDataElements().size() > 0 )
                     {
                         mobileProgramStage.setId( programStageInstanceId );
-                        this.saveProgramStage( mobileProgramStage, patientId, patient.getOrganisationUnit().getId() );
+                        if ( mobileProgramStage.isSingleEvent() )
+                        {
+                            this.saveProgramStage( mobileProgramStage, patientId, patient.getOrganisationUnit().getId() );
+                        }
+                        else
+                        {
+                            this.saveProgramStage( mobileProgramStage, patientId, 0 );
+                        }
                     }
                 }
 
@@ -856,9 +877,20 @@
                 .getPersonBId() );
             List<TrackedEntityAttributeValue> attributes = new ArrayList<TrackedEntityAttributeValue>(
                 relative.getAttributeValues() );
+            List<TrackedEntityAttributeValue> attributesInList = new ArrayList<TrackedEntityAttributeValue>();
+            
+            for ( TrackedEntityAttributeValue value : attributes )
+            {
+                if ( value != null && value.getAttribute().getDisplayInListNoProgram() )
+                {
+                    attributesInList.add( value );
+                }
+            }
+
+            Collections.sort( attributesInList, new TrackedEntityAttributeValueSortOrderComparator() );
 
             String relativeName = "";
-            for ( TrackedEntityAttributeValue value : attributes )
+            for ( TrackedEntityAttributeValue value : attributesInList )
             {
                 if ( value != null && value.getAttribute().getDisplayInListNoProgram() )
                 {
@@ -894,8 +926,12 @@
         ProgramInstance programInstance )
     {
         List<org.hisp.dhis.api.mobile.model.LWUITmodel.ProgramStage> mobileProgramStages = new ArrayList<>();
-
-        for ( ProgramStageInstance eachProgramStageInstance : programInstance.getProgramStageInstances() )
+        List<ProgramStageInstance> proStageInstanceList = new ArrayList<ProgramStageInstance>(
+            programInstance.getProgramStageInstances() );
+
+        Collections.sort( proStageInstanceList, new ProgramStageInstanceVisitDateComparator() );
+
+        for ( ProgramStageInstance eachProgramStageInstance : proStageInstanceList )
         {
             // only for Mujhu database, because there is null program stage
             // instance. This condition should be removed in the future
@@ -1097,8 +1133,7 @@
         }
         else
         {
-            String instanceInfo = findPatientInAdvanced( enrollmentRelationship.getPersonBName(), orgUnitId,
-                0 );
+            String instanceInfo = findPatientInAdvanced( enrollmentRelationship.getPersonBName(), orgUnitId, 0 );
             if ( instanceInfo == null || instanceInfo.trim().length() == 0 )
             {
                 throw NotAllowedException.NO_BENEFICIARY_FOUND;
@@ -1430,10 +1465,24 @@
         this.programStageService = programStageService;
     }
 
+    private TrackedEntityInstanceStore instanceStore;
+
+    public void setInstanceStore( TrackedEntityInstanceStore instanceStore )
+    {
+        this.instanceStore = instanceStore;
+    }
+
+    private I18nFormat format;
+
+    public void setFormat( I18nFormat format )
+    {
+        this.format = format;
+    }
+
     @Override
     public Patient savePatient( org.hisp.dhis.api.mobile.model.LWUITmodel.Patient patient, int orgUnitId,
         String programIdText )
-        throws NotAllowedException
+    throws NotAllowedException
     {
         TrackedEntityInstance patientWeb = new TrackedEntityInstance();
         patientWeb.setOrganisationUnit( organisationUnitService.getOrganisationUnit( orgUnitId ) );
@@ -1463,10 +1512,11 @@
             }
         }
 
-        patientWeb.setTrackedEntity( trackedEntityService.getTrackedEntityByName( "Person" ) );
+        patientWeb.setTrackedEntity( trackedEntityService.getTrackedEntityByName( patient.getTrackedEntityName() ) );
         patientId = entityInstanceService.createTrackedEntityInstance( patientWeb, null, null, patientAttributeValues );
         TrackedEntityInstance newTrackedEntityInstance = entityInstanceService
             .getTrackedEntityInstance( this.patientId );
+        String errorMsg = null;
         try
         {
             for ( org.hisp.dhis.api.mobile.model.LWUITmodel.ProgramInstance mobileProgramInstance : patient
@@ -1476,10 +1526,33 @@
                 enrollProgram( patientId + "-" + mobileProgramInstance.getProgramId(),
                     mobileProgramInstance.getProgramStageInstances(), incidentDate );
             }
+            
+            Program program = programService.getProgram( Integer.parseInt( programIdText ) );
+            String[] errorCode = entityInstanceService.validateTrackedEntityInstance( newTrackedEntityInstance,
+                program, format ).split( "_" );
+            int code = Integer.parseInt( errorCode[0] );
+
+            if ( code >= 1 )
+            {
+                entityInstanceService.deleteTrackedEntityInstance( newTrackedEntityInstance );
+                if ( code == TrackedEntityInstanceService.ERROR_DUPLICATE_IDENTIFIER )
+                {
+                    errorMsg = "Duplicate value of " + attributeService.getTrackedEntityAttribute( Integer.parseInt( errorCode[1] ) ).getDisplayName();
+                }
+                else
+                {
+                    errorMsg = "Validation error";
+                }
+            }
         }
         catch ( Exception e )
         {
-            throw new NotAllowedException( e.getMessage() );
+            e.printStackTrace();
+        }
+        
+        if(errorMsg!=null)
+        {
+            throw new NotAllowedException( errorMsg );
         }
 
         return getPatientModel( newTrackedEntityInstance );
@@ -1487,6 +1560,103 @@
     }
 
     @Override
+    public Patient updatePatient( org.hisp.dhis.api.mobile.model.LWUITmodel.Patient patient, int orgUnitId,
+        String programIdText )
+        throws NotAllowedException
+    {
+        TrackedEntityInstance entityInstance = entityInstanceService.getTrackedEntityInstance( patient.getId() );
+        TrackedEntityInstance tempTEI = entityInstance;
+        TrackedEntity trackedEntity = null;
+
+        Program program = programService.getProgram( Integer.parseInt( programIdText ) );
+        trackedEntity = program.getTrackedEntity();
+
+        entityInstance.setTrackedEntity( trackedEntity );
+
+        // get attributes to be saved/updated/deleted
+        Collection<TrackedEntityAttribute> attributes = attributeService.getAllTrackedEntityAttributes();
+
+        List<TrackedEntityAttributeValue> valuesForSave = new ArrayList<TrackedEntityAttributeValue>();
+        List<TrackedEntityAttributeValue> valuesForUpdate = new ArrayList<TrackedEntityAttributeValue>();
+        Collection<TrackedEntityAttributeValue> valuesForDelete = null;
+
+        TrackedEntityAttributeValue attributeValue = null;
+
+        Collection<org.hisp.dhis.api.mobile.model.PatientAttribute> attributesMobile = patient.getAttributes();
+
+        if ( attributes != null && attributes.size() > 0 )
+        {
+            valuesForDelete = attValueService.getTrackedEntityAttributeValues( entityInstance );
+            tempTEI.getAttributeValues().clear();
+
+            for ( TrackedEntityAttribute attribute : attributes )
+            {
+                String value = getAttributeValue( attributesMobile, attribute.getName() );
+
+                if ( value != null )
+                {
+                    attributeValue = attValueService.getTrackedEntityAttributeValue( entityInstance, attribute );
+
+                    if ( attributeValue == null )
+                    {
+                        attributeValue = new TrackedEntityAttributeValue();
+                        attributeValue.setEntityInstance( entityInstance );
+                        attributeValue.setAttribute( attribute );
+                        attributeValue.setValue( value.trim() );
+                        valuesForSave.add( attributeValue );
+                    }
+                    else
+                    {
+                        attributeValue.setValue( value.trim() );
+                        valuesForUpdate.add( attributeValue );
+                        valuesForDelete.remove( attributeValue );
+                    }
+                    tempTEI.getAttributeValues().add( attributeValue );
+                }
+            }
+        }
+
+        // validate
+        String[] errorCode = entityInstanceService.validateTrackedEntityInstance( tempTEI, program, format )
+            .split( "_" );
+        int code = Integer.parseInt( errorCode[0] );
+
+        if ( code >= 1 )
+        {
+            if ( code == TrackedEntityInstanceService.ERROR_DUPLICATE_IDENTIFIER )
+            {
+                throw new NotAllowedException( "Duplicate value of "
+                    + attributeService.getTrackedEntityAttribute( Integer.parseInt( errorCode[1] ) ).getDisplayName() );
+            }
+            else
+            {
+                throw new NotAllowedException( "Validation error" );
+            }
+        }
+
+        entityInstanceService.updateTrackedEntityInstance( entityInstance, null, null, valuesForSave, valuesForUpdate,
+            valuesForDelete );
+        enrollProgram( patient.getId() + "-" + programIdText, null, new Date() );
+        entityInstance = entityInstanceService.getTrackedEntityInstance( patient.getId() );
+        entityInstance.setTrackedEntity( trackedEntity );
+
+        return getPatientModel( entityInstance );
+    }
+
+    public String getAttributeValue( Collection<org.hisp.dhis.api.mobile.model.PatientAttribute> attributesMobile,
+        String attributeName )
+    {
+        for ( org.hisp.dhis.api.mobile.model.PatientAttribute attributeMobile : attributesMobile )
+        {
+            if ( attributeMobile.getName().equals( attributeName ) )
+            {
+                return attributeMobile.getValue();
+            }
+        }
+        return null;
+    }
+
+    @Override
     public org.hisp.dhis.api.mobile.model.LWUITmodel.Patient findPatient( String patientId )
         throws NotAllowedException
     {
@@ -1633,10 +1803,11 @@
             // NOTE: this line should be here but because the mobile client uses
             // the int TEI id, we will temprarily get the int id for now.
             // instanceInfo += (String) row.get( instanceIndex ) + "/";
-                        
-            TrackedEntityInstance tei = entityInstanceService.getTrackedEntityInstance( (String) row.get( instanceIndex ) );
+
+            TrackedEntityInstance tei = entityInstanceService.getTrackedEntityInstance( (String) row
+                .get( instanceIndex ) );
             instanceInfo += tei.getId() + "/";
-            //end of temproary fix
+            // end of temproary fix
 
             String attText = "";
             for ( Integer attIndex : attributesIndex )
@@ -1799,23 +1970,44 @@
     public org.hisp.dhis.api.mobile.model.LWUITmodel.Patient generateRepeatableEvent( int orgUnitId, String eventInfo )
         throws NotAllowedException
     {
-        OrganisationUnit orgUnit = organisationUnitService.getOrganisationUnit( orgUnitId );
-
-        String mobileProgramStageId = eventInfo.substring( 0, eventInfo.indexOf( "$" ) );
-
-        String nextDueDate = eventInfo.substring( eventInfo.indexOf( "$" ) + 1, eventInfo.length() );
-
-        ProgramStageInstance oldProgramStageIntance = programStageInstanceService.getProgramStageInstance( Integer
-            .valueOf( mobileProgramStageId ) );
-
-        ProgramInstance programInstance = oldProgramStageIntance.getProgramInstance();
-
-        ProgramStageInstance newProgramStageInstance = new ProgramStageInstance( programInstance,
-            oldProgramStageIntance.getProgramStage() );
-
-        newProgramStageInstance.setDueDate( PeriodUtil.stringToDate( nextDueDate ) );
-
-        newProgramStageInstance.setOrganisationUnit( orgUnit );
+      //OrganisationUnit orgUnit = organisationUnitService.getOrganisationUnit( orgUnitId );
+
+        String[] keys = eventInfo.split( "_" );
+        ProgramStage programStage = programStageService.getProgramStage( Integer.parseInt( keys[4] ) );
+        int mobileProgramStageId = Integer.parseInt( keys[3] );
+        String nextDueDate = keys[2];
+        Program program = programService.getProgram( Integer.parseInt( keys[1] ) );
+        TrackedEntityInstance trackedEntityInstance = entityInstanceService.getTrackedEntityInstance( Integer
+            .parseInt( keys[0] ) );
+
+        ProgramInstance programInstance = null;
+        ProgramStageInstance newProgramStageInstance = null;
+        if ( mobileProgramStageId != 0 )
+        {
+            ProgramStageInstance oldProgramStageIntance = programStageInstanceService
+                .getProgramStageInstance( mobileProgramStageId );
+
+            programInstance = oldProgramStageIntance.getProgramInstance();
+
+            newProgramStageInstance = new ProgramStageInstance( programInstance,
+                oldProgramStageIntance.getProgramStage() );
+
+            newProgramStageInstance.setDueDate( PeriodUtil.stringToDate( nextDueDate ) );
+
+            //newProgramStageInstance.setOrganisationUnit( orgUnit );
+        }
+        else
+        {
+            programInstance = programInstanceService.getProgramInstances( trackedEntityInstance, program ).iterator()
+                .next();
+
+            newProgramStageInstance = new ProgramStageInstance();
+            newProgramStageInstance.setProgramInstance( programInstance );
+            newProgramStageInstance.setProgramStage( programStage );
+            newProgramStageInstance.setDueDate( PeriodUtil.stringToDate( nextDueDate ) );
+            newProgramStageInstance.setExecutionDate( PeriodUtil.stringToDate( nextDueDate ) );
+            //newProgramStageInstance.setOrganisationUnit( orgUnit );
+        }
 
         programInstance.getProgramStageInstances().add( newProgramStageInstance );
 
@@ -1831,9 +2023,9 @@
 
         programInstanceService.updateProgramInstance( programInstance );
 
-        org.hisp.dhis.api.mobile.model.LWUITmodel.Patient mobilePatient = getPatientModel( entityInstanceService
-            .getTrackedEntityInstance( programInstance.getEntityInstance().getId() ) ); // TODO:
-                                                                                        // SHERIE
+        TrackedEntityInstance tei = entityInstanceService.getTrackedEntityInstance( programInstance.getEntityInstance()
+            .getId() );
+        org.hisp.dhis.api.mobile.model.LWUITmodel.Patient mobilePatient = getPatientModel( tei );
 
         return mobilePatient;
     }
@@ -2317,4 +2509,113 @@
         this.trackedEntityService = trackedEntityService;
     }
 
+    @Override
+    public Patient registerRelative( Patient patient, int orgUnitId, String programId )
+        throws NotAllowedException
+    {
+        TrackedEntityInstance patientWeb = new TrackedEntityInstance();
+        patientWeb.setOrganisationUnit( organisationUnitService.getOrganisationUnit( orgUnitId ) );
+
+        Set<TrackedEntityAttribute> patientAttributeSet = new HashSet<TrackedEntityAttribute>();
+        Set<TrackedEntityAttributeValue> patientAttributeValues = new HashSet<TrackedEntityAttributeValue>();
+
+        Collection<org.hisp.dhis.api.mobile.model.PatientAttribute> attributesMobile = patient.getAttributes();
+
+        if ( attributesMobile != null )
+        {
+            for ( org.hisp.dhis.api.mobile.model.PatientAttribute paAtt : attributesMobile )
+            {
+
+                TrackedEntityAttribute patientAttribute = attributeService.getTrackedEntityAttributeByName( paAtt
+                    .getName() );
+
+                patientAttributeSet.add( patientAttribute );
+
+                TrackedEntityAttributeValue patientAttributeValue = new TrackedEntityAttributeValue();
+
+                patientAttributeValue.setEntityInstance( patientWeb );
+                patientAttributeValue.setAttribute( patientAttribute );
+                patientAttributeValue.setValue( paAtt.getValue() );
+                patientAttributeValues.add( patientAttributeValue );
+
+            }
+        }
+
+        patientWeb.setTrackedEntity( trackedEntityService.getTrackedEntityByName( patient.getTrackedEntityName() ) );
+
+        if ( patient.getIdToAddRelative() != 0 )
+        {
+            TrackedEntityInstance relative = entityInstanceService.getTrackedEntityInstance( patient
+                .getIdToAddRelative() );
+            if ( relative == null )
+            {
+                throw new NotAllowedException( "relative does not exist" );
+            }
+            patientId = entityInstanceService.createTrackedEntityInstance( patientWeb, relative.getUid(),
+                patient.getRelTypeIdToAdd(), patientAttributeValues );
+        }
+        else
+        {
+            patientId = entityInstanceService.createTrackedEntityInstance( patientWeb, null, null,
+                patientAttributeValues );
+        }
+        TrackedEntityInstance newTrackedEntityInstance = entityInstanceService
+            .getTrackedEntityInstance( this.patientId );
+
+        String errorMsg = null;
+
+        try
+        {
+
+            for ( org.hisp.dhis.api.mobile.model.LWUITmodel.ProgramInstance mobileProgramInstance : patient
+                .getEnrollmentPrograms() )
+            {
+                Date incidentDate = PeriodUtil.stringToDate( mobileProgramInstance.getDateOfIncident() );
+                enrollProgram( patientId + "-" + mobileProgramInstance.getProgramId(),
+                    mobileProgramInstance.getProgramStageInstances(), incidentDate );
+            }
+
+            Program program = programService.getProgram( Integer.parseInt( programId ) );
+            String[] errorCode = entityInstanceService.validateTrackedEntityInstance( newTrackedEntityInstance,
+                program, format ).split( "_" );
+            int code = Integer.parseInt( errorCode[0] );
+
+            if ( code >= 1 )
+            {
+                entityInstanceService.deleteTrackedEntityInstance( newTrackedEntityInstance );
+                if ( code == TrackedEntityInstanceService.ERROR_DUPLICATE_IDENTIFIER )
+                {
+                    errorMsg = "Duplicate value of "
+                        + attributeService.getTrackedEntityAttribute( Integer.parseInt( errorCode[1] ) )
+                            .getDisplayName();
+                }
+                else
+                {
+                    errorMsg = "Validation error";
+                }
+            }
+
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
+        if ( errorMsg != null )
+        {
+            throw new NotAllowedException( errorMsg );
+        }
+
+        if ( patient.getEnrollmentRelationship() != null )
+        {
+            org.hisp.dhis.api.mobile.model.LWUITmodel.Relationship enrollmentRelationship = patient
+                .getEnrollmentRelationship();
+            enrollmentRelationship.setPersonBId( newTrackedEntityInstance.getId() );
+            addRelationship( enrollmentRelationship, orgUnitId );
+        }
+
+        return getPatientModel( newTrackedEntityInstance );
+
+    }
+
 }
\ No newline at end of file

=== modified file 'dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/DefaultProgramService.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/DefaultProgramService.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/main/java/org/hisp/dhis/mobile/service/DefaultProgramService.java	2015-01-28 10:50:56 +0000
@@ -298,6 +298,13 @@
             pr.getProgramAttributes().add( this.getPatientAttributeForMobile( ppa ) );
         }
 
+        if ( program.getRelatedProgram() != null )
+        {
+            pr.setRelationshipText( program.getRelationshipText() );
+            pr.setRelatedProgramId( program.getRelatedProgram().getId() );
+            pr.setRelationshipType( program.getRelationshipType().getId() );
+        }
+
         return pr;
     }
 
@@ -319,6 +326,8 @@
         {
             mobileAttribute.setDisplayedInList( false );
         }
+        
+        mobileAttribute.setMandatory( ppa.isMandatory() );
 
         if ( pa.getValueType().equals( TrackedEntityAttribute.TYPE_OPTION_SET ) )
         {

=== modified file 'dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitTest.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitTest.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitTest.java	2015-01-28 10:50:56 +0000
@@ -84,6 +84,7 @@
         unit.setGenerateRepeatableEventUrl( "generateRepeatableEventUrl" );
         unit.setUploadSingleEventWithoutRegistration( "uploadSingleEventWithoutRegistration" );
         unit.setCompleteProgramInstanceUrl( "completeProgramInstance" );
+        unit.setRegisterRelativeUrl( "registerRelativeUrl" );
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DataOutputStream dos = new DataOutputStream( baos );

=== modified file 'dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitsTest.java'
--- dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitsTest.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-mobile/src/test/java/org/hisp/dhis/mobile/api/model/OrgUnitsTest.java	2015-01-28 10:50:56 +0000
@@ -109,6 +109,7 @@
         orgUnit.setGenerateRepeatableEventUrl( "generateRepeatableEvent" );
         orgUnit.setUploadSingleEventWithoutRegistration( "uploadSingleEventWithoutRegistration" );
         orgUnit.setCompleteProgramInstanceUrl( "completeProgramInstance" );
+        orgUnit.setRegisterRelativeUrl( "registerRelative" );
 
         return orgUnit;
     }

=== modified file 'dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java'
--- dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-services/dhis-service-tracker/src/main/java/org/hisp/dhis/trackedentity/hibernate/HibernateTrackedEntityInstanceStore.java	2015-01-28 10:50:56 +0000
@@ -217,9 +217,9 @@
         final String wordEnd = statementBuilder.getRegexpWordEnd();
         final String anyChar = "\\.*?";
 
-        String sql = "from trackedentityinstance tei " + 
-            "inner join trackedentity te on tei.trackedentityid = te.trackedentityid " +
-            "inner join organisationunit ou on tei.organisationunitid = ou.organisationunitid ";
+        String sql = "from trackedentityinstance tei "
+            + "inner join trackedentity te on tei.trackedentityid = te.trackedentityid "
+            + "inner join organisationunit ou on tei.organisationunitid = ou.organisationunitid ";
 
         for ( QueryItem item : params.getAttributesAndFilters() )
         {
@@ -227,14 +227,14 @@
 
             final String joinClause = item.hasFilter() ? "inner join" : "left join";
 
-            sql += joinClause + " " + 
-                "trackedentityattributevalue as " + col + " " + "on " + col + ".trackedentityinstanceid = tei.trackedentityinstanceid " + 
-                "and " + col + ".trackedentityattributeid = " + item.getItem().getId() + " ";
+            sql += joinClause + " " + "trackedentityattributevalue as " + col + " " + "on " + col
+                + ".trackedentityinstanceid = tei.trackedentityinstanceid " + "and " + col
+                + ".trackedentityattributeid = " + item.getItem().getId() + " ";
 
             if ( !params.isOrQuery() && item.hasFilter() )
             {
                 for ( QueryFilter filter : item.getFilters() )
-                {                    
+                {
                     final String encodedFilter = statementBuilder.encode( filter.getFilter(), false );
 
                     final String queryCol = item.isNumeric() ? (col + ".value") : "lower(" + col + ".value)";
@@ -270,7 +270,8 @@
         else if ( params.isOrganisationUnitMode( OrganisationUnitSelectionMode.ALL ) )
         {
         }
-        else // SELECTED (default)
+        else
+        // SELECTED (default)
         {
             sql += hlp.whereAnd() + " tei.organisationunitid in ("
                 + getCommaDelimitedString( getIdentifiers( params.getOrganisationUnits() ) ) + ") ";
@@ -278,20 +279,15 @@
 
         if ( params.hasProgram() )
         {
-            sql += hlp.whereAnd() + " exists (" +
-                "select pi.trackedentityinstanceid " +
-                "from programinstance pi ";
+            sql += hlp.whereAnd() + " exists (" + "select pi.trackedentityinstanceid " + "from programinstance pi ";
 
             if ( params.hasEventStatus() )
             {
-                sql += 
-                    "left join programstageinstance psi " +
-                    "on pi.programinstanceid = psi.programinstanceid ";
+                sql += "left join programstageinstance psi " + "on pi.programinstanceid = psi.programinstanceid ";
             }
 
-            sql += 
-                "where pi.trackedentityinstanceid = tei.trackedentityinstanceid " +
-                "and pi.programid = " + params.getProgram().getId() + " ";
+            sql += "where pi.trackedentityinstanceid = tei.trackedentityinstanceid " + "and pi.programid = "
+                + params.getProgram().getId() + " ";
 
             if ( params.hasProgramStatus() )
             {
@@ -325,7 +321,7 @@
         {
             final String start = params.getQuery().isOperator( QueryOperator.LIKE ) ? anyChar : wordStart;
             final String end = params.getQuery().isOperator( QueryOperator.LIKE ) ? anyChar : wordEnd;
-            
+
             sql += hlp.whereAnd() + " (";
 
             List<String> queryTokens = getTokens( params.getQuery().getFilter() );
@@ -340,9 +336,7 @@
                 {
                     final String col = statementBuilder.columnQuote( item.getItemId() );
 
-                    sql += 
-                        col + ".value " + regexp + " '" + start + 
-                        StringUtils.lowerCase( query ) + end + "' or ";
+                    sql += col + ".value " + regexp + " '" + start + StringUtils.lowerCase( query ) + end + "' or ";
                 }
 
                 sql = removeLastOr( sql ) + ") and ";
@@ -363,33 +357,28 @@
 
         if ( params.isEventStatus( EventStatus.COMPLETED ) )
         {
-            sql = 
-                "and psi.executiondate >= '" + start + "' and psi.executiondate <= '" + end + "' " +
-                "and psi.status = '" + EventStatus.COMPLETED.name() + "' ";
+            sql = "and psi.executiondate >= '" + start + "' and psi.executiondate <= '" + end + "' "
+                + "and psi.status = '" + EventStatus.COMPLETED.name() + "' ";
         }
         else if ( params.isEventStatus( EventStatus.VISITED ) )
         {
-            sql = 
-                "and psi.executiondate >= '" + start + "' and psi.executiondate <= '" + end + "' " + 
-                "and psi.status = '" + EventStatus.ACTIVE.name() + "' ";
+            sql = "and psi.executiondate >= '" + start + "' and psi.executiondate <= '" + end + "' "
+                + "and psi.status = '" + EventStatus.ACTIVE.name() + "' ";
         }
         else if ( params.isEventStatus( EventStatus.SCHEDULE ) )
         {
-            sql = 
-                "and psi.executiondate is null and psi.duedate >= '" + start + "' and psi.duedate <= '" + end + "' " +
-                "and psi.status is not null and date(now()) <= date(psi.duedate) ";
+            sql = "and psi.executiondate is null and psi.duedate >= '" + start + "' and psi.duedate <= '" + end + "' "
+                + "and psi.status is not null and date(now()) <= date(psi.duedate) ";
         }
         else if ( params.isEventStatus( EventStatus.OVERDUE ) )
         {
-            sql = 
-                "and psi.executiondate is null and psi.duedate >= '" + start + "' and psi.duedate <= '" + end + "' " +
-                "and psi.status is not null and date(now()) > date(psi.duedate) ";
+            sql = "and psi.executiondate is null and psi.duedate >= '" + start + "' and psi.duedate <= '" + end + "' "
+                + "and psi.status is not null and date(now()) > date(psi.duedate) ";
         }
         else if ( params.isEventStatus( EventStatus.SKIPPED ) )
         {
-            sql = 
-                "and psi.duedate >= '" + start + "' and psi.duedate <= '" + end + "' " +
-                "and psi.status = '" + EventStatus.SKIPPED.name() + "' ";
+            sql = "and psi.duedate >= '" + start + "' and psi.duedate <= '" + end + "' " + "and psi.status = '"
+                + EventStatus.SKIPPED.name() + "' ";
         }
 
         return sql;
@@ -399,41 +388,48 @@
     public String validate( TrackedEntityInstance instance, TrackedEntityAttributeValue attributeValue, Program program )
     {
         TrackedEntityAttribute attribute = attributeValue.getAttribute();
-
-        if ( attribute.isUnique() )
-        {
-            Criteria criteria = getCriteria();
-            criteria.add( Restrictions.ne( "id", instance.getId() ) );
-            criteria.createAlias( "attributeValues", "attributeValue" );
-            criteria.createAlias( "attributeValue.attribute", "attribute" );
-            criteria.add( Restrictions.eq( "attributeValue.value", attributeValue.getValue() ) );
-            criteria.add( Restrictions.eq( "attributeValue.attribute", attribute ) );
-
-            if ( attribute.getId() != 0 )
-            {
-                criteria.add( Restrictions.ne( "id", attribute.getId() ) );
-            }
-
-            if ( attribute.getOrgunitScope() )
-            {
-                criteria.add( Restrictions.eq( "organisationUnit", instance.getOrganisationUnit() ) );
-            }
-
-            if ( program != null && attribute.getProgramScope() )
-            {
-                criteria.createAlias( "programInstances", "programInstance" );
-                criteria.add( Restrictions.eq( "programInstance.program", program ) );
-            }
-
-            Number rs = (Number) criteria.setProjection( Projections.projectionList().add( 
-                Projections.property( "attribute.id" ) ) ).uniqueResult();
-
-            if ( rs != null && rs.intValue() > 0 )
-            {
-                return ERROR_DUPLICATE_IDENTIFIER + SEPARATOR + rs.intValue();
-            }
-        }
-        
+        try
+        {
+
+            if ( attribute.isUnique() )
+            {
+                Criteria criteria = getCriteria();
+                criteria.add( Restrictions.ne( "id", instance.getId() ) );
+                criteria.createAlias( "attributeValues", "attributeValue" );
+                criteria.createAlias( "attributeValue.attribute", "attribute" );
+                criteria.add( Restrictions.eq( "attributeValue.value", attributeValue.getValue() ) );
+                criteria.add( Restrictions.eq( "attributeValue.attribute", attribute ) );
+
+                if ( attribute.getId() != 0 )
+                {
+                    criteria.add( Restrictions.ne( "id", attribute.getId() ) );
+                }
+
+                if ( attribute.getOrgunitScope() )
+                {
+                    criteria.add( Restrictions.eq( "organisationUnit", instance.getOrganisationUnit() ) );
+                }
+
+                if ( program != null && attribute.getProgramScope() )
+                {
+                    criteria.createAlias( "programInstances", "programInstance" );
+                    criteria.add( Restrictions.eq( "programInstance.program", program ) );
+                }
+
+                Number rs = (Number) criteria.setProjection(
+                    Projections.projectionList().add( Projections.property( "attribute.id" ) ) ).uniqueResult();
+
+                if ( rs != null && rs.intValue() > 0 )
+                {
+                    return ERROR_DUPLICATE_IDENTIFIER + SEPARATOR + rs.intValue();
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+
         return null;
     }
 }

=== modified file 'dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileClientController.java'
--- dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileClientController.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileClientController.java	2015-01-28 10:50:56 +0000
@@ -174,6 +174,7 @@
         orgUnit.setUploadSingleEventWithoutRegistration( getUrl( request, unit.getId(),
             "uploadSingleEventWithoutRegistration" ) );
         orgUnit.setCompleteProgramInstanceUrl(getUrl( request, unit.getId(), "completeProgramInstance" ) );
+        orgUnit.setRegisterRelativeUrl(getUrl( request, unit.getId(), "registerRelative" ));
 
         // generate URL for download new version
         String full = UrlUtils.buildFullRequestUrl( request );

=== modified file 'dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileOrganisationUnitController.java'
--- dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileOrganisationUnitController.java	2015-01-17 07:41:26 +0000
+++ dhis-2/dhis-web/dhis-web-api-mobile/src/main/java/org/hisp/dhis/api/mobile/controller/MobileOrganisationUnitController.java	2015-01-28 10:50:56 +0000
@@ -90,7 +90,7 @@
 
     @Autowired
     private IProgramService programService;
-    
+
     @Autowired
     private RelationshipTypeService relationshipTypeService;
 
@@ -121,9 +121,7 @@
     // For client version 2.8 and lower
     @RequestMapping( method = RequestMethod.GET, value = "orgUnits/{id}/all" )
     @ResponseBody
-    public MobileModel getAllDataForOrgUnit2_8( @PathVariable
-    int id, @RequestHeader( "accept-language" )
-    String locale )
+    public MobileModel getAllDataForOrgUnit2_8( @PathVariable int id, @RequestHeader( "accept-language" ) String locale )
     {
         MobileModel mobileModel = new MobileModel();
         mobileModel.setClientVersion( DataStreamSerializable.TWO_POINT_EIGHT );
@@ -138,10 +136,8 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "orgUnits/{id}/updateDataSets" )
     @ResponseBody
-    public DataSetList checkUpdatedDataSet2_8( @PathVariable
-    int id, @RequestBody
-    DataSetList dataSetList, @RequestHeader( "accept-language" )
-    String locale )
+    public DataSetList checkUpdatedDataSet2_8( @PathVariable int id, @RequestBody DataSetList dataSetList,
+        @RequestHeader( "accept-language" ) String locale )
     {
         DataSetList returnList = facilityReportingService.getUpdatedDataSet( dataSetList, getUnit( id ), locale );
         returnList.setClientVersion( DataStreamSerializable.TWO_POINT_EIGHT );
@@ -156,9 +152,7 @@
      */
     @RequestMapping( method = RequestMethod.POST, value = "orgUnits/{id}/dataSets" )
     @ResponseBody
-    public String saveDataSetValues2_8( @PathVariable
-    int id, @RequestBody
-    DataSetValue dataSetValue )
+    public String saveDataSetValues2_8( @PathVariable int id, @RequestBody DataSetValue dataSetValue )
         throws NotAllowedException
     {
         facilityReportingService.saveDataSetValues( getUnit( id ), dataSetValue );
@@ -174,9 +168,7 @@
      */
     @RequestMapping( method = RequestMethod.POST, value = "orgUnits/{id}/activities" )
     @ResponseBody
-    public String saveActivityReport2_8( @PathVariable
-    int id, @RequestBody
-    ActivityValue activityValue )
+    public String saveActivityReport2_8( @PathVariable int id, @RequestBody ActivityValue activityValue )
         throws NotAllowedException
     {
         // FIXME set the last argument to 0 to fix compilation error
@@ -186,10 +178,8 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "orgUnits/{id}/activitiyplan" )
     @ResponseBody
-    public MobileModel updatePrograms2_8( @PathVariable
-    int id, @RequestHeader( "accept-language" )
-    String locale, @RequestBody
-    ModelList programsFromClient )
+    public MobileModel updatePrograms2_8( @PathVariable int id, @RequestHeader( "accept-language" ) String locale,
+        @RequestBody ModelList programsFromClient )
     {
         MobileModel model = new MobileModel();
         model.setClientVersion( DataStreamSerializable.TWO_POINT_EIGHT );
@@ -201,9 +191,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "orgUnits/{id}/changeLanguageDataSet" )
     @ResponseBody
-    public DataSetList changeLanguageDataSet2_8( @PathVariable
-    int id, @RequestHeader( "accept-language" )
-    String locale )
+    public DataSetList changeLanguageDataSet2_8( @PathVariable int id, @RequestHeader( "accept-language" ) String locale )
     {
         return facilityReportingService.getDataSetsForLocale( getUnit( id ), locale );
     }
@@ -212,10 +200,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/orgUnits/{id}/all" )
     @ResponseBody
-    public MobileModel getAllDataForOrgUnit( @PathVariable
-    String clientVersion, @PathVariable
-    int id, @RequestHeader( "accept-language" )
-    String locale )
+    public MobileModel getAllDataForOrgUnit( @PathVariable String clientVersion, @PathVariable int id,
+        @RequestHeader( "accept-language" ) String locale )
     {
         MobileModel mobileModel = new MobileModel();
         mobileModel.setClientVersion( clientVersion );
@@ -233,11 +219,8 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/updateDataSets" )
     @ResponseBody
-    public DataSetList checkUpdatedDataSet( @PathVariable
-    String clientVersion, @PathVariable
-    int id, @RequestBody
-    DataSetList dataSetList, @RequestHeader( "accept-language" )
-    String locale )
+    public DataSetList checkUpdatedDataSet( @PathVariable String clientVersion, @PathVariable int id,
+        @RequestBody DataSetList dataSetList, @RequestHeader( "accept-language" ) String locale )
     {
         DataSetList returnList = facilityReportingService.getUpdatedDataSet( dataSetList, getUnit( id ), locale );
         returnList.setClientVersion( clientVersion );
@@ -253,9 +236,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/dataSets" )
     @ResponseBody
-    public String saveDataSetValues( @PathVariable
-    int id, @RequestBody
-    DataSetValue dataSetValue )
+    public String saveDataSetValues( @PathVariable int id, @RequestBody DataSetValue dataSetValue )
         throws NotAllowedException
     {
         facilityReportingService.saveDataSetValues( getUnit( id ), dataSetValue );
@@ -264,9 +245,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/dataSetValue" )
     @ResponseBody
-    public DataSetValueList getDataSetValues( @PathVariable
-    int id, @RequestBody
-    DataSetList dataSetList )
+    public DataSetValueList getDataSetValues( @PathVariable int id, @RequestBody DataSetList dataSetList )
         throws NotAllowedException
     {
         return facilityReportingService.getDataSetValues( getUnit( id ), dataSetList );
@@ -274,11 +253,8 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/activitiyplan" )
     @ResponseBody
-    public MobileModel updatePrograms( @PathVariable
-    String clientVersion, @PathVariable
-    int id, @RequestHeader( "accept-language" )
-    String locale, @RequestBody
-    ModelList programsFromClient )
+    public MobileModel updatePrograms( @PathVariable String clientVersion, @PathVariable int id,
+        @RequestHeader( "accept-language" ) String locale, @RequestBody ModelList programsFromClient )
     {
         MobileModel model = new MobileModel();
         model.setClientVersion( clientVersion );
@@ -316,9 +292,7 @@
      */
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/activities" )
     @ResponseBody
-    public String saveActivityReport( @PathVariable
-    int id, @RequestBody
-    ActivityValue activityValue )
+    public String saveActivityReport( @PathVariable int id, @RequestBody ActivityValue activityValue )
         throws NotAllowedException
     {
         // FIXME set the last argument to 0 to fix compilation error
@@ -328,9 +302,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/orgUnits/{id}/changeLanguageDataSet" )
     @ResponseBody
-    public DataSetList changeLanguageDataSet( @PathVariable
-    int id, @RequestHeader( "accept-language" )
-    String locale )
+    public DataSetList changeLanguageDataSet( @PathVariable int id, @RequestHeader( "accept-language" ) String locale )
     {
         return facilityReportingService.getDataSetsForLocale( getUnit( id ), locale );
     }
@@ -344,9 +316,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/all" )
     @ResponseBody
-    public org.hisp.dhis.api.mobile.model.LWUITmodel.MobileModel getAllDataForOrgUnitLWUIT( @PathVariable
-    String clientVersion, @PathVariable
-    int id )
+    public org.hisp.dhis.api.mobile.model.LWUITmodel.MobileModel getAllDataForOrgUnitLWUIT(
+        @PathVariable String clientVersion, @PathVariable int id )
     {
         org.hisp.dhis.api.mobile.model.LWUITmodel.MobileModel mobileModel = new org.hisp.dhis.api.mobile.model.LWUITmodel.MobileModel();
         mobileModel.setClientVersion( clientVersion );
@@ -366,9 +337,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/findPatient" )
     @ResponseBody
-    public Patient findPatientByName( @PathVariable
-    int id, @RequestHeader( "patientId" )
-    String patientId )
+    public Patient findPatientByName( @PathVariable int id, @RequestHeader( "patientId" ) String patientId )
         throws NotAllowedException
     {
         return activityReportingService.findPatient( patientId );
@@ -376,9 +345,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/findPatients" )
     @ResponseBody
-    public PatientList findPatientsById( @PathVariable
-    int id, @RequestHeader( "patientIds" )
-    String patientIds )
+    public PatientList findPatientsById( @PathVariable int id, @RequestHeader( "patientIds" ) String patientIds )
         throws NotAllowedException
     {
         return activityReportingService.findPatients( patientIds );
@@ -386,10 +353,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/findPatientInAdvanced/{programId}" )
     @ResponseBody
-    public String findPatientInAdvanced( @PathVariable
-    int programId, @PathVariable
-    int id, @RequestHeader( "name" )
-    String keyword )
+    public String findPatientInAdvanced( @PathVariable int programId, @PathVariable int id,
+        @RequestHeader( "name" ) String keyword )
         throws NotAllowedException
     {
         return activityReportingService.findPatientInAdvanced( keyword, id, programId );
@@ -397,10 +362,8 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/uploadProgramStage/{patientId}" )
     @ResponseBody
-    public String saveProgramStage( @PathVariable
-    int patientId, @PathVariable
-    int id, @RequestBody
-    ProgramStage programStage )
+    public String saveProgramStage( @PathVariable int patientId, @PathVariable int id,
+        @RequestBody ProgramStage programStage )
         throws NotAllowedException
     {
         return activityReportingService.saveProgramStage( programStage, patientId, id );
@@ -408,9 +371,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/completeProgramInstance" )
     @ResponseBody
-    public String completeProgramInstance( @PathVariable
-    int id, @RequestBody
-    ProgramInstance programInstance )
+    public String completeProgramInstance( @PathVariable int id, @RequestBody ProgramInstance programInstance )
         throws NotAllowedException
     {
         return activityReportingService.completeProgramInstance( programInstance.getId() );
@@ -418,9 +379,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/uploadSingleEventWithoutRegistration" )
     @ResponseBody
-    public String saveSingleEventWithoutRegistration( @PathVariable
-    int id, @RequestBody
-    ProgramStage programStage )
+    public String saveSingleEventWithoutRegistration( @PathVariable int id, @RequestBody ProgramStage programStage )
         throws NotAllowedException
     {
         return activityReportingService.saveSingleEventWithoutRegistration( programStage, id );
@@ -428,9 +387,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/enrollProgram" )
     @ResponseBody
-    public Patient enrollProgram( @PathVariable
-    int id, @RequestHeader( "enrollInfo" )
-    String enrollInfo )
+    public Patient enrollProgram( @PathVariable int id, @RequestHeader( "enrollInfo" ) String enrollInfo )
         throws NotAllowedException
     {
         return activityReportingService.enrollProgram( enrollInfo, null, new Date() );
@@ -438,9 +395,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/addRelationship" )
     @ResponseBody
-    public Patient addRelationship( @PathVariable
-    int id, @RequestBody
-    Relationship enrollmentRelationship )
+    public Patient addRelationship( @PathVariable int id, @RequestBody Relationship enrollmentRelationship )
         throws NotAllowedException
     {
         return activityReportingService.addRelationship( enrollmentRelationship, id );
@@ -448,9 +403,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/downloadAnonymousProgram" )
     @ResponseBody
-    public Program getAnonymousProgram( @PathVariable
-    int id, @RequestHeader( "programType" )
-    String programType )
+    public Program getAnonymousProgram( @PathVariable int id, @RequestHeader( "programType" ) String programType )
         throws NotAllowedException
     {
         return activityReportingService.getAllProgramByOrgUnit( id, programType );
@@ -458,9 +411,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/findProgram" )
     @ResponseBody
-    public Program findProgram( @PathVariable
-    int id, @RequestHeader( "info" )
-    String programInfo )
+    public Program findProgram( @PathVariable int id, @RequestHeader( "info" ) String programInfo )
         throws NotAllowedException
     {
         return activityReportingService.findProgram( programInfo );
@@ -468,9 +419,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/findLostToFollowUp" )
     @ResponseBody
-    public String findLostToFollowUp( @PathVariable
-    int id, @RequestHeader( "searchEventInfos" )
-    String searchEventInfos )
+    public String findLostToFollowUp( @PathVariable int id, @RequestHeader( "searchEventInfos" ) String searchEventInfos )
         throws NotAllowedException
     {
         return activityReportingService.findLostToFollowUp( id, searchEventInfos );
@@ -478,9 +427,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/handleLostToFollowUp" )
     @ResponseBody
-    public Notification handleLostToFollowUp( @PathVariable
-    int id, @RequestBody
-    LostEvent lostEvent )
+    public Notification handleLostToFollowUp( @PathVariable int id, @RequestBody LostEvent lostEvent )
         throws NotAllowedException
     {
         return activityReportingService.handleLostToFollowUp( lostEvent );
@@ -488,9 +435,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/generateRepeatableEvent" )
     @ResponseBody
-    public Patient generateRepeatableEvent( @PathVariable
-    int id, @RequestHeader( "eventInfo" )
-    String eventInfo )
+    public Patient generateRepeatableEvent( @PathVariable int id, @RequestHeader( "eventInfo" ) String eventInfo )
         throws NotAllowedException
     {
         return activityReportingService.generateRepeatableEvent( id, eventInfo );
@@ -548,22 +493,25 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/registerPerson" )
     @ResponseBody
-    public Patient savePatient( @PathVariable
-    int id, @RequestBody
-    Patient patient, @RequestHeader( "programid" )
-    String programId )
+    public Patient savePatient( @PathVariable int id, @RequestBody Patient patient,
+        @RequestHeader( "programid" ) String programId )
         throws NotAllowedException
     {
-        return activityReportingService.savePatient( patient, id, programId );
+
+        if ( patient.getId() == 0 )
+        {
+            return activityReportingService.savePatient( patient, id, programId );
+        }
+        else
+        {
+            return activityReportingService.updatePatient( patient, id, programId );
+        }
     }
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/getVariesInfo" )
     @ResponseBody
-    public PatientIdentifierAndAttribute getVariesInfo( @PathVariable
-    String clientVersion, @PathVariable
-    int id, @RequestHeader( "accept-language" )
-    String locale, @RequestHeader( "programid" )
-    String programId )
+    public PatientIdentifierAndAttribute getVariesInfo( @PathVariable String clientVersion, @PathVariable int id,
+        @RequestHeader( "accept-language" ) String locale, @RequestHeader( "programid" ) String programId )
     {
         PatientIdentifierAndAttribute patientIdentifierAndAttribute = new PatientIdentifierAndAttribute();
         patientIdentifierAndAttribute.setClientVersion( clientVersion );
@@ -575,9 +523,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/sendFeedback" )
     @ResponseBody
-    public String sendFeedback( @PathVariable
-    int id, @RequestBody
-    Message message )
+    public String sendFeedback( @PathVariable int id, @RequestBody Message message )
         throws NotAllowedException
     {
         return activityReportingService.sendFeedback( message );
@@ -586,9 +532,7 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/orgUnits/{id}/findUser" )
     @ResponseBody
-    public Recipient findUser( String clientVersion, @PathVariable
-    int id, @RequestHeader( "name" )
-    String keyword )
+    public Recipient findUser( String clientVersion, @PathVariable int id, @RequestHeader( "name" ) String keyword )
         throws NotAllowedException
     {
         Recipient recipient = new Recipient();
@@ -598,10 +542,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/findVisitSchedule/{programId}" )
     @ResponseBody
-    public String findVisitSchedule( @PathVariable
-    int programId, @PathVariable
-    int id, @RequestHeader( "details" )
-    String info )
+    public String findVisitSchedule( @PathVariable int programId, @PathVariable int id,
+        @RequestHeader( "details" ) String info )
         throws NotAllowedException
     {
         return activityReportingService.findVisitSchedule( id, programId, info );
@@ -609,9 +551,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/sendMessage" )
     @ResponseBody
-    public String sendMessage( @PathVariable
-    int id, @RequestBody
-    Message message )
+    public String sendMessage( @PathVariable int id, @RequestBody Message message )
         throws NotAllowedException
     {
         return activityReportingService.sendMessage( message );
@@ -633,9 +573,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/orgUnits/{id}/getMessage" )
     @ResponseBody
-    public Conversation getMessage( String clientVersion, @PathVariable
-    int id, @RequestHeader( "id" )
-    String conversationId )
+    public Conversation getMessage( String clientVersion, @PathVariable int id,
+        @RequestHeader( "id" ) String conversationId )
         throws NotAllowedException
     {
 
@@ -648,9 +587,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/orgUnits/{id}/replyMessage" )
     @ResponseBody
-    public String replyMessage( @PathVariable
-    int id, @RequestBody
-    Message message )
+    public String replyMessage( @PathVariable int id, @RequestBody Message message )
         throws NotAllowedException
     {
         return activityReportingService.replyMessage( message );
@@ -659,9 +596,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/sendFeedback" )
     @ResponseBody
-    public String sendFeedbackTracker( @PathVariable
-    int id, @RequestBody
-    Message message )
+    public String sendFeedbackTracker( @PathVariable int id, @RequestBody Message message )
         throws NotAllowedException
     {
         return activityReportingService.sendFeedback( message );
@@ -670,9 +605,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/findUser" )
     @ResponseBody
-    public Recipient findUserTracker( String clientVersion, @PathVariable
-    int id, @RequestHeader( "name" )
-    String keyword )
+    public Recipient findUserTracker( String clientVersion, @PathVariable int id,
+        @RequestHeader( "name" ) String keyword )
         throws NotAllowedException
     {
         Recipient recipient = new Recipient();
@@ -682,9 +616,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/sendMessage" )
     @ResponseBody
-    public String sendMessageTracker( @PathVariable
-    int id, @RequestBody
-    Message message )
+    public String sendMessageTracker( @PathVariable int id, @RequestBody Message message )
         throws NotAllowedException
     {
         return activityReportingService.sendMessage( message );
@@ -706,9 +638,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/LWUIT/orgUnits/{id}/getMessage" )
     @ResponseBody
-    public Conversation getMessageTracker( String clientVersion, @PathVariable
-    int id, @RequestHeader( "id" )
-    String conversationId )
+    public Conversation getMessageTracker( String clientVersion, @PathVariable int id,
+        @RequestHeader( "id" ) String conversationId )
         throws NotAllowedException
     {
 
@@ -721,9 +652,7 @@
 
     @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/replyMessage" )
     @ResponseBody
-    public String replyMessageTracker( @PathVariable
-    int id, @RequestBody
-    Message message )
+    public String replyMessageTracker( @PathVariable int id, @RequestBody Message message )
         throws NotAllowedException
     {
         return activityReportingService.replyMessage( message );
@@ -732,9 +661,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/orgUnits/{id}/downloadInterpretation" )
     @ResponseBody
-    public Interpretation downloadInterpretation( String clientVersion, @PathVariable
-    int id, @RequestHeader( "uId" )
-    String uId )
+    public Interpretation downloadInterpretation( String clientVersion, @PathVariable int id,
+        @RequestHeader( "uId" ) String uId )
         throws NotAllowedException
     {
         Interpretation interpretation = activityReportingService.getInterpretation( uId );
@@ -743,9 +671,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/orgUnits/{id}/postInterpretation" )
     @ResponseBody
-    public Interpretation postInterpretation( String clientVersion, @PathVariable
-    int id, @RequestHeader( "data" )
-    String data )
+    public Interpretation postInterpretation( String clientVersion, @PathVariable int id,
+        @RequestHeader( "data" ) String data )
         throws NotAllowedException
     {
         Interpretation interpretation = new Interpretation();
@@ -755,9 +682,8 @@
 
     @RequestMapping( method = RequestMethod.GET, value = "{clientVersion}/orgUnits/{id}/postComment" )
     @ResponseBody
-    public InterpretationComment postInterpretationComment( String clientVersion, @PathVariable
-    int id, @RequestHeader( "data" )
-    String data )
+    public InterpretationComment postInterpretationComment( String clientVersion, @PathVariable int id,
+        @RequestHeader( "data" ) String data )
         throws NotAllowedException
     {
         InterpretationComment message = new InterpretationComment();
@@ -765,4 +691,13 @@
         return message;
     }
 
+    @RequestMapping( method = RequestMethod.POST, value = "{clientVersion}/LWUIT/orgUnits/{id}/registerRelative" )
+    @ResponseBody
+    public Patient registerRelative( @PathVariable int id, @RequestBody Patient patient,
+        @RequestHeader( "programid" ) String programId )
+        throws NotAllowedException
+    {
+        return activityReportingService.registerRelative( patient, id, programId );
+    }
+
 }