From ae7d78a16e5f02b5a8878c6d7dc25a5bd2659f5b Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Tue, 17 Jul 2012 19:17:58 +0100 Subject: [PATCH] use google gson to support translating json request body into a map. This is towards supporting more robust update of attributes wheter they be optional or mandatory. --- mifosng-provider/build.gradle | 2 + .../platform/api/FundsApiResource.java | 48 ++- .../platform/api/LoanProductApiResource.java | 81 ++-- .../platform/api/OfficeApiResource.java | 93 ++++- .../api/commands/LoanProductCommand.java | 353 ++++++++---------- .../errorhandling/InvalidJsonException.java | 9 + .../InvalidJsonExceptionMapper.java | 31 ++ .../MalformedJsonExceptionMapper.java | 33 ++ .../UnsupportedParameterException.java | 20 + .../UnsupportedParameterExceptionMapper.java | 44 +++ .../ApiDataConversionService.java | 3 + .../ApiDataConversionServiceImpl.java | 135 +++++++ .../mifosng/platform/fund/domain/Fund.java | 9 +- .../platform/loan/domain/LoanProduct.java | 9 +- .../loan/domain/LoanProductRelatedDetail.java | 47 ++- .../service/LoanProductCommandValidator.java | 24 +- ...WritePlatformServiceJpaRepositoryImpl.java | 14 +- .../platform/organisation/domain/Office.java | 7 +- 18 files changed, 633 insertions(+), 329 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonException.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonExceptionMapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/MalformedJsonExceptionMapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterException.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterExceptionMapper.java diff --git a/mifosng-provider/build.gradle b/mifosng-provider/build.gradle index a14c2fc66..1d84929a6 100644 --- a/mifosng-provider/build.gradle +++ b/mifosng-provider/build.gradle @@ -89,6 +89,8 @@ dependencies { [group: 'com.sun.jersey.contribs', name: 'jersey-spring', version: jerseyVersion], [group: 'com.sun.jersey.contribs', name: 'jersey-multipart', version: jerseyVersion], + [group: 'com.google.code.gson', name: 'gson', version: '2.2.2'], + [group: 'org.springframework.data', name: 'spring-data-jpa', version: springDataJpaVersion], [group: 'joda-time', name: 'joda-time', version: '2.1'], diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/FundsApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/FundsApiResource.java index a63b50275..7de327eee 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/FundsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/FundsApiResource.java @@ -1,6 +1,8 @@ package org.mifosng.platform.api; +import java.lang.reflect.Type; import java.util.Collection; +import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -24,6 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + @Path("/funds") @Component @Scope("singleton") @@ -53,9 +58,25 @@ public class FundsApiResource { } @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON }) - public Response createFund(final FundCommand command) { + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response createFund(final String jsonRequestBody) { + + Gson gson = new Gson(); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gson.fromJson(jsonRequestBody, typeOfMap); + + String name = null; + if (requestMap.containsKey("name")) { + name = requestMap.get("name"); + } + + String externalId = null; + if (requestMap.containsKey("externalId")) { + externalId = requestMap.get("externalId"); + } + + FundCommand command = new FundCommand(name, externalId); Long fundId = this.writePlatformService.createFund(command); @@ -77,10 +98,25 @@ public class FundsApiResource { @PUT @Path("{fundId}") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON }) - public Response updateFund(@PathParam("fundId") final Long fundId, final FundCommand command) { + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateFund(@PathParam("fundId") final Long fundId, final String jsonRequestBody) { + Gson gson = new Gson(); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gson.fromJson(jsonRequestBody, typeOfMap); + + String name = null; + if (requestMap.containsKey("name")) { + name = requestMap.get("name"); + } + + String externalId = null; + if (requestMap.containsKey("externalId")) { + externalId = requestMap.get("externalId"); + } + + FundCommand command = new FundCommand(name, externalId); command.setId(fundId); Long entityId = this.writePlatformService.updateFund(command); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java index 947659fde..eec824274 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanProductApiResource.java @@ -1,8 +1,6 @@ package org.mifosng.platform.api; -import java.math.BigDecimal; import java.util.Collection; -import java.util.Locale; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -49,40 +47,23 @@ public class LoanProductApiResource { private ApiJSONFormattingService jsonFormattingService; @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({ MediaType.APPLICATION_JSON }) - public Response createLoanProduct(LoanProductCommand command) { + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response createLoanProduct(final String jsonRequestBody) { - Locale clientApplicationLocale = this.apiDataConversionService.localeFromString(command.getLocale()); + final LoanProductCommand command = this.apiDataConversionService.convertJsonToCommand(null, jsonRequestBody); - BigDecimal principalValue = this.apiDataConversionService.convertFrom(command.getPrincipal(), "principal", clientApplicationLocale); - BigDecimal inArrearsToleranceValue = this.apiDataConversionService.convertFrom(command.getInArrearsTolerance(), "inArrearsTolerance", clientApplicationLocale); - BigDecimal interestRatePerPeriodValue = this.apiDataConversionService.convertFrom(command.getInterestRatePerPeriod(),"interestRatePerPeriod", clientApplicationLocale); - - Integer digitsAfterDecimalValue = this.apiDataConversionService.convertToInteger(command.getDigitsAfterDecimal(), "digitsAfterDecimal", clientApplicationLocale); - Integer repaymentEveryValue = this.apiDataConversionService.convertToInteger(command.getRepaymentEvery(), "repaymentEvery", clientApplicationLocale); - Integer numberOfRepaymentsValue = this.apiDataConversionService.convertToInteger(command.getNumberOfRepayments(), "numberOfRepayments", clientApplicationLocale); - - command.setPrincipalValue(principalValue); - command.setInArrearsToleranceValue(inArrearsToleranceValue); - command.setInterestRatePerPeriodValue(interestRatePerPeriodValue); - command.setDigitsAfterDecimalValue(digitsAfterDecimalValue); - command.setRepaymentEveryValue(repaymentEveryValue); - command.setNumberOfRepaymentsValue(numberOfRepaymentsValue); - - EntityIdentifier entityIdentifier = this.loanProductWritePlatformService - .createLoanProduct(command); + EntityIdentifier entityIdentifier = this.loanProductWritePlatformService.createLoanProduct(command); return Response.ok().entity(entityIdentifier).build(); } @GET - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveAllLoanProducts(@Context UriInfo uriInfo) { + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String retrieveAllLoanProducts(@Context final UriInfo uriInfo) { - Collection products = this.loanProductReadPlatformService - .retrieveAllLoanProducts(); + Collection products = this.loanProductReadPlatformService.retrieveAllLoanProducts(); String selectedFields = ""; return this.jsonFormattingService.convertRequest(products, filterName, @@ -96,12 +77,11 @@ public class LoanProductApiResource { */ @GET @Path("template") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveNewLoanProductDetails(@Context UriInfo uriInfo) { + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) { - LoanProductData loanProduct = this.loanProductReadPlatformService - .retrieveNewLoanProductDetails(); + LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails(); String selectedFields = defaultFieldList + "," + allowedFieldList; return this.jsonFormattingService.convertRequest(loanProduct, @@ -111,14 +91,11 @@ public class LoanProductApiResource { @GET @Path("{productId}") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveLoanProductDetails( - @PathParam("productId") final Long productId, - @Context UriInfo uriInfo) { + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String retrieveLoanProductDetails(@PathParam("productId") final Long productId, final UriInfo uriInfo) { - LoanProductData loanProduct = this.loanProductReadPlatformService - .retrieveLoanProduct(productId); + LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); String selectedFields = ""; return this.jsonFormattingService.convertRequest(loanProduct, @@ -128,28 +105,12 @@ public class LoanProductApiResource { @PUT @Path("{productId}") - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Produces({ MediaType.APPLICATION_JSON }) - public Response updateLoanProduct(@PathParam("productId") final Long productId, final LoanProductCommand command) { - - Locale clientApplicationLocale = this.apiDataConversionService.localeFromString(command.getLocale()); + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateLoanProduct(@PathParam("productId") final Long productId, final String jsonRequestBody) { - BigDecimal principalValue = this.apiDataConversionService.convertFrom(command.getPrincipal(), "principal", clientApplicationLocale); - BigDecimal inArrearsToleranceValue = this.apiDataConversionService.convertFrom(command.getInArrearsTolerance(), "inArrearsTolerance", clientApplicationLocale); - BigDecimal interestRatePerPeriodValue = this.apiDataConversionService.convertFrom(command.getInterestRatePerPeriod(),"interestRatePerPeriod", clientApplicationLocale); + LoanProductCommand command = this.apiDataConversionService.convertJsonToCommand(productId, jsonRequestBody); - Integer digitsAfterDecimalValue = this.apiDataConversionService.convertToInteger(command.getDigitsAfterDecimal(), "digitsAfterDecimal", clientApplicationLocale); - Integer repaymentEveryValue = this.apiDataConversionService.convertToInteger(command.getRepaymentEvery(), "repaymentEvery", clientApplicationLocale); - Integer numberOfRepaymentsValue = this.apiDataConversionService.convertToInteger(command.getNumberOfRepayments(), "numberOfRepayments", clientApplicationLocale); - - command.setPrincipalValue(principalValue); - command.setInArrearsToleranceValue(inArrearsToleranceValue); - command.setInterestRatePerPeriodValue(interestRatePerPeriodValue); - command.setDigitsAfterDecimalValue(digitsAfterDecimalValue); - command.setRepaymentEveryValue(repaymentEveryValue); - command.setNumberOfRepaymentsValue(numberOfRepaymentsValue); - - command.setId(productId); EntityIdentifier entityIdentifier = this.loanProductWritePlatformService.updateLoanProduct(command); return Response.ok().entity(entityIdentifier).build(); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java index 40a15d9c1..ea132c76f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java @@ -1,6 +1,8 @@ package org.mifosng.platform.api; +import java.lang.reflect.Type; import java.util.Collection; +import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -27,6 +29,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + @Path("/offices") @Component @Scope("singleton") @@ -76,14 +81,42 @@ public class OfficeApiResource { } @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON }) - public Response createOffice(final OfficeCommand command) { - - LocalDate openingLocalDate = apiDataConversionService.convertFrom( - command.getOpeningDate(), "openingDate", - command.getDateFormat()); - command.setOpeningLocalDate(openingLocalDate); + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response createOffice(final String jsonRequestBody) { + + Gson gson = new Gson(); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gson.fromJson(jsonRequestBody, typeOfMap); + + String name = null; + if (requestMap.containsKey("name")) { + name = requestMap.get("name"); + } + + String externalId = null; + if (requestMap.containsKey("externalId")) { + externalId = requestMap.get("externalId"); + } + + Long parentId = null; + if (requestMap.containsKey("parentId")) { + parentId = Long.valueOf(requestMap.get("parentId")); + } + + String openingDate = null; + if (requestMap.containsKey("openingDate")) { + openingDate = requestMap.get("openingDate"); + } + + String dateFormat = null; + if (requestMap.containsKey("dateFormat")) { + dateFormat = requestMap.get("dateFormat"); + } + + LocalDate openingLocalDate = apiDataConversionService.convertFrom(openingDate, "openingDate", dateFormat); + + OfficeCommand command = new OfficeCommand(name, externalId, parentId, openingLocalDate); Long officeId = this.writePlatformService.createOffice(command); @@ -112,15 +145,41 @@ public class OfficeApiResource { @Path("{officeId}") @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Produces({ MediaType.APPLICATION_JSON }) - public Response updateOffice(@PathParam("officeId") final Long officeId, - final OfficeCommand command) { - - LocalDate openingLocalDate = apiDataConversionService.convertFrom( - command.getOpeningDate(), "openingDate", - command.getDateFormat()); - command.setOpeningLocalDate(openingLocalDate); - command.setId(officeId); - + public Response updateOffice(@PathParam("officeId") final Long officeId, final String jsonRequestBody) { + + Gson gson = new Gson(); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gson.fromJson(jsonRequestBody, typeOfMap); + + String name = null; + if (requestMap.containsKey("name")) { + name = requestMap.get("name"); + } + + String externalId = null; + if (requestMap.containsKey("externalId")) { + externalId = requestMap.get("externalId"); + } + + Long parentId = null; + if (requestMap.containsKey("parentId")) { + parentId = Long.valueOf(requestMap.get("parentId")); + } + + String openingDate = null; + if (requestMap.containsKey("openingDate")) { + openingDate = requestMap.get("openingDate"); + } + + String dateFormat = null; + if (requestMap.containsKey("dateFormat")) { + dateFormat = requestMap.get("dateFormat"); + } + + LocalDate openingLocalDate = apiDataConversionService.convertFrom(openingDate, "openingDate", dateFormat); + + OfficeCommand command = new OfficeCommand(officeId, name, externalId, parentId, openingLocalDate); + Long entityId = this.writePlatformService.updateOffice(command); return Response.ok().entity(new EntityIdentifier(entityId)).build(); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java index 156017516..37138d085 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java @@ -1,236 +1,189 @@ package org.mifosng.platform.api.commands; import java.math.BigDecimal; +import java.util.Set; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement +/** + * Immutable command for creating or updating details of a loan product. + */ public class LoanProductCommand { - private Long id; - private String name; - private String description; - private String externalId; + private final Long id; + private final String name; + private final String description; + private final String externalId; - private Long fundId; + private final Long fundId; - private String locale; - private String currencyCode; - private String digitsAfterDecimal; - private Integer digitsAfterDecimalValue; - private BigDecimal principalValue; - private String principal; - private BigDecimal inArrearsToleranceValue; - private String inArrearsTolerance; + private final String currencyCode; + private final Integer digitsAfterDecimal; + private final BigDecimal principal; + private final BigDecimal inArrearsTolerance; - private String numberOfRepayments; - private Integer numberOfRepaymentsValue; - private String repaymentEvery; - private Integer repaymentEveryValue; + private final Integer numberOfRepayments; + private final Integer repaymentEvery; - private String interestRatePerPeriod; - private BigDecimal interestRatePerPeriodValue; + private final BigDecimal interestRatePerPeriod; - private Integer repaymentFrequencyType; - private Integer interestRateFrequencyType; - private Integer amortizationType; - private Integer interestType; - private Integer interestCalculationPeriodType; + private final Integer repaymentFrequencyType; + private final Integer interestRateFrequencyType; + private final Integer amortizationType; + private final Integer interestType; + private final Integer interestCalculationPeriodType; - protected LoanProductCommand() { - // + private final Set modifiedParameters; + + public LoanProductCommand(Set modifiedParameters, final Long id, final String name, final String description, final String externalId, final Long fundId, + final String currencyCode, final Integer digitsAfterDecimal, + final BigDecimal principal, final BigDecimal inArrearsTolerance, final Integer numberOfRepayments, + final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, + final Integer repaymentFrequencyType, final Integer interestRateFrequencyType, final Integer amortizationType, final Integer interestType, + final Integer interestCalculationPeriodType) { + this.modifiedParameters = modifiedParameters; + this.id = id; + this.name = name; + this.description = description; + this.externalId = externalId; + this.fundId = fundId; + this.currencyCode = currencyCode; + this.digitsAfterDecimal = digitsAfterDecimal; + this.principal = principal; + this.inArrearsTolerance = inArrearsTolerance; + this.numberOfRepayments = numberOfRepayments; + this.repaymentEvery = repaymentEvery; + this.interestRatePerPeriod = interestRatePerPeriod; + this.repaymentFrequencyType = repaymentFrequencyType; + this.interestRateFrequencyType = interestRateFrequencyType; + this.amortizationType = amortizationType; + this.interestType = interestType; + this.interestCalculationPeriodType = interestCalculationPeriodType; } public Long getId() { return id; } - public void setId(Long id) { - this.id = id; - } - public String getName() { return name; } - public void setName(String name) { - this.name = name; - } - public String getDescription() { return description; } - public void setDescription(String description) { - this.description = description; - } - public String getExternalId() { return externalId; } - public void setExternalId(String externalId) { - this.externalId = externalId; - } - - public String getCurrencyCode() { - return currencyCode; - } - - public void setCurrencyCode(String currencyCode) { - this.currencyCode = currencyCode; - } - - public String getLocale() { - return locale; - } - - public void setLocale(String locale) { - this.locale = locale; - } - - public String getDigitsAfterDecimal() { - return digitsAfterDecimal; - } - - public void setDigitsAfterDecimal(String digitsAfterDecimal) { - this.digitsAfterDecimal = digitsAfterDecimal; - } - - public Integer getDigitsAfterDecimalValue() { - return digitsAfterDecimalValue; - } - - public void setDigitsAfterDecimalValue(Integer digitsAfterDecimalValue) { - this.digitsAfterDecimalValue = digitsAfterDecimalValue; - } - - public BigDecimal getPrincipalValue() { - return principalValue; - } - - public void setPrincipalValue(BigDecimal principalValue) { - this.principalValue = principalValue; - } - - public String getPrincipal() { - return principal; - } - - public void setPrincipal(String principal) { - this.principal = principal; - } - - public BigDecimal getInArrearsToleranceValue() { - return inArrearsToleranceValue; - } - - public void setInArrearsToleranceValue(BigDecimal inArrearsToleranceValue) { - this.inArrearsToleranceValue = inArrearsToleranceValue; - } - - public String getInArrearsTolerance() { - return inArrearsTolerance; - } - - public void setInArrearsTolerance(String inArrearsTolerance) { - this.inArrearsTolerance = inArrearsTolerance; - } - - public String getNumberOfRepayments() { - return numberOfRepayments; - } - - public void setNumberOfRepayments(String numberOfRepayments) { - this.numberOfRepayments = numberOfRepayments; - } - - public Integer getNumberOfRepaymentsValue() { - return numberOfRepaymentsValue; - } - - public void setNumberOfRepaymentsValue(Integer numberOfRepaymentsValue) { - this.numberOfRepaymentsValue = numberOfRepaymentsValue; - } - - public String getRepaymentEvery() { - return repaymentEvery; - } - - public void setRepaymentEvery(String repaymentEvery) { - this.repaymentEvery = repaymentEvery; - } - - public Integer getRepaymentEveryValue() { - return repaymentEveryValue; - } - - public void setRepaymentEveryValue(Integer repaymentEveryValue) { - this.repaymentEveryValue = repaymentEveryValue; - } - - public String getInterestRatePerPeriod() { - return interestRatePerPeriod; - } - - public void setInterestRatePerPeriod(String interestRatePerPeriod) { - this.interestRatePerPeriod = interestRatePerPeriod; - } - - public BigDecimal getInterestRatePerPeriodValue() { - return interestRatePerPeriodValue; - } - - public void setInterestRatePerPeriodValue(BigDecimal interestRatePerPeriodValue) { - this.interestRatePerPeriodValue = interestRatePerPeriodValue; - } - - public Integer getRepaymentFrequencyType() { - return repaymentFrequencyType; - } - - public void setRepaymentFrequencyType(Integer repaymentFrequencyType) { - this.repaymentFrequencyType = repaymentFrequencyType; - } - - public Integer getInterestRateFrequencyType() { - return interestRateFrequencyType; - } - - public void setInterestRateFrequencyType(Integer interestRateFrequencyType) { - this.interestRateFrequencyType = interestRateFrequencyType; - } - - public Integer getAmortizationType() { - return amortizationType; - } - - public void setAmortizationType(Integer amortizationType) { - this.amortizationType = amortizationType; - } - - public Integer getInterestType() { - return interestType; - } - - public void setInterestType(Integer interestType) { - this.interestType = interestType; - } - - public Integer getInterestCalculationPeriodType() { - return interestCalculationPeriodType; - } - - public void setInterestCalculationPeriodType( - Integer interestCalculationPeriodType) { - this.interestCalculationPeriodType = interestCalculationPeriodType; - } - public Long getFundId() { return fundId; } - public void setFundId(Long fundId) { - this.fundId = fundId; + public String getCurrencyCode() { + return currencyCode; + } + + public Integer getDigitsAfterDecimal() { + return digitsAfterDecimal; + } + + public BigDecimal getPrincipal() { + return principal; + } + + public BigDecimal getInArrearsTolerance() { + return inArrearsTolerance; + } + + public Integer getNumberOfRepayments() { + return numberOfRepayments; + } + + public Integer getRepaymentEvery() { + return repaymentEvery; + } + + public BigDecimal getInterestRatePerPeriod() { + return interestRatePerPeriod; + } + + public Integer getRepaymentFrequencyType() { + return repaymentFrequencyType; + } + + public Integer getInterestRateFrequencyType() { + return interestRateFrequencyType; + } + + public Integer getAmortizationType() { + return amortizationType; + } + + public Integer getInterestType() { + return interestType; + } + + public Integer getInterestCalculationPeriodType() { + return interestCalculationPeriodType; + } + + public boolean isNameChanged() { + return this.modifiedParameters.contains("name"); + } + + public boolean isDescriptionChanged() { + return this.modifiedParameters.contains("description"); + } + + public boolean isFundChanged() { + return this.modifiedParameters.contains("fundId"); + } + + public boolean isCurrencyCodeChanged() { + return this.modifiedParameters.contains("currencyCode"); + } + + public boolean isDigitsAfterDecimalChanged() { + return this.modifiedParameters.contains("digitsAfterDecimal"); + } + + public boolean isPrincipalChanged() { + return this.modifiedParameters.contains("principal"); + } + + public boolean isInArrearsToleranceChanged() { + return this.modifiedParameters.contains("inArrearsTolerance"); + } + + public boolean isNumberOfRepaymentsChanged() { + return this.modifiedParameters.contains("numberOfRepayments"); + } + + public boolean isRepaymentEveryChanged() { + return this.modifiedParameters.contains("repaymentEvery"); + } + + public boolean isRepaymentFrequencyTypeChanged() { + return this.modifiedParameters.contains("repaymentFrequencyType"); + } + + public boolean isAmortizationTypeChanged() { + return this.modifiedParameters.contains("amortizationType"); + } + + public boolean isInterestTypeChanged() { + return this.modifiedParameters.contains("interestType"); + } + + public boolean isInterestRatePerPeriodChanged() { + return this.modifiedParameters.contains("interestRatePerPeriod"); + } + + public boolean isInterestRateFrequencyTypeChanged() { + return this.modifiedParameters.contains("interestRateFrequencyType"); + } + + public boolean isInterestCalculationPeriodTypeChanged() { + return this.modifiedParameters.contains("interestCalculationPeriodType"); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonException.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonException.java new file mode 100644 index 000000000..765202d5b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonException.java @@ -0,0 +1,9 @@ +package org.mifosng.platform.api.errorhandling; + + +/** + * A {@link RuntimeException} that is thrown in the case where invalid JSON is sent in the body of the request to the platform api. + */ +public class InvalidJsonException extends RuntimeException { + // +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonExceptionMapper.java new file mode 100644 index 000000000..7456bf7eb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/InvalidJsonExceptionMapper.java @@ -0,0 +1,31 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.platform.api.data.ApiParameterError; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link InvalidJsonException} thrown by platform into a HTTP API friendly format. + */ +@Provider +@Component +@Scope("singleton") +public class InvalidJsonExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(final InvalidJsonException exception) { + + String globalisationMessageCode = "error.msg.invalid.request.body"; + String defaultUserMessage = "The JSON provided in the body of the request is invalid or missing."; + + ApiParameterError error = ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage); + + return Response.status(Status.BAD_REQUEST).entity(error).type(MediaType.APPLICATION_JSON).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/MalformedJsonExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/MalformedJsonExceptionMapper.java new file mode 100644 index 000000000..dcc1d39e1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/MalformedJsonExceptionMapper.java @@ -0,0 +1,33 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.platform.api.data.ApiParameterError; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import com.google.gson.stream.MalformedJsonException; + +/** + * An {@link ExceptionMapper} to map {@link MalformedJsonException} thrown by platform into a HTTP API friendly format. + */ +@Provider +@Component +@Scope("singleton") +public class MalformedJsonExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(final MalformedJsonException exception) { + + String globalisationMessageCode = "error.msg.invalid.request.body"; + String defaultUserMessage = "The JSON provided in the body of the request is invalid or missing."; + + ApiParameterError error = ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage); + + return Response.status(Status.BAD_REQUEST).entity(error).type(MediaType.APPLICATION_JSON).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterException.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterException.java new file mode 100644 index 000000000..a42af0a93 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterException.java @@ -0,0 +1,20 @@ +package org.mifosng.platform.api.errorhandling; + +import java.util.List; + +/** + * A {@link RuntimeException} that is thrown in the case where invalid + * parameters are sent in the body of the request to the platform api. + */ +public class UnsupportedParameterException extends RuntimeException { + + private final List unsupportedParameters; + + public UnsupportedParameterException(List unsupportedParameters) { + this.unsupportedParameters = unsupportedParameters; + } + + public List getUnsupportedParameters() { + return unsupportedParameters; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterExceptionMapper.java new file mode 100644 index 000000000..b2ba083ff --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/UnsupportedParameterExceptionMapper.java @@ -0,0 +1,44 @@ +package org.mifosng.platform.api.errorhandling; + +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.platform.api.data.ApiGlobalErrorResponse; +import org.mifosng.platform.api.data.ApiParameterError; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link UnsupportedParameterException} thrown by platform into a HTTP API friendly format. + */ +@Provider +@Component +@Scope("singleton") +public class UnsupportedParameterExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(final UnsupportedParameterException exception) { + + List errors = new ArrayList(); + + for (String parameterName : exception.getUnsupportedParameters()) { + StringBuilder validationErrorCode = new StringBuilder("error.msg.parameter.unsupported"); + StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameterName).append(" is not supported."); + ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), parameterName, parameterName); + + errors.add(error); + } + + ApiGlobalErrorResponse invalidParameterError = ApiGlobalErrorResponse.badClientRequest( + "validation.msg.validation.errors.exist", + "Validation errors exist.", errors); + + return Response.status(Status.BAD_REQUEST).entity(invalidParameterError).type(MediaType.APPLICATION_JSON).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java index 3e1ecacd6..1db2281f1 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionService.java @@ -4,6 +4,7 @@ import java.math.BigDecimal; import java.util.Locale; import org.joda.time.LocalDate; +import org.mifosng.platform.api.commands.LoanProductCommand; public interface ApiDataConversionService { @@ -18,4 +19,6 @@ public interface ApiDataConversionService { Locale localeFromString(String locale); Locale localeFrom(String languageCode, String courntryCode, String variantCode); + + LoanProductCommand convertJsonToCommand(Long productId, String jsonRequestBody); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index 555a17d97..8e42dea6f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -1,5 +1,6 @@ package org.mifosng.platform.api.infrastructure; +import java.lang.reflect.Type; import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -7,21 +8,36 @@ import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; +import org.mifosng.platform.api.commands.LoanProductCommand; import org.mifosng.platform.api.data.ApiParameterError; +import org.mifosng.platform.api.errorhandling.InvalidJsonException; +import org.mifosng.platform.api.errorhandling.UnsupportedParameterException; import org.mifosng.platform.exceptions.PlatformApiDataValidationException; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.format.number.NumberFormatter; import org.springframework.stereotype.Service; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + @Service public class ApiDataConversionServiceImpl implements ApiDataConversionService { + private final Gson gsonConverter; + + public ApiDataConversionServiceImpl() { + gsonConverter = new Gson(); + } + @Override public LocalDate convertFrom(String dateAsString, String parameterName, String dateFormat) { @@ -110,6 +126,18 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { @Override public BigDecimal convertFrom(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) { + if (clientApplicationLocale == null) { + + List dataValidationErrors = new ArrayList(); + String defaultMessage = new StringBuilder("The parameter '" + parameterName + "' requires a 'locale' parameter to be passed with it.").toString(); + ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage, parameterName); + dataValidationErrors.add(error); + + throw new PlatformApiDataValidationException( + "validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); + } + try { BigDecimal number = null; @@ -213,4 +241,111 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { return new Locale(languageCode.toLowerCase(), courntryCode.toUpperCase(), variantCode); } + + @Override + public LoanProductCommand convertJsonToCommand(final Long resourceIdentifier, final String json) { + + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); + + + Set supportedParams = new HashSet( + Arrays.asList("name", "description", "externalId", "fundId", "currencyCode", "digitsAfterDecimalValue", + "principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", + "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType") + ); + + checkForUnsupportedParameters(requestMap, supportedParams); + + Set modifiedParameters = new HashSet(); + + String name = extractStringParameter("name", requestMap, modifiedParameters); + String description = extractStringParameter("description", requestMap, modifiedParameters); + String externalId = extractStringParameter("externalId", requestMap, modifiedParameters); + Long fundId = extractLongParameter("fundId", requestMap, modifiedParameters); + String currencyCode = extractStringParameter("currencyCode", requestMap, modifiedParameters); + Integer digitsAfterDecimalValue = extractIntegerParameter("digitsAfterDecimal", requestMap, modifiedParameters); + BigDecimal principalValue = extractBigDecimalParameter("principal", requestMap, modifiedParameters); + BigDecimal inArrearsToleranceValue = extractBigDecimalParameter("inArrearsTolerance", requestMap, modifiedParameters); + BigDecimal interestRatePerPeriodValue = extractBigDecimalParameter("interestRatePerPeriod", requestMap, modifiedParameters); + + Integer repaymentEveryValue = extractIntegerParameter("repaymentEvery", requestMap, modifiedParameters); + Integer numberOfRepaymentsValue = extractIntegerParameter("numberOfRepayments", requestMap, modifiedParameters); + Integer repaymentFrequencyTypeValue = extractIntegerParameter("repaymentFrequencyType", requestMap, modifiedParameters); + + Integer interestRateFrequencyTypeValue = extractIntegerParameter("interestRateFrequencyType", requestMap, modifiedParameters); + Integer amortizationTypeValue = extractIntegerParameter("amortizationType", requestMap, modifiedParameters); + Integer interestTypeValue = extractIntegerParameter("interestType", requestMap, modifiedParameters); + Integer interestCalculationPeriodTypeValue = extractIntegerParameter("interestCalculationPeriodType", requestMap, modifiedParameters); + + return new LoanProductCommand(modifiedParameters, resourceIdentifier, name, description, externalId, fundId, + currencyCode, digitsAfterDecimalValue, principalValue, inArrearsToleranceValue, numberOfRepaymentsValue, repaymentEveryValue, interestRatePerPeriodValue, + repaymentFrequencyTypeValue, interestRateFrequencyTypeValue, amortizationTypeValue, interestTypeValue, interestCalculationPeriodTypeValue); + } + + private void checkForUnsupportedParameters(Map requestMap, Set supportedParams) { + List unsupportedParameterList = new ArrayList(); + for (String providedParameter : requestMap.keySet()) { + if (!supportedParams.contains(providedParameter)) { + unsupportedParameterList.add(providedParameter); + } + } + + if (!unsupportedParameterList.isEmpty()) { + throw new UnsupportedParameterException(unsupportedParameterList); + } + } + + private String extractStringParameter(final String paramName, final Map requestMap, final Set modifiedParameters) { + String paramValue = null; + if (requestMap.containsKey(paramName)) { + paramValue = requestMap.get(paramName); + modifiedParameters.add(paramName); + } + return paramValue; + } + + private Integer extractIntegerParameter(final String paramName, final Map requestMap, final Set modifiedParameters) { + Integer paramValue = null; + if (requestMap.containsKey(paramName)) { + paramValue = convertToInteger(requestMap.get(paramName), paramName, extractLocaleValue(requestMap)); + modifiedParameters.add(paramName); + } + return paramValue; + } + + private BigDecimal extractBigDecimalParameter(final String paramName, final Map requestMap, final Set modifiedParameters) { + BigDecimal paramValue = null; + if (requestMap.containsKey(paramName)) { + paramValue = convertFrom(requestMap.get(paramName), paramName, extractLocaleValue(requestMap)); + modifiedParameters.add(paramName); + } + return paramValue; + } + + private Long extractLongParameter(final String paramName, final Map requestMap, final Set modifiedParameters) { + Long paramValue = null; + if (requestMap.containsKey(paramName)) { + String valueAsString = requestMap.get(paramName); + if (StringUtils.isNotBlank(valueAsString)) { + paramValue = Long.valueOf(valueAsString); + } + modifiedParameters.add(paramName); + } + return paramValue; + } + + private Locale extractLocaleValue(Map requestMap) { + Locale clientApplicationLocale = null; + String locale = null; + if (requestMap.containsKey("locale")) { + locale = requestMap.get("locale"); + clientApplicationLocale = localeFromString(locale); + } + return clientApplicationLocale; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/fund/domain/Fund.java b/mifosng-provider/src/main/java/org/mifosng/platform/fund/domain/Fund.java index 39db24913..744cd34fa 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/fund/domain/Fund.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/fund/domain/Fund.java @@ -5,6 +5,7 @@ import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.UniqueConstraint; +import org.apache.commons.lang.StringUtils; import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.user.domain.AppUser; @@ -35,9 +36,13 @@ public class Fund extends AbstractAuditableCustom { this.name = fundName; } - public void update(FundCommand command) { - if (command.getName() != null) { + public void update(final FundCommand command) { + if (StringUtils.isNotBlank(command.getName())) { this.name = command.getName(); } + + if (command.getExternalId() != null) { + this.externalId = StringUtils.defaultIfEmpty(command.getExternalId(), null); + } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java index 18259aa9d..15f1cf01f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java @@ -133,13 +133,16 @@ public class LoanProduct extends AbstractAuditableCustom { } public void update(final LoanProductCommand command, final Fund fund) { - if (command.getName() != null) { + + if (command.isNameChanged()) { this.name = command.getName(); } - if (command.getDescription() != null) { + + if (command.isDescriptionChanged()) { this.description = command.getDescription(); } - if (fund != null) { + + if (command.isFundChanged()) { this.fund = fund; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java index 392ac60c0..6837713c8 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java @@ -171,39 +171,50 @@ public class LoanProductRelatedDetail implements return interestRebateAllowed; } - public void update(LoanProductCommand command) { + public void update(final LoanProductCommand command) { - if (command.getCurrencyCode() != null && command.getDigitsAfterDecimal() != null) { - this.currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimalValue()); + Integer digitsAfterDecimalChanged = this.currency.getDigitsAfterDecimal(); + if (command.isDigitsAfterDecimalChanged()) { + digitsAfterDecimalChanged = command.getDigitsAfterDecimal(); } - if (command.getPrincipal() != null) { - this.principal = command.getPrincipalValue(); + + String currencyCodeChanged = this.currency.getCode(); + if (command.isCurrencyCodeChanged()) { + currencyCodeChanged = command.getCurrencyCode(); } - if (command.getRepaymentEvery() != null) { - this.repayEvery = command.getRepaymentEveryValue(); + + if (command.isDigitsAfterDecimalChanged() || command.isCurrencyCodeChanged()) { + this.currency = new MonetaryCurrency(currencyCodeChanged, digitsAfterDecimalChanged); } - if (command.getRepaymentFrequencyType() != null) { + + if (command.isPrincipalChanged()) { + this.principal = command.getPrincipal(); + } + if (command.isRepaymentEveryChanged()) { + this.repayEvery = command.getRepaymentEvery(); + } + if (command.isRepaymentFrequencyTypeChanged()) { this.repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequencyType()); } - if (command.getNumberOfRepayments() != null) { - this.numberOfRepayments = command.getNumberOfRepaymentsValue(); + if (command.isNumberOfRepaymentsChanged()) { + this.numberOfRepayments = command.getNumberOfRepayments(); } - if (command.getAmortizationType() != null) { + if (command.isAmortizationTypeChanged()) { this.amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationType()); } - if (command.getInArrearsToleranceValue() != null) { - this.inArrearsTolerance = command.getInArrearsToleranceValue(); + if (command.isInArrearsToleranceChanged()) { + this.inArrearsTolerance = command.getInArrearsTolerance(); } - if (command.getInterestRatePerPeriod() != null) { - this.nominalInterestRatePerPeriod = command.getInterestRatePerPeriodValue(); + if (command.isInterestRatePerPeriodChanged()) { + this.nominalInterestRatePerPeriod = command.getInterestRatePerPeriod(); } - if (command.getInterestRateFrequencyType() != null) { + if (command.isInterestRateFrequencyTypeChanged()) { this.interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType()); } - if (command.getInterestType() != null) { + if (command.isInterestTypeChanged()) { this.interestMethod = InterestMethod.fromInt(command.getInterestType()); } - if (command.getInterestCalculationPeriodType() != null) { + if (command.isInterestCalculationPeriodTypeChanged()) { this.interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodType()); } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java index 735adc1fb..9580c8195 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java @@ -27,17 +27,17 @@ public class LoanProductCommandValidator { baseDataValidator.reset().parameter("description").value(command.getDescription()).ignoreIfNull().notExceedingLengthOf(500); baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100); baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).ignoreIfNull().notBlank(); - baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimalValue()).ignoreIfNull().notNull().inMinMaxRange(0, 6); + baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimal()).ignoreIfNull().notNull().inMinMaxRange(0, 6); - baseDataValidator.reset().parameter("principal").value(command.getPrincipalValue()).ignoreIfNull().notNull().positiveAmount(); - baseDataValidator.reset().parameter("inArrearsTolerance").value(command.getInArrearsToleranceValue()).ignoreIfNull().notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("principal").value(command.getPrincipal()).ignoreIfNull().notNull().positiveAmount(); + baseDataValidator.reset().parameter("inArrearsTolerance").value(command.getInArrearsTolerance()).ignoreIfNull().notNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter("repaymentFrequencyType").value(command.getRepaymentFrequencyType()).ignoreIfNull().notNull().inMinMaxRange(0, 3); - baseDataValidator.reset().parameter("repaymentEvery").value(command.getRepaymentEveryValue()).ignoreIfNull().notNull().greaterThanZero(); - baseDataValidator.reset().parameter("numberOfRepayments").value(command.getNumberOfRepaymentsValue()).ignoreIfNull().notNull().greaterThanZero(); + baseDataValidator.reset().parameter("repaymentEvery").value(command.getRepaymentEvery()).ignoreIfNull().notNull().greaterThanZero(); + baseDataValidator.reset().parameter("numberOfRepayments").value(command.getNumberOfRepayments()).ignoreIfNull().notNull().greaterThanZero(); - baseDataValidator.reset().parameter("interestRatePerPeriod").value(command.getInterestRatePerPeriodValue()).ignoreIfNull().notBlank(); + baseDataValidator.reset().parameter("interestRatePerPeriod").value(command.getInterestRatePerPeriod()).ignoreIfNull().notBlank(); baseDataValidator.reset().parameter("interestRateFrequencyType").value(command.getInterestRateFrequencyType()).ignoreIfNull().notNull().inMinMaxRange(0, 3); baseDataValidator.reset().parameter("amortizationType").value(command.getAmortizationType()).ignoreIfNull().notNull().inMinMaxRange(0, 1); @@ -63,16 +63,16 @@ public class LoanProductCommandValidator { baseDataValidator.reset().parameter("description").value(command.getDescription()).notExceedingLengthOf(500); baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).notExceedingLengthOf(100); baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).notBlank(); - baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimalValue()).notNull().inMinMaxRange(0, 6); + baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimal()).notNull().inMinMaxRange(0, 6); - baseDataValidator.reset().parameter("principal").value(command.getPrincipalValue()).notNull().positiveAmount(); - baseDataValidator.reset().parameter("inArrearsTolerance").value(command.getInArrearsToleranceValue()).notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter("principal").value(command.getPrincipal()).notNull().positiveAmount(); + baseDataValidator.reset().parameter("inArrearsTolerance").value(command.getInArrearsTolerance()).notNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter("repaymentFrequencyType").value(command.getRepaymentFrequencyType()).notNull().inMinMaxRange(0, 3); - baseDataValidator.reset().parameter("repaymentEvery").value(command.getRepaymentEveryValue()).notNull().greaterThanZero(); - baseDataValidator.reset().parameter("numberOfRepayments").value(command.getNumberOfRepaymentsValue()).notNull().greaterThanZero(); + baseDataValidator.reset().parameter("repaymentEvery").value(command.getRepaymentEvery()).notNull().greaterThanZero(); + baseDataValidator.reset().parameter("numberOfRepayments").value(command.getNumberOfRepayments()).notNull().greaterThanZero(); - baseDataValidator.reset().parameter("interestRatePerPeriod").value(command.getInterestRatePerPeriodValue()).notNull(); + baseDataValidator.reset().parameter("interestRatePerPeriod").value(command.getInterestRatePerPeriod()).notNull(); baseDataValidator.reset().parameter("interestRateFrequencyType").value(command.getInterestRateFrequencyType()).notNull().inMinMaxRange(0, 3); baseDataValidator.reset().parameter("amortizationType").value(command.getAmortizationType()).notNull().inMinMaxRange(0, 1); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java index 209f30935..1873c6ca4 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java @@ -59,18 +59,18 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro PeriodFrequencyType interestFrequencyType = PeriodFrequencyType .fromInt(command.getInterestRateFrequencyType()); - MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimalValue()); + MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal()); - BigDecimal annualInterestRate = this.aprCalculator.calculateFrom(interestFrequencyType, command.getInterestRatePerPeriodValue()); + BigDecimal annualInterestRate = this.aprCalculator.calculateFrom(interestFrequencyType, command.getInterestRatePerPeriod()); // associating fund with loan product at creation is optional for now. Fund fund = findFundByIdIfProvided(command.getFundId()); LoanProduct loanproduct = new LoanProduct(fund, command.getName(), command.getDescription(), - currency, command.getPrincipalValue(), - command.getInterestRatePerPeriodValue(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, - command.getRepaymentEveryValue(), repaymentFrequencyType, command.getNumberOfRepaymentsValue(), - amortizationMethod, command.getInArrearsToleranceValue()); + currency, command.getPrincipal(), + command.getInterestRatePerPeriod(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, + command.getRepaymentEvery(), repaymentFrequencyType, command.getNumberOfRepayments(), + amortizationMethod, command.getInArrearsTolerance()); this.loanProductRepository.save(loanproduct); @@ -90,7 +90,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro @Transactional @Override - public EntityIdentifier updateLoanProduct(LoanProductCommand command) { + public EntityIdentifier updateLoanProduct(final LoanProductCommand command) { this.context.authenticatedUser(); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/Office.java b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/Office.java index 4253991e8..31eef32f7 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/Office.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/organisation/domain/Office.java @@ -99,14 +99,13 @@ public class Office extends AbstractAuditableCustom { } - public void update(OfficeCommand command) { - // ignore nulls when updating - if (command.getName() != null) { + public void update(final OfficeCommand command) { + if (StringUtils.isNotBlank(command.getName())) { this.name = command.getName().trim(); } if (command.getExternalId() != null) { - this.externalId = command.getExternalId().trim(); + this.externalId = StringUtils.defaultIfEmpty(command.getExternalId().trim(), null); } if (command.getOpeningDate() != null) {