MIFOSX-746: musoni specific approach to auto penalties on late installments.

This commit is contained in:
Keith Woodlock
2013-11-15 11:43:29 +00:00
parent b43a9472e3
commit 6232e863ed
8 changed files with 164 additions and 154 deletions
@@ -34,6 +34,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/jobs") @Path("/jobs")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Component @Component
public class SchedulerJobApiResource { public class SchedulerJobApiResource {
@@ -59,8 +61,6 @@ public class SchedulerJobApiResource {
} }
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll(@Context final UriInfo uriInfo) { public String retrieveAll(@Context final UriInfo uriInfo) {
final List<JobDetailData> jobDetailDatas = this.schedulerJobRunnerReadService.findAllJobDeatils(); final List<JobDetailData> jobDetailDatas = this.schedulerJobRunnerReadService.findAllJobDeatils();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -69,8 +69,6 @@ public class SchedulerJobApiResource {
@GET @GET
@Path("{" + SchedulerJobApiConstants.JOB_ID + "}") @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) { public String retrieveOne(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId, @Context final UriInfo uriInfo) {
final JobDetailData jobDetailData = this.schedulerJobRunnerReadService.retrieveOne(jobId); final JobDetailData jobDetailData = this.schedulerJobRunnerReadService.retrieveOne(jobId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -79,8 +77,6 @@ public class SchedulerJobApiResource {
@GET @GET
@Path("{" + SchedulerJobApiConstants.JOB_ID + "}/" + SchedulerJobApiConstants.JOB_RUN_HISTORY) @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, 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("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) { @QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) {
@@ -94,8 +90,6 @@ public class SchedulerJobApiResource {
@POST @POST
@Path("{" + SchedulerJobApiConstants.JOB_ID + "}") @Path("{" + SchedulerJobApiConstants.JOB_ID + "}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response executeJob(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId, public Response executeJob(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId,
@QueryParam(SchedulerJobApiConstants.COMMAND) final String commandParam) { @QueryParam(SchedulerJobApiConstants.COMMAND) final String commandParam) {
Response response = Response.status(400).build(); Response response = Response.status(400).build();
@@ -110,8 +104,6 @@ public class SchedulerJobApiResource {
@PUT @PUT
@Path("{" + SchedulerJobApiConstants.JOB_ID + "}") @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) { public String updateJobDetail(@PathParam(SchedulerJobApiConstants.JOB_ID) final Long jobId, final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() // final CommandWrapper commandRequest = new CommandWrapperBuilder() //
@@ -130,5 +122,4 @@ public class SchedulerJobApiResource {
private boolean is(final String commandParam, final String commandValue) { private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
} }
} }
@@ -2,15 +2,20 @@ package org.mifosplatform.infrastructure.jobs.service;
public enum JobName { public enum JobName {
UPDATE_LOAN_SUMMARY("Update loan Summary"), UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), UPDATE_LOAN_PAID_IN_ADVANCE( UPDATE_LOAN_SUMMARY("Update loan Summary"), //
"Update Loan Paid In Advance"), APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), APPLY_HOLIDAYS_TO_LOANS( UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), //
"Apply Holidays To Loans"), POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"), TRANSFER_FEE_CHARGE_FOR_LOANS( UPDATE_LOAN_PAID_IN_ADVANCE("Update Loan Paid In Advance"), //
"Transfer Fee For Loans From Savings"), ACCOUNTING_RUNNING_BALANCE_UPDATE("Update Accounting Running Balances"), PAY_DUE_SAVINGS_CHARGES( APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), //
"Pay Due Savings Charges"),APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"); 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; private final String name;
JobName(final String name) { private JobName(final String name) {
this.name = name; this.name = name;
} }
@@ -351,10 +351,6 @@ public class JobRegisterServiceImpl implements JobRegisterService {
return cronTriggerFactoryBean.getObject(); return cronTriggerFactoryBean.getObject();
} }
/**
* @param throwable
* @return
*/
private String getStackTraceAsString(final Throwable throwable) { private String getStackTraceAsString(final Throwable throwable) {
final StackTraceElement[] stackTraceElements = throwable.getStackTrace(); final StackTraceElement[] stackTraceElements = throwable.getStackTrace();
final StringBuffer sb = new StringBuffer(throwable.toString()); final StringBuffer sb = new StringBuffer(throwable.toString());
@@ -2,14 +2,8 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.data;
import java.math.BigDecimal; 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 { public class OverdueLoanScheduleData {
private final Long loanId; private final Long loanId;
private final Long chargeId; private final Long chargeId;
private final String locale; private final String locale;
@@ -17,9 +11,8 @@ public class OverdueLoanScheduleData {
private final String dateFormat; private final String dateFormat;
private final String dueDate; private final String dueDate;
public OverdueLoanScheduleData(final Long loanId, final Long chargeId, final String dueDate, final BigDecimal amount,
final String dateFormat, final String locale) {
public OverdueLoanScheduleData(Long loanId, Long chargeId, String dueDate, BigDecimal amount, String dateFormat, String locale) {
this.loanId = loanId; this.loanId = loanId;
this.chargeId = chargeId; this.chargeId = chargeId;
this.dueDate = dueDate; this.dueDate = dueDate;
@@ -54,12 +47,7 @@ public class OverdueLoanScheduleData {
@Override @Override
public String toString() { public String toString() {
return "{" + return "{" + "chargeId:" + this.chargeId + ", locale:'" + this.locale + '\'' + ", amount:" + this.amount + ", dateFormat:'"
"chargeId:" + chargeId + + this.dateFormat + '\'' + ", dueDate:'" + this.dueDate + '\'' + '}';
", locale:'" + locale + '\'' +
", amount:" + amount +
", dateFormat:'" + dateFormat + '\'' +
", dueDate:'" + dueDate + '\'' +
'}';
} }
} }
@@ -5,6 +5,8 @@
*/ */
package org.mifosplatform.portfolio.loanaccount.service; package org.mifosplatform.portfolio.loanaccount.service;
import java.util.Collection;
import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.portfolio.calendar.data.CalendarData; 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.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
import java.util.Collection;
public interface LoanReadPlatformService { public interface LoanReadPlatformService {
LoanAccountData retrieveOne(Long loanId); LoanAccountData retrieveOne(Long loanId);
@@ -55,5 +55,10 @@ public interface LoanReadPlatformService {
Collection<StaffData> retrieveAllowedLoanOfficers(Long selectedOfficeId, boolean staffInSelectedOfficeOnly); Collection<StaffData> retrieveAllowedLoanOfficers(Long selectedOfficeId, boolean staffInSelectedOfficeOnly);
Collection<OverdueLoanScheduleData> 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<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized();
} }
@@ -5,6 +5,13 @@
*/ */
package org.mifosplatform.portfolio.loanaccount.service; 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.apache.commons.lang.StringUtils;
import org.joda.time.Days; import org.joda.time.Days;
import org.joda.time.LocalDate; 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.data.GroupRoleData;
import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.GroupReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.loanaccount.data.*; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
import org.mifosplatform.portfolio.loanaccount.domain.*; 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.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData; 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.stereotype.Service;
import org.springframework.util.CollectionUtils; 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 @Service
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@@ -662,7 +673,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final EnumOptionData groupStatus = ClientEnumerations.status(groupStatusEnum); final EnumOptionData groupStatus = ClientEnumerations.status(groupStatusEnum);
final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
groupData = GroupGeneralData.instance(groupId, groupName, groupExternalId, groupStatus, activationDate, groupOfficeId, 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"); final Integer loanCounter = JdbcSupport.getInteger(rs, "loanCounter");
@@ -678,9 +689,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter); feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter);
} }
} }
private static final class OverdueLoanSchedule implements RowMapper<OverdueLoanScheduleData>{
public String schema(){ private static final class MusoniOverdueLoanScheduleMapper implements RowMapper<OverdueLoanScheduleData> {
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," 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.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.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 @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 chargeId = rs.getLong("chargeId");
final Long loanId = rs.getLong("loanId"); final Long loanId = rs.getLong("loanId");
final BigDecimal amount = rs.getBigDecimal("amount"); 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 dueDate = rs.getString("dueDate");
final String locale = "en_GB"; final String locale = "en_GB";
final OverdueLoanScheduleData overdueLoanScheduleData = new OverdueLoanScheduleData(loanId, chargeId, dueDate, amount,
OverdueLoanScheduleData overdueLoanScheduleData = new OverdueLoanScheduleData(loanId,chargeId,dueDate, dateFormat, locale);
amount,dateFormat,locale) ;
return overdueLoanScheduleData; return overdueLoanScheduleData;
} }
@@ -1129,13 +1140,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
} }
@Override @Override
public Collection<OverdueLoanScheduleData> retrieveAllOverdueLoans() { public Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized() {
final OverdueLoanSchedule rm = new OverdueLoanSchedule(); final MusoniOverdueLoanScheduleMapper rm = new MusoniOverdueLoanScheduleMapper();
final String sql = "select " + rm.schema() + final String sql = "select "
" where CURDATE() > ls.duedate "+ + rm.schema()
" and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 "+ + " where CURDATE() > ls.duedate "
" and mc.charge_time_enum = 9 and ml.loan_status_id = 300 "+ + " and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 "
" 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) "; + " and mc.charge_time_enum = 9 and ml.loan_status_id = 300 "
return this.jdbcTemplate.query(sql,rm,new Object[] {}); + " 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[] {});
} }
} }
@@ -5,7 +5,17 @@
*/ */
package org.mifosplatform.portfolio.loanaccount.service; 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.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat; 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.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode; import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper; 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.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.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.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.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.domain.Client;
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand; 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.command.LoanUpdateCommand;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; 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.LoanDisbursalException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; 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.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.math.BigDecimal; import com.google.gson.JsonElement;
import java.util.*;
@Service @Service
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService { public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(LoanWritePlatformServiceJpaRepositoryImpl.class); private final static Logger logger = LoggerFactory.getLogger(LoanWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final LoanEventApiJsonValidator loanEventApiJsonValidator; private final LoanEventApiJsonValidator loanEventApiJsonValidator;
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer; private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
@@ -129,7 +152,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanReadPlatformService loanReadPlatformService; private final LoanReadPlatformService loanReadPlatformService;
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@Autowired @Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final LoanEventApiJsonValidator loanEventApiJsonValidator, final LoanEventApiJsonValidator loanEventApiJsonValidator,
@@ -146,8 +168,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final AccountTransfersWritePlatformService accountTransfersWritePlatformService, final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final LoanChargeReadPlatformService loanChargeReadPlatformService,final LoanReadPlatformService loanReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService, final LoanReadPlatformService loanReadPlatformService,
final FromJsonHelper fromApiJsonHelper ) { final FromJsonHelper fromApiJsonHelper) {
this.context = context; this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanAssembler = loanAssembler; this.loanAssembler = loanAssembler;
@@ -257,8 +279,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
for (final LoanCharge loanCharge : loanCharges) { for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount()); disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount());
}else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){ } else if (loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()) {
Set<LoanInstallmentCharge> chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); final Set<LoanInstallmentCharge> chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge);
loanCharge.addLoanInstallmentCharges(chargePerInstallments); loanCharge.addLoanInstallmentCharges(chargePerInstallments);
this.loanChargeRepository.save(loanCharge); this.loanChargeRepository.save(loanCharge);
} }
@@ -384,8 +406,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
for (final LoanCharge loanCharge : loanCharges) { for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount()); disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount());
}else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){ } else if (loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()) {
Set<LoanInstallmentCharge> chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); final Set<LoanInstallmentCharge> chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge);
loanCharge.addLoanInstallmentCharges(chargePerInstallments); loanCharge.addLoanInstallmentCharges(chargePerInstallments);
this.loanChargeRepository.save(loanCharge); this.loanChargeRepository.save(loanCharge);
} }
@@ -393,10 +415,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
for (final Map.Entry<Long, BigDecimal> entrySet : disBuLoanCharges.entrySet()) { for (final Map.Entry<Long, BigDecimal> 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(), final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(), "Loan Charge Payment", PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(),
locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null); "Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(),
entrySet.getKey(), null);
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
} }
} }
@@ -576,7 +600,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
} }
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
changes.put("note", noteText); changes.put("note", noteText);
@@ -653,8 +676,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
} }
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
changes.put("note", noteText); changes.put("note", noteText);
@@ -876,7 +897,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
} }
// we post Journal entries only for loans in active status // we post Journal entries only for loans in active status
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
} }
@@ -943,28 +963,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loanCharge.isWaived()) { if (loanCharge.isWaived()) {
throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_WAIVED, loanCharge.getId()); 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, } else if (loanCharge.isPaid()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_PAID,
loanCharge.getId()); loanCharge.getId()); }
}
Integer loanInstallmentNumber = null; Integer loanInstallmentNumber = null;
if (loanCharge.isInstalmentFee()){ if (loanCharge.isInstalmentFee()) {
LoanInstallmentCharge chargePerInstallment = null; LoanInstallmentCharge chargePerInstallment = null;
if(!StringUtils.isBlank(command.json())){ if (!StringUtils.isBlank(command.json())) {
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate"); final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber"); final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber");
if(dueDate !=null){ if (dueDate != null) {
chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate); chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate);
}else if(installmentNumber!=null){ } else if (installmentNumber != null) {
chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber); chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber);
} }
} }
if(chargePerInstallment == null){ if (chargePerInstallment == null) {
chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge(); chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
} }
if(chargePerInstallment.isWaived()){ if (chargePerInstallment.isWaived()) {
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId()); throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
}else if(chargePerInstallment.isPaid()){ } else if (chargePerInstallment.isPaid()) { throw new LoanChargeCannotBePayedException(
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); }
}
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber(); loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
} }
@@ -1042,8 +1060,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loanCharge.isWaived()) { if (loanCharge.isWaived()) {
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId()); 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, } 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( if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException(
LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_ACCOUNT_TRANSFER, loanCharge.getId()); } LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_ACCOUNT_TRANSFER, loanCharge.getId()); }
@@ -1054,25 +1071,24 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
Integer loanInstallmentNumber = null; Integer loanInstallmentNumber = null;
BigDecimal amount = loanCharge.amountOutstanding(); BigDecimal amount = loanCharge.amountOutstanding();
if (loanCharge.isInstalmentFee()){ if (loanCharge.isInstalmentFee()) {
LoanInstallmentCharge chargePerInstallment = null; LoanInstallmentCharge chargePerInstallment = null;
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate"); final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber"); final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber");
if(dueDate !=null){ if (dueDate != null) {
chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate); chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate);
}else if(installmentNumber!=null){ } else if (installmentNumber != null) {
chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber); chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber);
} }
if(chargePerInstallment == null){ if (chargePerInstallment == null) {
chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge(); chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
} }
if(chargePerInstallment.isWaived()){ if (chargePerInstallment.isWaived()) {
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId()); throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
}else if(chargePerInstallment.isPaid()){ } else if (chargePerInstallment.isPaid()) { throw new LoanChargeCannotBePayedException(
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); }
}
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber(); loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
amount= chargePerInstallment.getAmountOutstanding(); amount = chargePerInstallment.getAmountOutstanding();
} }
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
@@ -1080,9 +1096,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment"; final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment";
throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId); throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId);
} }
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS,
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null,
locale, fmt, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber); LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber);
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
@@ -1103,23 +1119,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
if (chargeDatas != null) { if (chargeDatas != null) {
for (final LoanChargeData chargeData : chargeDatas) { for (final LoanChargeData chargeData : chargeDatas) {
if(chargeData.isInstallmentFee()){ if (chargeData.isInstallmentFee()) {
Collection<LoanInstallmentChargeData> chargePerInstallments = this.LoanChargeReadPlatformService.retrieveInstallmentLoanCharges(chargeData.getId(), true); final Collection<LoanInstallmentChargeData> chargePerInstallments = this.LoanChargeReadPlatformService
.retrieveInstallmentLoanCharges(chargeData.getId(), true);
PortfolioAccountData portfolioAccountData = null; PortfolioAccountData portfolioAccountData = null;
for(LoanInstallmentChargeData installmentChargeData : chargePerInstallments){ for (final LoanInstallmentChargeData installmentChargeData : chargePerInstallments) {
if(!installmentChargeData.getDueDate().isAfter(new LocalDate())){ if (!installmentChargeData.getDueDate().isAfter(new LocalDate())) {
if(portfolioAccountData == null){ if (portfolioAccountData == null) {
portfolioAccountData = this.accountAssociationsReadPlatformService portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(chargeData
.retriveLoanAssociation(chargeData.getLoanId()); .getLoanId());
} }
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(),
installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null, portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null,
LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), installmentChargeData.getInstallmentNumber()); null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(),
installmentChargeData.getInstallmentNumber());
transferFeeCharge(sb, accountTransferDTO); 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 final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService
.retriveLoanAssociation(chargeData.getLoanId()); .retriveLoanAssociation(chargeData.getLoanId());
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(),
@@ -1601,22 +1619,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override @Override
@CronTarget(jobName = JobName.APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT) @CronTarget(jobName = JobName.APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT)
public void applyChargeForOverdueLoans() throws JobExecutionException { public void applyChargeForOverdueLoans() throws JobExecutionException {
final Collection<OverdueLoanScheduleData> overdueLoanScheduleData = this.loanReadPlatformService.retrieveAllOverdueLoans();
if(!overdueLoanScheduleData.isEmpty()) {
for(OverdueLoanScheduleData overdueLoanScheduleData1 : overdueLoanScheduleData){
try{
final JsonElement parsedCommand = fromApiJsonHelper.parse(overdueLoanScheduleData1.toString()); final Collection<OverdueLoanScheduleData> overdueLoanScheduledInstallments = this.loanReadPlatformService.retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized();
final JsonCommand command = JsonCommand.from(overdueLoanScheduleData1.toString(),parsedCommand,this.fromApiJsonHelper,null,null, if (!overdueLoanScheduledInstallments.isEmpty()) {
null,null,null,overdueLoanScheduleData1.getLoanId(),null,null,null,null,null,null,null); for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) {
this.applyChargeToOverdueLoanInstallment(overdueLoanScheduleData1.getLoanId(),overdueLoanScheduleData1.getChargeId(),command); 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<ApiParameterError> errors = e.getErrors(); final List<ApiParameterError> errors = e.getErrors();
for (final ApiParameterError error : errors) { for (final ApiParameterError error : errors) {
logger.error("Apply Charges due for overdue loans failed for account:" + overdueLoanScheduleData1.getLoanId() + " with message " logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId()
+ error.getDeveloperMessage()); + " with message " + error.getDeveloperMessage());
} }
} }
} }
@@ -1624,7 +1645,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
@Transactional @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); final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan); checkClientOrGroupActive(loan);
@@ -1636,6 +1658,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final String errorMessage = "Charge and Loan must have the same currency."; final String errorMessage = "Charge and Loan must have the same currency.";
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage); throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
} }
if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId); final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
if (portfolioAccountData == null) { if (portfolioAccountData == null) {
@@ -1656,13 +1679,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loan.status().isActive()) { if (loan.status().isActive()) {
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null); final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
this.loanTransactionRepository.save(applyLoanChargeTransaction); 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); this.loanRepository.saveAndFlush(loan);
if (changedTransactionDetail != null) { if (changedTransactionDetail != null) {
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -1671,7 +1687,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
} }
// we post Journal entries only for loans in active status // we post Journal entries only for loans in active status
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
} }
@@ -37,7 +37,6 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar;
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
import org.mifosplatform.portfolio.calendar.exception.NotValidRecurringDateException; 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.attendance.domain.ClientAttendance;
import org.mifosplatform.portfolio.meeting.exception.MeetingDateException; import org.mifosplatform.portfolio.meeting.exception.MeetingDateException;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -163,7 +162,7 @@ public class Meeting extends AbstractPersistable<Long> {
} }
public boolean isMeetingDateBefore(final LocalDate newStartDate) { 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) { private static boolean isValidMeetingDate(final CalendarInstance calendarInstance, final Date meetingDate) {
@@ -173,8 +172,7 @@ public class Meeting extends AbstractPersistable<Long> {
meetingDateLocalDate = LocalDate.fromDateFields(meetingDate); meetingDateLocalDate = LocalDate.fromDateFields(meetingDate);
} }
if (meetingDateLocalDate == null if (meetingDateLocalDate == null || !calendar.isValidRecurringDate(meetingDateLocalDate)) { return false; }
|| !calendar.isValidRecurringDate(meetingDateLocalDate)) { return false; }
return true; return true;
} }