Merge branch 'Fineract-521' into develop
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
index 4758b01..fec8a21 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java
@@ -262,14 +262,19 @@
 
         if (isOverdraftType != null) {
             if (inClauseAdded) {
-                sql += " and sp.allow_overdraft=?";
+                sql += " and sp.allow_overdraft=? and sp.deposit_type_enum = ?";
             } else {
-                sql += " where sp.allow_overdraft=?";
+                sql += " where sp.allow_overdraft=? and sp.deposit_type_enum = ?";
             }
-            return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper, isOverdraftType);
+            return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper, new Object[] {isOverdraftType, DepositAccountType.SAVINGS_DEPOSIT.getValue() });
         }
-
-        return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper);
+        
+        if(inClauseAdded) {
+        	sql += " and sp.deposit_type_enum = ?";
+        }else {
+        	 sql += " where sp.deposit_type_enum = ?";
+        }
+        return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper, new Object[] { DepositAccountType.SAVINGS_DEPOSIT.getValue() });
 
     }
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/api/ScorecardApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/spm/api/ScorecardApiResource.java
index 84987da..e2927d5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/api/ScorecardApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/api/ScorecardApiResource.java
@@ -18,7 +18,6 @@
  */
 package org.apache.fineract.spm.api;
 
-import java.util.Collections;
 import java.util.List;
 
 import javax.ws.rs.Consumes;
@@ -33,9 +32,8 @@
 import org.apache.fineract.portfolio.client.domain.Client;
 import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
 import org.apache.fineract.spm.data.ScorecardData;
-import org.apache.fineract.spm.domain.Scorecard;
 import org.apache.fineract.spm.domain.Survey;
-import org.apache.fineract.spm.exception.SurveyNotFoundException;
+import org.apache.fineract.spm.service.ScorecardReadPlatformService;
 import org.apache.fineract.spm.service.ScorecardService;
 import org.apache.fineract.spm.service.SpmService;
 import org.apache.fineract.spm.util.ScorecardMapper;
