Loan Delinquency Classification job
This commit is contained in:
committed by
Arnold Gálovics
parent
0af87a8766
commit
8b3975f8d7
+9
@@ -1155,6 +1155,15 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder assignDelinquency(final Long loanId) {
|
||||||
|
this.actionName = "UPDATEDELINQUENCY";
|
||||||
|
this.entityName = "LOAN";
|
||||||
|
this.entityId = loanId;
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.href = "/loans/" + loanId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder createCodeValue(final Long codeId) {
|
public CommandWrapperBuilder createCodeValue(final Long codeId) {
|
||||||
this.actionName = "CREATE";
|
this.actionName = "CREATE";
|
||||||
this.entityName = "CODEVALUE";
|
this.entityName = "CODEVALUE";
|
||||||
|
|||||||
+5
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.infrastructure.core.service;
|
package org.apache.fineract.infrastructure.core.service;
|
||||||
|
|
||||||
|
import static java.time.temporal.ChronoUnit.DAYS;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
@@ -76,4 +78,7 @@ public final class DateUtils {
|
|||||||
return ThreadLocalContextUtil.getBusinessDate();
|
return ThreadLocalContextUtil.getBusinessDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long getDifferenceInDays(final LocalDate localDateBefore, final LocalDate localDateAfter) {
|
||||||
|
return DAYS.between(localDateBefore, localDateAfter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -55,7 +55,8 @@ public enum JobName {
|
|||||||
EXECUTE_DIRTY_JOBS("Execute All Dirty Jobs"), //
|
EXECUTE_DIRTY_JOBS("Execute All Dirty Jobs"), //
|
||||||
INCREASE_BUSINESS_DATE_BY_1_DAY("Increase Business Date by 1 day"), //
|
INCREASE_BUSINESS_DATE_BY_1_DAY("Increase Business Date by 1 day"), //
|
||||||
INCREASE_COB_DATE_BY_1_DAY("Increase COB Date by 1 day"), //
|
INCREASE_COB_DATE_BY_1_DAY("Increase COB Date by 1 day"), //
|
||||||
LOAN_COB("Loan COB");
|
LOAN_COB("Loan COB"), //
|
||||||
|
LOAN_DELINQUENCY_CLASSIFICATION("Loan Delinquency Classification");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.delinquency.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class LoanDelinquencyTagHistoryData implements Serializable {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long loanId;
|
||||||
|
private String delinquencyRange;
|
||||||
|
private LocalDate addedOnDate;
|
||||||
|
private LocalDate liftedOnDate;
|
||||||
|
|
||||||
|
}
|
||||||
+2
-2
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.delinquency.domain;
|
package org.apache.fineract.portfolio.delinquency.domain;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.FetchType;
|
import javax.persistence.FetchType;
|
||||||
@@ -46,7 +46,7 @@ public class DelinquencyBucket extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
|
|
||||||
@ManyToMany(fetch = FetchType.LAZY)
|
@ManyToMany(fetch = FetchType.LAZY)
|
||||||
@JoinTable(name = "m_delinquency_bucket_mappings", joinColumns = @JoinColumn(name = "delinquency_bucket_id"), inverseJoinColumns = @JoinColumn(name = "delinquency_range_id"))
|
@JoinTable(name = "m_delinquency_bucket_mappings", joinColumns = @JoinColumn(name = "delinquency_bucket_id"), inverseJoinColumns = @JoinColumn(name = "delinquency_range_id"))
|
||||||
private Set<DelinquencyRange> ranges;
|
private List<DelinquencyRange> ranges;
|
||||||
|
|
||||||
@Version
|
@Version
|
||||||
private Long version;
|
private Long version;
|
||||||
|
|||||||
+1
@@ -25,4 +25,5 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
|||||||
public interface DelinquencyRangeRepository extends JpaRepository<DelinquencyRange, Long>, JpaSpecificationExecutor<DelinquencyRange> {
|
public interface DelinquencyRangeRepository extends JpaRepository<DelinquencyRange, Long>, JpaSpecificationExecutor<DelinquencyRange> {
|
||||||
|
|
||||||
Optional<DelinquencyRange> findByClassification(String classification);
|
Optional<DelinquencyRange> findByClassification(String classification);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -28,6 +28,8 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
|||||||
public interface LoanDelinquencyTagHistoryRepository
|
public interface LoanDelinquencyTagHistoryRepository
|
||||||
extends JpaRepository<LoanDelinquencyTagHistory, Long>, JpaSpecificationExecutor<LoanDelinquencyTagHistory> {
|
extends JpaRepository<LoanDelinquencyTagHistory, Long>, JpaSpecificationExecutor<LoanDelinquencyTagHistory> {
|
||||||
|
|
||||||
|
LoanDelinquencyTagHistory findFirstByLoanOrderByAddedOnDateDesc(Loan loan);
|
||||||
|
|
||||||
List<LoanDelinquencyTagHistory> findByLoanOrderByAddedOnDateDesc(Loan loan);
|
List<LoanDelinquencyTagHistory> findByLoanOrderByAddedOnDateDesc(Loan loan);
|
||||||
|
|
||||||
Optional<LoanDelinquencyTagHistory> findByLoanAndLiftedOnDate(Loan loan, LocalDate liftedOnDate);
|
Optional<LoanDelinquencyTagHistory> findByLoanAndLiftedOnDate(Loan loan, LocalDate liftedOnDate);
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.delinquency.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.annotation.CommandType;
|
||||||
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@CommandType(entity = "LOAN", action = "UPDATEDELINQUENCY")
|
||||||
|
public class UpdateLoanDelinquencyBucketCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final DelinquencyWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.applyDelinquencyTagToLoan(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.delinquency.mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Mappings;
|
||||||
|
|
||||||
|
@Mapper(componentModel = "spring")
|
||||||
|
public interface LoanDelinquencyTagMapper {
|
||||||
|
|
||||||
|
@Mappings({ @Mapping(target = "id", source = "source.id"), @Mapping(target = "loanId", source = "source.loan.id"),
|
||||||
|
@Mapping(target = "delinquencyRange", source = "source.delinquencyRange.classification"),
|
||||||
|
@Mapping(target = "addedOnDate", source = "source.addedOnDate"),
|
||||||
|
@Mapping(target = "liftedOnDate", source = "source.liftedOnDate") })
|
||||||
|
LoanDelinquencyTagHistoryData map(LoanDelinquencyTagHistory source);
|
||||||
|
|
||||||
|
List<LoanDelinquencyTagHistoryData> map(List<LoanDelinquencyTagHistory> sources);
|
||||||
|
|
||||||
|
}
|
||||||
+4
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.delinquency.service;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
||||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
|
||||||
|
|
||||||
public interface DelinquencyReadPlatformService {
|
public interface DelinquencyReadPlatformService {
|
||||||
|
|
||||||
@@ -33,4 +34,7 @@ public interface DelinquencyReadPlatformService {
|
|||||||
DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId);
|
DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId);
|
||||||
|
|
||||||
DelinquencyRangeData retrieveCurrentDelinquencyTag(Long loanId);
|
DelinquencyRangeData retrieveCurrentDelinquencyTag(Long loanId);
|
||||||
|
|
||||||
|
Collection<LoanDelinquencyTagHistoryData> retrieveDelinquencyRangeHistory(Long loanId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-36
@@ -18,28 +18,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.delinquency.service;
|
package org.apache.fineract.portfolio.delinquency.service;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
||||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
|
||||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
|
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
|
||||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
|
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
|
||||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
|
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
|
||||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
|
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
|
||||||
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
|
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
|
||||||
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
|
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
|
||||||
import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException;
|
|
||||||
import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
|
import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
|
||||||
import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
|
import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -54,6 +51,7 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
|
|||||||
private final LoanDelinquencyTagHistoryRepository repositoryLoanDelinquencyTagHistory;
|
private final LoanDelinquencyTagHistoryRepository repositoryLoanDelinquencyTagHistory;
|
||||||
private final DelinquencyRangeMapper mapperRange;
|
private final DelinquencyRangeMapper mapperRange;
|
||||||
private final DelinquencyBucketMapper mapperBucket;
|
private final DelinquencyBucketMapper mapperBucket;
|
||||||
|
private final LoanDelinquencyTagMapper mapperLoanDelinquencyTagHistory;
|
||||||
private final LoanAssembler loanAssembler;
|
private final LoanAssembler loanAssembler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,19 +74,10 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId) {
|
public DelinquencyBucketData retrieveDelinquencyBucket(Long delinquencyBucketId) {
|
||||||
try {
|
final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
|
||||||
final DelinquencyBucket delinquencyBucket = repositoryBucket.getReferenceById(delinquencyBucketId);
|
final DelinquencyBucketData delinquencyBucketData = mapperBucket.map(delinquencyBucket);
|
||||||
final DelinquencyBucketData delinquencyBucketData = mapperBucket.map(delinquencyBucket);
|
delinquencyBucketData.setRanges(mapperRange.map(delinquencyBucket.getRanges()));
|
||||||
|
return delinquencyBucketData;
|
||||||
final DelinquencyBucketMappingMapper rmMappings = new DelinquencyBucketMappingMapper();
|
|
||||||
final String sql = "select " + rmMappings.schema() + " where buc.id = ? order by dran.min_age_days";
|
|
||||||
List<DelinquencyRangeData> ranges = this.jdbcTemplate.query(sql, rmMappings, delinquencyBucketId); // NOSONAR
|
|
||||||
delinquencyBucketData.setRanges(ranges);
|
|
||||||
|
|
||||||
return delinquencyBucketData;
|
|
||||||
} catch (final EmptyResultDataAccessException e) {
|
|
||||||
throw DelinquencyBucketNotFoundException.notFound(delinquencyBucketId, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -102,24 +91,12 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class DelinquencyBucketMappingMapper implements RowMapper<DelinquencyRangeData> {
|
@Override
|
||||||
|
public Collection<LoanDelinquencyTagHistoryData> retrieveDelinquencyRangeHistory(Long loanId) {
|
||||||
public String schema() {
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
return " dran.id as id, dran.classification as classification, dran.min_age_days as minimumAgeDays, dran.max_age_days as maximumAgeDays from m_delinquency_bucket buc "
|
final List<LoanDelinquencyTagHistory> loanDelinquencyTagData = this.repositoryLoanDelinquencyTagHistory
|
||||||
+ " inner join m_delinquency_bucket_mappings dmap on dmap.delinquency_bucket_id = buc.id "
|
.findByLoanOrderByAddedOnDateDesc(loan);
|
||||||
+ " inner join m_delinquency_range dran on dran.id = dmap.delinquency_range_id ";
|
return mapperLoanDelinquencyTagHistory.map(loanDelinquencyTagData);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DelinquencyRangeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
|
||||||
|
|
||||||
final Long id = rs.getLong("id");
|
|
||||||
final String classification = rs.getString("classification");
|
|
||||||
final Integer minimumAgeDays = rs.getInt("minimumAgeDays");
|
|
||||||
final Integer maximumAgeDays = rs.getInt("maximumAgeDays");
|
|
||||||
|
|
||||||
return new DelinquencyRangeData(id, classification, minimumAgeDays, maximumAgeDays);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -37,4 +37,8 @@ public interface DelinquencyWritePlatformService {
|
|||||||
|
|
||||||
CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command);
|
CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult applyDelinquencyTagToLoan(Long loanId, JsonCommand command);
|
||||||
|
|
||||||
|
void applyDelinquencyTagToLoan(Long loanId, Long ageDays);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+133
-38
@@ -27,6 +27,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
@@ -48,12 +49,15 @@ import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeInval
|
|||||||
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
|
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
|
||||||
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
|
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
|
public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlatformService {
|
||||||
|
|
||||||
private final DelinquencyBucketParseAndValidator dataValidatorBucket;
|
private final DelinquencyBucketParseAndValidator dataValidatorBucket;
|
||||||
@@ -65,6 +69,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
|||||||
private final LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository;
|
private final LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository;
|
||||||
private final LoanRepositoryWrapper loanRepository;
|
private final LoanRepositoryWrapper loanRepository;
|
||||||
private final LoanProductRepository loanProductRepository;
|
private final LoanProductRepository loanProductRepository;
|
||||||
|
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
|
public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
|
||||||
@@ -139,11 +144,40 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
|||||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
|
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult applyDelinquencyTagToLoan(Long loanId, JsonCommand command) {
|
||||||
|
Map<String, Object> changes = new HashMap<>();
|
||||||
|
|
||||||
|
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
|
||||||
|
final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
|
||||||
|
if (delinquencyBucket != null) {
|
||||||
|
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||||
|
LoanRepaymentScheduleInstallment loanRepaymentSchedule = this.loanRepaymentScheduleInstallmentRepository
|
||||||
|
.findFirstByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(loan, businessDate, null);
|
||||||
|
|
||||||
|
Long overdueDays = 0L;
|
||||||
|
if (loanRepaymentSchedule != null) {
|
||||||
|
overdueDays = DateUtils.getDifferenceInDays(loanRepaymentSchedule.getDueDate(), businessDate);
|
||||||
|
}
|
||||||
|
changes = lookUpDelinquencyRange(loan, delinquencyBucket, overdueDays);
|
||||||
|
}
|
||||||
|
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loan.getId()).with(changes).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyDelinquencyTagToLoan(Long loanId, Long ageDays) {
|
||||||
|
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
|
||||||
|
final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
|
||||||
|
if (delinquencyBucket != null) {
|
||||||
|
lookUpDelinquencyRange(loan, delinquencyBucket, ageDays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private DelinquencyRange createDelinquencyRange(DelinquencyRangeData data, Map<String, Object> changes) {
|
private DelinquencyRange createDelinquencyRange(DelinquencyRangeData data, Map<String, Object> changes) {
|
||||||
Optional<DelinquencyRange> delinquencyRange = repositoryRange.findByClassification(data.getClassification());
|
Optional<DelinquencyRange> delinquencyRange = repositoryRange.findByClassification(data.getClassification());
|
||||||
|
|
||||||
if (delinquencyRange.isEmpty()) {
|
if (delinquencyRange.isEmpty()) {
|
||||||
if (data.getMinimumAgeDays() > data.getMaximumAgeDays()) {
|
if (data.getMaximumAgeDays() != null && data.getMinimumAgeDays() > data.getMaximumAgeDays()) {
|
||||||
final String errorMessage = "The age days values are invalid, the maximum age days can't be lower than minimum age days";
|
final String errorMessage = "The age days values are invalid, the maximum age days can't be lower than minimum age days";
|
||||||
throw new DelinquencyRangeInvalidAgesException(errorMessage, data.getMinimumAgeDays(), data.getMaximumAgeDays());
|
throw new DelinquencyRangeInvalidAgesException(errorMessage, data.getMinimumAgeDays(), data.getMaximumAgeDays());
|
||||||
}
|
}
|
||||||
@@ -222,18 +256,12 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
|||||||
|
|
||||||
private void validateDelinquencyRanges(List<DelinquencyRange> ranges) {
|
private void validateDelinquencyRanges(List<DelinquencyRange> ranges) {
|
||||||
// Sort the ranges based on the minAgeDays
|
// Sort the ranges based on the minAgeDays
|
||||||
final Comparator<DelinquencyRange> orderByMinAge = new Comparator<DelinquencyRange>() {
|
ranges = sortDelinquencyRangesByMinAge(ranges);
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(DelinquencyRange o1, DelinquencyRange o2) {
|
|
||||||
return o1.getMinimumAgeDays().compareTo(o2.getMinimumAgeDays());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Collections.sort(ranges, orderByMinAge);
|
|
||||||
DelinquencyRange prevDelinquencyRange = null;
|
DelinquencyRange prevDelinquencyRange = null;
|
||||||
for (DelinquencyRange delinquencyRange : ranges) {
|
for (DelinquencyRange delinquencyRange : ranges) {
|
||||||
if (prevDelinquencyRange != null) {
|
if (prevDelinquencyRange != null) {
|
||||||
if (isOverlaped(prevDelinquencyRange, delinquencyRange)) {
|
if (isOverlapped(prevDelinquencyRange, delinquencyRange)) {
|
||||||
final String errorMessage = "The delinquency ranges age days values are overlaped";
|
final String errorMessage = "The delinquency ranges age days values are overlaped";
|
||||||
throw new DelinquencyBucketAgesOverlapedException(errorMessage, prevDelinquencyRange, delinquencyRange);
|
throw new DelinquencyBucketAgesOverlapedException(errorMessage, prevDelinquencyRange, delinquencyRange);
|
||||||
}
|
}
|
||||||
@@ -242,37 +270,104 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isOverlaped(DelinquencyRange o1, DelinquencyRange o2) {
|
private boolean isOverlapped(DelinquencyRange o1, DelinquencyRange o2) {
|
||||||
return Math.max(o1.getMinimumAgeDays(), o2.getMinimumAgeDays()) <= Math.min(o1.getMaximumAgeDays(), o2.getMaximumAgeDays());
|
if (o2.getMaximumAgeDays() != null) { // Max Age undefined - Last one
|
||||||
}
|
return Math.max(o1.getMinimumAgeDays(), o2.getMinimumAgeDays()) <= Math.min(o1.getMaximumAgeDays(), o2.getMaximumAgeDays());
|
||||||
|
|
||||||
public void setLoanDelinquencyTag(Long loanId, Long delinquencyRangeId) {
|
|
||||||
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
|
|
||||||
|
|
||||||
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
|
|
||||||
List<LoanDelinquencyTagHistory> loanDelinquencyTagHistory = this.loanDelinquencyTagRepository
|
|
||||||
.findByLoanOrderByAddedOnDateDesc(loan);
|
|
||||||
// The delinquencyRangeId in null means just goes out from Delinquency
|
|
||||||
if (delinquencyRangeId == null) {
|
|
||||||
// The Loan will go out from Delinquency
|
|
||||||
if (!loanDelinquencyTagHistory.isEmpty()) {
|
|
||||||
LoanDelinquencyTagHistory loanDelinquencyTagPrev = loanDelinquencyTagHistory.get(0);
|
|
||||||
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
|
||||||
this.loanDelinquencyTagRepository.saveAndFlush(loanDelinquencyTagPrev);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// The previous Loan Delinquency Tag will set as Lifted
|
return Math.max(o1.getMinimumAgeDays(), o2.getMinimumAgeDays()) <= Math.min(o1.getMaximumAgeDays(), o2.getMinimumAgeDays());
|
||||||
if (!loanDelinquencyTagHistory.isEmpty()) {
|
|
||||||
LoanDelinquencyTagHistory loanDelinquencyTagPrev = loanDelinquencyTagHistory.get(0);
|
|
||||||
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
|
||||||
this.loanDelinquencyTagRepository.save(loanDelinquencyTagPrev);
|
|
||||||
}
|
|
||||||
|
|
||||||
final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
|
|
||||||
LoanDelinquencyTagHistory loanDelinquencyTag = new LoanDelinquencyTagHistory(delinquencyRange, loan, transactionDate, null);
|
|
||||||
loanDelinquencyTagHistory.add(loanDelinquencyTag);
|
|
||||||
this.loanDelinquencyTagRepository.saveAndFlush(loanDelinquencyTag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> lookUpDelinquencyRange(final Loan loan, final DelinquencyBucket delinquencyBucket, long ageDays) {
|
||||||
|
Map<String, Object> changes = new HashMap<>();
|
||||||
|
|
||||||
|
if (ageDays <= 0) { // No Delinquency
|
||||||
|
log.debug("Loan {} without delinquency range with {} days", loan.getId(), ageDays);
|
||||||
|
changes = setLoanDelinquencyTag(loan, null);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Sort the ranges based on the minAgeDays
|
||||||
|
List<DelinquencyRange> ranges = sortDelinquencyRangesByMinAge(delinquencyBucket.getRanges());
|
||||||
|
|
||||||
|
for (DelinquencyRange delinquencyRange : ranges) {
|
||||||
|
if (delinquencyRange.getMaximumAgeDays() == null) {
|
||||||
|
if (delinquencyRange.getMinimumAgeDays() <= ageDays) {
|
||||||
|
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||||
|
ageDays);
|
||||||
|
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (delinquencyRange.getMinimumAgeDays() <= ageDays && delinquencyRange.getMaximumAgeDays() >= ageDays) {
|
||||||
|
log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
|
||||||
|
ageDays);
|
||||||
|
changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changes.put("ageDays", ageDays);
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> setLoanDelinquencyTag(Long loanId, Long delinquencyRangeId) {
|
||||||
|
final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
|
||||||
|
return setLoanDelinquencyTag(loan, delinquencyRangeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> setLoanDelinquencyTag(Loan loan, Long delinquencyRangeId) {
|
||||||
|
Map<String, Object> changes = new HashMap<>();
|
||||||
|
List<LoanDelinquencyTagHistory> loanDelinquencyTagHistory = new ArrayList<>();
|
||||||
|
final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||||
|
Optional<LoanDelinquencyTagHistory> optLoanDelinquencyTag = this.loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(loan, null);
|
||||||
|
// The delinquencyRangeId in null means just goes out from Delinquency
|
||||||
|
LoanDelinquencyTagHistory loanDelinquencyTagPrev = null;
|
||||||
|
if (delinquencyRangeId == null) {
|
||||||
|
// The Loan will go out from Delinquency
|
||||||
|
if (optLoanDelinquencyTag.isPresent()) {
|
||||||
|
loanDelinquencyTagPrev = optLoanDelinquencyTag.get();
|
||||||
|
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
||||||
|
loanDelinquencyTagHistory.add(loanDelinquencyTagPrev);
|
||||||
|
changes.put("previous", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (optLoanDelinquencyTag.isPresent()) {
|
||||||
|
loanDelinquencyTagPrev = optLoanDelinquencyTag.get();
|
||||||
|
}
|
||||||
|
// If the Delinquency Tag has not changed
|
||||||
|
if (loanDelinquencyTagPrev != null && loanDelinquencyTagPrev.getDelinquencyRange().getId().equals(delinquencyRangeId)) {
|
||||||
|
changes.put("current", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||||
|
} else {
|
||||||
|
// The previous Loan Delinquency Tag will set as Lifted
|
||||||
|
if (loanDelinquencyTagPrev != null) {
|
||||||
|
loanDelinquencyTagPrev.setLiftedOnDate(transactionDate);
|
||||||
|
loanDelinquencyTagHistory.add(loanDelinquencyTagPrev);
|
||||||
|
changes.put("previous", loanDelinquencyTagPrev.getDelinquencyRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
final DelinquencyRange delinquencyRange = repositoryRange.getReferenceById(delinquencyRangeId);
|
||||||
|
LoanDelinquencyTagHistory loanDelinquencyTag = new LoanDelinquencyTagHistory(delinquencyRange, loan, transactionDate, null);
|
||||||
|
loanDelinquencyTagHistory.add(loanDelinquencyTag);
|
||||||
|
changes.put("current", loanDelinquencyTag.getDelinquencyRange());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loanDelinquencyTagHistory.size() > 0) {
|
||||||
|
this.loanDelinquencyTagRepository.saveAllAndFlush(loanDelinquencyTagHistory);
|
||||||
|
}
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DelinquencyRange> sortDelinquencyRangesByMinAge(List<DelinquencyRange> ranges) {
|
||||||
|
final Comparator<DelinquencyRange> orderByMinAge = new Comparator<DelinquencyRange>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(DelinquencyRange o1, DelinquencyRange o2) {
|
||||||
|
return o1.getMinimumAgeDays().compareTo(o2.getMinimumAgeDays());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Collections.sort(ranges, orderByMinAge);
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-104
@@ -54,6 +54,7 @@ import javax.ws.rs.core.Context;
|
|||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
@@ -100,6 +101,9 @@ import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
|||||||
import org.apache.fineract.portfolio.client.data.ClientData;
|
import org.apache.fineract.portfolio.client.data.ClientData;
|
||||||
import org.apache.fineract.portfolio.collateralmanagement.data.LoanCollateralResponseData;
|
import org.apache.fineract.portfolio.collateralmanagement.data.LoanCollateralResponseData;
|
||||||
import org.apache.fineract.portfolio.collateralmanagement.service.LoanCollateralManagementReadPlatformService;
|
import org.apache.fineract.portfolio.collateralmanagement.service.LoanCollateralManagementReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiResourceSwagger;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
|
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||||
import org.apache.fineract.portfolio.fund.data.FundData;
|
import org.apache.fineract.portfolio.fund.data.FundData;
|
||||||
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
||||||
@@ -201,6 +205,7 @@ import org.springframework.util.CollectionUtils;
|
|||||||
+ "Specifies rest frequency start date for interest recalculation. This date must be before or equal to disbursement date\n"
|
+ "Specifies rest frequency start date for interest recalculation. This date must be before or equal to disbursement date\n"
|
||||||
+ "recalculationCompoundingFrequencyDate\n"
|
+ "recalculationCompoundingFrequencyDate\n"
|
||||||
+ "Specifies compounding frequency start date for interest recalculation. This date must be equal to disbursement date")
|
+ "Specifies compounding frequency start date for interest recalculation. This date must be equal to disbursement date")
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class LoansApiResource {
|
public class LoansApiResource {
|
||||||
|
|
||||||
private final Set<String> loanDataParameters = new HashSet<>(Arrays.asList("id", "accountNo", "status", "externalId", "clientId",
|
private final Set<String> loanDataParameters = new HashSet<>(Arrays.asList("id", "accountNo", "status", "externalId", "clientId",
|
||||||
@@ -259,63 +264,8 @@ public class LoansApiResource {
|
|||||||
private final DefaultToApiJsonSerializer<GlimRepaymentTemplate> glimTemplateToApiJsonSerializer;
|
private final DefaultToApiJsonSerializer<GlimRepaymentTemplate> glimTemplateToApiJsonSerializer;
|
||||||
private final GLIMAccountInfoReadPlatformService glimAccountInfoReadPlatformService;
|
private final GLIMAccountInfoReadPlatformService glimAccountInfoReadPlatformService;
|
||||||
private final LoanCollateralManagementReadPlatformService loanCollateralManagementReadPlatformService;
|
private final LoanCollateralManagementReadPlatformService loanCollateralManagementReadPlatformService;
|
||||||
|
private final DefaultToApiJsonSerializer<LoanDelinquencyTagHistoryData> jsonSerializerTagHistory;
|
||||||
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
|
private final DelinquencyReadPlatformService delinquencyReadPlatformService;
|
||||||
final LoanProductReadPlatformService loanProductReadPlatformService,
|
|
||||||
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
|
|
||||||
final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
|
|
||||||
final LoanScheduleCalculationPlatformService calculationPlatformService,
|
|
||||||
final GuarantorReadPlatformService guarantorReadPlatformService,
|
|
||||||
final CodeValueReadPlatformService codeValueReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
|
|
||||||
final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
|
|
||||||
final DefaultToApiJsonSerializer<LoanApprovalData> loanApprovalDataToApiJsonSerializer,
|
|
||||||
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
|
|
||||||
final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper,
|
|
||||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
|
||||||
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformService noteReadPlatformService,
|
|
||||||
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
|
|
||||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
|
||||||
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
|
|
||||||
final AccountDetailsReadPlatformService accountDetailsReadPlatformService,
|
|
||||||
final EntityDatatableChecksReadService entityDatatableChecksReadService,
|
|
||||||
final BulkImportWorkbookService bulkImportWorkbookService,
|
|
||||||
final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService, final RateReadService rateReadService,
|
|
||||||
final ConfigurationDomainService configurationDomainService,
|
|
||||||
final DefaultToApiJsonSerializer<GlimRepaymentTemplate> glimTemplateToApiJsonSerializer,
|
|
||||||
final GLIMAccountInfoReadPlatformService glimAccountInfoReadPlatformService,
|
|
||||||
final LoanCollateralManagementReadPlatformService loanCollateralManagementReadPlatformService) {
|
|
||||||
this.context = context;
|
|
||||||
this.loanReadPlatformService = loanReadPlatformService;
|
|
||||||
this.loanProductReadPlatformService = loanProductReadPlatformService;
|
|
||||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
|
||||||
this.fundReadPlatformService = fundReadPlatformService;
|
|
||||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
|
||||||
this.loanChargeReadPlatformService = loanChargeReadPlatformService;
|
|
||||||
this.calculationPlatformService = calculationPlatformService;
|
|
||||||
this.guarantorReadPlatformService = guarantorReadPlatformService;
|
|
||||||
this.codeValueReadPlatformService = codeValueReadPlatformService;
|
|
||||||
this.groupReadPlatformService = groupReadPlatformService;
|
|
||||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
|
||||||
this.loanApprovalDataToApiJsonSerializer = loanApprovalDataToApiJsonSerializer;
|
|
||||||
this.loanScheduleToApiJsonSerializer = loanScheduleToApiJsonSerializer;
|
|
||||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
|
||||||
this.fromJsonHelper = fromJsonHelper;
|
|
||||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
|
||||||
this.calendarReadPlatformService = calendarReadPlatformService;
|
|
||||||
this.noteReadPlatformService = noteReadPlatformService;
|
|
||||||
this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl;
|
|
||||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
|
||||||
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
|
|
||||||
this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
|
|
||||||
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
|
|
||||||
this.rateReadService = rateReadService;
|
|
||||||
this.bulkImportWorkbookService = bulkImportWorkbookService;
|
|
||||||
this.bulkImportWorkbookPopulatorService = bulkImportWorkbookPopulatorService;
|
|
||||||
this.configurationDomainService = configurationDomainService;
|
|
||||||
this.glimTemplateToApiJsonSerializer = glimTemplateToApiJsonSerializer;
|
|
||||||
this.glimAccountInfoReadPlatformService = glimAccountInfoReadPlatformService;
|
|
||||||
this.loanCollateralManagementReadPlatformService = loanCollateralManagementReadPlatformService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This template API is used for loan approval, ideally this should be invoked on loan that are pending for
|
* This template API is used for loan approval, ideally this should be invoked on loan that are pending for
|
||||||
@@ -886,51 +836,37 @@ public class LoansApiResource {
|
|||||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||||
|
CommandWrapper commandRequest = null;
|
||||||
CommandProcessingResult result = null;
|
|
||||||
|
|
||||||
if (is(commandParam, "reject")) {
|
if (is(commandParam, "reject")) {
|
||||||
final CommandWrapper commandRequest = builder.rejectLoanApplication(loanId).build();
|
commandRequest = builder.rejectLoanApplication(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||||
final CommandWrapper commandRequest = builder.withdrawLoanApplication(loanId).build();
|
commandRequest = builder.withdrawLoanApplication(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "approve")) {
|
} else if (is(commandParam, "approve")) {
|
||||||
final CommandWrapper commandRequest = builder.approveLoanApplication(loanId).build();
|
commandRequest = builder.approveLoanApplication(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "disburse")) {
|
} else if (is(commandParam, "disburse")) {
|
||||||
final CommandWrapper commandRequest = builder.disburseLoanApplication(loanId).build();
|
commandRequest = builder.disburseLoanApplication(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "disburseToSavings")) {
|
} else if (is(commandParam, "disburseToSavings")) {
|
||||||
final CommandWrapper commandRequest = builder.disburseLoanToSavingsApplication(loanId).build();
|
commandRequest = builder.disburseLoanToSavingsApplication(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
} else if (is(commandParam, "undoapproval")) {
|
||||||
}
|
commandRequest = builder.undoLoanApplicationApproval(loanId).build();
|
||||||
|
|
||||||
if (is(commandParam, "undoapproval")) {
|
|
||||||
final CommandWrapper commandRequest = builder.undoLoanApplicationApproval(loanId).build();
|
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "undodisbursal")) {
|
} else if (is(commandParam, "undodisbursal")) {
|
||||||
final CommandWrapper commandRequest = builder.undoLoanApplicationDisbursal(loanId).build();
|
commandRequest = builder.undoLoanApplicationDisbursal(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "undolastdisbursal")) {
|
} else if (is(commandParam, "undolastdisbursal")) {
|
||||||
final CommandWrapper commandRequest = builder.undoLastDisbursalLoanApplication(loanId).build();
|
commandRequest = builder.undoLastDisbursalLoanApplication(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
} else if (is(commandParam, "assignloanofficer")) {
|
||||||
}
|
commandRequest = builder.assignLoanOfficer(loanId).build();
|
||||||
|
|
||||||
if (is(commandParam, "assignloanofficer")) {
|
|
||||||
final CommandWrapper commandRequest = builder.assignLoanOfficer(loanId).build();
|
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "unassignloanofficer")) {
|
} else if (is(commandParam, "unassignloanofficer")) {
|
||||||
final CommandWrapper commandRequest = builder.unassignLoanOfficer(loanId).build();
|
commandRequest = builder.unassignLoanOfficer(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "recoverGuarantees")) {
|
} else if (is(commandParam, "recoverGuarantees")) {
|
||||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(loanId).build();
|
commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
} else if (is(commandParam, "assigndelinquency")) {
|
||||||
|
commandRequest = builder.assignDelinquency(loanId).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null) {
|
if (commandRequest == null) {
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||||
}
|
}
|
||||||
|
CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
@@ -978,31 +914,25 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||||
|
|
||||||
CommandProcessingResult result = null;
|
CommandWrapper commandRequest = null;
|
||||||
|
|
||||||
if (is(commandParam, "reject")) {
|
if (is(commandParam, "reject")) {
|
||||||
final CommandWrapper commandRequest = builder.rejectGLIMApplication(glimId).build();
|
commandRequest = builder.rejectGLIMApplication(glimId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "approve")) {
|
} else if (is(commandParam, "approve")) {
|
||||||
final CommandWrapper commandRequest = builder.approveGLIMLoanApplication(glimId).build();
|
commandRequest = builder.approveGLIMLoanApplication(glimId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "disburse")) {
|
} else if (is(commandParam, "disburse")) {
|
||||||
final CommandWrapper commandRequest = builder.disburseGlimLoanApplication(glimId).build();
|
commandRequest = builder.disburseGlimLoanApplication(glimId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "glimrepayment")) {
|
} else if (is(commandParam, "glimrepayment")) {
|
||||||
final CommandWrapper commandRequest = builder.repaymentGlimLoanApplication(glimId).build();
|
commandRequest = builder.repaymentGlimLoanApplication(glimId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "undodisbursal")) {
|
} else if (is(commandParam, "undodisbursal")) {
|
||||||
final CommandWrapper commandRequest = builder.undoGLIMLoanDisbursal(glimId).build();
|
commandRequest = builder.undoGLIMLoanDisbursal(glimId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
} else if (is(commandParam, "undoapproval")) {
|
} else if (is(commandParam, "undoapproval")) {
|
||||||
final CommandWrapper commandRequest = builder.undoGLIMLoanApproval(glimId).build();
|
commandRequest = builder.undoGLIMLoanApproval(glimId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null) {
|
if (commandRequest == null) {
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||||
}
|
}
|
||||||
|
CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
@@ -1040,4 +970,21 @@ public class LoansApiResource {
|
|||||||
uploadedInputStream, fileDetail, locale, dateFormat);
|
uploadedInputStream, fileDetail, locale, dateFormat);
|
||||||
return this.toApiJsonSerializer.serialize(importDocumentId);
|
return this.toApiJsonSerializer.serialize(importDocumentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{loanId}/delinquencytags")
|
||||||
|
@Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "Retrieve the Loan Delinquency Tag history using the Loan Id", description = "")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = DelinquencyApiResourceSwagger.GetDelinquencyTagHistoryResponse.class))) })
|
||||||
|
public String getDelinquencyTagHistory(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||||
|
@Context final UriInfo uriInfo) {
|
||||||
|
context.authenticatedUser().validateHasReadPermission("DELINQUENCY_TAGS");
|
||||||
|
final Collection<LoanDelinquencyTagHistoryData> loanDelinquencyTagHistoryData = this.delinquencyReadPlatformService
|
||||||
|
.retrieveDelinquencyRangeHistory(loanId);
|
||||||
|
ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.jsonSerializerTagHistory.serialize(settings, loanDelinquencyTagHistoryData);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class LoanScheduleDelinquencyData implements Serializable {
|
||||||
|
|
||||||
|
private final Long loanId;
|
||||||
|
private final Long productId;
|
||||||
|
private final LocalDate dueDate;
|
||||||
|
private final LocalDate fromDate;
|
||||||
|
private final Long ageDays;
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -18,10 +18,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
public interface LoanRepaymentScheduleInstallmentRepository
|
public interface LoanRepaymentScheduleInstallmentRepository
|
||||||
extends JpaRepository<LoanRepaymentScheduleInstallment, Long>, JpaSpecificationExecutor<LoanRepaymentScheduleInstallment> {
|
extends JpaRepository<LoanRepaymentScheduleInstallment, Long>, JpaSpecificationExecutor<LoanRepaymentScheduleInstallment> {
|
||||||
|
|
||||||
|
LoanRepaymentScheduleInstallment findFirstByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(Loan loan,
|
||||||
|
LocalDate businnessDate, LocalDate obligationsMetOnDate);
|
||||||
|
|
||||||
|
List<LoanRepaymentScheduleInstallment> findByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(Loan loan,
|
||||||
|
LocalDate businnessDate, LocalDate obligationsMetOnDate);
|
||||||
}
|
}
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.jobs.setloandelinquencytags;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||||
|
import org.springframework.batch.core.Job;
|
||||||
|
import org.springframework.batch.core.Step;
|
||||||
|
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||||
|
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||||
|
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SetLoanDelinquencyTagsConfig {
|
||||||
|
|
||||||
|
private JobBuilderFactory jobs;
|
||||||
|
private StepBuilderFactory steps;
|
||||||
|
|
||||||
|
private LoanProductRepository loanProductRepository;
|
||||||
|
private DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||||
|
private LoanReadPlatformService loanReadPlatformService;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Step setLoanDelinquencyTagsStep() {
|
||||||
|
return steps.get(JobName.LOAN_DELINQUENCY_CLASSIFICATION.name()).tasklet(setLoanDelinquencyTagsTasklet()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Job setLoanDelinquencyTagsJob() {
|
||||||
|
return jobs.get(JobName.LOAN_DELINQUENCY_CLASSIFICATION.name()).start(setLoanDelinquencyTagsStep())
|
||||||
|
.incrementer(new RunIdIncrementer()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SetLoanDelinquencyTagsTasklet setLoanDelinquencyTagsTasklet() {
|
||||||
|
return new SetLoanDelinquencyTagsTasklet(loanProductRepository, delinquencyWritePlatformService, loanReadPlatformService);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.jobs.setloandelinquencytags;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
|
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||||
|
import org.springframework.batch.core.StepContribution;
|
||||||
|
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||||
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
|
import org.springframework.batch.repeat.RepeatStatus;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SetLoanDelinquencyTagsTasklet implements Tasklet {
|
||||||
|
|
||||||
|
private final LoanProductRepository loanProductRepository;
|
||||||
|
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||||
|
private final LoanReadPlatformService loanReadPlatformService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||||
|
Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData = this.loanReadPlatformService
|
||||||
|
.retrieveScheduleDelinquencyData(DateUtils.getBusinessLocalDate());
|
||||||
|
log.debug("Were found {} items", loanScheduleDelinquencyData.size());
|
||||||
|
for (LoanScheduleDelinquencyData loanDelinquencyData : loanScheduleDelinquencyData) {
|
||||||
|
this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData.getLoanId(),
|
||||||
|
loanDelinquencyData.getAgeDays());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("{}: Records affected by setLoanDelinquencyTags: {}", ThreadLocalContextUtil.getTenant().getName(),
|
||||||
|
loanScheduleDelinquencyData.size());
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-1
@@ -40,7 +40,8 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPeriodicAccruals(final LocalDate tillDate) throws JobExecutionException {
|
public void addPeriodicAccruals(final LocalDate tillDate) throws JobExecutionException {
|
||||||
Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList = this.loanReadPlatformService.retrivePeriodicAccrualData(tillDate);
|
Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList = this.loanReadPlatformService
|
||||||
|
.retrievePeriodicAccrualData(tillDate);
|
||||||
addPeriodicAccruals(tillDate, loanScheduleAccrualDataList);
|
addPeriodicAccruals(tillDate, loanScheduleAccrualDataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -33,6 +33,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
|
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
|
||||||
@@ -104,11 +105,13 @@ public interface LoanReadPlatformService {
|
|||||||
|
|
||||||
Collection<LoanScheduleAccrualData> retriveScheduleAccrualData();
|
Collection<LoanScheduleAccrualData> retriveScheduleAccrualData();
|
||||||
|
|
||||||
|
Collection<LoanScheduleDelinquencyData> retrieveScheduleDelinquencyData(LocalDate businessLocalDate);
|
||||||
|
|
||||||
LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
|
LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
|
||||||
|
|
||||||
LoanTransactionData retrieveLoanWriteoffTemplate(Long loanId);
|
LoanTransactionData retrieveLoanWriteoffTemplate(Long loanId);
|
||||||
|
|
||||||
Collection<LoanScheduleAccrualData> retrivePeriodicAccrualData(LocalDate tillDate);
|
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate);
|
||||||
|
|
||||||
Collection<Long> fetchLoansForInterestRecalculation();
|
Collection<Long> fetchLoansForInterestRecalculation();
|
||||||
|
|
||||||
|
|||||||
+46
-1
@@ -88,6 +88,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
@@ -1647,6 +1648,22 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<LoanScheduleDelinquencyData> retrieveScheduleDelinquencyData(LocalDate businessLocalDate) {
|
||||||
|
LoanScheduleDelinquencyMapper mapper = new LoanScheduleDelinquencyMapper(DateUtils.getBusinessLocalDate());
|
||||||
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
|
sqlBuilder.append("select ").append(mapper.schema())
|
||||||
|
.append(" where loan.loan_status_id=:active and ls.duedate <= :businessLocalDate and ")
|
||||||
|
.append(" mpl.delinquency_bucket_id is not null "); // Jusr the Loan Product linked to delinquency
|
||||||
|
// bucket
|
||||||
|
sqlBuilder.append(" order by loan.id, ls.duedate ");
|
||||||
|
Map<String, Object> paramMap = new HashMap<>(3);
|
||||||
|
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||||
|
paramMap.put("businessLocalDate", businessLocalDate);
|
||||||
|
|
||||||
|
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<LoanScheduleAccrualData> retriveScheduleAccrualData() {
|
public Collection<LoanScheduleAccrualData> retriveScheduleAccrualData() {
|
||||||
|
|
||||||
@@ -1673,7 +1690,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<LoanScheduleAccrualData> retrivePeriodicAccrualData(final LocalDate tillDate) {
|
public Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(final LocalDate tillDate) {
|
||||||
|
|
||||||
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
||||||
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||||
@@ -1816,6 +1833,34 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class LoanScheduleDelinquencyMapper implements RowMapper<LoanScheduleDelinquencyData> {
|
||||||
|
|
||||||
|
private LocalDate businessDate;
|
||||||
|
|
||||||
|
LoanScheduleDelinquencyMapper(LocalDate businessDate) {
|
||||||
|
this.businessDate = businessDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
|
sqlBuilder.append(" loan.id as loanId, ls.duedate as duedate, ls.fromdate as fromdate, loan.product_id as productId ")
|
||||||
|
.append(" from m_loan_repayment_schedule ls left join m_loan loan on loan.id=ls.loan_id ")
|
||||||
|
.append(" left join m_product_loan mpl on mpl.id = loan.product_id ");
|
||||||
|
return sqlBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoanScheduleDelinquencyData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||||
|
|
||||||
|
final Long loanId = rs.getLong("loanId");
|
||||||
|
final Long productId = rs.getLong("productId");
|
||||||
|
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "duedate");
|
||||||
|
final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromdate");
|
||||||
|
final long ageDays = DateUtils.getDifferenceInDays(dueDate, businessDate);
|
||||||
|
return new LoanScheduleDelinquencyData(loanId, productId, dueDate, fromDate, ageDays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId) {
|
public LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId) {
|
||||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||||
|
|||||||
+2
@@ -31,4 +31,6 @@ public interface LoanProductRepository extends JpaRepository<LoanProduct, Long>,
|
|||||||
List<LoanProduct> retrieveLoanProductsByChargeId(@Param("chargeId") Long chargeId);
|
List<LoanProduct> retrieveLoanProductsByChargeId(@Param("chargeId") Long chargeId);
|
||||||
|
|
||||||
Long countByDelinquencyBucket(DelinquencyBucket delinquencyBucket);
|
Long countByDelinquencyBucket(DelinquencyBucket delinquencyBucket);
|
||||||
|
|
||||||
|
List<LoanProduct> findByDelinquencyBucketNotNull();
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -143,6 +143,12 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
|||||||
this.aprCalculator, floatingRate, rates);
|
this.aprCalculator, floatingRate, rates);
|
||||||
loanProduct.updateLoanProductInRelatedClasses();
|
loanProduct.updateLoanProductInRelatedClasses();
|
||||||
|
|
||||||
|
if (command.parameterExists("delinquencyBucketId")) {
|
||||||
|
DelinquencyBucket delinquencyBucket = this.delinquencyBucketRepository
|
||||||
|
.getReferenceById(command.longValueOfParameterNamed("delinquencyBucketId"));
|
||||||
|
loanProduct.setDelinquencyBucket(delinquencyBucket);
|
||||||
|
}
|
||||||
|
|
||||||
this.loanProductRepository.saveAndFlush(loanProduct);
|
this.loanProductRepository.saveAndFlush(loanProduct);
|
||||||
|
|
||||||
// save accounting mappings
|
// save accounting mappings
|
||||||
|
|||||||
@@ -59,4 +59,5 @@
|
|||||||
<include file="parts/0037_add_loan_cob_job_data.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0037_add_loan_cob_job_data.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0038_add_reversal_data_to_loan_transaction.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0038_add_reversal_data_to_loan_transaction.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0039_add_loan_account_locks.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0039_add_loan_account_locks.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0040_add_delinquency_tags_job.xml" relativeToChangelogFile="true"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||||
|
<changeSet author="fineract" id="delinquency_tags_job-1">
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="organisation"/>
|
||||||
|
<column name="code" value="READ_DELINQUENCY_TAGS"/>
|
||||||
|
<column name="entity_name" value="DELINQUENCY_TAGS"/>
|
||||||
|
<column name="action_name" value="READ"/>
|
||||||
|
<column name="can_maker_checker" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="organisation"/>
|
||||||
|
<column name="code" value="UPDATE_DELINQUENCY_TAGS"/>
|
||||||
|
<column name="entity_name" value="DELINQUENCY_TAGS"/>
|
||||||
|
<column name="action_name" value="UPDATE"/>
|
||||||
|
<column name="can_maker_checker" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="organisation"/>
|
||||||
|
<column name="code" value="UPDATEDELINQUENCY_LOAN"/>
|
||||||
|
<column name="entity_name" value="LOAN"/>
|
||||||
|
<column name="action_name" value="UPDATEDELINQUENCY"/>
|
||||||
|
<column name="can_maker_checker" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="fineract" id="delinquency_tags_job-2">
|
||||||
|
<insert tableName="job">
|
||||||
|
<column name="name" value="Loan Delinquency Classification"/>
|
||||||
|
<column name="display_name" value="Loan Delinquency Classification"/>
|
||||||
|
<column name="cron_expression" value="0 0 22 1/1 * ? *"/>
|
||||||
|
<column name="create_time" valueDate="${current_datetime}"/>
|
||||||
|
<column name="task_priority" valueNumeric="5"/>
|
||||||
|
<column name="group_name"/>
|
||||||
|
<column name="previous_run_start_time"/>
|
||||||
|
<column name="job_key" value="Loan Delinquency ClassificationDetail1 _ DEFAULT"/>
|
||||||
|
<column name="initializing_errorlog"/>
|
||||||
|
<column name="is_active" valueBoolean="true"/>
|
||||||
|
<column name="currently_running" valueBoolean="false"/>
|
||||||
|
<column name="updates_allowed" valueBoolean="true"/>
|
||||||
|
<column name="scheduler_group" valueNumeric="0"/>
|
||||||
|
<column name="is_misfired" valueBoolean="false"/>
|
||||||
|
<column name="node_id" valueNumeric="1"/>
|
||||||
|
<column name="is_mismatched_job" valueBoolean="true"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+78
@@ -27,21 +27,32 @@ import io.restassured.builder.ResponseSpecBuilder;
|
|||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import io.restassured.specification.RequestSpecification;
|
import io.restassured.specification.RequestSpecification;
|
||||||
import io.restassured.specification.ResponseSpecification;
|
import io.restassured.specification.ResponseSpecification;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.client.models.DeleteDelinquencyBucketResponse;
|
import org.apache.fineract.client.models.DeleteDelinquencyBucketResponse;
|
||||||
import org.apache.fineract.client.models.DeleteDelinquencyRangeResponse;
|
import org.apache.fineract.client.models.DeleteDelinquencyRangeResponse;
|
||||||
import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
|
import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
|
||||||
import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
|
import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
|
||||||
|
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||||
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
|
import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
|
||||||
import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
|
import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
|
||||||
import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
|
import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
|
||||||
import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
|
import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
|
||||||
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||||
import org.apache.fineract.integrationtests.common.Utils;
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
|
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||||
|
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||||
|
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||||
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
|
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
|
||||||
import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
|
import org.apache.fineract.integrationtests.common.products.DelinquencyRangesHelper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class DelinquencyBucketsIntegrationTest {
|
public class DelinquencyBucketsIntegrationTest {
|
||||||
|
|
||||||
private ResponseSpecification responseSpec;
|
private ResponseSpecification responseSpec;
|
||||||
@@ -208,4 +219,71 @@ public class DelinquencyBucketsIntegrationTest {
|
|||||||
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, response403Spec, jsonBucket);
|
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, response403Spec, jsonBucket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoanClassificationJob() {
|
||||||
|
// Given
|
||||||
|
final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
|
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
|
||||||
|
|
||||||
|
ArrayList<Integer> rangeIds = new ArrayList<>();
|
||||||
|
String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
|
||||||
|
PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
|
||||||
|
jsonRange);
|
||||||
|
rangeIds.add(delinquencyRangeResponse.getResourceId());
|
||||||
|
jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
|
||||||
|
// Create
|
||||||
|
delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
|
||||||
|
rangeIds.add(delinquencyRangeResponse.getResourceId());
|
||||||
|
|
||||||
|
final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
|
||||||
|
delinquencyRangeResponse.getResourceId());
|
||||||
|
final String classificationExpected = range.getClassification();
|
||||||
|
log.info("Expected Delinquency Range classification {}", classificationExpected);
|
||||||
|
|
||||||
|
String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
|
||||||
|
PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
|
||||||
|
responseSpec, jsonBucket);
|
||||||
|
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
|
||||||
|
delinquencyBucketResponse.getResourceId());
|
||||||
|
|
||||||
|
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
|
||||||
|
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucket.getId());
|
||||||
|
final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
|
||||||
|
|
||||||
|
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = loanTransactionHelper.getLoanProduct(loanProductId);
|
||||||
|
log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
|
||||||
|
|
||||||
|
LocalDate todaysDate = Utils.getLocalDateOfTenant();
|
||||||
|
todaysDate = todaysDate.minusDays(40);
|
||||||
|
final String operationDate = Utils.dateFormatter.format(todaysDate);
|
||||||
|
final String principalAmount = "10000";
|
||||||
|
|
||||||
|
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principalAmount).withLoanTermFrequency("12")
|
||||||
|
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
|
||||||
|
.withRepaymentFrequencyTypeAsMonths() //
|
||||||
|
.withInterestRatePerPeriod("2") //
|
||||||
|
.withExpectedDisbursementDate(operationDate) //
|
||||||
|
.withInterestTypeAsDecliningBalance() //
|
||||||
|
.withSubmittedOnDate(operationDate) //
|
||||||
|
.build(clientId.toString(), loanProductId.toString(), null);
|
||||||
|
final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||||
|
loanTransactionHelper.approveLoan(operationDate, principalAmount, loanId, null);
|
||||||
|
loanTransactionHelper.disburseLoanWithNetDisbursalAmount(operationDate, loanId, principalAmount);
|
||||||
|
|
||||||
|
// When
|
||||||
|
final String jobName = "Loan Delinquency Classification";
|
||||||
|
schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
|
|
||||||
|
final GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||||
|
log.info("Loan Delinquency Range {}", getLoansLoanIdResponse.getDelinquencyRange().getClassification());
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertNotNull(delinquencyBucketResponse);
|
||||||
|
assertNotNull(getLoanProductsProductResponse);
|
||||||
|
assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
|
||||||
|
assertNotNull(getLoansLoanIdResponse);
|
||||||
|
assertEquals(getLoansLoanIdResponse.getDelinquencyRange().getClassification(), classificationExpected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import java.time.temporal.ChronoField;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -421,4 +422,9 @@ public final class Utils {
|
|||||||
}
|
}
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String convertToJson(HashMap<String, Object> map) {
|
||||||
|
return new Gson().toJson(map);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-1
@@ -132,6 +132,11 @@ public class LoanProductTestBuilder {
|
|||||||
private String installmentAmountInMultiplesOf;
|
private String installmentAmountInMultiplesOf;
|
||||||
|
|
||||||
public String build(final String chargeId) {
|
public String build(final String chargeId) {
|
||||||
|
final HashMap<String, Object> map = build(chargeId, null);
|
||||||
|
return new Gson().toJson(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, Object> build(final String chargeId, final Integer delinquencyBucketId) {
|
||||||
final HashMap<String, Object> map = new HashMap<>();
|
final HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
if (chargeId != null) {
|
if (chargeId != null) {
|
||||||
@@ -234,7 +239,13 @@ public class LoanProductTestBuilder {
|
|||||||
if (installmentAmountInMultiplesOf != null) {
|
if (installmentAmountInMultiplesOf != null) {
|
||||||
map.put("installmentAmountInMultiplesOf", this.installmentAmountInMultiplesOf);
|
map.put("installmentAmountInMultiplesOf", this.installmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
return new Gson().toJson(map);
|
|
||||||
|
// Delinquency Bucket
|
||||||
|
if (delinquencyBucketId != null) {
|
||||||
|
map.put("delinquencyBucketId", delinquencyBucketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanProductTestBuilder withInstallmentAmountInMultiplesOf(String installmentAmountInMultiplesOf) {
|
public LoanProductTestBuilder withInstallmentAmountInMultiplesOf(String installmentAmountInMultiplesOf) {
|
||||||
|
|||||||
+19
@@ -38,6 +38,9 @@ import java.util.Map;
|
|||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||||
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
|
import org.apache.fineract.client.util.JSON;
|
||||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||||
import org.apache.fineract.integrationtests.common.Utils;
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
@@ -73,11 +76,20 @@ public class LoanTransactionHelper {
|
|||||||
|
|
||||||
public static final String DATE_TIME_FORMAT = "dd MMMM yyyy HH:mm";
|
public static final String DATE_TIME_FORMAT = "dd MMMM yyyy HH:mm";
|
||||||
|
|
||||||
|
private static final Gson GSON = new JSON().getGson();
|
||||||
|
|
||||||
public LoanTransactionHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
public LoanTransactionHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||||
this.requestSpec = requestSpec;
|
this.requestSpec = requestSpec;
|
||||||
this.responseSpec = responseSpec;
|
this.responseSpec = responseSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GetLoanProductsProductIdResponse getLoanProduct(final Integer loanProductId) {
|
||||||
|
final String GET_LOANPRODUCT_URL = "/fineract-provider/api/v1/loanproducts/" + loanProductId + "?" + Utils.TENANT_IDENTIFIER;
|
||||||
|
final String response = Utils.performServerGet(this.requestSpec, this.responseSpec, GET_LOANPRODUCT_URL);
|
||||||
|
log.info("Response {}", response);
|
||||||
|
return GSON.fromJson(response, GetLoanProductsProductIdResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getLoanProductId(final String loanProductJSON) {
|
public Integer getLoanProductId(final String loanProductJSON) {
|
||||||
return Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_LOAN_PRODUCT_URL, loanProductJSON, "resourceId");
|
return Utils.performServerPost(this.requestSpec, this.responseSpec, CREATE_LOAN_PRODUCT_URL, loanProductJSON, "resourceId");
|
||||||
}
|
}
|
||||||
@@ -157,6 +169,13 @@ public class LoanTransactionHelper {
|
|||||||
return Utils.performServerGet(requestSpec, responseSpec, URL, param);
|
return Utils.performServerGet(requestSpec, responseSpec, URL, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GetLoansLoanIdResponse getLoan(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||||
|
final Integer loanId) {
|
||||||
|
final String URL = "/fineract-provider/api/v1/loans/" + loanId + "?associations=all&" + Utils.TENANT_IDENTIFIER;
|
||||||
|
final String response = Utils.performServerGet(requestSpec, responseSpec, URL);
|
||||||
|
return GSON.fromJson(response, GetLoansLoanIdResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
public Object getLoanDetailExcludeFutureSchedule(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
public Object getLoanDetailExcludeFutureSchedule(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||||
final Integer loanID, final String param) {
|
final Integer loanID, final String param) {
|
||||||
final String URL = "/fineract-provider/api/v1/loans/" + loanID + "?associations=all&exclude=guarantors,futureSchedule&"
|
final String URL = "/fineract-provider/api/v1/loans/" + loanID + "?associations=all&exclude=guarantors,futureSchedule&"
|
||||||
|
|||||||
Reference in New Issue
Block a user