mifosx-281 done
This commit is contained in:
@@ -707,6 +707,14 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="alt">
|
||||||
|
<td></td>
|
||||||
|
<td>loans/{loanId}/transactions?command=recoverypayment</td>
|
||||||
|
<td><a href="#loans_transaction_recovery_payment">Make a Recovery Payment</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="alt">
|
<tr class="alt">
|
||||||
<td></td>
|
<td></td>
|
||||||
@@ -6971,6 +6979,60 @@ Request Body:
|
|||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!------recovery payment -->
|
||||||
|
<a id="loans_transaction_recovery_payment" name="loans_transaction_recovery_payment" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Make Recovery Payment </h4>
|
||||||
|
<p>only applies to written-off loans</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/loans/{loanId}/transactions?command=recoverypayment
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST loans/5/transactions?command=recoverypayment
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"dateFormat": "dd MMMM yyyy",
|
||||||
|
"locale": "en",
|
||||||
|
"transactionDate": "14 May 2013",
|
||||||
|
"transactionAmount": "500.00",
|
||||||
|
"paymentTypeId": "12",
|
||||||
|
"note": "check payment",
|
||||||
|
"accountNumber": "acc123",
|
||||||
|
"checkNumber": "che123",
|
||||||
|
"routingCode": "rou123",
|
||||||
|
"receiptNumber": "rec123",
|
||||||
|
"bankNumber": "ban123"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId": 1,
|
||||||
|
"clientId": 1,
|
||||||
|
"loanId": 5,
|
||||||
|
"resourceId": 564,
|
||||||
|
"changes": {
|
||||||
|
"transactionDate": "14 May 2013",
|
||||||
|
"transactionAmount": "500.00",
|
||||||
|
"locale": "en",
|
||||||
|
"dateFormat": "dd MMMM yyyy",
|
||||||
|
"note": "check payment",
|
||||||
|
"accountNumber": "acc123",
|
||||||
|
"checkNumber": "che123",
|
||||||
|
"routingCode": "rou123",
|
||||||
|
"receiptNumber": "rec123",
|
||||||
|
"bankNumber": "ban123"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!--- end of recovery payment -->
|
||||||
|
|
||||||
<a id="loans_transaction_undo_write-off_loan" name="loans_transaction_undo_write-off_loan" class="old-syle-anchor"> </a>
|
<a id="loans_transaction_undo_write-off_loan" name="loans_transaction_undo_write-off_loan" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
@@ -24105,6 +24167,14 @@ No Request Body:
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tr><tr>
|
||||||
|
<td></td>
|
||||||
|
<td>loans/{loanId}/transactions?command=recoverypayment</td>
|
||||||
|
<td><a href="#loans_transaction_recovery_payment">Make Recovery Payment</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
</tr><tr>
|
</tr><tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>loans/{loanId}/transactions?command=undowriteoff</td>
|
<td>loans/{loanId}/transactions?command=undowriteoff</td>
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
INSERT INTO `m_permission` (
|
||||||
|
`id` ,
|
||||||
|
`grouping` ,
|
||||||
|
`code` ,
|
||||||
|
`entity_name` ,
|
||||||
|
`action_name` ,
|
||||||
|
`can_maker_checker`
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
NULL , 'transaction_loan', 'RECOVERYPAYMENT_LOAN', 'LOAN', 'RECOVERYPAYMENT', '0'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Alter table m_loan
|
||||||
|
add column total_recovered_derived Decimal(19,6);
|
||||||
|
|
||||||
|
Alter table m_loan_transaction
|
||||||
|
add column recovered_portion_derived Decimal(19,6);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `acc_gl_account` (`name`, `hierarchy`, `gl_code`,`account_usage`, `classification_enum`,`description`)
|
||||||
|
select 'Loan Recovery (Temp)', '.', '220002-Temp', 1, 4,'Temporary account to track income from Loan recovery'
|
||||||
|
FROM m_product_loan WHERE accounting_type != 1
|
||||||
|
limit 1;
|
||||||
|
|
||||||
|
INSERT INTO `acc_product_mapping` (`gl_account_id`,`product_id`,`product_type`,`financial_account_type`)
|
||||||
|
select (select max(id) from acc_gl_account where classification_enum=4 and account_usage=1 LIMIT 1), mapping.product_id, mapping.product_type, 12
|
||||||
|
from acc_product_mapping mapping
|
||||||
|
where mapping.financial_account_type = 4 and mapping.product_type=1
|
||||||
|
group by mapping.product_id;
|
||||||
|
|
||||||
+6
-4
@@ -13,7 +13,7 @@ public class AccountingConstants {
|
|||||||
/*** Accounting placeholders for cash based accounting for loan products ***/
|
/*** Accounting placeholders for cash based accounting for loan products ***/
|
||||||
public static enum CASH_ACCOUNTS_FOR_LOAN {
|
public static enum CASH_ACCOUNTS_FOR_LOAN {
|
||||||
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), TRANSFERS_SUSPENSE(
|
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), TRANSFERS_SUSPENSE(
|
||||||
10), OVERPAYMENT(11);
|
10), OVERPAYMENT(11),INCOME_FROM_RECOVERY(12);
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ public class AccountingConstants {
|
|||||||
/*** Accounting placeholders for accrual based accounting for loan products ***/
|
/*** Accounting placeholders for accrual based accounting for loan products ***/
|
||||||
public static enum ACCRUAL_ACCOUNTS_FOR_LOAN {
|
public static enum ACCRUAL_ACCOUNTS_FOR_LOAN {
|
||||||
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(
|
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(
|
||||||
7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11);
|
7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11),INCOME_FROM_RECOVERY(12);
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
|
|
||||||
@@ -88,7 +88,8 @@ public class AccountingConstants {
|
|||||||
"receivableFeeAccountId"), PENALTIES_RECEIVABLE("receivablePenaltyAccountId"), TRANSFERS_SUSPENSE(
|
"receivableFeeAccountId"), PENALTIES_RECEIVABLE("receivablePenaltyAccountId"), TRANSFERS_SUSPENSE(
|
||||||
"transfersInSuspenseAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE(
|
"transfersInSuspenseAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE(
|
||||||
"paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING("feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING(
|
"paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING("feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING(
|
||||||
"penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID("incomeAccountId");
|
"penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID("incomeAccountId"),
|
||||||
|
INCOME_FROM_RECOVERY("incomeFromRecoveryAccountId");
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
@@ -110,7 +111,8 @@ public class AccountingConstants {
|
|||||||
FUND_SOURCE("fundSourceAccount"), LOAN_PORTFOLIO("loanPortfolioAccount"), INTEREST_ON_LOANS("interestOnLoanAccount"), INCOME_FROM_FEES(
|
FUND_SOURCE("fundSourceAccount"), LOAN_PORTFOLIO("loanPortfolioAccount"), INTEREST_ON_LOANS("interestOnLoanAccount"), INCOME_FROM_FEES(
|
||||||
"incomeFromFeeAccount"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccount"), LOSSES_WRITTEN_OFF("writeOffAccount"), OVERPAYMENT(
|
"incomeFromFeeAccount"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccount"), LOSSES_WRITTEN_OFF("writeOffAccount"), OVERPAYMENT(
|
||||||
"overpaymentLiabilityAccount"), INTEREST_RECEIVABLE("receivableInterestAccount"), FEES_RECEIVABLE("receivableFeeAccount"), PENALTIES_RECEIVABLE(
|
"overpaymentLiabilityAccount"), INTEREST_RECEIVABLE("receivableInterestAccount"), FEES_RECEIVABLE("receivableFeeAccount"), PENALTIES_RECEIVABLE(
|
||||||
"receivablePenaltyAccount"), TRANSFERS_SUSPENSE("transfersInSuspenseAccount"), INCOME_ACCOUNT_ID("incomeAccount");
|
"receivablePenaltyAccount"), TRANSFERS_SUSPENSE("transfersInSuspenseAccount"), INCOME_ACCOUNT_ID("incomeAccount"),
|
||||||
|
INCOME_FROM_RECOVERY("incomeFromRecoveryAccount");
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -28,6 +28,7 @@ public class LoanTransactionDTO {
|
|||||||
private final BigDecimal fees;
|
private final BigDecimal fees;
|
||||||
private final BigDecimal penalties;
|
private final BigDecimal penalties;
|
||||||
private final BigDecimal overPayment;
|
private final BigDecimal overPayment;
|
||||||
|
private final BigDecimal recoveryPayment;
|
||||||
|
|
||||||
/*** Boolean values determines if the transaction is reversed ***/
|
/*** Boolean values determines if the transaction is reversed ***/
|
||||||
private final boolean reversed;
|
private final boolean reversed;
|
||||||
@@ -38,7 +39,7 @@ public class LoanTransactionDTO {
|
|||||||
|
|
||||||
public LoanTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate,
|
public LoanTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate,
|
||||||
final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest,
|
final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest,
|
||||||
final BigDecimal fees, final BigDecimal penalties, final BigDecimal overPayment, final boolean reversed,
|
final BigDecimal fees, final BigDecimal penalties, final BigDecimal overPayment, final BigDecimal recoveryPayment, final boolean reversed,
|
||||||
final List<ChargePaymentDTO> feePayments, final List<ChargePaymentDTO> penaltyPayments) {
|
final List<ChargePaymentDTO> feePayments, final List<ChargePaymentDTO> penaltyPayments) {
|
||||||
this.paymentTypeId = paymentTypeId;
|
this.paymentTypeId = paymentTypeId;
|
||||||
this.transactionId = transactionId;
|
this.transactionId = transactionId;
|
||||||
@@ -53,6 +54,7 @@ public class LoanTransactionDTO {
|
|||||||
this.feePayments = feePayments;
|
this.feePayments = feePayments;
|
||||||
this.penaltyPayments = penaltyPayments;
|
this.penaltyPayments = penaltyPayments;
|
||||||
this.overPayment = overPayment;
|
this.overPayment = overPayment;
|
||||||
|
this.recoveryPayment = recoveryPayment;
|
||||||
this.officeId = officeId;
|
this.officeId = officeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +98,10 @@ public class LoanTransactionDTO {
|
|||||||
return this.overPayment;
|
return this.overPayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigDecimal getRecoveryPayment() {
|
||||||
|
return this.recoveryPayment;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isReversed() {
|
public boolean isReversed() {
|
||||||
return this.reversed;
|
return this.reversed;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -96,6 +96,7 @@ public class AccountingProcessorHelper {
|
|||||||
final BigDecimal fees = (BigDecimal) map.get("feeChargesPortion");
|
final BigDecimal fees = (BigDecimal) map.get("feeChargesPortion");
|
||||||
final BigDecimal penalties = (BigDecimal) map.get("penaltyChargesPortion");
|
final BigDecimal penalties = (BigDecimal) map.get("penaltyChargesPortion");
|
||||||
final BigDecimal overPayments = (BigDecimal) map.get("overPaymentPortion");
|
final BigDecimal overPayments = (BigDecimal) map.get("overPaymentPortion");
|
||||||
|
final BigDecimal recoveryPayments = (BigDecimal) map.get("recoveryPaymentPortion");
|
||||||
final boolean reversed = (Boolean) map.get("reversed");
|
final boolean reversed = (Boolean) map.get("reversed");
|
||||||
final Long paymentTypeId = (Long) map.get("paymentTypeId");
|
final Long paymentTypeId = (Long) map.get("paymentTypeId");
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ public class AccountingProcessorHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
|
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
|
||||||
transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed,
|
transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, recoveryPayments, reversed,
|
||||||
feePaymentDetails, penaltyPaymentDetails);
|
feePaymentDetails, penaltyPaymentDetails);
|
||||||
|
|
||||||
newLoanTransactions.add(transaction);
|
newLoanTransactions.add(transaction);
|
||||||
|
|||||||
+43
@@ -56,6 +56,12 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
|||||||
createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office);
|
createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Logic for handling recovery payments **/
|
||||||
|
else if (loanTransactionDTO.getTransactionType().isRecoveryRepayment()) {
|
||||||
|
createJournalEntriesForRecoveryRepayments(loanDTO, loanTransactionDTO, office);
|
||||||
|
}
|
||||||
|
|
||||||
/** Logic for Refunds of Overpayments **/
|
/** Logic for Refunds of Overpayments **/
|
||||||
else if (loanTransactionDTO.getTransactionType().isRefund()) {
|
else if (loanTransactionDTO.getTransactionType().isRefund()) {
|
||||||
createJournalEntriesForRefund(loanDTO, loanTransactionDTO, office);
|
createJournalEntriesForRefund(loanDTO, loanTransactionDTO, office);
|
||||||
@@ -202,6 +208,43 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
|||||||
paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
|
paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single Debit to fund source and multiple credits if applicable
|
||||||
|
* (loan portfolio for principal repayments, Interest on loans for interest
|
||||||
|
* repayments, Income from fees for fees payment and Income from penalties
|
||||||
|
* for penalty payment)
|
||||||
|
*
|
||||||
|
* In case the loan transaction is a reversal, all debits are turned into
|
||||||
|
* credits and vice versa
|
||||||
|
*/
|
||||||
|
private void createJournalEntriesForRecoveryRepayments(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) {
|
||||||
|
// loan properties
|
||||||
|
final Long loanProductId = loanDTO.getLoanProductId();
|
||||||
|
final Long loanId = loanDTO.getLoanId();
|
||||||
|
final String currencyCode = loanDTO.getCurrencyCode();
|
||||||
|
|
||||||
|
// transaction properties
|
||||||
|
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||||
|
final Date transactionDate = loanTransactionDTO.getTransactionDate();
|
||||||
|
final BigDecimal recoveryPaymentAmount = loanTransactionDTO.getRecoveryPayment();
|
||||||
|
final boolean isReversal = loanTransactionDTO.isReversed();
|
||||||
|
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||||
|
|
||||||
|
BigDecimal totalDebitAmount = new BigDecimal(0);
|
||||||
|
|
||||||
|
if (recoveryPaymentAmount != null && !(recoveryPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||||
|
totalDebitAmount = totalDebitAmount.add(recoveryPaymentAmount);
|
||||||
|
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY, loanProductId,
|
||||||
|
paymentTypeId, loanId, transactionId, transactionDate, recoveryPaymentAmount, isReversal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** create a single debit entry (or reversal) for the entire amount **/
|
||||||
|
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId,
|
||||||
|
paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit loan Portfolio and Debit Suspense Account for a Transfer
|
* Credit loan Portfolio and Debit Suspense Account for a Transfer
|
||||||
* Initiation. A Transfer acceptance would be treated the opposite i.e Debit
|
* Initiation. A Transfer acceptance would be treated the opposite i.e Debit
|
||||||
|
|||||||
+5
@@ -102,6 +102,11 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
|
|||||||
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
|
||||||
.notNull().integerGreaterThanZero();
|
.notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue()).value(incomeFromRecoveryAccountId)
|
||||||
|
.notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
||||||
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
|
||||||
|
|||||||
+18
-6
@@ -121,6 +121,10 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
element);
|
element);
|
||||||
final Long incomeFromPenaltyId = this.fromApiJsonHelper.extractLongNamed(
|
final Long incomeFromPenaltyId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
|
||||||
|
|
||||||
|
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), element);
|
||||||
|
|
||||||
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
||||||
final Long overPaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(),
|
final Long overPaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(),
|
||||||
@@ -140,16 +144,16 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
break;
|
break;
|
||||||
case CASH_BASED:
|
case CASH_BASED:
|
||||||
populateChangesForCashBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
|
populateChangesForCashBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
|
||||||
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId);
|
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,incomeFromRecoveryAccountId);
|
||||||
break;
|
break;
|
||||||
case ACCRUAL_PERIODIC:
|
case ACCRUAL_PERIODIC:
|
||||||
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
|
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
|
||||||
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,
|
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,incomeFromRecoveryAccountId,
|
||||||
receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
|
receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
|
||||||
break;
|
break;
|
||||||
case ACCRUAL_UPFRONT:
|
case ACCRUAL_UPFRONT:
|
||||||
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
|
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
|
||||||
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,
|
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,incomeFromRecoveryAccountId,
|
||||||
receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
|
receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -159,7 +163,7 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
|
|
||||||
private void populateChangesForAccrualBasedAccounting(final Map<String, Object> changes, final Long fundAccountId,
|
private void populateChangesForAccrualBasedAccounting(final Map<String, Object> changes, final Long fundAccountId,
|
||||||
final Long loanPortfolioAccountId, final Long incomeFromInterestId, final Long incomeFromFeeId, final Long incomeFromPenaltyId,
|
final Long loanPortfolioAccountId, final Long incomeFromInterestId, final Long incomeFromFeeId, final Long incomeFromPenaltyId,
|
||||||
final Long writeOffAccountId, final Long overPaymentAccountId, final Long transfersInSuspenseAccountId,
|
final Long writeOffAccountId, final Long overPaymentAccountId, final Long transfersInSuspenseAccountId,final Long incomeFromRecoveryAccountId,
|
||||||
final Long receivableInterestAccountId, final Long receivableFeeAccountId, final Long receivablePenaltyAccountId) {
|
final Long receivableInterestAccountId, final Long receivableFeeAccountId, final Long receivablePenaltyAccountId) {
|
||||||
|
|
||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId);
|
||||||
@@ -167,13 +171,13 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), receivablePenaltyAccountId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), receivablePenaltyAccountId);
|
||||||
|
|
||||||
populateChangesForCashBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId, incomeFromFeeId,
|
populateChangesForCashBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId, incomeFromFeeId,
|
||||||
incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId);
|
incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,incomeFromRecoveryAccountId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateChangesForCashBasedAccounting(final Map<String, Object> changes, final Long fundAccountId,
|
private void populateChangesForCashBasedAccounting(final Map<String, Object> changes, final Long fundAccountId,
|
||||||
final Long loanPortfolioAccountId, final Long incomeFromInterestId, final Long incomeFromFeeId, final Long incomeFromPenaltyId,
|
final Long loanPortfolioAccountId, final Long incomeFromInterestId, final Long incomeFromFeeId, final Long incomeFromPenaltyId,
|
||||||
final Long writeOffAccountId, final Long overPaymentAccountId, final Long transfersInSuspenseAccountId) {
|
final Long writeOffAccountId, final Long overPaymentAccountId, final Long transfersInSuspenseAccountId,final Long incomeFromRecoveryAccountId) {
|
||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), fundAccountId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), fundAccountId);
|
||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanPortfolioAccountId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanPortfolioAccountId);
|
||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), incomeFromInterestId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), incomeFromInterestId);
|
||||||
@@ -182,6 +186,8 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId);
|
||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), overPaymentAccountId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), overPaymentAccountId);
|
||||||
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId);
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId);
|
||||||
|
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), incomeFromRecoveryAccountId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,6 +221,9 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
|
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
|
||||||
loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
|
loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
|
||||||
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(), changes);
|
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(), changes);
|
||||||
|
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(),
|
||||||
|
loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.getValue(),
|
||||||
|
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.toString(), changes);
|
||||||
|
|
||||||
// expenses
|
// expenses
|
||||||
mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
|
mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
|
||||||
@@ -255,6 +264,9 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
|
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
|
||||||
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
|
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
|
||||||
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(), changes);
|
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(), changes);
|
||||||
|
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(),
|
||||||
|
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.getValue(),
|
||||||
|
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.toString(), changes);
|
||||||
|
|
||||||
// expenses
|
// expenses
|
||||||
mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
|
mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
|
||||||
|
|||||||
+4
@@ -125,6 +125,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.LOSSES_WRITTEN_OFF.getValue(), gLAccountData);
|
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.LOSSES_WRITTEN_OFF.getValue(), gLAccountData);
|
||||||
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT)) {
|
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT)) {
|
||||||
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.OVERPAYMENT.getValue(), gLAccountData);
|
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.OVERPAYMENT.getValue(), gLAccountData);
|
||||||
|
} else if(glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY)) {
|
||||||
|
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.INCOME_FROM_RECOVERY.getValue(), gLAccountData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingType)
|
} else if (AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingType)
|
||||||
@@ -161,6 +163,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.FEES_RECEIVABLE.getValue(), gLAccountData);
|
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.FEES_RECEIVABLE.getValue(), gLAccountData);
|
||||||
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE)) {
|
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE)) {
|
||||||
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.PENALTIES_RECEIVABLE.getValue(), gLAccountData);
|
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.PENALTIES_RECEIVABLE.getValue(), gLAccountData);
|
||||||
|
} else if((glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY))){
|
||||||
|
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS.INCOME_FROM_RECOVERY.getValue(), gLAccountData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -77,6 +77,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
|
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
|
||||||
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
|
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
|
||||||
|
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
|
||||||
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), loanProductId,
|
||||||
|
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.getValue());
|
||||||
|
|
||||||
// expenses
|
// expenses
|
||||||
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
|
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
|
||||||
@@ -125,6 +128,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
|
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
|
||||||
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
|
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
|
||||||
|
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
|
||||||
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), loanProductId,
|
||||||
|
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.getValue());
|
||||||
|
|
||||||
// expenses
|
// expenses
|
||||||
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
|
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
|
||||||
|
|||||||
Regular → Executable
+3
@@ -381,6 +381,9 @@ public class CommandWrapper {
|
|||||||
public boolean isLoanRepayment() {
|
public boolean isLoanRepayment() {
|
||||||
return this.actionName.equalsIgnoreCase("REPAYMENT") && this.entityName.equalsIgnoreCase("LOAN");
|
return this.actionName.equalsIgnoreCase("REPAYMENT") && this.entityName.equalsIgnoreCase("LOAN");
|
||||||
}
|
}
|
||||||
|
public boolean isLoanRecoveryPayment() {
|
||||||
|
return this.actionName.equalsIgnoreCase("RECOVERYPAYMENT") && this.entityName.equalsIgnoreCase("LOAN");
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isLoanRepaymentAdjustment() {
|
public boolean isLoanRepaymentAdjustment() {
|
||||||
return this.actionName.equalsIgnoreCase("ADJUST") && this.entityName.equalsIgnoreCase("LOAN");
|
return this.actionName.equalsIgnoreCase("ADJUST") && this.entityName.equalsIgnoreCase("LOAN");
|
||||||
|
|||||||
Regular → Executable
+9
@@ -576,6 +576,15 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder loanRecoveryPaymentTransaction(final Long loanId){
|
||||||
|
this.actionName = "RECOVERYPAYMENT";
|
||||||
|
this.entityName = "LOAN";
|
||||||
|
this.entityId = null;
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.href = "/loans/" + loanId + "/transactions/template?command=recoverypayment";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder waiveInterestPortionTransaction(final Long loanId) {
|
public CommandWrapperBuilder waiveInterestPortionTransaction(final Long loanId) {
|
||||||
this.actionName = "WAIVEINTERESTPORTION";
|
this.actionName = "WAIVEINTERESTPORTION";
|
||||||
this.entityName = "LOAN";
|
this.entityName = "LOAN";
|
||||||
|
|||||||
Regular → Executable
+5
-3
@@ -5,8 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.commands.service;
|
package org.mifosplatform.commands.service;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.mifosplatform.commands.domain.CommandSource;
|
import org.mifosplatform.commands.domain.CommandSource;
|
||||||
import org.mifosplatform.commands.domain.CommandSourceRepository;
|
import org.mifosplatform.commands.domain.CommandSourceRepository;
|
||||||
@@ -26,6 +24,8 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class SynchronousCommandProcessingService implements CommandProcessingService {
|
public class SynchronousCommandProcessingService implements CommandProcessingService {
|
||||||
|
|
||||||
@@ -339,7 +339,9 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
handler = this.applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isLoanRepayment()) {
|
} else if (wrapper.isLoanRepayment()) {
|
||||||
handler = this.applicationContext.getBean("loanRepaymentCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("loanRepaymentCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isLoanRepaymentAdjustment()) {
|
} else if (wrapper.isLoanRecoveryPayment()){
|
||||||
|
handler = this.applicationContext.getBean("loanRecoveryPaymentCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
}else if (wrapper.isLoanRepaymentAdjustment()) {
|
||||||
handler = this.applicationContext.getBean("loanRepaymentAdjustmentCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("loanRepaymentAdjustmentCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isWaiveInterestPortionOnLoan()) {
|
} else if (wrapper.isWaiveInterestPortionOnLoan()) {
|
||||||
handler = this.applicationContext.getBean("waiveInterestPortionOnLoanCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("waiveInterestPortionOnLoanCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
|||||||
Regular → Executable
+12
-16
@@ -5,22 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.api;
|
package org.mifosplatform.portfolio.loanaccount.api;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.PathParam;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.QueryParam;
|
|
||||||
import javax.ws.rs.core.Context;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.commands.domain.CommandWrapper;
|
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||||
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||||
@@ -40,6 +24,15 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.ws.rs.*;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Path("/loans/{loanId}/transactions")
|
@Path("/loans/{loanId}/transactions")
|
||||||
@Component
|
@Component
|
||||||
@Scope("singleton")
|
@Scope("singleton")
|
||||||
@@ -152,6 +145,9 @@ public class LoanTransactionsApiResource {
|
|||||||
} else if (is(commandParam, "undowriteoff")) {
|
} else if (is(commandParam, "undowriteoff")) {
|
||||||
final CommandWrapper commandRequest = builder.undoWriteOffLoanTransaction(loanId).build();
|
final CommandWrapper commandRequest = builder.undoWriteOffLoanTransaction(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
}else if(is(commandParam,"recoverypayment")){
|
||||||
|
final CommandWrapper commandRequest = builder.loanRecoveryPaymentTransaction(loanId).build();
|
||||||
|
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam); }
|
if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam); }
|
||||||
|
|||||||
Regular → Executable
+141
-48
@@ -5,36 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import com.google.gson.JsonArray;
|
||||||
import java.math.MathContext;
|
import com.google.gson.JsonObject;
|
||||||
import java.math.RoundingMode;
|
import com.google.gson.JsonPrimitive;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Embedded;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.FetchType;
|
|
||||||
import javax.persistence.JoinColumn;
|
|
||||||
import javax.persistence.ManyToOne;
|
|
||||||
import javax.persistence.OneToMany;
|
|
||||||
import javax.persistence.OneToOne;
|
|
||||||
import javax.persistence.OrderBy;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
import javax.persistence.Temporal;
|
|
||||||
import javax.persistence.TemporalType;
|
|
||||||
import javax.persistence.Transient;
|
|
||||||
import javax.persistence.UniqueConstraint;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.ObjectUtils;
|
import org.apache.commons.lang.ObjectUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.hibernate.annotations.LazyCollection;
|
import org.hibernate.annotations.LazyCollection;
|
||||||
@@ -74,21 +47,8 @@ import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
import org.mifosplatform.portfolio.loanaccount.exception.*;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.*;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateException;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentDateException;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentDateException;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
|
||||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
@@ -98,9 +58,12 @@ import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
|||||||
import org.mifosplatform.useradministration.domain.AppUser;
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonObject;
|
import javax.persistence.*;
|
||||||
import com.google.gson.JsonPrimitive;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.MathContext;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
|
@Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
|
||||||
@@ -304,6 +267,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
|
||||||
private final Set<LoanTermVariations> loanTermVariations = new HashSet<LoanTermVariations>();
|
private final Set<LoanTermVariations> loanTermVariations = new HashSet<LoanTermVariations>();
|
||||||
|
|
||||||
|
@Column(name = "total_recovered_derived", scale = 6, precision = 19)
|
||||||
|
private BigDecimal totalRecovered;
|
||||||
|
|
||||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||||
@@ -992,6 +958,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
final Money overpaidBy = calculateTotalOverpayment();
|
final Money overpaidBy = calculateTotalOverpayment();
|
||||||
this.totalOverpaid = overpaidBy.getAmountDefaultedToNullIfZero();
|
this.totalOverpaid = overpaidBy.getAmountDefaultedToNullIfZero();
|
||||||
|
|
||||||
|
final Money recoveredAmount = calculateTotalRecoveredPayments();
|
||||||
|
this.totalRecovered = recoveredAmount.getAmountDefaultedToNullIfZero();
|
||||||
|
|
||||||
final Money principal = this.loanRepaymentScheduleDetail.getPrincipal();
|
final Money principal = this.loanRepaymentScheduleDetail.getPrincipal();
|
||||||
this.summary.updateSummary(loanCurrency(), principal, this.repaymentScheduleInstallments, this.loanSummaryWrapper,
|
this.summary.updateSummary(loanCurrency(), principal, this.repaymentScheduleInstallments, this.loanSummaryWrapper,
|
||||||
isDisbursed());
|
isDisbursed());
|
||||||
@@ -2172,6 +2141,26 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
public ChangedTransactionDetail makeRecoveryRepayment(final LoanTransaction repaymentTransaction,
|
||||||
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||||
|
final List<Long> existingReversedTransactionIds, final boolean allowTransactionsOnHoliday, final List<Holiday> holidays,
|
||||||
|
final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) {
|
||||||
|
|
||||||
|
validateAccountStatus(LoanEvent.LOAN_WRITTEN_OFF);
|
||||||
|
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.WRITE_OFF_OUTSTANDING, repaymentTransaction.getTransactionDate());
|
||||||
|
|
||||||
|
validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
||||||
|
validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
||||||
|
|
||||||
|
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||||
|
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||||
|
|
||||||
|
final ChangedTransactionDetail changedTransactionDetail = handleRecoveryRepayment(repaymentTransaction,
|
||||||
|
loanLifecycleStateMachine, null);
|
||||||
|
|
||||||
|
return changedTransactionDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
@@ -2232,6 +2221,77 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ChangedTransactionDetail handleRecoveryRepayment(final LoanTransaction loanTransaction,
|
||||||
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction){
|
||||||
|
ChangedTransactionDetail changedTransactionDetail = null;
|
||||||
|
|
||||||
|
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WRITTEN_OFF,
|
||||||
|
LoanStatus.fromInt(this.loanStatus));
|
||||||
|
this.loanStatus = statusEnum.getValue();
|
||||||
|
|
||||||
|
loanTransaction.updateLoan(this);
|
||||||
|
|
||||||
|
final boolean isTransactionChronologicallyLatest = isChronologicallyLatestRepaymentOrWaiver(loanTransaction, this.loanTransactions);
|
||||||
|
|
||||||
|
if (loanTransaction.isNotZero(loanCurrency())) {
|
||||||
|
this.loanTransactions.add(loanTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loanTransaction.isRecoveryRepayment()) {
|
||||||
|
final String errorMessage = "A transaction of type recovery repayment was expected but not received.";
|
||||||
|
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.recovery.repayment.transaction", errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
final LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
|
||||||
|
if (loanTransactionDate.isBefore(getWrittenOffDate())) {
|
||||||
|
final String errorMessage = "The transaction date cannot be before the loan written-off date: "
|
||||||
|
+ getWrittenOffDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.before.written-off.date", errorMessage,
|
||||||
|
loanTransactionDate, getWrittenOffDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loanTransactionDate.isAfter(DateUtils.getLocalDateOfTenant())) {
|
||||||
|
final String errorMessage = "The transaction date cannot be in the future.";
|
||||||
|
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage, loanTransactionDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.loanProduct.isMultiDisburseLoan() && adjustedTransaction == null) {
|
||||||
|
BigDecimal totalDisbursed = getDisbursedAmount();
|
||||||
|
if (totalDisbursed.compareTo(this.summary.getTotalPrincipalRepaid()) < 0) {
|
||||||
|
final String errorMessage = "The transaction cannot be done before the loan disbursement: "
|
||||||
|
+ getApprovedOnDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.done.before.disbursement", errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
|
if (isTransactionChronologicallyLatest && adjustedTransaction == null) {
|
||||||
|
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments,
|
||||||
|
charges());
|
||||||
|
} else {
|
||||||
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
|
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||||
|
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges());
|
||||||
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
|
mapEntry.getValue().updateLoan(this);
|
||||||
|
}
|
||||||
|
/***
|
||||||
|
* Commented since throwing exception if external id present for one
|
||||||
|
* of the transactions. for this need to save the reversed
|
||||||
|
* transactions first and then new transactions.
|
||||||
|
*/
|
||||||
|
// this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLoanSummaryDerivedFields();
|
||||||
|
|
||||||
|
// doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||||
|
|
||||||
|
return changedTransactionDetail;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private ChangedTransactionDetail handleRepaymentOrWaiverTransaction(final LoanTransaction loanTransaction,
|
private ChangedTransactionDetail handleRepaymentOrWaiverTransaction(final LoanTransaction loanTransaction,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction) {
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction) {
|
||||||
|
|
||||||
@@ -2588,6 +2648,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return totalPaidInRepayments.minus(cumulativeTotalPaidOnInstallments);
|
return totalPaidInRepayments.minus(cumulativeTotalPaidOnInstallments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money calculateTotalRecoveredPayments(){
|
||||||
|
Money totalRecoveredPayments = getTotalRecoveredPayments();
|
||||||
|
//in case logic for reversing recovered payment is implemented handle subtraction from totalRecoveredPayments
|
||||||
|
return totalRecoveredPayments;
|
||||||
|
}
|
||||||
|
|
||||||
private MonetaryCurrency loanCurrency() {
|
private MonetaryCurrency loanCurrency() {
|
||||||
return this.loanRepaymentScheduleDetail.getCurrency();
|
return this.loanRepaymentScheduleDetail.getCurrency();
|
||||||
}
|
}
|
||||||
@@ -2929,6 +2995,14 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return disbursementDate;
|
return disbursementDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalDate getWrittenOffDate(){
|
||||||
|
LocalDate writtenOffDate = null;
|
||||||
|
if (this.writtenOffOnDate != null) {
|
||||||
|
writtenOffDate = new LocalDate(this.writtenOffOnDate);
|
||||||
|
}
|
||||||
|
return writtenOffDate;
|
||||||
|
}
|
||||||
|
|
||||||
public LocalDate getExpectedDisbursedOnLocalDateForTemplate() {
|
public LocalDate getExpectedDisbursedOnLocalDateForTemplate() {
|
||||||
|
|
||||||
LocalDate expectedDisbursementDate = null;
|
LocalDate expectedDisbursementDate = null;
|
||||||
@@ -2997,6 +3071,17 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return cumulativePaid;
|
return cumulativePaid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money getTotalRecoveredPayments(){
|
||||||
|
Money cumulativePaid = Money.zero(loanCurrency());
|
||||||
|
|
||||||
|
for(final LoanTransaction recoveredPayment : this.loanTransactions){
|
||||||
|
if(recoveredPayment.isRecoveryRepayment()) {
|
||||||
|
cumulativePaid = cumulativePaid.plus(recoveredPayment.getRecoveredPaymentPortion(loanCurrency())) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cumulativePaid;
|
||||||
|
}
|
||||||
|
|
||||||
private Money getTotalInterestOutstandingOnLoan() {
|
private Money getTotalInterestOutstandingOnLoan() {
|
||||||
Money cumulativeInterest = Money.zero(loanCurrency());
|
Money cumulativeInterest = Money.zero(loanCurrency());
|
||||||
|
|
||||||
@@ -3681,6 +3766,14 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
dataValidationErrors.add(error);
|
dataValidationErrors.add(error);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case LOAN_WRITTEN_OFF:
|
||||||
|
if(!isClosedWrittenOff()){
|
||||||
|
final String defaultUserMessage = "Active Loan Account is not allowed for recovery repayment. Loan Account is Active.";
|
||||||
|
final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.close.account.is.not.active",
|
||||||
|
defaultUserMessage);
|
||||||
|
dataValidationErrors.add(error);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Regular → Executable
+5
-2
@@ -5,17 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
public interface LoanAccountDomainService {
|
public interface LoanAccountDomainService {
|
||||||
|
|
||||||
LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||||
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
||||||
|
|
||||||
|
LoanTransaction makeLoanRecoveryRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||||
|
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
||||||
|
|
||||||
LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||||
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
||||||
|
|
||||||
|
|||||||
Regular → Executable
+88
-6
@@ -5,12 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
|
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||||
@@ -43,6 +37,12 @@ import org.springframework.dao.DataIntegrityViolationException;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||||
|
|
||||||
@@ -284,4 +284,86 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public LoanTransaction makeLoanRecoveryRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate, BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId) {
|
||||||
|
checkClientOrGroupActive(loan);
|
||||||
|
|
||||||
|
// TODO: Is it required to validate transaction date with meeting dates
|
||||||
|
// if repayments is synced with meeting?
|
||||||
|
/*
|
||||||
|
* if(loan.isSyncDisbursementWithMeeting()){ // validate actual
|
||||||
|
* disbursement date against meeting date CalendarInstance
|
||||||
|
* calendarInstance =
|
||||||
|
* this.calendarInstanceRepository.findCalendarInstaneByLoanId
|
||||||
|
* (loan.getId(), CalendarEntityType.LOANS.getValue());
|
||||||
|
* this.loanEventApiJsonValidator
|
||||||
|
* .validateRepaymentDateWithMeetingDate(transactionDate,
|
||||||
|
* calendarInstance); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||||
|
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||||
|
|
||||||
|
final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount);
|
||||||
|
final LoanTransaction newRepaymentTransaction = LoanTransaction.recoveryRepayment(loan.getOffice(), repaymentAmount, paymentDetail,
|
||||||
|
transactionDate, txnExternalId);
|
||||||
|
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||||
|
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||||
|
transactionDate.toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||||
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
|
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||||
|
|
||||||
|
final ChangedTransactionDetail changedTransactionDetail = loan.makeRecoveryRepayment(newRepaymentTransaction,
|
||||||
|
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday,
|
||||||
|
holidays, workingDays, allowTransactionsOnNonWorkingDay);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loanTransactionRepository.save(newRepaymentTransaction);
|
||||||
|
} catch (DataIntegrityViolationException e) {
|
||||||
|
final Throwable realCause = e.getCause();
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||||
|
if (realCause.getMessage().toLowerCase().contains("external_id_unique")) {
|
||||||
|
baseDataValidator.reset().parameter("externalId").value(newRepaymentTransaction.getExternalId())
|
||||||
|
.failWithCode("value.must.be.unique");
|
||||||
|
}
|
||||||
|
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||||
|
"Validation errors exist.", dataValidationErrors); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* TODO Vishwas Batch save is giving me a
|
||||||
|
* HibernateOptimisticLockingFailureException, looping and saving for
|
||||||
|
* the time being, not a major issue for now as this loop is entered
|
||||||
|
* only in edge cases (when a payment is made before the latest payment
|
||||||
|
* recorded against the loan)
|
||||||
|
***/
|
||||||
|
|
||||||
|
this.loanRepository.saveAndFlush(loan);
|
||||||
|
|
||||||
|
if (changedTransactionDetail != null) {
|
||||||
|
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
|
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||||
|
// update loan with references to the newly created transactions
|
||||||
|
loan.getLoanTransactions().add(mapEntry.getValue());
|
||||||
|
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(noteText)) {
|
||||||
|
final Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText);
|
||||||
|
this.noteRepository.save(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
|
builderResult.withEntityId(newRepaymentTransaction.getId()) //
|
||||||
|
.withOfficeId(loan.getOfficeId()) //
|
||||||
|
.withClientId(loan.getClientId()) //
|
||||||
|
.withGroupId(loan.getGroupId()); //
|
||||||
|
|
||||||
|
return newRepaymentTransaction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Regular → Executable
+1
@@ -15,6 +15,7 @@ public enum LoanEvent {
|
|||||||
LOAN_WITHDRAWN, //
|
LOAN_WITHDRAWN, //
|
||||||
LOAN_APPROVED, //
|
LOAN_APPROVED, //
|
||||||
LOAN_APPROVAL_UNDO, //
|
LOAN_APPROVAL_UNDO, //
|
||||||
|
LOAN_WRITTEN_OFF,
|
||||||
LOAN_DISBURSED, //
|
LOAN_DISBURSED, //
|
||||||
LOAN_DISBURSAL_UNDO, //
|
LOAN_DISBURSAL_UNDO, //
|
||||||
LOAN_REPAYMENT_OR_WAIVER, //
|
LOAN_REPAYMENT_OR_WAIVER, //
|
||||||
|
|||||||
Regular → Executable
+21
-20
@@ -5,26 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.JoinColumn;
|
|
||||||
import javax.persistence.ManyToOne;
|
|
||||||
import javax.persistence.OneToMany;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
import javax.persistence.Temporal;
|
|
||||||
import javax.persistence.TemporalType;
|
|
||||||
import javax.persistence.UniqueConstraint;
|
|
||||||
|
|
||||||
import org.hibernate.annotations.LazyCollection;
|
import org.hibernate.annotations.LazyCollection;
|
||||||
import org.hibernate.annotations.LazyCollectionOption;
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
@@ -40,6 +20,10 @@ import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
|
|||||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All monetary transactions against a loan are modelled through this entity.
|
* All monetary transactions against a loan are modelled through this entity.
|
||||||
* Disbursements, Repayments, Waivers, Write-off etc
|
* Disbursements, Repayments, Waivers, Write-off etc
|
||||||
@@ -95,6 +79,9 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true)
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true)
|
||||||
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<LoanChargePaidBy>();
|
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<LoanChargePaidBy>();
|
||||||
|
|
||||||
|
@Column(name = "recovered_portion_derived", scale = 6, precision = 19, nullable = true)
|
||||||
|
private BigDecimal recoveredPaymentPortion;
|
||||||
|
|
||||||
protected LoanTransaction() {
|
protected LoanTransaction() {
|
||||||
this.loan = null;
|
this.loan = null;
|
||||||
this.dateOf = null;
|
this.dateOf = null;
|
||||||
@@ -111,6 +98,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
final LocalDate paymentDate, final String externalId) {
|
final LocalDate paymentDate, final String externalId) {
|
||||||
return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
|
return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
|
||||||
}
|
}
|
||||||
|
public static LoanTransaction recoveryRepayment(final Office office, final Money amount, final PaymentDetail paymentDetail,
|
||||||
|
final LocalDate paymentDate, final String externalId) {
|
||||||
|
return new LoanTransaction(null, office, LoanTransactionType.RECOVERY_REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
|
||||||
|
}
|
||||||
|
|
||||||
public static LoanTransaction loanPayment(final Loan loan, final Office office, final Money amount, final PaymentDetail paymentDetail,
|
public static LoanTransaction loanPayment(final Loan loan, final Office office, final Money amount, final PaymentDetail paymentDetail,
|
||||||
final LocalDate paymentDate, final String externalId, final LoanTransactionType transactionType) {
|
final LocalDate paymentDate, final String externalId, final LoanTransactionType transactionType) {
|
||||||
@@ -296,6 +287,11 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
this.overPaymentPortion = defaultToNullIfZero(getOverPaymentPortion(currency).plus(overPayment).getAmount());
|
this.overPaymentPortion = defaultToNullIfZero(getOverPaymentPortion(currency).plus(overPayment).getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateRecoveredPayments(final Money recoveredPayment){
|
||||||
|
final MonetaryCurrency currency = recoveredPayment.getCurrency();
|
||||||
|
this.recoveredPaymentPortion = defaultToNullIfZero(getRecoveredPaymentPortion(currency).plus(recoveredPayment).getAmount());
|
||||||
|
}
|
||||||
|
|
||||||
public Money getPrincipalPortion(final MonetaryCurrency currency) {
|
public Money getPrincipalPortion(final MonetaryCurrency currency) {
|
||||||
return Money.of(currency, this.principalPortion);
|
return Money.of(currency, this.principalPortion);
|
||||||
}
|
}
|
||||||
@@ -320,6 +316,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
return Money.of(currency, this.overPaymentPortion);
|
return Money.of(currency, this.overPaymentPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money getRecoveredPaymentPortion(final MonetaryCurrency currency){
|
||||||
|
return Money.of(currency,this.recoveredPaymentPortion) ;
|
||||||
|
}
|
||||||
|
|
||||||
public Money getAmount(final MonetaryCurrency currency) {
|
public Money getAmount(final MonetaryCurrency currency) {
|
||||||
return Money.of(currency, this.amount);
|
return Money.of(currency, this.amount);
|
||||||
}
|
}
|
||||||
@@ -471,6 +471,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
thisTransactionData.put("feeChargesPortion", this.feeChargesPortion);
|
thisTransactionData.put("feeChargesPortion", this.feeChargesPortion);
|
||||||
thisTransactionData.put("penaltyChargesPortion", this.penaltyChargesPortion);
|
thisTransactionData.put("penaltyChargesPortion", this.penaltyChargesPortion);
|
||||||
thisTransactionData.put("overPaymentPortion", this.overPaymentPortion);
|
thisTransactionData.put("overPaymentPortion", this.overPaymentPortion);
|
||||||
|
thisTransactionData.put("recoveryPaymentPortion", this.recoveredPaymentPortion);
|
||||||
|
|
||||||
if (this.paymentDetail != null) {
|
if (this.paymentDetail != null) {
|
||||||
thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId());
|
thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId());
|
||||||
|
|||||||
Regular → Executable
+5
-2
@@ -106,7 +106,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
for (final LoanTransaction loanTransaction : transactionstoBeProcessed) {
|
for (final LoanTransaction loanTransaction : transactionstoBeProcessed) {
|
||||||
|
|
||||||
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver()) {
|
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||||
// pass through for new transactions
|
// pass through for new transactions
|
||||||
if (loanTransaction.getId() == null) {
|
if (loanTransaction.getId() == null) {
|
||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
@@ -202,7 +202,10 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionAmountUnprocessed.isGreaterThanZero()) {
|
if(loanTransaction.isRecoveryRepayment()){
|
||||||
|
loanTransaction.updateRecoveredPayments(transactionAmountUnprocessed);
|
||||||
|
System.out.println(loanTransaction.getRecoveredPaymentPortion(currency)) ;
|
||||||
|
}else if(transactionAmountUnprocessed.isGreaterThanZero() && !loanTransaction.isRecoveryRepayment()) {
|
||||||
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
|
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
|
||||||
loanTransaction.updateOverPayments(transactionAmountUnprocessed);
|
loanTransaction.updateOverPayments(transactionAmountUnprocessed);
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created with IntelliJ IDEA.
|
||||||
|
* User: andrew
|
||||||
|
* Date: 2-4-14
|
||||||
|
* Time: 15:21
|
||||||
|
* To change this template use File | Settings | File Templates.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class LoanRecoveryPaymentCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final LoanWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public LoanRecoveryPaymentCommandHandler(LoanWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(JsonCommand command) {
|
||||||
|
return writePlatformService.makeLoanRecoveryPayment(command.getLoanId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -30,6 +30,8 @@ public interface LoanWritePlatformService {
|
|||||||
|
|
||||||
CommandProcessingResult makeLoanRepayment(Long loanId, JsonCommand command);
|
CommandProcessingResult makeLoanRepayment(Long loanId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult makeLoanRecoveryPayment(Long loanId, JsonCommand command);
|
||||||
|
|
||||||
Map<String, Object> makeLoanBulkRepayment(CollectionSheetBulkRepaymentCommand bulkRepaymentCommand);
|
Map<String, Object> makeLoanBulkRepayment(CollectionSheetBulkRepaymentCommand bulkRepaymentCommand);
|
||||||
|
|
||||||
CommandProcessingResult adjustLoanTransaction(Long loanId, Long transactionId, JsonCommand command);
|
CommandProcessingResult adjustLoanTransaction(Long loanId, Long transactionId, JsonCommand command);
|
||||||
|
|||||||
Regular → Executable
+40
-34
@@ -5,17 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.service;
|
package org.mifosplatform.portfolio.loanaccount.service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import com.google.gson.JsonElement;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.joda.time.format.DateTimeFormat;
|
import org.joda.time.format.DateTimeFormat;
|
||||||
@@ -63,16 +53,11 @@ import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
|||||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
import org.mifosplatform.portfolio.charge.exception.*;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
|
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException;
|
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException.LOAN_CHARGE_CANNOT_BE_PAYED_REASON;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException.LOAN_CHARGE_CANNOT_BE_PAYED_REASON;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException;
|
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException.LOAN_CHARGE_CANNOT_BE_UPDATED_REASON;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException.LOAN_CHARGE_CANNOT_BE_UPDATED_REASON;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException;
|
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LOAN_CHARGE_CANNOT_BE_WAIVED_REASON;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LOAN_CHARGE_CANNOT_BE_WAIVED_REASON;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
|
|
||||||
import org.mifosplatform.portfolio.client.domain.Client;
|
import org.mifosplatform.portfolio.client.domain.Client;
|
||||||
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
|
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
|
||||||
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
||||||
@@ -85,22 +70,7 @@ import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
import org.mifosplatform.portfolio.loanaccount.domain.*;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanOverdueInstallmentCharge;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException;
|
import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
|
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
|
||||||
@@ -130,7 +100,8 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
|
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
|
||||||
@@ -1951,4 +1922,39 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
this.accountTransferRepository.save(transferTransaction);
|
this.accountTransferRepository.save(transferTransaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult makeLoanRecoveryPayment(Long loanId, JsonCommand command) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
|
||||||
|
this.loanEventApiJsonValidator.validateNewRepaymentTransaction(command.json());
|
||||||
|
|
||||||
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
|
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||||
|
|
||||||
|
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||||
|
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||||
|
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
|
||||||
|
changes.put("locale", command.locale());
|
||||||
|
changes.put("dateFormat", command.dateFormat());
|
||||||
|
changes.put("paymentTypeId",command.stringValueOfParameterNamed("paymentTypeId"));
|
||||||
|
|
||||||
|
final String noteText = command.stringValueOfParameterNamed("note");
|
||||||
|
if (StringUtils.isNotBlank(noteText)) {
|
||||||
|
changes.put("note", noteText);
|
||||||
|
}
|
||||||
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
|
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
||||||
|
|
||||||
|
final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder();
|
||||||
|
this.loanAccountDomainService.makeLoanRecoveryRepayment(loan, commandProcessingResultBuilder, transactionDate, transactionAmount,
|
||||||
|
paymentDetail, noteText, txnExternalId);
|
||||||
|
|
||||||
|
return commandProcessingResultBuilder.withCommandId(command.commandId()) //
|
||||||
|
.withLoanId(loanId) //
|
||||||
|
.with(changes) //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Regular → Executable
+11
-1
@@ -54,7 +54,7 @@ public final class LoanProductDataValidator {
|
|||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(),
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), LoanProductConstants.useBorrowerCycleParameterName,
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), LoanProductConstants.useBorrowerCycleParameterName,
|
||||||
LoanProductConstants.principalVariationsForBorrowerCycleParameterName,
|
LoanProductConstants.principalVariationsForBorrowerCycleParameterName,
|
||||||
LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
|
LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
|
||||||
@@ -299,6 +299,11 @@ public final class LoanProductDataValidator {
|
|||||||
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
|
||||||
.notNull().integerGreaterThanZero();
|
.notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue()).value(incomeFromRecoveryAccountId)
|
||||||
|
.notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
||||||
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
|
||||||
@@ -600,6 +605,11 @@ public final class LoanProductDataValidator {
|
|||||||
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
|
||||||
.ignoreIfNull().integerGreaterThanZero();
|
.ignoreIfNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), element);
|
||||||
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue()).value(incomeFromRecoveryAccountId)
|
||||||
|
.notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
|
||||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
|
||||||
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
|
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
|
||||||
|
|||||||
Reference in New Issue
Block a user