MIFOSX-37: support ability to use different account number generation strategies for client account numbers (programatically for now, from ui,configuration later)

This commit is contained in:
Keith Woodlock
2013-01-10 17:01:53 +00:00
parent 7db9d409eb
commit 4cac334d71
13 changed files with 438 additions and 353 deletions
@@ -0,0 +1,15 @@
ALTER TABLE `m_client`
ADD COLUMN `middlename` VARCHAR(50) DEFAULT NULL AFTER `firstname`;
ALTER TABLE `m_client`
ADD COLUMN `fullname` VARCHAR(100) DEFAULT NULL AFTER `lastname`;
UPDATE `m_client`
SET
`fullname` = `lastname`
WHERE `firstname` is NULL;
UPDATE `m_client`
SET
`lastname` = null
WHERE `fullname` is not NULL;
@@ -67,7 +67,7 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo
final ClientCommand command = this.clientCommandFromApiJsonDeserializer.commandFromApiJson(json);
return ClientData.dataChangeInstance(resourceIdentifier, command.getOfficeId(), command.getExternalId(), command.getFirstname(),
command.getLastname(), command.getClientOrBusinessName(), command.getJoiningDate());
command.getMiddlename(), command.getLastname(), command.getFullname(), command.getJoiningDate());
}
@Override
@@ -47,7 +47,7 @@ import org.springframework.stereotype.Component;
@Scope("singleton")
public class ClientsApiResource {
private final Set<String> CLIENT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "officeName", "externalId",
private final Set<String> CLIENT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "accountNo", "officeId", "officeName", "externalId",
"firstname", "lastname", "joinedDate", "displayName", "clientOrBusinessName", "allowedOffices", "imagePresent"));
private final PlatformSecurityContext context;
@@ -1,84 +1,31 @@
package org.mifosplatform.portfolio.client.command;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
/**
*
*/
public class ClientCommand {
@SuppressWarnings("unused")
private final String accountNo;
private final String externalId;
private final String firstname;
private final String middlename;
private final String lastname;
private final String clientOrBusinessName;
private final String fullname;
private final Long officeId;
private final LocalDate joinedDate;
public ClientCommand(final String externalId, final String firstname, final String lastname, final String clientOrBusinessName,
final Long officeId, final LocalDate joinedDate) {
public ClientCommand(final String accountNo, final String externalId, final String firstname, final String middlename,
final String lastname, final String fullname, final Long officeId, final LocalDate joinedDate) {
this.accountNo = accountNo;
this.externalId = externalId;
this.firstname = firstname;
this.middlename = middlename;
this.lastname = lastname;
this.clientOrBusinessName = clientOrBusinessName;
this.fullname = fullname;
this.officeId = officeId;
this.joinedDate = joinedDate;
}
public void validateForCreate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
if (StringUtils.isNotBlank(this.clientOrBusinessName)) {
baseDataValidator.reset().parameter("firstname").value(this.firstname)
.mustBeBlankWhenParameterProvided("clientOrBusinessName", this.clientOrBusinessName);
baseDataValidator.reset().parameter("lastname").value(this.lastname)
.mustBeBlankWhenParameterProvided("clientOrBusinessName", this.clientOrBusinessName);
} else {
baseDataValidator.reset().parameter("firstname").value(this.firstname).notBlank();
baseDataValidator.reset().parameter("lastname").value(this.lastname).notBlank();
}
baseDataValidator.reset().parameter("joinedDate").value(this.joinedDate).notBlank();
baseDataValidator.reset().parameter("externalId").value(this.externalId).notExceedingLengthOf(100);
baseDataValidator.reset().parameter("officeId").value(this.officeId).notNull().integerGreaterThanZero();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
if (this.clientOrBusinessName != null && StringUtils.isNotBlank(this.clientOrBusinessName)) {
baseDataValidator.reset().parameter("firstname").value(this.firstname)
.mustBeBlankWhenParameterProvided("clientOrBusinessName", this.clientOrBusinessName);
baseDataValidator.reset().parameter("lastname").value(this.lastname)
.mustBeBlankWhenParameterProvided("clientOrBusinessName", this.clientOrBusinessName);
} else if (StringUtils.isBlank(this.clientOrBusinessName)) {
baseDataValidator.reset().parameter("firstname").value(this.firstname).ignoreIfNull().notBlank();
baseDataValidator.reset().parameter("lastname").value(this.lastname).ignoreIfNull().notBlank();
}
baseDataValidator.reset().parameter("joinedDate").value(this.joinedDate).ignoreIfNull().notBlank();
baseDataValidator.reset().parameter("externalId").value(this.externalId).ignoreIfNull().notExceedingLengthOf(100);
baseDataValidator.reset().anyOfNotNull(this.firstname, this.lastname, this.clientOrBusinessName, this.joinedDate, this.externalId,
this.officeId);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public String getExternalId() {
return this.externalId;
}
@@ -87,12 +34,16 @@ public class ClientCommand {
return this.firstname;
}
public String getMiddlename() {
return this.middlename;
}
public String getLastname() {
return this.lastname;
}
public String getClientOrBusinessName() {
return this.clientOrBusinessName;
public String getFullname() {
return this.fullname;
}
public Long getOfficeId() {
@@ -14,13 +14,14 @@ final public class ClientData {
private final Long id;
private final String accountNo;
private final String externalId;
private final String firstname;
private final String middlename;
private final String lastname;
private final String clientOrBusinessName;
private final String fullname;
private final String displayName;
private final Long officeId;
private final String officeName;
private final String externalId;
private final LocalDate joinedDate;
private final String imageKey;
@SuppressWarnings("unused")
@@ -31,91 +32,52 @@ final public class ClientData {
private final ClientData currentChange;
private final Collection<ClientData> allChanges;
private static String buildDisplayNameFrom(final String firstname, final String lastname) {
String displayName = null;
StringBuilder displayNameBuilder = new StringBuilder();
if (StringUtils.isNotBlank(firstname)) {
displayNameBuilder.append(firstname).append(' ');
}
if (StringUtils.isNotBlank(lastname)) {
displayNameBuilder.append(lastname);
displayName = displayNameBuilder.toString();
}
return displayName;
}
public static ClientData dataChangeInstance(final Long id, final Long officeId, final String externalId, final String firstname,
final String lastname, final String clientOrBusinessName, final LocalDate joiningDate) {
final String middlename, final String lastname, final String fullname, final LocalDate joiningDate) {
String firstnameValue = firstname;
String lastnameValue = lastname;
if (StringUtils.isNotBlank(clientOrBusinessName)) {
firstnameValue = null;
lastnameValue = clientOrBusinessName;
}
final String displayName = buildDisplayNameFrom(firstnameValue, lastnameValue);
return new ClientData(null, officeId, null, id, firstnameValue, lastnameValue, displayName, externalId, joiningDate, null, null, null,
null);
String localDisplayName = null;
return new ClientData(null, officeId, null, id, firstname, middlename, lastname, fullname, localDisplayName, externalId,
joiningDate, null, null, null, null);
}
public static ClientData integrateChanges(final ClientData clientData, ClientData currentChange, final Collection<ClientData> allChanges) {
String firstname = clientData.firstname;
String lastname = clientData.lastname;
if (StringUtils.isNotBlank(clientData.clientOrBusinessName)) {
firstname = null;
lastname = clientData.clientOrBusinessName;
}
final String displayName = buildDisplayNameFrom(firstname, lastname);
return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName,
clientData.externalId, clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, currentChange, allChanges);
return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname,
clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId,
clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, currentChange, allChanges);
}
public static ClientData template(final Long officeId, final LocalDate joinedDate, final List<OfficeLookup> allowedOffices) {
return new ClientData(null, officeId, null, null, null, null, null, null, joinedDate, null, allowedOffices, null, null);
return new ClientData(null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, allowedOffices, null, null);
}
public static ClientData templateOnTop(final ClientData clientData, final List<OfficeLookup> allowedOffices) {
String firstname = clientData.firstname;
String lastname = clientData.lastname;
if (StringUtils.isNotBlank(clientData.clientOrBusinessName)) {
firstname = null;
lastname = clientData.clientOrBusinessName;
}
final String displayName = buildDisplayNameFrom(firstname, lastname);
return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName,
clientData.externalId, clientData.joinedDate, clientData.imageKey, allowedOffices, clientData.currentChange,
clientData.allChanges);
return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname,
clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId,
clientData.joinedDate, clientData.imageKey, allowedOffices, clientData.currentChange, clientData.allChanges);
}
public static ClientData clientIdentifier(final Long id, final String accountIdentifier, final String firstname, final String lastname, final Long officeId,
public static ClientData clientIdentifier(final Long id, final String accountIdentifier, final String firstname,
final String middlename, final String lastname, final String fullname, final String displayName, final Long officeId,
final String officeName) {
final String displayName = buildDisplayNameFrom(firstname, lastname);
return new ClientData(accountIdentifier, officeId, officeName, id, firstname, lastname, displayName, null, null, null, null, null, null);
return new ClientData(accountIdentifier, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, null,
null, null, null, null, null);
}
public ClientData(final String accountNo, final Long officeId, final String officeName, final Long id, final String firstname, final String lastname,
final String displayName, final String externalId, final LocalDate joinedDate, final String imageKey,
final List<OfficeLookup> allowedOffices, final ClientData currentChange, final Collection<ClientData> allChanges) {
public ClientData(final String accountNo, final Long officeId, final String officeName, final Long id, final String firstname,
final String middlename, final String lastname, final String fullname, final String displayName, final String externalId,
final LocalDate joinedDate, final String imageKey, final List<OfficeLookup> allowedOffices, final ClientData currentChange,
final Collection<ClientData> allChanges) {
this.accountNo = accountNo;
this.officeId = officeId;
this.officeName = officeName;
this.id = id;
this.firstname = StringUtils.defaultIfEmpty(firstname, null);
/*** unset last name for business name **/
if (StringUtils.isBlank(firstname)) {
this.lastname = null;
this.clientOrBusinessName = lastname;
} else {
this.lastname = lastname;
this.clientOrBusinessName = null;
}
this.displayName = displayName;
this.middlename = StringUtils.defaultIfEmpty(middlename, null);
this.lastname = StringUtils.defaultIfEmpty(lastname, null);
this.fullname = StringUtils.defaultIfEmpty(fullname, null);
this.displayName = StringUtils.defaultIfEmpty(displayName, null);
this.externalId = StringUtils.defaultIfEmpty(externalId, null);
this.joinedDate = joinedDate;
this.imageKey = imageKey;
@@ -1,12 +0,0 @@
package org.mifosplatform.portfolio.client.domain;
/**
* Responsible for generating unique account identifiers based some rules or patterns.
*
* @see ClientAccountIdentifierGenerator
*/
public interface AccountIdentifierGenerator {
String generate();
}
@@ -0,0 +1,12 @@
package org.mifosplatform.portfolio.client.domain;
import org.springframework.stereotype.Component;
@Component
public class AccountIdentifierGeneratorFactory {
public AccountNumberGenerator determineClientAccountNoGenerator(final Long clientId) {
return new ClientAccountNumberGenerator(clientId, 9);
}
}
@@ -0,0 +1,12 @@
package org.mifosplatform.portfolio.client.domain;
/**
* Responsible for generating unique account number based on some rules or patterns.
*
* @see ClientAccountNumberGenerator
*/
public interface AccountNumberGenerator {
String generate();
}
@@ -1,7 +1,9 @@
package org.mifosplatform.portfolio.client.domain;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Column;
@@ -11,10 +13,14 @@ import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator;
import org.mifosplatform.organisation.office.domain.Office;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -23,21 +29,27 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
@Table(name = "m_client")
public class Client extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@Column(name = "account_no", length=40, unique=true, nullable=false)
@Column(name = "account_no", length = 40, unique = true, nullable = false)
private String accountNumber;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@Column(name = "firstname", length = 50)
private String firstName;
private String firstname;
@Column(name = "middlename", length = 50)
private String middlename;
@Column(name = "lastname", length = 50)
private String lastName;
private String lastname;
@Column(name = "display_name", length = 50)
@Column(name = "fullname", length = 100)
private String fullname;
@SuppressWarnings("unused")
@Column(name = "display_name", length = 100)
private String displayName;
@Column(name = "joined_date")
@@ -53,36 +65,34 @@ public class Client extends AbstractPersistable<Long> {
@Column(name = "image_key", length = 500)
private String imageKey;
@Transient
private boolean accountNumberRequiresAutoGeneration = false;
public static Client fromJson(final Office clientOffice, final JsonCommand command) {
final String joiningDateParamName = "joinedDate";
final LocalDate joiningDate = command.localDateValueOfParameterNamed(joiningDateParamName);
final String firstnameParamName = "firstname";
String firstname = command.stringValueOfParameterNamed(firstnameParamName);
final String lastnameParamName = "lastname";
String lastname = command.stringValueOfParameterNamed(lastnameParamName);
final String clientOrBusinessNameParamName = "clientOrBusinessName";
final String clientOrBusinessName = command.stringValueOfParameterNamed(clientOrBusinessNameParamName);
if (StringUtils.isNotBlank(clientOrBusinessName)) {
lastname = clientOrBusinessName;
firstname = null;
}
final String externalIdParamName = "externalId";
final String externalId = command.stringValueOfParameterNamed(externalIdParamName);
return new Client(clientOffice, firstname, lastname, joiningDate, externalId);
final String accountNo = command.stringValueOfParameterNamed("accountNo");
final String firstname = command.stringValueOfParameterNamed("firstname");
final String middlename = command.stringValueOfParameterNamed("middlename");
final String lastname = command.stringValueOfParameterNamed("lastname");
final String fullname = command.stringValueOfParameterNamed("fullname");
final LocalDate joiningDate = command.localDateValueOfParameterNamed("joinedDate");
final String externalId = command.stringValueOfParameterNamed("externalId");
return new Client(clientOffice, accountNo, firstname, middlename, lastname, fullname, joiningDate, externalId);
}
protected Client() {
//
}
private Client(final Office office, final String firstName, final String lastName, final LocalDate openingDate, final String externalId) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
private Client(final Office office, final String accountNo, final String firstname, final String middlename, final String lastname, final String fullname,
final LocalDate openingDate, final String externalId) {
if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true;
} else {
this.accountNumber = accountNo;
}
this.office = office;
if (StringUtils.isNotBlank(externalId)) {
this.externalId = externalId.trim();
@@ -90,18 +100,40 @@ public class Client extends AbstractPersistable<Long> {
this.externalId = null;
}
this.joinedDate = openingDate.toDateMidnight().toDate();
if (StringUtils.isNotBlank(firstName)) {
this.firstName = firstName.trim();
if (StringUtils.isNotBlank(firstname)) {
this.firstname = firstname.trim();
} else {
this.firstName = null;
this.firstname = null;
}
this.lastName = lastName.trim();
// populate display name
if (this.firstName != null) {
this.displayName = this.firstName + " " + this.lastName;
if (StringUtils.isNotBlank(middlename)) {
this.middlename = middlename.trim();
} else {
this.displayName = this.lastName;
this.middlename = null;
}
if (StringUtils.isNotBlank(lastname)) {
this.lastname = lastname.trim();
} else {
this.lastname = null;
}
if (StringUtils.isNotBlank(fullname)) {
this.fullname = fullname.trim();
} else {
this.fullname = null;
}
deriveDisplayName();
validateNameParts();
}
public boolean isAccountNumberRequiresAutoGeneration() {
return this.accountNumberRequiresAutoGeneration;
}
public void setAccountNumberRequiresAutoGeneration(boolean accountNumberRequiresAutoGeneration) {
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
}
public boolean identifiedBy(final String identifier) {
@@ -111,84 +143,137 @@ public class Client extends AbstractPersistable<Long> {
public boolean identifiedBy(Long clientId) {
return getId().equals(clientId);
}
public void changeOffice(final Office newOffice) {
this.office = newOffice;
}
public void updateAccountIdentifier(final String accountIdentifier) {
this.accountNumber = accountIdentifier;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
final String officeIdParamName = "officeId";
if (command.isChangeInLongParameterNamed(officeIdParamName, this.office.getId())) {
final Long newValue = command.longValueOfParameterNamed(officeIdParamName);
actualChanges.put(officeIdParamName, newValue);
}
final String joiningDateParamName = "joinedDate";
if (command.isChangeInLocalDateParameterNamed(joiningDateParamName, getJoiningLocalDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(joiningDateParamName);
actualChanges.put(joiningDateParamName, valueAsInput);
actualChanges.put("dateFormat", dateFormatAsInput);
actualChanges.put("locale", localeAsInput);
final LocalDate newValue = command.localDateValueOfParameterNamed(joiningDateParamName);
this.joinedDate = newValue.toDate();
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(9);
final String accountNoParamName = "accountNo";
if (command.isChangeInStringParameterNamed(accountNoParamName, this.accountNumber)) {
final String newValue = command.stringValueOfParameterNamed(accountNoParamName);
actualChanges.put(accountNoParamName, newValue);
this.accountNumber = StringUtils.defaultIfEmpty(newValue, null);
}
final String clientOrBusinessNameParamName = "clientOrBusinessName";
final String lastnameParamName = "lastname";
if (command.isChangeInStringParameterNamed(lastnameParamName, getLastnameIfNotClientOrBusinessName())) {
final String newValue = command.stringValueOfParameterNamed(lastnameParamName);
actualChanges.put(lastnameParamName, newValue);
if (StringUtils.isNotBlank(getClientOrBusinessName())) {
actualChanges.put(clientOrBusinessNameParamName, "");
}
this.lastName = newValue;
}
final String firstnameParamName = "firstname";
if (command.isChangeInStringParameterNamed(firstnameParamName, this.firstName)) {
final String newValue = command.stringValueOfParameterNamed(firstnameParamName);
actualChanges.put(firstnameParamName, newValue);
this.firstName = newValue;
}
if (command.isChangeInStringParameterNamed(clientOrBusinessNameParamName, getClientOrBusinessName())) {
final String newValue = command.stringValueOfParameterNamed(clientOrBusinessNameParamName);
actualChanges.put(clientOrBusinessNameParamName, newValue);
if (StringUtils.isNotBlank(newValue)) {
this.lastName = newValue;
this.firstName = null;
this.displayName = this.lastName;
} else {
deriveDisplayName();
}
}
final String externalIdParamName = "externalId";
if (command.isChangeInStringParameterNamed(externalIdParamName, this.externalId)) {
final String newValue = command.stringValueOfParameterNamed(externalIdParamName);
actualChanges.put(externalIdParamName, newValue);
this.externalId = StringUtils.defaultIfEmpty(newValue, null);
}
final String officeIdParamName = "officeId";
if (command.isChangeInLongParameterNamed(officeIdParamName, this.office.getId())) {
final Long newValue = command.longValueOfParameterNamed(officeIdParamName);
actualChanges.put(officeIdParamName, newValue);
}
final String firstnameParamName = "firstname";
if (command.isChangeInStringParameterNamed(firstnameParamName, this.firstname)) {
final String newValue = command.stringValueOfParameterNamed(firstnameParamName);
actualChanges.put(firstnameParamName, newValue);
this.firstname = StringUtils.defaultIfEmpty(newValue, null);
}
final String middlenameParamName = "middlename";
if (command.isChangeInStringParameterNamed(middlenameParamName, this.middlename)) {
final String newValue = command.stringValueOfParameterNamed(middlenameParamName);
actualChanges.put(middlenameParamName, newValue);
this.middlename = StringUtils.defaultIfEmpty(newValue, null);
}
final String lastnameParamName = "lastname";
if (command.isChangeInStringParameterNamed(lastnameParamName, this.lastname)) {
final String newValue = command.stringValueOfParameterNamed(lastnameParamName);
actualChanges.put(lastnameParamName, newValue);
this.lastname = StringUtils.defaultIfEmpty(newValue, null);
}
final String fullnameParamName = "fullname";
if (command.isChangeInStringParameterNamed(fullnameParamName, this.fullname)) {
final String newValue = command.stringValueOfParameterNamed(fullnameParamName);
actualChanges.put(fullnameParamName, newValue);
this.fullname = newValue;
}
validateNameParts();
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
final String joiningDateParamName = "joinedDate";
if (command.isChangeInLocalDateParameterNamed(joiningDateParamName, getJoiningLocalDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(joiningDateParamName);
actualChanges.put(joiningDateParamName, valueAsInput);
actualChanges.put("dateFormat", dateFormatAsInput);
actualChanges.put("locale", localeAsInput);
final LocalDate newValue = command.localDateValueOfParameterNamed(joiningDateParamName);
this.joinedDate = newValue.toDate();
}
deriveDisplayName();
return actualChanges;
}
private void deriveDisplayName() {
this.displayName = this.firstName + " " + this.lastName;
private void validateNameParts() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
if (StringUtils.isNotBlank(this.fullname)) {
baseDataValidator.reset().parameter("firstname").value(this.firstname)
.mustBeBlankWhenParameterProvided("fullname", this.fullname);
baseDataValidator.reset().parameter("middlename").value(this.middlename)
.mustBeBlankWhenParameterProvided("fullname", this.fullname);
baseDataValidator.reset().parameter("lastname").value(this.lastname)
.mustBeBlankWhenParameterProvided("fullname", this.fullname);
}
if (StringUtils.isBlank(this.fullname)) {
baseDataValidator.reset().parameter("firstname").value(this.firstname).notBlank().notExceedingLengthOf(50);
baseDataValidator.reset().parameter("middlename").value(this.middlename).ignoreIfNull().notExceedingLengthOf(50);
baseDataValidator.reset().parameter("lastname").value(this.lastname).notBlank().notExceedingLengthOf(50);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
private void deriveDisplayName() {
StringBuilder nameBuilder = new StringBuilder();
if (StringUtils.isNotBlank(this.firstname)) {
nameBuilder.append(this.firstname).append(' ');
}
if (StringUtils.isNotBlank(this.middlename)) {
nameBuilder.append(this.middlename).append(' ');
}
if (StringUtils.isNotBlank(this.lastname)) {
nameBuilder.append(this.lastname);
}
if (StringUtils.isNotBlank(this.fullname)) {
nameBuilder = new StringBuilder(this.fullname);
}
this.displayName = nameBuilder.toString();
}
private LocalDate getJoiningLocalDate() {
LocalDate joiningLocalDate = null;
if (this.joinedDate != null) {
@@ -197,26 +282,6 @@ public class Client extends AbstractPersistable<Long> {
return joiningLocalDate;
}
/*
*
*/
private String getLastnameIfNotClientOrBusinessName() {
String lastname = null;
if (StringUtils.isNotBlank(this.firstName) && StringUtils.isNotBlank(this.lastName)) {
lastname = this.lastName;
}
return lastname;
}
private String getClientOrBusinessName() {
String clientOrBusinessName = null;
if (StringUtils.isBlank(this.firstName) && StringUtils.isNotBlank(this.lastName)) {
clientOrBusinessName = this.lastName;
}
return clientOrBusinessName;
}
/**
* Delete is a <i>soft delete</i>. Updates flag on client so it wont appear
* in query/report results.
@@ -240,54 +305,22 @@ public class Client extends AbstractPersistable<Long> {
return office;
}
public void setOffice(Office office) {
this.office = office;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
return firstname;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
return lastname;
}
public Date getJoiningDate() {
return joinedDate;
}
public void setJoiningDate(Date joiningDate) {
this.joinedDate = joiningDate;
}
public String getExternalId() {
return externalId;
}
public void setExternalId(String externalId) {
this.externalId = externalId;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public String getImageKey() {
return imageKey;
}
@@ -1,16 +1,16 @@
package org.mifosplatform.portfolio.client.domain;
/**
* Example {@link AccountIdentifierGenerator} for clients that takes clients
* Example {@link AccountNumberGenerator} for clients that takes clients
* auto generated database id and zero fills it ensuring the identifier is
* always of a given <code>maxLength</code>.
*/
public class ClientAccountIdentifierGenerator implements AccountIdentifierGenerator {
public class ClientAccountNumberGenerator implements AccountNumberGenerator {
private final Long clientId;
private final int maxLength;
public ClientAccountIdentifierGenerator(final Long clientId, int maxLength) {
public ClientAccountNumberGenerator(final Long clientId, int maxLength) {
this.clientId = clientId;
this.maxLength = maxLength;}
@@ -1,14 +1,19 @@
package org.mifosplatform.portfolio.client.serialization;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
@@ -29,8 +34,8 @@ public final class ClientCommandFromApiJsonDeserializer extends AbstractFromApiJ
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "externalId", "firstname", "lastname",
"clientOrBusinessName", "officeId", "joinedDate", "locale", "dateFormat"));
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("id", "accountNo", "externalId", "firstname",
"middlename", "lastname", "fullname", "officeId", "joinedDate", "locale", "dateFormat"));
private final FromJsonHelper fromApiJsonHelper;
@@ -48,13 +53,113 @@ public final class ClientCommandFromApiJsonDeserializer extends AbstractFromApiJ
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final Long officeId = fromApiJsonHelper.extractLongNamed("officeId", element);
final String accountNo = fromApiJsonHelper.extractStringNamed("accountNo", element);
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
final String firstname = fromApiJsonHelper.extractStringNamed("firstname", element);
final String middlename = fromApiJsonHelper.extractStringNamed("middlename", element);
final String lastname = fromApiJsonHelper.extractStringNamed("lastname", element);
final String clientOrBusinessName = fromApiJsonHelper.extractStringNamed("clientOrBusinessName", element);
final String fullname = fromApiJsonHelper.extractStringNamed("fullname", element);
final Long officeId = fromApiJsonHelper.extractLongNamed("officeId", element);
final LocalDate joiningDate = fromApiJsonHelper.extractLocalDateNamed("joinedDate", element);
return new ClientCommand(externalId, firstname, lastname, clientOrBusinessName, officeId, joiningDate);
return new ClientCommand(accountNo, externalId, firstname, middlename, lastname, fullname, officeId, joiningDate);
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
boolean atLeastOneParameterPassedForUpdate = false;
final String accountNoParameterName = "accountNo";
if (fromApiJsonHelper.parameterExists(accountNoParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String accountNo = fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
final String externalIdParameterName = "externalId";
if (fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String externalId = fromApiJsonHelper.extractStringNamed(externalIdParameterName, element);
baseDataValidator.reset().parameter(externalIdParameterName).value(externalId).ignoreIfNull().notExceedingLengthOf(100);
}
final String officeIdParameterName = "officeId";
if (fromApiJsonHelper.parameterExists(officeIdParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final Long officeId = fromApiJsonHelper.extractLongNamed(officeIdParameterName, element);
baseDataValidator.reset().parameter(officeIdParameterName).value(officeId).notNull().integerGreaterThanZero();
}
final String joinedDateParameterName = "joinedDate";
if (fromApiJsonHelper.parameterExists(joinedDateParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String joinedDateStr = fromApiJsonHelper.extractStringNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDateStr).notBlank();
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDate).notNull();
}
if (!atLeastOneParameterPassedForUpdate) {
final Object forceError = null;
baseDataValidator.reset().anyOfNotNull(forceError);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
final String accountNoParameterName = "accountNo";
if (fromApiJsonHelper.parameterExists(accountNoParameterName, element)) {
final String accountNo = fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).notBlank().notExceedingLengthOf(20);
}
final String externalIdParameterName = "externalId";
if (fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
final String externalId = fromApiJsonHelper.extractStringNamed(externalIdParameterName, element);
baseDataValidator.reset().parameter(externalIdParameterName).value(externalId).ignoreIfNull().notExceedingLengthOf(100);
}
final String officeIdParameterName = "officeId";
if (fromApiJsonHelper.parameterExists(officeIdParameterName, element)) {
final Long officeId = fromApiJsonHelper.extractLongNamed(officeIdParameterName, element);
baseDataValidator.reset().parameter(officeIdParameterName).value(officeId).notNull().integerGreaterThanZero();
}
final String joinedDateParameterName = "joinedDate";
if (fromApiJsonHelper.parameterExists(joinedDateParameterName, element)) {
final String joinedDateStr = fromApiJsonHelper.extractStringNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDateStr).notBlank();
final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(joinedDateParameterName, element);
baseDataValidator.reset().parameter(joinedDateParameterName).value(joinedDate).notNull();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -130,7 +130,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private static final class ClientMapper implements RowMapper<ClientData> {
public String clientSchema() {
return "c.account_no as accountNo, c.office_id as officeId, o.name as officeName, c.id as id, c.firstname as firstname, c.lastname as lastname, c.display_name as displayName, "
return "c.account_no as accountNo, c.office_id as officeId, o.name as officeName, c.id as id, "
+ "c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "
+ "c.fullname as fullname, c.display_name as displayName, "
+ "c.external_id as externalId, c.joined_date as joinedDate, c.image_key as imagekey from m_client c join m_office o on o.id = c.office_id "
+ " where o.hierarchy like ? and c.is_deleted=0 ";
}
@@ -141,19 +143,18 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final String accountNo = rs.getString("accountNo");
final Long officeId = JdbcSupport.getLong(rs, "officeId");
final Long id = JdbcSupport.getLong(rs, "id");
String firstname = rs.getString("firstname");
if (StringUtils.isBlank(firstname)) {
firstname = "";
}
final String firstname = rs.getString("firstname");
final String middlename = rs.getString("middlename");
final String lastname = rs.getString("lastname");
final String fullname = rs.getString("fullname");
final String displayName = rs.getString("displayName");
final String externalId = rs.getString("externalId");
final LocalDate joinedDate = JdbcSupport.getLocalDate(rs, "joinedDate");
final String imageKey = rs.getString("imageKey");
final String officeName = rs.getString("officeName");
return new ClientData(accountNo, officeId, officeName, id, firstname, lastname, displayName, externalId, joinedDate, imageKey, null, null,
null);
return new ClientData(accountNo, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, externalId,
joinedDate, imageKey, null, null, null);
}
}
@@ -250,36 +251,37 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
}
}
}
List<ClientAccountSummaryData> pendingApprovalSavingAccounts = new ArrayList<ClientAccountSummaryData>();
List<ClientAccountSummaryData> approvedSavingAccounts = new ArrayList<ClientAccountSummaryData>();
List<ClientAccountSummaryData> withdrawnByClientSavingAccounts = new ArrayList<ClientAccountSummaryData>();
List<ClientAccountSummaryData> rejectedSavingAccounts = new ArrayList<ClientAccountSummaryData>();
List<ClientAccountSummaryData> closedSavingAccounts = new ArrayList<ClientAccountSummaryData>();
ClientSavingAccountSummaryDataMapper clientSavingAccountSummaryDataMapper = new ClientSavingAccountSummaryDataMapper();
String savingAccountsSql = "select " + clientSavingAccountSummaryDataMapper.schema() + " where sa.client_id=? and sa.is_deleted=0";
List<ClientAccountSummaryData> savingAccountsResults = this.jdbcTemplate.query(savingAccountsSql, clientSavingAccountSummaryDataMapper, new Object[] {clientId});
String savingAccountsSql = "select " + clientSavingAccountSummaryDataMapper.schema()
+ " where sa.client_id=? and sa.is_deleted=0";
List<ClientAccountSummaryData> savingAccountsResults = this.jdbcTemplate.query(savingAccountsSql,
clientSavingAccountSummaryDataMapper, new Object[] { clientId });
if (savingAccountsResults != null) {
for (ClientAccountSummaryData account : savingAccountsResults){
if (account.accountStatusId() == 100)
pendingApprovalSavingAccounts.add(account);
else if (account.accountStatusId() ==300)
approvedSavingAccounts.add(account);
else if (account.accountStatusId() == 400)
withdrawnByClientSavingAccounts.add(account);
else if (account.accountStatusId() == 500)
rejectedSavingAccounts.add(account);
else if (account.accountStatusId() == 600)
closedSavingAccounts.add(account);
}
}
for (ClientAccountSummaryData account : savingAccountsResults) {
if (account.accountStatusId() == 100)
pendingApprovalSavingAccounts.add(account);
else if (account.accountStatusId() == 300)
approvedSavingAccounts.add(account);
else if (account.accountStatusId() == 400)
withdrawnByClientSavingAccounts.add(account);
else if (account.accountStatusId() == 500)
rejectedSavingAccounts.add(account);
else if (account.accountStatusId() == 600) closedSavingAccounts.add(account);
}
}
return new ClientAccountSummaryCollectionData(pendingApprovalLoans, awaitingDisbursalLoans, openLoans, closedLoans,
pendingApprovalDepositAccounts, approvedDepositAccounts, withdrawnByClientDespositAccounts, rejectedDepositAccounts,
closedDepositAccounts, preclosedDepositAccounts, maturedDepositAccounts,
pendingApprovalSavingAccounts,approvedSavingAccounts,withdrawnByClientSavingAccounts,rejectedSavingAccounts,closedSavingAccounts);
closedDepositAccounts, preclosedDepositAccounts, maturedDepositAccounts, pendingApprovalSavingAccounts,
approvedSavingAccounts, withdrawnByClientSavingAccounts, rejectedSavingAccounts, closedSavingAccounts);
} catch (EmptyResultDataAccessException e) {
throw new ClientNotFoundException(clientId);
}
@@ -524,8 +526,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private static final class ClientIdentifierMapper implements RowMapper<ClientData> {
public String clientLookupByIdentifierSchema() {
return "c.id as id, c.account_no as accountNo, c.firstname as firstname, c.lastname as lastname, " + "c.office_id as officeId, o.name as officeName "
+ "from m_client c, m_office o, m_client_identifier ci " + "where o.id = c.office_id and c.id=ci.client_id "
return "c.id as id, c.account_no as accountNo, c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "
+ "c.fullname as fullname, c.display_name as displayName," + "c.office_id as officeId, o.name as officeName "
+ " from m_client c, m_office o, m_client_identifier ci " + "where o.id = c.office_id and c.id=ci.client_id "
+ "and ci.document_type_id= ? and ci.document_key like ?";
}
@@ -534,21 +537,20 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final Long id = rs.getLong("id");
final String accountNo = rs.getString("accountNo");
String firstname = rs.getString("firstname");
if (StringUtils.isBlank(firstname)) {
firstname = "";
}
final String firstname = rs.getString("firstname");
final String middlename = rs.getString("middlename");
final String lastname = rs.getString("lastname");
final String fullname = rs.getString("fullname");
final String displayName = rs.getString("displayName");
final Long officeId = rs.getLong("officeId");
final String officeName = rs.getString("officeName");
return ClientData.clientIdentifier(id, accountNo, firstname, lastname, officeId, officeName);
return ClientData.clientIdentifier(id, accountNo, firstname, middlename, lastname, fullname, displayName, officeId, officeName);
}
}
private static final class ClientSavingAccountSummaryDataMapper implements RowMapper<ClientAccountSummaryData> {
private static final class ClientSavingAccountSummaryDataMapper implements RowMapper<ClientAccountSummaryData> {
public String schema() {
@@ -571,5 +573,5 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
return new ClientAccountSummaryData(id, externalId, productId, productName, accountStatusId);
}
}
}
@@ -20,12 +20,11 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.portfolio.client.command.ClientCommand;
import org.mifosplatform.portfolio.client.command.ClientIdentifierCommand;
import org.mifosplatform.portfolio.client.command.ClientNoteCommand;
import org.mifosplatform.portfolio.client.domain.AccountIdentifierGenerator;
import org.mifosplatform.portfolio.client.domain.AccountIdentifierGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientAccountIdentifierGenerator;
import org.mifosplatform.portfolio.client.domain.ClientIdentifier;
import org.mifosplatform.portfolio.client.domain.ClientIdentifierRepository;
import org.mifosplatform.portfolio.client.domain.ClientRepository;
@@ -59,6 +58,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer;
private final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer;
private final AccountIdentifierGeneratorFactory accountIdentifierGeneratorFactory;
@Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepository clientRepository,
@@ -66,7 +66,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final NoteRepository noteRepository, final CodeValueRepository codeValueRepository,
final ClientCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final ClientNoteCommandFromApiJsonDeserializer clientNoteFromApiJsonDeserializer,
final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer) {
final ClientIdentifierCommandFromApiJsonDeserializer clientIdentifierCommandFromApiJsonDeserializer,
final AccountIdentifierGeneratorFactory accountIdentifierGeneratorFactory) {
this.context = context;
this.clientRepository = clientRepository;
this.clientIdentifierRepository = clientIdentifierRepository;
@@ -76,6 +77,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.clientNoteFromApiJsonDeserializer = clientNoteFromApiJsonDeserializer;
this.clientIdentifierCommandFromApiJsonDeserializer = clientIdentifierCommandFromApiJsonDeserializer;
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
}
@Transactional
@@ -119,8 +121,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
try {
context.authenticatedUser();
final ClientCommand clientCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
clientCommand.validateForCreate();
this.fromApiJsonDeserializer.validateForCreate(command.json());
final String officeIdParamName = "officeId";
final Long officeId = command.longValueOfParameterNamed(officeIdParamName);
@@ -131,14 +132,19 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final Client newClient = Client.fromJson(clientOffice, command);
this.clientRepository.save(newClient);
// now use client account identifier generator to generate proper
// account number
AccountIdentifierGenerator generator = new ClientAccountIdentifierGenerator(newClient.getId(), 9);
newClient.updateAccountIdentifier(generator.generate());
this.clientRepository.save(newClient);
if (newClient.isAccountNumberRequiresAutoGeneration()) {
final AccountNumberGenerator accountNoGenerator = this.accountIdentifierGeneratorFactory
.determineClientAccountNoGenerator(newClient.getId());
newClient.updateAccountIdentifier(accountNoGenerator.generate());
this.clientRepository.save(newClient);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(clientOffice.getId())
.withClientId(newClient.getId()).withEntityId(newClient.getId()).build();
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(clientOffice.getId()) //
.withClientId(newClient.getId()) //
.withEntityId(newClient.getId()) //
.build();
} catch (DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
@@ -152,8 +158,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
try {
context.authenticatedUser();
final ClientCommand clientCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
clientCommand.validateForUpdate();
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final Client clientForUpdate = this.clientRepository.findOne(clientId);
if (clientForUpdate == null || clientForUpdate.isDeleted()) { throw new ClientNotFoundException(clientId); }