diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 7a5315acf..380b4852c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -167,7 +167,7 @@ public class CommandWrapper { public boolean isLoanResource() { return this.resource.equalsIgnoreCase("LOANS"); } - + public boolean isApproveLoanApplication() { return this.actionName.equalsIgnoreCase("APPROVE") && this.entityName.equalsIgnoreCase("LOAN"); } @@ -191,4 +191,28 @@ public class CommandWrapper { public boolean isUndoDisbursementOfLoan() { return this.actionName.equalsIgnoreCase("DISBURSALUNDO") && this.entityName.equalsIgnoreCase("LOAN"); } + + public boolean isLoanRepayment() { + return this.actionName.equalsIgnoreCase("REPAYMENT") && this.entityName.equalsIgnoreCase("LOAN"); + } + + public boolean isLoanRepaymentAdjustment() { + return this.actionName.equalsIgnoreCase("ADJUST") && this.entityName.equalsIgnoreCase("LOAN"); + } + + public boolean isWaiveInterestPortionOnLoan() { + return this.actionName.equalsIgnoreCase("WAIVEINTERESTPORTION") && this.entityName.equalsIgnoreCase("LOAN"); + } + + public boolean isLoanWriteOff() { + return this.actionName.equalsIgnoreCase("WRITEOFF") && this.entityName.equalsIgnoreCase("LOAN"); + } + + public boolean isCloseLoanAsObligationsMet() { + return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("LOAN"); + } + + public boolean isCloseLoanAsRescheduled() { + return this.actionName.equalsIgnoreCase("CLOSEASRESCHEDULED") && this.entityName.equalsIgnoreCase("LOAN"); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index d662f620b..26fe37116 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -224,6 +224,18 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = applicationContext.getBean("disburseLoanCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUndoDisbursementOfLoan()) { handler = applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isLoanRepayment()) { + handler = applicationContext.getBean("loanRepaymentCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isLoanRepaymentAdjustment()) { + handler = applicationContext.getBean("loanRepaymentAdjustmentCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isWaiveInterestPortionOnLoan()) { + handler = applicationContext.getBean("waiveInterestPortionOnLoanCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isLoanWriteOff()) { + handler = applicationContext.getBean("writeOffLoanCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isCloseLoanAsObligationsMet()) { + handler = applicationContext.getBean("closeLoanCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isCloseLoanAsRescheduled()) { + handler = applicationContext.getBean("closeLoanAsRescheduledCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isCreate()) { handler = applicationContext.getBean("createLoanApplicationCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUpdate()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java index 365f8cc19..83d6fd9b7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/GoogleGsonPortfolioApiJsonSerializerService.java @@ -16,7 +16,6 @@ import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionDat import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; import org.mifosplatform.portfolio.group.data.GroupData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; -import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; import org.mifosplatform.portfolio.loanaccount.gaurantor.data.GuarantorData; import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData; import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData; @@ -42,7 +41,7 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi "createdOn", "lastModifedOn", "locale", "name", "description", "currencyCode", "digitsAfterDecimal", "interstRate", "minInterestRate", "maxInterestRate", "savingsDepositAmount", "savingProductType", "tenureType", "tenure", "frequency", "interestType", "interestCalculationMethod", "minimumBalanceForWithdrawal", "isPartialDepositAllowed", "isLockinPeriodAllowed", - "lockinPeriod", "lockinPeriodType", "currencyOptions", "savingsProductTypeOptions", "tenureTypeOptions","depositEvery", + "lockinPeriod", "lockinPeriodType", "currencyOptions", "savingsProductTypeOptions", "tenureTypeOptions", "depositEvery", "savingFrequencyOptions", "savingsInterestTypeOptions", "lockinPeriodTypeOptions", "interestCalculationOptions")); private static final Set SAVINGS_DEPOSIT_PRODUCT_DATA_PARAMETERS = new HashSet(Arrays.asList("currencyOptions", @@ -78,9 +77,6 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi private static final Set GROUP_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "officeId", "name", "externalId", "clientMembers", "allowedClients", "allowedOffices")); - private static final Set LOAN_TRANSACTION_NEW_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "type", "date", - "currency", "amount")); - private static final Set LOAN_REASSIGNMENT_DATA_PARAMETERS = new HashSet(Arrays.asList("officeId", "fromLoanOfficerId", "assignmentDate", "officeOptions", "loanOfficerOptions", "accountSummaryCollection")); @@ -206,14 +202,6 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi return helper.serializedJsonFrom(gsonDeserializer, group); } - @Override - public String serializeLoanTransactionDataToJson(final boolean prettyPrint, final Set responseParameters, - final LoanTransactionData transaction) { - final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy( - LOAN_TRANSACTION_NEW_DATA_PARAMETERS, prettyPrint, responseParameters); - return helper.serializedJsonFrom(gsonDeserializer, transaction); - } - @Override public String serializeLoanReassignmentDataToJson(boolean prettyPrint, Set responseParameters, BulkTransferLoanOfficerData loanReassignmentData) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java index 86d8e5330..efb39b291 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionService.java @@ -4,9 +4,7 @@ import org.mifosplatform.organisation.staff.command.BulkTransferLoanOfficerComma import org.mifosplatform.portfolio.client.command.NoteCommand; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.group.command.GroupCommand; -import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; -import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand; import org.mifosplatform.portfolio.loanaccount.gaurantor.command.GuarantorCommand; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand; import org.mifosplatform.portfolio.savingsaccountproduct.command.SavingProductCommand; @@ -29,10 +27,6 @@ public interface PortfolioApiDataConversionService { LoanChargeCommand convertJsonToLoanChargeCommand(Long loanChargeId, Long loanId, String json); - LoanTransactionCommand convertJsonToLoanTransactionCommand(Long resourceIdentifier, String json); - - AdjustLoanTransactionCommand convertJsonToAdjustLoanTransactionCommand(Long loanId, Long transactionId, String json); - NoteCommand convertJsonToNoteCommand(Long resourceIdentifier, Long clientId, String json); DepositAccountCommand convertJsonToDepositAccountCommand(Long resourceIdentifier, String json); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java index d711c41ab..a0fb54517 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiDataConversionServiceImpl.java @@ -27,9 +27,7 @@ import org.mifosplatform.portfolio.client.command.NoteCommand; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.serialization.ClientCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.group.command.GroupCommand; -import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; -import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand; import org.mifosplatform.portfolio.loanaccount.gaurantor.command.GuarantorCommand; import org.mifosplatform.portfolio.savingsaccount.command.SavingAccountCommand; import org.mifosplatform.portfolio.savingsaccountproduct.command.SavingProductCommand; @@ -203,50 +201,6 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo submittedOnDate); } - @Override - public LoanTransactionCommand convertJsonToLoanTransactionCommand(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("transactionDate", "transactionAmount", "note", "dateFormat", - "locale")); - - checkForUnsupportedParameters(requestMap, supportedParams); - - Set modifiedParameters = new HashSet(); - - LocalDate transactionDate = extractLocalDateParameter("transactionDate", requestMap, modifiedParameters); - BigDecimal transactionAmount = extractBigDecimalParameter("transactionAmount", requestMap, modifiedParameters); - String note = extractStringParameter("note", requestMap, modifiedParameters); - - return new LoanTransactionCommand(resourceIdentifier, transactionDate, transactionAmount, note); - } - - @Override - public AdjustLoanTransactionCommand convertJsonToAdjustLoanTransactionCommand(final Long loanId, final Long transactionId, - 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("transactionDate", "transactionAmount", "note", "dateFormat", - "locale")); - - checkForUnsupportedParameters(requestMap, supportedParams); - - Set modifiedParameters = new HashSet(); - - LocalDate transactionDate = extractLocalDateParameter("transactionDate", requestMap, modifiedParameters); - BigDecimal transactionAmount = extractBigDecimalParameter("transactionAmount", requestMap, modifiedParameters); - String note = extractStringParameter("note", requestMap, modifiedParameters); - - return new AdjustLoanTransactionCommand(loanId, transactionId, transactionDate, note, transactionAmount); - } - @Override public NoteCommand convertJsonToNoteCommand(final Long noteId, final Long clientId, final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } @@ -741,7 +695,7 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo Set supportedParams = new HashSet(Arrays.asList("locale", "name", "description", "currencyCode", "digitsAfterDecimal", "interestRate", "minInterestRate", "maxInterestRate", "savingsDepositAmount", "savingProductType", "tenureType", "tenure", "frequency", "interestType", "interestCalculationMethod", "minimumBalanceForWithdrawal", - "isPartialDepositAllowed", "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType","depositEvery")); + "isPartialDepositAllowed", "isLockinPeriodAllowed", "lockinPeriod", "lockinPeriodType", "depositEvery")); checkForUnsupportedParameters(requestMap, supportedParams); Set modifiedParameters = new HashSet(); @@ -767,9 +721,9 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo Integer lockinPeriodType = extractIntegerParameter("lockinPeriodType", requestMap, modifiedParameters); return new SavingProductCommand(modifiedParameters, resourceIdentifier, name, description, currencyCode, digitsAfterDecimalValue, - interestRate, minInterestRate, maxInterestRate, savingsDepositAmount, depositEvery, savingProductType, tenureType, tenure, frequency, - interestType, interestCalculationMethod, minimumBalanceForWithdrawal, isPartialDepositAllowed, isLockinPeriodAllowed, - lockinPeriod, lockinPeriodType); + interestRate, minInterestRate, maxInterestRate, savingsDepositAmount, depositEvery, savingProductType, tenureType, tenure, + frequency, interestType, interestCalculationMethod, minimumBalanceForWithdrawal, isPartialDepositAllowed, + isLockinPeriodAllowed, lockinPeriod, lockinPeriodType); } @Override diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiJsonSerializerService.java index 3e8428326..8107f8272 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/PortfolioApiJsonSerializerService.java @@ -13,7 +13,6 @@ import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionDat import org.mifosplatform.portfolio.group.data.GroupAccountSummaryCollectionData; import org.mifosplatform.portfolio.group.data.GroupData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; -import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; import org.mifosplatform.portfolio.loanaccount.gaurantor.data.GuarantorData; import org.mifosplatform.portfolio.savingsaccount.data.SavingAccountData; import org.mifosplatform.portfolio.savingsaccount.data.SavingScheduleData; @@ -51,8 +50,6 @@ public interface PortfolioApiJsonSerializerService { String serializeGroupDataToJson(boolean prettyPrint, Set responseParameters, GroupData group); - String serializeLoanTransactionDataToJson(boolean prettyPrint, Set responseParameters, LoanTransactionData newTransactionData); - String serializeLoanReassignmentDataToJson(boolean prettyPrint, Set responseParameters, BulkTransferLoanOfficerData loanReassignmentData); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanChargesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanChargesApiResource.java new file mode 100644 index 000000000..778cbcf5e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanChargesApiResource.java @@ -0,0 +1,170 @@ +package org.mifosplatform.portfolio.loanaccount.api; + +import java.util.Collection; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; +import org.mifosplatform.infrastructure.core.api.PortfolioApiDataConversionService; +import org.mifosplatform.infrastructure.core.api.PortfolioApiJsonSerializerService; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; +import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; +import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData; +import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/loans/{loanId}/charges") +@Component +@Scope("singleton") +public class LoanChargesApiResource { + + private final PlatformSecurityContext context; + private final LoanWritePlatformService loanWritePlatformService; + private final ChargeReadPlatformService chargeReadPlatformService; + private final PortfolioApiJsonSerializerService apiJsonSerializerService; + private final PortfolioApiDataConversionService apiDataConversionService; + private final DefaultToApiJsonSerializer toApiJsonSerializer; +// private final ApiRequestParameterHelper apiRequestParameterHelper; +// private final FromJsonHelper fromJsonHelper; +// private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + public LoanChargesApiResource(final PlatformSecurityContext context, final LoanWritePlatformService loanWritePlatformService, + final ChargeReadPlatformService chargeReadPlatformService, + final DefaultToApiJsonSerializer toApiJsonSerializer, +// final ApiRequestParameterHelper apiRequestParameterHelper, + final PortfolioApiJsonSerializerService apiJsonSerializerService, + final PortfolioApiDataConversionService apiDataConversionService + // , final FromJsonHelper fromJsonHelper, + // final PortfolioCommandSourceWritePlatformService + // commandsSourceWritePlatformService + ) { + this.context = context; + this.loanWritePlatformService = loanWritePlatformService; + this.chargeReadPlatformService = chargeReadPlatformService; + this.toApiJsonSerializer = toApiJsonSerializer; +// this.apiRequestParameterHelper = apiRequestParameterHelper; + this.apiJsonSerializerService = apiJsonSerializerService; + this.apiDataConversionService = apiDataConversionService; +// this.fromJsonHelper = fromJsonHelper; +// this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public Response addLoanCharge(@PathParam("loanId") final Long loanId, final String jsonRequestBody) { + + final LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(null, loanId, jsonRequestBody); + + final EntityIdentifier identifier = this.loanWritePlatformService.addLoanCharge(command); + + return Response.ok().entity(identifier).build(); + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveNewLoanChargeDetails(@Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission("LOAN"); + + final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); + final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + + final boolean feeChargesOnly = false; + final Collection chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly); + final LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions); + + return this.apiJsonSerializerService.serializeLoanChargeDataToJson(prettyPrint, responseParameters, loanChargeTemplate); + } + + @GET + @Path("{chargeId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission("LOAN"); + + final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); + final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + + final LoanChargeData loanCharge = this.chargeReadPlatformService.retrieveLoanChargeDetails(loanChargeId, loanId); + + return this.apiJsonSerializerService.serializeLoanChargeDataToJson(prettyPrint, responseParameters, loanCharge); + } + + @PUT + @Path("{chargeId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String updateLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId, + final String jsonRequestBody) { + + final LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(loanChargeId, loanId, + jsonRequestBody); + + final EntityIdentifier identifier = this.loanWritePlatformService.updateLoanCharge(command); + + return this.toApiJsonSerializer.serialize(identifier); + } + + @POST + @Path("{chargeId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String waiveLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId, + @QueryParam("command") final String commandParam) { + + final LoanChargeCommand command = LoanChargeCommand.forWaiver(loanChargeId, loanId); + + String json = ""; + if (is(commandParam, "waive")) { + final EntityIdentifier identifier = this.loanWritePlatformService.waiveLoanCharge(command); + json = this.toApiJsonSerializer.serialize(identifier); + } else { + throw new UnrecognizedQueryParamException("command", commandParam); + } + + return json; + } + + @DELETE + @Path("{chargeId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId) { + + final EntityIdentifier identifier = this.loanWritePlatformService.deleteLoanCharge(loanId, loanChargeId); + return this.toApiJsonSerializer.serialize(identifier); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java new file mode 100644 index 000000000..d457d7290 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoanTransactionsApiResource.java @@ -0,0 +1,150 @@ +package org.mifosplatform.portfolio.loanaccount.api; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; +import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +// FIXME: - undo disbursal fails when transactions have notes against them +// FIXME: - repayments before disbursement date allowed + +@Path("/loans/{loanId}/transactions") +@Component +@Scope("singleton") +public class LoanTransactionsApiResource { + + private final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "type", "date", "currency", "amount")); + + private final String resourceNameForPermissions = "LOAN"; + + private final PlatformSecurityContext context; + private final LoanReadPlatformService loanReadPlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + public LoanTransactionsApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + this.context = context; + this.loanReadPlatformService = loanReadPlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + + @GET + @Path("template") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTransactionTemplate(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + + LoanTransactionData transactionData = null; + if (is(commandParam, "repayment")) { + transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(loanId); + } else if (is(commandParam, "waiveinterest")) { + transactionData = this.loanReadPlatformService.retrieveWaiveInterestDetails(loanId); + } else if (is(commandParam, "writeoff")) { + transactionData = this.loanReadPlatformService.retrieveNewClosureDetails(); + } else if (is(commandParam, "close-rescheduled")) { + transactionData = this.loanReadPlatformService.retrieveNewClosureDetails(); + } else if (is(commandParam, "close")) { + transactionData = this.loanReadPlatformService.retrieveNewClosureDetails(); + } else { + throw new UnrecognizedQueryParamException("command", commandParam); + } + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transactionData, RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{transactionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveTransaction(@PathParam("loanId") final Long loanId, @PathParam("transactionId") final Long transactionId, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); + + final LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransaction(loanId, transactionId); + + final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, transactionData, RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String executeLoanTransaction(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam, + final String apiRequestBodyAsJson) { + + EntityIdentifier result = null; + if (is(commandParam, "repayment")) { + result = this.commandsSourceWritePlatformService.logCommandSource("REPAYMENT", "LOAN", "N/A", "loans", loanId, "transactions", + null, apiRequestBodyAsJson); + } else if (is(commandParam, "waiveinterest")) { + result = this.commandsSourceWritePlatformService.logCommandSource("WAIVEINTERESTPORTION", "LOAN", "N/A", "loans", loanId, + "transactions", null, apiRequestBodyAsJson); + } else if (is(commandParam, "writeoff")) { + result = this.commandsSourceWritePlatformService.logCommandSource("WRITEOFF", "LOAN", "N/A", "loans", loanId, "transactions", + null, apiRequestBodyAsJson); + } else if (is(commandParam, "close-rescheduled")) { + result = this.commandsSourceWritePlatformService.logCommandSource("CLOSEASRESCHEDULED", "LOAN", "N/A", "loans", loanId, + "transactions", null, apiRequestBodyAsJson); + } else if (is(commandParam, "close")) { + result = this.commandsSourceWritePlatformService + .logCommandSource("CLOSE", "LOAN", "N/A", "loans", loanId, apiRequestBodyAsJson); + } + + if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam); } + + return this.toApiJsonSerializer.serialize(result); + } + + @POST + @Path("{transactionId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String adjustLoanTransaction(@PathParam("loanId") final Long loanId, @PathParam("transactionId") final Long transactionId, + final String apiRequestBodyAsJson) { + + final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("ADJUST", "LOAN", "N/A", "loans", loanId, + "transactions", transactionId, apiRequestBodyAsJson); + + return this.toApiJsonSerializer.serialize(result); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index 521974cfc..5587c6aae 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -47,9 +47,6 @@ import org.mifosplatform.portfolio.charge.data.ChargeData; import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; import org.mifosplatform.portfolio.fund.data.FundData; import org.mifosplatform.portfolio.fund.service.FundReadPlatformService; -import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommand; -import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; -import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData; import org.mifosplatform.portfolio.loanaccount.data.LoanBasicDetailsData; @@ -412,188 +409,6 @@ public class LoansApiResource { return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); } - @POST - @Path("{loanId}/transactions") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public Response executeLoanTransaction(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam, - final String jsonRequestBody) { - - final LoanTransactionCommand command = this.apiDataConversionService.convertJsonToLoanTransactionCommand(loanId, jsonRequestBody); - - EntityIdentifier identifier = null; - if (is(commandParam, "repayment")) { - identifier = this.loanWritePlatformService.makeLoanRepayment(command); - } else if (is(commandParam, "waiveinterest")) { - identifier = this.loanWritePlatformService.waiveInterestOnLoan(command); - } else if (is(commandParam, "writeoff")) { - identifier = this.loanWritePlatformService.writeOff(command); - } else if (is(commandParam, "close-rescheduled")) { - identifier = this.loanWritePlatformService.closeAsRescheduled(command); - } else if (is(commandParam, "close")) { - identifier = this.loanWritePlatformService.closeLoan(command); - } - - if (identifier == null) { throw new UnrecognizedQueryParamException("command", commandParam); } - - return Response.ok().entity(identifier).build(); - } - - @GET - @Path("{loanId}/transactions/template") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveNewRepaymentDetails(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam, - @Context final UriInfo uriInfo) { - - context.authenticatedUser().validateHasReadPermission("LOAN"); - - final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); - final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); - - LoanTransactionData transactionData = null; - if (is(commandParam, "repayment")) { - transactionData = this.loanReadPlatformService.retrieveNewLoanRepaymentDetails(loanId); - } else if (is(commandParam, "waiveinterest")) { - transactionData = this.loanReadPlatformService.retrieveNewLoanWaiveInterestDetails(loanId); - } else if (is(commandParam, "writeoff")) { - transactionData = this.loanReadPlatformService.retrieveNewClosureDetails(); - } else if (is(commandParam, "close-rescheduled")) { - transactionData = this.loanReadPlatformService.retrieveNewClosureDetails(); - } else if (is(commandParam, "close")) { - transactionData = this.loanReadPlatformService.retrieveNewClosureDetails(); - } else { - throw new UnrecognizedQueryParamException("command", commandParam); - } - - return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, transactionData); - } - - @GET - @Path("{loanId}/transactions/{transactionId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveTransaction(@PathParam("loanId") final Long loanId, @PathParam("transactionId") final Long transactionId, - @Context final UriInfo uriInfo) { - - context.authenticatedUser().validateHasReadPermission("LOAN"); - - final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); - final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); - - final LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransactionDetails(loanId, transactionId); - - return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, transactionData); - } - - @POST - @Path("{loanId}/transactions/{transactionId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public Response adjustLoanTransaction(@PathParam("loanId") final Long loanId, @PathParam("transactionId") final Long transactionId, - final String jsonRequestBody) { - - final AdjustLoanTransactionCommand command = this.apiDataConversionService.convertJsonToAdjustLoanTransactionCommand(loanId, - transactionId, jsonRequestBody); - - EntityIdentifier identifier = this.loanWritePlatformService.adjustLoanTransaction(command); - - return Response.ok().entity(identifier).build(); - } - - @POST - @Path("{loanId}/charges") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public Response addLoanCharge(@PathParam("loanId") final Long loanId, final String jsonRequestBody) { - - final LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(null, loanId, jsonRequestBody); - - final EntityIdentifier identifier = this.loanWritePlatformService.addLoanCharge(command); - - return Response.ok().entity(identifier).build(); - } - - @GET - @Path("{loanId}/charges/template") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveNewLoanChargeDetails(@Context final UriInfo uriInfo) { - - context.authenticatedUser().validateHasReadPermission("LOAN"); - - final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); - final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); - - final boolean feeChargesOnly = false; - final Collection chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly); - final LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions); - - return this.apiJsonSerializerService.serializeLoanChargeDataToJson(prettyPrint, responseParameters, loanChargeTemplate); - } - - @GET - @Path("{loanId}/charges/{chargeId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId, - @Context final UriInfo uriInfo) { - - context.authenticatedUser().validateHasReadPermission("LOAN"); - - final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); - final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); - - final LoanChargeData loanCharge = this.chargeReadPlatformService.retrieveLoanChargeDetails(loanChargeId, loanId); - - return this.apiJsonSerializerService.serializeLoanChargeDataToJson(prettyPrint, responseParameters, loanCharge); - } - - @PUT - @Path("{loanId}/charges/{chargeId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String updateLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId, - final String jsonRequestBody) { - - final LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(loanChargeId, loanId, - jsonRequestBody); - - final EntityIdentifier identifier = this.loanWritePlatformService.updateLoanCharge(command); - - return this.toApiJsonSerializer.serialize(identifier); - } - - @POST - @Path("{loanId}/charges/{chargeId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String waiveLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId, - @QueryParam("command") final String commandParam) { - - final LoanChargeCommand command = LoanChargeCommand.forWaiver(loanChargeId, loanId); - - String json = ""; - if (is(commandParam, "waive")) { - final EntityIdentifier identifier = this.loanWritePlatformService.waiveLoanCharge(command); - json = this.toApiJsonSerializer.serialize(identifier); - } else { - throw new UnrecognizedQueryParamException("command", commandParam); - } - - return json; - } - - @DELETE - @Path("{loanId}/charges/{chargeId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String deleteLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId) { - - final EntityIdentifier identifier = this.loanWritePlatformService.deleteLoanCharge(loanId, loanChargeId); - return this.toApiJsonSerializer.serialize(identifier); - } - @POST @Path("{loanId}/assign") @Consumes({ MediaType.APPLICATION_JSON }) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/AdjustLoanTransactionCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/AdjustLoanTransactionCommand.java deleted file mode 100644 index f7e1b740e..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/AdjustLoanTransactionCommand.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.mifosplatform.portfolio.loanaccount.command; - -import java.math.BigDecimal; - -import org.joda.time.LocalDate; - -/** - * Immutable command for adjusting loan transactions. - */ -public class AdjustLoanTransactionCommand { - - private final Long loanId; - private final Long transactionId; - private final LocalDate transactionDate; - private final BigDecimal transactionAmount; - private final String note; - - public AdjustLoanTransactionCommand(final Long loanId, final Long transactionId, - final LocalDate transactionLocalDate, final String note, - final BigDecimal transactionAmountValue) { - this.loanId = loanId; - this.transactionId = transactionId; - this.transactionDate = transactionLocalDate; - this.transactionAmount = transactionAmountValue; - this.note = note; - } - - public Long getLoanId() { - return loanId; - } - - public Long getTransactionId() { - return transactionId; - } - - public LocalDate getTransactionDate() { - return transactionDate; - } - - public BigDecimal getTransactionAmount() { - return transactionAmount; - } - - public String getNote() { - return note; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/AdjustLoanTransactionCommandValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/AdjustLoanTransactionCommandValidator.java deleted file mode 100644 index 9754f89b7..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/AdjustLoanTransactionCommandValidator.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.mifosplatform.portfolio.loanaccount.command; - -import java.util.ArrayList; -import java.util.List; - -import org.mifosplatform.infrastructure.core.data.ApiParameterError; -import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; -import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; - -public class AdjustLoanTransactionCommandValidator { - - private final AdjustLoanTransactionCommand command; - - public AdjustLoanTransactionCommandValidator(AdjustLoanTransactionCommand command) { - this.command = command; - } - - public void validate() { - - List dataValidationErrors = new ArrayList(); - - DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction"); - - baseDataValidator.reset().parameter("loanId").value(command.getLoanId()).notNull().integerGreaterThanZero(); - baseDataValidator.reset().parameter("transactionId").value(command.getTransactionId()).notNull().integerGreaterThanZero(); - baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionDate()).notNull(); - baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmount()).notNull().zeroOrPositiveAmount(); - baseDataValidator.reset().parameter("note").value(command.getNote()).notExceedingLengthOf(1000); - - if (!dataValidationErrors.isEmpty()) { - throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); - } - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java index 07f5071b5..5b94a421f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanChargeCommand.java @@ -9,7 +9,7 @@ import org.joda.time.LocalDate; /** * Immutable command for creating and updating loan charges. */ -public class LoanChargeCommand { +public class LoanChargeCommand implements Comparable { private final Long id; private final Long chargeId; @@ -43,6 +43,15 @@ public class LoanChargeCommand { this.chargeCalculationType = chargeCalculationType; this.specifiedDueDate = specifiedDueDate; } + + @Override + public int compareTo(final LoanChargeCommand o) { + int comparison = this.chargeId.compareTo(o.chargeId); + if (comparison == 0) { + comparison = this.amount.compareTo(o.amount); + } + return comparison; + } public Long getId() { return id; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanTransactionCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanTransactionCommand.java index a91d5fedc..334060386 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanTransactionCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanTransactionCommand.java @@ -1,39 +1,52 @@ package org.mifosplatform.portfolio.loanaccount.command; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; /** * Immutable command for loan transactions. */ public class LoanTransactionCommand { - private final Long loanId; - private final LocalDate transactionDate; - private final BigDecimal transactionAmount; - private final String note; + private final LocalDate transactionDate; + private final BigDecimal transactionAmount; + private final String note; - public LoanTransactionCommand(final Long loanId, final LocalDate paymentDate, final BigDecimal paymentAmount,final String note) { - this.loanId = loanId; - this.transactionDate = paymentDate; - this.transactionAmount = paymentAmount; - this.note = note; - } + public LoanTransactionCommand(final LocalDate paymentDate, final BigDecimal paymentAmount, final String note) { + this.transactionDate = paymentDate; + this.transactionAmount = paymentAmount; + this.note = note; + } - public Long getLoanId() { - return loanId; - } + public void validate() { - public LocalDate getTransactionDate() { - return transactionDate; - } + List dataValidationErrors = new ArrayList(); - public BigDecimal getTransactionAmount() { - return transactionAmount; - } + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction"); - public String getNote() { - return note; - } + baseDataValidator.reset().parameter("transactionDate").value(this.transactionDate).notNull(); + baseDataValidator.reset().parameter("transactionAmount").value(this.transactionAmount).notNull().positiveAmount(); + baseDataValidator.reset().parameter("note").value(this.note).notExceedingLengthOf(1000); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + + public void validateNonMonetaryTransaction() { + List dataValidationErrors = new ArrayList(); + + DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction"); + + baseDataValidator.reset().parameter("transactionDate").value(this.transactionDate).notNull(); + baseDataValidator.reset().parameter("note").value(this.note).notExceedingLengthOf(1000); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanTransactionCommandValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanTransactionCommandValidator.java deleted file mode 100644 index f9b5921ef..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/command/LoanTransactionCommandValidator.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.mifosplatform.portfolio.loanaccount.command; - -import java.util.ArrayList; -import java.util.List; - -import org.mifosplatform.infrastructure.core.data.ApiParameterError; -import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; -import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; - -public class LoanTransactionCommandValidator { - - private final LoanTransactionCommand command; - - public LoanTransactionCommandValidator(final LoanTransactionCommand command) { - this.command = command; - } - - public void validate() { - - List dataValidationErrors = new ArrayList(); - - DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction"); - - baseDataValidator.reset().parameter("loanId").value(command.getLoanId()).notNull().integerGreaterThanZero(); - baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionDate()).notNull(); - baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmount()).notNull().positiveAmount(); - baseDataValidator.reset().parameter("note").value(command.getNote()).notExceedingLengthOf(1000); - - if (!dataValidationErrors.isEmpty()) { - throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); - } - } - - public void validateNonMonetaryTransaction() { - List dataValidationErrors = new ArrayList(); - - DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction"); - - baseDataValidator.reset().parameter("loanId").value(command.getLoanId()).notNull().integerGreaterThanZero(); - baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionDate()).notNull(); - baseDataValidator.reset().parameter("note").value(command.getNote()).notExceedingLengthOf(1000); - - if (!dataValidationErrors.isEmpty()) { - throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); - } - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index c8e538cbb..5f164527a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -850,13 +850,10 @@ public class Loan extends AbstractAuditableCustom { approvedOn = command.localDateValueOfParameterNamed("eventDate"); } - final Locale locale = new Locale(command.locale()); - final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); - this.approvedOnDate = approvedOn.toDate(); actualChanges.put("locale", command.locale()); actualChanges.put("dateFormat", command.dateFormat()); - actualChanges.put("approvedOnDate", approvedOn.toString(fmt)); + actualChanges.put("approvedOnDate", command.stringValueOfParameterNamed("approvedOnDate")); final LocalDate submittalDate = new LocalDate(this.submittedOnDate); if (approvedOn.isBefore(submittalDate)) { @@ -901,7 +898,7 @@ public class Loan extends AbstractAuditableCustom { private void disburse(final LocalDate expectedDisbursedOnLocalDate) { this.disbursedOnDate = expectedDisbursedOnLocalDate.toDate(); this.expectedMaturityDate = determineExpectedMaturityDate().toDate(); - + handleDisbursementTransaction(expectedDisbursedOnLocalDate); } @@ -928,7 +925,7 @@ public class Loan extends AbstractAuditableCustom { this.disbursedOnDate = disbursedOn.toDate(); this.expectedMaturityDate = determineExpectedMaturityDate().toDate(); - + actualChanges.put("locale", command.locale()); actualChanges.put("dateFormat", command.dateFormat()); actualChanges.put("disbursedOnDate", disbursedOn.toString(fmt)); @@ -1059,21 +1056,12 @@ public class Loan extends AbstractAuditableCustom { wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, this.charges); } - /** - * - * @param amount - * The amount of interest to be waived. - * @param transactionDate - * The date on which the interest waiver is to be processed - * against. - * @param defaultLoanLifecycleStateMachine - * - * @return {@link LoanTransaction} details of executed transaction - */ - public LoanTransaction waiveInterest(final BigDecimal amount, final LocalDate transactionDate, - final LoanLifecycleStateMachine loanLifecycleStateMachine) { + public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) { - final Money amountToWaive = Money.of(loanCurrency(), amount); + final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + + final Money amountToWaive = Money.of(loanCurrency(), transactionAmount); final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(this, amountToWaive, transactionDate); handleRepaymentOrWaiverTransaction(waiveInterestTransaction, loanLifecycleStateMachine, null); @@ -1081,8 +1069,15 @@ public class Loan extends AbstractAuditableCustom { return waiveInterestTransaction; } - public void makeRepayment(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) { + public LoanTransaction makeRepayment(final LocalDate transactionDate, final BigDecimal transactionAmount, + final LoanLifecycleStateMachine loanLifecycleStateMachine) { + + final Money repayment = Money.of(loanCurrency(), transactionAmount); + final LoanTransaction loanTransaction = LoanTransaction.repayment(repayment, transactionDate); + handleRepaymentOrWaiverTransaction(loanTransaction, loanLifecycleStateMachine, null); + + return loanTransaction; } private void handleRepaymentOrWaiverTransaction(final LoanTransaction loanTransaction, @@ -1286,8 +1281,14 @@ public class Loan extends AbstractAuditableCustom { return LoanTransaction.waiver(this, possibleInterestToWaive, transactionDate); } - public void adjustExistingTransaction(final LoanTransaction transactionForAdjustment, final LoanTransaction newTransactionDetail, - final LoanLifecycleStateMachine loanLifecycleStateMachine) { + public LoanTransaction adjustExistingTransaction(final LocalDate transactionDate, final BigDecimal transactionAmountValue, + final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment) { + + final Money transactionAmount = Money.of(loanCurrency(), transactionAmountValue); + LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate); + if (transactionForAdjustment.isInterestWaiver()) { + newTransactionDetail = LoanTransaction.waiver(this, transactionAmount, transactionDate); + } if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()) { final String errorMessage = "Only transactions of type repayment or waiver can be adjusted."; @@ -1304,6 +1305,8 @@ public class Loan extends AbstractAuditableCustom { if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) { handleRepaymentOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment); } + + return newTransactionDetail; } private boolean isRepaidInFull() { @@ -1365,16 +1368,21 @@ public class Loan extends AbstractAuditableCustom { return this.loanRepaymentScheduleDetail.getCurrency(); } - public LoanTransaction closeAsWrittenOff(final LocalDate writtenOffOnLocalDate, - final LoanLifecycleStateMachine loanLifecycleStateMachine) { + public LoanTransaction closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map changes) { - final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, - LoanStatus.fromInt(this.loanStatus)); - this.loanStatus = statusEnum.getValue(); + final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, LoanStatus.fromInt(this.loanStatus)); + if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { + this.loanStatus = statusEnum.getValue(); + changes.put("status", LoanEnumerations.status(this.loanStatus)); + } + + final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed("transactionDate"); this.closedOnDate = writtenOffOnLocalDate.toDate(); this.writtenOffOnDate = writtenOffOnLocalDate.toDate(); - + changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); + changes.put("writtenOffOnDate", command.stringValueOfParameterNamed("transactionDate")); + if (writtenOffOnLocalDate.isBefore(this.getDisbursementDate())) { final String errorMessage = "The date on which a loan is written off cannot be before the loan disbursement date: " + getDisbursementDate().toString(); @@ -1404,7 +1412,12 @@ public class Loan extends AbstractAuditableCustom { return loanTransaction; } - public LoanTransaction close(final LocalDate closureDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) { + public LoanTransaction close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map changes) { + + final LocalDate closureDate = command.localDateValueOfParameterNamed("transactionDate"); + + this.closedOnDate = closureDate.toDate(); + changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); if (closureDate.isBefore(this.getDisbursementDate())) { final String errorMessage = "The date on which a loan is closed cannot be before the loan disbursement date: " @@ -1424,7 +1437,7 @@ public class Loan extends AbstractAuditableCustom { final Money outstanding = getTotalOutstanding(); if (outstanding.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(outstanding)) { - updateLoanForClosure(closureDate, loanLifecycleStateMachine); + updateLoanForClosure(closureDate, loanLifecycleStateMachine, changes); loanTransaction = LoanTransaction.writeoff(this, closureDate); boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions); @@ -1454,7 +1467,7 @@ public class Loan extends AbstractAuditableCustom { if (totalLoanOverpayment.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(totalLoanOverpayment)) { // TODO - technically should set somewhere that this loan has // 'overpaid' amount - updateLoanForClosure(closureDate, loanLifecycleStateMachine); + updateLoanForClosure(closureDate, loanLifecycleStateMachine, changes); } else if (totalLoanOverpayment.isGreaterThanZero()) { final String errorMessage = "The loan is marked as 'Overpaid' and cannot be moved to 'Closed (obligations met)."; throw new InvalidLoanStateTransitionException("close", "loan.is.overpaid", errorMessage, totalLoanOverpayment.toString()); @@ -1474,23 +1487,35 @@ public class Loan extends AbstractAuditableCustom { return totalOutstanding; } - private void updateLoanForClosure(final LocalDate closureDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) { + private void updateLoanForClosure(final LocalDate closureDate, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map changes) { + final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus)); - this.loanStatus = statusEnum.getValue(); + if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { + this.loanStatus = statusEnum.getValue(); + changes.put("status", LoanEnumerations.status(this.loanStatus)); + } this.closedOnDate = closureDate.toDate(); } /** * Behaviour added to comply with capability of previous mifos product to * support easier transition to mifosx platform. + * @param changes */ - public void closeAsMarkedForReschedule(final LocalDate rescheduledOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) { + public void closeAsMarkedForReschedule(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map changes) { + final LocalDate rescheduledOn = command.localDateValueOfParameterNamed("transactionDate"); + final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RESCHEDULE, LoanStatus.fromInt(this.loanStatus)); - this.loanStatus = statusEnum.getValue(); + if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { + this.loanStatus = statusEnum.getValue(); + changes.put("status", LoanEnumerations.status(this.loanStatus)); + } this.closedOnDate = rescheduledOn.toDate(); this.rescheduledOnDate = rescheduledOn.toDate(); + changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate")); + changes.put("rescheduledOnDate", command.stringValueOfParameterNamed("transactionDate")); LocalDate rescheduledOnLocalDate = new LocalDate(rescheduledOnDate); if (rescheduledOnLocalDate.isBefore(this.getDisbursementDate())) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessor.java index 04e60cb2e..5de8f5694 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessor.java @@ -8,12 +8,13 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; public interface LoanRepaymentScheduleTransactionProcessor { - void handleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency, List installments, Set charges); + void handleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency, List installments, + Set charges); - void handleTransaction(LocalDate disbursementDate, List repaymentsOrWaivers, MonetaryCurrency currency, List repaymentScheduleInstallments, Set charges); + void handleTransaction(LocalDate disbursementDate, List repaymentsOrWaivers, MonetaryCurrency currency, + List repaymentScheduleInstallments, Set charges); - void handleWriteOff(LoanTransaction loanTransaction, - MonetaryCurrency loanCurrency, - List repaymentScheduleInstallments); + void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency loanCurrency, + List repaymentScheduleInstallments); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java new file mode 100644 index 000000000..3586171b1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.loanaccount.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CloseLoanAsRescheduledCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public CloseLoanAsRescheduledCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public EntityIdentifier processCommand(final JsonCommand command) { + + return this.writePlatformService.closeAsRescheduled(command.resourceId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/CloseLoanCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/CloseLoanCommandHandler.java new file mode 100644 index 000000000..a0d0f06b2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/CloseLoanCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.loanaccount.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CloseLoanCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public CloseLoanCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public EntityIdentifier processCommand(final JsonCommand command) { + + return this.writePlatformService.closeLoan(command.resourceId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/LoanRepaymentAdjustmentCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/LoanRepaymentAdjustmentCommandHandler.java new file mode 100644 index 000000000..a1a75342a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/LoanRepaymentAdjustmentCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.loanaccount.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class LoanRepaymentAdjustmentCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public LoanRepaymentAdjustmentCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public EntityIdentifier processCommand(final JsonCommand command) { + + return this.writePlatformService.adjustLoanTransaction(command.resourceId(), command.subResourceId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java new file mode 100644 index 000000000..00ffe78e6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.loanaccount.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class LoanRepaymentCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public LoanRepaymentCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public EntityIdentifier processCommand(final JsonCommand command) { + + return this.writePlatformService.makeLoanRepayment(command.resourceId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/WaiveInterestPortionOnLoanCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/WaiveInterestPortionOnLoanCommandHandler.java new file mode 100644 index 000000000..437e99911 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/WaiveInterestPortionOnLoanCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.loanaccount.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class WaiveInterestPortionOnLoanCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public WaiveInterestPortionOnLoanCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public EntityIdentifier processCommand(final JsonCommand command) { + + return this.writePlatformService.waiveInterestOnLoan(command.resourceId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/WriteOffLoanCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/WriteOffLoanCommandHandler.java new file mode 100644 index 000000000..9e5dda106 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/handler/WriteOffLoanCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.loanaccount.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.EntityIdentifier; +import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class WriteOffLoanCommandHandler implements NewCommandSourceHandler { + + private final LoanWritePlatformService writePlatformService; + + @Autowired + public WriteOffLoanCommandHandler(final LoanWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public EntityIdentifier processCommand(final JsonCommand command) { + + return this.writePlatformService.writeOff(command.resourceId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanTransactionCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanTransactionCommandFromApiJsonDeserializer.java new file mode 100644 index 000000000..db3970f6d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanTransactionCommandFromApiJsonDeserializer.java @@ -0,0 +1,58 @@ +package org.mifosplatform.portfolio.loanaccount.serialization; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; +import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer; +import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer; +import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; +import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +/** + * Implementation of {@link FromApiJsonDeserializer} for + * {@link LoanTransactionCommand}'s. + */ +@Component +public final class LoanTransactionCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer { + + /** + * The parameters supported for this command. + */ + final Set supportedParameters = new HashSet(Arrays.asList("transactionDate", "transactionAmount", "note", "dateFormat", + "locale" ,"status", "closedOnDate", "writtenOffOnDate", "rescheduledOnDate")); + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public LoanTransactionCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + @Override + public LoanTransactionCommand commandFromApiJson(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + + final JsonElement element = fromApiJsonHelper.parse(json); + final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed("transactionDate", element); + final BigDecimal transactionAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element); + final String note = fromApiJsonHelper.extractStringNamed("note", element); + + return new LoanTransactionCommand(transactionDate, transactionAmount, note); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java index 60e5555dc..b79764734 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -12,25 +12,25 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDat public interface LoanReadPlatformService { - LoanBasicDetailsData retrieveLoanAccountDetails(Long loanId); + LoanBasicDetailsData retrieveLoanAccountDetails(Long loanId); - LoanScheduleData retrieveRepaymentSchedule(Long loanId, CurrencyData currency, DisbursementData disbursement, BigDecimal totalChargesAtDisbursement, BigDecimal inArrearsTolerance); + LoanScheduleData retrieveRepaymentSchedule(Long loanId, CurrencyData currency, DisbursementData disbursement, + BigDecimal totalChargesAtDisbursement, BigDecimal inArrearsTolerance); - Collection retrieveLoanTransactions(Long loanId); + Collection retrieveLoanTransactions(Long loanId); - LoanPermissionData retrieveLoanPermissions( - LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed, - int repaymentAndWaiveCount, boolean existsGuarantor); + LoanPermissionData retrieveLoanPermissions(LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed, int repaymentAndWaiveCount, + boolean existsGuarantor); - LoanBasicDetailsData retrieveClientAndProductDetails(Long clientId, Long productId); + LoanBasicDetailsData retrieveClientAndProductDetails(Long clientId, Long productId); LoanBasicDetailsData retrieveGroupAndProductDetails(Long groupId, Long productId); - LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId); + LoanTransactionData retrieveLoanTransactionTemplate(Long loanId); - LoanTransactionData retrieveNewLoanWaiveInterestDetails(Long loanId); + LoanTransactionData retrieveWaiveInterestDetails(Long loanId); - LoanTransactionData retrieveLoanTransactionDetails(Long loanId, Long transactionId); + LoanTransactionData retrieveLoanTransaction(Long loanId, Long transactionId); - LoanTransactionData retrieveNewClosureDetails(); + LoanTransactionData retrieveNewClosureDetails(); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index d735c002b..0a191db12 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -47,616 +47,571 @@ import org.springframework.stereotype.Service; @Service public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { - private final JdbcTemplate jdbcTemplate; - private final PlatformSecurityContext context; - private final LoanRepository loanRepository; - private final ApplicationCurrencyRepository applicationCurrencyRepository; - private final LoanProductReadPlatformService loanProductReadPlatformService; - private final ClientReadPlatformService clientReadPlatformService; - private final GroupReadPlatformService groupReadPlatformService; - private final LoanTransactionRepository loanTransactionRepository; + private final JdbcTemplate jdbcTemplate; + private final PlatformSecurityContext context; + private final LoanRepository loanRepository; + private final ApplicationCurrencyRepository applicationCurrencyRepository; + private final LoanProductReadPlatformService loanProductReadPlatformService; + private final ClientReadPlatformService clientReadPlatformService; + private final GroupReadPlatformService groupReadPlatformService; + private final LoanTransactionRepository loanTransactionRepository; - @Autowired - public LoanReadPlatformServiceImpl( - final PlatformSecurityContext context, - final LoanRepository loanRepository, - final LoanTransactionRepository loanTransactionRepository, - final ApplicationCurrencyRepository applicationCurrencyRepository, - final LoanProductReadPlatformService loanProductReadPlatformService, - final ClientReadPlatformService clientReadPlatformService, - final GroupReadPlatformService groupReadPlatformService, - final TenantAwareRoutingDataSource dataSource) { - this.context = context; - this.loanRepository = loanRepository; - this.loanTransactionRepository = loanTransactionRepository; - this.applicationCurrencyRepository = applicationCurrencyRepository; - this.loanProductReadPlatformService = loanProductReadPlatformService; - this.clientReadPlatformService = clientReadPlatformService; - this.groupReadPlatformService = groupReadPlatformService; - this.jdbcTemplate = new JdbcTemplate(dataSource); - } + @Autowired + public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository, + final LoanTransactionRepository loanTransactionRepository, final ApplicationCurrencyRepository applicationCurrencyRepository, + final LoanProductReadPlatformService loanProductReadPlatformService, final ClientReadPlatformService clientReadPlatformService, + final GroupReadPlatformService groupReadPlatformService, final TenantAwareRoutingDataSource dataSource) { + this.context = context; + this.loanRepository = loanRepository; + this.loanTransactionRepository = loanTransactionRepository; + this.applicationCurrencyRepository = applicationCurrencyRepository; + this.loanProductReadPlatformService = loanProductReadPlatformService; + this.clientReadPlatformService = clientReadPlatformService; + this.groupReadPlatformService = groupReadPlatformService; + this.jdbcTemplate = new JdbcTemplate(dataSource); + } - @Override - public LoanBasicDetailsData retrieveLoanAccountDetails(final Long loanId) { + @Override + public LoanBasicDetailsData retrieveLoanAccountDetails(final Long loanId) { - try { - context.authenticatedUser(); + try { + context.authenticatedUser(); - LoanMapper rm = new LoanMapper(); - - String sql = "select " + rm.loanSchema() + " where l.id = ?"; - - return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId }); + LoanMapper rm = new LoanMapper(); - } catch (EmptyResultDataAccessException e) { - throw new LoanNotFoundException(loanId); - } + String sql = "select " + rm.loanSchema() + " where l.id = ?"; - } + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId }); - @Override - public LoanScheduleData retrieveRepaymentSchedule(final Long loanId, - final CurrencyData currency, - final DisbursementData disbursement, - final BigDecimal totalFeeChargesAtDisbursement, - final BigDecimal inArrearsTolerance) { + } catch (EmptyResultDataAccessException e) { + throw new LoanNotFoundException(loanId); + } - try { - context.authenticatedUser(); + } - final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement); - final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment"; - - final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursement.disbursementDate(), disbursement.amount(), totalFeeChargesAtDisbursement, disbursement.isDisbursed()); - - final Collection repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); - - final Collection periods = new ArrayList(repaymentSchedulePeriods.size()+1); - periods.add(disbursementPeriod); - periods.addAll(repaymentSchedulePeriods); - - LoanSchedulePeriodDataWrapper wrapper = new LoanSchedulePeriodDataWrapper(periods); - - final Integer loanTermInDays = wrapper.deriveCumulativeLoanTermInDays(); - - final BigDecimal cumulativePrincipalDisbursed = wrapper.deriveCumulativePrincipalDisbursed(); - final BigDecimal cumulativePrincipalDue = wrapper.deriveCumulativePrincipalDue(); - final BigDecimal cumulativePrincipalPaid = wrapper.deriveCumulativePrincipalPaid(); - final BigDecimal cumulativePrincipalWrittenOff = wrapper.deriveCumulativePrincipalWrittenOff(); - final BigDecimal cumulativePrincipalOutstanding = wrapper.deriveCumulativePrincipalOutstanding(); - - final BigDecimal cumulativeInterestExpected = wrapper.deriveCumulativeInterestExpected(); - final BigDecimal cumulativeInterestPaid = wrapper.deriveCumulativeInterestPaid(); - final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived(); - final BigDecimal cumulativeInterestWrittenOff = wrapper.deriveCumulativeInterestWrittenOff(); - final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding(); - - final BigDecimal cumulativeFeeChargesExpected = wrapper.deriveCumulativeFeeChargesToDate(); - final BigDecimal cumulativeFeeChargesPaid = wrapper.deriveCumulativeFeeChargesPaid(); - final BigDecimal cumulativeFeeChargesWaived = wrapper.deriveCumulativeFeeChargesWaived(); - final BigDecimal cumulativeFeeChargesWrittenOff = wrapper.deriveCumulativeFeeChargesWrittenOff(); - final BigDecimal cumulativeFeeChargesOutstanding = wrapper.deriveCumulativeFeeChargesOutstanding(); - - final BigDecimal cumulativePenaltyChargesExpected = wrapper.deriveCumulativePenaltyChargesToDate(); - final BigDecimal cumulativePenaltyChargesPaid = wrapper.deriveCumulativePenaltyChargesPaid(); - final BigDecimal cumulativePenaltyChargesWaived = wrapper.deriveCumulativePenaltyChargesWaived(); - final BigDecimal cumulativePenaltyChargesWrittenOff = wrapper.deriveCumulativePenaltyChargesWrittenOff(); - final BigDecimal cumulativePenaltyChargesOutstanding = wrapper.deriveCumulativePenaltyChargesOutstanding(); - - final BigDecimal totalExpectedCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesExpected).add(cumulativePenaltyChargesExpected); - final BigDecimal totalExpectedRepayment = cumulativePrincipalDisbursed.add(totalExpectedCostOfLoan); - - final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeFeeChargesPaid).add(cumulativePenaltyChargesPaid); - final BigDecimal totalWaivedToDate = cumulativeInterestWaived.add(cumulativeFeeChargesWaived).add(cumulativePenaltyChargesWaived); - final BigDecimal totalWrittenOffToDate = cumulativePrincipalWrittenOff.add(cumulativeInterestWrittenOff).add(cumulativeFeeChargesWrittenOff).add(cumulativePenaltyChargesWrittenOff); - - final BigDecimal totalOutstanding = cumulativePrincipalOutstanding.add(cumulativeInterestOutstanding).add(cumulativeFeeChargesOutstanding).add(cumulativePenaltyChargesOutstanding); + @Override + public LoanScheduleData retrieveRepaymentSchedule(final Long loanId, final CurrencyData currency, final DisbursementData disbursement, + final BigDecimal totalFeeChargesAtDisbursement, final BigDecimal inArrearsTolerance) { - final BigDecimal totalOverdue = wrapper.deriveCumulativeTotalOverdue(); - - final MonetaryCurrency monetaryCurrency = new MonetaryCurrency(currency.code(), currency.decimalPlaces()); - final Money tolerance = Money.of(monetaryCurrency, inArrearsTolerance); - final Money totalOverdueMoney = Money.of(monetaryCurrency, totalOverdue); - boolean isWaiveAllowed = totalOverdueMoney.isGreaterThanZero() && (tolerance.isGreaterThan(totalOverdueMoney) || tolerance.isEqualTo(totalOverdueMoney)); + try { + context.authenticatedUser(); - BigDecimal totalInArrears = null; - if (!isWaiveAllowed) { - totalInArrears = totalOverdueMoney.getAmount(); - } - - return new LoanScheduleData(currency, periods, loanTermInDays, - cumulativePrincipalDisbursed, cumulativePrincipalDue, cumulativePrincipalPaid, cumulativePrincipalWrittenOff, cumulativePrincipalOutstanding, - cumulativeInterestExpected, cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestWrittenOff, cumulativeInterestOutstanding, - cumulativeFeeChargesExpected, cumulativeFeeChargesPaid, cumulativeFeeChargesWaived, cumulativeFeeChargesWrittenOff, cumulativeFeeChargesOutstanding, - cumulativePenaltyChargesExpected, cumulativePenaltyChargesPaid, cumulativePenaltyChargesWaived, cumulativePenaltyChargesWrittenOff, cumulativePenaltyChargesOutstanding, - totalExpectedCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalWrittenOffToDate, totalOutstanding, totalInArrears); - } catch (EmptyResultDataAccessException e) { - throw new LoanNotFoundException(loanId); - } - } + final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement); + final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment"; - @Override - public Collection retrieveLoanTransactions(final Long loanId) { - try { - context.authenticatedUser(); + final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod( + disbursement.disbursementDate(), disbursement.amount(), totalFeeChargesAtDisbursement, disbursement.isDisbursed()); - LoanTransactionsMapper rm = new LoanTransactionsMapper(); + final Collection repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); - // retrieve all loan transactions that are not invalid and have not been 'contra'ed by another transaction - // repayments at time of disbursement (e.g. charges) - String sql = "select " - + rm.LoanPaymentsSchema() - + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and tr.contra_id is null order by tr.transaction_date ASC"; - return this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); - } catch (EmptyResultDataAccessException e) { - return null; - } - } + final Collection periods = new ArrayList(repaymentSchedulePeriods.size() + 1); + periods.add(disbursementPeriod); + periods.addAll(repaymentSchedulePeriods); - @Override - public LoanPermissionData retrieveLoanPermissions( - final LoanBasicDetailsData loanBasicDetails, - final boolean isWaiverAllowed, - final int repaymentAndWaiveCount, - final boolean existsGuarantor) { + LoanSchedulePeriodDataWrapper wrapper = new LoanSchedulePeriodDataWrapper(periods); - final boolean pendingApproval = (loanBasicDetails.getStatus().getId().equals(100L)); - final boolean waitingForDisbursal = (loanBasicDetails.getStatus().getId().equals(200L)); - final boolean isActive = (loanBasicDetails.getStatus().getId().equals(300L)); - final boolean closedObligationsMet = (loanBasicDetails.getStatus().getId().equals(600L)); - final boolean closedWrittenOff = (loanBasicDetails.getStatus().getId().equals(602L)); - final boolean closedRescheduled = (loanBasicDetails.getStatus().getId().equals(602L)); - - final boolean closed = closedObligationsMet || closedWrittenOff || closedRescheduled; - - final boolean isOverpaid = (loanBasicDetails.getStatus().getId().equals(700L)); - boolean addLoanChargeAllowed = true; - boolean setGuarantorAllowed = true; - boolean editGuarantorAllowed = true; - if (closed || isOverpaid) { - addLoanChargeAllowed = false; - setGuarantorAllowed = false; - editGuarantorAllowed = false; - } - - if(existsGuarantor){ - setGuarantorAllowed = false; - }else{ - editGuarantorAllowed = false; - } + final Integer loanTermInDays = wrapper.deriveCumulativeLoanTermInDays(); - final boolean waiveAllowed = isWaiverAllowed && isActive; - final boolean makeRepaymentAllowed = isActive; - final boolean closeLoanAllowed = !closed && isActive; - final boolean closeLoanAsRescheduledAllowed = !closed && !isOverpaid && isActive; - - final boolean rejectAllowed = pendingApproval; - final boolean withdrawnByApplicantAllowed = waitingForDisbursal - || pendingApproval; + final BigDecimal cumulativePrincipalDisbursed = wrapper.deriveCumulativePrincipalDisbursed(); + final BigDecimal cumulativePrincipalDue = wrapper.deriveCumulativePrincipalDue(); + final BigDecimal cumulativePrincipalPaid = wrapper.deriveCumulativePrincipalPaid(); + final BigDecimal cumulativePrincipalWrittenOff = wrapper.deriveCumulativePrincipalWrittenOff(); + final BigDecimal cumulativePrincipalOutstanding = wrapper.deriveCumulativePrincipalOutstanding(); - final boolean undoApprovalAllowed = waitingForDisbursal; + final BigDecimal cumulativeInterestExpected = wrapper.deriveCumulativeInterestExpected(); + final BigDecimal cumulativeInterestPaid = wrapper.deriveCumulativeInterestPaid(); + final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived(); + final BigDecimal cumulativeInterestWrittenOff = wrapper.deriveCumulativeInterestWrittenOff(); + final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding(); - final boolean undoDisbursalAllowed = isActive - && (repaymentAndWaiveCount == 0); + final BigDecimal cumulativeFeeChargesExpected = wrapper.deriveCumulativeFeeChargesToDate(); + final BigDecimal cumulativeFeeChargesPaid = wrapper.deriveCumulativeFeeChargesPaid(); + final BigDecimal cumulativeFeeChargesWaived = wrapper.deriveCumulativeFeeChargesWaived(); + final BigDecimal cumulativeFeeChargesWrittenOff = wrapper.deriveCumulativeFeeChargesWrittenOff(); + final BigDecimal cumulativeFeeChargesOutstanding = wrapper.deriveCumulativeFeeChargesOutstanding(); - final boolean disbursalAllowed = waitingForDisbursal; + final BigDecimal cumulativePenaltyChargesExpected = wrapper.deriveCumulativePenaltyChargesToDate(); + final BigDecimal cumulativePenaltyChargesPaid = wrapper.deriveCumulativePenaltyChargesPaid(); + final BigDecimal cumulativePenaltyChargesWaived = wrapper.deriveCumulativePenaltyChargesWaived(); + final BigDecimal cumulativePenaltyChargesWrittenOff = wrapper.deriveCumulativePenaltyChargesWrittenOff(); + final BigDecimal cumulativePenaltyChargesOutstanding = wrapper.deriveCumulativePenaltyChargesOutstanding(); - return new LoanPermissionData(closeLoanAllowed, closeLoanAsRescheduledAllowed, - addLoanChargeAllowed, waiveAllowed, makeRepaymentAllowed, - rejectAllowed, withdrawnByApplicantAllowed, - undoApprovalAllowed, undoDisbursalAllowed, disbursalAllowed, - setGuarantorAllowed,editGuarantorAllowed, - pendingApproval, waitingForDisbursal, closedObligationsMet); - } + final BigDecimal totalExpectedCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesExpected).add( + cumulativePenaltyChargesExpected); + final BigDecimal totalExpectedRepayment = cumulativePrincipalDisbursed.add(totalExpectedCostOfLoan); - @Override - public LoanBasicDetailsData retrieveClientAndProductDetails(final Long clientId, final Long productId) { + final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeFeeChargesPaid) + .add(cumulativePenaltyChargesPaid); + final BigDecimal totalWaivedToDate = cumulativeInterestWaived.add(cumulativeFeeChargesWaived).add( + cumulativePenaltyChargesWaived); + final BigDecimal totalWrittenOffToDate = cumulativePrincipalWrittenOff.add(cumulativeInterestWrittenOff) + .add(cumulativeFeeChargesWrittenOff).add(cumulativePenaltyChargesWrittenOff); - context.authenticatedUser(); + final BigDecimal totalOutstanding = cumulativePrincipalOutstanding.add(cumulativeInterestOutstanding) + .add(cumulativeFeeChargesOutstanding).add(cumulativePenaltyChargesOutstanding); - final LocalDate expectedDisbursementDate = new LocalDate(); - final ClientData clientAccount = this.clientReadPlatformService.retrieveIndividualClient(clientId); - - LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewIndividualClientLoanCreation(clientAccount.id(), clientAccount.displayName(), expectedDisbursementDate, - clientAccount.officeId()); - - if (productId != null) { - LoanProductData selectedProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); - loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(loanDetails, selectedProduct); - } + final BigDecimal totalOverdue = wrapper.deriveCumulativeTotalOverdue(); - return loanDetails; - } + final MonetaryCurrency monetaryCurrency = new MonetaryCurrency(currency.code(), currency.decimalPlaces()); + final Money tolerance = Money.of(monetaryCurrency, inArrearsTolerance); + final Money totalOverdueMoney = Money.of(monetaryCurrency, totalOverdue); + boolean isWaiveAllowed = totalOverdueMoney.isGreaterThanZero() + && (tolerance.isGreaterThan(totalOverdueMoney) || tolerance.isEqualTo(totalOverdueMoney)); + + BigDecimal totalInArrears = null; + if (!isWaiveAllowed) { + totalInArrears = totalOverdueMoney.getAmount(); + } + + return new LoanScheduleData(currency, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue, + cumulativePrincipalPaid, cumulativePrincipalWrittenOff, cumulativePrincipalOutstanding, cumulativeInterestExpected, + cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestWrittenOff, cumulativeInterestOutstanding, + cumulativeFeeChargesExpected, cumulativeFeeChargesPaid, cumulativeFeeChargesWaived, cumulativeFeeChargesWrittenOff, + cumulativeFeeChargesOutstanding, cumulativePenaltyChargesExpected, cumulativePenaltyChargesPaid, + cumulativePenaltyChargesWaived, cumulativePenaltyChargesWrittenOff, cumulativePenaltyChargesOutstanding, + totalExpectedCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalWrittenOffToDate, + totalOutstanding, totalInArrears); + } catch (EmptyResultDataAccessException e) { + throw new LoanNotFoundException(loanId); + } + } + + @Override + public Collection retrieveLoanTransactions(final Long loanId) { + try { + context.authenticatedUser(); + + LoanTransactionsMapper rm = new LoanTransactionsMapper(); + + // retrieve all loan transactions that are not invalid and have not + // been 'contra'ed by another transaction + // repayments at time of disbursement (e.g. charges) + String sql = "select " + + rm.LoanPaymentsSchema() + + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and tr.contra_id is null order by tr.transaction_date ASC"; + return this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); + } catch (EmptyResultDataAccessException e) { + return null; + } + } + + @Override + public LoanPermissionData retrieveLoanPermissions(final LoanBasicDetailsData loanBasicDetails, final boolean isWaiverAllowed, + final int repaymentAndWaiveCount, final boolean existsGuarantor) { + + final boolean pendingApproval = (loanBasicDetails.getStatus().getId().equals(100L)); + final boolean waitingForDisbursal = (loanBasicDetails.getStatus().getId().equals(200L)); + final boolean isActive = (loanBasicDetails.getStatus().getId().equals(300L)); + final boolean closedObligationsMet = (loanBasicDetails.getStatus().getId().equals(600L)); + final boolean closedWrittenOff = (loanBasicDetails.getStatus().getId().equals(602L)); + final boolean closedRescheduled = (loanBasicDetails.getStatus().getId().equals(602L)); + + final boolean closed = closedObligationsMet || closedWrittenOff || closedRescheduled; + + final boolean isOverpaid = (loanBasicDetails.getStatus().getId().equals(700L)); + boolean addLoanChargeAllowed = true; + boolean setGuarantorAllowed = true; + boolean editGuarantorAllowed = true; + if (closed || isOverpaid) { + addLoanChargeAllowed = false; + setGuarantorAllowed = false; + editGuarantorAllowed = false; + } + + if (existsGuarantor) { + setGuarantorAllowed = false; + } else { + editGuarantorAllowed = false; + } + + final boolean waiveAllowed = isWaiverAllowed && isActive; + final boolean makeRepaymentAllowed = isActive; + final boolean closeLoanAllowed = !closed && isActive; + final boolean closeLoanAsRescheduledAllowed = !closed && !isOverpaid && isActive; + + final boolean rejectAllowed = pendingApproval; + final boolean withdrawnByApplicantAllowed = waitingForDisbursal || pendingApproval; + + final boolean undoApprovalAllowed = waitingForDisbursal; + + final boolean undoDisbursalAllowed = isActive && (repaymentAndWaiveCount == 0); + + final boolean disbursalAllowed = waitingForDisbursal; + + return new LoanPermissionData(closeLoanAllowed, closeLoanAsRescheduledAllowed, addLoanChargeAllowed, waiveAllowed, + makeRepaymentAllowed, rejectAllowed, withdrawnByApplicantAllowed, undoApprovalAllowed, undoDisbursalAllowed, + disbursalAllowed, setGuarantorAllowed, editGuarantorAllowed, pendingApproval, waitingForDisbursal, closedObligationsMet); + } + + @Override + public LoanBasicDetailsData retrieveClientAndProductDetails(final Long clientId, final Long productId) { + + context.authenticatedUser(); + + final LocalDate expectedDisbursementDate = new LocalDate(); + final ClientData clientAccount = this.clientReadPlatformService.retrieveIndividualClient(clientId); + + LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewIndividualClientLoanCreation(clientAccount.id(), + clientAccount.displayName(), expectedDisbursementDate, clientAccount.officeId()); + + if (productId != null) { + LoanProductData selectedProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); + loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(loanDetails, selectedProduct); + } + + return loanDetails; + } @Override public LoanBasicDetailsData retrieveGroupAndProductDetails(Long groupId, Long productId) { - context.authenticatedUser(); + context.authenticatedUser(); - final LocalDate expectedDisbursementDate = new LocalDate(); - final GroupData groupAccount = this.groupReadPlatformService.retrieveGroup(groupId); + final LocalDate expectedDisbursementDate = new LocalDate(); + final GroupData groupAccount = this.groupReadPlatformService.retrieveGroup(groupId); - LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewGroupLoanCreation(groupAccount.getId(), groupAccount.getName(), expectedDisbursementDate, - groupAccount.getOfficeId()); + LoanBasicDetailsData loanDetails = LoanBasicDetailsData.populateForNewGroupLoanCreation(groupAccount.getId(), + groupAccount.getName(), expectedDisbursementDate, groupAccount.getOfficeId()); - if (productId != null) { - LoanProductData selectedProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); - loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(loanDetails, selectedProduct); - } + if (productId != null) { + LoanProductData selectedProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); + loanDetails = LoanBasicDetailsData.populateForNewLoanCreation(loanDetails, selectedProduct); + } - return loanDetails; + return loanDetails; } @Override - public LoanTransactionData retrieveNewLoanRepaymentDetails(final Long loanId) { + public LoanTransactionData retrieveLoanTransactionTemplate(final Long loanId) { - context.authenticatedUser(); + context.authenticatedUser(); - // TODO - OPTIMIZE - write simple sql query to fetch back date of - // possible next transaction date. - Loan loan = this.loanRepository.findOne(loanId); - if (loan == null) { - throw new LoanNotFoundException(loanId); - } + // TODO - OPTIMIZE - write simple sql query to fetch back date of + // possible next transaction date. + Loan loan = this.loanRepository.findOne(loanId); + if (loan == null) { throw new LoanNotFoundException(loanId); } - final String currencyCode = loan.repaymentScheduleDetail().getPrincipal().getCurrencyCode(); - ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode); - if (currency == null) { - throw new CurrencyNotFoundException(currencyCode); - } + final String currencyCode = loan.repaymentScheduleDetail().getPrincipal().getCurrencyCode(); + ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode); + if (currency == null) { throw new CurrencyNotFoundException(currencyCode); } - final CurrencyData currencyData = new CurrencyData(currency.getCode(), - currency.getName(), currency.getDecimalPlaces(), - currency.getDisplaySymbol(), currency.getNameCode()); + final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), currency.getDecimalPlaces(), + currency.getDisplaySymbol(), currency.getNameCode()); - final LocalDate earliestUnpaidInstallmentDate = loan.possibleNextRepaymentDate(); - - final Money possibleNextRepaymentAmount = loan.possibleNextRepaymentAmount(); - final EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT); - return new LoanTransactionData(null, transactionType, currencyData, earliestUnpaidInstallmentDate, possibleNextRepaymentAmount.getAmount(), null, null, null, null); - } + final LocalDate earliestUnpaidInstallmentDate = loan.possibleNextRepaymentDate(); - @Override - public LoanTransactionData retrieveNewLoanWaiveInterestDetails(final Long loanId) { + final Money possibleNextRepaymentAmount = loan.possibleNextRepaymentAmount(); + final EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT); + return new LoanTransactionData(null, transactionType, currencyData, earliestUnpaidInstallmentDate, + possibleNextRepaymentAmount.getAmount(), null, null, null, null); + } - context.authenticatedUser(); + @Override + public LoanTransactionData retrieveWaiveInterestDetails(final Long loanId) { - // TODO - OPTIMIZE - write simple sql query to fetch back overdue interest that can be waived along with the date of repayment period interest is overdue. - final Loan loan = this.loanRepository.findOne(loanId); - if (loan == null) { - throw new LoanNotFoundException(loanId); - } + context.authenticatedUser(); - final String currencyCode = loan.repaymentScheduleDetail().getPrincipal().getCurrencyCode(); - final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode); - if (currency == null) { - throw new CurrencyNotFoundException(currencyCode); - } - - final CurrencyData currencyData = new CurrencyData(currency.getCode(), - currency.getName(), currency.getDecimalPlaces(), - currency.getDisplaySymbol(), currency.getNameCode()); + // TODO - OPTIMIZE - write simple sql query to fetch back overdue + // interest that can be waived along with the date of repayment period + // interest is overdue. + final Loan loan = this.loanRepository.findOne(loanId); + if (loan == null) { throw new LoanNotFoundException(loanId); } - final LoanTransaction waiveOfInterest = loan.deriveDefaultInterestWaiverTransaction(); + final String currencyCode = loan.repaymentScheduleDetail().getPrincipal().getCurrencyCode(); + final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode); + if (currency == null) { throw new CurrencyNotFoundException(currencyCode); } - final EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WAIVE_INTEREST); - - return new LoanTransactionData(null, transactionType, currencyData, waiveOfInterest.getTransactionDate(), waiveOfInterest.getAmount(), null, null, null, null); - } - - @Override - public LoanTransactionData retrieveNewClosureDetails() { - - context.authenticatedUser(); + final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), currency.getDecimalPlaces(), + currency.getDisplaySymbol(), currency.getNameCode()); - EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); - - return new LoanTransactionData(null, transactionType, null, new LocalDate(), null, null, null, null, null); - } + final LoanTransaction waiveOfInterest = loan.deriveDefaultInterestWaiverTransaction(); - @Override - public LoanTransactionData retrieveLoanTransactionDetails( - final Long loanId, final Long transactionId) { + final EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WAIVE_INTEREST); - context.authenticatedUser(); + return new LoanTransactionData(null, transactionType, currencyData, waiveOfInterest.getTransactionDate(), + waiveOfInterest.getAmount(), null, null, null, null); + } - final Loan loan = this.loanRepository.findOne(loanId); - if (loan == null) { - throw new LoanNotFoundException(loanId); - } + @Override + public LoanTransactionData retrieveNewClosureDetails() { - final String currencyCode = loan.repaymentScheduleDetail().getPrincipal().getCurrencyCode(); - final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode); - if (currency == null) { - throw new CurrencyNotFoundException(currencyCode); - } + context.authenticatedUser(); - final LoanTransaction transaction = this.loanTransactionRepository.findOne(transactionId); - if (transaction == null) { - throw new LoanTransactionNotFoundException(transactionId); - } + EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); - if (transaction.isNotBelongingToLoanOf(loan)) { - throw new LoanTransactionNotFoundException(transactionId, loanId); - } + return new LoanTransactionData(null, transactionType, null, new LocalDate(), null, null, null, null, null); + } - final CurrencyData currencyData = new CurrencyData(currency.getCode(), - currency.getName(), currency.getDecimalPlaces(), - currency.getDisplaySymbol(), currency.getNameCode()); - - return transaction.toData(currencyData); - } + @Override + public LoanTransactionData retrieveLoanTransaction(final Long loanId, final Long transactionId) { - private static final class LoanMapper implements - RowMapper { + context.authenticatedUser(); - public String loanSchema() { - return "l.id as id, l.external_id as externalId, l.fund_id as fundId, f.name as fundName, " - + " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription, c.id as clientId, c.display_name as clientName, " - + " c.office_id as clientOfficeId, g.id as groupId, g.name as groupName, g.office_id as groupOfficeId," - + " l.submittedon_date as submittedOnDate," - + " l.total_charges_due_at_disbursement_derived as totalDisbursementCharges," - + " l.approvedon_date as approvedOnDate, l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate," - + " l.interest_calculated_from_date as interestChargedFromDate, l.closedon_date as closedOnDate, l.expected_maturedon_date as expectedMaturityDate, " - + " l.principal_amount as principal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery," - + " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, " - + " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, " - + " l.term_frequency as termFrequency, l.term_period_frequency_enum as termPeriodFrequencyType, " - + " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.interest_calculated_in_period_enum as interestCalculationPeriodType," - + " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, " - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " - + " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName" - + " from m_loan l" - + " left join m_client c on c.id = l.client_id" - + " left join m_group g on g.id = l.group_id" - + " join m_product_loan lp on lp.id = l.product_id" - + " join m_currency rc on rc.`code` = l.currency_code" - + " left join m_fund f on f.id = l.fund_id" - + " left join m_staff s on s.id = l.loan_officer_id"; - } + final Loan loan = this.loanRepository.findOne(loanId); + if (loan == null) { throw new LoanNotFoundException(loanId); } - @Override - public LoanBasicDetailsData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) - throws SQLException { + final String currencyCode = loan.repaymentScheduleDetail().getPrincipal().getCurrencyCode(); + final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode); + if (currency == null) { throw new CurrencyNotFoundException(currencyCode); } - String currencyCode = rs.getString("currencyCode"); - String currencyName = rs.getString("currencyName"); - String currencyNameCode = rs.getString("currencyNameCode"); - String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - Integer currencyDigits = JdbcSupport.getInteger(rs,"currencyDigits"); - CurrencyData currencyData = new CurrencyData(currencyCode, - currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + final LoanTransaction transaction = this.loanTransactionRepository.findOne(transactionId); + if (transaction == null) { throw new LoanTransactionNotFoundException(transactionId); } - Long id = rs.getLong("id"); - String externalId = rs.getString("externalId"); + if (transaction.isNotBelongingToLoanOf(loan)) { throw new LoanTransactionNotFoundException(transactionId, loanId); } - Long clientId = JdbcSupport.getLong(rs, "clientId"); - Long clientOfficeId = JdbcSupport.getLong(rs, "clientOfficeId");; - String clientName = rs.getString("clientName"); + final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), currency.getDecimalPlaces(), + currency.getDisplaySymbol(), currency.getNameCode()); - Long groupId = JdbcSupport.getLong(rs, "groupId"); - Long groupOfficeId = JdbcSupport.getLong(rs, "groupOfficeId");; - String groupName = rs.getString("groupName"); + return transaction.toData(currencyData); + } - Long fundId = JdbcSupport.getLong(rs, "fundId"); - String fundName = rs.getString("fundName"); - Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId"); - String loanOfficerName = rs.getString("loanOfficerName"); - Long loanProductId = JdbcSupport.getLong(rs, "loanProductId"); - String loanProductName = rs.getString("loanProductName"); - String loanProductDescription = rs.getString("loanProductDescription"); - - LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, - "submittedOnDate"); - LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, - "approvedOnDate"); - LocalDate expectedDisbursementDate = JdbcSupport.getLocalDate(rs, - "expectedDisbursementDate"); - LocalDate actualDisbursementDate = JdbcSupport.getLocalDate(rs, - "actualDisbursementDate"); - LocalDate expectedFirstRepaymentOnDate = JdbcSupport.getLocalDate( - rs, "expectedFirstRepaymentOnDate"); - LocalDate interestChargedFromDate = JdbcSupport.getLocalDate(rs, - "interestChargedFromDate"); - LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, - "closedOnDate"); - LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, - "expectedMaturityDate"); + private static final class LoanMapper implements RowMapper { - BigDecimal principal = rs.getBigDecimal("principal"); - BigDecimal inArrearsTolerance = rs.getBigDecimal("inArrearsTolerance"); - BigDecimal totalDisbursementCharges = rs.getBigDecimal("totalDisbursementCharges"); + public String loanSchema() { + return "l.id as id, l.external_id as externalId, l.fund_id as fundId, f.name as fundName, " + + " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription, c.id as clientId, c.display_name as clientName, " + + " c.office_id as clientOfficeId, g.id as groupId, g.name as groupName, g.office_id as groupOfficeId," + + " l.submittedon_date as submittedOnDate," + + " l.total_charges_due_at_disbursement_derived as totalDisbursementCharges," + + " l.approvedon_date as approvedOnDate, l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate," + + " l.interest_calculated_from_date as interestChargedFromDate, l.closedon_date as closedOnDate, l.expected_maturedon_date as expectedMaturityDate, " + + " l.principal_amount as principal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery," + + " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, " + + " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, " + + " l.term_frequency as termFrequency, l.term_period_frequency_enum as termPeriodFrequencyType, " + + " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.interest_calculated_in_period_enum as interestCalculationPeriodType," + + " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName" + " from m_loan l" + + " left join m_client c on c.id = l.client_id" + " left join m_group g on g.id = l.group_id" + + " join m_product_loan lp on lp.id = l.product_id" + " join m_currency rc on rc.`code` = l.currency_code" + + " left join m_fund f on f.id = l.fund_id" + " left join m_staff s on s.id = l.loan_officer_id"; + } - Integer numberOfRepayments = JdbcSupport.getInteger(rs,"numberOfRepayments"); - Integer repaymentEvery = JdbcSupport.getInteger(rs,"repaymentEvery"); - BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod"); - BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate"); + @Override + public LoanBasicDetailsData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - Integer termFrequency = JdbcSupport.getInteger(rs,"termFrequency"); - Integer termPeriodFrequencyTypeInt = JdbcSupport.getInteger(rs,"termPeriodFrequencyType"); - EnumOptionData termPeriodFrequencyType = LoanEnumerations.termFrequencyType(termPeriodFrequencyTypeInt); - - int repaymentFrequencyTypeInt = JdbcSupport.getInteger(rs,"repaymentFrequencyType"); - EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeInt); - - int interestRateFrequencyTypeInt = JdbcSupport.getInteger(rs,"interestRateFrequencyType"); - EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeInt); + String currencyCode = rs.getString("currencyCode"); + String currencyName = rs.getString("currencyName"); + String currencyNameCode = rs.getString("currencyNameCode"); + String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, + currencyNameCode); - Integer transactionStrategyId = JdbcSupport.getInteger(rs,"transactionStrategyId"); - - int amortizationTypeInt = JdbcSupport.getInteger(rs,"amortizationType"); - int interestTypeInt = JdbcSupport.getInteger(rs, "interestType"); - int interestCalculationPeriodTypeInt = JdbcSupport.getInteger(rs,"interestCalculationPeriodType"); - - EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeInt); - EnumOptionData interestType = LoanEnumerations.interestType(interestTypeInt); - EnumOptionData interestCalculationPeriodType = LoanEnumerations.interestCalculationPeriodType(interestCalculationPeriodTypeInt); + Long id = rs.getLong("id"); + String externalId = rs.getString("externalId"); - Integer lifeCycleStatusId = JdbcSupport.getInteger(rs, "lifeCycleStatusId"); - EnumOptionData status = LoanEnumerations.status(lifeCycleStatusId); - - LocalDate lifeCycleStatusDate = submittedOnDate; - if (approvedOnDate != null) { - lifeCycleStatusDate = approvedOnDate; - } - if (actualDisbursementDate != null) { - lifeCycleStatusDate = actualDisbursementDate; - } - if (closedOnDate != null) { - lifeCycleStatusDate = closedOnDate; - } + Long clientId = JdbcSupport.getLong(rs, "clientId"); + Long clientOfficeId = JdbcSupport.getLong(rs, "clientOfficeId"); + ; + String clientName = rs.getString("clientName"); - Collection charges = null; - return new LoanBasicDetailsData(id, externalId, - clientId, clientName, clientOfficeId, - groupId, groupName, groupOfficeId, - loanProductId, loanProductName, loanProductDescription, - fundId, fundName, - closedOnDate, - submittedOnDate, approvedOnDate, expectedDisbursementDate, - actualDisbursementDate, expectedMaturityDate, - expectedFirstRepaymentOnDate, interestChargedFromDate, - currencyData, - principal, inArrearsTolerance, numberOfRepayments, - repaymentEvery, interestRatePerPeriod, annualInterestRate, - repaymentFrequencyType, interestRateFrequencyType, - amortizationType, interestType, - interestCalculationPeriodType, - status, - lifeCycleStatusDate, termFrequency, termPeriodFrequencyType, transactionStrategyId, charges, - loanOfficerId, loanOfficerName, totalDisbursementCharges); - } - } + Long groupId = JdbcSupport.getLong(rs, "groupId"); + Long groupOfficeId = JdbcSupport.getLong(rs, "groupOfficeId"); + ; + String groupName = rs.getString("groupName"); - private static final class LoanScheduleMapper implements RowMapper { + Long fundId = JdbcSupport.getLong(rs, "fundId"); + String fundName = rs.getString("fundName"); + Long loanOfficerId = JdbcSupport.getLong(rs, "loanOfficerId"); + String loanOfficerName = rs.getString("loanOfficerName"); + Long loanProductId = JdbcSupport.getLong(rs, "loanProductId"); + String loanProductName = rs.getString("loanProductName"); + String loanProductDescription = rs.getString("loanProductDescription"); - private LocalDate lastDueDate; - private BigDecimal outstandingLoanPrincipalBalance; - - public LoanScheduleMapper(final DisbursementData disbursement) { - this.lastDueDate = disbursement.disbursementDate(); - this.outstandingLoanPrincipalBalance = disbursement.amount(); - } + LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate"); + LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate"); + LocalDate expectedDisbursementDate = JdbcSupport.getLocalDate(rs, "expectedDisbursementDate"); + LocalDate actualDisbursementDate = JdbcSupport.getLocalDate(rs, "actualDisbursementDate"); + LocalDate expectedFirstRepaymentOnDate = JdbcSupport.getLocalDate(rs, "expectedFirstRepaymentOnDate"); + LocalDate interestChargedFromDate = JdbcSupport.getLocalDate(rs, "interestChargedFromDate"); + LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate"); + LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate"); - public String loanScheduleSchema() { + BigDecimal principal = rs.getBigDecimal("principal"); + BigDecimal inArrearsTolerance = rs.getBigDecimal("inArrearsTolerance"); + BigDecimal totalDisbursementCharges = rs.getBigDecimal("totalDisbursementCharges"); - return " ls.loan_id as loanId, ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, " - + " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, " - + " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff, " - + " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, " - + " ls.penalty_charges_amount as penaltyChargesDue, ls.penalty_charges_completed_derived as penaltyChargesPaid, ls.penalty_charges_waived_derived as penaltyChargesWaived, ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff " - + " from m_loan l " - + " join m_loan_repayment_schedule ls on ls.loan_id = l.id "; - } + Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments"); + Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaymentEvery"); + BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod"); + BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate"); - @Override - public LoanSchedulePeriodData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + Integer termFrequency = JdbcSupport.getInteger(rs, "termFrequency"); + Integer termPeriodFrequencyTypeInt = JdbcSupport.getInteger(rs, "termPeriodFrequencyType"); + EnumOptionData termPeriodFrequencyType = LoanEnumerations.termFrequencyType(termPeriodFrequencyTypeInt); - final Long loanId = rs.getLong("loanId"); - final Integer period = JdbcSupport.getInteger(rs, "period"); - LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate"); - final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); - final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue"); - final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid"); - final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff"); - - // TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns? - final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff); - - final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue"); - final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid"); - final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived"); - final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff"); - - // TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns? - final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff); - final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid); - - final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue"); - final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid"); - final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived"); - final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff"); - - final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff); - final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid); - - final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesDue"); - final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesPaid"); - final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWaived"); - final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWrittenOff"); - - final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived).subtract(penaltyChargesWrittenOff); - final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid); + int repaymentFrequencyTypeInt = JdbcSupport.getInteger(rs, "repaymentFrequencyType"); + EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeInt); - final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue).add(penaltyChargesExpectedDue); - - final BigDecimal totalDueForPeriod = principalDue.add(totalExpectedCostOfLoanForPeriod); - final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid).add(penaltyChargesPaid); - final BigDecimal totalWaivedForPeriod = interestWaived.add(feeChargesWaived).add(penaltyChargesWaived); - final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff).add(penaltyChargesWrittenOff); - final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding).add(penaltyChargesOutstanding); - - final BigDecimal totalActualCostOfLoanForPeriod = interestActualDue.add(feeChargesActualDue).add(penaltyChargesActualDue); - - if (fromDate == null) { - fromDate = this.lastDueDate; - } - final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue); - - // update based on current period values - this.lastDueDate = dueDate; - outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue); - - return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate, - principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan, - interestExpectedDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, - feeChargesExpectedDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, - penaltyChargesExpectedDue, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, - totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod, totalActualCostOfLoanForPeriod); - } - } + int interestRateFrequencyTypeInt = JdbcSupport.getInteger(rs, "interestRateFrequencyType"); + EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeInt); - private static final class LoanTransactionsMapper implements RowMapper { + Integer transactionStrategyId = JdbcSupport.getInteger(rs, "transactionStrategyId"); - public String LoanPaymentsSchema() { + int amortizationTypeInt = JdbcSupport.getInteger(rs, "amortizationType"); + int interestTypeInt = JdbcSupport.getInteger(rs, "interestType"); + int interestCalculationPeriodTypeInt = JdbcSupport.getInteger(rs, "interestCalculationPeriodType"); - return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, " - + "tr.principal_portion_derived as principal, " - + "tr.interest_portion_derived as interest, " - + "tr.fee_charges_portion_derived as fees, " - + "tr.penalty_charges_portion_derived as penalties, " - + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, " - + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode " - + " from m_loan l " - + " join m_loan_transaction tr on tr.loan_id = l.id" - + " join m_currency rc on rc.`code` = l.currency_code "; - } + EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeInt); + EnumOptionData interestType = LoanEnumerations.interestType(interestTypeInt); + EnumOptionData interestCalculationPeriodType = LoanEnumerations.interestCalculationPeriodType(interestCalculationPeriodTypeInt); - @Override - public LoanTransactionData mapRow(final ResultSet rs, - @SuppressWarnings("unused") final int rowNum) throws SQLException { + Integer lifeCycleStatusId = JdbcSupport.getInteger(rs, "lifeCycleStatusId"); + EnumOptionData status = LoanEnumerations.status(lifeCycleStatusId); - final String currencyCode = rs.getString("currencyCode"); - final String currencyName = rs.getString("currencyName"); - final String currencyNameCode = rs.getString("currencyNameCode"); - final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final CurrencyData currencyData = new CurrencyData(currencyCode, - currencyName, currencyDigits, currencyDisplaySymbol, - currencyNameCode); + LocalDate lifeCycleStatusDate = submittedOnDate; + if (approvedOnDate != null) { + lifeCycleStatusDate = approvedOnDate; + } + if (actualDisbursementDate != null) { + lifeCycleStatusDate = actualDisbursementDate; + } + if (closedOnDate != null) { + lifeCycleStatusDate = closedOnDate; + } - final Long id = rs.getLong("id"); - final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); - final EnumOptionData transactionType = LoanEnumerations.transactionType(transactionTypeInt); - final LocalDate date = JdbcSupport.getLocalDate(rs, "date"); - final BigDecimal totalAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "total"); - final BigDecimal principalPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principal"); - final BigDecimal interestPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interest"); - final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); - final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); - - return new LoanTransactionData(id, transactionType, currencyData, date, totalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - } - } + Collection charges = null; + return new LoanBasicDetailsData(id, externalId, clientId, clientName, clientOfficeId, groupId, groupName, groupOfficeId, + loanProductId, loanProductName, loanProductDescription, fundId, fundName, closedOnDate, submittedOnDate, + approvedOnDate, expectedDisbursementDate, actualDisbursementDate, expectedMaturityDate, expectedFirstRepaymentOnDate, + interestChargedFromDate, currencyData, principal, inArrearsTolerance, numberOfRepayments, repaymentEvery, + interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, + interestType, interestCalculationPeriodType, status, lifeCycleStatusDate, termFrequency, termPeriodFrequencyType, + transactionStrategyId, charges, loanOfficerId, loanOfficerName, totalDisbursementCharges); + } + } + + private static final class LoanScheduleMapper implements RowMapper { + + private LocalDate lastDueDate; + private BigDecimal outstandingLoanPrincipalBalance; + + public LoanScheduleMapper(final DisbursementData disbursement) { + this.lastDueDate = disbursement.disbursementDate(); + this.outstandingLoanPrincipalBalance = disbursement.amount(); + } + + public String loanScheduleSchema() { + + return " ls.loan_id as loanId, ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, " + + " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, " + + " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff, " + + " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, " + + " ls.penalty_charges_amount as penaltyChargesDue, ls.penalty_charges_completed_derived as penaltyChargesPaid, ls.penalty_charges_waived_derived as penaltyChargesWaived, ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff " + + " from m_loan l " + " join m_loan_repayment_schedule ls on ls.loan_id = l.id "; + } + + @Override + public LoanSchedulePeriodData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long loanId = rs.getLong("loanId"); + final Integer period = JdbcSupport.getInteger(rs, "period"); + LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate"); + final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); + final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue"); + final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid"); + final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff"); + + // TODO - KW - rather than calculate this here should we put + // calculation on derived column on table like other columns? + final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff); + + final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue"); + final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid"); + final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived"); + final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff"); + + // TODO - KW - rather than calculate this here should we put + // calculation on derived column on table like other columns? + final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff); + final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid); + + final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue"); + final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid"); + final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived"); + final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff"); + + final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff); + final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid); + + final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesDue"); + final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesPaid"); + final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWaived"); + final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWrittenOff"); + + final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived).subtract( + penaltyChargesWrittenOff); + final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid); + + final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue).add( + penaltyChargesExpectedDue); + + final BigDecimal totalDueForPeriod = principalDue.add(totalExpectedCostOfLoanForPeriod); + final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid).add(penaltyChargesPaid); + final BigDecimal totalWaivedForPeriod = interestWaived.add(feeChargesWaived).add(penaltyChargesWaived); + final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff) + .add(penaltyChargesWrittenOff); + final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding) + .add(penaltyChargesOutstanding); + + final BigDecimal totalActualCostOfLoanForPeriod = interestActualDue.add(feeChargesActualDue).add(penaltyChargesActualDue); + + if (fromDate == null) { + fromDate = this.lastDueDate; + } + final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue); + + // update based on current period values + this.lastDueDate = dueDate; + outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue); + + return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate, principalDue, principalPaid, + principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan, interestExpectedDue, interestPaid, + interestWaived, interestWrittenOff, interestOutstanding, feeChargesExpectedDue, feeChargesPaid, feeChargesWaived, + feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesExpectedDue, penaltyChargesPaid, penaltyChargesWaived, + penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, + totalWrittenOffForPeriod, totalOutstandingForPeriod, totalActualCostOfLoanForPeriod); + } + } + + private static final class LoanTransactionsMapper implements RowMapper { + + public String LoanPaymentsSchema() { + + return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, " + + "tr.principal_portion_derived as principal, " + "tr.interest_portion_derived as interest, " + + "tr.fee_charges_portion_derived as fees, " + "tr.penalty_charges_portion_derived as penalties, " + + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, " + + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode " + + " from m_loan l " + " join m_loan_transaction tr on tr.loan_id = l.id" + + " join m_currency rc on rc.`code` = l.currency_code "; + } + + @Override + public LoanTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol, + currencyNameCode); + + final Long id = rs.getLong("id"); + final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); + final EnumOptionData transactionType = LoanEnumerations.transactionType(transactionTypeInt); + final LocalDate date = JdbcSupport.getLocalDate(rs, "date"); + final BigDecimal totalAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "total"); + final BigDecimal principalPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principal"); + final BigDecimal interestPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interest"); + final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); + final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); + + return new LoanTransactionData(id, transactionType, currencyData, date, totalAmount, principalPortion, interestPortion, + feeChargesPortion, penaltyChargesPortion); + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java index 80d0c3d49..c6dcc1559 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java @@ -3,9 +3,7 @@ package org.mifosplatform.portfolio.loanaccount.service; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.EntityIdentifier; import org.mifosplatform.organisation.staff.command.BulkTransferLoanOfficerCommand; -import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; -import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand; import org.springframework.security.access.prepost.PreAuthorize; public interface LoanWritePlatformService { @@ -26,25 +24,19 @@ public interface LoanWritePlatformService { EntityIdentifier disburseLoan(Long loanId, JsonCommand command); - public EntityIdentifier undoLoanDisbursal(Long loanId, JsonCommand command); + EntityIdentifier undoLoanDisbursal(Long loanId, JsonCommand command); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'REPAYMENT_LOAN', 'REPAYMENTINPAST_LOAN')") - public EntityIdentifier makeLoanRepayment(LoanTransactionCommand command); + EntityIdentifier makeLoanRepayment(Long loanId, JsonCommand command); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'ADJUST_LOAN')") - EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command); + EntityIdentifier adjustLoanTransaction(Long loanId, Long transactionId, JsonCommand command); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WAIVEINTERESTPORTION_LOAN')") - EntityIdentifier waiveInterestOnLoan(LoanTransactionCommand command); + EntityIdentifier waiveInterestOnLoan(Long loanId, JsonCommand command); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'WRITEOFF_LOAN')") - EntityIdentifier writeOff(LoanTransactionCommand command); + EntityIdentifier writeOff(Long loanId, JsonCommand command); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CLOSE_LOAN')") - EntityIdentifier closeLoan(LoanTransactionCommand command); + EntityIdentifier closeLoan(Long loanId, JsonCommand command); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CLOSEASRESCHEDULED_LOAN')") - EntityIdentifier closeAsRescheduled(LoanTransactionCommand command); + EntityIdentifier closeAsRescheduled(Long loanId, JsonCommand command); @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_LOANCHARGE')") EntityIdentifier addLoanCharge(LoanChargeCommand command); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index f2c2f5680..48dd2e9ed 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -1,6 +1,8 @@ package org.mifosplatform.portfolio.loanaccount.service; +import java.math.BigDecimal; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -15,8 +17,6 @@ import org.mifosplatform.infrastructure.security.exception.NoAuthorizationExcept import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepository; -import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; -import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.staff.command.BulkTransferLoanOfficerCommand; import org.mifosplatform.organisation.staff.command.BulkTransferLoanOfficerCommandValidator; import org.mifosplatform.organisation.staff.domain.Staff; @@ -31,14 +31,11 @@ import org.mifosplatform.portfolio.client.domain.Note; import org.mifosplatform.portfolio.client.domain.NoteRepository; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.fund.domain.Fund; -import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommand; -import org.mifosplatform.portfolio.loanaccount.command.AdjustLoanTransactionCommandValidator; import org.mifosplatform.portfolio.loanaccount.command.LoanApplicationCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommandValidator; import org.mifosplatform.portfolio.loanaccount.command.LoanStateTransitionCommand; import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand; -import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommandValidator; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; @@ -59,6 +56,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanSchedule import org.mifosplatform.portfolio.loanaccount.serialization.CalculateLoanScheduleQueryFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanStateTransitionCommandFromApiJsonDeserializer; +import org.mifosplatform.portfolio.loanaccount.serialization.LoanTransactionCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository; import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy; @@ -79,6 +77,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanApplicationCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final CalculateLoanScheduleQueryFromApiJsonDeserializer calculateLoanScheduleQueryFromApiJsonDeserializer; private final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer; + private final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer; private final LoanRepository loanRepository; private final NoteRepository noteRepository; private final LoanScheduleCalculationPlatformService calculationPlatformService; @@ -96,6 +95,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper, final LoanApplicationCommandFromApiJsonDeserializer fromApiJsonDeserializer, final LoanStateTransitionCommandFromApiJsonDeserializer loanStateTransitionCommandFromApiJsonDeserializer, + final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer, final CalculateLoanScheduleQueryFromApiJsonDeserializer calculateLoanScheduleQueryFromApiJsonDeserializer, final AprCalculator aprCalculator, final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler, final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository, @@ -107,6 +107,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.fromJsonHelper = fromJsonHelper; this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.loanStateTransitionCommandFromApiJsonDeserializer = loanStateTransitionCommandFromApiJsonDeserializer; + this.loanTransactionCommandFromApiJsonDeserializer = loanTransactionCommandFromApiJsonDeserializer; this.calculateLoanScheduleQueryFromApiJsonDeserializer = calculateLoanScheduleQueryFromApiJsonDeserializer; this.aprCalculator = aprCalculator; this.loanAssembler = loanAssembler; @@ -412,179 +413,222 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override - public EntityIdentifier makeLoanRepayment(final LoanTransactionCommand command) { + public EntityIdentifier makeLoanRepayment(final Long loanId, final JsonCommand command) { - AppUser currentUser = context.authenticatedUser(); + final AppUser currentUser = context.authenticatedUser(); - LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command); - validator.validate(); + final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command + .json()); + loanTransactionCommand.validate(); + + final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + + final Map changes = new LinkedHashMap(); + changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); + changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount")); + + final Loan loan = retrieveLoanBy(loanId); - Loan loan = this.loanRepository.findOne(command.getLoanId()); - if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); } - - LocalDate transactionDate = command.getTransactionDate(); if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) { throw new NoAuthorizationException( "error.msg.no.permission.to.make.repayment.on.loan.in.past"); } - Money repayment = Money.of(loan.repaymentScheduleDetail().getPrincipal().getCurrency(), command.getTransactionAmount()); - - LoanTransaction loanRepayment = LoanTransaction.repayment(repayment, transactionDate); - loan.makeRepayment(loanRepayment, defaultLoanLifecycleStateMachine()); + final LoanTransaction loanRepayment = loan.makeRepayment(transactionDate, transactionAmount, defaultLoanLifecycleStateMachine()); this.loanTransactionRepository.save(loanRepayment); this.loanRepository.save(loan); - String noteText = command.getNote(); + final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { + changes.put("note", noteText); Note note = Note.loanTransactionNote(loan, loanRepayment, noteText); this.noteRepository.save(note); } + + changes.put("locale", command.locale()); + changes.put("dateFormat", command.dateFormat()); - return new EntityIdentifier(loan.getId()); + return EntityIdentifier.subResourceResult(loanId, loanRepayment.getId(), command.commandId(), changes); } @Transactional @Override - public EntityIdentifier adjustLoanTransaction(final AdjustLoanTransactionCommand command) { + public EntityIdentifier adjustLoanTransaction(final Long loanId, final Long transactionId, final JsonCommand command) { context.authenticatedUser(); - AdjustLoanTransactionCommandValidator validator = new AdjustLoanTransactionCommandValidator(command); - validator.validate(); + final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command + .json()); + loanTransactionCommand.validate(); + + final Loan loan = retrieveLoanBy(loanId); - Loan loan = this.loanRepository.findOne(command.getLoanId()); - if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); } + final LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(transactionId); + if (transactionToAdjust == null) { throw new LoanTransactionNotFoundException(transactionId); } - LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(command.getTransactionId()); - if (transactionToAdjust == null) { throw new LoanTransactionNotFoundException(command.getTransactionId()); } - - final MonetaryCurrency currency = loan.repaymentScheduleDetail().getPrincipal().getCurrency(); - final Money transactionAmount = Money.of(currency, command.getTransactionAmount()); - - // adjustment is only supported for repayments and waivers at present - LocalDate transactionDate = command.getTransactionDate(); - LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate); - if (transactionToAdjust.isInterestWaiver()) { - newTransactionDetail = LoanTransaction.waiver(loan, transactionAmount, transactionDate); - } - - loan.adjustExistingTransaction(transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine()); - - if (newTransactionDetail.isGreaterThanZero(currency)) { + final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); + + final Map changes = new LinkedHashMap(); + changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); + changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount")); + + final LoanTransaction newTransactionDetail = loan.adjustExistingTransaction(transactionDate, transactionAmount, defaultLoanLifecycleStateMachine(), + transactionToAdjust); + if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { this.loanTransactionRepository.save(newTransactionDetail); } this.loanRepository.save(loan); - String noteText = command.getNote(); + final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { + changes.put("note", noteText); Note note = Note.loanTransactionNote(loan, newTransactionDetail, noteText); this.noteRepository.save(note); } - return new EntityIdentifier(loan.getId()); + changes.put("locale", command.locale()); + changes.put("dateFormat", command.dateFormat()); + + return EntityIdentifier.subResourceResult(loanId, transactionId, command.commandId(), changes); } @Transactional @Override - public EntityIdentifier waiveInterestOnLoan(final LoanTransactionCommand command) { + public EntityIdentifier waiveInterestOnLoan(final Long loanId, final JsonCommand command) { context.authenticatedUser(); - final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command); - validator.validate(); + final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command + .json()); + loanTransactionCommand.validate(); + + final Map changes = new LinkedHashMap(); + changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); + changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount")); - final Loan loan = this.loanRepository.findOne(command.getLoanId()); - if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); } + final Loan loan = retrieveLoanBy(loanId); - final LoanTransaction waiveTransaction = loan.waiveInterest(command.getTransactionAmount(), command.getTransactionDate(), - defaultLoanLifecycleStateMachine()); + final LoanTransaction waiveTransaction = loan.waiveInterest(command, defaultLoanLifecycleStateMachine()); this.loanTransactionRepository.save(waiveTransaction); this.loanRepository.save(loan); - final String noteText = command.getNote(); + final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { + changes.put("note", noteText); final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText); this.noteRepository.save(note); } + + changes.put("locale", command.locale()); + changes.put("dateFormat", command.dateFormat()); - return new EntityIdentifier(loan.getId()); + return EntityIdentifier.subResourceResult(loanId, waiveTransaction.getId(), command.commandId(), changes); } @Transactional @Override - public EntityIdentifier writeOff(final LoanTransactionCommand command) { + public EntityIdentifier writeOff(final Long loanId, final JsonCommand command) { context.authenticatedUser(); - final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command); - validator.validateNonMonetaryTransaction(); + final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command + .json()); + loanTransactionCommand.validateNonMonetaryTransaction(); + + final Map changes = new LinkedHashMap(); + changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); - final Loan loan = this.loanRepository.findOne(command.getLoanId()); - if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); } + final Loan loan = retrieveLoanBy(loanId); - final LoanTransaction writeoff = loan.closeAsWrittenOff(command.getTransactionDate(), defaultLoanLifecycleStateMachine()); + final LoanTransaction writeoff = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine(), changes); this.loanTransactionRepository.save(writeoff); this.loanRepository.save(loan); - final String noteText = command.getNote(); + final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { + changes.put("note", noteText); final Note note = Note.loanTransactionNote(loan, writeoff, noteText); this.noteRepository.save(note); } - return new EntityIdentifier(loan.getId()); + changes.put("locale", command.locale()); + changes.put("dateFormat", command.dateFormat()); + + return EntityIdentifier.subResourceResult(loanId, writeoff.getId(), command.commandId(), changes); } @Transactional @Override - public EntityIdentifier closeLoan(final LoanTransactionCommand command) { + public EntityIdentifier closeLoan(final Long loanId, final JsonCommand command) { context.authenticatedUser(); - final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command); - validator.validateNonMonetaryTransaction(); + final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command + .json()); + loanTransactionCommand.validateNonMonetaryTransaction(); - final Loan loan = this.loanRepository.findOne(command.getLoanId()); - if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); } + final Loan loan = retrieveLoanBy(loanId); + + final Map changes = new LinkedHashMap(); + changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); - final LoanTransaction possibleClosingTransaction = loan.close(command.getTransactionDate(), defaultLoanLifecycleStateMachine()); + final LoanTransaction possibleClosingTransaction = loan.close(command, defaultLoanLifecycleStateMachine(), changes); if (possibleClosingTransaction != null) { this.loanTransactionRepository.save(possibleClosingTransaction); } this.loanRepository.save(loan); - final String noteText = command.getNote(); + final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { + changes.put("note", noteText); final Note note = Note.loanNote(loan, noteText); this.noteRepository.save(note); } - - return new EntityIdentifier(loan.getId()); + + changes.put("locale", command.locale()); + changes.put("dateFormat", command.dateFormat()); + + EntityIdentifier result = null; + if (possibleClosingTransaction != null) { + result = EntityIdentifier.subResourceResult(loanId, possibleClosingTransaction.getId(), command.commandId(), changes); + } else { + result = EntityIdentifier.resourceResult(loanId, command.commandId(), changes); + } + + return result; } @Transactional @Override - public EntityIdentifier closeAsRescheduled(final LoanTransactionCommand command) { + public EntityIdentifier closeAsRescheduled(final Long loanId, final JsonCommand command) { context.authenticatedUser(); - final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command); - validator.validateNonMonetaryTransaction(); + final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command + .json()); + loanTransactionCommand.validateNonMonetaryTransaction(); - final Loan loan = this.loanRepository.findOne(command.getLoanId()); - if (loan == null) { throw new LoanNotFoundException(command.getLoanId()); } + final Loan loan = retrieveLoanBy(loanId); + + final Map changes = new LinkedHashMap(); + changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate")); - loan.closeAsMarkedForReschedule(command.getTransactionDate(), defaultLoanLifecycleStateMachine()); + loan.closeAsMarkedForReschedule(command, defaultLoanLifecycleStateMachine(), changes); this.loanRepository.save(loan); - final String noteText = command.getNote(); + final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { + changes.put("note", noteText); final Note note = Note.loanNote(loan, noteText); this.noteRepository.save(note); } + + changes.put("locale", command.locale()); + changes.put("dateFormat", command.dateFormat()); - return new EntityIdentifier(loan.getId()); + return EntityIdentifier.resourceResult(loanId, command.commandId(), changes); } @Transactional