diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties index c75f3f156..d1ad014d6 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages.properties @@ -29,12 +29,13 @@ label.longname=Full name: label.joiningdate=Joining date: error.msg.header=You have the following errors: -validation.msg.client.office.cannot.be.blank=A client must be associated with an office. +validation.msg.client.officeId.cannot.be.blank=A client must be associated with an office. validation.msg.client.firstname.cannot.be.blank=First name cannot be blank. validation.msg.client.lastname.cannot.be.blank=Last name cannot be blank. -validation.msg.client.joining.date.cannot.be.blank=Joining date cannot be blank. +validation.msg.client.joiningDateFormatted.cannot.be.blank=Joining date cannot be blank. validation.msg.client.fullname.must.be.provide.or.firstname.lastname=A fullname must be provided or else both firstname and lastname details. -validation.msg.client.fullname.cannot.entered.when.firstname.or.lastname.entered=You cannot provide both fullname and firstname or lastname details. A fullname must be provided or else both firstname and lastname details. +validation.msg.client.firstname.cannot.also.be.provided.when.fullname.is.populated=You cannot provide both fullname and firstname or lastname details. A fullname must be provided or else both firstname and lastname details. +validation.msg.client.lastname.cannot.also.be.provided.when.fullname.is.populated=You cannot provide both fullname and firstname or lastname details. A fullname must be provided or else both firstname and lastname details. error.msg.client.id.invalid=Client does not exist. error.msg.office.id.invalid=Office does not exist. diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties index 70f3bd6ad..e27ddd82e 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/static/global-translations/messages_fr.properties @@ -28,13 +28,13 @@ label.longname=Full name:(fr) label.joiningdate=Joining date(fr): error.msg.header=You have the following errors:(fr) -validation.msg.client.office.cannot.be.blank=A client must be associated with an office.(fr) +validation.msg.client.officeId.cannot.be.blank=A client must be associated with an office.(fr) validation.msg.client.firstname.cannot.be.blank=First name cannot be blank.(fr) validation.msg.client.lastname.cannot.be.blank=Last name cannot be blank.(fr) -validation.msg.client.joining.date.cannot.be.blank=Joining date cannot be blank.(fr) +validation.msg.client.joiningDateFormatted.cannot.be.blank=Joining date cannot be blank.(fr) validation.msg.client.fullname.must.be.provide.or.firstname.lastname=A fullname must be provided or else both firstname and lastname details.(fr) -validation.msg.client.fullname.cannot.entered.when.firstname.or.lastname.entered=You cannot provide both fullname and firstname or lastname details. A fullname must be provided or else both firstname and lastname details.(fr) - +validation.msg.client.firstname.cannot.also.be.provided.when.fullname.is.populated=You cannot provide both fullname and firstname or lastname details. A fullname must be provided or else both firstname and lastname details.(fr) +validation.msg.client.lastname.cannot.also.be.provided.when.fullname.is.populated=You cannot provide both fullname and firstname or lastname details. A fullname must be provided or else both firstname and lastname details.(fr) error.msg.client.id.invalid=Client does not exist.(fr) error.msg.office.id.invalid=Office does not exist.(fr) diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/home.jsp b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/home.jsp index 5dc837d2c..c45076c9c 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/home.jsp +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/home.jsp @@ -295,7 +295,6 @@ $(document).ready(function() { contentType: 'application/json', cache: false, beforeSend: function(xhr) { - console.log("base64: " + base64); xhr.setRequestHeader("Authorization", "Basic " + base64); }, error: function(xhr, status, index, anchor) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/Specifications.java b/mifosng-provider/src/main/java/org/mifosng/platform/Specifications.java index 2e33c4e90..bbe288c3b 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/Specifications.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/Specifications.java @@ -6,6 +6,7 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.mifosng.platform.client.domain.Client; +import org.mifosng.platform.client.domain.Note; import org.mifosng.platform.loan.domain.Loan; import org.mifosng.platform.loan.domain.LoanProduct; import org.mifosng.platform.loan.domain.LoanTransaction; @@ -102,4 +103,14 @@ public final class Specifications { } }; } + + public static Specification notesThatMatch(final Organisation organisation, final Long id) { + return new Specification() { + + @Override + public Predicate toPredicate(final Root root, final CriteriaQuery query, final CriteriaBuilder cb) { + return cb.and(cb.equal(root.get("organisation"), organisation), cb.equal(root.get("id"), id)); + } + }; + } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java index 9825c9db9..4d89af70d 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java @@ -31,7 +31,7 @@ public class Client extends AbstractAuditableCustom { @SuppressWarnings("unused") @ManyToOne @JoinColumn(name = "office_id", nullable = false) - private final Office office; + private Office office; @SuppressWarnings("unused") @Column(name = "firstname", length=50) @@ -105,12 +105,20 @@ public class Client extends AbstractAuditableCustom { } public void update(Office clientOffice, String firstname, String lastname, String externalId, LocalDate joiningDate) { - this.joiningDate = joiningDate.toDate(); - this.firstName = firstname; - this.lastName = lastname; - this.externalId = externalId; - if (StringUtils.isNotBlank(externalId)) { - this.externalId = externalId.trim(); - } + if (clientOffice != null) { + this.office = clientOffice; + } + if (joiningDate != null) { + this.joiningDate = joiningDate.toDate(); + } + if (StringUtils.isNotBlank(firstname)) { + this.firstName = firstname; + } + if (StringUtils.isNotBlank(firstname)) { + this.lastName = lastname; + } + if (externalId != null) { + this.externalId = externalId.trim(); + } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientCommandValidator.java index b33e7db4c..0c3ef432a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientCommandValidator.java @@ -18,7 +18,25 @@ public class ClientCommandValidator { } public void validateForUpdate() { + List dataValidationErrors = new ArrayList(); + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client"); + + if (command.getFullname() != null && StringUtils.isNotBlank(command.getFullname())) { + baseDataValidator.reset().parameter("firstname").value(command.getFirstname()).mustBeBlankWhenParameterProvided("fullname", command.getFullname()); + baseDataValidator.reset().parameter("lastname").value(command.getLastname()).mustBeBlankWhenParameterProvided("fullname", command.getFullname()); + } else if (StringUtils.isBlank(command.getFullname())) { + baseDataValidator.reset().parameter("firstname").value(command.getFirstname()).ignoreIfNull().notBlank(); + baseDataValidator.reset().parameter("lastname").value(command.getLastname()).ignoreIfNull().notBlank(); + } + + baseDataValidator.reset().parameter("joiningDateFormatted").value(command.getJoiningDateFormatted()).ignoreIfNull().notBlank(); + baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100); + baseDataValidator.reset().parameter("officeId").value(command.getOfficeId()).ignoreIfNull().notNull().greaterThanZero(); + + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); + } } public void validateForCreate() { @@ -30,47 +48,16 @@ public class ClientCommandValidator { baseDataValidator.reset().parameter("firstname").value(command.getFirstname()).mustBeBlankWhenParameterProvided("fullname", command.getFullname()); baseDataValidator.reset().parameter("lastname").value(command.getLastname()).mustBeBlankWhenParameterProvided("fullname", command.getFullname()); } else { - baseDataValidator.reset().parameter("firstname").value(command.getFirstname()).notBlank();//.andNotBlank("lastname", command.getLastname()); - baseDataValidator.reset().parameter("lastname").value(command.getLastname()).notBlank();//.andNotBlank("firstname", command.getFirstname()); + baseDataValidator.reset().parameter("firstname").value(command.getFirstname()).notBlank(); + baseDataValidator.reset().parameter("lastname").value(command.getLastname()).notBlank(); } - baseDataValidator.reset().parameter("openingDateFormatted").value(command.getJoiningDateFormatted()).notBlank(); + baseDataValidator.reset().parameter("joiningDateFormatted").value(command.getJoiningDateFormatted()).notBlank(); baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).notExceedingLengthOf(100); baseDataValidator.reset().parameter("officeId").value(command.getOfficeId()).notNull().greaterThanZero(); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); } - -// if (StringUtils.isNotBlank(command.getFullname()) && (StringUtils.isNotBlank(command.getFirstname()) || StringUtils.isNotBlank(command.getLastname()))) { -// ApiParameterError error = ApiParameterError.parameterError("validation.msg.client.fullname.cannot.entered.when.firstname.or.lastname.entered", "The parameter fullname cannot be blank.", "fullname"); -// dataValidationErrors.add(error); -// } -// if (StringUtils.isBlank(command.getFirstname()) && StringUtils.isBlank(command.getLastname()) ) { -// -// if (StringUtils.isBlank(command.getFullname())) { -// ApiParameterError error = ApiParameterError.parameterError("validation.msg.client.fullname.must.be.provide.or.firstname.lastname", "The parameter fullname cannot be blank or firstName and lastName must be both provided.", "fullname"); -// dataValidationErrors.add(error); -// } -// } else { -// if (StringUtils.isBlank(command.getFirstname())) { -// ApiParameterError error = ApiParameterError.parameterError("validation.msg.client.firstname.cannot.be.blank", "The parameter firstname cannot be blank.", "firstname"); -// dataValidationErrors.add(error); -// } -// -// if (StringUtils.isBlank(command.getLastname())) { -// ApiParameterError error = ApiParameterError.parameterError("validation.msg.client.lastname.cannot.be.blank", "The parameter lastname cannot be blank.", "lastname"); -// dataValidationErrors.add(error); -// } -// } -// -// if (command.getJoiningDate() == null) { -// ApiParameterError error = ApiParameterError.parameterError("validation.msg.client.joining.date.cannot.be.blank", "The parameter joiningDate cannot be blank.", "joiningDate"); -// dataValidationErrors.add(error); -// } -// -// if (!dataValidationErrors.isEmpty()) { -// throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); -// } } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java index 6b1e402f3..a6765aa4c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java @@ -18,7 +18,7 @@ import org.mifosng.data.ClientLoanAccountSummaryData; import org.mifosng.data.NoteData; import org.mifosng.data.OfficeData; import org.mifosng.data.OfficeLookup; -import org.mifosng.platform.exceptions.PlatformResourceNotFoundException; +import org.mifosng.platform.exceptions.ClientNotFoundException; import org.mifosng.platform.organisation.domain.Organisation; import org.mifosng.platform.organisation.service.OfficeReadPlatformService; import org.mifosng.platform.security.PlatformSecurityContext; @@ -91,9 +91,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService return this.jdbcTemplate.queryForObject(sql, rm, new Object[] {clientId, currentUser.getOrganisation().getId() }); } catch (EmptyResultDataAccessException e) { - throw new PlatformResourceNotFoundException( - "error.msg.client.id.invalid", - "Client with identifier {0} does not exist", clientId); + throw new ClientNotFoundException(clientId); } } @@ -109,10 +107,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService clientData.setOrganisationId(currentUser.getOrganisation().getId()); clientData.setAllowedOffices(offices); -// clientData.setDisplayName(""); -// clientData.setFirstname(""); -// clientData.setLastname(""); -// clientData.setId(Long.valueOf(-1)); clientData.setJoinedDate(new LocalDate()); return clientData; @@ -205,46 +199,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService return new ClientLoanAccountSummaryCollectionData(pendingApprovalLoans, awaitingDisbursalLoans, openLoans, closedLoans); } catch (EmptyResultDataAccessException e) { - throw new PlatformResourceNotFoundException( - "error.msg.client.id.invalid", - "Client with identifier {0} does not exist", clientId); + throw new ClientNotFoundException(clientId); } - -// Collection allLoans = this.loanRepository.findAll(loansThatMatch(currentUser.getOrganisation(), client)); -// -// List pendingApprovalLoans = new ArrayList(); -// List awaitingDisbursalLoans = new ArrayList(); -// List openLoans = new ArrayList(); -// List closedLoans = new ArrayList(); -// -// for (Loan realLoan : allLoans) { -// -// ApplicationCurrency currency = this.applicationCurrencyRepository -// .findOneByCode(realLoan.getLoanRepaymentScheduleDetail() -// .getPrincipal().getCurrencyCode()); -// -// CurrencyData currencyData = new CurrencyData(currency.getCode(), -// currency.getName(), currency.getDecimalPlaces(), -// currency.getDisplaySymbol(), currency.getNameCode()); -// -// LoanAccountData loan = convertToData(realLoan, currencyData); -// -// if (loan.isClosed() || loan.isInterestRebateOutstanding()) { -// closedLoans.add(loan); -// } else if (loan.isPendingApproval()) { -// pendingApprovalLoans.add(loan); -// } else if (loan.isWaitingForDisbursal()) { -// awaitingDisbursalLoans.add(loan); -// } else if (loan.isOpen()) { -// openLoans.add(loan); -// } -// } -// -// Collections.sort(closedLoans, new ClosedLoanComparator()); -// -// return new ClientDataWithAccountsData(clientAccount, -// pendingApprovalLoans, awaitingDisbursalLoans, openLoans, -// closedLoans); } private static final class ClientLoanAccountSummaryDataMapper implements RowMapper { @@ -277,7 +233,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService boolean waitingForDisbursal = statusMapper.isAwaitingDisbursal(); boolean open = statusMapper.isOpen(); boolean closed = statusMapper.isClosed(); -// LocalDate lifeCycleStatusDate; return new ClientLoanAccountSummaryData(id, externalId, productId, loanProductName, loanStatusId, lifeCycleStatusText, pendingApproval, waitingForDisbursal, open, closed); } @@ -302,9 +257,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService new Object[] { currentUser.getOrganisation().getId(), clientId, noteId }); } catch (EmptyResultDataAccessException e) { - throw new PlatformResourceNotFoundException( - "error.msg.client.id.invalid", - "Client with identifier {0} does not exist", clientId); + throw new ClientNotFoundException(clientId); } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index b6d6a5c26..97ffc0f3f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -1,6 +1,7 @@ package org.mifosng.platform.client.service; import static org.mifosng.platform.Specifications.clientsThatMatch; +import static org.mifosng.platform.Specifications.notesThatMatch; import static org.mifosng.platform.Specifications.officesThatMatch; import org.apache.commons.lang.StringUtils; @@ -11,6 +12,8 @@ import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.client.domain.ClientRepository; import org.mifosng.platform.client.domain.Note; import org.mifosng.platform.client.domain.NoteRepository; +import org.mifosng.platform.exceptions.ClientNotFoundException; +import org.mifosng.platform.exceptions.NoteNotFoundException; import org.mifosng.platform.organisation.domain.Office; import org.mifosng.platform.organisation.domain.OfficeRepository; import org.mifosng.platform.security.PlatformSecurityContext; @@ -80,7 +83,6 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP } Client clientForUpdate = this.clientRepository.findOne(clientsThatMatch(currentUser.getOrganisation(), command.getId())); - clientForUpdate.update(clientOffice, firstname, lastname, command.getExternalId(), command.getJoiningDate()); this.clientRepository.save(clientForUpdate); @@ -94,8 +96,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP AppUser currentUser = context.authenticatedUser(); - // FIXME - check resource is found for current organisation - throw PlatformXXXException if not found. - Client clientForUpdate = this.clientRepository.findOne(command.getClientId()); + Client clientForUpdate = this.clientRepository.findOne(clientsThatMatch(currentUser.getOrganisation(), command.getClientId())); + if (clientForUpdate == null) { + throw new ClientNotFoundException(command.getClientId()); + } Note note = Note.clientNote(currentUser.getOrganisation(), clientForUpdate, command.getNote()); @@ -110,9 +114,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP AppUser currentUser = context.authenticatedUser(); - // FIXME - check resource is found for current organisation - throw PlatformXXXException if not found. - Note noteForUpdate = this.noteRepository.findOne(command.getId()); - + Note noteForUpdate = this.noteRepository.findOne(notesThatMatch(currentUser.getOrganisation(), command.getId())); + if (noteForUpdate == null) { + throw new NoteNotFoundException(command.getClientId()); + } noteForUpdate.update(command.getNote()); return new EntityIdentifier(noteForUpdate.getId()); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ClientNotFoundException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ClientNotFoundException.java new file mode 100644 index 000000000..4609919ec --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/ClientNotFoundException.java @@ -0,0 +1,11 @@ +package org.mifosng.platform.exceptions; + +/** + * A {@link RuntimeException} thrown when client resources are not found. + */ +public class ClientNotFoundException extends PlatformResourceNotFoundException { + + public ClientNotFoundException(Long id) { + super("error.msg.client.id.invalid", "Client with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/NoteNotFoundException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/NoteNotFoundException.java new file mode 100644 index 000000000..a7c750c9d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/NoteNotFoundException.java @@ -0,0 +1,11 @@ +package org.mifosng.platform.exceptions; + +/** + * A {@link RuntimeException} thrown when note resources are not found. + */ +public class NoteNotFoundException extends PlatformResourceNotFoundException { + + public NoteNotFoundException(Long id) { + super("error.msg.note.id.invalid", "Note with identifier " + id + " does not exist", id); + } +} \ No newline at end of file