Hi all The main reason we put in the restrictions around allowing only one reschedule, was to also enable users to undo them easily if they were made by mistake. I think that is something that can be solved, but would require a bit of extra work ensuring that the correct old schedules are grabbed and reapplied to the loan.
S Sander van der Heyden CTO Musoni Services Mobile (NL): +31 (0)6 14239505 Skype: s.vdheyden Website: musonisystem.com Follow us on Twitter! <https://twitter.com/musonimfi> Postal address: Hillegomstraat 12-14, office 0.09, 1058 LS, Amsterdam, The Netherlands On Fri, Apr 8, 2016 at 5:46 PM, Ed Cable <edca...@mifos.org> wrote: > Sander, have you had a chance to review this thread? > > Andris' team is in need of this feature and wanted to get feedback on the > approach they've taken to see if they can continue with that or they need > to follow the path that was proposed by Pramod. > > Ed > > On Wed, Apr 6, 2016 at 9:38 AM, Ed Cable <edca...@mifos.org> wrote: > >> Zack and Robert have been working on contributing a fix to add the >> ability to reschedule a loan multiple times. >> >> They have taken a different approach than what Pramod had previously >> outlined so we wanted to discuss their proposed fix with Sander and his >> team who have provided the initial fix to reschedule a loan a single time. >> >> Ed >> >> On Mon, Apr 4, 2016 at 9:32 PM, Adi Raju < >> adi.r...@confluxtechnologies.com >> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBYWRpLnJhanUlNDBjb25mbHV4dGVjaG5vbG9naWVzLmNvbQ==> >> > wrote: >> >>> Hi Robert, >>> >>> >>> >>> Validator classes generally only perform API parameter validations, in >>> other words they are the first check point before proceeding to more >>> costlier DB or calculation tasks. >>> >>> All that is done in this change is that the validation at the first >>> check point is removed. >>> >>> These checkpoints were added by earlier developers because they haven’t >>> addressed those scenarios in further calculations. >>> >>> If the core code works for multi-reschedule, they wouldn’t have put this >>> check in the first place. >>> >>> >>> >>> I really doubt this solution is working as it is supposed to be. Have >>> you been able to test it against expected schedule and its values post >>> reschedule action? Does other like retrieve/approve/reject reschedule APIs >>> work with this solution? >>> >>> >>> >>> +Sander, who can help us with more clarifications on why such >>> validations were added. >>> >>> >>> >>> Regards, >>> >>> Adi >>> >>> >>> >>> >>> >>> *From:* robert wizglobal [mailto:rob...@wizglobal.co.ke >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBcm9iZXJ0JTQwd2l6Z2xvYmFsLmNvLmtl>] >>> >>> *Sent:* 04 April 2016 22:13 >>> *To:* Adi Raju <adi.r...@confluxtechnologies.com >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBYWRpLnJhanUlNDBjb25mbHV4dGVjaG5vbG9naWVzLmNvbQ==> >>> > >>> *Cc:* Zack Wizglobal <z...@wizglobal.co.ke >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBemFjayU0MHdpemdsb2JhbC5jby5rZQ==>>; >>> Ed Cable <edca...@mifos.org >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBZWRjYWJsZSU0MG1pZm9zLm9yZw==>>; >>> pra...@confluxtechnologies.com >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBcHJhbW9kJTQwY29uZmx1eHRlY2hub2xvZ2llcy5jb20=>; >>> Agris Varpins <agris.varp...@mtgcapital.ch >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBYWdyaXMudmFycGlucyU0MG10Z2NhcGl0YWwuY2g=>>; >>> Andris Kaneps <akan...@mtgcapital.ch >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBYWthbmVwcyU0MG10Z2NhcGl0YWwuY2g=>>; >>> Philippe Storm <pst...@watucredit.com >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBcHN0b3JtJTQwd2F0dWNyZWRpdC5jb20=> >>> > >>> *Subject:* Re: Mifos fix >>> >>> >>> >>> Hello Adi >>> >>> >>> >>> It Was Not a Major Fix Below is the Change i did on the >>> *LoanRescheduleRequestDataValidator >>> Class* >>> >>> >>> >>> /** >>> >>> * Licensed to the Apache Software Foundation (ASF) under one >>> >>> * or more contributor license agreements. See the NOTICE file >>> >>> * distributed with this work for additional information >>> >>> * regarding copyright ownership. The ASF licenses this file >>> >>> * to you under the Apache License, Version 2.0 (the >>> >>> * "License"); you may not use this file except in compliance >>> >>> * with the License. You may obtain a copy of the License at >>> >>> * >>> >>> * http://www.apache.org/licenses/LICENSE-2.0 >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRnd3dy5hcGFjaGUub3JnJTJGbGljZW5zZXMlMkZMSUNFTlNFLTIuMA==> >>> >>> * >>> >>> * Unless required by applicable law or agreed to in writing, >>> >>> * software distributed under the License is distributed on an >>> >>> * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY >>> >>> * KIND, either express or implied. See the License for the >>> >>> * specific language governing permissions and limitations >>> >>> * under the License. >>> >>> */ >>> >>> package org.apache.fineract.portfolio.loanaccount.rescheduleloan.data; >>> >>> >>> >>> import java.lang.reflect.Type; >>> >>> import java.util.ArrayList; >>> >>> import java.util.List; >>> >>> import java.util.Map; >>> >>> >>> >>> import org.apache.commons.lang.StringUtils; >>> >>> import org.apache.fineract.infrastructure.core.api.JsonCommand; >>> >>> import org.apache.fineract.infrastructure.core.data.ApiParameterError; >>> >>> import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; >>> >>> import >>> org.apache.fineract.infrastructure.core.exception.InvalidJsonException; >>> >>> import >>> org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; >>> >>> import >>> org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; >>> >>> import org.apache.fineract.portfolio.loanaccount.domain.Loan; >>> >>> import >>> org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; >>> >>> import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; >>> >>> import >>> org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants; >>> >>> import >>> org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; >>> >>> import >>> org.apache.fineract.portfolio.loanaccount.rescheduleloan.service.LoanRescheduleRequestReadPlatformService; >>> >>> import org.joda.time.LocalDate; >>> >>> import org.springframework.beans.factory.annotation.Autowired; >>> >>> import org.springframework.stereotype.Component; >>> >>> >>> >>> import com.google >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRmNvbS5nb29nbGU=> >>> .gson.JsonElement; >>> >>> import com.google >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRmNvbS5nb29nbGU=> >>> .gson.reflect.TypeToken; >>> >>> >>> >>> @Component >>> >>> public class LoanRescheduleRequestDataValidator { >>> >>> >>> >>> private final FromJsonHelper fromJsonHelper; >>> >>> private final LoanRescheduleRequestReadPlatformService >>> loanRescheduleRequestReadPlatformService; >>> >>> >>> >>> @Autowired >>> >>> public LoanRescheduleRequestDataValidator(FromJsonHelper >>> fromJsonHelper, >>> >>> LoanRescheduleRequestReadPlatformService >>> loanRescheduleRequestReadPlatformService) { >>> >>> this.fromJsonHelper = fromJsonHelper; >>> >>> this.loanRescheduleRequestReadPlatformService = >>> loanRescheduleRequestReadPlatformService; >>> >>> } >>> >>> >>> >>> /** >>> >>> * Validates the request to create a new loan reschedule entry >>> >>> * >>> >>> * @param jsonCommand >>> >>> * the JSON command object (instance of the JsonCommand >>> class) >>> >>> * @return void >>> >>> **/ >>> >>> public void validateForCreateAction(final JsonCommand jsonCommand, >>> final Loan loan) { >>> >>> >>> >>> final String jsonString = jsonCommand.json(); >>> >>> >>> >>> if (StringUtils.isBlank(jsonString)) { throw new >>> InvalidJsonException(); } >>> >>> >>> >>> final Type typeToken = new TypeToken<Map<String, Object>>() >>> {}.getType(); >>> >>> this.fromJsonHelper >>> >>> .checkForUnsupportedParameters(typeToken, jsonString, >>> RescheduleLoansApiConstants.CREATE_REQUEST_DATA_PARAMETERS); >>> >>> >>> >>> final List<ApiParameterError> dataValidationErrors = new >>> ArrayList<>(); >>> >>> final DataValidatorBuilder dataValidatorBuilder = new >>> DataValidatorBuilder(dataValidationErrors).resource(StringUtils >>> >>> .lowerCase(RescheduleLoansApiConstants.ENTITY_NAME)); >>> >>> >>> >>> final JsonElement jsonElement = jsonCommand.parsedJson(); >>> >>> >>> >>> if (!loan.status().isActive()) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.is.not.active", >>> "Loan is not active"); >>> >>> } >>> >>> >>> >>> final Long loanId = >>> this.fromJsonHelper.extractLongNamed(RescheduleLoansApiConstants.loanIdParamName, >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.loanIdParamName).value(loanId).notNull() >>> >>> .integerGreaterThanZero(); >>> >>> >>> >>> final LocalDate submittedOnDate = >>> this.fromJsonHelper.extractLocalDateNamed(RescheduleLoansApiConstants.submittedOnDateParamName, >>> >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.submittedOnDateParamName).value(submittedOnDate).notNull(); >>> >>> >>> >>> if (submittedOnDate != null && >>> loan.getDisbursementDate().isAfter(submittedOnDate)) { >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.submittedOnDateParamName) >>> >>> .failWithCode("before.loan.disbursement.date", >>> "Submission date cannot be before the loan disbursement date"); >>> >>> } >>> >>> >>> >>> final LocalDate rescheduleFromDate = >>> this.fromJsonHelper.extractLocalDateNamed( >>> >>> RescheduleLoansApiConstants.rescheduleFromDateParamName, >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName).value(rescheduleFromDate).notNull(); >>> >>> >>> >>> final Integer graceOnPrincipal = >>> this.fromJsonHelper.extractIntegerWithLocaleNamed( >>> >>> RescheduleLoansApiConstants.graceOnPrincipalParamName, >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.graceOnPrincipalParamName).value(graceOnPrincipal) >>> >>> .ignoreIfNull().integerGreaterThanZero(); >>> >>> >>> >>> final Integer graceOnInterest = >>> this.fromJsonHelper.extractIntegerWithLocaleNamed( >>> >>> RescheduleLoansApiConstants.graceOnInterestParamName, >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.graceOnInterestParamName).value(graceOnInterest).ignoreIfNull() >>> >>> .integerGreaterThanZero(); >>> >>> >>> >>> final Integer extraTerms = >>> this.fromJsonHelper.extractIntegerWithLocaleNamed(RescheduleLoansApiConstants.extraTermsParamName, >>> >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.extraTermsParamName).value(extraTerms).ignoreIfNull() >>> >>> .integerGreaterThanZero(); >>> >>> >>> >>> final Long rescheduleReasonId = >>> this.fromJsonHelper.extractLongNamed(RescheduleLoansApiConstants.rescheduleReasonIdParamName, >>> >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleReasonIdParamName).value(rescheduleReasonId).notNull() >>> >>> .integerGreaterThanZero(); >>> >>> >>> >>> final String rescheduleReasonComment = >>> this.fromJsonHelper.extractStringNamed( >>> >>> >>> RescheduleLoansApiConstants.rescheduleReasonCommentParamName, jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleReasonCommentParamName).value(rescheduleReasonComment) >>> >>> .ignoreIfNull().notExceedingLengthOf(500); >>> >>> >>> >>> final LocalDate adjustedDueDate = >>> this.fromJsonHelper.extractLocalDateNamed(RescheduleLoansApiConstants.adjustedDueDateParamName, >>> >>> jsonElement); >>> >>> >>> >>> if (adjustedDueDate != null && rescheduleFromDate != null && >>> adjustedDueDate.isBefore(rescheduleFromDate)) { >>> >>> dataValidatorBuilder >>> >>> .reset() >>> >>> >>> .parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) >>> >>> >>> .failWithCode("adjustedDueDate.before.rescheduleFromDate", >>> >>> "Adjusted due date cannot be before the >>> reschedule from date"); >>> >>> } >>> >>> >>> >>> // at least one of the following must be provided => >>> graceOnPrincipal, >>> >>> // graceOnInterest, extraTerms, newInterestRate >>> >>> if >>> (!this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.graceOnPrincipalParamName, >>> jsonElement) >>> >>> && >>> !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.graceOnInterestParamName, >>> jsonElement) >>> >>> && >>> !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.extraTermsParamName, >>> jsonElement) >>> >>> && >>> !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.newInterestRateParamName, >>> jsonElement) >>> >>> && >>> !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.adjustedDueDateParamName, >>> jsonElement)) { >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.graceOnPrincipalParamName).notNull(); >>> >>> } >>> >>> >>> >>> if (rescheduleFromDate != null) { >>> >>> LoanRepaymentScheduleInstallment installment = >>> loan.getRepaymentScheduleInstallment(rescheduleFromDate); >>> >>> >>> >>> if (installment == null) { >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) >>> >>> >>> .failWithCode("repayment.schedule.installment.does.not.exist", "Repayment >>> schedule installment does not exist"); >>> >>> } >>> >>> /* >>> >>> if (installment != null && installment.isObligationsMet()) { >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) >>> >>> >>> .failWithCode("repayment.schedule.installment.obligation.met", "Repayment >>> schedule installment obligation met"); >>> >>> } */ >>> >>> /* >>> >>> if (installment != null && installment.isPartlyPaid()) { >>> >>> >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) >>> >>> >>> .failWithCode("repayment.schedule.installment.partly.paid", "Repayment >>> schedule installment is partly paid"); >>> >>> } */ >>> >>> } >>> >>> >>> >>> if (loanId != null) { >>> >>> List<LoanRescheduleRequestData> loanRescheduleRequestData = >>> this.loanRescheduleRequestReadPlatformService >>> >>> .readLoanRescheduleRequests(loanId, >>> LoanStatus.APPROVED.getValue()); >>> >>> /* //commented this for loan reshedule >>> >>> if (loanRescheduleRequestData.size() > 0) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled", >>> >>> "The loan can only be rescheduled once."); >>> >>> } */ >>> >>> } >>> >>> if(loan.isMultiDisburmentLoan()) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(RescheduleLoansApiConstants.resheduleForMultiDisbursementNotSupportedErrorCode, >>> >>> "Loan rescheduling is not supported for >>> multidisbursement loans"); >>> >>> } >>> >>> >>> >>> if(loan.isInterestRecalculationEnabledForProduct()) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(RescheduleLoansApiConstants.resheduleWithInterestRecalculationNotSupportedErrorCode, >>> >>> "Loan rescheduling is not supported for the loan >>> product with interest recalculation enabled"); >>> >>> } >>> >>> >>> >>> if (!dataValidationErrors.isEmpty()) { throw new >>> PlatformApiDataValidationException(dataValidationErrors); } >>> >>> } >>> >>> >>> >>> /** >>> >>> * Validates a user request to approve a loan reschedule request >>> >>> * >>> >>> * @param jsonCommand >>> >>> * the JSON command object (instance of the JsonCommand >>> class) >>> >>> * @return void >>> >>> **/ >>> >>> public void validateForApproveAction(final JsonCommand jsonCommand, >>> LoanRescheduleRequest loanRescheduleRequest) { >>> >>> final String jsonString = jsonCommand.json(); >>> >>> >>> >>> if (StringUtils.isBlank(jsonString)) { throw new >>> InvalidJsonException(); } >>> >>> >>> >>> final Type typeToken = new TypeToken<Map<String, Object>>() >>> {}.getType(); >>> >>> this.fromJsonHelper.checkForUnsupportedParameters(typeToken, >>> jsonString, >>> >>> >>> RescheduleLoansApiConstants.APPROVE_REQUEST_DATA_PARAMETERS); >>> >>> >>> >>> final List<ApiParameterError> dataValidationErrors = new >>> ArrayList<>(); >>> >>> final DataValidatorBuilder dataValidatorBuilder = new >>> DataValidatorBuilder(dataValidationErrors).resource(StringUtils >>> >>> .lowerCase(RescheduleLoansApiConstants.ENTITY_NAME)); >>> >>> >>> >>> final JsonElement jsonElement = jsonCommand.parsedJson(); >>> >>> >>> >>> final LocalDate approvedOnDate = >>> this.fromJsonHelper.extractLocalDateNamed(RescheduleLoansApiConstants.approvedOnDateParam, >>> >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.approvedOnDateParam).value(approvedOnDate).notNull(); >>> >>> >>> >>> if (approvedOnDate != null && >>> loanRescheduleRequest.getSubmittedOnDate().isAfter(approvedOnDate)) { >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.approvedOnDateParam) >>> >>> .failWithCode("before.submission.date", "Approval >>> date cannot be before the request submission date."); >>> >>> } >>> >>> >>> >>> LoanRescheduleRequestStatusEnumData >>> loanRescheduleRequestStatusEnumData = LoanRescheduleRequestEnumerations >>> >>> .status(loanRescheduleRequest.getStatusEnum()); >>> >>> >>> >>> if (!loanRescheduleRequestStatusEnumData.isPendingApproval()) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode( >>> >>> "request.is.not.in >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRnJlcXVlc3QuaXMubm90Lmlu> >>> .submitted.and.pending.state", >>> >>> "Loan reschedule request approval is not allowed. " >>> >>> + "Loan reschedule request is not in >>> submitted and pending approval state."); >>> >>> } >>> >>> >>> >>> LocalDate rescheduleFromDate = >>> loanRescheduleRequest.getRescheduleFromDate(); >>> >>> final Loan loan = loanRescheduleRequest.getLoan(); >>> >>> >>> >>> if (loan != null) { >>> >>> Long loanId = loan.getId(); >>> >>> >>> >>> if (!loan.status().isActive()) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.is.not.active", >>> "Loan is not active"); >>> >>> } >>> >>> >>> >>> if (rescheduleFromDate != null) { >>> >>> LoanRepaymentScheduleInstallment installment = >>> loan.getRepaymentScheduleInstallment(rescheduleFromDate); >>> >>> >>> >>> if (installment == null) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode( >>> >>> >>> "loan.repayment.schedule.installment.does.not.exist", "Repayment schedule >>> installment does not exist"); >>> >>> } >>> >>> /* >>> >>> if (installment != null && >>> installment.isObligationsMet()) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode( >>> >>> "loan.repayment.schedule.installment." + >>> "obligation.met", "Repayment schedule installment obligation met"); >>> >>> } */ >>> >>> } >>> >>> >>> >>> if (loanId != null) { >>> >>> List<LoanRescheduleRequestData> >>> loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService >>> >>> .readLoanRescheduleRequests(loanId, >>> LoanStatus.APPROVED.getValue()); >>> >>> /* >>> >>> if (loanRescheduleRequestData.size() > 0) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled", >>> >>> "The loan can only be rescheduled once."); >>> >>> } */ >>> >>> } >>> >>> } >>> >>> >>> >>> if (!dataValidationErrors.isEmpty()) { throw new >>> PlatformApiDataValidationException(dataValidationErrors); } >>> >>> } >>> >>> >>> >>> /** >>> >>> * Validates a user request to reject a loan reschedule request >>> >>> * >>> >>> * @param jsonCommand >>> >>> * the JSON command object (instance of the JsonCommand >>> class) >>> >>> * @return void >>> >>> **/ >>> >>> public void validateForRejectAction(final JsonCommand jsonCommand, >>> LoanRescheduleRequest loanRescheduleRequest) { >>> >>> final String jsonString = jsonCommand.json(); >>> >>> >>> >>> if (StringUtils.isBlank(jsonString)) { throw new >>> InvalidJsonException(); } >>> >>> >>> >>> final Type typeToken = new TypeToken<Map<String, Object>>() >>> {}.getType(); >>> >>> this.fromJsonHelper >>> >>> .checkForUnsupportedParameters(typeToken, jsonString, >>> RescheduleLoansApiConstants.REJECT_REQUEST_DATA_PARAMETERS); >>> >>> >>> >>> final List<ApiParameterError> dataValidationErrors = new >>> ArrayList<>(); >>> >>> final DataValidatorBuilder dataValidatorBuilder = new >>> DataValidatorBuilder(dataValidationErrors).resource(StringUtils >>> >>> .lowerCase(RescheduleLoansApiConstants.ENTITY_NAME)); >>> >>> >>> >>> final JsonElement jsonElement = jsonCommand.parsedJson(); >>> >>> >>> >>> final LocalDate rejectedOnDate = >>> this.fromJsonHelper.extractLocalDateNamed(RescheduleLoansApiConstants.rejectedOnDateParam, >>> >>> jsonElement); >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rejectedOnDateParam).value(rejectedOnDate).notNull(); >>> >>> >>> >>> if (rejectedOnDate != null && >>> loanRescheduleRequest.getSubmittedOnDate().isAfter(rejectedOnDate)) { >>> >>> >>> dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rejectedOnDateParam) >>> >>> .failWithCode("before.submission.date", "Rejection >>> date cannot be before the request submission date."); >>> >>> } >>> >>> >>> >>> LoanRescheduleRequestStatusEnumData >>> loanRescheduleRequestStatusEnumData = LoanRescheduleRequestEnumerations >>> >>> .status(loanRescheduleRequest.getStatusEnum()); >>> >>> >>> >>> if (!loanRescheduleRequestStatusEnumData.isPendingApproval()) { >>> >>> >>> dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode( >>> >>> "request.is.not.in >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRnJlcXVlc3QuaXMubm90Lmlu> >>> .submitted.and.pending.state", >>> >>> "Loan reschedule request rejection is not allowed. " >>> >>> + "Loan reschedule request is not in >>> submitted and pending approval state."); >>> >>> } >>> >>> >>> >>> if (!dataValidationErrors.isEmpty()) { throw new >>> PlatformApiDataValidationException(dataValidationErrors); } >>> >>> } >>> >>> } >>> >>> >>> >>> >>> >>> On Sat, Apr 2, 2016 at 5:14 AM, Adi Raju < >>> adi.r...@confluxtechnologies.com >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBYWRpLnJhanUlNDBjb25mbHV4dGVjaG5vbG9naWVzLmNvbQ==>> >>> wrote: >>> >>> Hi Robert, >>> >>> Please send either a pull request or share your fork and branch details >>> for us to have a look at code changes. Also if possible send us a short >>> description of your technical solution. >>> >>> Regards, >>> Adi >>> >>> On 01-Apr-2016 7:00 pm, "Zack Wizglobal" <z...@wizglobal.co.ke >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBemFjayU0MHdpemdsb2JhbC5jby5rZQ==>> >>> wrote: >>> >>> Hi Ed, >>> >>> >>> >>> Thanks for the call. Robert copied here is our key developer for the >>> Mifos System and he will be able to answer all your questions. >>> >>> Robert here we have a team from Mifos who would want to know how we >>> implemented the loan reschedule. >>> >>> >>> >>> -- >>> Kind Regards, >>> >>> Zack Githinji >>> Systems Developer >>> Wizglobal Kenya >>> P.O. BOX 21373-00100 >>> Nairobi. >>> Mobile: +254 (0) 722 649199 >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=dGVsJTNBJTI1MkIyNTQlMjUyMCUyNTI4MCUyNTI5JTI1MjA3MjIlMjUyMDY0OTE5OQ==> >>> >>> z...@wizglobal.co.ke >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBemFjayU0MHdpemdsb2JhbC5jby5rZQ==> >>> www.wizglobal.co.ke >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRnd3dy53aXpnbG9iYWwuY28ua2U=> >>> >>> >>> >>> On 24 Mar 2016, at 21:06, Andris Kaneps <akan...@mtgcapital.ch >>> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBYWthbmVwcyU0MG10Z2NhcGl0YWwuY2g=>> >>> wrote: >>> >>> >>> >>> Dear Zack, Ed, Adi and Pramod, >>> >>> As you may know Mifos currently has a problem with rescheduling function >>> - we can't reschedule a loan more than once and its impossible to >>> reschedule a loan if any repayment has been entered. >>> >>> This function is crucial for Watu Credit loan product so we have >>> commissioned a Nairobi based software developer Wizglobal (represented by >>> Zack Githinji) to fix the problem. The fix currently is complete and we >>> have done preliminary testing. >>> >>> As discussed with Ed, we would like to contribute the fix to Mifos >>> community so that the problem is solved in next Mifos update. >>> >>> So Adi and Pramod, could you please get in touch directly with Zack to >>> discuss all the technical details? >>> >>> Kind regards, >>> >>> Andris Kaneps >>> >>> >>> >>> >>> >>> >>> >>> >>> >> >> >> >> -- >> *Ed Cable* >> Director of Community Programs, Mifos Initiative >> edca...@mifos.org >> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=bWFpbHRvJTNBZWRjYWJsZSU0MG1pZm9zLm9yZw==> >> | >> Skype: edcable | Mobile: +1.484.477.8649 >> >> *Collectively Creating a World of 3 Billion Maries | *http://mifos.org >> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRm1pZm9zLm9yZw==> >> >> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRmZhY2Vib29rLmNvbSUyRm1pZm9z> >> >> <https://web.chilipiper.com/link/mifos.org/57053b41e4b02bbec5bc216d?link=aHR0cCUzQSUyRiUyRnd3dy50d2l0dGVyLmNvbSUyRm1pZm9z> >> >> > > > -- > *Ed Cable* > Director of Community Programs, Mifos Initiative > edca...@mifos.org | Skype: edcable | Mobile: +1.484.477.8649 > > *Collectively Creating a World of 3 Billion Maries | *http://mifos.org > <http://facebook.com/mifos> <http://www.twitter.com/mifos> > >