tidy up error handling and services around loan product.

This commit is contained in:
Keith Woodlock
2012-05-10 21:09:37 +01:00
parent 01adabab47
commit 6f4a47a1ca
33 changed files with 839 additions and 1087 deletions
@@ -1,219 +0,0 @@
package org.mifosng.platform;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.CreateLoanProductCommand;
import org.mifosng.data.command.LoanProductCommandData;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.platform.exceptions.NewDataValidationException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
public class LoanProductValidator {
public void validateForUpdate(UpdateLoanProductCommand command) {
List<ErrorResponse> dataValidationErrors = new ArrayList<ErrorResponse>();
Long productId = command.getId();
if (productId == null) {
ErrorResponse error = new ErrorResponse("validation.msg.product.id.cannot.be.empty", "id");
dataValidationErrors.add(error);
}
validateRemainingAttributes(command, dataValidationErrors);
}
public void validateForCreate(CreateLoanProductCommand command) {
List<ErrorResponse> dataValidationErrors = new ArrayList<ErrorResponse>();
validateRemainingAttributes(command, dataValidationErrors);
}
private void validateRemainingAttributes(LoanProductCommandData command, List<ErrorResponse> dataValidationErrors) {
if (StringUtils.isBlank(command.getName())) {
ErrorResponse error = new ErrorResponse("validation.msg.product.name.cannot.be.blank", "name");
dataValidationErrors.add(error);
}
if (command.getDescription() != null) {
if (command.getDescription().length() > 500) {
ErrorResponse error = new ErrorResponse("validation.msg.product.description.exceeds.max.length", "description");
dataValidationErrors.add(error);
}
}
if (StringUtils.isBlank(command.getCurrencyCode())) {
ErrorResponse error = new ErrorResponse("validation.msg.product.currency.cannot.be.empty", "currencyCode");
dataValidationErrors.add(error);
}
if (command.getDigitsAfterDecimal() != null) {
if (command.getDigitsAfterDecimal() < 0 || command.getDigitsAfterDecimal() > 6) {
ErrorResponse error = new ErrorResponse("validation.msg.product.currency.digitsAfterDecimal.must.be.between.zero.and.six.inclusive", "selectedDigitsAfterDecimal", command.getDigitsAfterDecimal());
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.currency.digitsAfterDecimal.cannot.be.empty", "selectedDigitsAfterDecimal");
dataValidationErrors.add(error);
}
if (command.getPrincipal() != null) {
if (command.getPrincipal().doubleValue() <= Double.valueOf("0.0")) {
ErrorResponse error = new ErrorResponse("validation.msg.product.principal.amount.must.be.greater.than.zero", "amount");
dataValidationErrors.add(error);
}
// TODO - check number of digits before decimal and after decimal
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.principal.amount.cannot.be.empty", "amount");
dataValidationErrors.add(error);
}
if (command.getRepaymentFrequency() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
ErrorResponse error = null;
switch (frequencyType) {
case DAYS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 365);
break;
case WEEKS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 52);
break;
case MONTHS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 12);
break;
case YEARS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 2);
break;
default:
error = new ErrorResponse("validation.msg.product.repayment.frequency.invalid", "selectedRepaymentFrequencyOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.repayment.frequency.cannot.be.empty", "selectedRepaymentFrequencyOption");
dataValidationErrors.add(error);
}
if (command.getNumberOfRepayments() != null) {
if (command.getNumberOfRepayments() < 1 || command.getNumberOfRepayments() > 100) {
ErrorResponse error = new ErrorResponse("validation.msg.product.number.of.repayments.is.out.of.range", "numberOfRepayments");
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.number.of.repayments.cannot.be.empty", "numberOfRepayments");
dataValidationErrors.add(error);
}
if (command.getAmortizationMethod() != null) {
AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
ErrorResponse error = null;
switch (amortizationMethod) {
case EQUAL_PRINCIPAL:
break;
case EQUAL_INSTALLMENTS:
break;
default:
error = new ErrorResponse("validation.msg.product.amortization.method.invalid", "selectedAmortizationMethodOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.amortization.method.cannot.be.empty", "selectedAmortizationMethodOption");
dataValidationErrors.add(error);
}
// refactor in validation of monetary value.
if (command.getInArrearsToleranceAmount() != null) {
if (command.getInArrearsToleranceAmount().doubleValue() < Double.valueOf("0.0")) {
ErrorResponse error = new ErrorResponse("validation.msg.product.arrears.tolerance.amount.cannot.be.negative", "inArrearsTolerance");
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.arrears.tolerance.amount.cannot.be.empty", "inArrearsTolerance");
dataValidationErrors.add(error);
}
if (command.getInterestRatePerPeriod()!= null) {
if (command.getInterestRatePerPeriod().doubleValue() < Double.valueOf("0.0")) {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.rate.per.period.amount.cannot.be.negative", "nominalInterestRate");
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.rate.per.period.amount.cannot.be.empty", "nominalInterestRate");
dataValidationErrors.add(error);
}
// interest rate period type
if (command.getInterestRateFrequencyMethod() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
ErrorResponse error = null;
switch (frequencyType) {
case DAYS:
break;
case WEEKS:
break;
case MONTHS:
break;
case YEARS:
break;
default:
error = new ErrorResponse("validation.msg.product.interest.frequency.invalid", "selectedInterestFrequencyOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.frequency.cannot.be.empty", "selectedInterestFrequencyOption");
dataValidationErrors.add(error);
}
if (command.getInterestMethod() != null) {
InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
ErrorResponse error = null;
switch (interestMethod) {
case DECLINING_BALANCE:
break;
case FLAT:
break;
default:
error = new ErrorResponse("validation.msg.product.interest.method.invalid", "selectedInterestMethodOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.method.cannot.be.empty", "selectedInterestMethodOption");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new NewDataValidationException(dataValidationErrors, "Validation errors exist.");
}
}
private ErrorResponse validateNumberExistsAndInRange(String errorCodePreFix, String fieldName, Integer value, int min, int max) {
ErrorResponse error = null;
if (value == null) {
error = new ErrorResponse(errorCodePreFix + ".cannot.be.empty", fieldName);
} else {
if (value < min || value > max) {
error = new ErrorResponse(errorCodePreFix + ".outside.allowed.range", fieldName);
}
}
return error;
}
}
@@ -33,7 +33,6 @@ import org.mifosng.data.ClientDataWithAccountsData;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.DerivedLoanData;
import org.mifosng.data.EnumOptionReadModel;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ExtraDatasetRow;
import org.mifosng.data.ExtraDatasets;
import org.mifosng.data.LoanAccountData;
@@ -3,8 +3,8 @@ package org.mifosng.platform;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.ChangePasswordCommand;
import org.mifosng.data.command.CreateLoanProductCommand;
import org.mifosng.data.command.EnrollClientCommand;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.NoteCommand;
@@ -14,7 +14,6 @@ import org.mifosng.data.command.SignupCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.data.command.UpdateOrganisationCurrencyCommand;
import org.mifosng.data.command.UpdateUsernamePasswordCommand;
import org.mifosng.data.command.UserCommand;
@@ -53,9 +52,9 @@ public interface WritePlatformService {
void updateOrganisationCurrencies(UpdateOrganisationCurrencyCommand command);
EntityIdentifier createLoanProduct(CreateLoanProductCommand command);
EntityIdentifier createLoanProduct(LoanProductCommand command);
EntityIdentifier updateLoanProduct(UpdateLoanProductCommand command);
EntityIdentifier updateLoanProduct(LoanProductCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DELETE_LOAN_THAT_IS_SUBMITTED_AND_NOT_APPROVED')")
EntityIdentifier deleteLoan(Long loanId);
@@ -24,8 +24,8 @@ import org.mifosng.data.ScheduledLoanInstallment;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.ChangePasswordCommand;
import org.mifosng.data.command.CreateLoanProductCommand;
import org.mifosng.data.command.EnrollClientCommand;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.NoteCommand;
@@ -35,7 +35,6 @@ import org.mifosng.data.command.SignupCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.data.command.UpdateOrganisationCurrencyCommand;
import org.mifosng.data.command.UpdateUsernamePasswordCommand;
import org.mifosng.data.command.UserCommand;
@@ -74,6 +73,7 @@ import org.mifosng.platform.loan.domain.LoanStatusRepository;
import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.LoanTransactionRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loanproduct.service.LoanProductCommandValidator;
import org.mifosng.platform.organisation.domain.Office;
import org.mifosng.platform.organisation.domain.OfficeRepository;
import org.mifosng.platform.organisation.domain.Organisation;
@@ -444,11 +444,11 @@ public class WritePlatformServiceJpaRepositoryImpl implements
@Transactional
@Override
public EntityIdentifier createLoanProduct(final CreateLoanProductCommand command) {
public EntityIdentifier createLoanProduct(final LoanProductCommand command) {
AppUser currentUser = extractAuthenticatedUser();
LoanProductValidator validator = new LoanProductValidator();
LoanProductCommandValidator validator = new LoanProductCommandValidator();
validator.validateForCreate(command);
// assemble LoanProduct from data
@@ -496,11 +496,11 @@ public class WritePlatformServiceJpaRepositoryImpl implements
@Transactional
@Override
public EntityIdentifier updateLoanProduct(UpdateLoanProductCommand command) {
public EntityIdentifier updateLoanProduct(LoanProductCommand command) {
AppUser currentUser = extractAuthenticatedUser();
LoanProductValidator validator = new LoanProductValidator();
LoanProductCommandValidator validator = new LoanProductCommandValidator();
validator.validateForUpdate(command);
LoanProduct product = this.loanProductRepository.findOne(productThatMatches(currentUser.getOrganisation(), command.getId()));
@@ -6,7 +6,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -25,8 +24,8 @@ import org.mifosng.data.ErrorResponseList;
import org.mifosng.data.reports.GenericResultset;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.exceptions.ApplicationDomainRuleException;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.mifosng.platform.exceptions.NewDataValidationException;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -48,7 +47,7 @@ public class FlexibleReportingApiResource {
@Path("flexireport")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response retrieveReport(@Context UriInfo uriInfo, @Context HttpServletResponse httpServletResponse) {
public Response retrieveReport(@Context UriInfo uriInfo) {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
String rptDB = queryParams.getFirst("MRP_rptDB");
@@ -1,4 +1,4 @@
package org.mifosng.platform.api;
package org.mifosng.platform.api.loanproduct;
import java.util.Collection;
@@ -9,21 +9,15 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponseList;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanProductList;
import org.mifosng.data.command.CreateLoanProductCommand;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.WritePlatformService;
import org.mifosng.platform.exceptions.ApplicationDomainRuleException;
import org.mifosng.platform.exceptions.NewDataValidationException;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
import org.mifosng.platform.loanproduct.service.LoanProductWritePlatformService;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.domain.AppUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
@@ -59,10 +53,10 @@ import org.springframework.stereotype.Component;
public class LoanProductApiResource {
@Autowired
private ReadPlatformService readPlatformService;
private LoanProductReadPlatformService loanProductReadPlatformService;
@Autowired
private WritePlatformService writePlatformService;
private LoanProductWritePlatformService loanProductWritePlatformService;
@Autowired
private AppUserRepository appUserRepository;
@@ -99,20 +93,13 @@ public class LoanProductApiResource {
@POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON})
public Response createLoanProduct(CreateLoanProductCommand command) {
public Response createLoanProduct(LoanProductCommand command) {
try {
// TODO - move CreateLoanProductCommand and UpdateLoanProductCommand into one.
hardcodeUserIntoSecurityContext();
EntityIdentifier entityIdentifier = this.writePlatformService.createLoanProduct(command);
hardcodeUserIntoSecurityContext();
EntityIdentifier entityIdentifier = this.loanProductWritePlatformService.createLoanProduct(command);
return Response.ok().entity(entityIdentifier).build();
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
return Response.ok().entity(entityIdentifier).build();
}
@GET
@@ -120,17 +107,11 @@ public class LoanProductApiResource {
@Produces({MediaType.APPLICATION_JSON})
public Response retrieveAllLoanProducts() {
try {
hardcodeUserIntoSecurityContext();
Collection<LoanProductData> products = this.readPlatformService.retrieveAllLoanProducts();
hardcodeUserIntoSecurityContext();
Collection<LoanProductData> products = this.loanProductReadPlatformService.retrieveAllLoanProducts();
return Response.ok().entity(new LoanProductList(products)).build();
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
return Response.ok().entity(new LoanProductList(products)).build();
}
/*
@@ -142,17 +123,11 @@ public class LoanProductApiResource {
@Produces({ MediaType.APPLICATION_JSON})
public Response retrieveNewLoanProductDetails() {
try {
hardcodeUserIntoSecurityContext();
LoanProductData loanProduct = this.readPlatformService.retrieveNewLoanProductDetails();
hardcodeUserIntoSecurityContext();
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails();
return Response.ok().entity(loanProduct).build();
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
return Response.ok().entity(loanProduct).build();
}
@GET
@@ -161,17 +136,11 @@ public class LoanProductApiResource {
@Produces({ MediaType.APPLICATION_JSON})
public Response retrieveLoanProductDetails(@PathParam("productId") final Long productId) {
try {
hardcodeUserIntoSecurityContext();
LoanProductData loanProduct = this.readPlatformService.retrieveLoanProduct(productId);
hardcodeUserIntoSecurityContext();
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
return Response.ok().entity(loanProduct).build();
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
return Response.ok().entity(loanProduct).build();
}
// example PUT request
@@ -201,18 +170,13 @@ public class LoanProductApiResource {
@Path("{productId}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON })
public Response updateLoanProduct(@PathParam("productId") final Long productId, final UpdateLoanProductCommand command) {
public Response updateLoanProduct(@PathParam("productId") final Long productId, final LoanProductCommand command) {
try {
hardcodeUserIntoSecurityContext();
EntityIdentifier entityIdentifier = this.writePlatformService.updateLoanProduct(command);
return Response.ok().entity(entityIdentifier).build();
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
hardcodeUserIntoSecurityContext();
command.setId(productId);
EntityIdentifier entityIdentifier = this.loanProductWritePlatformService.updateLoanProduct(command);
return Response.ok().entity(entityIdentifier).build();
}
}
@@ -16,8 +16,8 @@ import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.OfficeData;
import org.mifosng.data.OfficeList;
import org.mifosng.data.command.OfficeCommand;
import org.mifosng.platform.WritePlatformService;
import org.mifosng.platform.organisation.service.OfficeReadPlatformService;
import org.mifosng.platform.organisation.service.OfficeWritePlatformService;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.domain.AppUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
@@ -37,7 +37,7 @@ public class OfficeApiResource {
private OfficeReadPlatformService readPlatformService;
@Autowired
private WritePlatformService writePlatformService;
private OfficeWritePlatformService writePlatformService;
@Autowired
private AppUserRepository appUserRepository;
@@ -0,0 +1,12 @@
package org.mifosng.platform.currency.service;
import java.util.List;
import org.mifosng.data.CurrencyData;
public interface CurrencyReadPlatformService {
List<CurrencyData> retrieveAllowedCurrencies();
List<CurrencyData> retrieveAllPlatformCurrencies();
}
@@ -0,0 +1,68 @@
package org.mifosng.platform.currency.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.mifosng.data.CurrencyData;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CurrencyReadPlatformServiceImpl implements CurrencyReadPlatformService {
private final SimpleJdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
@Autowired
public CurrencyReadPlatformServiceImpl(final PlatformSecurityContext context, final DataSource dataSource) {
this.context = context;
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Override
public List<CurrencyData> retrieveAllowedCurrencies() {
AppUser currentUser = context.authenticatedUser();
String sql = "select c.code as code, c.name as name, c.decimal_places as decimalPlaces, c.display_symbol as displaySymbol, c.internationalized_name_code as nameCode from org_organisation_currency c where c.org_id = ?";
RowMapper<CurrencyData> rm = new CurrencyMapper();
return this.jdbcTemplate.query(sql, rm, new Object[] { currentUser
.getOrganisation().getId() });
}
@Override
public List<CurrencyData> retrieveAllPlatformCurrencies() {
String sql = "select c.code as code, c.name as name, c.decimal_places as decimalPlaces, c.display_symbol as displaySymbol, c.internationalized_name_code as nameCode from ref_currency c";
RowMapper<CurrencyData> rm = new CurrencyMapper();
return this.jdbcTemplate.query(sql, rm, new Object[] {});
}
private static final class CurrencyMapper implements RowMapper<CurrencyData> {
@Override
public CurrencyData mapRow(final ResultSet rs, final int rowNum)
throws SQLException {
String code = rs.getString("code");
String name = rs.getString("name");
int decimalPlaces = rs.getInt("decimalPlaces");
String displaySymbol = rs.getString("displaySymbol");
String nameCode = rs.getString("nameCode");
return new CurrencyData(code, name, decimalPlaces, displaySymbol,
nameCode);
}
}
}
@@ -10,7 +10,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
@@ -134,7 +134,7 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
return this.loanProductRelatedDetail.getAmortizationMethod();
}
public void update(UpdateLoanProductCommand command) {
public void update(LoanProductCommand command) {
this.name = command.getName();
this.description = command.getDescription();
this.loanProductRelatedDetail.update(command);
@@ -8,7 +8,7 @@ import javax.persistence.Embedded;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
@@ -173,7 +173,7 @@ public class LoanProductRelatedDetail implements
return interestRebateAllowed;
}
public void update(UpdateLoanProductCommand command) {
public void update(LoanProductCommand command) {
this.currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
this.principal = command.getPrincipal();
@@ -0,0 +1,18 @@
package org.mifosng.platform.loanproduct.service;
import java.util.List;
import org.mifosng.data.EnumOptionReadModel;
public interface LoanDropdownReadPlatformService {
List<EnumOptionReadModel> retrieveLoanAmortizationMethodOptions();
List<EnumOptionReadModel> retrieveLoanInterestMethodOptions();
List<EnumOptionReadModel> retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionReadModel> retrieveRepaymentFrequencyOptions();
List<EnumOptionReadModel> retrieveInterestFrequencyOptions();
}
@@ -0,0 +1,99 @@
package org.mifosng.platform.loanproduct.service;
import java.util.Arrays;
import java.util.List;
import javax.sql.DataSource;
import org.mifosng.data.EnumOptionReadModel;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
/*
* FIXME - remove hardcoded return of loan dropdown options
*/
@SuppressWarnings("unused")
@Service
public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlatformService {
private final PlatformSecurityContext context;
private final SimpleJdbcTemplate jdbcTemplate;
@Autowired
public LoanDropdownReadPlatformServiceImpl(final PlatformSecurityContext context, final DataSource dataSource) {
this.context = context;
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Override
public List<EnumOptionReadModel> retrieveLoanAmortizationMethodOptions() {
EnumOptionReadModel equalInstallments = new EnumOptionReadModel(
"Equal installments", AmortizationMethod.EQUAL_INSTALLMENTS
.getValue().longValue());
EnumOptionReadModel equalPrinciplePayments = new EnumOptionReadModel(
"Equal principle payments", AmortizationMethod.EQUAL_PRINCIPAL
.getValue().longValue());
List<EnumOptionReadModel> allowedAmortizationMethods = Arrays.asList(
equalInstallments, equalPrinciplePayments);
return allowedAmortizationMethods;
}
@Override
public List<EnumOptionReadModel> retrieveLoanInterestMethodOptions() {
EnumOptionReadModel interestRateCalculationMethod2 = new EnumOptionReadModel(
"Declining Balance", Long.valueOf(0));
EnumOptionReadModel interestRateCalculationMethod1 = new EnumOptionReadModel(
"Flat", Long.valueOf(1));
List<EnumOptionReadModel> allowedRepaymentScheduleCalculationMethods = Arrays
.asList(interestRateCalculationMethod2,
interestRateCalculationMethod1);
return allowedRepaymentScheduleCalculationMethods;
}
@Override
public List<EnumOptionReadModel> retrieveLoanInterestRateCalculatedInPeriodOptions() {
EnumOptionReadModel option1 = new EnumOptionReadModel("Daily",
Long.valueOf(0));
EnumOptionReadModel option2 = new EnumOptionReadModel(
"Same as repayment period", Long.valueOf(1));
List<EnumOptionReadModel> allowedOptions = Arrays.asList(option1,
option2);
return allowedOptions;
}
@Override
public List<EnumOptionReadModel> retrieveRepaymentFrequencyOptions() {
EnumOptionReadModel frequency1 = new EnumOptionReadModel("Days",
Long.valueOf(0));
EnumOptionReadModel frequency2 = new EnumOptionReadModel("Weeks",
Long.valueOf(1));
EnumOptionReadModel frequency3 = new EnumOptionReadModel("Months",
Long.valueOf(2));
List<EnumOptionReadModel> repaymentFrequencyOptions = Arrays.asList(
frequency1, frequency2, frequency3);
return repaymentFrequencyOptions;
}
@Override
public List<EnumOptionReadModel> retrieveInterestFrequencyOptions() {
// support for monthly and annual percentage rate (MPR) and (APR)
EnumOptionReadModel perMonth = new EnumOptionReadModel("Per month",
PeriodFrequencyType.MONTHS.getValue().longValue());
EnumOptionReadModel perYear = new EnumOptionReadModel("Per year",
PeriodFrequencyType.YEARS.getValue().longValue());
List<EnumOptionReadModel> repaymentFrequencyOptions = Arrays.asList(
perMonth, perYear);
return repaymentFrequencyOptions;
}
}
@@ -0,0 +1,230 @@
package org.mifosng.platform.loanproduct.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ApiParameterError;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.data.command.LoanProductCommandData;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
public class LoanProductCommandValidator {
public void validateForUpdate(LoanProductCommand command) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
Long productId = command.getId();
if (productId == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.id.cannot.be.empty", "The parameter id cannot be blank.", "id");
dataValidationErrors.add(error);
}
validateRemainingAttributes(command, dataValidationErrors);
}
public void validateForCreate(LoanProductCommand command) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
validateRemainingAttributes(command, dataValidationErrors);
}
private void validateRemainingAttributes(LoanProductCommandData command, List<ApiParameterError> dataValidationErrors) {
if (StringUtils.isBlank(command.getName())) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.name.cannot.be.blank", "The parameter name cannot be blank.", "name");
dataValidationErrors.add(error);
}
if (command.getDescription() != null) {
if (command.getDescription().length() > 500) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.description.exceeds.max.length", "The parameter description cannot exceed max length of {}.", "description", 500);
dataValidationErrors.add(error);
}
}
if (StringUtils.isBlank(command.getCurrencyCode())) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.cannot.be.empty", "The parameter currencyCode cannot be blank.", "currencyCode");
dataValidationErrors.add(error);
}
if (command.getDigitsAfterDecimal() != null) {
if (command.getDigitsAfterDecimal() < 0 || command.getDigitsAfterDecimal() > 6) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.digitsAfterDecimal.must.be.between.zero.and.six.inclusive", "The parameter digitsAfterDecimal must be between {0} and {1} inclusive.", "selectedDigitsAfterDecimal", 0, 6);
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.digitsAfterDecimal.cannot.be.empty", "The parameter digitsAfterDecimal cannot be blank.", "selectedDigitsAfterDecimal");
dataValidationErrors.add(error);
}
if (command.getPrincipal() != null) {
if (command.getPrincipal().doubleValue() <= Double.valueOf("0.0")) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.must.be.greater.than.zero", "The parameter amount must be greater than zero.", "amount");
dataValidationErrors.add(error);
}
// TODO - check number of digits before decimal and after decimal
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.cannot.be.empty", "The parameter amount cannot be blank.", "amount");
dataValidationErrors.add(error);
}
if (command.getRepaymentFrequency() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
ApiParameterError error = null;
switch (frequencyType) {
case DAYS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 365);
break;
case WEEKS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 52);
break;
case MONTHS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 12);
break;
case YEARS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 2);
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.repayment.frequency.invalid", "The parameter selectedRepaymentFrequencyOption is invalid.", "selectedRepaymentFrequencyOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.repayment.frequency.cannot.be.empty",
"The parameter selectedRepaymentFrequencyOption cannot be blank.", "selectedRepaymentFrequencyOption");
dataValidationErrors.add(error);
}
if (command.getNumberOfRepayments() != null) {
if (command.getNumberOfRepayments() < 1 || command.getNumberOfRepayments() > 100) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.number.of.repayments.is.out.of.range",
"The parameter numberOfRepayments must be between {} and {} inclusive.", "numberOfRepayments", 1, 100);
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.number.of.repayments.cannot.be.empty",
"The parameter numberOfRepayments cannot be empty.", "numberOfRepayments");
dataValidationErrors.add(error);
}
if (command.getAmortizationMethod() != null) {
AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
ApiParameterError error = null;
switch (amortizationMethod) {
case EQUAL_PRINCIPAL:
break;
case EQUAL_INSTALLMENTS:
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.amortization.method.invalid",
"The parameter selectedAmortizationMethodOption cannot be empty.", "selectedAmortizationMethodOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.amortization.method.cannot.be.empty",
"The parameter selectedAmortizationMethodOption cannot be empty.", "selectedAmortizationMethodOption");
dataValidationErrors.add(error);
}
// refactor in validation of monetary value.
if (command.getInArrearsToleranceAmount() != null) {
if (command.getInArrearsToleranceAmount().doubleValue() < Double.valueOf("0.0")) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.arrears.tolerance.amount.cannot.be.negative",
"The parameter inArrearsTolerance cannot be less than zero.", "inArrearsTolerance");
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.arrears.tolerance.amount.cannot.be.empty",
"The parameter inArrearsTolerance cannot be empty.", "inArrearsTolerance");
dataValidationErrors.add(error);
}
if (command.getInterestRatePerPeriod()!= null) {
if (command.getInterestRatePerPeriod().doubleValue() < Double.valueOf("0.0")) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.rate.per.period.amount.cannot.be.negative",
"The parameter nominalInterestRate cannot be less than zero.", "nominalInterestRate");
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.rate.per.period.amount.cannot.be.empty",
"The parameter nominalInterestRate cannot be empty.", "nominalInterestRate");
dataValidationErrors.add(error);
}
// interest rate period type
if (command.getInterestRateFrequencyMethod() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
ApiParameterError error = null;
switch (frequencyType) {
case DAYS:
break;
case WEEKS:
break;
case MONTHS:
break;
case YEARS:
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.interest.frequency.invalid",
"The parameter selectedInterestFrequencyOption is invalid.", "selectedInterestFrequencyOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.frequency.cannot.be.empty",
"The parameter selectedInterestFrequencyOption cannot be empty.", "selectedInterestFrequencyOption");
dataValidationErrors.add(error);
}
if (command.getInterestMethod() != null) {
InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
ApiParameterError error = null;
switch (interestMethod) {
case DECLINING_BALANCE:
break;
case FLAT:
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.interest.method.invalid",
"The parameter selectedInterestMethodOption is invalid.", "selectedInterestMethodOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.method.cannot.be.empty",
"The parameter selectedInterestMethodOption cannot be empty.", "selectedInterestMethodOption");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
private ApiParameterError validateNumberExistsAndInRange(String errorCodePreFix, String fieldName, Integer value, int min, int max) {
ApiParameterError error = null;
if (value == null) {
error = ApiParameterError.parameterError(errorCodePreFix + ".cannot.be.empty", "The parameter " + fieldName + " cannot be blank.", fieldName);
} else {
if (value < min || value > max) {
error = ApiParameterError.parameterError(errorCodePreFix + ".outside.allowed.range", "The parameter " + fieldName + " must be between {0} and {1} inclusive.", fieldName, min, max);
}
}
return error;
}
}
@@ -0,0 +1,14 @@
package org.mifosng.platform.loanproduct.service;
import java.util.Collection;
import org.mifosng.data.LoanProductData;
public interface LoanProductReadPlatformService {
Collection<LoanProductData> retrieveAllLoanProducts();
LoanProductData retrieveLoanProduct(Long productId);
LoanProductData retrieveNewLoanProductDetails();
}
@@ -0,0 +1,200 @@
package org.mifosng.platform.loanproduct.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import javax.sql.DataSource;
import org.joda.time.DateTime;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.EnumOptionReadModel;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.MoneyData;
import org.mifosng.platform.currency.service.CurrencyReadPlatformService;
import org.mifosng.platform.exceptions.PlatformResourceNotFoundException;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatformService {
private final PlatformSecurityContext context;
private final CurrencyReadPlatformService currencyReadPlatformService;
private final SimpleJdbcTemplate jdbcTemplate;
private final LoanDropdownReadPlatformService dropdownReadPlatformService;
@Autowired
public LoanProductReadPlatformServiceImpl(final PlatformSecurityContext context, final CurrencyReadPlatformService currencyReadPlatformService,
final LoanDropdownReadPlatformService dropdownReadPlatformService,
final DataSource dataSource) {
this.context = context;
this.currencyReadPlatformService = currencyReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Override
public LoanProductData retrieveLoanProduct(final Long loanProductId) {
try {
List<CurrencyData> allowedCurrencies = currencyReadPlatformService.retrieveAllowedCurrencies();
LoanProductMapper rm = new LoanProductMapper(allowedCurrencies);
String sql = "select " + rm.loanProductSchema() + " where lp.id = ?";
LoanProductData productData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanProductId });
populateProductDataWithDropdownOptions(productData);
return productData;
} catch (EmptyResultDataAccessException e) {
throw new PlatformResourceNotFoundException("error.msg.loanproduct.id.invalid", "Loan product with identifier {0} does not exist.", loanProductId);
}
}
@Override
public Collection<LoanProductData> retrieveAllLoanProducts() {
AppUser currentUser = this.context.authenticatedUser();
List<CurrencyData> allowedCurrencies = currencyReadPlatformService.retrieveAllowedCurrencies();
LoanProductMapper rm = new LoanProductMapper(allowedCurrencies);
String sql = "select " + rm.loanProductSchema() + " where lp.org_id = ?";
return this.jdbcTemplate.query(sql, rm, new Object[] { currentUser.getOrganisation().getId() });
}
@Override
public LoanProductData retrieveNewLoanProductDetails() {
LoanProductData productData = new LoanProductData();
populateProductDataWithDropdownOptions(productData);
if (productData.getPossibleCurrencies().size() >= 1) {
CurrencyData currency = productData.getPossibleCurrencies().get(0);
MoneyData zero = MoneyData.zero(currency);
productData.setPrincipalMoney(zero);
productData.setInArrearsTolerance(zero);
}
productData.setAmortizationMethod(Integer.valueOf(1));
productData.setInterestMethod(Integer.valueOf(0));
productData.setRepaymentPeriodFrequency(2);
productData.setInterestRatePeriod(2);
productData.setInterestRateCalculatedInPeriod(1);
productData.setRepaidEvery(1);
productData.setNumberOfRepayments(0);
return productData;
}
private static final class LoanProductMapper implements RowMapper<LoanProductData> {
private final List<CurrencyData> allowedCurrencies;
public LoanProductMapper(List<CurrencyData> allowedCurrencies) {
this.allowedCurrencies = allowedCurrencies;
}
public String loanProductSchema() {
return "lp.id as id, lp.name as name, lp.description as description, lp.flexible_repayment_schedule as isFlexible, lp.interest_rebate as isInterestRebateAllowed, "
+ "lp.principal_amount as principal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, "
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, "
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
+ "lp.created_date as createdon, lp.lastmodified_date as modifiedon "
+ " from portfolio_product_loan lp";
}
@Override
public LoanProductData mapRow(final ResultSet rs, final int rowNum)
throws SQLException {
Long id = rs.getLong("id");
String name = rs.getString("name");
String description = rs.getString("description");
boolean isFlexible = rs.getBoolean("isFlexible");
boolean isInterestRebateAllowed = rs
.getBoolean("isInterestRebateAllowed");
String currencyCode = rs.getString("currencyCode");
int currencyDigits = rs.getInt("currencyDigits");
CurrencyData currencyData = findCurrencyByCode(currencyCode, allowedCurrencies);
currencyData.setDecimalPlaces(currencyDigits);
BigDecimal principal = rs.getBigDecimal("principal");
BigDecimal tolerance = rs.getBigDecimal("tolerance");
MoneyData principalMoney = MoneyData.of(currencyData, principal);
MoneyData toleranceMoney = MoneyData.of(currencyData, tolerance);
BigDecimal interestRatePerPeriod = rs
.getBigDecimal("interestRatePerPeriod");
int interestRatePeriod = rs.getInt("interestRatePerPeriodFreq");
BigDecimal annualInterestRate = rs
.getBigDecimal("annualInterestRate");
int interestMethod = rs.getInt("interestMethod");
int interestCalculationInPeriodMethod = rs.getInt("interestCalculationInPeriodMethod");
int repaidEvery = rs.getInt("repaidEvery");
int repaymentFrequency = rs.getInt("repaymentPeriodFrequency");
int numberOfRepayments = rs.getInt("numberOfRepayments");
int amortizationMethod = rs.getInt("amortizationMethod");
DateTime createdOn = new DateTime(rs.getDate("createdOn"));
DateTime lastModifedOn = new DateTime(rs.getDate("modifiedon"));
return new LoanProductData(id, name, description, isFlexible,
isInterestRebateAllowed, principalMoney,
interestRatePerPeriod, interestRatePeriod,
annualInterestRate, interestMethod, interestCalculationInPeriodMethod,
repaidEvery,
repaymentFrequency, numberOfRepayments, amortizationMethod,
toleranceMoney, createdOn, lastModifedOn);
}
private CurrencyData findCurrencyByCode(String currencyCode, List<CurrencyData> allowedCurrencies) {
CurrencyData match = null;
for (CurrencyData currencyData : allowedCurrencies) {
if (currencyData.getCode().equalsIgnoreCase(currencyCode)) {
match = currencyData;
break;
}
}
return match;
}
}
private void populateProductDataWithDropdownOptions(LoanProductData productData) {
List<CurrencyData> possibleCurrencies = currencyReadPlatformService.retrieveAllowedCurrencies();
List<EnumOptionReadModel> possibleAmortizationOptions = dropdownReadPlatformService.retrieveLoanAmortizationMethodOptions();
List<EnumOptionReadModel> possibleInterestOptions = dropdownReadPlatformService.retrieveLoanInterestMethodOptions();
List<EnumOptionReadModel> possibleInterestRateCalculatedInPeriodOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionReadModel> repaymentFrequencyOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyOptions();
List<EnumOptionReadModel> interestFrequencyOptions = dropdownReadPlatformService.retrieveInterestFrequencyOptions();
productData.setPossibleCurrencies(possibleCurrencies);
productData.setPossibleAmortizationOptions(possibleAmortizationOptions);
productData.setPossibleInterestOptions(possibleInterestOptions);
productData.setPossibleInterestRateCalculatedInPeriodOptions(possibleInterestRateCalculatedInPeriodOptions);
productData.setRepaymentFrequencyOptions(repaymentFrequencyOptions);
productData.setInterestFrequencyOptions(interestFrequencyOptions);
}
}
@@ -0,0 +1,11 @@
package org.mifosng.platform.loanproduct.service;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.LoanProductCommand;
public interface LoanProductWritePlatformService {
EntityIdentifier createLoanProduct(LoanProductCommand command);
EntityIdentifier updateLoanProduct(LoanProductCommand command);
}
@@ -0,0 +1,107 @@
package org.mifosng.platform.loanproduct.service;
import static org.mifosng.platform.Specifications.productThatMatches;
import java.math.BigDecimal;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.exceptions.PlatformResourceNotFoundException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.LoanProduct;
import org.mifosng.platform.loan.domain.LoanProductRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
private final PlatformSecurityContext context;
private final LoanProductRepository loanProductRepository;
@Autowired
public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanProductRepository loanProductRepository) {
this.context = context;
this.loanProductRepository = loanProductRepository;
}
@Transactional
@Override
public EntityIdentifier createLoanProduct(final LoanProductCommand command) {
AppUser currentUser = this.context.authenticatedUser();
LoanProductCommandValidator validator = new LoanProductCommandValidator();
validator.validateForCreate(command);
// assemble LoanProduct from data
InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType
.fromInt(command.getRepaymentFrequency());
PeriodFrequencyType interestFrequencyType = PeriodFrequencyType
.fromInt(command.getInterestRateFrequencyMethod());
MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
// apr calculator
BigDecimal annualInterestRate = BigDecimal.ZERO;
switch (interestFrequencyType) {
case DAYS:
break;
case WEEKS:
annualInterestRate = command.getInterestRatePerPeriod().multiply(BigDecimal.valueOf(52));
break;
case MONTHS:
annualInterestRate = command.getInterestRatePerPeriod().multiply(BigDecimal.valueOf(12));
break;
case YEARS:
annualInterestRate = command.getInterestRatePerPeriod().multiply(BigDecimal.valueOf(1));
break;
case INVALID:
break;
}
LoanProduct loanproduct = new LoanProduct(currentUser.getOrganisation(), command.getName(), command.getDescription(),
currency, command.getPrincipal(),
command.getInterestRatePerPeriod(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod,
command.getRepaymentEvery(), repaymentFrequencyType, command.getNumberOfRepayments(), amortizationMethod, command.getInArrearsToleranceAmount(),
command.isFlexibleRepaymentSchedule(), command.isInterestRebateAllowed());
this.loanProductRepository.save(loanproduct);
return new EntityIdentifier(loanproduct.getId());
}
@Transactional
@Override
public EntityIdentifier updateLoanProduct(LoanProductCommand command) {
AppUser currentUser = this.context.authenticatedUser();
LoanProductCommandValidator validator = new LoanProductCommandValidator();
validator.validateForUpdate(command);
LoanProduct product = this.loanProductRepository.findOne(productThatMatches(currentUser.getOrganisation(), command.getId()));
if (product == null) {
throw new PlatformResourceNotFoundException("error.msg.loanproduct.id.invalid", "Loan product with identifier {0} does not exist.", command.getId());
}
product.update(command);
this.loanProductRepository.save(product);
return new EntityIdentifier(Long.valueOf(product.getId()));
}
}
@@ -7,6 +7,7 @@ import org.mifosng.platform.exceptions.NoAuthorizationException;
import org.mifosng.platform.exceptions.PlatformDataIntegrityException;
import org.mifosng.platform.organisation.domain.Office;
import org.mifosng.platform.organisation.domain.OfficeRepository;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,6 +12,7 @@ import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosng.data.OfficeData;
import org.mifosng.platform.exceptions.PlatformResourceNotFoundException;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
@@ -99,7 +100,7 @@ public class OfficeReadPlatformServiceImpl implements OfficeReadPlatformService
return selectedOffice;
} catch (EmptyResultDataAccessException e) {
throw new PlatformResourceNotFoundException("error.msg.office.id.invalid", "Office with identifier {0} does not exist", officeId);
throw new PlatformResourceNotFoundException("error.msg.office.id.invalid", "Office with identifier {0} does not exist.", officeId);
}
}
}
@@ -19,13 +19,12 @@ import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ErrorResponseList;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanProductList;
import org.mifosng.data.command.CreateLoanProductCommand;
import org.mifosng.data.command.UpdateLoanProductCommand;
import org.mifosng.data.command.LoanProductCommand;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.WritePlatformService;
import org.mifosng.platform.exceptions.ApplicationDomainRuleException;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.mifosng.platform.exceptions.NewDataValidationException;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.security.access.AccessDeniedException;
@@ -58,7 +57,7 @@ public class LoanProductResource {
@Path("new")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response createLoanProduct(final CreateLoanProductCommand command) {
public Response createLoanProduct(final LoanProductCommand command) {
try {
EntityIdentifier entityIdentifier = this.writePlatformService.createLoanProduct(command);
@@ -81,7 +80,7 @@ public class LoanProductResource {
@Path("update")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response updateLoanProduct(final UpdateLoanProductCommand command) {
public Response updateLoanProduct(final LoanProductCommand command) {
try {
EntityIdentifier entityIdentifier = this.writePlatformService.updateLoanProduct(command);
@@ -1,4 +1,4 @@
package org.mifosng.platform.organisation.service;
package org.mifosng.platform.security;
import org.mifosng.platform.user.domain.AppUser;
@@ -1,4 +1,4 @@
package org.mifosng.platform.organisation.service;
package org.mifosng.platform.security;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.mifosng.platform.user.domain.AppUser;