@@ -45,7 +43,7 @@
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
-@Path("/surveys/{surveyId}/scorecards")
+@Path("/surveys/scorecards")
 @Component
 @Scope("singleton")
 public class ScorecardApiResource {
@@ -54,68 +52,63 @@
     private final SpmService spmService;
     private final ScorecardService scorecardService;
     private final ClientRepositoryWrapper clientRepositoryWrapper;
+    private final ScorecardReadPlatformService scorecardReadPlatformService;
 
     @Autowired
     public ScorecardApiResource(final PlatformSecurityContext securityContext, final SpmService spmService,
-                                final ScorecardService scorecardService, final ClientRepositoryWrapper clientRepositoryWrapper) {
-        super();
+            final ScorecardService scorecardService, final ClientRepositoryWrapper clientRepositoryWrapper,
+            final ScorecardReadPlatformService scorecardReadPlatformService) {
         this.securityContext = securityContext;
         this.spmService = spmService;
         this.scorecardService = scorecardService;
         this.clientRepositoryWrapper = clientRepositoryWrapper;
+        this.scorecardReadPlatformService = scorecardReadPlatformService;
     }
 
     @GET
+    @Path("{surveyId}")
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Transactional
     public List<ScorecardData> findBySurvey(@PathParam("surveyId") final Long surveyId) {
         this.securityContext.authenticatedUser();
-
-        final Survey survey = findSurvey(surveyId);
-
-        final List<Scorecard> scorecards = this.scorecardService.findBySurvey(survey);
-
-        if (scorecards != null) {
-            return ScorecardMapper.map(scorecards);
-        }
-
-        return Collections.EMPTY_LIST;
+        this.spmService.findById(surveyId);
+        return (List<ScorecardData>) this.scorecardReadPlatformService.retrieveScorecardBySurvey(surveyId);
     }
 
     @POST
+    @Path("{surveyId}")
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Transactional
     public void createScorecard(@PathParam("surveyId") final Long surveyId, final ScorecardData scorecardData) {
         final AppUser appUser = this.securityContext.authenticatedUser();
-        final Survey survey = findSurvey(surveyId);
+        final Survey survey = this.spmService.findById(surveyId);
         final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(scorecardData.getClientId());
         this.scorecardService.createScorecard(ScorecardMapper.map(scorecardData, survey, appUser, client));
     }
 
-    @Path("/clients/{clientId}")
     @GET
+    @Path("{surveyId}/clients/{clientId}")
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Transactional
-    public List<ScorecardData> findBySurveyClient(@PathParam("surveyId") final Long surveyId,
-                                                  @PathParam("clientId") final Long clientId) {
+    public List<ScorecardData> findBySurveyAndClient(@PathParam("surveyId") final Long surveyId, @PathParam("clientId") final Long clientId) {
         this.securityContext.authenticatedUser();
-        final Survey survey = findSurvey(surveyId);
-        final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
-        final List<Scorecard> scorecards = this.scorecardService.findBySurveyAndClient(survey, client);
-        if (scorecards != null) {
-            return ScorecardMapper.map(scorecards);
-        }
-        return Collections.EMPTY_LIST;
+        this.spmService.findById(surveyId);
+        this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
+        return (List<ScorecardData>) this.scorecardReadPlatformService.retrieveScorecardBySurveyAndClient(surveyId, clientId);
+
     }
 
-    private Survey findSurvey(final Long surveyId) {
-        final Survey survey = this.spmService.findById(surveyId);
-        if (survey == null) {
-            throw new SurveyNotFoundException(surveyId);
-        }
-        return survey;
+    @GET
+    @Path("clients/{clientId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Transactional
+    public List<ScorecardData> findByClient(@PathParam("clientId") final Long clientId) {
+        this.securityContext.authenticatedUser();
+        this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
+        return (List<ScorecardData>) this.scorecardReadPlatformService.retrieveScorecardByClient(clientId);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/api/SpmApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/spm/api/SpmApiResource.java
index 8da5c15..46afed0 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/api/SpmApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/api/SpmApiResource.java
@@ -18,10 +18,23 @@
  */
 package org.apache.fineract.spm.api;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.spm.data.SurveyData;
 import org.apache.fineract.spm.domain.Survey;
-import org.apache.fineract.spm.exception.SurveyNotFoundException;
 import org.apache.fineract.spm.service.SpmService;
 import org.apache.fineract.spm.util.SurveyMapper;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -29,10 +42,7 @@
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
-import javax.ws.rs.*;
-import javax.ws.rs.core.MediaType;
-import java.util.ArrayList;
-import java.util.List;
+import com.google.gson.Gson;
 
 @Path("/surveys")
 @Component
@@ -43,8 +53,7 @@
     private final SpmService spmService;
 
     @Autowired
-    public SpmApiResource(final PlatformSecurityContext securityContext,
-                          final SpmService spmService) {
+    public SpmApiResource(final PlatformSecurityContext securityContext, final SpmService spmService) {
         this.securityContext = securityContext;
         this.spmService = spmService;
     }
@@ -55,17 +64,13 @@
     @Transactional
     public List<SurveyData> fetchActiveSurveys() {
         this.securityContext.authenticatedUser();
-
         final List<SurveyData> result = new ArrayList<>();
-
         final List<Survey> surveys = this.spmService.fetchValidSurveys();
-
         if (surveys != null) {
             for (final Survey survey : surveys) {
                 result.add(SurveyMapper.map(survey));
             }
         }
-
         return result;
     }
 
@@ -76,13 +81,7 @@
     @Transactional
     public SurveyData findSurvey(@PathParam("id") final Long id) {
         this.securityContext.authenticatedUser();
-
         final Survey survey = this.spmService.findById(id);
-
-        if (survey == null) {
-            throw new SurveyNotFoundException(id);
-        }
-
         return SurveyMapper.map(survey);
     }
 
@@ -90,12 +89,25 @@
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Transactional
-    public void createSurvey(final SurveyData surveyData) {
+    public String createSurvey(final SurveyData surveyData) {
         this.securityContext.authenticatedUser();
-
-        final Survey survey = SurveyMapper.map(surveyData);
-
+        final Survey survey = SurveyMapper.map(surveyData, new Survey());
         this.spmService.createSurvey(survey);
+        return getResponse(survey.getId());
+
+    }
+
+    @PUT
+    @Path("/{id}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Transactional
+    public String editSurvey(@PathParam("id") final Long id, final SurveyData surveyData) {
+        this.securityContext.authenticatedUser();
+        final Survey surveyToUpdate = this.spmService.findById(id);
+        final Survey survey = SurveyMapper.map(surveyData, surveyToUpdate);
+        this.spmService.updateSurvey(survey);
+        return getResponse(survey.getId());
     }
 
     @DELETE
@@ -105,7 +117,13 @@
     @Transactional
     public void deactivateSurvey(@PathParam("id") final Long id) {
         this.securityContext.authenticatedUser();
-
         this.spmService.deactivateSurvey(id);
     }
+    
+    private String getResponse(Long id) {
+        Gson gson = new Gson();
+        HashMap<String, Object> response = new HashMap<>();
+        response.put("resourceId", id);
+        return gson.toJson(response);
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardData.java b/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardData.java
index b19b79e..6c35993 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardData.java
@@ -18,58 +18,96 @@
  */
 package org.apache.fineract.spm.data;
 
-import java.util.Date;
+import java.util.ArrayList;
 import java.util.List;
 
 public class ScorecardData {
 
+    private Long id;
     private Long userId;
+    private String username;
     private Long clientId;
-    private Date createdOn;
+    private Long surveyId;
+    private String surveyName;
     private List<ScorecardValue> scorecardValues;
 
     public ScorecardData() {
         super();
     }
 
-    public ScorecardData(final Long userId, final Long clientId, final Date createdOn,
-                         final List<ScorecardValue> scorecardValues) {
-        super();
+    private ScorecardData(final Long id, final Long userId, final String username, final Long surveyId, final String surveyName,
+            final Long clientId) {
+        this.id = id;
         this.userId = userId;
         this.clientId = clientId;
-        this.createdOn = createdOn;
-        this.scorecardValues = scorecardValues;
+        this.scorecardValues = new ArrayList<>();
+        this.surveyId = surveyId;
+        this.surveyName = surveyName;
+        this.username = username;
+    }
+
+    public static ScorecardData instance(final Long id, final Long userId, final String username, final Long surveyId,
+            final String surveyName, final Long clientId) {
+        return new ScorecardData(id, userId, username, surveyId, surveyName, clientId);
     }
 
     public Long getUserId() {
         return userId;
     }
 
-    public void setUserId(Long userId) {
-        this.userId = userId;
-    }
-
     public Long getClientId() {
         return clientId;
     }
 
-    public void setClientId(Long clientId) {
-        this.clientId = clientId;
-    }
-
-    public Date getCreatedOn() {
-        return createdOn;
-    }
-
-    public void setCreatedOn(Date createdOn) {
-        this.createdOn = createdOn;
-    }
-
     public List<ScorecardValue> getScorecardValues() {
         return scorecardValues;
     }
 
     public void setScorecardValues(List<ScorecardValue> scorecardValues) {
-        this.scorecardValues = scorecardValues;
+        if (this.scorecardValues == null) {
+            this.scorecardValues = new ArrayList<>();
+        }
+        this.scorecardValues.addAll(scorecardValues);
     }
+
+    public String getUsername() {
+        return this.username;
+    }
+
+    public Long getSurveyId() {
+        return this.surveyId;
+    }
+
+    public String getSurveyName() {
+        return this.surveyName;
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public void setClientId(Long clientId) {
+        this.clientId = clientId;
+    }
+
+    public void setSurveyId(Long surveyId) {
+        this.surveyId = surveyId;
+    }
+
+    public void setSurveyName(String surveyName) {
+        this.surveyName = surveyName;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardValue.java b/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardValue.java
index 9f22c2a..c45b19b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardValue.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/data/ScorecardValue.java
@@ -18,21 +18,28 @@
  */
 package org.apache.fineract.spm.data;
 
+import java.util.Date;
+
 public class ScorecardValue {
 
     private Long questionId;
     private Long responseId;
     private Integer value;
+    private Date createdOn;
 
     public ScorecardValue() {
         super();
     }
 
-    public ScorecardValue(final Long questionId, final Long responseId, final Integer value) {
-        super();
+    private ScorecardValue(final Long questionId, final Long responseId, final Integer value, final Date createdOn) {
         this.questionId = questionId;
         this.responseId = responseId;
         this.value = value;
+        this.createdOn = createdOn;
+    }
+
+    public static ScorecardValue instance(final Long questionId, final Long responseId, final Integer value, final Date createdOn) {
+        return new ScorecardValue(questionId, responseId, value, createdOn);
     }
 
     public Long getQuestionId() {
@@ -58,4 +65,16 @@
     public void setValue(Integer value) {
         this.value = value;
     }
+
+    
+    public Date getCreatedOn() {
+        return this.createdOn;
+    }
+
+    
+    public void setCreatedOn(Date createdOn) {
+        this.createdOn = createdOn;
+    }
+    
+    
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/domain/Survey.java b/fineract-provider/src/main/java/org/apache/fineract/spm/domain/Survey.java
index bd68905..ad4f961 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/domain/Survey.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/domain/Survey.java
@@ -21,6 +21,8 @@
 import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
 
 import javax.persistence.*;
+
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -28,11 +30,11 @@
 @Table(name = "m_surveys")
 public class Survey extends AbstractPersistableCustom<Long> {
 
-    @OneToMany(mappedBy = "survey", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+    @OneToMany(mappedBy = "survey", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval=true)
     @OrderBy("sequenceNo")
     private List<Component> components;
 
-    @OneToMany(mappedBy = "survey", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+    @OneToMany(mappedBy = "survey", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval=true)
     @OrderBy("sequenceNo")
     private List<Question> questions;
 
@@ -73,7 +75,13 @@
     }
 
     public void setQuestions(List<Question> questions) {
-        this.questions = questions;
+        if(this.questions != null){
+            this.questions.clear();;
+        }else{
+            this.questions = new ArrayList<>();
+        }
+        
+        this.questions.addAll(questions);
     }
 
     public String getKey() {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/domain/SurveyValidator.java b/fineract-provider/src/main/java/org/apache/fineract/spm/domain/SurveyValidator.java
new file mode 100644
index 0000000..a3e2ee6
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/domain/SurveyValidator.java
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.spm.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.spm.util.SurveyApiConstants;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SurveyValidator {
+
+    public void validate(final Survey survey) {
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+
+        final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
+                .resource(SurveyApiConstants.SURVEY_RESOURCE_NAME);
+
+        baseDataValidator.reset().parameter(SurveyApiConstants.keyParamName).value(survey.getKey()).notNull().notBlank()
+                .notExceedingLengthOf(SurveyApiConstants.maxKeyLength);
+
+        baseDataValidator.reset().parameter(SurveyApiConstants.nameParamName).value(survey.getName()).notNull().notBlank()
+                .notExceedingLengthOf(SurveyApiConstants.maxNameLength);
+
+        baseDataValidator.reset().parameter(SurveyApiConstants.countryCodeParamName).value(survey.getCountryCode()).notNull().notBlank()
+                .notExceedingLengthOf(SurveyApiConstants.maxCountryCodeLength);
+        baseDataValidator.reset().parameter(SurveyApiConstants.descriptionParamName).value(survey.getDescription()).ignoreIfNull()
+                .notExceedingLengthOf(SurveyApiConstants.maxDescriptionLength);
+        List<Question> questions = survey.getQuestions();
+        baseDataValidator.reset().parameter(SurveyApiConstants.questionParamName).value(questions).notNull();
+        validateQuestions(baseDataValidator, questions);
+        throwExceptionIfValidationWarningsExist(dataValidationErrors);
+
+    }
+
+    private void validateQuestions(final DataValidatorBuilder baseDataValidator, List<Question> questions) {
+        if (questions != null) {
+            baseDataValidator.reset().parameter(SurveyApiConstants.questionParamName + "." + SurveyApiConstants.lengthParamName)
+                    .value(questions.toArray()).arrayNotEmpty();
+            for (Question question : questions) {
+                baseDataValidator.reset().parameter(SurveyApiConstants.questionParamName + "." + SurveyApiConstants.keyParamName)
+                        .value(question.getKey()).notNull().notExceedingLengthOf(SurveyApiConstants.maxKeyLength);
+                baseDataValidator.reset().parameter(SurveyApiConstants.questionParamName + "." + SurveyApiConstants.textParamName)
+                        .value(question.getText()).notNull().notExceedingLengthOf(SurveyApiConstants.maxTextLength);
+                baseDataValidator.reset().parameter(SurveyApiConstants.questionParamName + "." + SurveyApiConstants.descriptionParamName)
+                        .value(question.getDescription()).ignoreIfNull().notExceedingLengthOf(SurveyApiConstants.maxDescriptionLength);
+                validateOptions(baseDataValidator, question);
+
+            }
+        }
+    }
+
+    private void validateOptions(final DataValidatorBuilder baseDataValidator, Question question) {
+        List<Response> responses = question.getResponses();
+        baseDataValidator.reset().parameter(SurveyApiConstants.questionParamName + "." + SurveyApiConstants.optionsParamName)
+                .value(responses).notNull();
+        if (responses != null) {
+            baseDataValidator.reset().parameter(SurveyApiConstants.questionParamName + "." + SurveyApiConstants.optionsParamName)
+                    .value(responses.toArray()).arrayNotEmpty();
+            for (Response response : responses) {
+                baseDataValidator.reset().parameter(SurveyApiConstants.optionsParamName + "." + SurveyApiConstants.textParamName)
+                        .value(response.getText()).notNull().notExceedingLengthOf(SurveyApiConstants.maxTextLength);
+                baseDataValidator.reset().parameter(SurveyApiConstants.optionsParamName + "." + SurveyApiConstants.valueParamName)
+                        .value(response.getValue()).notNull().notGreaterThanMax(SurveyApiConstants.maxOptionsValue);
+            }
+        }
+    }
+
+    private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
+        if (!dataValidationErrors.isEmpty()) {
+            throw new PlatformApiDataValidationException(dataValidationErrors);
+        }
+    }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/service/ScorecardReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/spm/service/ScorecardReadPlatformService.java
new file mode 100644
index 0000000..78d74dc
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/service/ScorecardReadPlatformService.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.spm.service;
+
+import java.util.Collection;
+
+import org.apache.fineract.spm.data.ScorecardData;
+
+
+public interface ScorecardReadPlatformService {
+    
+    Collection<ScorecardData> retrieveScorecardByClient(final Long clientId);
+    
+    Collection<ScorecardData> retrieveScorecardBySurveyAndClient(final Long surveyId,final Long clientId);
+    
+    Collection<ScorecardData> retrieveScorecardBySurvey(final Long surveyId);
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/service/ScorecardReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/spm/service/ScorecardReadPlatformServiceImpl.java
new file mode 100644
index 0000000..2859a3c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/service/ScorecardReadPlatformServiceImpl.java
@@ -0,0 +1,144 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.spm.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.spm.data.ScorecardData;
+import org.apache.fineract.spm.data.ScorecardValue;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ScorecardReadPlatformServiceImpl implements ScorecardReadPlatformService {
+
+    private final JdbcTemplate jdbcTemplate;
+    private final PlatformSecurityContext context;
+
+    @Autowired
+    public ScorecardReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource) {
+        this.context = context;
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+    }
+
+    private static final class ScorecardMapper implements RowMapper<ScorecardData> {
+
+        public String schema() {
+            StringBuilder sb = new StringBuilder(50);
+            sb.append(" sc.id as id, sc.survey_id as surveyId, s.a_name as surveyName, ");
+            sb.append(" sc.client_id as clientId,");
+            sb.append(" sc.user_id as userId, user.username as username ");
+            sb.append(" from m_survey_scorecards sc ");
+            sb.append(" left join m_surveys s ON s.id = sc.survey_id ");
+            sb.append(" left join m_appuser user ON user.id = sc.user_id ");
+
+            return sb.toString();
+        }
+
+        @Override
+        public ScorecardData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
+
+            final Long id = rs.getLong("id");
+            final Long surveyId = rs.getLong("surveyId");
+            final String surveyName = rs.getString("surveyName");
+            final Long clientId = rs.getLong("clientId");
+            final Long userId = rs.getLong("userId");
+            final String username = rs.getString("username");
+
+            return ScorecardData.instance(id, userId, username, surveyId, surveyName, clientId);
+        }
+    }
+
+    private static final class ScorecardValueMapper implements RowMapper<ScorecardValue> {
+
+        public String schema() {
+            StringBuilder sb = new StringBuilder(50);
+            sb.append(" sc.question_id as questionId, sc.response_id as responseId, ");
+            sb.append(" sc.created_on as createdOn, sc.a_value as value ");
+            sb.append(" from m_survey_scorecards sc  ");
+            sb.append(" where sc.survey_id = ? and sc.client_id = ?  ");
+
+            return sb.toString();
+        }
+
+        @Override
+        public ScorecardValue mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
+
+            final Long questionId = rs.getLong("questionId");
+            final Long responseId = rs.getLong("responseId");
+            final LocalDate createdOn = JdbcSupport.getLocalDate(rs, "createdOn");
+            final Integer value = rs.getInt("value");
+
+            return ScorecardValue.instance(questionId, responseId, value, createdOn.toDate());
+        }
+    }
+
+    List<ScorecardValue> getScorecardValueBySurveyAndClient(final Long surveyId, final Long clientId) {
+        ScorecardValueMapper scvm = new ScorecardValueMapper();
+        String sql = "select " + scvm.schema();
+        return this.jdbcTemplate.query(sql, scvm, new Object[] { surveyId, clientId });
+    }
+
+    Collection<ScorecardData> updateScorecardValues(Collection<ScorecardData> scorecard) {
+        for (ScorecardData scorecardData : scorecard) {
+            scorecardData.setScorecardValues(getScorecardValueBySurveyAndClient(scorecardData.getSurveyId(), scorecardData.getClientId()));
+        }
+        return scorecard;
+    }
+
+    @Override
+    public Collection<ScorecardData> retrieveScorecardBySurvey(Long surveyId) {
+        this.context.authenticatedUser();
+        ScorecardMapper scm = new ScorecardMapper();
+        String sql = "select " + scm.schema() + " where sc.survey_id = ? " + " group by sc.survey_id, sc.client_id ";
+        Collection<ScorecardData> scorecardDatas = this.jdbcTemplate.query(sql, scm, new Object[] { surveyId });
+        updateScorecardValues(scorecardDatas);
+        return scorecardDatas;
+    }
+
+    @Override
+    public Collection<ScorecardData> retrieveScorecardByClient(Long clientId) {
+        this.context.authenticatedUser();
+        ScorecardMapper scm = new ScorecardMapper();
+        String sql = "select " + scm.schema() + " where sc.client_id = ? " + " group by sc.survey_id, sc.client_id ";
+        Collection<ScorecardData> scorecardDatas = this.jdbcTemplate.query(sql, scm, new Object[] { clientId });
+        updateScorecardValues(scorecardDatas);
+        return scorecardDatas;
+    }
+
+    @Override
+    public Collection<ScorecardData> retrieveScorecardBySurveyAndClient(Long surveyId, Long clientId) {
+        this.context.authenticatedUser();
+        ScorecardMapper scm = new ScorecardMapper();
+        String sql = "select " + scm.schema() + " where sc.survey_id = ? and sc.client_id = ? " + " group by sc.survey_id, sc.client_id ";
+        Collection<ScorecardData> scorecardDatas = this.jdbcTemplate.query(sql, scm, new Object[] { surveyId, clientId });
+        updateScorecardValues(scorecardDatas);
+        return scorecardDatas;
+    }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/service/SpmService.java b/fineract-provider/src/main/java/org/apache/fineract/spm/service/SpmService.java
index 424bdaf..52c159d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/service/SpmService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/service/SpmService.java
@@ -18,29 +18,39 @@
  */
 package org.apache.fineract.spm.service;
 
+import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.spm.domain.Survey;
+import org.apache.fineract.spm.domain.SurveyValidator;
+import org.apache.fineract.spm.exception.SurveyNotFoundException;
 import org.apache.fineract.spm.repository.SurveyRepository;
+import org.apache.openjpa.persistence.EntityExistsException;
 import org.joda.time.DateTime;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.orm.jpa.JpaSystemException;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.util.Date;
 import java.util.List;
 
+import javax.persistence.PersistenceException;
+
 @Service
 public class SpmService {
 
     private final PlatformSecurityContext securityContext;
     private final SurveyRepository surveyRepository;
+    private final SurveyValidator surveyValidator;
 
     @Autowired
     public SpmService(final PlatformSecurityContext securityContext,
-                      final SurveyRepository surveyRepository) {
+                      final SurveyRepository surveyRepository,
+                      final SurveyValidator surveyValidator) {
         super();
         this.securityContext = securityContext;
         this.surveyRepository = surveyRepository;
+        this.surveyValidator = surveyValidator;
     }
 
     public List<Survey> fetchValidSurveys() {
@@ -51,61 +61,83 @@
 
     public Survey findById(final Long id) {
         this.securityContext.authenticatedUser();
-
-        return this.surveyRepository.findOne(id);
+        Survey survey = this.surveyRepository.findOne(id);
+        if (survey == null) {
+            throw new SurveyNotFoundException(id);
+        }
+        return survey;
     }
 
     public Survey createSurvey(final Survey survey) {
         this.securityContext.authenticatedUser();
-
+        this.surveyValidator.validate(survey);
         final Survey previousSurvey = this.surveyRepository.findByKey(survey.getKey(), new Date());
 
         if (previousSurvey != null) {
             this.deactivateSurvey(previousSurvey.getId());
         }
-
         // set valid from to start of today
-        final DateTime validFrom = DateTime
-                .now()
-                .withHourOfDay(0)
-                .withMinuteOfHour(0)
-                .withSecondOfMinute(0)
-                .withMillisOfSecond(0);
-
+        final DateTime validFrom = getStartOfToday();
         survey.setValidFrom(validFrom.toDate());
-
         // set valid from to end in 100 years
-        final DateTime validTo = validFrom
-                .withDayOfMonth(31)
-                .withMonthOfYear(12)
-                .withHourOfDay(23)
-                .withMinuteOfHour(59)
-                .withSecondOfMinute(59)
-                .withMillisOfSecond(999)
-                .plusYears(100);
+        final DateTime validTo = validFrom.withDayOfMonth(31).withMonthOfYear(12).withHourOfDay(23).withMinuteOfHour(59)
+                .withSecondOfMinute(59).withMillisOfSecond(999).plusYears(100);
 
         survey.setValidTo(validTo.toDate());
-
-        return this.surveyRepository.save(survey);
+        try {
+            this.surveyRepository.saveAndFlush(survey);
+        } catch (final EntityExistsException dve) {
+            handleDataIntegrityIssues(dve, dve, survey.getKey());
+        } catch (final DataIntegrityViolationException dve) {
+            handleDataIntegrityIssues(dve.getMostSpecificCause(), dve, survey.getKey());
+        } catch (final JpaSystemException dve) {
+            handleDataIntegrityIssues(dve.getMostSpecificCause(), dve, survey.getKey());
+        } catch (final PersistenceException dve) {
+            handleDataIntegrityIssues(dve, dve, survey.getKey());
+        }
+        return survey ;
+    }
+    
+    public Survey updateSurvey(final Survey survey) {
+        try {
+            this.surveyValidator.validate(survey);
+            this.surveyRepository.saveAndFlush(survey);
+        } catch (final EntityExistsException dve) {
+            handleDataIntegrityIssues(dve, dve, survey.getKey());
+        } catch (final DataIntegrityViolationException dve) {
+            handleDataIntegrityIssues(dve.getMostSpecificCause(), dve, survey.getKey());
+        } catch (final JpaSystemException dve) {
+            handleDataIntegrityIssues(dve.getMostSpecificCause(), dve, survey.getKey());
+        } catch (final PersistenceException dve) {
+            handleDataIntegrityIssues(dve, dve, survey.getKey());
+        }
+        return survey;
     }
 
     public void deactivateSurvey(final Long id) {
         this.securityContext.authenticatedUser();
 
-        final Survey survey = this.surveyRepository.findOne(id);
+        final Survey survey = findById(id);
+        final DateTime dateTime = getStartOfToday().minusMillis(1);
+        survey.setValidTo(dateTime.toDate());
 
-        if (survey != null) {
-            // set valid to to yesterday night
-            final DateTime dateTime = DateTime
-                    .now()
-                    .withHourOfDay(23)
-                    .withMinuteOfHour(59)
-                    .withSecondOfMinute(59)
-                    .withMillisOfSecond(999)
-                    .minusDays(1);
-            survey.setValidTo(dateTime.toDate());
+        this.surveyRepository.save(survey);
+    }
+    
+    public static DateTime getStartOfToday() {
+        return DateTime.now().withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0).withMillisOfSecond(0);
+    }
+    
+    private void handleDataIntegrityIssues(final Throwable realCause, final Exception dve, String key) {
 
-            this.surveyRepository.save(survey);
-        }
+        if (realCause.getMessage().contains("m_survey_scorecards")) { throw new PlatformDataIntegrityException(
+                "error.msg.survey.cannot.be.modified.as.used.in.client.survey",
+                "Survey can not be edited as it is already used in client survey", "name", key); }
+
+        if (realCause.getMessage().contains("key")) { throw new PlatformDataIntegrityException("error.msg.survey.duplicate.key",
+                "Survey with key already exists", "name", key); }
+
+        throw new PlatformDataIntegrityException("error.msg.survey.unknown.data.integrity.issue",
+                "Unknown data integrity issue with resource: " + realCause.getMessage());
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/util/ScorecardMapper.java b/fineract-provider/src/main/java/org/apache/fineract/spm/util/ScorecardMapper.java
index c317ccd..60588d8 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/util/ScorecardMapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/util/ScorecardMapper.java
@@ -18,7 +18,10 @@
  */
 package org.apache.fineract.spm.util;
 
-import org.apache.fineract.organisation.staff.domain.Staff;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.portfolio.client.domain.Client;
 import org.apache.fineract.spm.data.ScorecardData;
 import org.apache.fineract.spm.data.ScorecardValue;
@@ -28,38 +31,12 @@
 import org.apache.fineract.spm.domain.Survey;
 import org.apache.fineract.useradministration.domain.AppUser;
 
-import java.util.*;
-
 public class ScorecardMapper {
 
     private ScorecardMapper() {
         super();
     }
 
-    public static List<ScorecardData> map(final List<Scorecard> scorecards) {
-        final Map<Date, ScorecardData> scorecardDataMap = new HashMap<>();
-        ScorecardData scorecardData = null;
-        if (scorecards != null && scorecards.isEmpty()) {
-            for (Scorecard scorecard : scorecards) {
-                if ((scorecardData = scorecardDataMap.get(scorecard.getCreatedOn())) == null) {
-                    scorecardData = new ScorecardData();
-                    scorecardDataMap.put(scorecard.getCreatedOn(), scorecardData);
-                    scorecardData.setUserId(scorecard.getAppUser().getId());
-                    scorecardData.setClientId(scorecard.getClient().getId());
-                    scorecardData.setCreatedOn(scorecard.getCreatedOn());
-                    scorecardData.setScorecardValues(new ArrayList<ScorecardValue>());
-                }
-
-                scorecardData.getScorecardValues().add(new ScorecardValue(scorecard.getQuestion().getId(), scorecard.getResponse().getId(),
-                        scorecard.getValue()));
-            }
-
-            return new ArrayList<>(scorecardDataMap.values());
-        }
-
-        return Collections.EMPTY_LIST;
-    }
-
     public static List<Scorecard> map(final ScorecardData scorecardData, final Survey survey,
                                       final AppUser appUser, final Client client) {
         final List<Scorecard> scorecards = new ArrayList<>();
@@ -74,7 +51,7 @@
                ScorecardMapper.setQuestionAndResponse(scorecardValue, scorecard, survey);
                scorecard.setAppUser(appUser);
                scorecard.setClient(client);
-               scorecard.setCreatedOn(scorecardData.getCreatedOn());
+               scorecard.setCreatedOn(DateUtils.getLocalDateOfTenant().toDate());
                scorecard.setValue(scorecardValue.getValue());
            }
         }
@@ -97,4 +74,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/util/SurveyApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/spm/util/SurveyApiConstants.java
new file mode 100644
index 0000000..6b93511
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/util/SurveyApiConstants.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.spm.util;
+
+
+public class SurveyApiConstants {
+    
+    public static final String SURVEY_RESOURCE_NAME = "survey";
+    public static final String keyParamName = "key";
+    public static final String nameParamName = "name";
+    public static final String countryCodeParamName = "countrycode";
+    public static final String descriptionParamName = "description";
+    public static final String sequenceNumberParamName = "sequenceNo";
+    public static final String valueParamName = "value";
+    public static final String questionParamName = "question";
+    public static final String optionsParamName = "options";
+    public static final String textParamName = "text";
+    public static final String lengthParamName = "length";
+    
+    //to validate length/max value  
+    public static final Integer maxCountryCodeLength = 2;
+    public static final Integer maxTextLength = 255;
+    public static final Integer maxNameLength = 255;
+    public static final Integer maxKeyLength = 32;
+    public static final Integer maxOptionsValue = 9999;
+    public static final Integer maxDescriptionLength = 4000;
+    
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/spm/util/SurveyMapper.java b/fineract-provider/src/main/java/org/apache/fineract/spm/util/SurveyMapper.java
index 45ebd57..db3718d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/spm/util/SurveyMapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/spm/util/SurveyMapper.java
@@ -45,8 +45,7 @@
         return surveyData;
     }
 
-    public static Survey map(final SurveyData surveyData) {
-        final Survey survey = new Survey();
+    public static Survey map(final SurveyData surveyData, Survey survey) {        
         survey.setComponents(SurveyMapper.mapComponentDatas(surveyData.getComponentDatas(), survey));
         survey.setQuestions(SurveyMapper.mapQuestionDatas(surveyData.getQuestionDatas(), survey));
         survey.setKey(surveyData.getKey());
diff --git a/fineract-provider/src/main/resources/META-INF/spring/spmContext.xml b/fineract-provider/src/main/resources/META-INF/spring/spmContext.xml
index 4248110..d057b10 100644
--- a/fineract-provider/src/main/resources/META-INF/spring/spmContext.xml
+++ b/fineract-provider/src/main/resources/META-INF/spring/spmContext.xml
@@ -32,7 +32,7 @@
 
     <tx:annotation-driven />
 
-    <context:component-scan base-package="org.apache.fineract.spm"/>
+    <context:component-scan base-package="org.apache.fineract.spm.**"/>
 
     <jpa:repositories base-package="org.apache.fineract.spm.repository"/>