Merge pull request #859 from pramodn02/MIFOSX-1081

MIFOSX-1081 - Added Non Performing assets to Loan
This commit is contained in:
Vishwas Babu A J
2014-04-14 13:32:39 -07:00
12 changed files with 131 additions and 40 deletions
+7 -3
View File
@@ -5800,6 +5800,7 @@ GET https://DomainName/api/v1/loans/{loanId}
"canDisburse":false,
"emiAmountVariations": [],
"inArrears": true,
"isNPA":false,
"overdueCharges": [
{
"id": 20,
@@ -6087,7 +6088,8 @@ GET https://DomainName/mifosng-provider/api/v1/loans
]
},
"feeChargesAtDisbursementCharged": 0,
"inArrears":false
"inArrears":false,
"isNPA":false
}
]
}
@@ -9656,7 +9658,8 @@ GET https://DomainName/api/v1/loanproducts/{productId}
"glCode": "11"
}
},
"multiDisburseLoan":true,"maxTrancheCount":3,"outstandingLoanBalance":36000.000000
"multiDisburseLoan":true,"maxTrancheCount":3,"outstandingLoanBalance":36000.000000,
"overdueDaysForNPA":2
]
}
</code>
@@ -9698,7 +9701,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
numberOfRepaymentVariationsForBorrowerCycle,
interestRateVariationsForBorrowerCycle,
multiDisburseLoan,maxTrancheCount,
outstandingLoanBalance
outstandingLoanBalance,overdueDaysForNPA
</td>
</tr>
@@ -9769,6 +9772,7 @@ Request Body:
"feeToIncomeAccountMappings":[],
"penaltyToIncomeAccountMappings":[],
"charges":[],
"overdueDaysForNPA":2,
"dateFormat":"dd MMMM yyyy",
"locale":"en",
"fundSourceAccountId":1,
@@ -0,0 +1,7 @@
ALTER TABLE `m_product_loan`
ADD COLUMN `overdue_days_for_npa` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_arrears_ageing`;
ALTER TABLE `m_loan`
ADD COLUMN `is_npa` TINYINT(1) NOT NULL DEFAULT '0' AFTER `grace_on_arrears_ageing`;
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`) VALUES ('Update Non Performing Assets', 'Update Non Performing Assets', '0 0 0 1/1 * ? *', now());
@@ -13,7 +13,8 @@ public enum JobName {
PAY_DUE_SAVINGS_CHARGES("Pay Due Savings Charges"), //
APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"),
EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"),
ADD_ACCRUAL_ENTRIES("Add Accrual Transactions");
ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"),
UPDATE_NPA("Update Non Performing Assets");
private final String name;
@@ -158,6 +158,8 @@ public class LoanAccountData {
private final Boolean inArrears;
private final Boolean isNPA;
private final Collection<ChargeData> overdueCharges;
/**
@@ -244,6 +246,7 @@ public class LoanAccountData {
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
final LoanProductData product = null;
final Boolean inArrears = null;
final Boolean isNPA = null;
final Collection<ChargeData> overdueCharges = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -258,7 +261,7 @@ public class LoanAccountData {
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
product, inArrears, graceOnArrearsAgeing, overdueCharges);
product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA);
}
/**
@@ -345,6 +348,7 @@ public class LoanAccountData {
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
final LoanProductData product = null;
final Boolean inArrears = null;
final Boolean isNPA = null;
final Collection<ChargeData> overdueCharges = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
@@ -359,7 +363,8 @@ public class LoanAccountData {
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount,
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges);
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
isNPA);
}
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
@@ -380,7 +385,8 @@ public class LoanAccountData {
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA);
}
/**
@@ -468,6 +474,7 @@ public class LoanAccountData {
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
final LoanProductData product = null;
final Boolean inArrears = null;
final Boolean isNPA = null;
final Collection<ChargeData> overdueCharges = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
@@ -482,7 +489,7 @@ public class LoanAccountData {
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance,
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges);
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA);
}
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
@@ -502,7 +509,8 @@ public class LoanAccountData {
acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA);
}
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
@@ -609,6 +617,7 @@ public class LoanAccountData {
final BigDecimal fixedEmi = null;
Map<Long, LoanBorrowerCycleData> memberVariations = null;
final Boolean inArrears = null;
final Boolean isNPA = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId,
@@ -625,7 +634,7 @@ public class LoanAccountData {
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, product.getMultiDisburseLoan(), fixedEmi,
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges());
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA);
}
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -677,7 +686,7 @@ public class LoanAccountData {
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount,
product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears,
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges());
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA);
}
/*
@@ -699,7 +708,7 @@ public class LoanAccountData {
final LoanSummaryData loanSummary, final BigDecimal feeChargesDueAtDisbursementCharged,
final Boolean syncDisbursementWithMeeting, final Integer loancounter, final Integer loanProductCounter,
final Boolean multiDisburseLoan, final BigDecimal fixedEmiAmont, final BigDecimal outstandingLoanBalance,
final Boolean inArrears, final Integer graceOnArrearsAgeing) {
final Boolean inArrears, final Integer graceOnArrearsAgeing, final Boolean isNPA) {
final LoanScheduleData repaymentSchedule = null;
final Collection<LoanTransactionData> transactions = null;
@@ -743,7 +752,7 @@ public class LoanAccountData {
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance,
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges);
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA);
}
/*
@@ -779,7 +788,7 @@ public class LoanAccountData {
loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,
accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount,
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
acc.graceOnArrearsAgeing, overdueCharges);
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA);
}
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
@@ -809,7 +818,8 @@ public class LoanAccountData {
acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA);
}
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
@@ -865,7 +875,8 @@ public class LoanAccountData {
acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges);
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA);
}
private LoanAccountData(
@@ -919,7 +930,7 @@ public class LoanAccountData {
final Boolean multiDisburseLoan, BigDecimal fixedEmiAmount, final BigDecimal maxOutstandingLoanBalance,
final Collection<LoanTermVariationsData> emiAmountVariations, final Map<Long, LoanBorrowerCycleData> memberVariations,
final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing,
final Collection<ChargeData> overdueCharges) {
final Collection<ChargeData> overdueCharges, final Boolean isNPA) {
this.id = id;
this.accountNo = accountNo;
this.status = status;
@@ -1057,6 +1068,7 @@ public class LoanAccountData {
this.inArrears = inArrears;
this.graceOnArrearsAgeing = graceOnArrearsAgeing;
this.overdueCharges = overdueCharges;
this.isNPA = isNPA;
}
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
@@ -487,7 +487,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " la.total_overdue_derived as totalOverdue,"
+ " la.overdue_since_date_derived as overdueSinceDate,"
+ " l.sync_disbursement_with_meeting as syncDisbursementWithMeeting,"
+ " l.loan_counter as loanCounter, l.loan_product_counter as loanProductCounter"
+ " l.loan_counter as loanCounter, l.loan_product_counter as loanProductCounter,"
+ " l.is_npa as isNPA "
+ " from m_loan l" //
+ " join m_product_loan lp on lp.id = l.product_id" //
+ " join m_currency rc on rc.`code` = l.currency_code" //
@@ -709,6 +710,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Integer loanCounter = JdbcSupport.getInteger(rs, "loanCounter");
final Integer loanProductCounter = JdbcSupport.getInteger(rs, "loanProductCounter");
final BigDecimal fixedEmiAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "fixedEmiAmount");
final Boolean isNPA = rs.getBoolean("isNPA");
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData,
loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName,
@@ -718,7 +720,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment,
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary,
feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter, multiDisburseLoan,
fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing);
fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing, isNPA);
}
}
@@ -1319,7 +1321,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
.append(" where ((if(ls.fee_charges_waived_derived is null , ls.fee_charges_amount,(ls.fee_charges_amount-ls.fee_charges_waived_derived)) <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))")
.append(" or (if(ls.penalty_charges_waived_derived is null , ls.penalty_charges_amount,(ls.penalty_charges_amount-ls.penalty_charges_waived_derived)) <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))")
.append(" or (if(ls.interest_waived_derived is null , ls.interest_amount,(ls.interest_amount-ls.interest_waived_derived)) <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))")
.append(" and loan.loan_status_id=? and mpl.accounting_type=?");
.append(" and loan.loan_status_id=? and mpl.accounting_type=? and loan.is_npa=0");
return this.jdbcTemplate.query(sqlBuilder.toString(), mapper, new Object[] { LoanStatus.ACTIVE.getValue(),
AccountingRuleType.ACCRUAL_PERIODIC.getValue() });
}
@@ -54,5 +54,7 @@ public interface LoanProductConstants {
public static final String outstandingLoanBalanceParameterName = "outstandingLoanBalance";
public static final String graceOnArrearsAgeingParameterName = "graceOnArrearsAgeing";
public static final String overdueDaysForNPAParameterName = "overdueDaysForNPA";
}
@@ -72,6 +72,7 @@ public class LoanProductData {
private final Integer graceOnInterestPayment;
private final Integer graceOnInterestCharged;
private final Integer graceOnArrearsAgeing;
private final Integer overdueDaysForNPA;
// charges
private final Collection<ChargeData> charges;
@@ -144,6 +145,7 @@ public class LoanProductData {
final Integer graceOnInterestPayment = null;
final Integer graceOnInterestCharged = null;
final Integer graceOnArrearsAgeing = null;
final Integer overdueDaysForNPA = null;
final Collection<ChargeData> charges = null;
final Collection<LoanProductBorrowerCycleVariationData> principalVariations = new ArrayList<LoanProductBorrowerCycleVariationData>(
1);
@@ -168,7 +170,7 @@ public class LoanProductData {
transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
graceOnArrearsAgeing);
graceOnArrearsAgeing, overdueDaysForNPA);
}
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
@@ -199,6 +201,7 @@ public class LoanProductData {
final Integer graceOnInterestPayment = null;
final Integer graceOnInterestCharged = null;
final Integer graceOnArrearsAgeing = null;
final Integer overdueDaysForNPA = null;
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = null;
@@ -226,7 +229,7 @@ public class LoanProductData {
transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
graceOnArrearsAgeing);
graceOnArrearsAgeing, overdueDaysForNPA);
}
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -263,6 +266,7 @@ public class LoanProductData {
final Integer graceOnInterestPayment = null;
final Integer graceOnInterestCharged = null;
final Integer graceOnArrearsAgeing = null;
final Integer overdueDaysForNPA = null;
final Collection<ChargeData> charges = null;
final Collection<LoanProductBorrowerCycleVariationData> principalVariationsForBorrowerCycle = new ArrayList<LoanProductBorrowerCycleVariationData>(
@@ -290,7 +294,7 @@ public class LoanProductData {
transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId,
principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle,
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing);
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA);
}
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
@@ -318,7 +322,7 @@ public class LoanProductData {
Collection<LoanProductBorrowerCycleVariationData> principalVariations,
Collection<LoanProductBorrowerCycleVariationData> interestRateVariations,
Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations, Boolean multiDisburseLoan,
Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing) {
Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing, final Integer overdueDaysForNPA) {
this.id = id;
this.name = name;
this.shortName = shortName;
@@ -383,6 +387,7 @@ public class LoanProductData {
this.outstandingLoanBalance = outstandingLoanBalance;
this.maxTrancheCount = maxTrancheCount;
this.graceOnArrearsAgeing = graceOnArrearsAgeing;
this.overdueDaysForNPA = overdueDaysForNPA;
}
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
@@ -469,6 +474,7 @@ public class LoanProductData {
this.accountingRuleOptions = accountingRuleOptions;
this.valueConditionTypeOptions = valueConditionTypeOptions;
this.graceOnArrearsAgeing = productData.graceOnArrearsAgeing;
this.overdueDaysForNPA = productData.overdueDaysForNPA;
}
@@ -655,16 +661,15 @@ public class LoanProductData {
return this.outstandingLoanBalance;
}
public Integer getGraceOnArrearsAgeing() {
return this.graceOnArrearsAgeing;
}
public Collection<ChargeData> overdueFeeCharges(){
public Collection<ChargeData> overdueFeeCharges() {
Collection<ChargeData> overdueFeeCharges = new ArrayList<ChargeData>();
Collection<ChargeData> charges = charges();
for(ChargeData chargeData : charges){
if(chargeData.isOverdueInstallmentCharge()){
for (ChargeData chargeData : charges) {
if (chargeData.isOverdueInstallmentCharge()) {
overdueFeeCharges.add(chargeData);
}
}
@@ -116,6 +116,9 @@ public class LoanProduct extends AbstractPersistable<Long> {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanProduct", orphanRemoval = true)
private Set<LoanProductBorrowerCycleVariations> borrowerCycleVariations = new HashSet<LoanProductBorrowerCycleVariations>();
@Column(name = "overdue_days_for_npa", nullable = true)
private Integer overdueDaysForNPA;
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
@@ -180,13 +183,15 @@ public class LoanProduct extends AbstractPersistable<Long> {
final Integer graceOnArrearsAgeing = command.integerValueOfParameterNamed(LoanProductConstants.graceOnArrearsAgeingParameterName);
final Integer overdueDaysForNPA = command.integerValueOfParameterNamed(LoanProductConstants.overdueDaysForNPAParameterName);
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal,
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType,
annualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment,
graceOnInterestCharged, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle,
startDate, closeDate, externalId, useBorrowerCycle, loanProductBorrowerCycleVariations, multiDisburseLoan, maxTrancheCount,
outstandingLoanBalance, graceOnArrearsAgeing);
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA);
}
/**
@@ -396,7 +401,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
final AccountingRuleType accountingRuleType, final boolean includeInBorrowerCycle, final LocalDate startDate,
final LocalDate closeDate, final String externalId, final boolean useBorrowerCycle,
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan,
final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing) {
final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
final Integer overdueDaysForNPA) {
this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim();
@@ -442,6 +448,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
borrowerCycleVariations.updateLoanProduct(this);
}
this.loanProducTrancheDetails = new LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, outstandingLoanBalance);
this.overdueDaysForNPA = overdueDaysForNPA;
}
public MonetaryCurrency getCurrency() {
@@ -606,6 +613,13 @@ public class LoanProduct extends AbstractPersistable<Long> {
}
loanProducTrancheDetails.update(command, actualChanges, localeAsInput);
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.overdueDaysForNPAParameterName, this.overdueDaysForNPA)) {
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.overdueDaysForNPAParameterName);
actualChanges.put(LoanProductConstants.overdueDaysForNPAParameterName, newValue);
actualChanges.put("locale", localeAsInput);
this.overdueDaysForNPA = newValue;
}
return actualChanges;
}
@@ -60,7 +60,8 @@ public final class LoanProductDataValidator {
LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName, LoanProductConstants.shortName,
LoanProductConstants.multiDisburseLoanParameterName, LoanProductConstants.outstandingLoanBalanceParameterName,
LoanProductConstants.maxTrancheCountParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName));
LoanProductConstants.maxTrancheCountParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName,
LoanProductConstants.overdueDaysForNPAParameterName));
private final FromJsonHelper fromApiJsonHelper;
@@ -255,7 +256,12 @@ public final class LoanProductDataValidator {
final Integer graceOnArrearsAgeing = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
LoanProductConstants.graceOnArrearsAgeingParameterName, element);
baseDataValidator.reset().parameter(LoanProductConstants.graceOnArrearsAgeingParameterName).value(graceOnArrearsAgeing)
.zeroOrPositiveAmount();
.integerZeroOrGreater();
final Integer overdueDaysForNPA = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
LoanProductConstants.overdueDaysForNPAParameterName, element);
baseDataValidator.reset().parameter(LoanProductConstants.overdueDaysForNPAParameterName).value(overdueDaysForNPA)
.integerZeroOrGreater();
// accounting related data validation
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
@@ -528,7 +534,14 @@ public final class LoanProductDataValidator {
final Integer graceOnArrearsAgeing = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
LoanProductConstants.graceOnArrearsAgeingParameterName, element);
baseDataValidator.reset().parameter(LoanProductConstants.graceOnArrearsAgeingParameterName).value(graceOnArrearsAgeing)
.zeroOrPositiveAmount();
.integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.overdueDaysForNPAParameterName, element)) {
final Integer overdueDaysForNPA = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
LoanProductConstants.overdueDaysForNPAParameterName, element);
baseDataValidator.reset().parameter(LoanProductConstants.overdueDaysForNPAParameterName).value(overdueDaysForNPA)
.integerZeroOrGreater();
}
//
@@ -117,7 +117,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
+ "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,lp.grace_on_arrears_ageing as graceOnArrearsAgeing, "
+ "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,lp.grace_on_arrears_ageing as graceOnArrearsAgeing,lp.overdue_days_for_npa as overdueDaysForNPA, "
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
+ "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, "
+ "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, "
@@ -164,6 +164,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final Integer graceOnInterestPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestPayment");
final Integer graceOnInterestCharged = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestCharged");
final Integer graceOnArrearsAgeing = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnArrearsAgeing");
final Integer overdueDaysForNPA = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "overdueDaysForNPA");
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId);
@@ -227,7 +228,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
transactionStrategyId, transactionStrategyName, graceOnPrincipalPayment, graceOnInterestPayment,
graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, useBorrowerCycle, startDate,
closeDate, status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle,
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing);
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
graceOnArrearsAgeing, overdueDaysForNPA);
}
}
@@ -18,4 +18,6 @@ public interface ScheduledJobRunnerService {
void applyAnnualFeeForSavings();
void applyDueChargesForSavings() throws JobExecutionException;
void updateNPA();
}
@@ -61,8 +61,10 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
updateSqlBuilder.append("SUM(IFNULL(mr.interest_completed_derived,0)) as interest_repaid_derived,");
updateSqlBuilder.append("SUM(IFNULL(mr.interest_waived_derived,0)) as interest_waived_derived,");
updateSqlBuilder.append("SUM(IFNULL(mr.interest_writtenoff_derived,0)) as interest_writtenoff_derived,");
updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_amount,0)) + IFNULL((select SUM(lc.amount) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_id=1),0) as fee_charges_charged_derived,");
updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_completed_derived,0)) + IFNULL((select SUM(lc.amount_paid_derived) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_id=1),0) as fee_charges_repaid_derived,");
updateSqlBuilder
.append("SUM(IFNULL(mr.fee_charges_amount,0)) + IFNULL((select SUM(lc.amount) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_id=1),0) as fee_charges_charged_derived,");
updateSqlBuilder
.append("SUM(IFNULL(mr.fee_charges_completed_derived,0)) + IFNULL((select SUM(lc.amount_paid_derived) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_id=1),0) as fee_charges_repaid_derived,");
updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_waived_derived,0)) as fee_charges_waived_derived,");
updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_writtenoff_derived,0)) as fee_charges_writtenoff_derived,");
updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_amount,0)) as penalty_charges_charged_derived,");
@@ -216,8 +218,8 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
logger.error("Apply annual fee failed for account:" + savingsAccountReference.getAccountNo() + " with message "
+ error.getDeveloperMessage());
}
} catch (final Exception ex){
//need to handle this scenario
} catch (final Exception ex) {
// need to handle this scenario
}
}
@@ -254,4 +256,29 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
if (errorMsg.length() > 0) { throw new JobExecutionException(errorMsg.toString()); }
}
@Transactional
@Override
@CronTarget(jobName = JobName.UPDATE_NPA)
public void updateNPA() {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSourceServiceFactory.determineDataSourceService().retrieveDataSource());
jdbcTemplate.update("UPDATE m_loan ml SET ml.is_npa=0");
final StringBuilder updateSqlBuilder = new StringBuilder(900);
updateSqlBuilder.append("UPDATE m_loan as ml,");
updateSqlBuilder.append(" (select loan.id from m_loan_repayment_schedule mr ");
updateSqlBuilder
.append(" INNER JOIN m_loan loan on mr.loan_id = loan.id INNER JOIN m_product_loan mpl on mpl.id = loan.product_id ");
updateSqlBuilder.append("WHERE loan.loan_status_id = 300 and mr.completed_derived is false ");
updateSqlBuilder
.append(" and mr.duedate < SUBDATE(CURDATE(),INTERVAL ifnull(mpl.overdue_days_for_npa,0) day) group by loan.id) as sl ");
updateSqlBuilder.append("SET ml.is_npa=1 where ml.id=sl.id");
final int result = jdbcTemplate.update(updateSqlBuilder.toString());
logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by update: " + result);
}
}