From 6232e863ed5f62de0420e1dca32ecd9ff72a46b1 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Fri, 15 Nov 2013 11:42:52 +0000 Subject: [PATCH] MIFOSX-746: musoni specific approach to auto penalties on late installments. --- .../jobs/api/SchedulerJobApiResource.java | 15 +- .../infrastructure/jobs/service/JobName.java | 19 +- .../jobs/service/JobRegisterServiceImpl.java | 6 +- .../data/OverdueLoanScheduleData.java | 22 +-- .../service/LoanReadPlatformService.java | 11 +- .../service/LoanReadPlatformServiceImpl.java | 64 ++++--- ...WritePlatformServiceJpaRepositoryImpl.java | 175 ++++++++++-------- .../portfolio/meeting/domain/Meeting.java | 6 +- 8 files changed, 164 insertions(+), 154 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java index b7e4f816d..59372f055 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java @@ -34,6 +34,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Path("/jobs") +@Consumes({ MediaType.APPLICATION_JSON }) +@Produces({ MediaType.APPLICATION_JSON }) @Component public class SchedulerJobApiResource { @@ -59,8 +61,6 @@ public class SchedulerJobApiResource { } @GET - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) public String retrieveAll(@Context final UriInfo uriInfo) { final List jobDetailDatas = this.schedulerJobRunnerReadService.findAllJobDeatils(); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); @@ -69,8 +69,6 @@ public class SchedulerJobApiResource { @GET @Path("{" + SchedulerJobApiConstants.JOB_ID + "}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) public String retrieveOne(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId, @Context final UriInfo uriInfo) { final JobDetailData jobDetailData = this.schedulerJobRunnerReadService.retrieveOne(jobId); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); @@ -79,8 +77,6 @@ public class SchedulerJobApiResource { @GET @Path("{" + SchedulerJobApiConstants.JOB_ID + "}/" + SchedulerJobApiConstants.JOB_RUN_HISTORY) - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) public String retrieveHistory(@Context final UriInfo uriInfo, @PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId, @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) { @@ -94,8 +90,6 @@ public class SchedulerJobApiResource { @POST @Path("{" + SchedulerJobApiConstants.JOB_ID + "}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) public Response executeJob(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId, @QueryParam(SchedulerJobApiConstants.COMMAND) final String commandParam) { Response response = Response.status(400).build(); @@ -110,8 +104,6 @@ public class SchedulerJobApiResource { @PUT @Path("{" + SchedulerJobApiConstants.JOB_ID + "}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) public String updateJobDetail(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId, final String jsonRequestBody) { final CommandWrapper commandRequest = new CommandWrapperBuilder() // @@ -130,5 +122,4 @@ public class SchedulerJobApiResource { private boolean is(final String commandParam, final String commandValue) { return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); } - -} +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java index a1393c73a..dcebb47de 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobName.java @@ -2,15 +2,20 @@ package org.mifosplatform.infrastructure.jobs.service; public enum JobName { - UPDATE_LOAN_SUMMARY("Update loan Summary"), UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), UPDATE_LOAN_PAID_IN_ADVANCE( - "Update Loan Paid In Advance"), APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), APPLY_HOLIDAYS_TO_LOANS( - "Apply Holidays To Loans"), POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"), TRANSFER_FEE_CHARGE_FOR_LOANS( - "Transfer Fee For Loans From Savings"), ACCOUNTING_RUNNING_BALANCE_UPDATE("Update Accounting Running Balances"), PAY_DUE_SAVINGS_CHARGES( - "Pay Due Savings Charges"),APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"); + UPDATE_LOAN_SUMMARY("Update loan Summary"), // + UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), // + UPDATE_LOAN_PAID_IN_ADVANCE("Update Loan Paid In Advance"), // + APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), // + APPLY_HOLIDAYS_TO_LOANS("Apply Holidays To Loans"), // + POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"), // + TRANSFER_FEE_CHARGE_FOR_LOANS("Transfer Fee For Loans From Savings"), // + ACCOUNTING_RUNNING_BALANCE_UPDATE("Update Accounting Running Balances"), // + PAY_DUE_SAVINGS_CHARGES("Pay Due Savings Charges"), // + APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"); private final String name; - JobName(final String name) { + private JobName(final String name) { this.name = name; } @@ -18,4 +23,4 @@ public enum JobName { public String toString() { return this.name; } -} +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java index d03fb4154..f7a73d49d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java @@ -38,7 +38,7 @@ import org.springframework.stereotype.Service; * Service class to create and load batch jobs to Scheduler using * {@link SchedulerFactoryBean} ,{@link MethodInvokingJobDetailFactoryBean} and * {@link CronTriggerFactoryBean} - * + * */ @Service public class JobRegisterServiceImpl implements JobRegisterService { @@ -351,10 +351,6 @@ public class JobRegisterServiceImpl implements JobRegisterService { return cronTriggerFactoryBean.getObject(); } - /** - * @param throwable - * @return - */ private String getStackTraceAsString(final Throwable throwable) { final StackTraceElement[] stackTraceElements = throwable.getStackTrace(); final StringBuffer sb = new StringBuffer(throwable.toString()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/OverdueLoanScheduleData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/OverdueLoanScheduleData.java index a7fccd9c6..c9a079f30 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/OverdueLoanScheduleData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/OverdueLoanScheduleData.java @@ -2,14 +2,8 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.data; import java.math.BigDecimal; -/** - * Created with IntelliJ IDEA. - * User: andrew - * Date: 13-11-13 - * Time: 11:53 - * To change this template use File | Settings | File Templates. - */ public class OverdueLoanScheduleData { + private final Long loanId; private final Long chargeId; private final String locale; @@ -17,9 +11,8 @@ public class OverdueLoanScheduleData { private final String dateFormat; private final String dueDate; - - - public OverdueLoanScheduleData(Long loanId, Long chargeId, String dueDate, BigDecimal amount, String dateFormat, String locale) { + public OverdueLoanScheduleData(final Long loanId, final Long chargeId, final String dueDate, final BigDecimal amount, + final String dateFormat, final String locale) { this.loanId = loanId; this.chargeId = chargeId; this.dueDate = dueDate; @@ -54,12 +47,7 @@ public class OverdueLoanScheduleData { @Override public String toString() { - return "{" + - "chargeId:" + chargeId + - ", locale:'" + locale + '\'' + - ", amount:" + amount + - ", dateFormat:'" + dateFormat + '\'' + - ", dueDate:'" + dueDate + '\'' + - '}'; + return "{" + "chargeId:" + this.chargeId + ", locale:'" + this.locale + '\'' + ", amount:" + this.amount + ", dateFormat:'" + + this.dateFormat + '\'' + ", dueDate:'" + this.dueDate + '\'' + '}'; } } 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 4e84280dc..1d8d51661 100755 --- 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 @@ -5,6 +5,8 @@ */ package org.mifosplatform.portfolio.loanaccount.service; +import java.util.Collection; + import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.portfolio.calendar.data.CalendarData; @@ -15,8 +17,6 @@ import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoan import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData; -import java.util.Collection; - public interface LoanReadPlatformService { LoanAccountData retrieveOne(Long loanId); @@ -55,5 +55,10 @@ public interface LoanReadPlatformService { Collection retrieveAllowedLoanOfficers(Long selectedOfficeId, boolean staffInSelectedOfficeOnly); - Collection retrieveAllOverdueLoans(); + /* + * musoni-specific at present - will find overdue scheduled installments that have a special 'overdue charge' associated with the loan product. + * + * The 'overdue-charge' is only ever applied once to an installment and as a result overdue installments with this charge already applied are not returned. + */ + Collection retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(); } \ 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 643b4f6ac..9177fb09d 100755 --- 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 @@ -5,6 +5,13 @@ */ package org.mifosplatform.portfolio.loanaccount.service; +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + import org.apache.commons.lang.StringUtils; import org.joda.time.Days; import org.joda.time.LocalDate; @@ -40,8 +47,19 @@ import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.data.GroupRoleData; import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; -import org.mifosplatform.portfolio.loanaccount.data.*; -import org.mifosplatform.portfolio.loanaccount.domain.*; +import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; +import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData; +import org.mifosplatform.portfolio.loanaccount.data.LoanApplicationTimelineData; +import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData; +import org.mifosplatform.portfolio.loanaccount.data.LoanSummaryData; +import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; +import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData; +import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData; @@ -64,13 +82,6 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import java.math.BigDecimal; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; - @Service public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { @@ -662,7 +673,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final EnumOptionData groupStatus = ClientEnumerations.status(groupStatusEnum); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); groupData = GroupGeneralData.instance(groupId, groupName, groupExternalId, groupStatus, activationDate, groupOfficeId, - null, groupParentId, null, groupStaffId, null, groupHierarchy,null); + null, groupParentId, null, groupStaffId, null, groupHierarchy, null); } final Integer loanCounter = JdbcSupport.getInteger(rs, "loanCounter"); @@ -678,9 +689,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter); } } - private static final class OverdueLoanSchedule implements RowMapper{ - public String schema(){ + private static final class MusoniOverdueLoanScheduleMapper implements RowMapper { + + public String schema() { return " ls.loan_id as loanId, ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, ls.obligations_met_on_date as obligationsMetOnDate, ls.completed_derived as complete," + " 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, " @@ -696,17 +708,16 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } @Override - public OverdueLoanScheduleData mapRow(ResultSet rs, int rowNum) throws SQLException { + public OverdueLoanScheduleData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { final Long chargeId = rs.getLong("chargeId"); final Long loanId = rs.getLong("loanId"); final BigDecimal amount = rs.getBigDecimal("amount"); - final String dateFormat = "yyyy-MM-dd"; + final String dateFormat = "yyyy-MM-dd"; final String dueDate = rs.getString("dueDate"); - final String locale = "en_GB"; + final String locale = "en_GB"; - - OverdueLoanScheduleData overdueLoanScheduleData = new OverdueLoanScheduleData(loanId,chargeId,dueDate, - amount,dateFormat,locale) ; + final OverdueLoanScheduleData overdueLoanScheduleData = new OverdueLoanScheduleData(loanId, chargeId, dueDate, amount, + dateFormat, locale); return overdueLoanScheduleData; } @@ -1129,13 +1140,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } @Override - public Collection retrieveAllOverdueLoans() { - final OverdueLoanSchedule rm = new OverdueLoanSchedule(); - final String sql = "select " + rm.schema() + - " where CURDATE() > ls.duedate "+ - " and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 "+ - " and mc.charge_time_enum = 9 and ml.loan_status_id = 300 "+ - " and plc.charge_id not in (SELECT charge_id from m_loan_charge as mlc where mlc.loan_id = ml.id and mlc.charge_id = plc.charge_id and mlc.due_for_collection_as_of_date > fromdate AND mlc.due_for_collection_as_of_date <= duedate) "; - return this.jdbcTemplate.query(sql,rm,new Object[] {}); + public Collection retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized() { + final MusoniOverdueLoanScheduleMapper rm = new MusoniOverdueLoanScheduleMapper(); + final String sql = "select " + + rm.schema() + + " where CURDATE() > ls.duedate " + + " and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 " + + " and mc.charge_time_enum = 9 and ml.loan_status_id = 300 " + + " and plc.charge_id not in (SELECT charge_id from m_loan_charge as mlc where mlc.loan_id = ml.id and mlc.charge_id = plc.charge_id and mlc.due_for_collection_as_of_date > fromdate AND mlc.due_for_collection_as_of_date <= duedate) "; + return this.jdbcTemplate.query(sql, rm, new Object[] {}); } } \ No newline at end of file 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 d09f5ec6b..261d7f257 100755 --- 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 @@ -5,7 +5,17 @@ */ package org.mifosplatform.portfolio.loanaccount.service; -import com.google.gson.JsonElement; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; @@ -49,11 +59,15 @@ import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode; import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; -import org.mifosplatform.portfolio.charge.exception.*; +import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON; +import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException.LOAN_CHARGE_CANNOT_BE_PAYED_REASON; +import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException.LOAN_CHARGE_CANNOT_BE_UPDATED_REASON; +import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LOAN_CHARGE_CANNOT_BE_WAIVED_REASON; +import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand; @@ -65,7 +79,19 @@ import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; -import org.mifosplatform.portfolio.loanaccount.domain.*; +import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail; +import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService; +import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; +import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository; +import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge; +import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine; +import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; +import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; @@ -93,16 +119,13 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; -import java.math.BigDecimal; -import java.util.*; - +import com.google.gson.JsonElement; @Service public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService { + private final static Logger logger = LoggerFactory.getLogger(LoanWritePlatformServiceJpaRepositoryImpl.class); - - private final PlatformSecurityContext context; private final LoanEventApiJsonValidator loanEventApiJsonValidator; private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer; @@ -129,7 +152,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanReadPlatformService loanReadPlatformService; private final FromJsonHelper fromApiJsonHelper; - @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanEventApiJsonValidator loanEventApiJsonValidator, @@ -146,8 +168,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final AccountTransfersWritePlatformService accountTransfersWritePlatformService, final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, - final LoanChargeReadPlatformService loanChargeReadPlatformService,final LoanReadPlatformService loanReadPlatformService, - final FromJsonHelper fromApiJsonHelper ) { + final LoanChargeReadPlatformService loanChargeReadPlatformService, final LoanReadPlatformService loanReadPlatformService, + final FromJsonHelper fromApiJsonHelper) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; @@ -257,8 +279,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf for (final LoanCharge loanCharge : loanCharges) { if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount()); - }else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){ - Set chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); + } else if (loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()) { + final Set chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); loanCharge.addLoanInstallmentCharges(chargePerInstallments); this.loanChargeRepository.save(loanCharge); } @@ -384,8 +406,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf for (final LoanCharge loanCharge : loanCharges) { if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount()); - }else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){ - Set chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); + } else if (loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()) { + final Set chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); loanCharge.addLoanInstallmentCharges(chargePerInstallments); this.loanChargeRepository.save(loanCharge); } @@ -393,10 +415,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); for (final Map.Entry entrySet : disBuLoanCharges.entrySet()) { - final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan.getId()); + final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan + .getId()); final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(), - PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(), "Loan Charge Payment", - locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null); + PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(), + "Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), + entrySet.getKey(), null); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } } @@ -576,7 +600,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } - final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { changes.put("note", noteText); @@ -653,8 +676,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } - - final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { changes.put("note", noteText); @@ -876,7 +897,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } - // we post Journal entries only for loans in active status postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } @@ -943,28 +963,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (loanCharge.isWaived()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_WAIVED, loanCharge.getId()); } else if (loanCharge.isPaid()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_PAID, - loanCharge.getId()); - } - Integer loanInstallmentNumber = null; - if (loanCharge.isInstalmentFee()){ + loanCharge.getId()); } + Integer loanInstallmentNumber = null; + if (loanCharge.isInstalmentFee()) { LoanInstallmentCharge chargePerInstallment = null; - if(!StringUtils.isBlank(command.json())){ + if (!StringUtils.isBlank(command.json())) { final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate"); final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber"); - if(dueDate !=null){ + if (dueDate != null) { chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate); - }else if(installmentNumber!=null){ + } else if (installmentNumber != null) { chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber); } } - if(chargePerInstallment == null){ + if (chargePerInstallment == null) { chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge(); } - if(chargePerInstallment.isWaived()){ + if (chargePerInstallment.isWaived()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId()); - }else if(chargePerInstallment.isPaid()){ - throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); - } + } else if (chargePerInstallment.isPaid()) { throw new LoanChargeCannotBePayedException( + LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); } loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber(); } @@ -1042,8 +1060,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (loanCharge.isWaived()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId()); } else if (loanCharge.isPaid()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, - loanCharge.getId()); - } + loanCharge.getId()); } if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException( LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_ACCOUNT_TRANSFER, loanCharge.getId()); } @@ -1052,37 +1069,36 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); - Integer loanInstallmentNumber = null; + Integer loanInstallmentNumber = null; BigDecimal amount = loanCharge.amountOutstanding(); - if (loanCharge.isInstalmentFee()){ + if (loanCharge.isInstalmentFee()) { LoanInstallmentCharge chargePerInstallment = null; final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate"); final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber"); - if(dueDate !=null){ + if (dueDate != null) { chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate); - }else if(installmentNumber!=null){ + } else if (installmentNumber != null) { chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber); } - if(chargePerInstallment == null){ + if (chargePerInstallment == null) { chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge(); } - if(chargePerInstallment.isWaived()){ + if (chargePerInstallment.isWaived()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId()); - }else if(chargePerInstallment.isPaid()){ - throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); - } + } else if (chargePerInstallment.isPaid()) { throw new LoanChargeCannotBePayedException( + LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); } loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber(); - amount= chargePerInstallment.getAmountOutstanding(); + amount = chargePerInstallment.getAmountOutstanding(); } - + final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); if (portfolioAccountData == null) { final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment"; throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId); } - final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, - PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", - locale, fmt, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber); + final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS, + PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null, + LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); return new CommandProcessingResultBuilder() // @@ -1103,23 +1119,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final StringBuilder sb = new StringBuilder(); if (chargeDatas != null) { for (final LoanChargeData chargeData : chargeDatas) { - if(chargeData.isInstallmentFee()){ - Collection chargePerInstallments = this.LoanChargeReadPlatformService.retrieveInstallmentLoanCharges(chargeData.getId(), true); + if (chargeData.isInstallmentFee()) { + final Collection chargePerInstallments = this.LoanChargeReadPlatformService + .retrieveInstallmentLoanCharges(chargeData.getId(), true); PortfolioAccountData portfolioAccountData = null; - for(LoanInstallmentChargeData installmentChargeData : chargePerInstallments){ - if(!installmentChargeData.getDueDate().isAfter(new LocalDate())){ - if(portfolioAccountData == null){ - portfolioAccountData = this.accountAssociationsReadPlatformService - .retriveLoanAssociation(chargeData.getLoanId()); + for (final LoanInstallmentChargeData installmentChargeData : chargePerInstallments) { + if (!installmentChargeData.getDueDate().isAfter(new LocalDate())) { + if (portfolioAccountData == null) { + portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(chargeData + .getLoanId()); } final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, - portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null, - LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), installmentChargeData.getInstallmentNumber()); + portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, + null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), + installmentChargeData.getInstallmentNumber()); transferFeeCharge(sb, accountTransferDTO); } } - }else if (chargeData.getDueDate() != null && !chargeData.getDueDate().isAfter(new LocalDate())) { + } else if (chargeData.getDueDate() != null && !chargeData.getDueDate().isAfter(new LocalDate())) { final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService .retriveLoanAssociation(chargeData.getLoanId()); final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), @@ -1601,22 +1619,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Override @CronTarget(jobName = JobName.APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT) public void applyChargeForOverdueLoans() throws JobExecutionException { - final Collection overdueLoanScheduleData = this.loanReadPlatformService.retrieveAllOverdueLoans(); - if(!overdueLoanScheduleData.isEmpty()) { - for(OverdueLoanScheduleData overdueLoanScheduleData1 : overdueLoanScheduleData){ - try{ - final JsonElement parsedCommand = fromApiJsonHelper.parse(overdueLoanScheduleData1.toString()); + final Collection overdueLoanScheduledInstallments = this.loanReadPlatformService.retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(); - final JsonCommand command = JsonCommand.from(overdueLoanScheduleData1.toString(),parsedCommand,this.fromApiJsonHelper,null,null, - null,null,null,overdueLoanScheduleData1.getLoanId(),null,null,null,null,null,null,null); - this.applyChargeToOverdueLoanInstallment(overdueLoanScheduleData1.getLoanId(),overdueLoanScheduleData1.getChargeId(),command); + if (!overdueLoanScheduledInstallments.isEmpty()) { + for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) { + try { - }catch(final PlatformApiDataValidationException e){ + final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString()); + + final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper, + null, null, null, null, null, overdueInstallment.getLoanId(), null, null, null, null, null, null, null); + + applyChargeToOverdueLoanInstallment(overdueInstallment.getLoanId(), overdueInstallment.getChargeId(), command); + + } catch (final PlatformApiDataValidationException e) { final List errors = e.getErrors(); for (final ApiParameterError error : errors) { - logger.error("Apply Charges due for overdue loans failed for account:" + overdueLoanScheduleData1.getLoanId() + " with message " - + error.getDeveloperMessage()); + logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId() + + " with message " + error.getDeveloperMessage()); } } } @@ -1624,7 +1645,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } @Transactional - public void applyChargeToOverdueLoanInstallment(Long loanId, Long loanChargeId,JsonCommand command) { + public void applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final JsonCommand command) { + final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); @@ -1636,6 +1658,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final String errorMessage = "Charge and Loan must have the same currency."; throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage); } + if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); if (portfolioAccountData == null) { @@ -1656,13 +1679,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (loan.status().isActive()) { final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null); this.loanTransactionRepository.save(applyLoanChargeTransaction); - /*** - * TODO Vishwas Batch save is giving me a - * HibernateOptimisticLockingFailureException, looping and saving - * for the time being, not a major issue for now as this loop is - * entered only in edge cases (when a payment is made before the - * latest payment recorded against the loan) - ***/ this.loanRepository.saveAndFlush(loan); if (changedTransactionDetail != null) { for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -1671,7 +1687,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } - // we post Journal entries only for loans in active status postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java index 929fa7f94..9c2aa2afd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java @@ -37,7 +37,6 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.calendar.exception.NotValidRecurringDateException; -import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.meeting.attendance.domain.ClientAttendance; import org.mifosplatform.portfolio.meeting.exception.MeetingDateException; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -163,7 +162,7 @@ public class Meeting extends AbstractPersistable { } public boolean isMeetingDateBefore(final LocalDate newStartDate) { - return (this.meetingDate != null && newStartDate != null && getMeetingDateLocalDate().isBefore(newStartDate)) ? true : false; + return this.meetingDate != null && newStartDate != null && getMeetingDateLocalDate().isBefore(newStartDate) ? true : false; } private static boolean isValidMeetingDate(final CalendarInstance calendarInstance, final Date meetingDate) { @@ -173,8 +172,7 @@ public class Meeting extends AbstractPersistable { meetingDateLocalDate = LocalDate.fromDateFields(meetingDate); } - if (meetingDateLocalDate == null - || !calendar.isValidRecurringDate(meetingDateLocalDate)) { return false; } + if (meetingDateLocalDate == null || !calendar.isValidRecurringDate(meetingDateLocalDate)) { return false; } return true; }