diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 47902bb85..bf4364b86 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -2053,7 +2053,8 @@ Content-Type: application/json Request Body: "dateFormat": "dd MMMM yyyy", "locale": "en", "active": true, - "activationDate": "04 March 2009" + "activationDate": "04 March 2009", + "submittedOnDate":"04 March 2009" } @@ -2378,6 +2379,16 @@ GET https://DomainName/api/v1/clients/{clientId} "displayName": "Client of group", "officeId": 1, "officeName": "Head Office", + "timeline": { + "activatedOnDate": [ + 2009, + 3, + 4 + ], + "activatedByUsername": "mifos", + "activatedByFirstname": "App", + "activatedByLastname": "Administrator" + }, "groups": [ { "id": 1, @@ -3908,7 +3919,8 @@ Content-Type: application/json Request Body: "dateFormat": "dd MMMM yyyy", "locale": "en", "active": true, - "activationDate": "04 March 2009" + "activationDate": "04 March 2009", + "submittedOnDate":"04 March 2009" } @@ -4127,7 +4139,17 @@ GET https://DomainName/mifosng-provider/api/v1/groups/1 "externalId": "000-1A", "officeId": 1, "officeName": "Head Office", - "hierarchy": ".1." + "hierarchy": ".1.", + "timeline": { + "activatedOnDate": [ + 2013, + 11, + 14 + ], + "activatedByUsername": "mifos", + "activatedByFirstname": "App", + "activatedByLastname": "Administrator" + } } @@ -4144,6 +4166,16 @@ GET https://DomainName/mifosng-provider/api/v1/groups/1?associations=clientMembe "officeId": 1, "officeName": "Head Office", "hierarchy": ".1.", + "timeline": { + "activatedOnDate": [ + 2013, + 11, + 14 + ], + "activatedByUsername": "mifos", + "activatedByFirstname": "App", + "activatedByLastname": "Administrator" + }, "clientMembers": [ { "id": 1, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java index 077a480ce..aa53e669f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java @@ -57,7 +57,7 @@ public class ClientApiConstants { public static final Set CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, mobileNoParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName, - staffIdParamName)); + staffIdParamName,submittedOnDateParamName)); public static final Set CLIENT_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, accountNoParamName, externalIdParamName, mobileNoParamName, firstnameParamName, middlenameParamName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java index 32b04cdf2..caff2cb07 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java @@ -132,6 +132,12 @@ public final class ClientDataValidator { baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).value(active).trueOrFalseRequired(false); } + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.submittedOnDateParamName, element)) + { + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.submittedOnDateParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.submittedOnDateParamName).value(submittedOnDate).notNull(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index a8d7b366b..c4f7cc318 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -151,11 +151,6 @@ public final class Client extends AbstractPersistable { final String lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName); final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName); - LocalDate submittedOnDate = new LocalDate(); - if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) { - submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName); - } - ClientStatus status = ClientStatus.PENDING; boolean active = false; if (command.hasParameter("active")) { @@ -168,18 +163,14 @@ public final class Client extends AbstractPersistable { status = ClientStatus.ACTIVE; activationDate = command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName); officeJoiningDate = activationDate; - if (activationDate.isAfter(DateUtils.getLocalDateOfTenant())) { - - final String defaultUserMessage = "Activation date cannot be in the future."; - final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.activationDate.in.the.future", - defaultUserMessage, ClientApiConstants.activationDateParamName, activationDate); - - final List dataValidationErrors = new ArrayList(); - dataValidationErrors.add(error); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } + } + LocalDate submittedOnDate = new LocalDate(); + if (active && submittedOnDate.isAfter(activationDate)) { + submittedOnDate = activationDate; + } + if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) { + submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName); } return new Client(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, @@ -202,18 +193,6 @@ public final class Client extends AbstractPersistable { this.accountNumber = accountNo; } - if (isDateInTheFuture(submittedOnDate)) { - - final String defaultUserMessage = "submitted date cannot be in the future."; - final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.in.the.future", - defaultUserMessage, ClientApiConstants.submittedOnDateParamName, submittedOnDate); - - final List dataValidationErrors = new ArrayList(); - dataValidationErrors.add(error); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - this.submittedOnDate = submittedOnDate.toDate(); this.submittedBy = currentUser; @@ -302,6 +281,7 @@ public final class Client extends AbstractPersistable { } public void activate(final AppUser currentUser, final DateTimeFormatter formatter, final LocalDate activationLocalDate) { + if (isActive()) { final String defaultUserMessage = "Cannot activate client. Client is already active."; final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.already.active", defaultUserMessage, @@ -461,6 +441,24 @@ public final class Client extends AbstractPersistable { private void validateActivationDate(final List dataValidationErrors) { + if (getSubmittedOnDate() != null && isDateInTheFuture(getSubmittedOnDate())) { + + final String defaultUserMessage = "submitted date cannot be in the future."; + final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.in.the.future", + defaultUserMessage, ClientApiConstants.submittedOnDateParamName, this.submittedOnDate); + + dataValidationErrors.add(error); + } + + if (getActivationLocalDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getActivationLocalDate())) { + + final String defaultUserMessage = "submitted date cannot be after the activation date"; + final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.after.activation.date", + defaultUserMessage, ClientApiConstants.submittedOnDateParamName, this.submittedOnDate); + + dataValidationErrors.add(error); + } + if (getActivationLocalDate() != null && isDateInTheFuture(getActivationLocalDate())) { final String defaultUserMessage = "Activation date cannot be in the future."; @@ -470,7 +468,7 @@ public final class Client extends AbstractPersistable { dataValidationErrors.add(error); } - if (this.activationDate != null) { + if (getActivationLocalDate() != null) { if (this.office.isOpeningDateAfter(getActivationLocalDate())) { final String defaultUserMessage = "Client activation date cannot be a date before the office opening date."; final ApiParameterError error = ApiParameterError.parameterError( diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java index 4c7d4a54e..ed4edf4ab 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java @@ -65,7 +65,7 @@ public class GroupingTypesApiConstants { public static final Set CENTER_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, idParamName, nameParamName, externalIdParamName, officeIdParamName, staffIdParamName, activeParamName, - activationDateParamName, groupMembersParamName)); + activationDateParamName, groupMembersParamName,submittedOnDateParamName)); public static final Set GROUP_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, idParamName, nameParamName, externalIdParamName, centerIdParamName, officeIdParamName, staffIdParamName, activeParamName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java index c06b50210..4b94deeee 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java @@ -28,6 +28,7 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; @@ -38,6 +39,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants; import org.mifosplatform.portfolio.group.exception.ClientExistInGroupException; @@ -133,15 +135,14 @@ public final class Group extends AbstractPersistable { LocalDate groupActivationDate = null; if (active) { status = GroupingTypeStatus.ACTIVE; - groupActivationDate = activationDate;// set activation date only if - // group is made active + groupActivationDate = activationDate; } return new Group(office, staff, parent, groupLevel, name, externalId, status, groupActivationDate, clientMembers, groupMembers, submittedOnDate, currentUser); } - public Group(final Office office, final Staff staff, final Group parent, final GroupLevel groupLevel, final String name, + private Group(final Office office, final Staff staff, final Group parent, final GroupLevel groupLevel, final String name, final String externalId, final GroupingTypeStatus status, final LocalDate activationDate, final Set clientMembers, final Set groupMembers, final LocalDate submittedOnDate, final AppUser currentUser) { this.office = office; @@ -176,20 +177,56 @@ public final class Group extends AbstractPersistable { this.groupMembers.addAll(groupMembers); } - if (isDateInTheFuture(submittedOnDate)) { - - final String defaultUserMessage = "submitted date cannot be in the future."; - final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.submittedOnDate.in.the.future", - defaultUserMessage, "SubmittedOnDate"); - - final List dataValidationErrors = new ArrayList(); - dataValidationErrors.add(error); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - this.submittedOnDate = submittedOnDate.toDate(); this.submittedBy = currentUser; + + validate(); + } + + private void validate() { + final List dataValidationErrors = new ArrayList(); + validateActivationDate(dataValidationErrors); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateActivationDate(final List dataValidationErrors) { + + if (getSubmittedOnDate() != null && isDateInTheFuture(getSubmittedOnDate())) { + + final String defaultUserMessage = "Submitted on date cannot be in the future."; + final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.submittedOnDate.in.the.future", + defaultUserMessage, ClientApiConstants.submittedOnDateParamName, this.submittedOnDate); + + dataValidationErrors.add(error); + } + + if (getActivationLocalDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getActivationLocalDate())) { + + final String defaultUserMessage = "Submitted on date cannot be after the activation date"; + final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.submittedOnDate.after.activation.date", + defaultUserMessage, GroupingTypesApiConstants.submittedOnDateParamName, this.submittedOnDate); + + dataValidationErrors.add(error); + } + + if (getActivationLocalDate() != null && isDateInTheFuture(getActivationLocalDate())) { + + final String defaultUserMessage = "Activation date cannot be in the future."; + final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.activationDate.in.the.future", + defaultUserMessage, GroupingTypesApiConstants.activationDateParamName, getActivationLocalDate()); + + dataValidationErrors.add(error); + } + + if (getActivationLocalDate() != null) { + if (this.office.isOpeningDateAfter(getActivationLocalDate())) { + final String defaultUserMessage = "Activation date cannot be a date before the office opening date."; + final ApiParameterError error = ApiParameterError.parameterError( + "error.msg.group.activationDate.cannot.be.before.office.activation.date", defaultUserMessage, + GroupingTypesApiConstants.activationDateParamName, getActivationLocalDate()); + dataValidationErrors.add(error); + } + } } public void activate(final AppUser currentUser, final DateTimeFormatter formatter, final LocalDate activationLocalDate) { @@ -204,21 +241,11 @@ public final class Group extends AbstractPersistable { throw new PlatformApiDataValidationException(dataValidationErrors); } - if (isDateInTheFuture(activationLocalDate)) { - - final String defaultUserMessage = "Activation date cannot be in the future."; - final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.activationDate.in.the.future", - defaultUserMessage, "activationDate", activationLocalDate); - - final List dataValidationErrors = new ArrayList(); - dataValidationErrors.add(error); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - this.activationDate = activationLocalDate.toDate(); this.status = GroupingTypeStatus.ACTIVE.getValue(); this.activatedBy = currentUser; + + validate(); } public boolean isActivatedAfter(final LocalDate submittedOn) { @@ -292,6 +319,10 @@ public final class Group extends AbstractPersistable { return actualChanges; } + public LocalDate getSubmittedOnDate() { + return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null); + } + public LocalDate getActivationLocalDate() { LocalDate activationLocalDate = null; if (this.activationDate != null) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java index 41ac94d6d..43e617b6d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java @@ -97,6 +97,12 @@ public final class GroupingTypesDataValidator { baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).value(active).trueOrFalseRequired(false); } + if (this.fromApiJsonHelper.parameterExists(GroupingTypesApiConstants.submittedOnDateParamName, element)) + { + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(GroupingTypesApiConstants.submittedOnDateParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.submittedOnDateParamName).value(submittedOnDate).notNull(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -155,6 +161,12 @@ public final class GroupingTypesDataValidator { baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).value(active).trueOrFalseRequired(false); } + if (this.fromApiJsonHelper.parameterExists(GroupingTypesApiConstants.submittedOnDateParamName, element)) + { + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(GroupingTypesApiConstants.submittedOnDateParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.submittedOnDateParamName).value(submittedOnDate).notNull(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -209,6 +221,12 @@ public final class GroupingTypesDataValidator { baseDataValidator.reset().parameter(ClientApiConstants.activeParamName).value(active).trueOrFalseRequired(false); } + if (this.fromApiJsonHelper.parameterExists(GroupingTypesApiConstants.submittedOnDateParamName, element)) + { + final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(GroupingTypesApiConstants.submittedOnDateParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.submittedOnDateParamName).value(submittedOnDate).notNull(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java index 0b6712894..9f61cd56a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java @@ -132,14 +132,15 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group final Set groupMembers = assembleSetOfChildGroups(officeId, command); + final boolean active = command.booleanPrimitiveValueOfParameterNamed(GroupingTypesApiConstants.activeParamName); LocalDate submittedOnDate = new LocalDate(); - + if (active && submittedOnDate.isAfter(activationDate)) { + submittedOnDate = activationDate; + } if(command.hasParameter(GroupingTypesApiConstants.submittedOnDateParamName)){ - submittedOnDate = command.localDateValueOfParameterNamed(GroupingTypesApiConstants.submittedOnDateParamName); } - final boolean active = command.booleanPrimitiveValueOfParameterNamed(GroupingTypesApiConstants.activeParamName); final Group newGroup = Group.newGroup(groupOffice, staff, parentGroup, groupLevel, name, externalId, active, activationDate, clientMembers, groupMembers,submittedOnDate,currentUser); @@ -193,7 +194,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group try { this.fromApiJsonDeserializer.validateForActivation(command, GroupingTypesApiConstants.GROUP_RESOURCE_NAME); - AppUser currentUser = this.context.authenticatedUser(); + final AppUser currentUser = this.context.authenticatedUser(); final Group group = this.groupRepository.findOneWithNotFoundDetection(groupId);