Merge pull request #1015 from pramodn02/develop
MIFOSX-1416,MIFOSX-1417 : Periodic accrual changes
This commit is contained in:
+73
-1
@@ -1418,7 +1418,14 @@
|
|||||||
<td><a href="#financialactivityaccounts_delete">Delete a Financial Activity to Account Mapping</a></td>
|
<td><a href="#financialactivityaccounts_delete">Delete a Financial Activity to Account Mapping</a></td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="#periodicaccrualaccounting">Periodic Accrual Accounting</a></td>
|
||||||
|
<td>accrualaccounting</td>
|
||||||
|
<td><a href="#periodicaccrualaccounting_run">Execute Periodic Accrual Accounting</a><br> </td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -29493,6 +29500,71 @@ Content-Type: application/json
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- start Periodic accrual API doc-->
|
||||||
|
|
||||||
|
<a id="periodicaccrualaccounting" name="periodicaccrualaccounting" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Periodic Accrual Accounting</h3>
|
||||||
|
<p>
|
||||||
|
Periodic Accrual is to accrue the loan income till the specific date or till batch job scheduled time.
|
||||||
|
</p>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Field Descriptions</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>accrueTill</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>which specifies periodic accruals should happen till the given Date
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a id="periodicaccrualaccounting_run" name="periodicaccrualaccounting_run" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Executes Periodic Accrual Accounting</h4>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>accrueTill
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/accrualaccounting
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST financialactivityaccounts
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
"locale": "en",
|
||||||
|
"dateFormat": "dd MMMM yyyy",
|
||||||
|
"accrueTill":"04 June 2014"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- end Periodic accrual API doc-->
|
||||||
|
|
||||||
<!-- start of Search API doc -->
|
<!-- start of Search API doc -->
|
||||||
<a id="search" name="search" class="old-syle-anchor"> </a>
|
<a id="search" name="search" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `scheduler_group`) VALUES ('Add Periodic Accrual Transactions', 'Add Periodic Accrual Transactions', '0 2 0 1/1 * ? *', now(), 4, 3);
|
||||||
|
|
||||||
|
UPDATE `job` SET `scheduler_group`=3 WHERE `name`='Add Accrual Transactions';
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('accounting', 'EXECUTE_PERIODICACCRUALACCOUNTING', 'PERIODICACCRUALACCOUNTING', 'EXECUTE', 0);
|
||||||
+155
@@ -8,6 +8,7 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
@@ -21,6 +22,7 @@ import org.mifosplatform.integrationtests.common.accounting.Account;
|
|||||||
import org.mifosplatform.integrationtests.common.accounting.AccountHelper;
|
import org.mifosplatform.integrationtests.common.accounting.AccountHelper;
|
||||||
import org.mifosplatform.integrationtests.common.accounting.JournalEntry;
|
import org.mifosplatform.integrationtests.common.accounting.JournalEntry;
|
||||||
import org.mifosplatform.integrationtests.common.accounting.JournalEntryHelper;
|
import org.mifosplatform.integrationtests.common.accounting.JournalEntryHelper;
|
||||||
|
import org.mifosplatform.integrationtests.common.accounting.PeriodicAccrualAccountingHelper;
|
||||||
import org.mifosplatform.integrationtests.common.charges.ChargesHelper;
|
import org.mifosplatform.integrationtests.common.charges.ChargesHelper;
|
||||||
import org.mifosplatform.integrationtests.common.fixeddeposit.FixedDepositAccountHelper;
|
import org.mifosplatform.integrationtests.common.fixeddeposit.FixedDepositAccountHelper;
|
||||||
import org.mifosplatform.integrationtests.common.fixeddeposit.FixedDepositAccountStatusChecker;
|
import org.mifosplatform.integrationtests.common.fixeddeposit.FixedDepositAccountStatusChecker;
|
||||||
@@ -83,6 +85,7 @@ public class AccountingScenarioIntegrationTest {
|
|||||||
private RecurringDepositProductHelper recurringDepositProductHelper;
|
private RecurringDepositProductHelper recurringDepositProductHelper;
|
||||||
private RecurringDepositAccountHelper recurringDepositAccountHelper;
|
private RecurringDepositAccountHelper recurringDepositAccountHelper;
|
||||||
private SchedulerJobHelper schedulerJobHelper;
|
private SchedulerJobHelper schedulerJobHelper;
|
||||||
|
private PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
@@ -95,6 +98,7 @@ public class AccountingScenarioIntegrationTest {
|
|||||||
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||||
this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec);
|
this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec);
|
||||||
this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec, this.responseSpec);
|
this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec, this.responseSpec);
|
||||||
|
this.periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(this.requestSpec, this.responseSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -645,6 +649,156 @@ public class AccountingScenarioIntegrationTest {
|
|||||||
System.out.println("Repayment 5 Done ......");
|
System.out.println("Repayment 5 Done ......");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkPeriodicAccrualAccountingTillCurrentDateFlow() {
|
||||||
|
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||||
|
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||||
|
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||||
|
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||||
|
|
||||||
|
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccountingEnabled(assetAccount, incomeAccount, expenseAccount,
|
||||||
|
overpaymentAccount);
|
||||||
|
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, this.DATE_OF_JOINING);
|
||||||
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID);
|
||||||
|
|
||||||
|
final float FEE_PORTION = 50.0f;
|
||||||
|
final float PENALTY_PORTION = 100.0f;
|
||||||
|
Integer flat = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||||
|
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, String.valueOf(FEE_PORTION), false));
|
||||||
|
Integer flatSpecifiedDueDate = ChargesHelper.createCharges(requestSpec, responseSpec, ChargesHelper.getLoanSpecifiedDueDateJSON(
|
||||||
|
ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, String.valueOf(PENALTY_PORTION), true));
|
||||||
|
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoan(this.EXPECTED_DISBURSAL_DATE, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
|
||||||
|
|
||||||
|
Calendar todayDate = Calendar.getInstance();
|
||||||
|
final String currentDate = dateFormat.format(todayDate.getTime());
|
||||||
|
|
||||||
|
todayDate.add(Calendar.DATE, -4);
|
||||||
|
|
||||||
|
final String LOAN_DISBURSEMENT_DATE = dateFormat.format(todayDate.getTime());
|
||||||
|
|
||||||
|
todayDate.add(Calendar.MONTH, 2);
|
||||||
|
final String FIRST_REPAYMENT_DATE = dateFormat.format(todayDate.getTime());
|
||||||
|
|
||||||
|
todayDate = Calendar.getInstance();
|
||||||
|
todayDate.add(Calendar.DATE, -2);
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoan(LOAN_DISBURSEMENT_DATE, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
this.loanTransactionHelper.addChargesForLoan(
|
||||||
|
loanID,
|
||||||
|
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatSpecifiedDueDate),
|
||||||
|
dateFormat.format(todayDate.getTime()), String.valueOf(PENALTY_PORTION)));
|
||||||
|
todayDate.add(Calendar.DATE, 1);
|
||||||
|
this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(
|
||||||
|
String.valueOf(flat), dateFormat.format(todayDate.getTime()), String.valueOf(FEE_PORTION)));
|
||||||
|
|
||||||
|
// CHECK ACCOUNT ENTRIES
|
||||||
|
System.out.println("Entries ......");
|
||||||
|
final float PRINCIPAL_VALUE_FOR_EACH_PERIOD = 2000.0f;
|
||||||
|
final float TOTAL_INTEREST = 1000.0f;
|
||||||
|
final JournalEntry[] assetAccountInitialEntry = { new JournalEntry(this.LP_PRINCIPAL, JournalEntry.TransactionType.CREDIT),
|
||||||
|
new JournalEntry(this.LP_PRINCIPAL, JournalEntry.TransactionType.DEBIT), };
|
||||||
|
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, LOAN_DISBURSEMENT_DATE, assetAccountInitialEntry);
|
||||||
|
|
||||||
|
final String jobName = "Add Periodic Accrual Transactions";
|
||||||
|
try {
|
||||||
|
this.schedulerJobHelper.excuteJob(jobName);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAKE 1
|
||||||
|
final float FIRST_INTEREST = 200.0f;
|
||||||
|
final float INTEREST_4_DAYS = 13.33F;
|
||||||
|
this.loanTransactionHelper.checkAccrualTransactionForRepayment(getDateAsLocalDate(currentDate), INTEREST_4_DAYS, FEE_PORTION,
|
||||||
|
PENALTY_PORTION, loanID);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkPeriodicAccrualAccountingAPIFlow() {
|
||||||
|
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||||
|
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||||
|
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||||
|
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||||
|
|
||||||
|
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccountingEnabled(assetAccount, incomeAccount, expenseAccount,
|
||||||
|
overpaymentAccount);
|
||||||
|
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, this.DATE_OF_JOINING);
|
||||||
|
final Integer loanID = applyForLoanApplication(clientID, loanProductID);
|
||||||
|
|
||||||
|
final float FEE_PORTION = 50.0f;
|
||||||
|
final float PENALTY_PORTION = 100.0f;
|
||||||
|
Integer flat = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||||
|
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, String.valueOf(FEE_PORTION), false));
|
||||||
|
Integer flatSpecifiedDueDate = ChargesHelper.createCharges(requestSpec, responseSpec, ChargesHelper.getLoanSpecifiedDueDateJSON(
|
||||||
|
ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, String.valueOf(PENALTY_PORTION), true));
|
||||||
|
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoan(this.EXPECTED_DISBURSAL_DATE, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
|
||||||
|
|
||||||
|
Calendar todayDate = Calendar.getInstance();
|
||||||
|
final String currentDate = dateFormat.format(todayDate.getTime());
|
||||||
|
|
||||||
|
todayDate.add(Calendar.DATE, -4);
|
||||||
|
|
||||||
|
final String LOAN_DISBURSEMENT_DATE = dateFormat.format(todayDate.getTime());
|
||||||
|
|
||||||
|
todayDate.add(Calendar.MONTH, 2);
|
||||||
|
final String FIRST_REPAYMENT_DATE = dateFormat.format(todayDate.getTime());
|
||||||
|
|
||||||
|
todayDate = Calendar.getInstance();
|
||||||
|
todayDate.add(Calendar.DATE, -2);
|
||||||
|
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoan(LOAN_DISBURSEMENT_DATE, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
this.loanTransactionHelper.addChargesForLoan(
|
||||||
|
loanID,
|
||||||
|
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatSpecifiedDueDate),
|
||||||
|
dateFormat.format(todayDate.getTime()), String.valueOf(PENALTY_PORTION)));
|
||||||
|
todayDate.add(Calendar.DATE, 1);
|
||||||
|
String runOndate = dateFormat.format(todayDate.getTime());
|
||||||
|
this.loanTransactionHelper
|
||||||
|
.addChargesForLoan(
|
||||||
|
loanID,
|
||||||
|
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flat), runOndate,
|
||||||
|
String.valueOf(FEE_PORTION)));
|
||||||
|
|
||||||
|
// CHECK ACCOUNT ENTRIES
|
||||||
|
System.out.println("Entries ......");
|
||||||
|
final float PRINCIPAL_VALUE_FOR_EACH_PERIOD = 2000.0f;
|
||||||
|
final float TOTAL_INTEREST = 1000.0f;
|
||||||
|
final JournalEntry[] assetAccountInitialEntry = { new JournalEntry(this.LP_PRINCIPAL, JournalEntry.TransactionType.CREDIT),
|
||||||
|
new JournalEntry(this.LP_PRINCIPAL, JournalEntry.TransactionType.DEBIT), };
|
||||||
|
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, LOAN_DISBURSEMENT_DATE, assetAccountInitialEntry);
|
||||||
|
|
||||||
|
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(runOndate);
|
||||||
|
|
||||||
|
// MAKE 1
|
||||||
|
final float INTEREST_3_DAYS = 10.0F;
|
||||||
|
this.loanTransactionHelper.checkAccrualTransactionForRepayment(getDateAsLocalDate(runOndate), INTEREST_3_DAYS, FEE_PORTION,
|
||||||
|
PENALTY_PORTION, loanID);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Integer createLoanProductWithPeriodicAccrualAccountingEnabled(final Account... accounts) {
|
private Integer createLoanProductWithPeriodicAccrualAccountingEnabled(final Account... accounts) {
|
||||||
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||||
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(this.LP_PRINCIPAL.toString()).withRepaymentTypeAsMonth()
|
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(this.LP_PRINCIPAL.toString()).withRepaymentTypeAsMonth()
|
||||||
@@ -774,4 +928,5 @@ public class AccountingScenarioIntegrationTest {
|
|||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package org.mifosplatform.integrationtests.common.accounting;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.mifosplatform.integrationtests.common.Utils;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.jayway.restassured.specification.RequestSpecification;
|
||||||
|
import com.jayway.restassured.specification.ResponseSpecification;
|
||||||
|
|
||||||
|
public class PeriodicAccrualAccountingHelper {
|
||||||
|
|
||||||
|
private static final String PERIODIC_ACCRUAL_URL = "/mifosng-provider/api/v1/accrualaccounting";
|
||||||
|
private final RequestSpecification requestSpec;
|
||||||
|
private final ResponseSpecification responseSpec;
|
||||||
|
|
||||||
|
public PeriodicAccrualAccountingHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||||
|
this.requestSpec = requestSpec;
|
||||||
|
this.responseSpec = responseSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object runPeriodicAccrualAccounting(String date) {
|
||||||
|
String json = getRunPeriodicAccrual(date);
|
||||||
|
return Utils.performServerPost(this.requestSpec, this.responseSpec, PERIODIC_ACCRUAL_URL + "?" + Utils.TENANT_IDENTIFIER, json, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRunPeriodicAccrual(String date) {
|
||||||
|
final HashMap<String, String> map = new HashMap<>();
|
||||||
|
map.put("dateFormat", "dd MMMM yyyy");
|
||||||
|
map.put("locale", "en_GB");
|
||||||
|
map.put("accrueTill", date);
|
||||||
|
return new Gson().toJson(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.accrual.api;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||||
|
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/accrualaccounting")
|
||||||
|
@Component
|
||||||
|
@Scope("singleton")
|
||||||
|
public class AccrualAccountingApiResource {
|
||||||
|
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
private final DefaultToApiJsonSerializer<String> apiJsonSerializerService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public AccrualAccountingApiResource(final DefaultToApiJsonSerializer<String> apiJsonSerializerService,
|
||||||
|
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||||
|
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||||
|
this.apiJsonSerializerService = apiJsonSerializerService;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String executePeriodicAccrualAccounting(final String jsonRequestBody) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder().excuteAccrualAccounting().withJson(jsonRequestBody).build();
|
||||||
|
|
||||||
|
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
|
||||||
|
return this.apiJsonSerializerService.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package org.mifosplatform.accounting.accrual.api;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class AccrualAccountingConstants {
|
||||||
|
|
||||||
|
public static final String accrueTillParamName = "accrueTill";
|
||||||
|
public static final String localeParamName = "locale";
|
||||||
|
public static final String dateFormatParamName = "dateFormat";
|
||||||
|
|
||||||
|
public static final String PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME = "periodicaccrual";
|
||||||
|
public static final String PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE = "execution.failed";
|
||||||
|
|
||||||
|
public static final Set<String> LOAN_PERIODIC_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(accrueTillParamName,
|
||||||
|
localeParamName, dateFormatParamName));
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.accrual.handler;
|
||||||
|
|
||||||
|
import org.mifosplatform.accounting.accrual.service.AccrualAccountingWritePlatformService;
|
||||||
|
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ExecutePeriodicAccrualCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final AccrualAccountingWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ExecutePeriodicAccrualCommandHandler(final AccrualAccountingWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.executeLoansPeriodicAccrual(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.accrual.serialization;
|
||||||
|
|
||||||
|
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.LOAN_PERIODIC_REQUEST_DATA_PARAMETERS;
|
||||||
|
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME;
|
||||||
|
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.accrueTillParamName;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
|
||||||
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.guarantor.command.GuarantorCommand;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link FromApiJsonDeserializer} for
|
||||||
|
* {@link GuarantorCommand}'s.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public final class AccrualAccountingDataValidator {
|
||||||
|
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public AccrualAccountingDataValidator(final FromJsonHelper fromApiJsonfromApiJsonHelper) {
|
||||||
|
this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateLoanPeriodicAccrualData(final String json) {
|
||||||
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, LOAN_PERIODIC_REQUEST_DATA_PARAMETERS);
|
||||||
|
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||||
|
.resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final LocalDate date = this.fromApiJsonHelper.extractLocalDateNamed(accrueTillParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(accrueTillParamName).value(date).notNull().validateDateBefore(LocalDate.now());
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||||
|
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.accrual.service;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
|
public interface AccrualAccountingWritePlatformService {
|
||||||
|
|
||||||
|
CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command);
|
||||||
|
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 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.accounting.accrual.service;
|
||||||
|
|
||||||
|
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE;
|
||||||
|
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME;
|
||||||
|
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.accrueTillParamName;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
import org.mifosplatform.accounting.accrual.serialization.AccrualAccountingDataValidator;
|
||||||
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.service.LoanAccrualWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AccrualAccountingWritePlatformServiceImpl implements AccrualAccountingWritePlatformService {
|
||||||
|
|
||||||
|
private final LoanAccrualWritePlatformService loanAccrualWritePlatformService;
|
||||||
|
private final AccrualAccountingDataValidator accountingDataValidator;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public AccrualAccountingWritePlatformServiceImpl(final LoanAccrualWritePlatformService loanAccrualWritePlatformService,
|
||||||
|
final AccrualAccountingDataValidator accountingDataValidator) {
|
||||||
|
this.loanAccrualWritePlatformService = loanAccrualWritePlatformService;
|
||||||
|
this.accountingDataValidator = accountingDataValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command) {
|
||||||
|
this.accountingDataValidator.validateLoanPeriodicAccrualData(command.json());
|
||||||
|
LocalDate tilldate = command.localDateValueOfParameterNamed(accrueTillParamName);
|
||||||
|
String errorlog = this.loanAccrualWritePlatformService.addPeriodicAccruals(tilldate);
|
||||||
|
if (errorlog.length() > 0) {
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||||
|
.resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME);
|
||||||
|
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE, errorlog);
|
||||||
|
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||||
|
}
|
||||||
|
return CommandProcessingResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -267,6 +267,14 @@ public class CommandWrapper {
|
|||||||
return this.entityName.equalsIgnoreCase("JOURNALENTRY");
|
return this.entityName.equalsIgnoreCase("JOURNALENTRY");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPeriodicAccrualResource() {
|
||||||
|
return this.entityName.equalsIgnoreCase("PERIODICACCRUALACCOUNTING");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExecute() {
|
||||||
|
return this.actionName.equalsIgnoreCase("EXECUTE");
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRevertJournalEntry() {
|
public boolean isRevertJournalEntry() {
|
||||||
return this.actionName.equalsIgnoreCase("REVERSE") && this.entityName.equalsIgnoreCase("JOURNALENTRY");
|
return this.actionName.equalsIgnoreCase("REVERSE") && this.entityName.equalsIgnoreCase("JOURNALENTRY");
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -809,6 +809,14 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder excuteAccrualAccounting() {
|
||||||
|
this.actionName = "EXECUTE";
|
||||||
|
this.entityName = "PERIODICACCRUALACCOUNTING";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/accrualaccounting";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder createGLAccount() {
|
public CommandWrapperBuilder createGLAccount() {
|
||||||
this.actionName = "CREATE";
|
this.actionName = "CREATE";
|
||||||
this.entityName = "GLACCOUNT";
|
this.entityName = "GLACCOUNT";
|
||||||
|
|||||||
+4
@@ -418,6 +418,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
} else {
|
} else {
|
||||||
throw new UnsupportedCommandException(wrapper.commandName());
|
throw new UnsupportedCommandException(wrapper.commandName());
|
||||||
}
|
}
|
||||||
|
} else if (wrapper.isPeriodicAccrualResource()) {
|
||||||
|
if (wrapper.isExecute()) {
|
||||||
|
handler = this.applicationContext.getBean("executePeriodicAccrualCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
}
|
||||||
} else if (wrapper.isSavingsProductResource()) {
|
} else if (wrapper.isSavingsProductResource()) {
|
||||||
if (wrapper.isCreate()) {
|
if (wrapper.isCreate()) {
|
||||||
handler = this.applicationContext.getBean("createSavingsProductCommandHandler", NewCommandSourceHandler.class);
|
handler = this.applicationContext.getBean("createSavingsProductCommandHandler", NewCommandSourceHandler.class);
|
||||||
|
|||||||
+2
-1
@@ -16,7 +16,8 @@ public enum JobName {
|
|||||||
ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"),
|
ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"),
|
||||||
UPDATE_NPA("Update Non Performing Assets"),
|
UPDATE_NPA("Update Non Performing Assets"),
|
||||||
UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"),
|
UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"),
|
||||||
TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings");
|
TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"),
|
||||||
|
ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|||||||
Executable
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 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.common.domain;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <ul>
|
||||||
|
* People typically use either of the following settings when calculating there
|
||||||
|
* interest using the daily method:
|
||||||
|
* <li>360 and</li>
|
||||||
|
* <li>365</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public enum DaysInMonthType {
|
||||||
|
|
||||||
|
INVALID(0, "DaysInMonthType.invalid"), //
|
||||||
|
ACTUAL(1, "DaysInMonthType.actual"), //
|
||||||
|
DAYS_30(30, "DaysInMonthType.days360");
|
||||||
|
|
||||||
|
private final Integer value;
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
private DaysInMonthType(final Integer value, final String code) {
|
||||||
|
this.value = value;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return this.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] integerValues() {
|
||||||
|
final List<Integer> values = new ArrayList<>();
|
||||||
|
for (final DaysInMonthType enumType : values()) {
|
||||||
|
if (enumType.getValue() > 0) {
|
||||||
|
values.add(enumType.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DaysInMonthType fromInt(final Integer type) {
|
||||||
|
DaysInMonthType repaymentFrequencyType = DaysInMonthType.INVALID;
|
||||||
|
if (type != null) {
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
repaymentFrequencyType = DaysInMonthType.ACTUAL;
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
repaymentFrequencyType = DaysInMonthType.DAYS_30;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repaymentFrequencyType;
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+74
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* 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.common.domain;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <ul>
|
||||||
|
* People typically use either of the following settings when calculating there
|
||||||
|
* interest using the daily method:
|
||||||
|
* <li>360 and</li>
|
||||||
|
* <li>365</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public enum DaysInYearType {
|
||||||
|
|
||||||
|
INVALID(0, "DaysInYearType.invalid"), //
|
||||||
|
ACTUAL(1, "DaysInYearType.actual"), //
|
||||||
|
DAYS_360(360, "DaysInYearType.days360"), //
|
||||||
|
DAYS_364(364, "DaysInYearType.days364"), //
|
||||||
|
DAYS_365(365, "DaysInYearType.days365");
|
||||||
|
|
||||||
|
private final Integer value;
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
private DaysInYearType(final Integer value, final String code) {
|
||||||
|
this.value = value;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return this.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] integerValues() {
|
||||||
|
final List<Integer> values = new ArrayList<>();
|
||||||
|
for (final DaysInYearType enumType : values()) {
|
||||||
|
if (enumType.getValue() > 0) {
|
||||||
|
values.add(enumType.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DaysInYearType fromInt(final Integer type) {
|
||||||
|
DaysInYearType repaymentFrequencyType = DaysInYearType.INVALID;
|
||||||
|
if (type != null) {
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
repaymentFrequencyType = DaysInYearType.ACTUAL;
|
||||||
|
break;
|
||||||
|
case 360:
|
||||||
|
repaymentFrequencyType = DaysInYearType.DAYS_360;
|
||||||
|
break;
|
||||||
|
case 364:
|
||||||
|
repaymentFrequencyType = DaysInYearType.DAYS_364;
|
||||||
|
break;
|
||||||
|
case 365:
|
||||||
|
repaymentFrequencyType = DaysInYearType.DAYS_365;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repaymentFrequencyType;
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
-3
@@ -5,12 +5,19 @@ import java.util.Date;
|
|||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||||
|
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||||
|
|
||||||
public class LoanScheduleAccrualData {
|
public class LoanScheduleAccrualData {
|
||||||
|
|
||||||
private final Long loanId;
|
private final Long loanId;
|
||||||
private final Long officeId;
|
private final Long officeId;
|
||||||
|
private final LocalDate accruedTill;
|
||||||
|
private final PeriodFrequencyType repaymentFrequency;
|
||||||
|
private final Integer repayEvery;
|
||||||
|
private final Integer numberOfDaysInYear;
|
||||||
|
private final Integer numberOfDaysInMonth;
|
||||||
private final LocalDate dueDate;
|
private final LocalDate dueDate;
|
||||||
|
private final LocalDate fromDate;
|
||||||
private final Long repaymentScheduleId;
|
private final Long repaymentScheduleId;
|
||||||
private final Long loanProductId;
|
private final Long loanProductId;
|
||||||
private final BigDecimal interestIncome;
|
private final BigDecimal interestIncome;
|
||||||
@@ -20,14 +27,22 @@ public class LoanScheduleAccrualData {
|
|||||||
private final BigDecimal accruedFeeIncome;
|
private final BigDecimal accruedFeeIncome;
|
||||||
private final BigDecimal accruedPenaltyIncome;
|
private final BigDecimal accruedPenaltyIncome;
|
||||||
private final CurrencyData currencyData;
|
private final CurrencyData currencyData;
|
||||||
|
private final BigDecimal dueDateFeeIncome;
|
||||||
|
private final BigDecimal dueDatePenaltyIncome;
|
||||||
|
|
||||||
public LoanScheduleAccrualData(final Long loanId, final Long officeId, final LocalDate dueDate, final Long repaymentScheduleId,
|
public LoanScheduleAccrualData(final Long loanId, final Long officeId, final LocalDate accruedTill, final Integer numberOfDaysInMonth,
|
||||||
final Long loanProductId, final BigDecimal interestIncome, final BigDecimal feeIncome, final BigDecimal penaltyIncome,
|
final Integer numberOfDaysInYear, final PeriodFrequencyType repaymentFrequency, final Integer repayEvery,
|
||||||
|
final LocalDate dueDate, final LocalDate fromDate, final Long repaymentScheduleId, final Long loanProductId,
|
||||||
|
final BigDecimal interestIncome, final BigDecimal feeIncome, final BigDecimal penaltyIncome,
|
||||||
final BigDecimal accruedInterestIncome, final BigDecimal accruedFeeIncome, final BigDecimal accruedPenaltyIncome,
|
final BigDecimal accruedInterestIncome, final BigDecimal accruedFeeIncome, final BigDecimal accruedPenaltyIncome,
|
||||||
final CurrencyData currencyData) {
|
final CurrencyData currencyData, final BigDecimal dueDateFeeIncome, final BigDecimal dueDatePenaltyIncome) {
|
||||||
this.loanId = loanId;
|
this.loanId = loanId;
|
||||||
this.officeId = officeId;
|
this.officeId = officeId;
|
||||||
|
this.accruedTill = accruedTill;
|
||||||
|
this.numberOfDaysInMonth = numberOfDaysInMonth;
|
||||||
|
this.numberOfDaysInYear = numberOfDaysInYear;
|
||||||
this.dueDate = dueDate;
|
this.dueDate = dueDate;
|
||||||
|
this.fromDate = fromDate;
|
||||||
this.repaymentScheduleId = repaymentScheduleId;
|
this.repaymentScheduleId = repaymentScheduleId;
|
||||||
this.loanProductId = loanProductId;
|
this.loanProductId = loanProductId;
|
||||||
this.interestIncome = interestIncome;
|
this.interestIncome = interestIncome;
|
||||||
@@ -37,6 +52,10 @@ public class LoanScheduleAccrualData {
|
|||||||
this.accruedInterestIncome = accruedInterestIncome;
|
this.accruedInterestIncome = accruedInterestIncome;
|
||||||
this.accruedPenaltyIncome = accruedPenaltyIncome;
|
this.accruedPenaltyIncome = accruedPenaltyIncome;
|
||||||
this.currencyData = currencyData;
|
this.currencyData = currencyData;
|
||||||
|
this.repaymentFrequency = repaymentFrequency;
|
||||||
|
this.repayEvery = repayEvery;
|
||||||
|
this.dueDateFeeIncome = dueDateFeeIncome;
|
||||||
|
this.dueDatePenaltyIncome = dueDatePenaltyIncome;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getLoanId() {
|
public Long getLoanId() {
|
||||||
@@ -91,4 +110,36 @@ public class LoanScheduleAccrualData {
|
|||||||
return this.currencyData;
|
return this.currencyData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalDate getAccruedTill() {
|
||||||
|
return this.accruedTill;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getNumberOfDaysInYear() {
|
||||||
|
return this.numberOfDaysInYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getNumberOfDaysInMonth() {
|
||||||
|
return this.numberOfDaysInMonth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getFromDateAsLocaldate() {
|
||||||
|
return this.fromDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeriodFrequencyType getRepaymentFrequency() {
|
||||||
|
return this.repaymentFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getRepayEvery() {
|
||||||
|
return this.repayEvery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getDueDateFeeIncome() {
|
||||||
|
return this.dueDateFeeIncome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getDueDatePenaltyIncome() {
|
||||||
|
return this.dueDatePenaltyIncome;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -1,5 +1,6 @@
|
|||||||
package org.mifosplatform.portfolio.loanaccount.service;
|
package org.mifosplatform.portfolio.loanaccount.service;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||||
|
|
||||||
|
|
||||||
@@ -7,4 +8,8 @@ public interface LoanAccrualWritePlatformService {
|
|||||||
|
|
||||||
void addAccrualAccounting() throws JobExecutionException;
|
void addAccrualAccounting() throws JobExecutionException;
|
||||||
|
|
||||||
|
void addPeriodicAccruals() throws JobExecutionException;
|
||||||
|
|
||||||
|
String addPeriodicAccruals(LocalDate tilldate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+158
-5
@@ -1,6 +1,7 @@
|
|||||||
package org.mifosplatform.portfolio.loanaccount.service;
|
package org.mifosplatform.portfolio.loanaccount.service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -11,11 +12,15 @@ import java.util.Set;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.joda.time.Days;
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
|
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||||
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
|
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
|
||||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||||
import org.mifosplatform.infrastructure.jobs.service.JobName;
|
import org.mifosplatform.infrastructure.jobs.service.JobName;
|
||||||
|
import org.mifosplatform.portfolio.common.domain.DaysInMonthType;
|
||||||
|
import org.mifosplatform.portfolio.common.domain.DaysInYearType;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
@@ -72,11 +77,151 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CronTarget(jobName = JobName.ADD_PERIODIC_ACCRUAL_ENTRIES)
|
||||||
|
public void addPeriodicAccruals() throws JobExecutionException {
|
||||||
|
String errors = addPeriodicAccruals(LocalDate.now());
|
||||||
|
if (errors.length() > 0) { throw new JobExecutionException(errors); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String addPeriodicAccruals(final LocalDate tilldate) {
|
||||||
|
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = this.loanReadPlatformService.retrivePeriodicAccrualData(tilldate);
|
||||||
|
return addPeriodicAccruals(tilldate, loanScheduleAccrualDatas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addPeriodicAccruals(final LocalDate tilldate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
Set<Long> loansIds = new HashSet<>();
|
||||||
|
for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) {
|
||||||
|
try {
|
||||||
|
if (!loansIds.contains(accrualData.getLoanId())) {
|
||||||
|
if (accrualData.getDueDateAsLocaldate().isAfter(tilldate)) {
|
||||||
|
addAccrualTillSpecificDate(tilldate, accrualData);
|
||||||
|
} else {
|
||||||
|
addAccrualAccounting(accrualData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
loansIds.add(accrualData.getLoanId());
|
||||||
|
Throwable realCause = e;
|
||||||
|
if (e.getCause() != null) {
|
||||||
|
realCause = e.getCause();
|
||||||
|
}
|
||||||
|
sb.append("failed to add accural transaction for repayment with id " + accrualData.getRepaymentScheduleId()
|
||||||
|
+ " with message " + realCause.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanScheduleAccrualData accrualData) throws Exception {
|
||||||
|
int totalNumberOfDays = Days.daysBetween(accrualData.getFromDateAsLocaldate(), accrualData.getDueDateAsLocaldate()).getDays();
|
||||||
|
LocalDate startDate = accrualData.getAccruedTill();
|
||||||
|
if (startDate == null) {
|
||||||
|
startDate = accrualData.getFromDateAsLocaldate();
|
||||||
|
}
|
||||||
|
int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays();
|
||||||
|
if(daysToBeAccrued < 1){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int daysInSchedule = Days.daysBetween(accrualData.getFromDateAsLocaldate(), tilldate).getDays();
|
||||||
|
switch (accrualData.getRepaymentFrequency()) {
|
||||||
|
case MONTHS:
|
||||||
|
totalNumberOfDays = calculateTotalNumberOfDaysForMonth(totalNumberOfDays, accrualData);
|
||||||
|
break;
|
||||||
|
case YEARS:
|
||||||
|
totalNumberOfDays = calculateTotalNumberOfDaysForYear(totalNumberOfDays, accrualData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
double interestPerDay = accrualData.getInterestIncome().doubleValue() / totalNumberOfDays;
|
||||||
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
|
BigDecimal interestportion = null;
|
||||||
|
BigDecimal feeportion = accrualData.getDueDateFeeIncome();
|
||||||
|
BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome();
|
||||||
|
if (daysInSchedule >= totalNumberOfDays) {
|
||||||
|
interestportion = accrualData.getInterestIncome();
|
||||||
|
} else {
|
||||||
|
double iterest = interestPerDay * daysToBeAccrued;
|
||||||
|
interestportion = BigDecimal.valueOf(iterest);
|
||||||
|
}
|
||||||
|
interestportion = interestportion.setScale(accrualData.getCurrencyData().decimalPlaces(), RoundingMode.HALF_EVEN);
|
||||||
|
amount = amount.add(interestportion);
|
||||||
|
|
||||||
|
BigDecimal totalAccInterest = accrualData.getAccruedInterestIncome();
|
||||||
|
if (totalAccInterest == null) {
|
||||||
|
totalAccInterest = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
totalAccInterest = totalAccInterest.add(interestportion);
|
||||||
|
BigDecimal totalAccFee = accrualData.getAccruedFeeIncome();
|
||||||
|
|
||||||
|
if (feeportion != null) {
|
||||||
|
if (totalAccFee == null) {
|
||||||
|
totalAccFee = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
feeportion = feeportion.subtract(totalAccFee);
|
||||||
|
amount = amount.add(feeportion);
|
||||||
|
totalAccFee = totalAccFee.add(feeportion);
|
||||||
|
if (feeportion.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
|
feeportion = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BigDecimal totalAccPenalty = accrualData.getAccruedPenaltyIncome();
|
||||||
|
if (penaltyportion != null) {
|
||||||
|
if (totalAccPenalty == null) {
|
||||||
|
totalAccPenalty = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
penaltyportion = penaltyportion.subtract(totalAccPenalty);
|
||||||
|
amount = amount.add(penaltyportion);
|
||||||
|
totalAccPenalty = totalAccFee.add(penaltyportion);
|
||||||
|
if (penaltyportion.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
|
penaltyportion = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (amount.compareTo(BigDecimal.ZERO) != 0) {
|
||||||
|
addAccrualAccounting(accrualData, amount, interestportion, totalAccInterest, feeportion, totalAccFee, penaltyportion,
|
||||||
|
totalAccPenalty, tilldate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateTotalNumberOfDaysForMonth(final int totalNumberOfDays, final LoanScheduleAccrualData accrualData) {
|
||||||
|
int numberOfDays = totalNumberOfDays;
|
||||||
|
DaysInMonthType type = DaysInMonthType.fromInt(accrualData.getNumberOfDaysInMonth());
|
||||||
|
switch (type) {
|
||||||
|
case DAYS_30:
|
||||||
|
numberOfDays = 30 * accrualData.getRepayEvery();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return numberOfDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateTotalNumberOfDaysForYear(final int totalNumberOfDays, final LoanScheduleAccrualData accrualData) {
|
||||||
|
int numberOfDays = totalNumberOfDays;
|
||||||
|
DaysInYearType type = DaysInYearType.fromInt(accrualData.getNumberOfDaysInMonth());
|
||||||
|
switch (type) {
|
||||||
|
case DAYS_360:
|
||||||
|
numberOfDays = 360 * accrualData.getRepayEvery();
|
||||||
|
break;
|
||||||
|
case DAYS_364:
|
||||||
|
numberOfDays = 364 * accrualData.getRepayEvery();
|
||||||
|
break;
|
||||||
|
case DAYS_365:
|
||||||
|
numberOfDays = 365 * accrualData.getRepayEvery();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return numberOfDays;
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData) throws Exception {
|
public void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData) throws Exception {
|
||||||
|
|
||||||
String transactionSql = "INSERT INTO m_loan_transaction (loan_id,office_id,is_reversed,transaction_type_enum,transaction_date,amount,interest_portion_derived,"
|
|
||||||
+ "fee_charges_portion_derived,penalty_charges_portion_derived) VALUES (?, ?, 0, ?, ?, ?, ?, ?, ?)";
|
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
BigDecimal interestportion = null;
|
BigDecimal interestportion = null;
|
||||||
BigDecimal totalAccInterest = null;
|
BigDecimal totalAccInterest = null;
|
||||||
@@ -119,11 +264,19 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
penaltyportion = null;
|
penaltyportion = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
addAccrualAccounting(scheduleAccrualData, amount, interestportion, totalAccInterest, feeportion, totalAccFee, penaltyportion,
|
||||||
|
totalAccPenalty, scheduleAccrualData.getDueDateAsLocaldate());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData, BigDecimal amount, BigDecimal interestportion,
|
||||||
|
BigDecimal totalAccInterest, BigDecimal feeportion, BigDecimal totalAccFee, BigDecimal penaltyportion,
|
||||||
|
BigDecimal totalAccPenalty, final LocalDate accruredTill) throws Exception {
|
||||||
TransactionStatus transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
|
TransactionStatus transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
|
||||||
try {
|
try {
|
||||||
|
String transactionSql = "INSERT INTO m_loan_transaction (loan_id,office_id,is_reversed,transaction_type_enum,transaction_date,amount,interest_portion_derived,"
|
||||||
|
+ "fee_charges_portion_derived,penalty_charges_portion_derived) VALUES (?, ?, 0, ?, ?, ?, ?, ?, ?)";
|
||||||
this.jdbcTemplate.update(transactionSql, scheduleAccrualData.getLoanId(), scheduleAccrualData.getOfficeId(),
|
this.jdbcTemplate.update(transactionSql, scheduleAccrualData.getLoanId(), scheduleAccrualData.getOfficeId(),
|
||||||
LoanTransactionType.ACCRUAL.getValue(), scheduleAccrualData.getDueDate(), amount, interestportion, feeportion,
|
LoanTransactionType.ACCRUAL.getValue(), accruredTill.toDate(), amount, interestportion, feeportion, penaltyportion);
|
||||||
penaltyportion);
|
|
||||||
final Long transactonId = this.jdbcTemplate.queryForLong("SELECT LAST_INSERT_ID()");
|
final Long transactonId = this.jdbcTemplate.queryForLong("SELECT LAST_INSERT_ID()");
|
||||||
|
|
||||||
Map<String, Object> transactionMap = toMapData(transactonId, amount, interestportion, feeportion, penaltyportion,
|
Map<String, Object> transactionMap = toMapData(transactonId, amount, interestportion, feeportion, penaltyportion,
|
||||||
@@ -135,7 +288,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
scheduleAccrualData.getRepaymentScheduleId());
|
scheduleAccrualData.getRepaymentScheduleId());
|
||||||
|
|
||||||
String updateLoan = "UPDATE m_loan SET accrued_till=? WHERE id=?";
|
String updateLoan = "UPDATE m_loan SET accrued_till=? WHERE id=?";
|
||||||
this.jdbcTemplate.update(updateLoan, scheduleAccrualData.getDueDate(), scheduleAccrualData.getLoanId());
|
this.jdbcTemplate.update(updateLoan, accruredTill.toDate(), scheduleAccrualData.getLoanId());
|
||||||
final Map<String, Object> accountingBridgeData = deriveAccountingBridgeData(scheduleAccrualData, transactionMap);
|
final Map<String, Object> accountingBridgeData = deriveAccountingBridgeData(scheduleAccrualData, transactionMap);
|
||||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
+3
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanaccount.service;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.infrastructure.core.service.Page;
|
import org.mifosplatform.infrastructure.core.service.Page;
|
||||||
import org.mifosplatform.organisation.staff.data.StaffData;
|
import org.mifosplatform.organisation.staff.data.StaffData;
|
||||||
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||||
@@ -84,4 +85,6 @@ public interface LoanReadPlatformService {
|
|||||||
LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
|
LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
|
||||||
|
|
||||||
LoanTransactionData retrieveLoanWriteoffTemplate(Long loanId);
|
LoanTransactionData retrieveLoanWriteoffTemplate(Long loanId);
|
||||||
|
|
||||||
|
Collection<LoanScheduleAccrualData> retrivePeriodicAccrualData(LocalDate tillDate);
|
||||||
}
|
}
|
||||||
+104
-3
@@ -11,11 +11,15 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.joda.time.Days;
|
import org.joda.time.Days;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
import org.mifosplatform.accounting.common.AccountingRuleType;
|
import org.mifosplatform.accounting.common.AccountingRuleType;
|
||||||
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||||
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
|
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||||
@@ -45,6 +49,7 @@ import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
|||||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||||
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
|
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
|
||||||
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
||||||
|
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||||
import org.mifosplatform.portfolio.fund.service.FundReadPlatformService;
|
import org.mifosplatform.portfolio.fund.service.FundReadPlatformService;
|
||||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||||
@@ -88,6 +93,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
|
|||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
@@ -110,6 +116,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
private final StaffReadPlatformService staffReadPlatformService;
|
private final StaffReadPlatformService staffReadPlatformService;
|
||||||
private final PaginationHelper<LoanAccountData> paginationHelper = new PaginationHelper<>();
|
private final PaginationHelper<LoanAccountData> paginationHelper = new PaginationHelper<>();
|
||||||
private final LoanMapper loaanLoanMapper = new LoanMapper();
|
private final LoanMapper loaanLoanMapper = new LoanMapper();
|
||||||
|
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||||
|
private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository,
|
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository,
|
||||||
@@ -134,6 +142,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
this.calendarReadPlatformService = calendarReadPlatformService;
|
this.calendarReadPlatformService = calendarReadPlatformService;
|
||||||
this.staffReadPlatformService = staffReadPlatformService;
|
this.staffReadPlatformService = staffReadPlatformService;
|
||||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||||
|
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1328,6 +1337,90 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
AccountingRuleType.ACCRUAL_PERIODIC.getValue() });
|
AccountingRuleType.ACCRUAL_PERIODIC.getValue() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<LoanScheduleAccrualData> retrivePeriodicAccrualData(final LocalDate tillDate) {
|
||||||
|
|
||||||
|
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
||||||
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
|
sqlBuilder
|
||||||
|
.append("select ")
|
||||||
|
.append(mapper.schema())
|
||||||
|
.append(" where ((if(ls.fee_charges_waived_derived is null , ls.fee_charges_amount,(ls.fee_charges_amount-ls.fee_charges_waived_derived)) <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))")
|
||||||
|
.append(" or (if(ls.penalty_charges_waived_derived is null , ls.penalty_charges_amount,(ls.penalty_charges_amount-ls.penalty_charges_waived_derived)) <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))")
|
||||||
|
.append(" or (if(ls.interest_waived_derived is null , ls.interest_amount,(ls.interest_amount-ls.interest_waived_derived)) <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))")
|
||||||
|
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and loan.is_npa=0 and (ls.duedate <= :tilldate or (ls.duedate > :tilldate and ls.fromdate < :tilldate)) order by loan.id,ls.duedate");
|
||||||
|
Map<String, Object> paramMap = new HashMap<>(3);
|
||||||
|
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||||
|
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
|
||||||
|
paramMap.put("tilldate", formatter.print(tillDate));
|
||||||
|
|
||||||
|
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class LoanSchedulePeriodicAccrualMapper implements RowMapper<LoanScheduleAccrualData> {
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
|
sqlBuilder
|
||||||
|
.append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,")
|
||||||
|
.append("loan.accrued_till as accruedTill, loan.repayment_period_frequency_enum as frequencyEnum, ")
|
||||||
|
.append("loan.repay_every as repayEvery,")
|
||||||
|
.append("(select sum(lc.amount) as dueamount from m_loan_charge lc where lc.loan_id = loan.id and lc.is_active = 1 and lc.waived = 0 and lc.is_penalty = 0 and lc.due_for_collection_as_of_date > ls.fromdate and lc.due_for_collection_as_of_date<= if(:tilldate < ls.duedate, :tilldate , ls.duedate)) as feedueIn,")
|
||||||
|
.append("(select sum(lc.amount) as dueamount from m_loan_charge lc where lc.loan_id = loan.id and lc.is_active = 1 and lc.waived = 0 and lc.is_penalty = 1 and lc.due_for_collection_as_of_date > ls.fromdate and lc.due_for_collection_as_of_date<= if(:tilldate < ls.duedate, :tilldate , ls.duedate)) as penalitydueIn,")
|
||||||
|
.append("ls.duedate as duedate,ls.fromdate as fromdate ,ls.id as scheduleId,loan.product_id as productId,")
|
||||||
|
.append("if(ls.interest_waived_derived is null,ls.interest_amount,(ls.interest_amount-ls.interest_waived_derived)) as interest,")
|
||||||
|
.append("if(ls.penalty_charges_waived_derived is null , ls.penalty_charges_amount,(ls.penalty_charges_amount-ls.penalty_charges_waived_derived)) as penalty,")
|
||||||
|
.append("if(ls.fee_charges_waived_derived is null , ls.fee_charges_amount,(ls.fee_charges_amount-ls.fee_charges_waived_derived)) as charges,")
|
||||||
|
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
|
||||||
|
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
|
||||||
|
.append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode")
|
||||||
|
.append(" from m_loan_repayment_schedule ls ").append(" left join m_loan loan on loan.id=ls.loan_id ")
|
||||||
|
.append(" left join m_product_loan mpl on mpl.id = loan.product_id")
|
||||||
|
.append(" left join m_client mc on mc.id = loan.client_id ").append(" left join m_group mg on mg.id = loan.group_id")
|
||||||
|
.append(" left join m_currency curr on curr.code = loan.currency_code");
|
||||||
|
return sqlBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoanScheduleAccrualData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||||
|
|
||||||
|
final Long loanId = rs.getLong("loanId");
|
||||||
|
final Long officeId = rs.getLong("officeId");
|
||||||
|
final LocalDate accruedTill = JdbcSupport.getLocalDate(rs, "accruedTill");
|
||||||
|
final Integer numberOfDaysInMonth = 30;
|
||||||
|
final Integer numberOfDaysInYear = 360;
|
||||||
|
final Integer frequencyEnum = JdbcSupport.getInteger(rs, "frequencyEnum");
|
||||||
|
final Integer repayEvery = JdbcSupport.getInteger(rs, "repayEvery");
|
||||||
|
final PeriodFrequencyType frequency = PeriodFrequencyType.fromInt(frequencyEnum);
|
||||||
|
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "duedate");
|
||||||
|
final LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromdate");
|
||||||
|
final Long repaymentScheduleId = rs.getLong("scheduleId");
|
||||||
|
final Long loanProductId = rs.getLong("productId");
|
||||||
|
final BigDecimal interestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "interest");
|
||||||
|
final BigDecimal feeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "charges");
|
||||||
|
final BigDecimal penaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalty");
|
||||||
|
final BigDecimal accruedInterestIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accinterest");
|
||||||
|
final BigDecimal accruedFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accfeecharege");
|
||||||
|
final BigDecimal accruedPenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "accpenalty");
|
||||||
|
|
||||||
|
final String currencyCode = rs.getString("currencyCode");
|
||||||
|
final String currencyName = rs.getString("currencyName");
|
||||||
|
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||||
|
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||||
|
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||||
|
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||||
|
final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
|
||||||
|
currencyDisplaySymbol, currencyNameCode);
|
||||||
|
|
||||||
|
final BigDecimal dueDateFeeIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "feedueIn");
|
||||||
|
final BigDecimal dueDatePenaltyIncome = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "penalitydueIn");
|
||||||
|
return new LoanScheduleAccrualData(loanId, officeId, accruedTill, numberOfDaysInMonth, numberOfDaysInYear, frequency,
|
||||||
|
repayEvery, dueDate, fromDate, repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome,
|
||||||
|
accruedInterestIncome, accruedFeeIncome, accruedPenaltyIncome, currencyData, dueDateFeeIncome, dueDatePenaltyIncome);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static final class LoanScheduleAccrualMapper implements RowMapper<LoanScheduleAccrualData> {
|
private static final class LoanScheduleAccrualMapper implements RowMapper<LoanScheduleAccrualData> {
|
||||||
|
|
||||||
public String schema() {
|
public String schema() {
|
||||||
@@ -1371,10 +1464,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||||
final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
|
final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
|
||||||
currencyDisplaySymbol, currencyNameCode);
|
currencyDisplaySymbol, currencyNameCode);
|
||||||
return new LoanScheduleAccrualData(loanId, officeId, dueDate, repaymentScheduleId, loanProductId, interestIncome, feeIncome,
|
final LocalDate accruedTill = null;
|
||||||
penaltyIncome, accruedInterestIncome, accruedFeeIncome, accruedPenaltyIncome, currencyData);
|
final Integer numberOfDaysInMonth = null;
|
||||||
|
final Integer numberOfDaysInYear = null;
|
||||||
|
final PeriodFrequencyType frequency = null;
|
||||||
|
final Integer repayEvery = null;
|
||||||
|
final LocalDate fromDate = null;
|
||||||
|
final BigDecimal dueDateFeeincome = null;
|
||||||
|
final BigDecimal dueDatePenaltyIncome = null;
|
||||||
|
return new LoanScheduleAccrualData(loanId, officeId, accruedTill, numberOfDaysInMonth, numberOfDaysInYear, frequency,
|
||||||
|
repayEvery, dueDate, fromDate, repaymentScheduleId, loanProductId, interestIncome, feeIncome, penaltyIncome,
|
||||||
|
accruedInterestIncome, accruedFeeIncome, accruedPenaltyIncome, currencyData, dueDateFeeincome, dueDatePenaltyIncome);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user