MIFOSX-1146: added support for disbursing loan amount to savings account
This commit is contained in:
@@ -876,6 +876,14 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>loans/{loanId}?command=disburseToSavings</td>
|
||||||
|
<td><a href="#loans_disburse_to_savings">Disburse Loan To Savings Account</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
<tr class="alt">
|
<tr class="alt">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>loans/{loanId}?command=undoDisbursal</td>
|
<td>loans/{loanId}?command=undoDisbursal</td>
|
||||||
@@ -6927,6 +6935,74 @@ Request Body:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a id="loans_disburse_to_savings" name="loans_disburse_to_savings" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Disburse Loan To Savings Account</h4>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>actualDisbursementDate</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Optional Fields</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>transactionAmount,fixedEmiAmount</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/loans/{loanId}?command=disburseToSavings
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST loans/1?command=disburse
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"dateFormat": "dd MMMM yyyy",
|
||||||
|
"locale": "en",
|
||||||
|
"transactionAmount":10000,
|
||||||
|
"fixedEmiAmount""1100,
|
||||||
|
"actualDisbursementDate": "14 May 2013",
|
||||||
|
"note": ""
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId": 1,
|
||||||
|
"clientId": 1,
|
||||||
|
"loanId": 1,
|
||||||
|
"resourceId": 1,
|
||||||
|
"changes": {
|
||||||
|
"status": {
|
||||||
|
"id": 300,
|
||||||
|
"code": "loanStatusType.active",
|
||||||
|
"value": "Active",
|
||||||
|
"pendingApproval": false,
|
||||||
|
"waitingForDisbursal": false,
|
||||||
|
"active": true,
|
||||||
|
"closedObligationsMet": false,
|
||||||
|
"closedWrittenOff": false,
|
||||||
|
"closedRescheduled": false,
|
||||||
|
"closed": false,
|
||||||
|
"overpaid": false
|
||||||
|
},
|
||||||
|
"locale": "en",
|
||||||
|
"dateFormat": "dd MMMM yyyy",
|
||||||
|
"actualDisbursementDate": "14 May 2013",
|
||||||
|
"transactionAmount":10000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a id="loans_disburse_undo" name="loans_disburse_undo" class="old-syle-anchor"> </a>
|
<a id="loans_disburse_undo" name="loans_disburse_undo" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
@@ -28905,6 +28981,14 @@ No Request Body:
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="alt">
|
||||||
|
<td></td>
|
||||||
|
<td>loans/{loanId}?command=disburseToSavings</td>
|
||||||
|
<td><a href="#loans_disburse_to_savings">Disburse Loan To Savings Account</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>loans/{loanId}?command=undoDisbursal</td>
|
<td>loans/{loanId}?command=undoDisbursal</td>
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_loan', 'DISBURSETOSAVINGS_LOAN', 'LOAN', 'DISBURSETOSAVINGS', 0);
|
||||||
+186
-12
@@ -56,8 +56,8 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
final Integer loanProductID = createLoanProduct();
|
final Integer loanProductID = createLoanProduct(false);
|
||||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, null);
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID, null, null, "12,000.00");
|
||||||
final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec,
|
final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec,
|
||||||
loanID);
|
loanID);
|
||||||
verifyLoanRepaymentSchedule(loanSchedule);
|
verifyLoanRepaymentSchedule(loanSchedule);
|
||||||
@@ -70,7 +70,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
final Integer loanProductID = createLoanProduct();
|
final Integer loanProductID = createLoanProduct(false);
|
||||||
|
|
||||||
List<HashMap> charges = new ArrayList<HashMap>();
|
List<HashMap> charges = new ArrayList<HashMap>();
|
||||||
Integer flatDisbursement = ChargesHelper.createCharges(requestSpec, responseSpec, ChargesHelper.getLoanDisbursementJSON());
|
Integer flatDisbursement = ChargesHelper.createCharges(requestSpec, responseSpec, ChargesHelper.getLoanDisbursementJSON());
|
||||||
@@ -85,7 +85,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
ChargesHelper.getLoanDisbursementJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST, "1"));
|
ChargesHelper.getLoanDisbursementJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST, "1"));
|
||||||
addCharges(charges, interestPercentage, "1", null);
|
addCharges(charges, interestPercentage, "1", null);
|
||||||
|
|
||||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, charges);
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID, charges, null, "12,000.00");
|
||||||
Assert.assertNotNull(loanID);
|
Assert.assertNotNull(loanID);
|
||||||
|
|
||||||
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
@@ -177,7 +177,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
final Integer loanProductID = createLoanProduct();
|
final Integer loanProductID = createLoanProduct(false);
|
||||||
|
|
||||||
List<HashMap> charges = new ArrayList<HashMap>();
|
List<HashMap> charges = new ArrayList<HashMap>();
|
||||||
Integer flat = ChargesHelper.createCharges(requestSpec, responseSpec,
|
Integer flat = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||||
@@ -196,7 +196,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST, "1", false));
|
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST, "1", false));
|
||||||
addCharges(charges, interestPercentage, "1", "29 September 2011");
|
addCharges(charges, interestPercentage, "1", "29 September 2011");
|
||||||
|
|
||||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, charges);
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID, charges, null, "12,000.00");
|
||||||
Assert.assertNotNull(loanID);
|
Assert.assertNotNull(loanID);
|
||||||
|
|
||||||
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
@@ -336,7 +336,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
final Integer loanProductID = createLoanProduct();
|
final Integer loanProductID = createLoanProduct(false);
|
||||||
|
|
||||||
List<HashMap> charges = new ArrayList<HashMap>();
|
List<HashMap> charges = new ArrayList<HashMap>();
|
||||||
Integer flat = ChargesHelper.createCharges(requestSpec, responseSpec,
|
Integer flat = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||||
@@ -354,7 +354,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
ChargesHelper.getLoanInstallmentJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST, "1", false));
|
ChargesHelper.getLoanInstallmentJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_INTEREST, "1", false));
|
||||||
addCharges(charges, interestPercentage, "1", "29 September 2011");
|
addCharges(charges, interestPercentage, "1", "29 September 2011");
|
||||||
|
|
||||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, charges);
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID, charges, null, "12,000.00");
|
||||||
Assert.assertNotNull(loanID);
|
Assert.assertNotNull(loanID);
|
||||||
|
|
||||||
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
@@ -594,6 +594,148 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoanCharges_DISBURSEMENT_TO_SAVINGS() {
|
||||||
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
|
SavingsAccountHelper savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||||
|
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
|
final Integer loanProductID = createLoanProduct(false);
|
||||||
|
|
||||||
|
final Integer savingsId = createSavings(clientID);
|
||||||
|
|
||||||
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID, null, savingsId.toString(), "12,000.00");
|
||||||
|
Assert.assertNotNull(loanID);
|
||||||
|
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoan("20 September 2011", loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
HashMap summary = savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
Float balance = new Float(MINIMUM_OPENING_BALANCE);
|
||||||
|
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
|
||||||
|
|
||||||
|
// DISBURSE
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoanToSavings(SavingsAccountHelper.TRANSACTION_DATE, loanID);
|
||||||
|
System.out.println("DISBURSE " + loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
summary = savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
balance = new Float(MINIMUM_OPENING_BALANCE) + new Float("12000");
|
||||||
|
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.undoDisbursal(loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
summary = savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
balance = new Float(MINIMUM_OPENING_BALANCE);
|
||||||
|
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoanCharges_DISBURSEMENT_WITH_TRANCHES() {
|
||||||
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
|
final Integer loanProductID = createLoanProduct(true);
|
||||||
|
|
||||||
|
List<HashMap> tranches = new ArrayList<HashMap>();
|
||||||
|
tranches.add(createTrancheDetail("1 March 2014", "25000"));
|
||||||
|
tranches.add(createTrancheDetail("23 April 2014", "20000"));
|
||||||
|
|
||||||
|
final Integer loanID = applyForLoanApplicationWithTranches(clientID, loanProductID, null, null, "45,000.00", tranches);
|
||||||
|
Assert.assertNotNull(loanID);
|
||||||
|
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoan("1 March 2014", loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
// DISBURSE first Tranche
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoan("1 March 2014", loanID);
|
||||||
|
System.out.println("DISBURSE " + loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
// DISBURSE Second Tranche
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoan("23 April 2014", loanID);
|
||||||
|
System.out.println("DISBURSE " + loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.undoDisbursal(loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoanCharges_DISBURSEMENT_TO_SAVINGS_WITH_TRANCHES() {
|
||||||
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
|
SavingsAccountHelper savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||||
|
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
|
final Integer loanProductID = createLoanProduct(true);
|
||||||
|
|
||||||
|
final Integer savingsId = createSavings(clientID);
|
||||||
|
|
||||||
|
List<HashMap> tranches = new ArrayList<HashMap>();
|
||||||
|
tranches.add(createTrancheDetail("1 March 2014", "25000"));
|
||||||
|
tranches.add(createTrancheDetail("23 April 2014", "20000"));
|
||||||
|
|
||||||
|
final Integer loanID = applyForLoanApplicationWithTranches(clientID, loanProductID, null, savingsId.toString(), "45,000.00",
|
||||||
|
tranches);
|
||||||
|
Assert.assertNotNull(loanID);
|
||||||
|
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoan("1 March 2014", loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
HashMap summary = savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
Float balance = new Float(MINIMUM_OPENING_BALANCE);
|
||||||
|
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
|
||||||
|
|
||||||
|
// DISBURSE first Tranche
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoanToSavings("1 March 2014", loanID);
|
||||||
|
System.out.println("DISBURSE " + loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
summary = savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
balance = new Float(MINIMUM_OPENING_BALANCE) + new Float("25000");
|
||||||
|
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
|
||||||
|
|
||||||
|
// DISBURSE Second Tranche
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoanToSavings("23 April 2014", loanID);
|
||||||
|
System.out.println("DISBURSE " + loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
summary = savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
balance = new Float(MINIMUM_OPENING_BALANCE) + new Float("25000") + new Float("20000");
|
||||||
|
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.undoDisbursal(loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
summary = savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
balance = new Float(MINIMUM_OPENING_BALANCE);
|
||||||
|
assertEquals("Verifying opening Balance", balance, summary.get("accountBalance"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void validateCharge(Integer amountPercentage, final List<HashMap> loanCharges, final String amount, final String outstanding,
|
private void validateCharge(Integer amountPercentage, final List<HashMap> loanCharges, final String amount, final String outstanding,
|
||||||
String amountPaid, String amountWaived) {
|
String amountPaid, String amountWaived) {
|
||||||
HashMap chargeDetail = getloanCharge(amountPercentage, loanCharges);
|
HashMap chargeDetail = getloanCharge(amountPercentage, loanCharges);
|
||||||
@@ -626,7 +768,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
Assert.assertTrue(new Float(twoDForm.format(new Float(val))).compareTo(new Float(twoDForm.format(new Float(val2)))) == 0);
|
Assert.assertTrue(new Float(twoDForm.format(new Float(val))).compareTo(new Float(twoDForm.format(new Float(val2)))) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer createLoanProduct() {
|
private Integer createLoanProduct(boolean multiDisburseLoan) {
|
||||||
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||||
final String loanProductJSON = new LoanProductTestBuilder() //
|
final String loanProductJSON = new LoanProductTestBuilder() //
|
||||||
.withPrincipal("12,000.00") //
|
.withPrincipal("12,000.00") //
|
||||||
@@ -637,14 +779,16 @@ public class ClientLoanIntegrationTest {
|
|||||||
.withInterestRateFrequencyTypeAsMonths() //
|
.withInterestRateFrequencyTypeAsMonths() //
|
||||||
.withAmortizationTypeAsEqualInstallments() //
|
.withAmortizationTypeAsEqualInstallments() //
|
||||||
.withInterestTypeAsDecliningBalance() //
|
.withInterestTypeAsDecliningBalance() //
|
||||||
|
.withTranches(multiDisburseLoan) //
|
||||||
.build(null);
|
.build(null);
|
||||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, List<HashMap> charges) {
|
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, List<HashMap> charges,
|
||||||
|
final String savingsId, String principal) {
|
||||||
System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||||
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||||
.withPrincipal("12,000.00") //
|
.withPrincipal(principal) //
|
||||||
.withLoanTermFrequency("4") //
|
.withLoanTermFrequency("4") //
|
||||||
.withLoanTermFrequencyAsMonths() //
|
.withLoanTermFrequencyAsMonths() //
|
||||||
.withNumberOfRepayments("4") //
|
.withNumberOfRepayments("4") //
|
||||||
@@ -656,7 +800,29 @@ public class ClientLoanIntegrationTest {
|
|||||||
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
|
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
|
||||||
.withExpectedDisbursementDate("20 September 2011") //
|
.withExpectedDisbursementDate("20 September 2011") //
|
||||||
.withSubmittedOnDate("20 September 2011") //
|
.withSubmittedOnDate("20 September 2011") //
|
||||||
.withCharges(charges).build(clientID.toString(), loanProductID.toString(), null);
|
.withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
|
||||||
|
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer applyForLoanApplicationWithTranches(final Integer clientID, final Integer loanProductID, List<HashMap> charges,
|
||||||
|
final String savingsId, String principal, List<HashMap> tranches) {
|
||||||
|
System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||||
|
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||||
|
.withPrincipal(principal) //
|
||||||
|
.withLoanTermFrequency("4") //
|
||||||
|
.withLoanTermFrequencyAsMonths() //
|
||||||
|
.withNumberOfRepayments("4") //
|
||||||
|
.withRepaymentEveryAfter("1") //
|
||||||
|
.withRepaymentFrequencyTypeAsMonths() //
|
||||||
|
.withInterestRatePerPeriod("2") //
|
||||||
|
.withAmortizationTypeAsEqualInstallments() //
|
||||||
|
.withInterestTypeAsDecliningBalance() //
|
||||||
|
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
|
||||||
|
.withExpectedDisbursementDate("1 March 2014") //
|
||||||
|
.withTranches(tranches) //
|
||||||
|
.withSubmittedOnDate("1 March 2014") //
|
||||||
|
|
||||||
|
.withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
|
||||||
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,4 +950,12 @@ public class ClientLoanIntegrationTest {
|
|||||||
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
|
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HashMap createTrancheDetail(final String date, final String amount) {
|
||||||
|
HashMap detail = new HashMap();
|
||||||
|
detail.put("expectedDisbursementDate", date);
|
||||||
|
detail.put("principal", amount);
|
||||||
|
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+9
-1
@@ -119,9 +119,17 @@ public class ChargesHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getLoanDisbursementJSON(final Integer chargeCalculationType, final String amount) {
|
public static String getLoanDisbursementJSON(final Integer chargeCalculationType, final String amount) {
|
||||||
|
return getLoanDisbursementJSON(chargeCalculationType, amount, ChargesHelper.CHARGE_PAYMENT_MODE_REGULAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLoanDisbursementAccountTransferJSON(final Integer chargeCalculationType, final String amount) {
|
||||||
|
return getLoanDisbursementJSON(chargeCalculationType, amount, ChargesHelper.CHARGE_PAYMENT_MODE_ACCOUNT_TRANSFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLoanDisbursementJSON(final Integer chargeCalculationType, final String amount, final Integer paymentmode) {
|
||||||
final HashMap<String, Object> map = populateDefaultsForLoan();
|
final HashMap<String, Object> map = populateDefaultsForLoan();
|
||||||
map.put("chargeTimeType", CHARGE_DISBURSEMENT_FEE);
|
map.put("chargeTimeType", CHARGE_DISBURSEMENT_FEE);
|
||||||
map.put("chargePaymentMode", ChargesHelper.CHARGE_PAYMENT_MODE_REGULAR);
|
map.put("chargePaymentMode", paymentmode);
|
||||||
map.put("amount", amount);
|
map.put("amount", amount);
|
||||||
map.put("chargeCalculationType", chargeCalculationType);
|
map.put("chargeCalculationType", chargeCalculationType);
|
||||||
String chargesCreateJson = new Gson().toJson(map);
|
String chargesCreateJson = new Gson().toJson(map);
|
||||||
|
|||||||
+15
-1
@@ -3,7 +3,6 @@ package org.mifosplatform.integrationtests.common.loans;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
@@ -35,6 +34,9 @@ public class LoanApplicationTestBuilder {
|
|||||||
private String expectedDisbursmentDate = "";
|
private String expectedDisbursmentDate = "";
|
||||||
private String submittedOnDate = "";
|
private String submittedOnDate = "";
|
||||||
private String loanType = "individual";
|
private String loanType = "individual";
|
||||||
|
private String fixedEmiAmount = "10000";
|
||||||
|
private String maxOutstandingLoanBalance = "36000";
|
||||||
|
private List<HashMap> disbursementData = null;
|
||||||
private List<HashMap> charges = new ArrayList<HashMap>();
|
private List<HashMap> charges = new ArrayList<HashMap>();
|
||||||
|
|
||||||
public String build(final String ID, final String loanProductId, final String savingsID) {
|
public String build(final String ID, final String loanProductId, final String savingsID) {
|
||||||
@@ -68,6 +70,14 @@ public class LoanApplicationTestBuilder {
|
|||||||
if (savingsID != null) {
|
if (savingsID != null) {
|
||||||
map.put("linkAccountId", savingsID);
|
map.put("linkAccountId", savingsID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (disbursementData != null) {
|
||||||
|
map.put("disbursementData", disbursementData);
|
||||||
|
map.put("fixedEmiAmount", fixedEmiAmount);
|
||||||
|
map.put("maxOutstandingLoanBalance", maxOutstandingLoanBalance);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return new Gson().toJson(map);
|
return new Gson().toJson(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,4 +191,8 @@ public class LoanApplicationTestBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanApplicationTestBuilder withTranches(final List<HashMap> disbursementData) {
|
||||||
|
this.disbursementData = disbursementData;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -53,6 +53,10 @@ public class LoanProductTestBuilder {
|
|||||||
private String maxPrincipal = "100000.00";
|
private String maxPrincipal = "100000.00";
|
||||||
private Account[] accountList = null;
|
private Account[] accountList = null;
|
||||||
|
|
||||||
|
private Boolean multiDisburseLoan = false;
|
||||||
|
private String outstandingLoanBalance = "35000";
|
||||||
|
private String maxTrancheCount = "35000";
|
||||||
|
|
||||||
public String build(final String chargeId) {
|
public String build(final String chargeId) {
|
||||||
final HashMap<String, Object> map = new HashMap<String, Object>();
|
final HashMap<String, Object> map = new HashMap<String, Object>();
|
||||||
|
|
||||||
@@ -83,6 +87,11 @@ public class LoanProductTestBuilder {
|
|||||||
map.put("minPrincipal", this.minPrincipal);
|
map.put("minPrincipal", this.minPrincipal);
|
||||||
map.put("maxPrincipal", this.maxPrincipal);
|
map.put("maxPrincipal", this.maxPrincipal);
|
||||||
map.put("overdueDaysForNPA", this.overdueDaysForNPA);
|
map.put("overdueDaysForNPA", this.overdueDaysForNPA);
|
||||||
|
if (multiDisburseLoan) {
|
||||||
|
map.put("multiDisburseLoan", this.multiDisburseLoan);
|
||||||
|
map.put("maxTrancheCount", this.maxTrancheCount);
|
||||||
|
map.put("outstandingLoanBalance", this.outstandingLoanBalance);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.accountingRule.equals(ACCRUAL_UPFRONT) || this.accountingRule.equals(ACCRUAL_PERIODIC)) {
|
if (this.accountingRule.equals(ACCRUAL_UPFRONT) || this.accountingRule.equals(ACCRUAL_PERIODIC)) {
|
||||||
map.putAll(getAccountMappingForAccrualBased());
|
map.putAll(getAccountMappingForAccrualBased());
|
||||||
@@ -215,6 +224,11 @@ public class LoanProductTestBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanProductTestBuilder withTranches(boolean multiDisburseLoan) {
|
||||||
|
this.multiDisburseLoan = multiDisburseLoan;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, String> getAccountMappingForCashBased() {
|
private Map<String, String> getAccountMappingForCashBased() {
|
||||||
final Map<String, String> map = new HashMap<String, String>();
|
final Map<String, String> map = new HashMap<String, String>();
|
||||||
for (int i = 0; i < this.accountList.length; i++) {
|
for (int i = 0; i < this.accountList.length; i++) {
|
||||||
|
|||||||
+5
@@ -22,6 +22,7 @@ public class LoanTransactionHelper {
|
|||||||
private static final String APPROVE_LOAN_COMMAND = "approve";
|
private static final String APPROVE_LOAN_COMMAND = "approve";
|
||||||
private static final String UNDO_APPROVAL_LOAN_COMMAND = "undoApproval";
|
private static final String UNDO_APPROVAL_LOAN_COMMAND = "undoApproval";
|
||||||
private static final String DISBURSE_LOAN_COMMAND = "disburse";
|
private static final String DISBURSE_LOAN_COMMAND = "disburse";
|
||||||
|
private static final String DISBURSE_LOAN_TO_SAVINGS_COMMAND = "disburseToSavings";
|
||||||
private static final String UNDO_DISBURSE_LOAN_COMMAND = "undoDisbursal";
|
private static final String UNDO_DISBURSE_LOAN_COMMAND = "undoDisbursal";
|
||||||
private static final String WRITE_OFF_LOAN_COMMAND = "writeoff";
|
private static final String WRITE_OFF_LOAN_COMMAND = "writeoff";
|
||||||
private static final String WAIVE_INTEREST_COMMAND = "waiveinterest";
|
private static final String WAIVE_INTEREST_COMMAND = "waiveinterest";
|
||||||
@@ -83,6 +84,10 @@ public class LoanTransactionHelper {
|
|||||||
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date));
|
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HashMap disburseLoanToSavings(final String date, final Integer loanID) {
|
||||||
|
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_TO_SAVINGS_COMMAND, loanID), getDisburseLoanAsJSON(date));
|
||||||
|
}
|
||||||
|
|
||||||
public HashMap undoDisbursal(final Integer loanID) {
|
public HashMap undoDisbursal(final Integer loanID) {
|
||||||
final String undoDisburseJson = "{'note' : 'UNDO DISBURSAL'}";
|
final String undoDisburseJson = "{'note' : 'UNDO DISBURSAL'}";
|
||||||
System.out.println("IN DISBURSE LOAN");
|
System.out.println("IN DISBURSE LOAN");
|
||||||
|
|||||||
@@ -374,6 +374,10 @@ public class CommandWrapper {
|
|||||||
return this.actionName.equalsIgnoreCase("DISBURSE") && this.entityName.equalsIgnoreCase("LOAN");
|
return this.actionName.equalsIgnoreCase("DISBURSE") && this.entityName.equalsIgnoreCase("LOAN");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDisbursementOfLoanToSavings() {
|
||||||
|
return this.actionName.equalsIgnoreCase("DISBURSETOSAVINGS") && this.entityName.equalsIgnoreCase("LOAN");
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isUndoDisbursementOfLoan() {
|
public boolean isUndoDisbursementOfLoan() {
|
||||||
return this.actionName.equalsIgnoreCase("DISBURSALUNDO") && this.entityName.equalsIgnoreCase("LOAN");
|
return this.actionName.equalsIgnoreCase("DISBURSALUNDO") && this.entityName.equalsIgnoreCase("LOAN");
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -702,6 +702,15 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder disburseLoanToSavingsApplication(final Long loanId) {
|
||||||
|
this.actionName = "DISBURSETOSAVINGS";
|
||||||
|
this.entityName = "LOAN";
|
||||||
|
this.entityId = loanId;
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.href = "/loans/" + loanId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder undoLoanApplicationApproval(final Long loanId) {
|
public CommandWrapperBuilder undoLoanApplicationApproval(final Long loanId) {
|
||||||
this.actionName = "APPROVALUNDO";
|
this.actionName = "APPROVALUNDO";
|
||||||
this.entityName = "LOAN";
|
this.entityName = "LOAN";
|
||||||
|
|||||||
+2
@@ -335,6 +335,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
handler = this.applicationContext.getBean("loanApplicationRejectedCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("loanApplicationRejectedCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isDisbursementOfLoan()) {
|
} else if (wrapper.isDisbursementOfLoan()) {
|
||||||
handler = this.applicationContext.getBean("disburseLoanCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("disburseLoanCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
} else if (wrapper.isDisbursementOfLoanToSavings()) {
|
||||||
|
handler = this.applicationContext.getBean("disburseLoanToSavingsCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isUndoDisbursementOfLoan()) {
|
} else if (wrapper.isUndoDisbursementOfLoan()) {
|
||||||
handler = this.applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("undoDisbursalLoanCommandHandler", NewCommandSourceHandler.class);
|
||||||
} else if (wrapper.isLoanRepayment()) {
|
} else if (wrapper.isLoanRepayment()) {
|
||||||
|
|||||||
+22
-3
@@ -7,6 +7,7 @@ import org.joda.time.LocalDate;
|
|||||||
import org.joda.time.format.DateTimeFormatter;
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||||
import org.mifosplatform.portfolio.account.domain.AccountTransferDetails;
|
import org.mifosplatform.portfolio.account.domain.AccountTransferDetails;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
|
|
||||||
public class AccountTransferDTO {
|
public class AccountTransferDTO {
|
||||||
@@ -27,12 +28,16 @@ public class AccountTransferDTO {
|
|||||||
private final Integer loanInstallmentNumber;
|
private final Integer loanInstallmentNumber;
|
||||||
private final Integer transferType;
|
private final Integer transferType;
|
||||||
private final AccountTransferDetails accountTransferDetails;
|
private final AccountTransferDetails accountTransferDetails;
|
||||||
|
private final String noteText;
|
||||||
|
private final String txnExternalId;
|
||||||
|
private final Loan loan;
|
||||||
|
|
||||||
public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount,
|
public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount,
|
||||||
final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId,
|
final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId,
|
||||||
final Long toAccountId, final String description, final Locale locale, final DateTimeFormatter fmt,
|
final Long toAccountId, final String description, final Locale locale, final DateTimeFormatter fmt,
|
||||||
final PaymentDetail paymentDetail, final Integer fromTransferType, final Integer toTransferType, final Long chargeId,
|
final PaymentDetail paymentDetail, final Integer fromTransferType, final Integer toTransferType, final Long chargeId,
|
||||||
Integer loanInstallmentNumber, Integer transferType, final AccountTransferDetails accountTransferDetails) {
|
Integer loanInstallmentNumber, Integer transferType, final AccountTransferDetails accountTransferDetails,final String noteText,
|
||||||
|
final String txnExternalId,final Loan loan) {
|
||||||
this.transactionDate = transactionDate;
|
this.transactionDate = transactionDate;
|
||||||
this.transactionAmount = transactionAmount;
|
this.transactionAmount = transactionAmount;
|
||||||
this.fromAccountType = fromAccountType;
|
this.fromAccountType = fromAccountType;
|
||||||
@@ -49,6 +54,9 @@ public class AccountTransferDTO {
|
|||||||
this.loanInstallmentNumber = loanInstallmentNumber;
|
this.loanInstallmentNumber = loanInstallmentNumber;
|
||||||
this.transferType = transferType;
|
this.transferType = transferType;
|
||||||
this.accountTransferDetails = accountTransferDetails;
|
this.accountTransferDetails = accountTransferDetails;
|
||||||
|
this.noteText = noteText;
|
||||||
|
this.txnExternalId = txnExternalId;
|
||||||
|
this.loan = loan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getTransactionDate() {
|
public LocalDate getTransactionDate() {
|
||||||
@@ -107,14 +115,25 @@ public class AccountTransferDTO {
|
|||||||
return this.loanInstallmentNumber;
|
return this.loanInstallmentNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Integer getTransferType() {
|
public Integer getTransferType() {
|
||||||
return this.transferType;
|
return this.transferType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public AccountTransferDetails getAccountTransferDetails() {
|
public AccountTransferDetails getAccountTransferDetails() {
|
||||||
return this.accountTransferDetails;
|
return this.accountTransferDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getNoteText() {
|
||||||
|
return this.noteText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTxnExternalId() {
|
||||||
|
return this.txnExternalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Loan getLoan() {
|
||||||
|
return this.loan;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-5
@@ -34,6 +34,7 @@ import org.mifosplatform.portfolio.account.domain.AccountTransferType;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
|
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
|
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
|
||||||
@@ -292,7 +293,11 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
Loan fromLoanAccount = null;
|
Loan fromLoanAccount = null;
|
||||||
SavingsAccount toSavingsAccount = null;
|
SavingsAccount toSavingsAccount = null;
|
||||||
if (accountTransferDetails == null) {
|
if (accountTransferDetails == null) {
|
||||||
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
if (accountTransferDTO.getLoan() == null) {
|
||||||
|
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
||||||
|
} else {
|
||||||
|
fromLoanAccount = accountTransferDTO.getLoan();
|
||||||
|
}
|
||||||
toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||||
} else {
|
} else {
|
||||||
fromLoanAccount = accountTransferDetails.fromLoanAccount();
|
fromLoanAccount = accountTransferDetails.fromLoanAccount();
|
||||||
@@ -300,15 +305,23 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
toSavingsAccount = accountTransferDetails.toSavingsAccount();
|
toSavingsAccount = accountTransferDetails.toSavingsAccount();
|
||||||
this.savingsAccountAssembler.setHelpers(toSavingsAccount);
|
this.savingsAccountAssembler.setHelpers(toSavingsAccount);
|
||||||
}
|
}
|
||||||
final LoanTransaction loanRefundTransaction = this.loanAccountDomainService.makeRefund(accountTransferDTO.getFromAccountId(),
|
LoanTransaction loanTransaction = null;
|
||||||
new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(),
|
if (LoanTransactionType.DISBURSEMENT.getValue().equals(accountTransferDTO.getFromTransferType())) {
|
||||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), null, null);
|
loanTransaction = this.loanAccountDomainService.makeDisburseTransaction(accountTransferDTO.getFromAccountId(),
|
||||||
|
accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
||||||
|
accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(), accountTransferDTO.getTxnExternalId());
|
||||||
|
} else {
|
||||||
|
loanTransaction = this.loanAccountDomainService.makeRefund(accountTransferDTO.getFromAccountId(),
|
||||||
|
new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(),
|
||||||
|
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(),
|
||||||
|
accountTransferDTO.getTxnExternalId());
|
||||||
|
}
|
||||||
|
|
||||||
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount,
|
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount,
|
||||||
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
||||||
accountTransferDTO.getPaymentDetail());
|
accountTransferDTO.getPaymentDetail());
|
||||||
accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(accountTransferDTO, fromLoanAccount,
|
accountTransferDetails = this.accountTransferAssembler.assembleLoanToSavingsTransfer(accountTransferDTO, fromLoanAccount,
|
||||||
toSavingsAccount, deposit, loanRefundTransaction);
|
toSavingsAccount, deposit, loanTransaction);
|
||||||
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
|
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
|
||||||
transferTransactionId = accountTransferDetails.getId();
|
transferTransactionId = accountTransferDetails.getId();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -217,7 +217,7 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst
|
|||||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, data.fromAccountType(),
|
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, data.fromAccountType(),
|
||||||
data.toAccountType(), data.fromAccount().accountId(), data.toAccount().accountId(), data.name()
|
data.toAccountType(), data.fromAccount().accountId(), data.toAccount().accountId(), data.name()
|
||||||
+ " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data
|
+ " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data
|
||||||
.transferType().getValue(), accountTransferDetails);
|
.transferType().getValue(), accountTransferDetails, null, null, null);
|
||||||
transferAmount(sb, accountTransferDTO, data.getId());
|
transferAmount(sb, accountTransferDTO, data.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -83,7 +83,7 @@ public class CollectionSheetWritePlatformServiceJpaRepositoryImpl implements Col
|
|||||||
final Map<String, Object> changes = new HashMap<String, Object>();
|
final Map<String, Object> changes = new HashMap<String, Object>();
|
||||||
final CollectionSheetBulkDisbursalCommand bulkDisbursalCommand = this.bulkDisbursalCommandFromApiJsonDeserializer
|
final CollectionSheetBulkDisbursalCommand bulkDisbursalCommand = this.bulkDisbursalCommandFromApiJsonDeserializer
|
||||||
.commandFromApiJson(command.json());
|
.commandFromApiJson(command.json());
|
||||||
changes.putAll(this.loanWritePlatformService.bulkLoanDisbursal(command, bulkDisbursalCommand));
|
changes.putAll(this.loanWritePlatformService.bulkLoanDisbursal(command, bulkDisbursalCommand, false));
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -96,8 +96,11 @@ public class LoanTransactionsApiResource {
|
|||||||
} else if (is(commandParam, "close")) {
|
} else if (is(commandParam, "close")) {
|
||||||
transactionData = this.loanReadPlatformService.retrieveNewClosureDetails();
|
transactionData = this.loanReadPlatformService.retrieveNewClosureDetails();
|
||||||
} else if (is(commandParam, "disburse")) {
|
} else if (is(commandParam, "disburse")) {
|
||||||
transactionData = this.loanReadPlatformService.retrieveDisbursalTemplate(loanId);
|
transactionData = this.loanReadPlatformService.retrieveDisbursalTemplate(loanId, true);
|
||||||
} else {
|
} else if(is(commandParam, "disburseToSavings")){
|
||||||
|
transactionData = this.loanReadPlatformService.retrieveDisbursalTemplate(loanId, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
@@ -551,6 +551,9 @@ public class LoansApiResource {
|
|||||||
} else if (is(commandParam, "disburse")) {
|
} else if (is(commandParam, "disburse")) {
|
||||||
final CommandWrapper commandRequest = builder.disburseLoanApplication(loanId).build();
|
final CommandWrapper commandRequest = builder.disburseLoanApplication(loanId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
} else if (is(commandParam, "disburseToSavings")) {
|
||||||
|
final CommandWrapper commandRequest = builder.disburseLoanToSavingsApplication(loanId).build();
|
||||||
|
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is(commandParam, "undoapproval")) {
|
if (is(commandParam, "undoapproval")) {
|
||||||
|
|||||||
+103
-105
@@ -94,7 +94,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
|||||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||||
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;
|
||||||
|
|
||||||
@@ -1687,17 +1686,74 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public void disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser, final JsonCommand command,
|
public void disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser, final JsonCommand command,
|
||||||
final ApplicationCurrency currency, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final ApplicationCurrency currency, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final Map<String, Object> actualChanges, final PaymentDetail paymentDetail,
|
final Map<String, Object> actualChanges, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
final List<Holiday> holidays, final WorkingDays workingDays, final boolean allowTransactionsOnHoliday,
|
||||||
final WorkingDays workingDays, final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay) {
|
final boolean allowTransactionsOnNonWorkingDay, final boolean recalculateSchedule) {
|
||||||
|
|
||||||
validateAccountStatus(LoanEvent.LOAN_DISBURSED);
|
|
||||||
Date lastDusburseDate = this.actualDisbursementDate;
|
|
||||||
|
|
||||||
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
||||||
LoanStatus.fromInt(this.loanStatus));
|
LoanStatus.fromInt(this.loanStatus));
|
||||||
boolean isMultiTrancheDisburse = false;
|
|
||||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
|
|
||||||
|
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||||
|
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||||
|
|
||||||
|
this.loanStatus = statusEnum.getValue();
|
||||||
|
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
||||||
|
|
||||||
|
this.disbursedBy = currentUser;
|
||||||
|
updateLoanScheduleDependentDerivedFields();
|
||||||
|
|
||||||
|
actualChanges.put("locale", command.locale());
|
||||||
|
actualChanges.put("dateFormat", command.dateFormat());
|
||||||
|
actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate"));
|
||||||
|
|
||||||
|
// validate if disbursement date is a holiday or a non-working day
|
||||||
|
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
||||||
|
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
||||||
|
|
||||||
|
handleDisbursementTransaction(actualDisbursementDate);
|
||||||
|
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||||
|
boolean isEmiAmountChanged = false;
|
||||||
|
if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
||||||
|
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
||||||
|
actualDisbursementDate.toDate(), emiAmount, this);
|
||||||
|
this.loanTermVariations.add(loanVariationTerms);
|
||||||
|
isEmiAmountChanged = true;
|
||||||
|
}
|
||||||
|
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
|
||||||
|
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||||
|
workingDays);
|
||||||
|
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||||
|
} else {
|
||||||
|
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||||
|
updateLoanSummaryDerivedFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an interest applied transaction of the interest is accrued
|
||||||
|
* upfront (Up front accrual)
|
||||||
|
**/
|
||||||
|
|
||||||
|
if (isUpfrontAccrualAccountingEnabledOnLoanProduct()) {
|
||||||
|
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
|
||||||
|
actualDisbursementDate);
|
||||||
|
this.loanTransactions.add(interestAppliedTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// changedTransactionDetail =
|
||||||
|
// reprocessTransactionForDisbursement(changedTransactionDetail);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canDisburse(final LocalDate actualDisbursementDate) {
|
||||||
|
Date lastDusburseDate = this.actualDisbursementDate;
|
||||||
|
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
||||||
|
LoanStatus.fromInt(this.loanStatus));
|
||||||
|
|
||||||
|
boolean isMultiTrancheDisburse = false;
|
||||||
if (LoanStatus.fromInt(this.loanStatus).isActive() && isAllTranchesNotDisbursed()) {
|
if (LoanStatus.fromInt(this.loanStatus).isActive() && isAllTranchesNotDisbursed()) {
|
||||||
LoanDisbursementDetails details = fetchLastDisburseDetail();
|
LoanDisbursementDetails details = fetchLastDisburseDetail();
|
||||||
|
|
||||||
@@ -1711,97 +1767,46 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
isMultiTrancheDisburse = true;
|
isMultiTrancheDisburse = true;
|
||||||
}
|
}
|
||||||
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus)) || isMultiTrancheDisburse) {
|
return (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus)) || isMultiTrancheDisburse);
|
||||||
|
}
|
||||||
|
|
||||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
public Money adjustDisburseAmount(final JsonCommand command, final LocalDate actualDisbursementDate) {
|
||||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
Money disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal().zero();
|
||||||
|
BigDecimal principalDisbursed = command.bigDecimalValueOfParameterNamed(LoanApiConstants.principalDisbursedParameterName);
|
||||||
this.loanStatus = statusEnum.getValue();
|
if (this.actualDisbursementDate == null) {
|
||||||
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
|
this.actualDisbursementDate = actualDisbursementDate.toDate();
|
||||||
|
|
||||||
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
|
||||||
Money disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal().zero();
|
|
||||||
BigDecimal principalDisbursed = command.bigDecimalValueOfParameterNamed(LoanApiConstants.principalDisbursedParameterName);
|
|
||||||
if (this.actualDisbursementDate == null) {
|
|
||||||
this.actualDisbursementDate = actualDisbursementDate.toDate();
|
|
||||||
}
|
|
||||||
BigDecimal diff = BigDecimal.ZERO;
|
|
||||||
Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail();
|
|
||||||
if (principalDisbursed == null) {
|
|
||||||
disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal();
|
|
||||||
if (!details.isEmpty()) {
|
|
||||||
disburseAmount = disburseAmount.zero();
|
|
||||||
for (LoanDisbursementDetails disbursementDetails : details) {
|
|
||||||
disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate());
|
|
||||||
disburseAmount = disburseAmount.plus(disbursementDetails.principal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
disburseAmount = disburseAmount.plus(principalDisbursed);
|
|
||||||
|
|
||||||
if (details.isEmpty()) {
|
|
||||||
diff = this.loanRepaymentScheduleDetail.getPrincipal().minus(principalDisbursed).getAmount();
|
|
||||||
} else {
|
|
||||||
for (LoanDisbursementDetails disbursementDetails : details) {
|
|
||||||
disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate());
|
|
||||||
diff = diff.add(disbursementDetails.principal().subtract(principalDisbursed));
|
|
||||||
disbursementDetails.updatePrincipal(principalDisbursed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.loanRepaymentScheduleDetail.setPrincipal(this.loanRepaymentScheduleDetail.getPrincipal().minus(diff).getAmount());
|
|
||||||
if (diff.compareTo(BigDecimal.ZERO) == -1) {
|
|
||||||
final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved amount ";
|
|
||||||
throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.amount", principalDisbursed,
|
|
||||||
this.loanRepaymentScheduleDetail.getPrincipal().getAmount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.disbursedBy = currentUser;
|
|
||||||
updateLoanScheduleDependentDerivedFields();
|
|
||||||
|
|
||||||
actualChanges.put("locale", command.locale());
|
|
||||||
actualChanges.put("dateFormat", command.dateFormat());
|
|
||||||
actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate"));
|
|
||||||
|
|
||||||
// validate if disbursement date is a holiday or a non-working day
|
|
||||||
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
|
||||||
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
|
||||||
|
|
||||||
handleDisbursementTransaction(paymentDetail, actualDisbursementDate, txnExternalId, disburseAmount);
|
|
||||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
|
||||||
boolean isEmiAmountChanged = false;
|
|
||||||
if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
|
||||||
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
|
||||||
actualDisbursementDate.toDate(), emiAmount, this);
|
|
||||||
this.loanTermVariations.add(loanVariationTerms);
|
|
||||||
isEmiAmountChanged = true;
|
|
||||||
}
|
|
||||||
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || diff.compareTo(BigDecimal.ZERO) != 0
|
|
||||||
|| isEmiAmountChanged) {
|
|
||||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
|
||||||
holidays, workingDays);
|
|
||||||
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
|
||||||
} else {
|
|
||||||
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
|
||||||
updateLoanSummaryDerivedFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an interest applied transaction of the interest is accrued
|
|
||||||
* upfront (Up front accrual)
|
|
||||||
**/
|
|
||||||
|
|
||||||
if (isUpfrontAccrualAccountingEnabledOnLoanProduct()) {
|
|
||||||
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
|
|
||||||
actualDisbursementDate);
|
|
||||||
this.loanTransactions.add(interestAppliedTransaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// changedTransactionDetail =
|
|
||||||
// reprocessTransactionForDisbursement(changedTransactionDetail);
|
|
||||||
}
|
}
|
||||||
|
BigDecimal diff = BigDecimal.ZERO;
|
||||||
|
Collection<LoanDisbursementDetails> details = fetchUndisbursedDetail();
|
||||||
|
if (principalDisbursed == null) {
|
||||||
|
disburseAmount = this.loanRepaymentScheduleDetail.getPrincipal();
|
||||||
|
if (!details.isEmpty()) {
|
||||||
|
disburseAmount = disburseAmount.zero();
|
||||||
|
for (LoanDisbursementDetails disbursementDetails : details) {
|
||||||
|
disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate());
|
||||||
|
disburseAmount = disburseAmount.plus(disbursementDetails.principal());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
disburseAmount = disburseAmount.plus(principalDisbursed);
|
||||||
|
|
||||||
|
if (details.isEmpty()) {
|
||||||
|
diff = this.loanRepaymentScheduleDetail.getPrincipal().minus(principalDisbursed).getAmount();
|
||||||
|
} else {
|
||||||
|
for (LoanDisbursementDetails disbursementDetails : details) {
|
||||||
|
disbursementDetails.updateActualDisbursementDate(actualDisbursementDate.toDate());
|
||||||
|
diff = diff.add(disbursementDetails.principal().subtract(principalDisbursed));
|
||||||
|
disbursementDetails.updatePrincipal(principalDisbursed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.loanRepaymentScheduleDetail.setPrincipal(this.loanRepaymentScheduleDetail.getPrincipal().minus(diff).getAmount());
|
||||||
|
if (diff.compareTo(BigDecimal.ZERO) == -1) {
|
||||||
|
final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved amount ";
|
||||||
|
throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.amount", principalDisbursed,
|
||||||
|
this.loanRepaymentScheduleDetail.getPrincipal().getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return disburseAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail reprocessTransactionForDisbursement() {
|
public ChangedTransactionDetail reprocessTransactionForDisbursement() {
|
||||||
@@ -1961,13 +1966,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
updateLoanSchedule(loanSchedule);
|
updateLoanSchedule(loanSchedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn,
|
private void handleDisbursementTransaction(final LocalDate disbursedOn) {
|
||||||
final String txnExternalId, Money disburseAmt) {
|
|
||||||
// track disbursement transaction
|
|
||||||
final LoanTransaction disbursementTransaction = LoanTransaction.disbursement(getOffice(), disburseAmt, paymentDetail, disbursedOn,
|
|
||||||
txnExternalId);
|
|
||||||
disbursementTransaction.updateLoan(this);
|
|
||||||
this.loanTransactions.add(disbursementTransaction);
|
|
||||||
|
|
||||||
// add repayment transaction to track incoming money from client to mfi
|
// add repayment transaction to track incoming money from client to mfi
|
||||||
// for (charges due at time of disbursement)
|
// for (charges due at time of disbursement)
|
||||||
@@ -2036,7 +2035,6 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.a.future.date", errorMessage, disbursedOn);
|
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.a.future.date", errorMessage, disbursedOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return disbursementTransaction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanTransaction handlePayDisbursementTransaction(final Long chargeId, final LoanTransaction chargesPayment,
|
public LoanTransaction handlePayDisbursementTransaction(final Long chargeId, final LoanTransaction chargesPayment,
|
||||||
@@ -3574,7 +3572,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return loanChargePerInstallments;
|
return loanChargePerInstallments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateAccountStatus(final LoanEvent event) {
|
public void validateAccountStatus(final LoanEvent event) {
|
||||||
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
|
||||||
|
|||||||
+3
@@ -23,4 +23,7 @@ public interface LoanAccountDomainService {
|
|||||||
|
|
||||||
LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount,
|
LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount,
|
||||||
PaymentDetail paymentDetail, String noteText, String txnExternalId, Integer transactionType, Integer installmentNumber);
|
PaymentDetail paymentDetail, String noteText, String txnExternalId, Integer transactionType, Integer installmentNumber);
|
||||||
|
|
||||||
|
LoanTransaction makeDisburseTransaction(Long loanId, LocalDate transactionDate, BigDecimal transactionAmount,
|
||||||
|
PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
||||||
}
|
}
|
||||||
|
|||||||
+39
@@ -270,6 +270,45 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
return newRefundTransaction;
|
return newRefundTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public LoanTransaction makeDisburseTransaction(final Long loanId, final LocalDate transactionDate, final BigDecimal transactionAmount,
|
||||||
|
final PaymentDetail paymentDetail, final String noteText, final String txnExternalId) {
|
||||||
|
final Loan loan = this.loanAccountAssembler.assembleFrom(loanId);
|
||||||
|
checkClientOrGroupActive(loan);
|
||||||
|
|
||||||
|
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||||
|
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||||
|
final Money amount = Money.of(loan.getCurrency(), transactionAmount);
|
||||||
|
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amount, paymentDetail, transactionDate,
|
||||||
|
txnExternalId);
|
||||||
|
disbursementTransaction.updateLoan(loan);
|
||||||
|
loan.getLoanTransactions().add(disbursementTransaction);
|
||||||
|
try {
|
||||||
|
this.loanTransactionRepository.save(disbursementTransaction);
|
||||||
|
} 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(disbursementTransaction.getExternalId())
|
||||||
|
.failWithCode("value.must.be.unique");
|
||||||
|
}
|
||||||
|
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||||
|
"Validation errors exist.", dataValidationErrors); }
|
||||||
|
}
|
||||||
|
this.loanRepository.saveAndFlush(loan);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(noteText)) {
|
||||||
|
final Note note = Note.loanTransactionNote(loan, disbursementTransaction, noteText);
|
||||||
|
this.noteRepository.save(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
|
return disbursementTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reverseTransfer(final LoanTransaction loanTransaction) {
|
public void reverseTransfer(final LoanTransaction loanTransaction) {
|
||||||
loanTransaction.reverse();
|
loanTransaction.reverse();
|
||||||
|
|||||||
+1
-1
@@ -27,6 +27,6 @@ public class DisburseLoanCommandHandler implements NewCommandSourceHandler {
|
|||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
return this.writePlatformService.disburseLoan(command.entityId(), command);
|
return this.writePlatformService.disburseLoan(command.entityId(), command, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DisburseLoanToSavingsCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final LoanWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public DisburseLoanToSavingsCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
|
return this.writePlatformService.disburseLoan(command.entityId(), command, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -205,7 +205,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
|
|
||||||
final Integer graceOnArrearsAgeing = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
|
final Integer graceOnArrearsAgeing = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
|
||||||
LoanProductConstants.graceOnArrearsAgeingParameterName, element);
|
LoanProductConstants.graceOnArrearsAgeingParameterName, element);
|
||||||
baseDataValidator.reset().parameter(LoanProductConstants.graceOnArrearsAgeingParameterName).value(graceOnArrearsAgeing).zeroOrPositiveAmount();
|
baseDataValidator.reset().parameter(LoanProductConstants.graceOnArrearsAgeingParameterName).value(graceOnArrearsAgeing)
|
||||||
|
.zeroOrPositiveAmount();
|
||||||
|
|
||||||
final String interestChargedFromDateParameterName = "interestChargedFromDate";
|
final String interestChargedFromDateParameterName = "interestChargedFromDate";
|
||||||
if (this.fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
|
if (this.fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
|
||||||
@@ -814,7 +815,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
|
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
|
||||||
.failWithCode(LoanApiConstants.DISBURSEMENT_DATE_START_WITH_ERROR);
|
.failWithCode(LoanApiConstants.DISBURSEMENT_DATE_START_WITH_ERROR);
|
||||||
}
|
}
|
||||||
if (!tatalDisbursement.equals(totalPrincipal)) {
|
if (tatalDisbursement.compareTo(totalPrincipal) != 0) {
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementPrincipalParameterName)
|
baseDataValidator.reset().parameter(LoanApiConstants.disbursementPrincipalParameterName)
|
||||||
.failWithCode(LoanApiConstants.PRINCIPAL_AMOUNT_SHOULD_BE_SAME);
|
.failWithCode(LoanApiConstants.PRINCIPAL_AMOUNT_SHOULD_BE_SAME);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -48,13 +48,20 @@ public final class LoanEventApiJsonValidator {
|
|||||||
"Validation errors exist.", dataValidationErrors); }
|
"Validation errors exist.", dataValidationErrors); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateDisbursement(final String json) {
|
public void validateDisbursement(final String json, boolean isAccountTransfer) {
|
||||||
|
|
||||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
|
|
||||||
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "externalId", "note",
|
Set<String> disbursementParameters = null;
|
||||||
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
|
|
||||||
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
|
if (isAccountTransfer) {
|
||||||
|
disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale",
|
||||||
|
"dateFormat", LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
|
||||||
|
} else {
|
||||||
|
disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale",
|
||||||
|
"dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
|
||||||
|
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
|
||||||
|
}
|
||||||
|
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ public interface LoanReadPlatformService {
|
|||||||
|
|
||||||
LoanTransactionData retrieveNewClosureDetails();
|
LoanTransactionData retrieveNewClosureDetails();
|
||||||
|
|
||||||
LoanTransactionData retrieveDisbursalTemplate(Long loanId);
|
LoanTransactionData retrieveDisbursalTemplate(Long loanId, boolean paymentDetailsRequired);
|
||||||
|
|
||||||
LoanAccountData retrieveTemplateWithCompleteGroupAndProductDetails(Long groupId, Long productId);
|
LoanAccountData retrieveTemplateWithCompleteGroupAndProductDetails(Long groupId, Long productId);
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -388,12 +388,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanTransactionData retrieveDisbursalTemplate(final Long loanId) {
|
public LoanTransactionData retrieveDisbursalTemplate(final Long loanId, boolean paymentDetailsRequired) {
|
||||||
final Loan loan = this.loanRepository.findOne(loanId);
|
final Loan loan = this.loanRepository.findOne(loanId);
|
||||||
if (loan == null) { throw new LoanNotFoundException(loanId); }
|
if (loan == null) { throw new LoanNotFoundException(loanId); }
|
||||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.DISBURSEMENT);
|
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.DISBURSEMENT);
|
||||||
final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService
|
Collection<CodeValueData> paymentOptions = null;
|
||||||
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
|
if (paymentDetailsRequired) {
|
||||||
|
paymentOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
|
||||||
|
}
|
||||||
return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDateForTemplate(),
|
return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDateForTemplate(),
|
||||||
loan.getDisburseAmountForTemplate(), null, null, null, null, null, paymentOptions, null, null, loan.retriveLastEmiAmount());
|
loan.getDisburseAmountForTemplate(), null, null, null, null, null, paymentOptions, null, null, loan.retriveLastEmiAmount());
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -22,9 +22,9 @@ import java.util.Map;
|
|||||||
|
|
||||||
public interface LoanWritePlatformService {
|
public interface LoanWritePlatformService {
|
||||||
|
|
||||||
CommandProcessingResult disburseLoan(Long loanId, JsonCommand command);
|
CommandProcessingResult disburseLoan(Long loanId, JsonCommand command, Boolean isAccountTransfer);
|
||||||
|
|
||||||
Map<String, Object> bulkLoanDisbursal(JsonCommand command, CollectionSheetBulkDisbursalCommand bulkDisbursalCommand);
|
Map<String, Object> bulkLoanDisbursal(JsonCommand command, CollectionSheetBulkDisbursalCommand bulkDisbursalCommand, Boolean isAccountTransfer);
|
||||||
|
|
||||||
CommandProcessingResult undoLoanDisbursal(Long loanId, JsonCommand command);
|
CommandProcessingResult undoLoanDisbursal(Long loanId, JsonCommand command);
|
||||||
|
|
||||||
|
|||||||
+137
-17
@@ -92,6 +92,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanEvent;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanOverdueInstallmentCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanOverdueInstallmentCharge;
|
||||||
@@ -217,11 +218,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command) {
|
public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command, Boolean isAccountTransfer) {
|
||||||
|
|
||||||
final AppUser currentUser = this.context.authenticatedUser();
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
|
||||||
this.loanEventApiJsonValidator.validateDisbursement(command.json());
|
this.loanEventApiJsonValidator.validateDisbursement(command.json(), isAccountTransfer);
|
||||||
|
|
||||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
checkClientOrGroupActive(loan);
|
checkClientOrGroupActive(loan);
|
||||||
@@ -259,11 +260,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||||
updateLoanCounters(loan, actualDisbursementDate);
|
updateLoanCounters(loan, actualDisbursementDate);
|
||||||
|
Money amountBeforeAdjust = loan.getPrincpal();
|
||||||
|
loan.validateAccountStatus(LoanEvent.LOAN_DISBURSED);
|
||||||
|
boolean canDisburse = loan.canDisburse(actualDisbursementDate);
|
||||||
|
if (canDisburse) {
|
||||||
|
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
|
||||||
|
boolean recalcualteSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
|
||||||
|
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||||
|
if (isAccountTransfer) {
|
||||||
|
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail);
|
||||||
|
} else {
|
||||||
|
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), disburseAmount, paymentDetail,
|
||||||
|
actualDisbursementDate, txnExternalId);
|
||||||
|
disbursementTransaction.updateLoan(loan);
|
||||||
|
loan.getLoanTransactions().add(disbursementTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds,
|
loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds,
|
||||||
existingReversedTransactionIds, changes, paymentDetail, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
existingReversedTransactionIds, changes, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays,
|
||||||
workingDays, allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay);
|
allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay, recalcualteSchedule);
|
||||||
|
}
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
|
||||||
@@ -306,7 +322,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
|
||||||
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loanId, "Loan Charge Payment",
|
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loanId, "Loan Charge Payment",
|
||||||
locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null,
|
locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null,
|
||||||
AccountTransferType.CHARGE_PAYMENT.getValue(), null);
|
AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null);
|
||||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +387,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
*****/
|
*****/
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> bulkLoanDisbursal(final JsonCommand command, final CollectionSheetBulkDisbursalCommand bulkDisbursalCommand) {
|
public Map<String, Object> bulkLoanDisbursal(final JsonCommand command, final CollectionSheetBulkDisbursalCommand bulkDisbursalCommand,
|
||||||
|
Boolean isAccountTransfer) {
|
||||||
final AppUser currentUser = this.context.authenticatedUser();
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
|
||||||
final SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions();
|
final SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions();
|
||||||
@@ -405,12 +422,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||||
|
loan.validateAccountStatus(LoanEvent.LOAN_DISBURSED);
|
||||||
updateLoanCounters(loan, actualDisbursementDate);
|
updateLoanCounters(loan, actualDisbursementDate);
|
||||||
|
boolean canDisburse = loan.canDisburse(actualDisbursementDate);
|
||||||
loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds,
|
if (canDisburse) {
|
||||||
existingReversedTransactionIds, changes, paymentDetail, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays,
|
Money amountBeforeAdjust = loan.getPrincpal();
|
||||||
allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay);
|
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
|
||||||
|
boolean recalcualteSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
|
||||||
|
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||||
|
if (isAccountTransfer) {
|
||||||
|
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail);
|
||||||
|
} else {
|
||||||
|
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), disburseAmount, paymentDetail,
|
||||||
|
actualDisbursementDate, txnExternalId);
|
||||||
|
disbursementTransaction.updateLoan(loan);
|
||||||
|
loan.getLoanTransactions().add(disbursementTransaction);
|
||||||
|
}
|
||||||
|
loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds,
|
||||||
|
existingReversedTransactionIds, changes, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays,
|
||||||
|
allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay, recalcualteSchedule);
|
||||||
|
}
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
this.loanRepository.save(loan);
|
this.loanRepository.save(loan);
|
||||||
@@ -463,7 +494,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
|
||||||
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(),
|
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(),
|
||||||
"Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(),
|
"Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(),
|
||||||
entrySet.getKey(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null);
|
entrySet.getKey(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null);
|
||||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1222,7 +1253,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS,
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS,
|
||||||
PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null,
|
PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null,
|
||||||
LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber,
|
LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber,
|
||||||
AccountTransferType.CHARGE_PAYMENT.getValue(), null);
|
AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null);
|
||||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
return new CommandProcessingResultBuilder() //
|
||||||
@@ -1235,6 +1266,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
.withSavingsId(portfolioAccountData.accountId()).build();
|
.withSavingsId(portfolioAccountData.accountId()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {
|
||||||
|
|
||||||
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
|
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||||
|
|
||||||
|
final Locale locale = command.extractLocale();
|
||||||
|
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||||
|
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan.getId());
|
||||||
|
if (portfolioAccountData == null) {
|
||||||
|
final String errorMessage = "Disburse Loan with id:" + loan.getId() + " requires linked savings account for payment";
|
||||||
|
throw new LinkedAccountRequiredException("loan.disburse.to.savings", errorMessage, loan.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount.getAmount(),
|
||||||
|
PortfolioAccountType.LOAN, PortfolioAccountType.SAVINGS, loan.getId(), portfolioAccountData.accountId(),
|
||||||
|
"Loan Disbursement", locale, fmt, paymentDetail, LoanTransactionType.DISBURSEMENT.getValue(), null, null, null,
|
||||||
|
AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, txnExternalId, loan);
|
||||||
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@CronTarget(jobName = JobName.TRANSFER_FEE_CHARGE_FOR_LOANS)
|
@CronTarget(jobName = JobName.TRANSFER_FEE_CHARGE_FOR_LOANS)
|
||||||
public void transferFeeCharges() throws JobExecutionException {
|
public void transferFeeCharges() throws JobExecutionException {
|
||||||
@@ -1257,7 +1309,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
|
installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
|
||||||
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null,
|
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null,
|
||||||
null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(),
|
null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(),
|
||||||
installmentChargeData.getInstallmentNumber(), AccountTransferType.CHARGE_PAYMENT.getValue(), null);
|
installmentChargeData.getInstallmentNumber(), AccountTransferType.CHARGE_PAYMENT.getValue(), null,
|
||||||
|
null, null, null);
|
||||||
transferFeeCharge(sb, accountTransferDTO);
|
transferFeeCharge(sb, accountTransferDTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1268,7 +1321,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
chargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
|
chargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
|
||||||
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null,
|
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null,
|
||||||
LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), null,
|
LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), null,
|
||||||
AccountTransferType.CHARGE_PAYMENT.getValue(), null);
|
AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null);
|
||||||
transferFeeCharge(sb, accountTransferDTO);
|
transferFeeCharge(sb, accountTransferDTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1919,6 +1972,73 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanTransaction disburseLoanAmountToSavings(final Long loanId, Long loanChargeId, final JsonCommand command,
|
||||||
|
final boolean isChargeIdIncludedInJson) {
|
||||||
|
|
||||||
|
LoanTransaction transaction = null;
|
||||||
|
|
||||||
|
this.loanEventApiJsonValidator.validateChargePaymentTransaction(command.json(), isChargeIdIncludedInJson);
|
||||||
|
if (isChargeIdIncludedInJson) {
|
||||||
|
loanChargeId = command.longValueOfParameterNamed("chargeId");
|
||||||
|
}
|
||||||
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
|
checkClientOrGroupActive(loan);
|
||||||
|
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
||||||
|
|
||||||
|
// Charges may be waived only when the loan associated with them are
|
||||||
|
// active
|
||||||
|
if (!loan.status().isActive()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.LOAN_INACTIVE,
|
||||||
|
loanCharge.getId()); }
|
||||||
|
|
||||||
|
// validate loan charge is not already paid or waived
|
||||||
|
if (loanCharge.isWaived()) {
|
||||||
|
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||||
|
} else if (loanCharge.isPaid()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID,
|
||||||
|
loanCharge.getId()); }
|
||||||
|
|
||||||
|
if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException(
|
||||||
|
LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_ACCOUNT_TRANSFER, loanCharge.getId()); }
|
||||||
|
|
||||||
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
|
|
||||||
|
final Locale locale = command.extractLocale();
|
||||||
|
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||||
|
Integer loanInstallmentNumber = null;
|
||||||
|
BigDecimal amount = loanCharge.amountOutstanding();
|
||||||
|
if (loanCharge.isInstalmentFee()) {
|
||||||
|
LoanInstallmentCharge chargePerInstallment = null;
|
||||||
|
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
||||||
|
final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber");
|
||||||
|
if (dueDate != null) {
|
||||||
|
chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate);
|
||||||
|
} else if (installmentNumber != null) {
|
||||||
|
chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber);
|
||||||
|
}
|
||||||
|
if (chargePerInstallment == null) {
|
||||||
|
chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
|
||||||
|
}
|
||||||
|
if (chargePerInstallment.isWaived()) {
|
||||||
|
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||||
|
} else if (chargePerInstallment.isPaid()) { throw new LoanChargeCannotBePayedException(
|
||||||
|
LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId()); }
|
||||||
|
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
|
||||||
|
amount = chargePerInstallment.getAmountOutstanding();
|
||||||
|
}
|
||||||
|
|
||||||
|
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||||
|
if (portfolioAccountData == null) {
|
||||||
|
final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment";
|
||||||
|
throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId);
|
||||||
|
}
|
||||||
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount, PortfolioAccountType.SAVINGS,
|
||||||
|
PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment", locale, fmt, null, null,
|
||||||
|
LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber,
|
||||||
|
AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null);
|
||||||
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
|
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
private void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||||
final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId);
|
final AccountTransferTransaction transferTransaction = this.accountTransferRepository.findByToLoanTransactionId(loanTransactionId);
|
||||||
if (transferTransaction != null) {
|
if (transferTransaction != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user