FINERACT-1783: Customizeable LoanRepaymentScheduleTransactionProcessor

This commit is contained in:
Aleksandar Vidakovic
2022-11-01 14:08:02 +01:00
committed by Aleksandar Vidakovic
parent f2756b25bb
commit fc895fce05
99 changed files with 2015 additions and 2547 deletions
+2 -2
View File
@@ -18,8 +18,8 @@
*/ */
description = 'ACME Corp.: Fineract COB Loan' description = 'ACME Corp.: Fineract COB Loan'
group = 'com.acme.fineract.cob' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-cob-loan' archivesBaseName = 'acme-fineract-loan-cob'
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
@@ -31,6 +31,10 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class AcmeNoopBusinessStep implements LoanCOBBusinessStep, InitializingBean { public class AcmeNoopBusinessStep implements LoanCOBBusinessStep, InitializingBean {
private static final String ENUM_STYLED_NAME = "ACME_LOAN_NOOP";
private static final String HUMAN_READABLE_NAME = "ACME Loan Noop";
// NOTE: just to demonstrate that dependency injection is working // NOTE: just to demonstrate that dependency injection is working
private final LoanAccountDomainService loanAccountDomainService; private final LoanAccountDomainService loanAccountDomainService;
@@ -46,11 +50,11 @@ public class AcmeNoopBusinessStep implements LoanCOBBusinessStep, InitializingBe
@Override @Override
public String getEnumStyledName() { public String getEnumStyledName() {
return "ACME_LOAN_NOOP"; return ENUM_STYLED_NAME;
} }
@Override @Override
public String getHumanReadableName() { public String getHumanReadableName() {
return "ACME Loan Noop"; return HUMAN_READABLE_NAME;
} }
} }
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet author="acme" id="1">
<insert tableName="m_batch_business_steps">
<column name="job_name" value="LOAN_CLOSE_OF_BUSINESS"/>
<column name="step_name" value="ACME_LOAN_NOOP"/>
<column name="step_order" value="5"/>
</insert>
</changeSet>
</databaseChangeLog>
+25
View File
@@ -0,0 +1,25 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'ACME Corp.: Fineract Loan Transaction Processors'
group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-loan-processor'
apply from: 'dependencies.gradle'
@@ -16,13 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
package org.apache.fineract.portfolio.loanaccount.domain;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy; dependencies {
import org.springframework.data.jpa.repository.JpaRepository; implementation(project(':fineract-provider'))
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LoanTransactionProcessingStrategyRepository
extends JpaRepository<LoanTransactionProcessingStrategy, Long>, JpaSpecificationExecutor<LoanTransactionProcessingStrategy> {
// no added behaviour
} }
@@ -0,0 +1,41 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.acme.fineract.loan.processor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
import org.springframework.stereotype.Component;
@Component
public class AcmeLoanRepaymentScheduleTransactionProcessor extends FineractStyleLoanRepaymentScheduleTransactionProcessor {
private static final String STRATEGY_CODE = "acme-standard-strategy";
private static final String STRATEGY_NAME = "ACME Corp.: standard loan transaction processing strategy";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
}
+2 -2
View File
@@ -18,8 +18,8 @@
*/ */
description = 'ACME Corp.: Fineract COB Starter' description = 'ACME Corp.: Fineract COB Starter'
group = 'com.acme.fineract.cob' group = 'com.acme.fineract'
archivesBaseName = 'acme-fineract-cob-starter' archivesBaseName = 'acme-fineract-loan-starter'
apply from: 'dependencies.gradle' apply from: 'dependencies.gradle'
@@ -18,7 +18,9 @@
*/ */
dependencies { dependencies {
implementation(project(':fineract-provider'))
implementation(project(':custom:acme:loan:cob')) implementation(project(':custom:acme:loan:cob'))
implementation(project(':custom:acme:loan:processor'))
implementation('org.springframework.boot:spring-boot-starter') implementation('org.springframework.boot:spring-boot-starter')
testImplementation(project(':fineract-provider')) testImplementation(project(':fineract-provider'))
testImplementation('org.springframework.boot:spring-boot-starter-jdbc') testImplementation('org.springframework.boot:spring-boot-starter-jdbc')
@@ -18,11 +18,25 @@
*/ */
package com.acme.fineract.loan.starter; package com.acme.fineract.loan.starter;
import com.acme.fineract.loan.processor.AcmeLoanRepaymentScheduleTransactionProcessor;
import java.util.List;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
@AutoConfiguration @AutoConfiguration
@ComponentScan("com.acme.fineract.loan.cob") @ComponentScans({ @ComponentScan("com.acme.fineract.loan.cob"), @ComponentScan("com.acme.fineract.loan.processor") })
@ConditionalOnProperty("acme.loan.enabled") @ConditionalOnProperty("acme.loan.enabled")
public class AcmeLoanAutoConfiguration {} public class AcmeLoanAutoConfiguration {
@Bean
public LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory(
AcmeLoanRepaymentScheduleTransactionProcessor defaultLoanRepaymentScheduleTransactionProcessor,
List<LoanRepaymentScheduleTransactionProcessor> processors) {
return new LoanRepaymentScheduleTransactionProcessorFactory(defaultLoanRepaymentScheduleTransactionProcessor, processors);
}
}
+1 -1
View File
@@ -85,7 +85,7 @@ jib {
} }
// NOTE: other custom dependencies // NOTE: other custom dependencies
implementation 'org.mariadb.jdbc:mariadb-java-client:3.0.7' implementation 'org.mariadb.jdbc:mariadb-java-client:3.0.7'
implementation 'mysql:mysql-connector-java:8.0.30' implementation 'mysql:mysql-connector-java:8.0.31'
implementation 'org.postgresql:postgresql:42.5.0' implementation 'org.postgresql:postgresql:42.5.0'
annotationProcessor('org.springframework.boot:spring-boot-autoconfigure-processor') annotationProcessor('org.springframework.boot:spring-boot-autoconfigure-processor')
annotationProcessor('org.springframework.boot:spring-boot-configuration-processor') annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')
@@ -341,10 +341,10 @@
}, },
{ {
"default": null, "default": null,
"name": "transactionProcessingStrategyId", "name": "transactionProcessingStrategyCode",
"type": [ "type": [
"null", "null",
"long" "string"
] ]
}, },
{ {
@@ -341,10 +341,10 @@
}, },
{ {
"default": null, "default": null,
"name": "transactionProcessingStrategyId", "name": "transactionProcessingStrategyCode",
"type": [ "type": [
"null", "null",
"long" "string"
] ]
}, },
{ {
@@ -1011,7 +1011,6 @@ CREATE TABLE `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -1027,7 +1026,6 @@ CREATE TABLE `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -1506,9 +1504,7 @@ CREATE TABLE `m_product_loan` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unq_name` (`name`), UNIQUE KEY `unq_name` (`name`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@@ -1770,36 +1766,6 @@ INSERT INTO `r_enum_value` VALUES ('amortization_method_enum',0,'Equal principle
/*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */; /*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `ref_loan_transaction_processing_strategy`
--
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = UTF8MB4 */;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` BIGINT DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` BIGINT DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `ref_loan_transaction_processing_strategy`
--
LOCK TABLES `ref_loan_transaction_processing_strategy` WRITE;
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` VALUES (1,'mifos-standard-strategy','Mifos style',NULL,NULL,NULL,NULL),(2,'heavensfamily-strategy','Heavensfamily',NULL,NULL,NULL,NULL),(3,'creocore-strategy','Creocore',NULL,NULL,NULL,NULL),(4,'rbi-india-strategy','RBI (India)',NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `rpt_sequence` -- Table structure for table `rpt_sequence`
-- --
@@ -1012,7 +1012,6 @@ CREATE TABLE `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -1028,7 +1027,6 @@ CREATE TABLE `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -1507,9 +1505,7 @@ CREATE TABLE `m_product_loan` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unq_name` (`name`), UNIQUE KEY `unq_name` (`name`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@@ -1771,36 +1767,6 @@ INSERT INTO `r_enum_value` VALUES ('amortization_method_enum',0,'Equal principle
/*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */; /*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `ref_loan_transaction_processing_strategy`
--
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = UTF8MB4 */;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` BIGINT DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` BIGINT DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `ref_loan_transaction_processing_strategy`
--
LOCK TABLES `ref_loan_transaction_processing_strategy` WRITE;
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` VALUES (1,'mifos-standard-strategy','Mifos style',NULL,NULL,NULL,NULL),(2,'heavensfamily-strategy','Heavensfamily',NULL,NULL,NULL,NULL),(3,'creocore-strategy','Creocore',NULL,NULL,NULL,NULL),(4,'rbi-india-strategy','RBI (India)',NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `rpt_sequence` -- Table structure for table `rpt_sequence`
-- --
@@ -930,7 +930,6 @@ CREATE TABLE `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -946,7 +945,6 @@ CREATE TABLE `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -1399,7 +1397,7 @@ CREATE TABLE `m_portfolio_command_source` (
LOCK TABLES `m_portfolio_command_source` WRITE; LOCK TABLES `m_portfolio_command_source` WRITE;
/*!40000 ALTER TABLE `m_portfolio_command_source` DISABLE KEYS */; /*!40000 ALTER TABLE `m_portfolio_command_source` DISABLE KEYS */;
INSERT INTO `m_portfolio_command_source` VALUES (1,'CREATE','CLIENT',2,NULL,1,NULL,NULL,'/clients/template',1,NULL,'{\"officeId\":\"2\",\"firstname\":\"Test\",\"middlename\":\"\",\"lastname\":\"One\",\"fullname\":\"\",\"externalId\":\"\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"joinedDate\":\"2011-02-01\"}',1,'2013-04-05 12:38:05',NULL,NULL,1),(2,'CREATE','LOAN',2,NULL,1,1,NULL,'/loans',1,NULL,'{\"clientId\":\"1\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"productId\":\"1\",\"loanOfficerId\":\"2\",\"submittedOnDate\":\"2011-04-01\",\"loanPurposeId\":\"25\",\"principal\":\"1,000,000.00\",\"loanTermFrequency\":\"12\",\"loanTermFrequencyType\":\"2\",\"numberOfRepayments\":\"12\",\"repaymentEvery\":\"1\",\"repaymentFrequencyType\":\"2\",\"interestRatePerPeriod\":\"24\",\"interestRateFrequencyType\":\"3\",\"expectedDisbursementDate\":\"2011-04-01\",\"amortizationType\":\"1\",\"interestType\":\"1\",\"interestCalculationPeriodType\":\"1\",\"inArrearsTolerance\":\"\",\"transactionProcessingStrategyId\":\"2\",\"repaymentsStartingFromDate\":\"\",\"interestChargedFromDate\":\"\",\"charges\":[{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-05-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-06-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-07-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-08-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-09-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-10-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-11-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-12-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-01-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-02-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-03-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-04-01\"}],\"collateral\":[{\"id\":\"\",\"type\":\"23\",\"description\":\" small description.\",\"value\":\"\"}]}',1,'2013-04-05 12:41:57',NULL,NULL,1),(3,'APPROVE','LOAN',2,NULL,1,1,NULL,'/loans/1',1,NULL,'{\"status\":{\"id\":200,\"code\":\"loanStatusType.approved\",\"value\":\"Approved\",\"pendingApproval\":false,\"waitingForDisbursal\":true,\"active\":false,\"closedObligationsMet\":false,\"closedWrittenOff\":false,\"closedRescheduled\":false,\"closed\":false,\"overpaid\":false},\"locale\":\"en\",\"dateFormat\":\"yyyy-MM-dd\",\"approvedOnDate\":\"2011-04-01\"}',1,'2013-04-05 12:42:09',NULL,NULL,1); INSERT INTO `m_portfolio_command_source` VALUES (1,'CREATE','CLIENT',2,NULL,1,NULL,NULL,'/clients/template',1,NULL,'{\"officeId\":\"2\",\"firstname\":\"Test\",\"middlename\":\"\",\"lastname\":\"One\",\"fullname\":\"\",\"externalId\":\"\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"joinedDate\":\"2011-02-01\"}',1,'2013-04-05 12:38:05',NULL,NULL,1),(2,'CREATE','LOAN',2,NULL,1,1,NULL,'/loans',1,NULL,'{\"clientId\":\"1\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"productId\":\"1\",\"loanOfficerId\":\"2\",\"submittedOnDate\":\"2011-04-01\",\"loanPurposeId\":\"25\",\"principal\":\"1,000,000.00\",\"loanTermFrequency\":\"12\",\"loanTermFrequencyType\":\"2\",\"numberOfRepayments\":\"12\",\"repaymentEvery\":\"1\",\"repaymentFrequencyType\":\"2\",\"interestRatePerPeriod\":\"24\",\"interestRateFrequencyType\":\"3\",\"expectedDisbursementDate\":\"2011-04-01\",\"amortizationType\":\"1\",\"interestType\":\"1\",\"interestCalculationPeriodType\":\"1\",\"inArrearsTolerance\":\"\",\"transactionProcessingStrategy\":\"heavensfamily-strategy\",\"repaymentsStartingFromDate\":\"\",\"interestChargedFromDate\":\"\",\"charges\":[{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-05-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-06-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-07-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-08-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-09-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-10-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-11-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-12-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-01-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-02-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-03-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-04-01\"}],\"collateral\":[{\"id\":\"\",\"type\":\"23\",\"description\":\" small description.\",\"value\":\"\"}]}',1,'2013-04-05 12:41:57',NULL,NULL,1),(3,'APPROVE','LOAN',2,NULL,1,1,NULL,'/loans/1',1,NULL,'{\"status\":{\"id\":200,\"code\":\"loanStatusType.approved\",\"value\":\"Approved\",\"pendingApproval\":false,\"waitingForDisbursal\":true,\"active\":false,\"closedObligationsMet\":false,\"closedWrittenOff\":false,\"closedRescheduled\":false,\"closed\":false,\"overpaid\":false},\"locale\":\"en\",\"dateFormat\":\"yyyy-MM-dd\",\"approvedOnDate\":\"2011-04-01\"}',1,'2013-04-05 12:42:09',NULL,NULL,1);
/*!40000 ALTER TABLE `m_portfolio_command_source` ENABLE KEYS */; /*!40000 ALTER TABLE `m_portfolio_command_source` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
@@ -1435,9 +1433,7 @@ CREATE TABLE `m_product_loan` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unq_name` (`name`), UNIQUE KEY `unq_name` (`name`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@@ -1709,36 +1705,6 @@ INSERT INTO `r_enum_value` VALUES ('amortization_method_enum',0,'Equal principle
/*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */; /*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `ref_loan_transaction_processing_strategy`
--
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = UTF8MB4 */;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` BIGINT DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` BIGINT DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `ref_loan_transaction_processing_strategy`
--
LOCK TABLES `ref_loan_transaction_processing_strategy` WRITE;
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` VALUES (2,'heavensfamily-strategy','Heavensfamily',NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `rpt_sequence` -- Table structure for table `rpt_sequence`
-- --
@@ -930,7 +930,6 @@ CREATE TABLE `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -946,7 +945,6 @@ CREATE TABLE `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -1399,7 +1397,7 @@ CREATE TABLE `m_portfolio_command_source` (
LOCK TABLES `m_portfolio_command_source` WRITE; LOCK TABLES `m_portfolio_command_source` WRITE;
/*!40000 ALTER TABLE `m_portfolio_command_source` DISABLE KEYS */; /*!40000 ALTER TABLE `m_portfolio_command_source` DISABLE KEYS */;
INSERT INTO `m_portfolio_command_source` VALUES (1,'CREATE','CLIENT',2,NULL,1,NULL,NULL,'/clients/template',1,NULL,'{\"officeId\":\"2\",\"firstname\":\"Test\",\"middlename\":\"\",\"lastname\":\"One\",\"fullname\":\"\",\"externalId\":\"\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"joinedDate\":\"2011-02-01\"}',1,'2013-04-05 12:38:05',NULL,NULL,1),(2,'CREATE','LOAN',2,NULL,1,1,NULL,'/loans',1,NULL,'{\"clientId\":\"1\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"productId\":\"1\",\"loanOfficerId\":\"2\",\"submittedOnDate\":\"2011-04-01\",\"loanPurposeId\":\"25\",\"principal\":\"1,000,000.00\",\"loanTermFrequency\":\"12\",\"loanTermFrequencyType\":\"2\",\"numberOfRepayments\":\"12\",\"repaymentEvery\":\"1\",\"repaymentFrequencyType\":\"2\",\"interestRatePerPeriod\":\"24\",\"interestRateFrequencyType\":\"3\",\"expectedDisbursementDate\":\"2011-04-01\",\"amortizationType\":\"1\",\"interestType\":\"1\",\"interestCalculationPeriodType\":\"1\",\"inArrearsTolerance\":\"\",\"transactionProcessingStrategyId\":\"2\",\"repaymentsStartingFromDate\":\"\",\"interestChargedFromDate\":\"\",\"charges\":[{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-05-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-06-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-07-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-08-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-09-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-10-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-11-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-12-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-01-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-02-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-03-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-04-01\"}],\"collateral\":[{\"id\":\"\",\"type\":\"23\",\"description\":\" small description.\",\"value\":\"\"}]}',1,'2013-04-05 12:41:57',NULL,NULL,1),(3,'APPROVE','LOAN',2,NULL,1,1,NULL,'/loans/1',1,NULL,'{\"status\":{\"id\":200,\"code\":\"loanStatusType.approved\",\"value\":\"Approved\",\"pendingApproval\":false,\"waitingForDisbursal\":true,\"active\":false,\"closedObligationsMet\":false,\"closedWrittenOff\":false,\"closedRescheduled\":false,\"closed\":false,\"overpaid\":false},\"locale\":\"en\",\"dateFormat\":\"yyyy-MM-dd\",\"approvedOnDate\":\"2011-04-01\"}',1,'2013-04-05 12:42:09',NULL,NULL,1); INSERT INTO `m_portfolio_command_source` VALUES (1,'CREATE','CLIENT',2,NULL,1,NULL,NULL,'/clients/template',1,NULL,'{\"officeId\":\"2\",\"firstname\":\"Test\",\"middlename\":\"\",\"lastname\":\"One\",\"fullname\":\"\",\"externalId\":\"\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"joinedDate\":\"2011-02-01\"}',1,'2013-04-05 12:38:05',NULL,NULL,1),(2,'CREATE','LOAN',2,NULL,1,1,NULL,'/loans',1,NULL,'{\"clientId\":\"1\",\"dateFormat\":\"yyyy-MM-dd\",\"locale\":\"en\",\"productId\":\"1\",\"loanOfficerId\":\"2\",\"submittedOnDate\":\"2011-04-01\",\"loanPurposeId\":\"25\",\"principal\":\"1,000,000.00\",\"loanTermFrequency\":\"12\",\"loanTermFrequencyType\":\"2\",\"numberOfRepayments\":\"12\",\"repaymentEvery\":\"1\",\"repaymentFrequencyType\":\"2\",\"interestRatePerPeriod\":\"24\",\"interestRateFrequencyType\":\"3\",\"expectedDisbursementDate\":\"2011-04-01\",\"amortizationType\":\"1\",\"interestType\":\"1\",\"interestCalculationPeriodType\":\"1\",\"inArrearsTolerance\":\"\",\"transactionProcessingStrategy\":\"heavensfamily-strategy\",\"repaymentsStartingFromDate\":\"\",\"interestChargedFromDate\":\"\",\"charges\":[{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-05-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-06-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-07-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-08-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-09-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-10-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-11-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2011-12-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-01-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-02-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-03-01\"},{\"chargeId\":\"1\",\"amount\":\"1,500\",\"dueDate\":\"2012-04-01\"}],\"collateral\":[{\"id\":\"\",\"type\":\"23\",\"description\":\" small description.\",\"value\":\"\"}]}',1,'2013-04-05 12:41:57',NULL,NULL,1),(3,'APPROVE','LOAN',2,NULL,1,1,NULL,'/loans/1',1,NULL,'{\"status\":{\"id\":200,\"code\":\"loanStatusType.approved\",\"value\":\"Approved\",\"pendingApproval\":false,\"waitingForDisbursal\":true,\"active\":false,\"closedObligationsMet\":false,\"closedWrittenOff\":false,\"closedRescheduled\":false,\"closed\":false,\"overpaid\":false},\"locale\":\"en\",\"dateFormat\":\"yyyy-MM-dd\",\"approvedOnDate\":\"2011-04-01\"}',1,'2013-04-05 12:42:09',NULL,NULL,1);
/*!40000 ALTER TABLE `m_portfolio_command_source` ENABLE KEYS */; /*!40000 ALTER TABLE `m_portfolio_command_source` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
@@ -1435,9 +1433,7 @@ CREATE TABLE `m_product_loan` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unq_name` (`name`), UNIQUE KEY `unq_name` (`name`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@@ -1709,36 +1705,6 @@ INSERT INTO `r_enum_value` VALUES ('amortization_method_enum',0,'Equal principle
/*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */; /*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `ref_loan_transaction_processing_strategy`
--
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = UTF8MB4 */;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` BIGINT DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` BIGINT DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `ref_loan_transaction_processing_strategy`
--
LOCK TABLES `ref_loan_transaction_processing_strategy` WRITE;
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` VALUES (2,'heavensfamily-strategy','Heavensfamily',NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `rpt_sequence` -- Table structure for table `rpt_sequence`
-- --
@@ -17,8 +17,6 @@
-- under the License. -- under the License.
-- --
DELETE FROM `ref_loan_transaction_processing_strategy` WHERE id in (1, 3, 4);
INSERT INTO `m_code` (`code_name`, `is_system_defined`) INSERT INTO `m_code` (`code_name`, `is_system_defined`)
VALUES VALUES
('FieldOfEmployment', '0'), ('FieldOfEmployment', '0'),
@@ -1012,7 +1012,6 @@ CREATE TABLE `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -1028,7 +1027,6 @@ CREATE TABLE `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -1507,9 +1505,7 @@ CREATE TABLE `m_product_loan` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unq_name` (`name`), UNIQUE KEY `unq_name` (`name`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@@ -1771,36 +1767,6 @@ INSERT INTO `r_enum_value` VALUES ('amortization_method_enum',0,'Equal principle
/*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */; /*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `ref_loan_transaction_processing_strategy`
--
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = UTF8MB4 */;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` BIGINT DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` BIGINT DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `ref_loan_transaction_processing_strategy`
--
LOCK TABLES `ref_loan_transaction_processing_strategy` WRITE;
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` VALUES (1,'mifos-standard-strategy','Mifos style',NULL,NULL,NULL,NULL),(2,'heavensfamily-strategy','Heavensfamily',NULL,NULL,NULL,NULL),(3,'creocore-strategy','Creocore',NULL,NULL,NULL,NULL),(4,'rbi-india-strategy','RBI (India)',NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `rpt_sequence` -- Table structure for table `rpt_sequence`
-- --
@@ -859,7 +859,6 @@ CREATE TABLE `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loan_guarantor` (`guarantor_id`), KEY `FK_m_loan_guarantor` (`guarantor_id`),
@@ -867,7 +866,6 @@ CREATE TABLE `m_loan` (
CONSTRAINT `FK7C885877240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`), CONSTRAINT `FK7C885877240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FKB6F935D87179A0CB` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`), CONSTRAINT `FKB6F935D87179A0CB` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`),
CONSTRAINT `FKB6F935D8C8D4B434` FOREIGN KEY (`product_id`) REFERENCES `m_product_loan` (`id`), CONSTRAINT `FKB6F935D8C8D4B434` FOREIGN KEY (`product_id`) REFERENCES `m_product_loan` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `m_loan_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`) CONSTRAINT `m_loan_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
@@ -1343,9 +1341,7 @@ CREATE TABLE `m_product_loan` (
`loan_transaction_strategy_id` BIGINT DEFAULT NULL, `loan_transaction_strategy_id` BIGINT DEFAULT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@@ -1687,36 +1683,6 @@ INSERT INTO `r_enum_value` VALUES ('amortization_method_enum',0,'Equal principle
/*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */; /*!40000 ALTER TABLE `r_enum_value` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `ref_loan_transaction_processing_strategy`
--
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = UTF8MB4 */;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` BIGINT DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` BIGINT DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=UTF8MB4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `ref_loan_transaction_processing_strategy`
--
LOCK TABLES `ref_loan_transaction_processing_strategy` WRITE;
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` VALUES (1,'mifos-standard-strategy','Mifos style',NULL,NULL,NULL,NULL),(2,'heavensfamily-strategy','Heavensfamily',NULL,NULL,NULL,NULL),(3,'creocore-strategy','Creocore',NULL,NULL,NULL,NULL),(4,'rbi-india-strategy','RBI (India)',NULL,NULL,NULL,NULL);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
UNLOCK TABLES;
-- --
-- Table structure for table `rpt_sequence` -- Table structure for table `rpt_sequence`
-- --
@@ -68,7 +68,6 @@ DROP TABLE IF EXISTS `m_savings_account`;
DROP TABLE IF EXISTS `m_savings_account_transaction`; DROP TABLE IF EXISTS `m_savings_account_transaction`;
DROP TABLE IF EXISTS `m_savings_product`; DROP TABLE IF EXISTS `m_savings_product`;
DROP TABLE IF EXISTS `m_staff`; DROP TABLE IF EXISTS `m_staff`;
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
DROP TABLE IF EXISTS `x_registered_table`; DROP TABLE IF EXISTS `x_registered_table`;
-- drop reporting related tables -- drop reporting related tables
@@ -102,18 +101,6 @@ CREATE TABLE `m_organisation_currency` (
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` BIGINT DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` BIGINT DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
CREATE TABLE `c_configuration` ( CREATE TABLE `c_configuration` (
`id` BIGINT NOT NULL AUTO_INCREMENT, `id` BIGINT NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL, `name` varchar(50) DEFAULT NULL,
@@ -482,9 +469,7 @@ CREATE TABLE `m_product_loan` (
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `unq_name` (`name`), UNIQUE KEY `unq_name` (`name`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
CREATE TABLE `m_product_loan_charge` ( CREATE TABLE `m_product_loan_charge` (
@@ -575,7 +560,6 @@ CREATE TABLE `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -591,7 +575,6 @@ CREATE TABLE `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -19,14 +19,6 @@
-- currency symbols may not apply through command line on windows so use a different client like mysql workbench -- currency symbols may not apply through command line on windows so use a different client like mysql workbench
INSERT INTO `ref_loan_transaction_processing_strategy`
(`id`,`code`,`name`)
VALUES
(1, 'mifos-standard-strategy', 'Mifos style'),
(2, 'heavensfamily-strategy', 'Heavensfamily'),
(3, 'creocore-strategy', 'Creocore'),
(4, 'rbi-india-strategy', 'RBI (India)');
INSERT INTO `c_configuration` INSERT INTO `c_configuration`
(`name`, `enabled`) (`name`, `enabled`)
VALUES VALUES
@@ -1,91 +1,5 @@
= Modules = Modules
NOTE: Currently modules are a proof of concept feature in Fineract. We are currently working towards a fully modular codebase and will publish more here when we are ready.
== Custom modules NOTE: Even if we are not quite there yet with full modularity you can already create your own custom modules to extend Fineract. Please see chapter <<_custom_modules>>.
Creating customizations for Fineract services is easy. The method described here will work both with our future module guidelines (aka "clean room" modules) and with the intermediary solution we will put in place to avoid major refactorings.
The folder structure for modules is based on a convention that ensures that your extensions don't clash with Fineract's internals. This is to make sure that your downstream forks of Fineract are easy to sync. In the past we had all kinds of strategies to add custom code - including editing existing sources in `fineract-provider`. This is not recommended.
IMPORTANT: At the moment the only service(s) we prepared to be overridden/replaced are `org.apache.fineract.portfolio.note.service.NoteReadPlatformService` and `org.apache.fineract.portfolio.note.service.NoteWritePlatformService`. Please reach out on the developer mailing list if you need other services.
The recommended folder structure is very simple. If you follow this recommendation you'll get some additional benefits, e. g. you don't even have to edit `settings.gradle` to include your new custom modules. Your modules will also be automatically included in a custom Fineract Docker image build that you can use for your production deployments.
Let's assume your company/org is called "ACME Inc." and you are trying to (fully/partially) replace an existing Fineract service, let's say those in `org.apache.fineract.portfolio.note`. The recommended folder structure would then look something like this:
[plantuml, format=svg]
----
include::{diagramsdir}/custom-folder-structure.puml[]
----
As soon as we can publish Fineract module JARs to Maven Central you'll have more freedom to setup your projects (including to setup separate Git repos). But for now please follow these instructions:
1. Create a folder under `custom` and name it according to your company/organisation (e. g. `acme` if your company is `ACME Inc.`); this way your custom modules can't clash even with other companies' modules
2. Under your company folder create a folder for the `category` or `domain` your module is targeting; e. g. "loan", "client", "account" etc.
3. Finally, setup `library` folders for the actual modules you want to create; usually that will be to replace/extend some existing service, so there could be a `service` folder, maybe even a `core` folder, e. g. if you want to add additional DTOs etc.; we have also an example for COB business steps
4. Per `category`/`domain` you should have a `starter` library; means: a Spring Boot auto-configuration setup that makes including your module in Fineract easier ("hands-free"); the necessary parts for a auto-configuration library are a Spring Java configuration class (annotated with `@Configuration`) and a text file at `META-INF/spring.factories` in your starter resource folder:
+
[source,properties]
----
include::{rootdir}/custom/acme/note/starter/src/main/resources/META-INF/spring.factories[]
----
+
Please make sure that your module libraries have proper `build.gradle` files:
+
[source,groovy]
----
include::{rootdir}/custom/acme/note/service/build.gradle[lines=19..]
----
+
NOTE: You don't need to edit `settings.gradle` to add your modules/libraries. If you follow above convention they'll get included automatically.
+
5. The dependency.gradle file could look something like this:
[source,groovy]
----
include::{rootdir}/custom/acme/note/service/dependencies.gradle[lines=19..]
----
NOTE: We've included by default some basic and useful dependencies for all custom modules, like Slf4j, Lombok, the usual testing frameworks (JUnit, Cucumber, Mockito etc.)
IMPORTANT: Do not include your custom module in `fineract-provider`'s dependency.gradle file. This creates a circular dependency and will fail your build.
=== Database Migrations
If database migrations are needed as part of your customizations then you can add your own migration scripts. This is again based on conventions:
1. Create folders `db/custom-changelog` in one of your `resources` folders; we recommend using the resources folder in your starter library, but actually any of your custom libs will do.
2. Under `db/custom-changelog` create an XML changelog file, e. g. `changelog-acme-note.xml`; you are free to choose a name for this file, but we recommend being consistent to avoid classpath conflicts.
3. Under `db/custom-changelog` create a folder `parts` for your specific changelogs
[plantuml, format=svg]
----
include::{diagramsdir}/custom-db-migration-folder-structure.puml[]
----
And here an example migration script:
[source,xml]
----
include::{rootdir}/custom/acme/note/starter/src/main/resources/db/custom-changelog/0001_acme_note_initial.xml[lines=22..]
----
IMPORTANT: By default custom database migration changelogs are executed in context `tenant_db`. That makes sure your changes will be applied to the tenant database (read: main database and not the tenant store database). In theory you could also target the tenant configuration database, but it's not recommended to do that.
=== Deployment
Custom modules (better: the JAR files) only need to be dropped in Fineract's `libs` folder if you run Fineract from the Spring Boot JAR file. Dynamic loading of external JARs is provided since Fineract version 1.5.0. For your convenience we've created a separate Docker image module that automatically includes your custom modules (see `custom/docker`). You can build this Docker image with
[source,bash]
----
./gradlew :custom:docker:jibDockerBuild
----
The Docker image with included custom modules is called `fineract-custom`.
NOTE: We'll provide soon a way to customize the Docker image parameters (image name, JVM implementation, JVM args, ports etc.).
=== Outlook
If this *proof of concept* is accepted we could prepare Fineract's services to be replaceable. This approach works already very well even if we don't have proper JAR libraries published on Maven Central. It's an important goal to separate customized code from Fineract's internals to have soon real modules.
@@ -0,0 +1,36 @@
= Custom Business Steps
It very easy to add your own business steps to Fineract's default steps:
1. Create a custom module (e. g. `custom/acme/steps`, follow the instructions on how to create a custom module)
2. Create a class that implements interace `org.apache.fineract.cob.COBBusinessStep`
3. Provide the custom database migration to add the necessary information about your business step in table `m_batch_business_steps`
.Business Step Interface
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/cob/COBBusinessStep.java[lines=19..]
----
== Business Step Implementation
.Custom Business Step Implementation Example
[source,java]
----
include::{rootdir}/custom/acme/loan/cob/src/main/java/com/acme/fineract/loan/cob/AcmeNoopBusinessStep.java[lines=19..]
----
As you can see this implementation is very simple and doesn't do much. There are some simple conventions though that you should follow implementing your own business steps:
1. Make sure the value returned by method `getEnumStyledName()` is unique; it's a good idea to choose a prefix that reflects the name of your organization (in this example `ACME_`)
2. You have more freedom for the value returned by `getHumanReadableName()`, but it's a good idea to keep this value as unique as possible
== Business Step Database Migration
.Business Step Database Migration Example
[source,xml]
----
include::{rootdir}/custom/acme/loan/cob/src/main/resources/db/custom-changelog/0001_acme_loan_cob.xml[lines=22..]
----
NOTE: See also chapter about batch jobs in this documentation.
@@ -0,0 +1,21 @@
= Custom Database Migration
If database migrations are needed as part of your customizations then you can add your own migration scripts. This is again based on conventions:
1. Create folders `db/custom-changelog` in one of your `resources` folders; we recommend using the resources folder in your starter library, but actually any of your custom libs will do.
2. Under `db/custom-changelog` create an XML changelog file, e. g. `changelog-acme-note.xml`; you are free to choose a name for this file, but we recommend being consistent to avoid classpath conflicts.
3. Under `db/custom-changelog` create a folder `parts` for your specific changelogs
[plantuml, format=svg]
----
include::{diagramsdir}/custom-db-migration-folder-structure.puml[]
----
And here an example migration script:
[source,xml]
----
include::{rootdir}/custom/acme/note/starter/src/main/resources/db/custom-changelog/0001_acme_note_initial.xml[lines=22..]
----
IMPORTANT: By default, custom database migration changelogs are executed in context `tenant_db`. That makes sure your changes will be applied to the tenant database (read: main database and not the tenant store database). In theory you could also target the tenant configuration database, but it's not recommended to do that.
@@ -0,0 +1,12 @@
= Deploying Custom Modules
Custom modules (better: the JAR files) only need to be dropped in Fineract's `libs` folder if you run Fineract from the Spring Boot JAR file. Dynamic loading of external JARs is provided since Fineract version 1.5.0. For your convenience we've created a separate Docker image module that automatically includes your custom modules (see `custom/docker`). You can build this Docker image with
[source,bash]
----
./gradlew :custom:docker:jibDockerBuild
----
The Docker image with included custom modules is called `fineract-custom`.
NOTE: We'll provide soon a way to customize the Docker image parameters (image name, JVM implementation, JVM args, ports etc.).
@@ -0,0 +1,17 @@
= Custom Modules
NOTE: Currently modules are a proof of concept feature in Fineract.
include::intro.adoc[leveloffset=+1]
include::service.adoc[leveloffset=+1]
include::business-step.adoc[leveloffset=+1]
include::loan-transaction-processor.adoc[leveloffset=+1]
include::database-migration.adoc[leveloffset=+1]
include::deployment.adoc[leveloffset=+1]
include::outlook.adoc[leveloffset=+1]
@@ -0,0 +1,47 @@
= Introduction
Creating customizations for Fineract services is easy. The method described here will work both with our future module guidelines (aka "clean room" modules) and with the intermediary solution we will put in place to avoid major refactorings.
The folder structure for modules is based on a convention that ensures that your extensions don't clash with Fineract's internals. This is to make sure that your downstream forks of Fineract are easy to sync. In the past we had all kinds of strategies to add custom code - including editing existing sources in `fineract-provider`. This is not recommended.
IMPORTANT: At the moment the only service(s) we prepared to be overridden/replaced are `org.apache.fineract.portfolio.note.service.NoteReadPlatformService` and `org.apache.fineract.portfolio.note.service.NoteWritePlatformService`. Please reach out on the developer mailing list if you need other services.
The recommended folder structure is very simple. If you follow this recommendation you'll get some additional benefits, e. g. you don't even have to edit `settings.gradle` to include your new custom modules. Your modules will also be automatically included in a custom Fineract Docker image build that you can use for your production deployments.
Let's assume your company/org is called "ACME Inc." and you are trying to (fully/partially) replace an existing Fineract service, let's say those in `org.apache.fineract.portfolio.note`. The recommended folder structure would then look something like this:
[plantuml, format=svg]
----
include::{diagramsdir}/custom-folder-structure.puml[]
----
As soon as we can publish Fineract module JARs to Maven Central you'll have more freedom to setup your projects (including to setup separate Git repos). But for now please follow these instructions:
1. Create a folder under `custom` and name it according to your company/organisation (e. g. `acme` if your company is `ACME Inc.`); this way your custom modules can't clash even with other companies' modules
2. Under your company folder create a folder for the `category` or `domain` your module is targeting; e. g. "loan", "client", "account" etc.
3. Finally, setup `library` folders for the actual modules you want to create; usually that will be to replace/extend some existing service, so there could be a `service` folder, maybe even a `core` folder, e. g. if you want to add additional DTOs etc.; we have also an example for COB business steps
4. Per `category`/`domain` you should have a `starter` library; means: a Spring Boot auto-configuration setup that makes including your module in Fineract easier ("hands-free"); the necessary parts for a auto-configuration library are a Spring Java configuration class (annotated with `@Configuration`) and a text file at `META-INF/spring.factories` in your starter resource folder:
+
[source,properties]
----
include::{rootdir}/custom/acme/note/starter/src/main/resources/META-INF/spring.factories[]
----
+
Please make sure that your module libraries have proper `build.gradle` files:
+
[source,groovy]
----
include::{rootdir}/custom/acme/note/service/build.gradle[lines=19..]
----
+
NOTE: You don't need to edit `settings.gradle` to add your modules/libraries. If you follow above convention they'll get included automatically.
+
5. The dependency.gradle file could look something like this:
[source,groovy]
----
include::{rootdir}/custom/acme/note/service/dependencies.gradle[lines=19..]
----
NOTE: We've included by default some basic and useful dependencies for all custom modules, like Slf4j, Lombok, the usual testing frameworks (JUnit, Cucumber, Mockito etc.)
IMPORTANT: Do not include your custom module in `fineract-provider`'s dependency.gradle file. This creates a circular dependency and will fail your build.
@@ -0,0 +1,114 @@
= Custom Loan Transaction Processors
Fineract has 7 built-in loan transaction processors:
1. `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor`
2. `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor`
3. `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor`
4. `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor`
5. `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor`
6. `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor`
7. `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor`
.Default Loan Transaction Processor configuration
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java[lines=38..80]
----
All default processor implementations are enabled by default, but can also be prevented from being loaded into memory by a simple configuration in `application.properties`. Use the environment variables you see below in your Kubernetes and Docker Compose deployments to override the default behavior.
.Default Loan Transaction Processor Application Properties
[source,properties]
----
include::{rootdir}/fineract-provider/src/main/resources/application.properties[lines=64..70]
----
== Implement Processors
.Loan Transaction Processor Interface
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java[lines=19..]
----
.Custom Loan Transaction Processor Example
[source,java]
----
include::{rootdir}/custom/acme/loan/processor/src/main/java/com/acme/fineract/loan/processor/AcmeLoanRepaymentScheduleTransactionProcessor.java[lines=19..]
----
The example implementation doesn't do much. We are just overriding one of the default processor implementations `org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor` and give the custom processor it's own lookup code and name (descriptive text for display in UIs, e. g. when configuring a loan product). As usual it is a good idea to follow some simple conventions:
1. Make sure the value returned by `getCode()` is unique. Prefixing it with characters that reflect your organization name (here `acme-`) is a good idea.
2. You have more freedom for the descriptive test returned by `getName()`, but it is still a good idea to keep the value unique to avoid confusion.
=== Method `getCode()`
Lookup value that is used to pick a loan transaction processor (see processor factory).
=== Method `getName()`
Descriptive text about the loan transaction processor that is mostly used in user interfaces.
=== Method `handleTransaction()`
TBD
=== Method `handleWriteOff()`
TBD
=== Method `handleRepaymentSchedule()`
TBD
=== Method `isInterestFirstRepaymentScheduleTransactionProcessor()`
TBD
=== Method `handleRefund()`
TBD
=== Method `handleChargeback()`
TBD
=== Method `processTransactionsFromDerivedFields()`
TBD
== Override Processor Factory
The processor factory has no reference to any specific implementation of the loan transaction processor interface. All avaible implementations will be injected here (internal default and custom implementations). Processor instances can be looked up via method `determineProcessor()`. You can pass either the code of the processor or the processor's name to look it up. If a matching processor can't be found then the factory function will either return the default instance or fails with an exception depending on the configuration in `application.properties`.
IMPORTANT: It is preferable to use the processor code to lookup processor instances. Lookups via processor names are only done in the import service via Excel sheets (should be fixed).
.Loan Transaction Processor Factory Implementation
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessorFactory.java[lines=19..]
----
This is the default factory auto-configuration.
.Loan Transaction Processor Factory Auto-Configuration
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java[lines=81..87]
----
If you need then you can override this, e.g. because you want to set a different default processor then you can do so in your custom module's auto-configuration.
.Custom Loan Transaction Processor Factory Auto-Configuration Example
[source,java]
----
include::{rootdir}/custom/acme/loan/starter/src/main/java/com/acme/fineract/loan/starter/AcmeLoanAutoConfiguration.java[lines=36..41]
----
.Processor Lookup Failure Configuration Property
[source,properties]
----
include::{rootdir}/fineract-provider/src/main/resources/application.properties[lines=71..71]
----
@@ -0,0 +1,3 @@
= Outlook
If this *proof of concept* is accepted we could prepare more of Fineract's internal services to be replaceable. This approach works already very well even if we don't have proper JAR libraries published on Maven Central. It's an important goal to separate customized code from Fineract's internals to have soon real modules.
@@ -0,0 +1,29 @@
= Note Service
The Note service is responsible for ... TBD
NOTE: We chose the note service because it's interface is very simple and has not many cross dependencies.
== Interfaces
.Note Read Service Interface
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteReadPlatformService.java[lines=19..]
----
.Note Write Service Interface
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteReadPlatformService.java[lines=19..]
----
== Auto Start Configuration
The rules to replace the Note services are very simple. If you provide an alternative implementation of the services then the default implementations will *not* be loaded.
.Note Auto Starter Configuration
[source,java]
----
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/starter/NoteAutoConfiguration.java[lines=19..]
----
@@ -0,0 +1,5 @@
= Custom Services
NOTE: We are still trying to figure out which internal services make most sense to be pluggable. Please join the discussion and let us know if you have a specific requirement.
include::service-note.adoc[leveloffset=+1]
+4
View File
@@ -19,6 +19,7 @@
| Michael Vorburger | mailto:mike@vorburger.ch[] | Michael Vorburger | mailto:mike@vorburger.ch[]
| Aleksandar Vidakovic | mailto:aleks@apache.org[] | Aleksandar Vidakovic | mailto:aleks@apache.org[]
| Arnold Galovics | mailto:arnold@apache.org[] | Arnold Galovics | mailto:arnold@apache.org[]
| Adam Saghy | mailto:adamsaghy@gmail.com[]
|=== |===
//ifdef::backend-pdf[] //ifdef::backend-pdf[]
@@ -34,7 +35,10 @@
| Arnold Galovics | Database architecture section | 2022-03-11 | 1.7.0 | Arnold Galovics | Database architecture section | 2022-03-11 | 1.7.0
| Aleksandar Vidakovic | Module section | 2022-03-23 | 1.7.0 | Aleksandar Vidakovic | Module section | 2022-03-23 | 1.7.0
| Aleksandar Vidakovic | Release section, file re-organisation | 2022-04-18 | 1.7.0 | Aleksandar Vidakovic | Release section, file re-organisation | 2022-04-18 | 1.7.0
| Arnold Galovics | Spring Batch | 2022-03-11 | 1.7.0
| Aleksandar Vidakovic | AsciiDoc cheatsheet | 2022-05-09 | 1.7.0 | Aleksandar Vidakovic | AsciiDoc cheatsheet | 2022-05-09 | 1.7.0
| Adam Saghy | Business data | 2022-06-13 | 1.7.0
| Aleksandar Vidakovic | Custom modules and extensions | 2022-10-29 | 1.8.0
|=== |===
toc::[] toc::[]
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

+2
View File
@@ -37,6 +37,8 @@ include::chapters/architecture/index.adoc[leveloffset=+1]
include::chapters/development/index.adoc[leveloffset=+1] include::chapters/development/index.adoc[leveloffset=+1]
include::chapters/custom/index.adoc[leveloffset=+1]
include::chapters/security/index.adoc[leveloffset=+1] include::chapters/security/index.adoc[leveloffset=+1]
include::chapters/testing/index.adoc[leveloffset=+1] include::chapters/testing/index.adoc[leveloffset=+1]
+1 -1
View File
@@ -152,7 +152,7 @@ configurations {
dependencies { dependencies {
driver 'org.mariadb.jdbc:mariadb-java-client:3.0.8' driver 'org.mariadb.jdbc:mariadb-java-client:3.0.8'
driver 'org.postgresql:postgresql:42.5.0' driver 'org.postgresql:postgresql:42.5.0'
driver 'mysql:mysql-connector-java:8.0.30' driver 'mysql:mysql-connector-java:8.0.31'
} }
URLClassLoader loader = GroovyObject.class.classLoader URLClassLoader loader = GroovyObject.class.classLoader
@@ -28,6 +28,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
@@ -48,28 +50,25 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
import org.apache.fineract.portfolio.loanaccount.data.LoanCollateralManagementData; import org.apache.fineract.portfolio.loanaccount.data.LoanCollateralManagementData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollateralQuantity; import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollateralQuantity;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service @Service
public class LoanImportHandler implements ImportHandler { public class LoanImportHandler implements ImportHandler {
public static final String EMPTY_STR = ""; public static final String EMPTY_STR = "";
private static final Logger LOG = LoggerFactory.getLogger(LoanImportHandler.class);
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
public LoanImportHandler(final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
}
@Override @Override
public Count process(final Workbook workbook, final String locale, final String dateFormat) { public Count process(final Workbook workbook, final String locale, final String dateFormat) {
@@ -234,24 +233,17 @@ public class LoanImportHandler implements ImportHandler {
if (ImportHandlerUtils.readAsDouble(LoanConstants.ARREARS_TOLERANCE_COL, row) != null) { if (ImportHandlerUtils.readAsDouble(LoanConstants.ARREARS_TOLERANCE_COL, row) != null) {
arrearsTolerance = BigDecimal.valueOf(ImportHandlerUtils.readAsDouble(LoanConstants.ARREARS_TOLERANCE_COL, row)); arrearsTolerance = BigDecimal.valueOf(ImportHandlerUtils.readAsDouble(LoanConstants.ARREARS_TOLERANCE_COL, row));
} }
String repaymentStrategy = ImportHandlerUtils.readAsString(LoanConstants.REPAYMENT_STRATEGY_COL, row);
Long repaymentStrategyId = null; String loanRepaymentScheduleTransactionProcessorStrategy = ImportHandlerUtils.readAsString(LoanConstants.REPAYMENT_STRATEGY_COL,
if (repaymentStrategy != null) { row);
if (repaymentStrategy.equalsIgnoreCase("Penalties, Fees, Interest, Principal order")) {
repaymentStrategyId = 1L; LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
} else if (repaymentStrategy.equalsIgnoreCase("HeavensFamily Unique")) { .determineProcessor(loanRepaymentScheduleTransactionProcessorStrategy);
repaymentStrategyId = 2L;
} else if (repaymentStrategy.equalsIgnoreCase("Creocore Unique")) { String repaymentStrategyCode = "mifos-standard-strategy";
repaymentStrategyId = 3L;
} else if (repaymentStrategy.equalsIgnoreCase("Overdue/Due Fee/Int,Principal")) { if (loanRepaymentScheduleTransactionProcessor != null) {
repaymentStrategyId = 4L; repaymentStrategyCode = loanRepaymentScheduleTransactionProcessor.getCode();
} else if (repaymentStrategy.equalsIgnoreCase("Principal, Interest, Penalties, Fees Order")) {
repaymentStrategyId = 5L;
} else if (repaymentStrategy.equalsIgnoreCase("Interest, Principal, Penalties, Fees Order")) {
repaymentStrategyId = 6L;
} else if (repaymentStrategy.equalsIgnoreCase("Early Repayment Strategy")) {
repaymentStrategyId = 7L;
}
} }
Integer graceOnPrincipalPayment = ImportHandlerUtils.readAsInt(LoanConstants.GRACE_ON_PRINCIPAL_PAYMENT_COL, row); Integer graceOnPrincipalPayment = ImportHandlerUtils.readAsInt(LoanConstants.GRACE_ON_PRINCIPAL_PAYMENT_COL, row);
Integer graceOnInterestPayment = ImportHandlerUtils.readAsInt(LoanConstants.GRACE_ON_INTEREST_PAYMENT_COL, row); Integer graceOnInterestPayment = ImportHandlerUtils.readAsInt(LoanConstants.GRACE_ON_INTEREST_PAYMENT_COL, row);
@@ -355,7 +347,7 @@ public class LoanImportHandler implements ImportHandler {
return LoanAccountData.importInstanceIndividual(loanTypeEnumOption, clientId, productId, loanOfficerId, submittedOnDate, return LoanAccountData.importInstanceIndividual(loanTypeEnumOption, clientId, productId, loanOfficerId, submittedOnDate,
fundId, principal, numberOfRepayments, repaidEvery, repaidEveryFrequencyEnums, loanTerm, loanTermFrequencyEnum, fundId, principal, numberOfRepayments, repaidEvery, repaidEveryFrequencyEnums, loanTerm, loanTermFrequencyEnum,
nominalInterestRate, submittedOnDate, amortizationEnumOption, interestMethodEnum, interestCalculationPeriodEnum, nominalInterestRate, submittedOnDate, amortizationEnumOption, interestMethodEnum, interestCalculationPeriodEnum,
arrearsTolerance, repaymentStrategyId, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, arrearsTolerance, repaymentStrategyCode, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
interestChargedFromDate, firstRepaymentOnDate, row.getRowNum(), externalId, null, charges, linkAccountId, locale, interestChargedFromDate, firstRepaymentOnDate, row.getRowNum(), externalId, null, charges, linkAccountId, locale,
dateFormat, loanCollateralManagementData); dateFormat, loanCollateralManagementData);
} else if (loanType.equals("jlg")) { } else if (loanType.equals("jlg")) {
@@ -364,7 +356,7 @@ public class LoanImportHandler implements ImportHandler {
return LoanAccountData.importInstanceIndividual(loanTypeEnumOption, clientId, productId, loanOfficerId, submittedOnDate, return LoanAccountData.importInstanceIndividual(loanTypeEnumOption, clientId, productId, loanOfficerId, submittedOnDate,
fundId, principal, numberOfRepayments, repaidEvery, repaidEveryFrequencyEnums, loanTerm, loanTermFrequencyEnum, fundId, principal, numberOfRepayments, repaidEvery, repaidEveryFrequencyEnums, loanTerm, loanTermFrequencyEnum,
nominalInterestRate, submittedOnDate, amortizationEnumOption, interestMethodEnum, interestCalculationPeriodEnum, nominalInterestRate, submittedOnDate, amortizationEnumOption, interestMethodEnum, interestCalculationPeriodEnum,
arrearsTolerance, repaymentStrategyId, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, arrearsTolerance, repaymentStrategyCode, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
interestChargedFromDate, firstRepaymentOnDate, row.getRowNum(), externalId, groupId, charges, linkAccountId, locale, interestChargedFromDate, firstRepaymentOnDate, row.getRowNum(), externalId, groupId, charges, linkAccountId, locale,
dateFormat, null); dateFormat, null);
} else { } else {
@@ -373,7 +365,7 @@ public class LoanImportHandler implements ImportHandler {
return LoanAccountData.importInstanceGroup(loanTypeEnumOption, groupIdforGroupLoan, productId, loanOfficerId, return LoanAccountData.importInstanceGroup(loanTypeEnumOption, groupIdforGroupLoan, productId, loanOfficerId,
submittedOnDate, fundId, principal, numberOfRepayments, repaidEvery, repaidEveryFrequencyEnums, loanTerm, submittedOnDate, fundId, principal, numberOfRepayments, repaidEvery, repaidEveryFrequencyEnums, loanTerm,
loanTermFrequencyEnum, nominalInterestRate, amortizationEnumOption, interestMethodEnum, loanTermFrequencyEnum, nominalInterestRate, amortizationEnumOption, interestMethodEnum,
interestCalculationPeriodEnum, arrearsTolerance, repaymentStrategyId, graceOnPrincipalPayment, interestCalculationPeriodEnum, arrearsTolerance, repaymentStrategyCode, graceOnPrincipalPayment,
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, firstRepaymentOnDate, row.getRowNum(), graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, firstRepaymentOnDate, row.getRowNum(),
externalId, linkAccountId, locale, dateFormat); externalId, linkAccountId, locale, dateFormat);
} }
@@ -426,7 +418,7 @@ public class LoanImportHandler implements ImportHandler {
statusCell.setCellStyle(ImportHandlerUtils.getCellStyle(workbook, IndexedColors.LIGHT_GREEN)); statusCell.setCellStyle(ImportHandlerUtils.getCellStyle(workbook, IndexedColors.LIGHT_GREEN));
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
errorCount++; errorCount++;
LOG.error("Problem occurred in importEntity function", ex); log.error("Problem occurred in importEntity function", ex);
errorMessage = ImportHandlerUtils.getErrorMessage(ex); errorMessage = ImportHandlerUtils.getErrorMessage(ex);
writeLoanErrorMessage(workbook, loanId, errorMessage, progressLevel, statusCell, errorReportCell, row); writeLoanErrorMessage(workbook, loanId, errorMessage, progressLevel, statusCell, errorReportCell, row);
} }
@@ -30,8 +30,8 @@ public class LoanComparatorByStatusActive implements Comparator<LoanAccountData>
@Override @Override
public int compare(LoanAccountData o1, LoanAccountData o2) { public int compare(LoanAccountData o1, LoanAccountData o2) {
boolean isData1StatusActive = o1.getStatusStringValue().equals("Active"); boolean isData1StatusActive = o1.getStatus() != null && o1.getStatus().getValue().equals("Active");
boolean isData2StatusActive = o2.getStatusStringValue().equals("Active"); boolean isData2StatusActive = o2.getStatus() != null && o2.getStatus().getValue().equals("Active");
// if both status active, these have the same rank // if both status active, these have the same rank
if (isData1StatusActive && isData2StatusActive) { if (isData1StatusActive && isData2StatusActive) {
@@ -156,7 +156,7 @@ public class GuarantorWorkbookPopulator extends AbstractWorkbookPopulator {
} }
writeString(GuarantorConstants.LOOKUP_CLIENT_NAME_COL, row, loan.getClientName() + "(" + loan.getClientId() + ")"); writeString(GuarantorConstants.LOOKUP_CLIENT_NAME_COL, row, loan.getClientName() + "(" + loan.getClientId() + ")");
writeString(GuarantorConstants.LOOKUP_ACCOUNT_NO_COL, row, writeString(GuarantorConstants.LOOKUP_ACCOUNT_NO_COL, row,
Long.parseLong(loan.getAccountNo()) + "-" + loan.getStatusStringValue()); Long.parseLong(loan.getAccountNo()) + "-" + loan.getStatus().getValue());
} }
} }
@@ -234,15 +234,15 @@ public class LoanRepaymentWorkbookPopulator extends AbstractWorkbookPopulator {
row = loanRepaymentSheet.createRow(rowIndex++); row = loanRepaymentSheet.createRow(rowIndex++);
writeString(LoanRepaymentConstants.LOOKUP_CLIENT_NAME_COL, row, loan.getClientName() + "(" + loan.getClientId() + ")"); writeString(LoanRepaymentConstants.LOOKUP_CLIENT_NAME_COL, row, loan.getClientName() + "(" + loan.getClientId() + ")");
writeString(LoanRepaymentConstants.LOOKUP_CLIENT_EXTERNAL_ID, row, clientIdToClientExternalId.get(loan.getClientId())); writeString(LoanRepaymentConstants.LOOKUP_CLIENT_EXTERNAL_ID, row, clientIdToClientExternalId.get(loan.getClientId()));
writeString(LoanRepaymentConstants.LOOKUP_ACCOUNT_NO_COL, row, loan.getAccountNo() + "-" + loan.getStatusStringValue()); writeString(LoanRepaymentConstants.LOOKUP_ACCOUNT_NO_COL, row, loan.getAccountNo() + "-" + loan.getStatus().getValue());
writeString(LoanRepaymentConstants.LOOKUP_PRODUCT_COL, row, loan.getLoanProductName()); writeString(LoanRepaymentConstants.LOOKUP_PRODUCT_COL, row, loan.getLoanProductName());
writeDouble(LoanRepaymentConstants.LOOKUP_PRINCIPAL_COL, row, loan.getPrincipal().doubleValue()); writeDouble(LoanRepaymentConstants.LOOKUP_PRINCIPAL_COL, row, loan.getPrincipal().doubleValue());
if (loan.getTotalOutstandingAmount() != null) { if (loan.getSummary() != null && loan.getSummary().getTotalOutstanding() != null) {
writeBigDecimal(LoanRepaymentConstants.LOOKUP_TOTAL_OUTSTANDING_AMOUNT_COL, row, loan.getTotalOutstandingAmount()); writeBigDecimal(LoanRepaymentConstants.LOOKUP_TOTAL_OUTSTANDING_AMOUNT_COL, row, loan.getSummary().getTotalOutstanding());
} }
if (loan.getDisbursementDate() != null) { if (loan.getTimeline() != null && loan.getTimeline().getDisbursementDate() != null) {
writeDate(LoanRepaymentConstants.LOOKUP_LOAN_DISBURSEMENT_DATE_COL, row, outputFormat.format(loan.getDisbursementDate()), writeDate(LoanRepaymentConstants.LOOKUP_LOAN_DISBURSEMENT_DATE_COL, row,
dateCellStyle, dateFormat); outputFormat.format(loan.getTimeline().getDisbursementDate()), dateCellStyle, dateFormat);
} }
} }
} }
@@ -37,12 +37,14 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException; import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@Primary @Primary
@Component @Component
public class FromJsonHelper { public class FromJsonHelper {
@@ -22,9 +22,15 @@ import org.apache.fineract.avro.loan.v1.LoanAccountDataV1;
import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroMapperConfig; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroMapperConfig;
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData; import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(config = AvroMapperConfig.class) @Mapper(config = AvroMapperConfig.class)
public interface LoanAccountDataMapper { public interface LoanAccountDataMapper {
// TODO: avoid prefix "is" in class attributes; I would recommend to fix this also in the Avro structures
@Mapping(source = "loanProductLinkedToFloatingRate", target = "isLoanProductLinkedToFloatingRate")
@Mapping(source = "floatingInterestRate", target = "isFloatingInterestRate")
@Mapping(source = "topup", target = "isTopup")
@Mapping(source = "interestRecalculationEnabled", target = "isInterestRecalculationEnabled")
LoanAccountDataV1 map(LoanAccountData source); LoanAccountDataV1 map(LoanAccountData source);
} }
@@ -24,8 +24,9 @@ import java.time.LocalTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import lombok.EqualsAndHashCode; import lombok.Data;
import lombok.Getter; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType; import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType;
import org.apache.fineract.portfolio.calendar.domain.CalendarRemindBy; import org.apache.fineract.portfolio.calendar.domain.CalendarRemindBy;
@@ -35,57 +36,55 @@ import org.apache.fineract.portfolio.calendar.service.CalendarEnumerations;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils; import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.NthDayType; import org.apache.fineract.portfolio.common.domain.NthDayType;
/** @Data
* Immutable data object representing a Calendar. @NoArgsConstructor
*/ @Accessors(chain = true)
@Getter
@EqualsAndHashCode
public final class CalendarData implements Serializable { public final class CalendarData implements Serializable {
private final Long id; private Long id;
private final Long calendarInstanceId; private Long calendarInstanceId;
private final Long entityId; private Long entityId;
private final EnumOptionData entityType; private EnumOptionData entityType;
private String title; private String title;
private final String description; private String description;
private final String location; private String location;
private final LocalDate startDate; private LocalDate startDate;
private final LocalDate endDate; private LocalDate endDate;
private final LocalTime meetingTime; private LocalTime meetingTime;
private final Integer duration; private Integer duration;
private final EnumOptionData type; private EnumOptionData type;
private final boolean repeating; private boolean repeating;
private final String recurrence; private String recurrence;
private final EnumOptionData frequency; private EnumOptionData frequency;
private final Integer interval; private Integer interval;
private final EnumOptionData repeatsOnDay; private EnumOptionData repeatsOnDay;
private final EnumOptionData repeatsOnNthDayOfMonth; private EnumOptionData repeatsOnNthDayOfMonth;
private final EnumOptionData remindBy; private EnumOptionData remindBy;
private final Integer firstReminder; private Integer firstReminder;
private final Integer secondReminder; private Integer secondReminder;
private final Collection<LocalDate> recurringDates; private Collection<LocalDate> recurringDates;
private final Collection<LocalDate> nextTenRecurringDates; private Collection<LocalDate> nextTenRecurringDates;
private final String humanReadable; private String humanReadable;
private final LocalDate recentEligibleMeetingDate; private LocalDate recentEligibleMeetingDate;
private final OffsetDateTime createdDate; private OffsetDateTime createdDate;
private final OffsetDateTime lastUpdatedDate; private OffsetDateTime lastUpdatedDate;
private final Long createdByUserId; private Long createdByUserId;
private final String createdByUsername; private String createdByUsername;
private final Long lastUpdatedByUserId; private Long lastUpdatedByUserId;
private final String lastUpdatedByUsername; private String lastUpdatedByUsername;
private final Integer repeatsOnDayOfMonth; private Integer repeatsOnDayOfMonth;
// template related // template related
final List<EnumOptionData> entityTypeOptions; private List<EnumOptionData> entityTypeOptions;
final List<EnumOptionData> calendarTypeOptions; private List<EnumOptionData> calendarTypeOptions;
final List<EnumOptionData> remindByOptions; private List<EnumOptionData> remindByOptions;
final List<EnumOptionData> frequencyOptions; private List<EnumOptionData> frequencyOptions;
final List<EnumOptionData> repeatsOnDayOptions; private List<EnumOptionData> repeatsOnDayOptions;
final List<EnumOptionData> frequencyNthDayTypeOptions; private List<EnumOptionData> frequencyNthDayTypeOptions;
// import fields // import fields
private transient Integer rowIndex; private Integer rowIndex;
private String dateFormat; private String dateFormat;
private String locale; private String locale;
private String centerId; private String centerId;
@@ -49,7 +49,7 @@ public interface LoanApiConstants {
String rejectedOnDateParameterName = "rejectedOnDate"; String rejectedOnDateParameterName = "rejectedOnDate";
String withdrawnOnDateParameterName = "withdrawnOnDate"; String withdrawnOnDateParameterName = "withdrawnOnDate";
String transactionProcessingStrategyIdParameterName = "transactionProcessingStrategyId"; String transactionProcessingStrategyCodeParameterName = "transactionProcessingStrategyCode";
String loanPurposeIdParameterName = "loanPurposeId"; String loanPurposeIdParameterName = "loanPurposeId";
String loanOfficerIdParameterName = "loanOfficerId"; String loanOfficerIdParameterName = "loanOfficerId";
String fundIdParameterName = "fundId"; String fundIdParameterName = "fundId";
@@ -69,6 +69,7 @@ public interface LoanApiConstants {
String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType"; String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType";
String interestTypeParameterName = "interestType"; String interestTypeParameterName = "interestType";
String amortizationTypeParameterName = "amortizationType"; String amortizationTypeParameterName = "amortizationType";
String amortizationTypeOptionsParameterName = "amortizationTypeOptions";
String repaymentFrequencyTypeParameterName = "repaymentFrequencyType"; String repaymentFrequencyTypeParameterName = "repaymentFrequencyType";
String loanTermFrequencyTypeParameterName = "loanTermFrequencyType"; String loanTermFrequencyTypeParameterName = "loanTermFrequencyType";
String loanTermFrequencyParameterName = "loanTermFrequency"; String loanTermFrequencyParameterName = "loanTermFrequency";
@@ -32,6 +32,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -102,6 +103,7 @@ import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.collateralmanagement.data.LoanCollateralResponseData; import org.apache.fineract.portfolio.collateralmanagement.data.LoanCollateralResponseData;
import org.apache.fineract.portfolio.collateralmanagement.service.LoanCollateralManagementReadPlatformService; import org.apache.fineract.portfolio.collateralmanagement.service.LoanCollateralManagementReadPlatformService;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiResourceSwagger; import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiResourceSwagger;
import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData; import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService; import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
@@ -121,6 +123,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData; import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType; import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
import org.apache.fineract.portfolio.loanaccount.exception.LoanTemplateTypeRequiredException; import org.apache.fineract.portfolio.loanaccount.exception.LoanTemplateTypeRequiredException;
import org.apache.fineract.portfolio.loanaccount.exception.NotSupportedLoanTemplateTypeException; import org.apache.fineract.portfolio.loanaccount.exception.NotSupportedLoanTemplateTypeException;
@@ -191,7 +194,7 @@ import org.springframework.util.CollectionUtils;
+ "Example Values: 0=Daily, 1=Same as repayment period\n" + "allowPartialPeriodInterestCalcualtion\n" + "Example Values: 0=Daily, 1=Same as repayment period\n" + "allowPartialPeriodInterestCalcualtion\n"
+ "This value will be supported along with interestCalculationPeriodType as Same as repayment period to calculate interest for partial periods. Example: Interest charged from is 5th of April , Principal is 10000 and interest is 1% per month then the interest will be (10000 * 1%)* (25/30) , it calculates for the month first then calculates exact periods between start date and end date(can be a decimal)\n" + "This value will be supported along with interestCalculationPeriodType as Same as repayment period to calculate interest for partial periods. Example: Interest charged from is 5th of April , Principal is 10000 and interest is 1% per month then the interest will be (10000 * 1%)* (25/30) , it calculates for the month first then calculates exact periods between start date and end date(can be a decimal)\n"
+ "inArrearsTolerance\n" + "The amount that can be 'waived' at end of all loan payments because it is too small to worry about.\n" + "inArrearsTolerance\n" + "The amount that can be 'waived' at end of all loan payments because it is too small to worry about.\n"
+ "This is also the tolerance amount assessed when determining if a loan is in arrears.\n" + "transactionProcessingStrategyId\n" + "This is also the tolerance amount assessed when determining if a loan is in arrears.\n" + "transactionProcessingStrategyCode\n"
+ "An enumeration that indicates the type of transaction processing strategy to be used. This relates to functionality that is also known as Payment Application Logic.\n" + "An enumeration that indicates the type of transaction processing strategy to be used. This relates to functionality that is also known as Payment Application Logic.\n"
+ "A number of out of the box approaches exist, some are custom to specific MFIs, some are more general and indicate the order in which payments are processed.\n" + "A number of out of the box approaches exist, some are custom to specific MFIs, some are more general and indicate the order in which payments are processed.\n"
+ "\n" + "\n"
@@ -213,7 +216,7 @@ public class LoansApiResource {
"clientId", "group", "loanProductId", "loanProductName", "loanProductDescription", "isLoanProductLinkedToFloatingRate", "clientId", "group", "loanProductId", "loanProductName", "loanProductDescription", "isLoanProductLinkedToFloatingRate",
"fundId", "fundName", "loanPurposeId", "loanPurposeName", "loanOfficerId", "loanOfficerName", "currency", "principal", "fundId", "fundName", "loanPurposeId", "loanPurposeName", "loanOfficerId", "loanOfficerName", "currency", "principal",
"totalOverpaid", "inArrearsTolerance", "termFrequency", "termPeriodFrequencyType", "numberOfRepayments", "repaymentEvery", "totalOverpaid", "inArrearsTolerance", "termFrequency", "termPeriodFrequencyType", "numberOfRepayments", "repaymentEvery",
"interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType", "transactionProcessingStrategyId", "interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType", "transactionProcessingStrategyCode",
"transactionProcessingStrategyName", "interestRateFrequencyType", "amortizationType", "interestType", "transactionProcessingStrategyName", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, "interestCalculationPeriodType", LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
"expectedFirstRepaymentOnDate", "graceOnPrincipalPayment", "recurringMoratoriumOnPrincipalPeriods", "graceOnInterestPayment", "expectedFirstRepaymentOnDate", "graceOnPrincipalPayment", "recurringMoratoriumOnPrincipalPeriods", "graceOnInterestPayment",
@@ -345,11 +348,15 @@ public class LoansApiResource {
if (templateType.equals("individual") || templateType.equals("jlg")) { if (templateType.equals("individual") || templateType.equals("jlg")) {
if (clientId == null) { if (clientId == null) {
newLoanAccount = newLoanAccount == null ? LoanAccountData.emptyTemplate() : newLoanAccount; newLoanAccount = newLoanAccount == null ? LoanAccountData.collateralTemplate(null) : newLoanAccount;
} else { } else {
final LoanAccountData loanAccountClientDetails = this.loanReadPlatformService.retrieveClientDetailsTemplate(clientId); final LoanAccountData loanAccountClientDetails = this.loanReadPlatformService.retrieveClientDetailsTemplate(clientId);
officeId = loanAccountClientDetails.officeId(); officeId = loanAccountClientDetails.getClientOfficeId();
if (officeId == null && loanAccountClientDetails.getGroup() != null) {
officeId = loanAccountClientDetails.getGroup().getOfficeId();
}
newLoanAccount = newLoanAccount == null ? loanAccountClientDetails newLoanAccount = newLoanAccount == null ? loanAccountClientDetails
: LoanAccountData.populateClientDefaults(newLoanAccount, loanAccountClientDetails); : LoanAccountData.populateClientDefaults(newLoanAccount, loanAccountClientDetails);
} }
@@ -364,7 +371,7 @@ public class LoansApiResource {
} else if (templateType.equals("group")) { } else if (templateType.equals("group")) {
final LoanAccountData loanAccountGroupData = this.loanReadPlatformService.retrieveGroupDetailsTemplate(groupId); final LoanAccountData loanAccountGroupData = this.loanReadPlatformService.retrieveGroupDetailsTemplate(groupId);
officeId = loanAccountGroupData.groupOfficeId(); officeId = loanAccountGroupData.getGroup() != null ? loanAccountGroupData.getGroup().getOfficeId() : null;
calendarOptions = this.loanReadPlatformService.retrieveCalendars(groupId); calendarOptions = this.loanReadPlatformService.retrieveCalendars(groupId);
newLoanAccount = newLoanAccount == null ? loanAccountGroupData newLoanAccount = newLoanAccount == null ? loanAccountGroupData
: LoanAccountData.populateGroupDefaults(newLoanAccount, loanAccountGroupData); : LoanAccountData.populateGroupDefaults(newLoanAccount, loanAccountGroupData);
@@ -373,7 +380,7 @@ public class LoansApiResource {
} else if (templateType.equals("jlgbulk")) { } else if (templateType.equals("jlgbulk")) {
// get group details along with members in that group // get group details along with members in that group
final LoanAccountData loanAccountGroupData = this.loanReadPlatformService.retrieveGroupAndMembersDetailsTemplate(groupId); final LoanAccountData loanAccountGroupData = this.loanReadPlatformService.retrieveGroupAndMembersDetailsTemplate(groupId);
officeId = loanAccountGroupData.groupOfficeId(); officeId = loanAccountGroupData.getGroup() != null ? loanAccountGroupData.getGroup().getOfficeId() : null;
calendarOptions = this.loanReadPlatformService.retrieveCalendars(groupId); calendarOptions = this.loanReadPlatformService.retrieveCalendars(groupId);
newLoanAccount = newLoanAccount == null ? loanAccountGroupData newLoanAccount = newLoanAccount == null ? loanAccountGroupData
: LoanAccountData.populateGroupDefaults(newLoanAccount, loanAccountGroupData); : LoanAccountData.populateGroupDefaults(newLoanAccount, loanAccountGroupData);
@@ -471,7 +478,8 @@ public class LoansApiResource {
loanBasicDetails = LoanAccountData.withInterestRecalculationCalendarData(loanBasicDetails, calendarData, loanBasicDetails = LoanAccountData.withInterestRecalculationCalendarData(loanBasicDetails, calendarData,
compoundingCalendarData); compoundingCalendarData);
} }
if (loanBasicDetails.isMonthlyRepaymentFrequencyType()) { if (loanBasicDetails.getRepaymentFrequencyType() != null
&& loanBasicDetails.getRepaymentFrequencyType().getId().intValue() == PeriodFrequencyType.MONTHS.getValue()) {
Collection<CalendarData> loanCalendarDatas = this.calendarReadPlatformService.retrieveCalendarsByEntity(loanId, Collection<CalendarData> loanCalendarDatas = this.calendarReadPlatformService.retrieveCalendarsByEntity(loanId,
CalendarEntityType.LOANS.getValue(), null); CalendarEntityType.LOANS.getValue(), null);
CalendarData calendarData = null; CalendarData calendarData = null;
@@ -542,9 +550,13 @@ public class LoansApiResource {
if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) { if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName); mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName);
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.repaymentScheduleRelatedData(); final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.getTimeline()
.repaymentScheduleRelatedData(loanBasicDetails.getCurrency(), loanBasicDetails.getPrincipal(),
loanBasicDetails.getApprovedPrincipal(), loanBasicDetails.getInArrearsTolerance(),
loanBasicDetails.getFeeChargesAtDisbursementCharged());
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, repaymentScheduleRelatedData, repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, repaymentScheduleRelatedData,
disbursementData, loanBasicDetails.isInterestRecalculationEnabled(), loanBasicDetails.getTotalPaidFeeCharges()); disbursementData, loanBasicDetails.isInterestRecalculationEnabled(),
loanBasicDetails.getSummary() != null ? loanBasicDetails.getSummary().getFeeChargesPaid() : BigDecimal.ZERO);
if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName) if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName)
&& loanBasicDetails.isInterestRecalculationEnabled()) { && loanBasicDetails.isInterestRecalculationEnabled()) {
@@ -553,7 +565,8 @@ public class LoansApiResource {
} }
if (associationParameters.contains(DataTableApiConstant.originalScheduleAssociateParamName) if (associationParameters.contains(DataTableApiConstant.originalScheduleAssociateParamName)
&& loanBasicDetails.isInterestRecalculationEnabled() && loanBasicDetails.isActive()) { && loanBasicDetails.isInterestRecalculationEnabled()
&& LoanStatus.fromInt(loanBasicDetails.getStatus().getId().intValue()).isActive()) {
mandatoryResponseParameters.add(DataTableApiConstant.originalScheduleAssociateParamName); mandatoryResponseParameters.add(DataTableApiConstant.originalScheduleAssociateParamName);
LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(loanId, LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(loanId,
repaymentScheduleRelatedData, disbursementData); repaymentScheduleRelatedData, disbursementData);
@@ -597,7 +610,7 @@ public class LoansApiResource {
if (associationParameters.contains(DataTableApiConstant.collectionAssociateParamName)) { if (associationParameters.contains(DataTableApiConstant.collectionAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.collectionAssociateParamName); mandatoryResponseParameters.add(DataTableApiConstant.collectionAssociateParamName);
if (loanBasicDetails.isActive()) { if (LoanStatus.fromInt(loanBasicDetails.getStatus().getId().intValue()).isActive()) {
collectionData = this.loanReadPlatformService.retrieveLoanCollectionData(loanId); collectionData = this.loanReadPlatformService.retrieveLoanCollectionData(loanId);
} }
} }
@@ -655,8 +668,12 @@ public class LoansApiResource {
} }
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate(); chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(loanBasicDetails.officeId(), Long officeId = loanBasicDetails.getClientOfficeId();
staffInSelectedOfficeOnly);
if (officeId == null && loanBasicDetails.getGroup() != null) {
officeId = loanBasicDetails.getGroup().getOfficeId();
}
allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(officeId, staffInSelectedOfficeOnly);
loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose"); loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose");
loanCollateralOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanCollateral"); loanCollateralOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanCollateral");
@@ -674,8 +691,8 @@ public class LoansApiResource {
mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName); mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName);
linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId); linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId);
} }
if (loanBasicDetails.groupId() != null) { if (loanBasicDetails.getGroup() != null && loanBasicDetails.getGroup().getId() != null) {
calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.groupId()); calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.getGroup().getId());
} }
if (loanBasicDetails.getProduct().isCanUseForTopup() && loanBasicDetails.getClientId() != null) { if (loanBasicDetails.getProduct().isCanUseForTopup() && loanBasicDetails.getClientId() != null) {
@@ -745,7 +762,7 @@ public class LoansApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Calculate loan repayment schedule | Submit a new Loan Application", description = "It calculates the loan repayment Schedule\n" @Operation(summary = "Calculate loan repayment schedule | Submit a new Loan Application", description = "It calculates the loan repayment Schedule\n"
+ "Submits a new loan application\n" + "Submits a new loan application\n"
+ "Mandatory Fields: clientId, productId, principal, loanTermFrequency, loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyId, expectedDisbursementDate, submittedOnDate, loanType\n" + "Mandatory Fields: clientId, productId, principal, loanTermFrequency, loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyCode, expectedDisbursementDate, submittedOnDate, loanType\n"
+ "Optional Fields: graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, linkAccountId, allowPartialPeriodInterestCalcualtion, fixedEmiAmount, maxOutstandingLoanBalance, disbursementData, graceOnArrearsAgeing, createStandingInstructionAtDisbursement (requires linkedAccountId if set to true)\n" + "Optional Fields: graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, linkAccountId, allowPartialPeriodInterestCalcualtion, fixedEmiAmount, maxOutstandingLoanBalance, disbursementData, graceOnArrearsAgeing, createStandingInstructionAtDisbursement (requires linkedAccountId if set to true)\n"
+ "Additional Mandatory Fields if interest recalculation is enabled for product and Rest frequency not same as repayment period: recalculationRestFrequencyDate\n" + "Additional Mandatory Fields if interest recalculation is enabled for product and Rest frequency not same as repayment period: recalculationRestFrequencyDate\n"
+ "Additional Mandatory Fields if interest recalculation with interest/fee compounding is enabled for product and compounding frequency not same as repayment period: recalculationCompoundingFrequencyDate\n" + "Additional Mandatory Fields if interest recalculation with interest/fee compounding is enabled for product and compounding frequency not same as repayment period: recalculationCompoundingFrequencyDate\n"
@@ -958,8 +958,8 @@ final class LoansApiResourceSwagger {
public BigDecimal fixedPrincipalPercentagePerInstallment; public BigDecimal fixedPrincipalPercentagePerInstallment;
public GetLoansLoanIdInterestType interestType; public GetLoansLoanIdInterestType interestType;
public GetLoansLoanIdInterestCalculationPeriodType interestCalculationPeriodType; public GetLoansLoanIdInterestCalculationPeriodType interestCalculationPeriodType;
@Schema(example = "2") @Schema(example = "mifos-standard-strategy")
public Integer transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
public GetLoansLoanIdTimeline timeline; public GetLoansLoanIdTimeline timeline;
public GetLoansLoanIdSummary summary; public GetLoansLoanIdSummary summary;
public GetLoansLoanIdRepaymentSchedule repaymentSchedule; public GetLoansLoanIdRepaymentSchedule repaymentSchedule;
@@ -1033,8 +1033,8 @@ final class LoansApiResourceSwagger {
public Integer interestCalculationPeriodType; public Integer interestCalculationPeriodType;
@Schema(example = "20 September 2011") @Schema(example = "20 September 2011")
public String expectedDisbursementDate; public String expectedDisbursementDate;
@Schema(example = "2") @Schema(example = "mifos-standard-strategy")
public Integer transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
@Schema(example = "360", allowableValues = "1, 360, 364, 36") @Schema(example = "360", allowableValues = "1, 360, 364, 36")
public Integer daysInYearType; public Integer daysInYearType;
@Schema(example = "individual") @Schema(example = "individual")
@@ -1155,8 +1155,8 @@ final class LoansApiResourceSwagger {
public BigDecimal fixedPrincipalPercentagePerInstallment; public BigDecimal fixedPrincipalPercentagePerInstallment;
@Schema(example = "04 March 2014") @Schema(example = "04 March 2014")
public String expectedDisbursementDate; public String expectedDisbursementDate;
@Schema(example = "1") @Schema(example = "mifos-standard-strategy")
public Integer transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
@Schema(example = "1") @Schema(example = "1")
public Long linkAccountId; public Long linkAccountId;
@Schema(example = "true") @Schema(example = "true")
@@ -20,17 +20,23 @@ package org.apache.fineract.portfolio.loanaccount.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@Accessors(chain = true)
public final class CollectionData { public final class CollectionData {
private final BigDecimal availableDisbursementAmount; private BigDecimal availableDisbursementAmount;
private final int pastDueDays; private int pastDueDays;
private final LocalDate nextPaymentDueDate; private LocalDate nextPaymentDueDate;
private final int delinquentDays; private int delinquentDays;
private final LocalDate delinquentDate; private LocalDate delinquentDate;
private final BigDecimal delinquentAmount; private BigDecimal delinquentAmount;
private final LocalDate lastPaymentDate; private LocalDate lastPaymentDate;
private final BigDecimal lastPaymentAmount; private BigDecimal lastPaymentAmount;
private CollectionData(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate, int delinquentDays, private CollectionData(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate, int delinquentDays,
LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate, BigDecimal lastPaymentAmount) { LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate, BigDecimal lastPaymentAmount) {
@@ -55,36 +61,4 @@ public final class CollectionData {
final BigDecimal zero = BigDecimal.ZERO; final BigDecimal zero = BigDecimal.ZERO;
return new CollectionData(zero, 0, null, 0, null, zero, null, zero); return new CollectionData(zero, 0, null, 0, null, zero, null, zero);
} }
public BigDecimal getAvailableDisbursementAmount() {
return availableDisbursementAmount;
}
public int getPastDueDays() {
return pastDueDays;
}
public LocalDate getNextPaymentDueDate() {
return nextPaymentDueDate;
}
public int getDelinquentDays() {
return delinquentDays;
}
public LocalDate getDelinquentDate() {
return delinquentDate;
}
public BigDecimal getDelinquentAmount() {
return delinquentAmount;
}
public LocalDate getLastPaymentDate() {
return lastPaymentDate;
}
public BigDecimal getLastPaymentAmount() {
return lastPaymentAmount;
}
} }
@@ -133,7 +133,6 @@ import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCom
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod; import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType; import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
@@ -159,7 +158,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public static final String FUND_ID = "fundId"; public static final String FUND_ID = "fundId";
public static final String LOAN_OFFICER_ID = "loanOfficerId"; public static final String LOAN_OFFICER_ID = "loanOfficerId";
public static final String LOAN_PURPOSE_ID = "loanPurposeId"; public static final String LOAN_PURPOSE_ID = "loanPurposeId";
public static final String TRANSACTION_PROCESSING_STRATEGY_ID = "transactionProcessingStrategyId"; public static final String TRANSACTION_PROCESSING_STRATEGY_CODE = "transactionProcessingStrategyCode";
public static final String SUBMITTED_ON_DATE = "submittedOnDate"; public static final String SUBMITTED_ON_DATE = "submittedOnDate";
public static final String DATE_FORMAT = "dateFormat"; public static final String DATE_FORMAT = "dateFormat";
public static final String LOCALE = "locale"; public static final String LOCALE = "locale";
@@ -226,9 +225,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
@JoinColumn(name = "loanpurpose_cv_id", nullable = true) @JoinColumn(name = "loanpurpose_cv_id", nullable = true)
private CodeValue loanPurpose; private CodeValue loanPurpose;
@ManyToOne(fetch = FetchType.EAGER) @Column(name = "loan_transaction_strategy_code", nullable = false)
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true) private String transactionProcessingStrategyCode;
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "loan_transaction_strategy_name")
private String transactionProcessingStrategyName;
@Embedded @Embedded
private LoanProductRelatedDetail loanRepaymentScheduleDetail; private LoanProductRelatedDetail loanRepaymentScheduleDetail;
@@ -431,48 +432,41 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType, public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges, final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final BigDecimal fixedEmiAmount,
final Set<LoanCollateralManagement> collateral, final BigDecimal fixedEmiAmount,
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance, final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate, final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) { final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) {
final LoanStatus status = null; return new Loan(accountNo, client, null, loanType, fund, officer, loanPurpose, transactionProcessingStrategyCode, loanProduct,
final Group group = null; loanRepaymentScheduleDetail, null, loanCharges, collateral, null, fixedEmiAmount, disbursementDetails,
final Boolean syncDisbursementWithMeeting = null; maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, fixedPrincipalPercentagePerInstallment);
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
} }
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType, public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges, final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting,
final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount, final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance, final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate, final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates,
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) { final BigDecimal fixedPrincipalPercentagePerInstallment) {
final LoanStatus status = null; return new Loan(accountNo, null, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategyCode, loanProduct,
final Client client = null; loanRepaymentScheduleDetail, null, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment); interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
} }
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group, public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges, final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting,
final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount, final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance, final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate, final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates,
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) { final BigDecimal fixedPrincipalPercentagePerInstallment) {
final LoanStatus status = null; return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategyCode, loanProduct,
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment); interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
} }
@@ -482,7 +476,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
private Loan(final String accountNo, final Client client, final Group group, final Integer loanType, final Fund fund, private Loan(final String accountNo, final Client client, final Group group, final Integer loanType, final Fund fund,
final Staff loanOfficer, final CodeValue loanPurpose, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final Staff loanOfficer, final CodeValue loanPurpose, final String transactionProcessingStrategyCode,
final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus, final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus,
final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting, final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting,
final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
@@ -509,7 +503,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
this.loanOfficer = loanOfficer; this.loanOfficer = loanOfficer;
this.loanPurpose = loanPurpose; this.loanPurpose = loanPurpose;
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
this.loanProduct = loanProduct; this.loanProduct = loanProduct;
if (loanStatus != null) { if (loanStatus != null) {
this.loanStatus = loanStatus.getValue(); this.loanStatus = loanStatus.getValue();
@@ -677,7 +671,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public ChangedTransactionDetail reprocessTransactions() { public ChangedTransactionDetail reprocessTransactions() {
ChangedTransactionDetail changedTransactionDetail = null; ChangedTransactionDetail changedTransactionDetail = null;
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
@@ -747,7 +741,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, this); loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, this);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<>(); final List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<>();
LocalDate startDate = getDisbursementDate(); LocalDate startDate = getDisbursementDate();
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments(); List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments();
@@ -829,7 +823,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loanCharge); removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loanCharge);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) { if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) {
/**** /****
* TODO Vishwas Currently we do not allow removing a loan charge after a loan is approved (hence there is no * TODO Vishwas Currently we do not allow removing a loan charge after a loan is approved (hence there is no
@@ -897,7 +891,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
if (!loanCharge.isDueAtDisbursement()) { if (!loanCharge.isDueAtDisbursement()) {
/**** /****
* TODO Vishwas Currently we do not allow waiving updating loan charge after a loan is approved (hence there * TODO Vishwas Currently we do not allow waiving updating loan charge after a loan is approved (hence there
@@ -1084,7 +1078,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
// Waive of charges whose due date falls after latest 'repayment' // Waive of charges whose due date falls after latest 'repayment'
// transaction dont require entire loan schedule to be reprocessed. // transaction dont require entire loan schedule to be reprocessed.
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) { if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) {
/**** /****
* TODO Vishwas Currently we do not allow waiving fully paid loan charge and waiving partially paid loan * TODO Vishwas Currently we do not allow waiving fully paid loan charge and waiving partially paid loan
@@ -1161,8 +1155,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
this.loanOfficer = newLoanOfficer; this.loanOfficer = newLoanOfficer;
} }
public void updateTransactionProcessingStrategy(final LoanTransactionProcessingStrategy strategy) { public void updateTransactionProcessingStrategy(final String transactionProcessingStrategyCode,
this.transactionProcessingStrategy = strategy; final String transactionProcessingStrategyName) {
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
this.transactionProcessingStrategyName = transactionProcessingStrategyName;
} }
public void updateLoanCharges(final Set<LoanCharge> loanCharges) { public void updateLoanCharges(final Set<LoanCharge> loanCharges) {
@@ -1495,9 +1491,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
actualChanges.put(LOAN_PURPOSE_ID, newValue); actualChanges.put(LOAN_PURPOSE_ID, newValue);
} }
if (command.isChangeInLongParameterNamed(TRANSACTION_PROCESSING_STRATEGY_ID, this.transactionProcessingStrategy.getId())) { if (command.isChangeInStringParameterNamed(TRANSACTION_PROCESSING_STRATEGY_CODE, transactionProcessingStrategyCode)) {
final Long newValue = command.longValueOfParameterNamed(TRANSACTION_PROCESSING_STRATEGY_ID); final String newValue = command.stringValueOfParameterNamed(TRANSACTION_PROCESSING_STRATEGY_CODE);
actualChanges.put(TRANSACTION_PROCESSING_STRATEGY_ID, newValue); actualChanges.put(TRANSACTION_PROCESSING_STRATEGY_CODE, newValue);
} }
if (command.isChangeInLocalDateParameterNamed(SUBMITTED_ON_DATE, getSubmittedOnDate())) { if (command.isChangeInLocalDateParameterNamed(SUBMITTED_ON_DATE, getSubmittedOnDate())) {
@@ -2008,7 +2004,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
existingLoanCollateral = loanCollateralList.toArray(new LoanCollateralManagementData[loanCollateralList.size()]); existingLoanCollateral = loanCollateralList.toArray(new LoanCollateralManagementData[0]);
return existingLoanCollateral; return existingLoanCollateral;
} }
@@ -2022,7 +2018,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
loanChargesList.add(loanCharge.toCommand()); loanChargesList.add(loanCharge.toCommand());
} }
existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[loanChargesList.size()]); existingLoanCharges = loanChargesList.toArray(new LoanChargeCommand[0]);
return existingLoanCharges; return existingLoanCharges;
} }
@@ -2641,7 +2637,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
if (!allNonContraTransactionsPostDisbursement.isEmpty()) { if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -3221,7 +3217,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final LoanRepaymentScheduleInstallment currentInstallment = fetchLoanRepaymentScheduleInstallment( final LoanRepaymentScheduleInstallment currentInstallment = fetchLoanRepaymentScheduleInstallment(
loanTransaction.getTransactionDate()); loanTransaction.getTransactionDate());
@@ -3649,7 +3645,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
writeOffTransaction.reverse(); writeOffTransaction.reverse();
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, this); loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, this);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
@@ -3724,7 +3720,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final AppUser currentUser, final ScheduleGeneratorDTO scheduleGeneratorDTO) { final AppUser currentUser, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO, ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
loanRepaymentScheduleTransactionProcessor); loanRepaymentScheduleTransactionProcessor);
@@ -3846,7 +3842,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
throw new InvalidLoanStateTransitionException("close", "cannot.be.a.future.date", errorMessage, closureDate); throw new InvalidLoanStateTransitionException("close", "cannot.be.a.future.date", errorMessage, closureDate);
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO, ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
loanRepaymentScheduleTransactionProcessor); loanRepaymentScheduleTransactionProcessor);
@@ -5133,7 +5129,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction( ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
@@ -5269,7 +5265,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public ChangedTransactionDetail processTransactions() { public ChangedTransactionDetail processTransactions() {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction( ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
@@ -5498,7 +5494,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public void processPostDisbursementTransactions() { public void processPostDisbursementTransactions() {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
final List<LoanTransaction> copyTransactions = new ArrayList<>(); final List<LoanTransaction> copyTransactions = new ArrayList<>();
if (!allNonContraTransactionsPostDisbursement.isEmpty()) { if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
@@ -5526,7 +5522,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO); final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, this, generatorDTO.getHolidayDetailDTO(), return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, this, generatorDTO.getHolidayDetailDTO(),
loanRepaymentScheduleTransactionProcessor, generatorDTO.getRecalculateFrom()); loanRepaymentScheduleTransactionProcessor, generatorDTO.getRecalculateFrom());
@@ -5544,7 +5540,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod); final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, this, installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, this,
scheduleGeneratorDTO.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor); scheduleGeneratorDTO.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor);
} else { } else {
@@ -5687,8 +5683,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
} }
public LoanTransactionProcessingStrategy transactionProcessingStrategy() { public String transactionProcessingStrategy() {
return this.transactionProcessingStrategy; return this.transactionProcessingStrategyCode;
} }
public boolean isNpa() { public boolean isNpa() {
@@ -6096,7 +6092,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
// If is a refund // If is a refund
if (adjustedTransaction == null) { if (adjustedTransaction == null) {
@@ -6130,7 +6126,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategyCode);
final Money overpaidAmount = calculateTotalOverpayment(); // Before Transaction final Money overpaidAmount = calculateTotalOverpayment(); // Before Transaction
if (overpaidAmount.isGreaterThanZero()) { if (overpaidAmount.isGreaterThanZero()) {
chargebackTransaction.setOverPayments(overpaidAmount); chargebackTransaction.setOverPayments(overpaidAmount);
@@ -18,56 +18,39 @@
*/ */
package org.apache.fineract.portfolio.loanaccount.domain; package org.apache.fineract.portfolio.loanaccount.domain;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor; import org.springframework.beans.factory.annotation.Value;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.springframework.stereotype.Component;
@Component @Slf4j
@RequiredArgsConstructor
public class LoanRepaymentScheduleTransactionProcessorFactory { public class LoanRepaymentScheduleTransactionProcessorFactory {
public LoanRepaymentScheduleTransactionProcessor determineProcessor( private final LoanRepaymentScheduleTransactionProcessor defaultLoanRepaymentScheduleTransactionProcessor;
final LoanTransactionProcessingStrategy transactionProcessingStrategy) {
LoanRepaymentScheduleTransactionProcessor processor = new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor(); private final List<LoanRepaymentScheduleTransactionProcessor> processors;
if (transactionProcessingStrategy != null) { @Value("${fineract.loan.transactionprocessor.error-not-found-fail}")
private Boolean errorNotFoundFail;
if (transactionProcessingStrategy.isStandardStrategy()) { public LoanRepaymentScheduleTransactionProcessor determineProcessor(final String transactionProcessingStrategy) {
processor = new FineractStyleLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isHeavensfamilyStrategy()) { Optional<LoanRepaymentScheduleTransactionProcessor> processor = processors.stream()
processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor(); .filter(p -> p.accept(transactionProcessingStrategy)).findFirst();
}
if (transactionProcessingStrategy.isEarlyPaymentStrategy()) { if (processor.isEmpty() && Boolean.TRUE.equals(errorNotFoundFail)) {
processor = new EarlyPaymentLoanRepaymentScheduleTransactionProcessor(); throw new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategy);
} } else {
return processor.orElse(defaultLoanRepaymentScheduleTransactionProcessor);
if (transactionProcessingStrategy.isCreocoreStrategy()) {
processor = new CreocoreLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isIndianRBIStrategy()) {
processor = new RBILoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isPrincipalInterestPenaltiesFeesOrderStrategy()) {
processor = new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isInterestPrincipalPenaltiesFeesOrderStrategy()) {
processor = new InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
}
} }
}
return processor; public List<TransactionProcessingStrategyData> getStrategies() {
return processors.stream().map(p -> new TransactionProcessingStrategyData(null, p.getCode(), p.getName())).toList();
} }
} }
@@ -55,6 +55,11 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
*/ */
public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implements LoanRepaymentScheduleTransactionProcessor { public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implements LoanRepaymentScheduleTransactionProcessor {
@Override
public boolean accept(String s) {
return getCode().equalsIgnoreCase(s) || getName().equalsIgnoreCase(s);
}
/** /**
* Provides support for passing all {@link LoanTransaction}'s so it will completely re-process the entire loan * Provides support for passing all {@link LoanTransaction}'s so it will completely re-process the entire loan
* schedule. This is required in cases where the {@link LoanTransaction} being processed is in the past and falls * schedule. This is required in cases where the {@link LoanTransaction} being processed is in the past and falls
@@ -30,6 +30,12 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
public interface LoanRepaymentScheduleTransactionProcessor { public interface LoanRepaymentScheduleTransactionProcessor {
String getCode();
String getName();
boolean accept(String s);
void handleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments, void handleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments,
Set<LoanCharge> charges); Set<LoanCharge> charges);
@@ -40,6 +40,20 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
*/ */
public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor { public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
private static final String STRATEGY_CODE = "creocore-strategy";
private static final String STRATEGY_NAME = "Creocore Unique";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
/** /**
* For creocore, early is defined as any date before the installment due date * For creocore, early is defined as any date before the installment due date
*/ */
@@ -34,6 +34,20 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
*/ */
public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor { public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
public static final String STRATEGY_CODE = "early-repayment-strategy";
private static final String STRATEGY_NAME = "Early Repayment Strategy";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
/** /**
* For early/'in advance' repayments, pay off in the same way as on-time payments, interest first, principal, * For early/'in advance' repayments, pay off in the same way as on-time payments, interest first, principal,
* penalties and charges. * penalties and charges.
@@ -40,6 +40,20 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor { public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
private static final String STRATEGY_CODE = "mifos-standard-strategy";
private static final String STRATEGY_NAME = "Penalties, Fees, Interest, Principal order";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
@Override @Override
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex, protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate, final Money transactionAmount) { final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate, final Money transactionAmount) {
@@ -41,6 +41,20 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor { public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
private static final String STRATEGY_CODE = "heavensfamily-strategy";
private static final String STRATEGY_NAME = "HeavensFamily Unique";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
/** /**
* For late repayments, pay off in the same way as on-time payments, interest first then principal. * For late repayments, pay off in the same way as on-time payments, interest first then principal.
*/ */
@@ -35,6 +35,20 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor
extends AbstractLoanRepaymentScheduleTransactionProcessor { extends AbstractLoanRepaymentScheduleTransactionProcessor {
private static final String STRATEGY_CODE = "interest-principal-penalties-fees-order-strategy";
private static final String STRATEGY_NAME = "Interest, Principal, Penalties, Fees Order";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
/** /**
* For early/'in advance' repayments, pay off in the same way as on-time payments, interest first, principal, * For early/'in advance' repayments, pay off in the same way as on-time payments, interest first, principal,
* penalties and charges. * penalties and charges.
@@ -35,6 +35,20 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor
extends AbstractLoanRepaymentScheduleTransactionProcessor { extends AbstractLoanRepaymentScheduleTransactionProcessor {
private static final String STRATEGY_CODE = "principal-interest-penalties-fees-order-strategy";
private static final String STRATEGY_NAME = "Principal, Interest, Penalties, Fees Order";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
/** /**
* For early/'in advance' repayments, pay off in the same way as on-time payments, interest first, principal, * For early/'in advance' repayments, pay off in the same way as on-time payments, interest first, principal,
* penalties and charges. * penalties and charges.
@@ -43,6 +43,20 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
*/ */
public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor { public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
private static final String STRATEGY_CODE = "rbi-india-strategy";
private static final String STRATEGY_NAME = "Overdue/Due Fee/Int,Principal";
@Override
public String getCode() {
return STRATEGY_CODE;
}
@Override
public String getName() {
return STRATEGY_NAME;
}
/** /**
* For creocore, early is defined as any date before the installment due date * For creocore, early is defined as any date before the installment due date
*/ */
@@ -25,8 +25,8 @@ import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourc
*/ */
public class LoanTransactionProcessingStrategyNotFoundException extends AbstractPlatformResourceNotFoundException { public class LoanTransactionProcessingStrategyNotFoundException extends AbstractPlatformResourceNotFoundException {
public LoanTransactionProcessingStrategyNotFoundException(final Long id) { public LoanTransactionProcessingStrategyNotFoundException(final String code) {
super("error.msg.transactions.processing.strategy.id.invalid", super("error.msg.transactions.processing.strategy.code.invalid",
"Loan transaction processing strategy with identifier " + id + " does not exist", id); "Loan transaction processing strategy with code " + code + " does not exist", code);
} }
} }
@@ -28,7 +28,6 @@ import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping; import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
@@ -132,8 +131,7 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(loan.transactionProcessingStrategy()); .determineProcessor(loan.transactionProcessingStrategy());
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod()); final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
final LoanLifecycleStateMachine loanLifecycleStateMachine = null; loan.setHelpers(null, this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
loan.setHelpers(loanLifecycleStateMachine, this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan, final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate); loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
final LoanScheduleModel loanScheduleModel = loanSchedule.getLoanScheduleModel(); final LoanScheduleModel loanScheduleModel = loanSchedule.getLoanScheduleModel();
@@ -48,7 +48,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
LoanApiConstants.clientIdParameterName, LoanApiConstants.groupIdParameterName, LoanApiConstants.loanTypeParameterName, LoanApiConstants.clientIdParameterName, LoanApiConstants.groupIdParameterName, LoanApiConstants.loanTypeParameterName,
LoanApiConstants.calendarIdParameterName, LoanApiConstants.productIdParameterName, LoanApiConstants.accountNoParameterName, LoanApiConstants.calendarIdParameterName, LoanApiConstants.productIdParameterName, LoanApiConstants.accountNoParameterName,
LoanApiConstants.externalIdParameterName, LoanApiConstants.fundIdParameterName, LoanApiConstants.loanOfficerIdParameterName, LoanApiConstants.externalIdParameterName, LoanApiConstants.fundIdParameterName, LoanApiConstants.loanOfficerIdParameterName,
LoanApiConstants.loanPurposeIdParameterName, LoanApiConstants.transactionProcessingStrategyIdParameterName, LoanApiConstants.loanPurposeIdParameterName, LoanApiConstants.transactionProcessingStrategyCodeParameterName,
LoanApiConstants.principalParamName, LoanApiConstants.inArrearsToleranceParameterName, LoanApiConstants.principalParamName, LoanApiConstants.inArrearsToleranceParameterName,
LoanApiConstants.interestRatePerPeriodParameterName, LoanApiConstants.repaymentEveryParameterName, LoanApiConstants.interestRatePerPeriodParameterName, LoanApiConstants.repaymentEveryParameterName,
LoanApiConstants.numberOfRepaymentsParameterName, LoanApiConstants.loanTermFrequencyParameterName, LoanApiConstants.numberOfRepaymentsParameterName, LoanApiConstants.loanTermFrequencyParameterName,
@@ -32,6 +32,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
@@ -46,18 +48,22 @@ import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollatera
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollateralQuantity; import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollateralQuantity;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals; import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals;
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants; import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException; import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component @Component
public final class LoanApplicationCommandFromApiJsonHelper { public final class LoanApplicationCommandFromApiJsonHelper {
@@ -72,8 +78,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
LoanApiConstants.numberOfRepaymentsParameterName, LoanApiConstants.repaymentEveryParameterName, LoanApiConstants.numberOfRepaymentsParameterName, LoanApiConstants.repaymentEveryParameterName,
LoanApiConstants.repaymentFrequencyTypeParameterName, LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyTypeParameterName, LoanApiConstants.repaymentFrequencyNthDayTypeParameterName,
LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName, LoanApiConstants.interestRatePerPeriodParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName, LoanApiConstants.interestRatePerPeriodParameterName,
LoanApiConstants.amortizationTypeParameterName, LoanApiConstants.interestTypeParameterName, LoanApiConstants.amortizationTypeParameterName, LoanApiConstants.amortizationTypeOptionsParameterName,
LoanApiConstants.isFloatingInterestRate, LoanApiConstants.interestRateDifferential, LoanApiConstants.interestTypeParameterName, LoanApiConstants.isFloatingInterestRate, LoanApiConstants.interestRateDifferential,
LoanApiConstants.interestCalculationPeriodTypeParameterName, LoanApiConstants.interestCalculationPeriodTypeParameterName,
LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
LoanApiConstants.interestRateFrequencyTypeParameterName, LoanApiConstants.expectedDisbursementDateParameterName, LoanApiConstants.interestRateFrequencyTypeParameterName, LoanApiConstants.expectedDisbursementDateParameterName,
@@ -84,7 +90,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
LoanApiConstants.externalIdParameterName, LoanApiConstants.fundIdParameterName, LoanApiConstants.loanOfficerIdParameterName, // optional LoanApiConstants.externalIdParameterName, LoanApiConstants.fundIdParameterName, LoanApiConstants.loanOfficerIdParameterName, // optional
LoanApiConstants.loanPurposeIdParameterName, LoanApiConstants.inArrearsToleranceParameterName, LoanApiConstants.loanPurposeIdParameterName, LoanApiConstants.inArrearsToleranceParameterName,
LoanApiConstants.chargesParameterName, LoanApiConstants.collateralParameterName, // optional LoanApiConstants.chargesParameterName, LoanApiConstants.collateralParameterName, // optional
LoanApiConstants.transactionProcessingStrategyIdParameterName, // settings LoanApiConstants.transactionProcessingStrategyCodeParameterName, // settings
LoanApiConstants.calendarIdParameterName, // optional LoanApiConstants.calendarIdParameterName, // optional
LoanApiConstants.syncDisbursementWithMeetingParameterName, // optional LoanApiConstants.syncDisbursementWithMeetingParameterName, // optional
LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName, LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
@@ -102,14 +108,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper; private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
private final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper; private final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper;
@Autowired private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
public LoanApplicationCommandFromApiJsonHelper(final FromJsonHelper fromApiJsonHelper,
final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper,
final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.apiJsonHelper = apiJsonHelper;
this.clientCollateralManagementRepositoryWrapper = clientCollateralManagementRepositoryWrapper;
}
public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans, final LoanProduct loanProduct) { public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans, final LoanProduct loanProduct) {
if (StringUtils.isBlank(json)) { if (StringUtils.isBlank(json)) {
@@ -388,10 +387,19 @@ public final class LoanApplicationCommandFromApiJsonHelper {
.notExceedingLengthOf(500); .notExceedingLengthOf(500);
} }
final Long transactionProcessingStrategyId = this.fromApiJsonHelper final String transactionProcessingStrategy = this.fromApiJsonHelper
.extractLongNamed(LoanApiConstants.transactionProcessingStrategyIdParameterName, element); .extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyIdParameterName) baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName)
.value(transactionProcessingStrategyId).notNull().integerGreaterThanZero(); .value(transactionProcessingStrategy).notNull();
LoanRepaymentScheduleTransactionProcessor processor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(transactionProcessingStrategy);
if (processor == null) {
Object[] values = loanRepaymentScheduleTransactionProcessorFactory.getStrategies().stream()
.map(TransactionProcessingStrategyData::getCode).toList().toArray(new String[0]);
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName).isOneOfTheseValues(values);
}
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.linkAccountIdParameterName, element)) { if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.linkAccountIdParameterName, element)) {
final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.linkAccountIdParameterName, element); final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.linkAccountIdParameterName, element);
@@ -626,12 +634,22 @@ public final class LoanApplicationCommandFromApiJsonHelper {
.integerGreaterThanZero(); .integerGreaterThanZero();
} }
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.transactionProcessingStrategyIdParameterName, element)) { if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element)) {
atLeastOneParameterPassedForUpdate = true; atLeastOneParameterPassedForUpdate = true;
final Long transactionProcessingStrategyId = this.fromApiJsonHelper final String transactionProcessingStrategy = this.fromApiJsonHelper
.extractLongNamed(LoanApiConstants.transactionProcessingStrategyIdParameterName, element); .extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element);
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyIdParameterName) baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName)
.value(transactionProcessingStrategyId).notNull().integerGreaterThanZero(); .value(transactionProcessingStrategy).notNull();
LoanRepaymentScheduleTransactionProcessor processor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(transactionProcessingStrategy);
if (processor == null) {
Object[] values = loanRepaymentScheduleTransactionProcessorFactory.getStrategies().stream()
.map(TransactionProcessingStrategyData::getCode).toList().toArray(new String[0]);
baseDataValidator.reset().parameter(LoanApiConstants.transactionProcessingStrategyCodeParameterName)
.isOneOfTheseValues(values);
}
} }
BigDecimal principal = null; BigDecimal principal = null;
@@ -128,7 +128,6 @@ import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType; import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException; import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException; import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
@@ -605,7 +604,9 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
productRelatedDetail.setRepayEvery(loan.loanProduct().getLoanProductRelatedDetail().getRepayEvery()); productRelatedDetail.setRepayEvery(loan.loanProduct().getLoanProductRelatedDetail().getRepayEvery());
} }
if (!transactionProcessingStrategy) { if (!transactionProcessingStrategy) {
loan.updateTransactionProcessingStrategy(loan.loanProduct().getRepaymentStrategy()); loan.updateTransactionProcessingStrategy(loan.loanProduct().getRepaymentStrategy(),
this.loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(loan.loanProduct().getRepaymentStrategy())
.getName());
} }
} }
@@ -933,12 +934,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
existingLoanApplication.updateLoanOfficerOnLoanApplication(newValue); existingLoanApplication.updateLoanOfficerOnLoanApplication(newValue);
} }
final String strategyIdParamName = "transactionProcessingStrategyId"; final String strategyCodeParamName = "transactionProcessingStrategyCode";
if (changes.containsKey(strategyIdParamName)) { if (changes.containsKey(strategyCodeParamName)) {
final Long strategyId = command.longValueOfParameterNamed(strategyIdParamName); final String strategy = command.stringValueOfParameterNamed(strategyCodeParamName);
final LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(strategyId);
existingLoanApplication.updateTransactionProcessingStrategy(strategy); existingLoanApplication.updateTransactionProcessingStrategy(strategy,
this.loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(strategy).getName());
} }
/** /**
@@ -70,10 +70,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTra
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException; import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals; import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals;
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataNotAllowedException; import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataNotAllowedException;
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException; import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
@@ -82,7 +80,6 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanSchedu
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException; import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException; import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException; import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
@@ -100,7 +97,6 @@ public class LoanAssembler {
private final ClientRepositoryWrapper clientRepository; private final ClientRepositoryWrapper clientRepository;
private final GroupRepository groupRepository; private final GroupRepository groupRepository;
private final FundRepository fundRepository; private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final StaffRepository staffRepository; private final StaffRepository staffRepository;
private final CodeValueRepositoryWrapper codeValueRepository; private final CodeValueRepositoryWrapper codeValueRepository;
private final LoanScheduleAssembler loanScheduleAssembler; private final LoanScheduleAssembler loanScheduleAssembler;
@@ -143,7 +139,10 @@ public class LoanAssembler {
final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element); final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element); final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
final Long loanOfficerId = this.fromApiJsonHelper.extractLongNamed("loanOfficerId", element); final Long loanOfficerId = this.fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element); final String transactionProcessingStrategyCode = this.fromApiJsonHelper.extractStringNamed("transactionProcessingStrategyCode",
element);
final String transactionProcessingStrategyName = this.loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(transactionProcessingStrategyCode).getName();
final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element); final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element); final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper
@@ -155,8 +154,6 @@ public class LoanAssembler {
.extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementPrincipalParameterName, element); .extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementPrincipalParameterName, element);
final Fund fund = findFundByIdIfProvided(fundId); final Fund fund = findFundByIdIfProvided(fundId);
final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId); final Staff loanOfficer = findLoanOfficerByIdIfProvided(loanOfficerId);
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(
transactionProcessingStrategyId);
CodeValue loanPurpose = null; CodeValue loanPurpose = null;
if (loanPurposeId != null) { if (loanPurposeId != null) {
loanPurpose = this.codeValueRepository.findOneWithNotFoundDetection(loanPurposeId); loanPurpose = this.codeValueRepository.findOneWithNotFoundDetection(loanPurposeId);
@@ -272,25 +269,25 @@ public class LoanAssembler {
} }
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(), loanProduct, loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(), loanProduct,
fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, null, fund, loanOfficer, loanPurpose, transactionProcessingStrategyCode, loanProductRelatedDetail, loanCharges, null,
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
fixedPrincipalPercentagePerInstallment); fixedPrincipalPercentagePerInstallment);
loanApplication.updateTransactionProcessingStrategy(transactionProcessingStrategyCode, transactionProcessingStrategyName);
} else if (group != null) { } else if (group != null) {
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer, loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, null, loanPurpose, transactionProcessingStrategyCode, loanProductRelatedDetail, loanCharges, null,
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates, createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
fixedPrincipalPercentagePerInstallment); fixedPrincipalPercentagePerInstallment);
loanApplication.updateTransactionProcessingStrategy(transactionProcessingStrategyCode, transactionProcessingStrategyName);
} else if (client != null) { } else if (client != null) {
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund, loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral, loanOfficer, loanPurpose, transactionProcessingStrategyCode, loanProductRelatedDetail, loanCharges, collateral,
fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement,
isFloatingInterestRate, interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment); isFloatingInterestRate, interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
loanApplication.updateTransactionProcessingStrategy(transactionProcessingStrategyCode, transactionProcessingStrategyName);
} }
final String externalId = this.fromApiJsonHelper.extractStringNamed("externalId", element); final String externalId = this.fromApiJsonHelper.extractStringNamed("externalId", element);
@@ -354,15 +351,6 @@ public class LoanAssembler {
return staff; return staff;
} }
public LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
LoanTransactionProcessingStrategy strategy = null;
if (transactionProcessingStrategyId != null) {
strategy = this.loanTransactionProcessingStrategyRepository.findById(transactionProcessingStrategyId)
.orElseThrow(() -> new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategyId));
}
return strategy;
}
public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final Loan loanApplication) { public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final Loan loanApplication) {
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
@@ -609,8 +609,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.is_equal_amortization as isEqualAmortization, l.interest_calculated_in_period_enum as interestCalculationPeriodType," + " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.is_equal_amortization as isEqualAmortization, l.interest_calculated_in_period_enum as interestCalculationPeriodType,"
+ " l.fixed_principal_percentage_per_installment fixedPrincipalPercentagePerInstallment, " + " l.fixed_principal_percentage_per_installment fixedPrincipalPercentagePerInstallment, "
+ " l.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion," + " l.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion,"
+ " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, " + " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_code as transactionStrategyCode, "
+ " lps.name as transactionStrategyName, " + " l.loan_transaction_strategy_name as transactionStrategyName, "
+ " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc." + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc."
+ sqlGenerator.escape("name") + sqlGenerator.escape("name")
+ " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + " as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
@@ -676,7 +676,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " left join m_appuser dbu on dbu.id = l.disbursedon_userid left join m_appuser cbu on cbu.id = l.closedon_userid" + " left join m_appuser dbu on dbu.id = l.disbursedon_userid left join m_appuser cbu on cbu.id = l.closedon_userid"
+ " left join m_code_value cv on cv.id = l.loanpurpose_cv_id" + " left join m_code_value cv on cv.id = l.loanpurpose_cv_id"
+ " left join m_code_value codev on codev.id = l.writeoff_reason_cv_id" + " left join m_code_value codev on codev.id = l.writeoff_reason_cv_id"
+ " left join ref_loan_transaction_processing_strategy lps on lps.id = l.loan_transaction_strategy_id"
+ " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id" + " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id"
+ " left join m_loan_topup as topup on l.id = topup.loan_id" + " left join m_loan_topup as topup on l.id = topup.loan_id"
+ " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id"; + " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id";
@@ -814,7 +813,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final int interestRateFrequencyTypeInt = JdbcSupport.getInteger(rs, "interestRateFrequencyType"); final int interestRateFrequencyTypeInt = JdbcSupport.getInteger(rs, "interestRateFrequencyType");
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeInt); final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeInt);
final Long transactionStrategyId = JdbcSupport.getLong(rs, "transactionStrategyId"); final String transactionStrategyCode = rs.getString("transactionStrategyCode");
final String transactionStrategyName = rs.getString("transactionStrategyName"); final String transactionStrategyName = rs.getString("transactionStrategyName");
final int amortizationTypeInt = JdbcSupport.getInteger(rs, "amortizationType"); final int amortizationTypeInt = JdbcSupport.getInteger(rs, "amortizationType");
@@ -996,7 +995,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
currencyData, proposedPrincipal, principal, approvedPrincipal, netDisbursalAmount, totalOverpaid, inArrearsTolerance, currencyData, proposedPrincipal, principal, approvedPrincipal, netDisbursalAmount, totalOverpaid, inArrearsTolerance,
termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, null, null, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, null, null,
transactionStrategyId, transactionStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType, transactionStrategyCode, transactionStrategyName, amortizationType, interestRatePerPeriod, interestRateFrequencyType,
annualInterestRate, interestType, isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType, annualInterestRate, interestType, isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
allowPartialPeriodInterestCalcualtion, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, allowPartialPeriodInterestCalcualtion, expectedFirstRepaymentOnDate, graceOnPrincipalPayment,
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate,
@@ -1932,14 +1931,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public LoanTransactionData retrieveLoanWriteoffTemplate(final Long loanId) { public LoanTransactionData retrieveLoanWriteoffTemplate(final Long loanId) {
final LoanAccountData loan = this.retrieveOne(loanId); final LoanAccountData loan = this.retrieveOne(loanId);
final BigDecimal outstandingLoanBalance = null;
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF);
final BigDecimal unrecognizedIncomePortion = null; final BigDecimal totalOutstanding = loan.getSummary() != null ? loan.getSummary().getTotalOutstanding() : null;
final List<CodeValueData> writeOffReasonOptions = new ArrayList<>( final List<CodeValueData> writeOffReasonOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(LoanApiConstants.WRITEOFFREASONS)); this.codeValueReadPlatformService.retrieveCodeValuesByCode(LoanApiConstants.WRITEOFFREASONS));
LoanTransactionData loanTransactionData = new LoanTransactionData(null, null, null, transactionType, null, loan.getCurrency(), LoanTransactionData loanTransactionData = new LoanTransactionData(null, null, null, transactionType, null, loan.getCurrency(),
DateUtils.getBusinessLocalDate(), loan.getTotalOutstandingAmount(), loan.getNetDisbursalAmount(), null, null, null, null, DateUtils.getBusinessLocalDate(), totalOutstanding, loan.getNetDisbursalAmount(), null, null, null, null, null, null, null,
null, null, null, null, outstandingLoanBalance, unrecognizedIncomePortion, false); null, null, null, false);
loanTransactionData.setWriteOffReasonOptions(writeOffReasonOptions); loanTransactionData.setWriteOffReasonOptions(writeOffReasonOptions);
return loanTransactionData; return loanTransactionData;
} }
@@ -2246,10 +2244,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Collection<InterestRatePeriodData> intRates = this.floatingRatesReadPlatformService final Collection<InterestRatePeriodData> intRates = this.floatingRatesReadPlatformService
.retrieveInterestRatePeriods(loanData.getLoanProductId()); .retrieveInterestRatePeriods(loanData.getLoanProductId());
for (final InterestRatePeriodData rate : intRates) { for (final InterestRatePeriodData rate : intRates) {
if (rate.getFromDate().compareTo(loanData.getDisbursementDate()) > 0 && loanData.isFloatingInterestRate()) { if (loanData.getTimeline() != null && rate.getFromDate().compareTo(loanData.getTimeline().getDisbursementDate()) > 0
&& loanData.isFloatingInterestRate()) {
updateInterestRatePeriodData(rate, loanData); updateInterestRatePeriodData(rate, loanData);
intRatePeriodData.add(rate); intRatePeriodData.add(rate);
} else if (rate.getFromDate().compareTo(loanData.getDisbursementDate()) <= 0) { } else if (loanData.getTimeline() != null
&& rate.getFromDate().compareTo(loanData.getTimeline().getDisbursementDate()) <= 0) {
updateInterestRatePeriodData(rate, loanData); updateInterestRatePeriodData(rate, loanData);
intRatePeriodData.add(rate); intRatePeriodData.add(rate);
break; break;
@@ -2275,8 +2275,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
} }
rate.setEffectiveInterestRate(effectiveInterestRate); rate.setEffectiveInterestRate(effectiveInterestRate);
if (rate.getFromDate().compareTo(loan.getDisbursementDate()) < 0) { if (loan.getTimeline() != null && rate.getFromDate().compareTo(loan.getTimeline().getDisbursementDate()) < 0) {
rate.setFromDate(loan.getDisbursementDate()); rate.setFromDate(loan.getTimeline().getDisbursementDate());
} }
} }
@@ -0,0 +1,88 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.starter;
import java.util.List;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LoanAccountAutoStarter {
@Bean
@ConditionalOnProperty("fineract.loan.transactionprocessor.creocore.enabled")
public CreocoreLoanRepaymentScheduleTransactionProcessor creocoreLoanRepaymentScheduleTransactionProcessor() {
return new CreocoreLoanRepaymentScheduleTransactionProcessor();
}
@Bean
@ConditionalOnProperty("fineract.loan.transactionprocessor.early-repayment.enabled")
public EarlyPaymentLoanRepaymentScheduleTransactionProcessor earlyPaymentLoanRepaymentScheduleTransactionProcessor() {
return new EarlyPaymentLoanRepaymentScheduleTransactionProcessor();
}
@Bean
@ConditionalOnProperty("fineract.loan.transactionprocessor.mifos-standard.enabled")
public FineractStyleLoanRepaymentScheduleTransactionProcessor fineractStyleLoanRepaymentScheduleTransactionProcessor() {
return new FineractStyleLoanRepaymentScheduleTransactionProcessor();
}
@Bean
@ConditionalOnProperty("fineract.loan.transactionprocessor.heavensfamily.enabled")
public HeavensFamilyLoanRepaymentScheduleTransactionProcessor heavensFamilyLoanRepaymentScheduleTransactionProcessor() {
return new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
}
@Bean
@ConditionalOnProperty("fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled")
public InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor interestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
return new InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
}
@Bean
@ConditionalOnProperty("fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled")
public PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor principalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() {
return new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor();
}
@Bean
@ConditionalOnProperty("fineract.loan.transactionprocessor.rbi-india.enabled")
public RBILoanRepaymentScheduleTransactionProcessor rbiLoanRepaymentScheduleTransactionProcessor() {
return new RBILoanRepaymentScheduleTransactionProcessor();
}
@Bean
@ConditionalOnMissingBean(LoanRepaymentScheduleTransactionProcessorFactory.class)
public LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory(
PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor defaultLoanRepaymentScheduleTransactionProcessor,
List<LoanRepaymentScheduleTransactionProcessor> processors) {
return new LoanRepaymentScheduleTransactionProcessorFactory(defaultLoanRepaymentScheduleTransactionProcessor, processors);
}
}
@@ -115,7 +115,7 @@ public interface LoanProductConstants {
String allowAttributeOverridesParamName = "allowAttributeOverrides"; String allowAttributeOverridesParamName = "allowAttributeOverrides";
String amortizationTypeParamName = "amortizationType"; String amortizationTypeParamName = "amortizationType";
String interestTypeParamName = "interestType"; String interestTypeParamName = "interestType";
String transactionProcessingStrategyIdParamName = "transactionProcessingStrategyId"; String transactionProcessingStrategyCodeParamName = "transactionProcessingStrategyCode";
String interestCalculationPeriodTypeParamName = "interestCalculationPeriodType"; String interestCalculationPeriodTypeParamName = "interestCalculationPeriodType";
String inArrearsToleranceParamName = "inArrearsTolerance"; String inArrearsToleranceParamName = "inArrearsTolerance";
String repaymentEveryParamName = "repaymentEvery"; String repaymentEveryParamName = "repaymentEvery";
@@ -99,7 +99,7 @@ public class LoanProductsApiResource {
"recurringMoratoriumOnPrincipalPeriods", "graceOnInterestPayment", "graceOnInterestCharged", "interestRatePerPeriod", "recurringMoratoriumOnPrincipalPeriods", "graceOnInterestPayment", "graceOnInterestCharged", "interestRatePerPeriod",
"minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "annualInterestRate", "amortizationType", "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "annualInterestRate", "amortizationType",
"interestType", "interestCalculationPeriodType", LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, "interestType", "interestCalculationPeriodType", LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
"inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "charges", "accountingRule", "inArrearsTolerance", "transactionProcessingStrategyCode", "transactionProcessingStrategyName", "charges", "accountingRule",
"externalId", "accountingMappings", "paymentChannelToFundSourceMappings", "fundOptions", "paymentTypeOptions", "externalId", "accountingMappings", "paymentChannelToFundSourceMappings", "fundOptions", "paymentTypeOptions",
"currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "amortizationTypeOptions", "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "amortizationTypeOptions",
"interestTypeOptions", "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions",
@@ -178,7 +178,7 @@ public class LoanProductsApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a Loan Product", description = "Depending of the Accounting Rule (accountingRule) selected, additional fields with details of the appropriate Ledger Account identifiers would need to be passed in.\n" @Operation(summary = "Create a Loan Product", description = "Depending of the Accounting Rule (accountingRule) selected, additional fields with details of the appropriate Ledger Account identifiers would need to be passed in.\n"
+ "\n" + "Refer MifosX Accounting Specs Draft for more details regarding the significance of the selected accounting rule\n\n" + "\n" + "Refer MifosX Accounting Specs Draft for more details regarding the significance of the selected accounting rule\n\n"
+ "Mandatory Fields: name, shortName, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyId, accountingRule, isInterestRecalculationEnabled, daysInYearType, daysInMonthType\n\n" + "Mandatory Fields: name, shortName, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyCode, accountingRule, isInterestRecalculationEnabled, daysInYearType, daysInMonthType\n\n"
+ "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion\n\n" + "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf, allowAttributeOverrides, allowPartialPeriodInterestCalcualtion\n\n"
+ "Additional Mandatory Fields for Cash(2) based accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n" + "Additional Mandatory Fields for Cash(2) based accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n"
+ "Additional Mandatory Fields for periodic (3) and upfront (4)accrual accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n" + "Additional Mandatory Fields for periodic (3) and upfront (4)accrual accounting: fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId, transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n"
@@ -109,8 +109,8 @@ final class LoanProductsApiResourceSwagger {
public Integer interestType; public Integer interestType;
@Schema(example = "1") @Schema(example = "1")
public Integer interestCalculationPeriodType; public Integer interestCalculationPeriodType;
@Schema(example = "1") @Schema(example = "mifos-standard-strategy")
public Long transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
@Schema(example = "false") @Schema(example = "false")
public Boolean isLinkedToFloatingInterestRates; public Boolean isLinkedToFloatingInterestRates;
@Schema(example = "false") @Schema(example = "false")
@@ -227,7 +227,7 @@ final class LoanProductsApiResourceSwagger {
@Schema(example = "true") @Schema(example = "true")
public boolean interestType; public boolean interestType;
@Schema(example = "true") @Schema(example = "true")
public boolean transactionProcessingStrategyId; public boolean transactionProcessingStrategyCode;
@Schema(example = "true") @Schema(example = "true")
public boolean interestCalculationPeriodType; public boolean interestCalculationPeriodType;
@Schema(example = "true") @Schema(example = "true")
@@ -503,8 +503,8 @@ final class LoanProductsApiResourceSwagger {
public BigDecimal fixedPrincipalPercentagePerInstallment; public BigDecimal fixedPrincipalPercentagePerInstallment;
public GetLoanProductsInterestType interestType; public GetLoanProductsInterestType interestType;
public GetLoansProductsInterestCalculationPeriodType interestCalculationPeriodType; public GetLoansProductsInterestCalculationPeriodType interestCalculationPeriodType;
@Schema(example = "1") @Schema(example = "mifos-standard-strategy")
public Long transactionProcessingStrategyId; public String transactionProcessingStrategy;
@Schema(example = "Mifos style") @Schema(example = "Mifos style")
public String transactionProcessingStrategyName; public String transactionProcessingStrategyName;
@Schema(example = "[]") @Schema(example = "[]")
@@ -1211,8 +1211,8 @@ final class LoanProductsApiResourceSwagger {
public BigDecimal fixedPrincipalPercentagePerInstallment; public BigDecimal fixedPrincipalPercentagePerInstallment;
public GetLoanProductsTemplateResponse.GetLoanProductsInterestTemplateType interestType; public GetLoanProductsTemplateResponse.GetLoanProductsInterestTemplateType interestType;
public GetLoanProductsResponse.GetLoansProductsInterestCalculationPeriodType interestCalculationPeriodType; public GetLoanProductsResponse.GetLoansProductsInterestCalculationPeriodType interestCalculationPeriodType;
@Schema(example = "1") @Schema(example = "mifos-standard-strategy")
public Long transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
@Schema(example = "Mifos style") @Schema(example = "Mifos style")
public String transactionProcessingStrategyName; public String transactionProcessingStrategyName;
@Schema(example = "[]") @Schema(example = "[]")
@@ -107,7 +107,7 @@ public class LoanProductData implements Serializable {
private final EnumOptionData interestCalculationPeriodType; private final EnumOptionData interestCalculationPeriodType;
private final Boolean allowPartialPeriodInterestCalculation; private final Boolean allowPartialPeriodInterestCalculation;
private final BigDecimal inArrearsTolerance; private final BigDecimal inArrearsTolerance;
private final Long transactionProcessingStrategyId; private final String transactionProcessingStrategyCode;
private final String transactionProcessingStrategyName; private final String transactionProcessingStrategyName;
private final Integer graceOnPrincipalPayment; private final Integer graceOnPrincipalPayment;
private final Integer recurringMoratoriumOnPrincipalPeriods; private final Integer recurringMoratoriumOnPrincipalPeriods;
@@ -228,7 +228,7 @@ public class LoanProductData implements Serializable {
final Boolean allowPartialPeriodInterestCalcualtion = null; final Boolean allowPartialPeriodInterestCalcualtion = null;
final Long fundId = null; final Long fundId = null;
final String fundName = null; final String fundName = null;
final Long transactionProcessingStrategyId = null; final String transactionProcessingStrategyCode = null;
final String transactionProcessingStrategyName = null; final String transactionProcessingStrategyName = null;
final Integer graceOnPrincipalPayment = null; final Integer graceOnPrincipalPayment = null;
final Integer recurringMoratoriumOnPrincipalPeriods = null; final Integer recurringMoratoriumOnPrincipalPeriods = null;
@@ -281,7 +281,7 @@ public class LoanProductData implements Serializable {
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName, amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, graceOnPrincipalPayment, transactionProcessingStrategyCode, transactionProcessingStrategyName, graceOnPrincipalPayment,
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType,
includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
@@ -331,7 +331,7 @@ public class LoanProductData implements Serializable {
final Boolean allowPartialPeriodInterestCalcualtion = null; final Boolean allowPartialPeriodInterestCalcualtion = null;
final Long fundId = null; final Long fundId = null;
final String fundName = null; final String fundName = null;
final Long transactionProcessingStrategyId = null; final String transactionProcessingStrategyCode = null;
final String transactionProcessingStrategyName = null; final String transactionProcessingStrategyName = null;
final Integer graceOnPrincipalPayment = null; final Integer graceOnPrincipalPayment = null;
final Integer recurringMoratoriumOnPrincipalPeriods = null; final Integer recurringMoratoriumOnPrincipalPeriods = null;
@@ -386,7 +386,7 @@ public class LoanProductData implements Serializable {
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName, amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, graceOnPrincipalPayment, transactionProcessingStrategyCode, transactionProcessingStrategyName, graceOnPrincipalPayment,
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType,
includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
@@ -441,7 +441,7 @@ public class LoanProductData implements Serializable {
final Boolean allowPartialPeriodInterestCalcualtion = null; final Boolean allowPartialPeriodInterestCalcualtion = null;
final Long fundId = null; final Long fundId = null;
final String fundName = null; final String fundName = null;
final Long transactionProcessingStrategyId = null; final String transactionProcessingStrategyCode = null;
final String transactionProcessingStrategyName = null; final String transactionProcessingStrategyName = null;
final Integer graceOnPrincipalPayment = null; final Integer graceOnPrincipalPayment = null;
@@ -498,7 +498,7 @@ public class LoanProductData implements Serializable {
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName, amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, graceOnPrincipalPayment, transactionProcessingStrategyCode, transactionProcessingStrategyName, graceOnPrincipalPayment,
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType,
includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariationsForBorrowerCycle, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariationsForBorrowerCycle,
interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount,
@@ -547,7 +547,7 @@ public class LoanProductData implements Serializable {
final Boolean allowPartialPeriodInterestCalcualtion = null; final Boolean allowPartialPeriodInterestCalcualtion = null;
final Long fundId = null; final Long fundId = null;
final String fundName = null; final String fundName = null;
final Long transactionProcessingStrategyId = null; final String transactionProcessingStrategyCode = null;
final String transactionProcessingStrategyName = null; final String transactionProcessingStrategyName = null;
final Integer graceOnPrincipalPayment = null; final Integer graceOnPrincipalPayment = null;
@@ -604,7 +604,7 @@ public class LoanProductData implements Serializable {
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName, amortizationType, interestType, interestCalculationPeriodType, allowPartialPeriodInterestCalcualtion, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, graceOnPrincipalPayment, transactionProcessingStrategyCode, transactionProcessingStrategyName, graceOnPrincipalPayment,
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, charges, accountingType,
includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariationsForBorrowerCycle, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariationsForBorrowerCycle,
interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount,
@@ -638,7 +638,7 @@ public class LoanProductData implements Serializable {
final BigDecimal maxInterestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType, final BigDecimal maxInterestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType, final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final Boolean allowPartialPeriodInterestCalculation, final Long fundId, final EnumOptionData interestCalculationPeriodType, final Boolean allowPartialPeriodInterestCalculation, final Long fundId,
final String fundName, final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName, final String fundName, final String transactionProcessingStrategyCode, final String transactionProcessingStrategyName,
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods,
final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final Collection<ChargeData> charges, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final Collection<ChargeData> charges,
final EnumOptionData accountingType, final boolean includeInBorrowerCycle, boolean useBorrowerCycle, final LocalDate startDate, final EnumOptionData accountingType, final boolean includeInBorrowerCycle, boolean useBorrowerCycle, final LocalDate startDate,
@@ -703,7 +703,7 @@ public class LoanProductData implements Serializable {
this.allowPartialPeriodInterestCalculation = allowPartialPeriodInterestCalculation; this.allowPartialPeriodInterestCalculation = allowPartialPeriodInterestCalculation;
this.fundId = fundId; this.fundId = fundId;
this.fundName = fundName; this.fundName = fundName;
this.transactionProcessingStrategyId = transactionProcessingStrategyId; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
this.transactionProcessingStrategyName = transactionProcessingStrategyName; this.transactionProcessingStrategyName = transactionProcessingStrategyName;
this.charges = charges; this.charges = charges;
this.accountingRule = accountingType; this.accountingRule = accountingType;
@@ -858,10 +858,10 @@ public class LoanProductData implements Serializable {
if (this.transactionProcessingStrategyOptions != null && this.transactionProcessingStrategyOptions.size() == 1) { if (this.transactionProcessingStrategyOptions != null && this.transactionProcessingStrategyOptions.size() == 1) {
final List<TransactionProcessingStrategyData> listOfOptions = new ArrayList<>(this.transactionProcessingStrategyOptions); final List<TransactionProcessingStrategyData> listOfOptions = new ArrayList<>(this.transactionProcessingStrategyOptions);
this.transactionProcessingStrategyId = listOfOptions.get(0).id(); this.transactionProcessingStrategyCode = listOfOptions.get(0).getCode();
this.transactionProcessingStrategyName = listOfOptions.get(0).name(); this.transactionProcessingStrategyName = listOfOptions.get(0).getName();
} else { } else {
this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId; this.transactionProcessingStrategyCode = productData.transactionProcessingStrategyCode;
this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName; this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
} }
@@ -19,28 +19,18 @@
package org.apache.fineract.portfolio.loanproduct.data; package org.apache.fineract.portfolio.loanproduct.data;
import java.io.Serializable; import java.io.Serializable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/** /**
* Immutable data object representing a transaction strategy option for a loan. * Immutable data object representing a transaction strategy option for a loan.
*/ */
@RequiredArgsConstructor
@Getter
public final class TransactionProcessingStrategyData implements Serializable { public final class TransactionProcessingStrategyData implements Serializable {
private final Long id; private final Long id;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final String code; private final String code;
private final String name; private final String name;
public TransactionProcessingStrategyData(final Long id, final String code, final String name) {
this.id = id;
this.code = code;
this.name = name;
}
public Long id() {
return this.id;
}
public String name() {
return this.name;
}
} }
@@ -82,9 +82,11 @@ public class LoanProduct extends AbstractPersistableCustom {
@JoinColumn(name = "fund_id") @JoinColumn(name = "fund_id")
private Fund fund; private Fund fund;
@ManyToOne @Column(name = "loan_transaction_strategy_code", nullable = false)
@JoinColumn(name = "loan_transaction_strategy_id") private String transactionProcessingStrategyCode;
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "loan_transaction_strategy_name")
private String transactionProcessingStrategyName;
@Column(name = "name", nullable = false, unique = true) @Column(name = "name", nullable = false, unique = true)
private String name; private String name;
@@ -200,7 +202,7 @@ public class LoanProduct extends AbstractPersistableCustom {
@JoinColumn(name = "delinquency_bucket_id") @JoinColumn(name = "delinquency_bucket_id")
private DelinquencyBucket delinquencyBucket; private DelinquencyBucket delinquencyBucket;
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy, public static LoanProduct assembleFromJson(final Fund fund, final String loanTransactionProcessingStrategy,
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate, final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate,
final List<Rate> productRates) { final List<Rate> productRates) {
@@ -568,8 +570,8 @@ public class LoanProduct extends AbstractPersistableCustom {
this.loanProductMinMaxConstraints = null; this.loanProductMinMaxConstraints = null;
} }
public LoanProduct(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String name, public LoanProduct(final Fund fund, final String transactionProcessingStrategyCode, final String name, final String shortName,
final String shortName, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final BigDecimal defaultMinPrincipal, final BigDecimal defaultMaxPrincipal, final BigDecimal defaultMinPrincipal, final BigDecimal defaultMaxPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod, final BigDecimal defaultMinNominalInterestRatePerPeriod, final BigDecimal defaultNominalInterestRatePerPeriod, final BigDecimal defaultMinNominalInterestRatePerPeriod,
final BigDecimal defaultMaxNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal defaultMaxNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
@@ -599,7 +601,7 @@ public class LoanProduct extends AbstractPersistableCustom {
final boolean allowApprovedDisbursedAmountsOverApplied, final String overAppliedCalculationType, final boolean allowApprovedDisbursedAmountsOverApplied, final String overAppliedCalculationType,
final Integer overAppliedNumber) { final Integer overAppliedNumber) {
this.fund = fund; this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
this.name = name.trim(); this.name = name.trim();
this.shortName = shortName.trim(); this.shortName = shortName.trim();
if (StringUtils.isNotBlank(description)) { if (StringUtils.isNotBlank(description)) {
@@ -743,12 +745,16 @@ public class LoanProduct extends AbstractPersistableCustom {
this.fund = fund; this.fund = fund;
} }
public void update(final LoanTransactionProcessingStrategy strategy) { public void setTransactionProcessingStrategyCode(final String transactionProcessingStrategyCode) {
this.transactionProcessingStrategy = strategy; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
} }
public LoanTransactionProcessingStrategy getRepaymentStrategy() { public void setTransactionProcessingStrategyName(final String transactionProcessingStrategyName) {
return this.transactionProcessingStrategy; this.transactionProcessingStrategyName = transactionProcessingStrategyName;
}
public String getRepaymentStrategy() {
return this.transactionProcessingStrategyCode;
} }
public boolean hasCurrencyCodeOf(final String currencyCode) { public boolean hasCurrencyCodeOf(final String currencyCode) {
@@ -884,14 +890,10 @@ public class LoanProduct extends AbstractPersistableCustom {
actualChanges.put(fundIdParamName, newValue); actualChanges.put(fundIdParamName, newValue);
} }
Long existingStrategyId = null; final String transactionProcessingStrategyCodeParamName = "transactionProcessingStrategyCode";
if (this.transactionProcessingStrategy != null) { if (command.isChangeInStringParameterNamed(transactionProcessingStrategyCodeParamName, this.transactionProcessingStrategyCode)) {
existingStrategyId = this.transactionProcessingStrategy.getId(); final String newValue = command.stringValueOfParameterNamed(transactionProcessingStrategyCodeParamName);
} actualChanges.put(transactionProcessingStrategyCodeParamName, newValue);
final String transactionProcessingStrategyParamName = "transactionProcessingStrategyId";
if (command.isChangeInLongParameterNamed(transactionProcessingStrategyParamName, existingStrategyId)) {
final Long newValue = command.longValueOfParameterNamed(transactionProcessingStrategyParamName);
actualChanges.put(transactionProcessingStrategyParamName, newValue);
} }
final String chargesParamName = "charges"; final String chargesParamName = "charges";
@@ -1035,11 +1037,11 @@ public class LoanProduct extends AbstractPersistableCustom {
} }
if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName) if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
.getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyIdParamName) .getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyCodeParamName)
.getAsBoolean() != this.loanConfigurableAttributes.getTransactionProcessingStrategyBoolean()) { .getAsBoolean() != this.loanConfigurableAttributes.getTransactionProcessingStrategyBoolean()) {
this.loanConfigurableAttributes.setTransactionProcessingStrategyId( this.loanConfigurableAttributes.setTransactionProcessingStrategyCode(
command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName) command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
.getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyIdParamName).getAsBoolean()); .getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyCodeParamName).getAsBoolean());
} }
if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName) if (command.parsedJson().getAsJsonObject().getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
@@ -43,8 +43,8 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
@Column(name = "interest_method_enum") @Column(name = "interest_method_enum")
private Boolean interestType; private Boolean interestType;
@Column(name = "loan_transaction_strategy_id") @Column(name = "loan_transaction_strategy_code")
private Boolean transactionProcessingStrategyId; private Boolean transactionProcessingStrategyCode;
@Column(name = "interest_calculated_in_period_enum") @Column(name = "interest_calculated_in_period_enum")
private Boolean interestCalculationPeriodType; private Boolean interestCalculationPeriodType;
@@ -62,7 +62,7 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
private Boolean graceOnArrearsAgeing; private Boolean graceOnArrearsAgeing;
private static final String[] supportedLoanConfigurableAttributes = { LoanProductConstants.amortizationTypeParamName, private static final String[] supportedLoanConfigurableAttributes = { LoanProductConstants.amortizationTypeParamName,
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
LoanProductConstants.interestCalculationPeriodTypeParamName, LoanProductConstants.inArrearsToleranceParamName, LoanProductConstants.interestCalculationPeriodTypeParamName, LoanProductConstants.inArrearsToleranceParamName,
LoanProductConstants.repaymentEveryParamName, LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName, LoanProductConstants.repaymentEveryParamName, LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName,
LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME }; LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME };
@@ -77,7 +77,7 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
.getAsJsonPrimitive(LoanProductConstants.interestTypeParamName).getAsBoolean(); .getAsJsonPrimitive(LoanProductConstants.interestTypeParamName).getAsBoolean();
final Boolean transactionProcessingStrategy = command.parsedJson().getAsJsonObject() final Boolean transactionProcessingStrategy = command.parsedJson().getAsJsonObject()
.getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName) .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
.getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyIdParamName).getAsBoolean(); .getAsJsonPrimitive(LoanProductConstants.transactionProcessingStrategyCodeParamName).getAsBoolean();
final Boolean interestCalcPeriod = command.parsedJson().getAsJsonObject() final Boolean interestCalcPeriod = command.parsedJson().getAsJsonObject()
.getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName) .getAsJsonObject(LoanProductConstants.allowAttributeOverridesParamName)
.getAsJsonPrimitive(LoanProductConstants.interestCalculationPeriodTypeParamName).getAsBoolean(); .getAsJsonPrimitive(LoanProductConstants.interestCalculationPeriodTypeParamName).getAsBoolean();
@@ -116,7 +116,7 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
arrearsTolerance, repaymentEvery, graceOnPrincipalAndInterestPayment, graceOnArrearsAging); arrearsTolerance, repaymentEvery, graceOnPrincipalAndInterestPayment, graceOnArrearsAging);
} }
public LoanProductConfigurableAttributes(Boolean amortization, Boolean interestMethod, Boolean transactionProcessingStrategy, public LoanProductConfigurableAttributes(Boolean amortization, Boolean interestMethod, Boolean transactionProcessingStrategyCode,
Boolean interestCalcPeriod, Boolean arrearsTolerance, Boolean repaymentEvery, Boolean graceOnPrincipalAndInterestPayment, Boolean interestCalcPeriod, Boolean arrearsTolerance, Boolean repaymentEvery, Boolean graceOnPrincipalAndInterestPayment,
Boolean graceOnArrearsAging) { Boolean graceOnArrearsAging) {
this.amortizationType = amortization; this.amortizationType = amortization;
@@ -126,7 +126,7 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
this.interestCalculationPeriodType = interestCalcPeriod; this.interestCalculationPeriodType = interestCalcPeriod;
this.graceOnPrincipalAndInterestPayment = graceOnPrincipalAndInterestPayment; this.graceOnPrincipalAndInterestPayment = graceOnPrincipalAndInterestPayment;
this.repaymentEvery = repaymentEvery; this.repaymentEvery = repaymentEvery;
this.transactionProcessingStrategyId = transactionProcessingStrategy; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
} }
protected LoanProductConfigurableAttributes() { protected LoanProductConfigurableAttributes() {
@@ -150,7 +150,7 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
} }
public Boolean getTransactionProcessingStrategyBoolean() { public Boolean getTransactionProcessingStrategyBoolean() {
return transactionProcessingStrategyId; return transactionProcessingStrategyCode;
} }
public Boolean getInterestCalcPeriodBoolean() { public Boolean getInterestCalcPeriodBoolean() {
@@ -185,8 +185,8 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
this.interestType = interestType; this.interestType = interestType;
} }
public void setTransactionProcessingStrategyId(Boolean transactionProcessingStrategyId) { public void setTransactionProcessingStrategyCode(Boolean transactionProcessingStrategyCode) {
this.transactionProcessingStrategyId = transactionProcessingStrategyId; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
} }
public void setInterestCalculationPeriodType(Boolean interestCalculationPeriodType) { public void setInterestCalculationPeriodType(Boolean interestCalculationPeriodType) {
@@ -223,7 +223,7 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
return Objects.equals(loanProduct, that.loanProduct) && Objects.equals(amortizationType, that.amortizationType) return Objects.equals(loanProduct, that.loanProduct) && Objects.equals(amortizationType, that.amortizationType)
&& Objects.equals(interestType, that.interestType) && Objects.equals(interestType, that.interestType)
&& Objects.equals(transactionProcessingStrategyId, that.transactionProcessingStrategyId) && Objects.equals(transactionProcessingStrategyCode, that.transactionProcessingStrategyCode)
&& Objects.equals(interestCalculationPeriodType, that.interestCalculationPeriodType) && Objects.equals(interestCalculationPeriodType, that.interestCalculationPeriodType)
&& Objects.equals(inArrearsTolerance, that.inArrearsTolerance) && Objects.equals(repaymentEvery, that.repaymentEvery) && Objects.equals(inArrearsTolerance, that.inArrearsTolerance) && Objects.equals(repaymentEvery, that.repaymentEvery)
&& Objects.equals(graceOnPrincipalAndInterestPayment, that.graceOnPrincipalAndInterestPayment) && Objects.equals(graceOnPrincipalAndInterestPayment, that.graceOnPrincipalAndInterestPayment)
@@ -232,7 +232,7 @@ public class LoanProductConfigurableAttributes extends AbstractPersistableCustom
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(loanProduct, amortizationType, interestType, transactionProcessingStrategyId, interestCalculationPeriodType, return Objects.hash(loanProduct, amortizationType, interestType, transactionProcessingStrategyCode, interestCalculationPeriodType,
inArrearsTolerance, repaymentEvery, graceOnPrincipalAndInterestPayment, graceOnArrearsAgeing); inArrearsTolerance, repaymentEvery, graceOnPrincipalAndInterestPayment, graceOnArrearsAgeing);
} }
} }
@@ -1,77 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanproduct.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
@Entity
@Table(name = "ref_loan_transaction_processing_strategy")
public class LoanTransactionProcessingStrategy extends AbstractPersistableCustom {
@Column(name = "code", unique = true)
private String code;
@Column(name = "name")
private String name;
@Column(name = "sort_order")
private Integer sortOrder; // Don't change this name as this property name
// is used as sort order while retrieving this
// objects
protected LoanTransactionProcessingStrategy() {
//
}
public TransactionProcessingStrategyData toData() {
return new TransactionProcessingStrategyData(getId(), this.code, this.name);
}
public boolean isStandardStrategy() {
return "mifos-standard-strategy".equalsIgnoreCase(this.code);
}
public boolean isHeavensfamilyStrategy() {
return "heavensfamily-strategy".equalsIgnoreCase(this.code);
}
public boolean isEarlyPaymentStrategy() {
return "early-repayment-strategy".equalsIgnoreCase(this.code);
}
public boolean isCreocoreStrategy() {
return "creocore-strategy".equalsIgnoreCase(this.code);
}
public boolean isIndianRBIStrategy() {
return "rbi-india-strategy".equalsIgnoreCase(this.code);
}
public boolean isPrincipalInterestPenaltiesFeesOrderStrategy() {
return "principal-interest-penalties-fees-order-strategy".equalsIgnoreCase(this.code);
}
public boolean isInterestPrincipalPenaltiesFeesOrderStrategy() {
return "interest-principal-penalties-fees-order-strategy".equalsIgnoreCase(this.code);
}
}
@@ -32,6 +32,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams; import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType; import org.apache.fineract.accounting.common.AccountingRuleType;
@@ -43,7 +45,10 @@ import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils; import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants; import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
@@ -53,9 +58,10 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType; import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException; import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component @Component
public final class LoanProductDataValidator { public final class LoanProductDataValidator {
@@ -78,7 +84,7 @@ public final class LoanProductDataValidator {
public static final String INTEREST_TYPE = "interestType"; public static final String INTEREST_TYPE = "interestType";
public static final String INTEREST_CALCULATION_PERIOD_TYPE = "interestCalculationPeriodType"; public static final String INTEREST_CALCULATION_PERIOD_TYPE = "interestCalculationPeriodType";
public static final String IN_ARREARS_TOLERANCE = "inArrearsTolerance"; public static final String IN_ARREARS_TOLERANCE = "inArrearsTolerance";
public static final String TRANSACTION_PROCESSING_STRATEGY_ID = "transactionProcessingStrategyId"; public static final String TRANSACTION_PROCESSING_STRATEGY_CODE = "transactionProcessingStrategyCode";
public static final String GRACE_ON_PRINCIPAL_PAYMENT = "graceOnPrincipalPayment"; public static final String GRACE_ON_PRINCIPAL_PAYMENT = "graceOnPrincipalPayment";
public static final String GRACE_ON_INTEREST_PAYMENT = "graceOnInterestPayment"; public static final String GRACE_ON_INTEREST_PAYMENT = "graceOnInterestPayment";
public static final String GRACE_ON_INTEREST_CHARGED = "graceOnInterestCharged"; public static final String GRACE_ON_INTEREST_CHARGED = "graceOnInterestCharged";
@@ -102,7 +108,7 @@ public final class LoanProductDataValidator {
NUMBER_OF_REPAYMENTS, MIN_NUMBER_OF_REPAYMENTS, MAX_NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY_TYPE, INTEREST_RATE_PER_PERIOD, NUMBER_OF_REPAYMENTS, MIN_NUMBER_OF_REPAYMENTS, MAX_NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY_TYPE, INTEREST_RATE_PER_PERIOD,
MIN_INTEREST_RATE_PER_PERIOD, MAX_INTEREST_RATE_PER_PERIOD, INTEREST_RATE_FREQUENCY_TYPE, AMORTIZATION_TYPE, INTEREST_TYPE, MIN_INTEREST_RATE_PER_PERIOD, MAX_INTEREST_RATE_PER_PERIOD, INTEREST_RATE_FREQUENCY_TYPE, AMORTIZATION_TYPE, INTEREST_TYPE,
INTEREST_CALCULATION_PERIOD_TYPE, LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME, INTEREST_CALCULATION_PERIOD_TYPE, LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
IN_ARREARS_TOLERANCE, TRANSACTION_PROCESSING_STRATEGY_ID, GRACE_ON_PRINCIPAL_PAYMENT, "recurringMoratoriumOnPrincipalPeriods", IN_ARREARS_TOLERANCE, TRANSACTION_PROCESSING_STRATEGY_CODE, GRACE_ON_PRINCIPAL_PAYMENT, "recurringMoratoriumOnPrincipalPeriods",
GRACE_ON_INTEREST_PAYMENT, GRACE_ON_INTEREST_CHARGED, "charges", ACCOUNTING_RULE, INCLUDE_IN_BORROWER_CYCLE, "startDate", GRACE_ON_INTEREST_PAYMENT, GRACE_ON_INTEREST_CHARGED, "charges", ACCOUNTING_RULE, INCLUDE_IN_BORROWER_CYCLE, "startDate",
"closeDate", "externalId", IS_LINKED_TO_FLOATING_INTEREST_RATES, FLOATING_RATES_ID, INTEREST_RATE_DIFFERENTIAL, "closeDate", "externalId", IS_LINKED_TO_FLOATING_INTEREST_RATES, FLOATING_RATES_ID, INTEREST_RATE_DIFFERENTIAL,
MIN_DIFFERENTIAL_LENDING_RATE, DEFAULT_DIFFERENTIAL_LENDING_RATE, MAX_DIFFERENTIAL_LENDING_RATE, MIN_DIFFERENTIAL_LENDING_RATE, DEFAULT_DIFFERENTIAL_LENDING_RATE, MAX_DIFFERENTIAL_LENDING_RATE,
@@ -149,7 +155,7 @@ public final class LoanProductDataValidator {
LoanProductConstants.OVER_APPLIED_NUMBER, LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME)); LoanProductConstants.OVER_APPLIED_NUMBER, LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME));
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName, private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
LoanProductConstants.interestCalculationPeriodTypeParamName, LoanProductConstants.inArrearsToleranceParamName, LoanProductConstants.interestCalculationPeriodTypeParamName, LoanProductConstants.inArrearsToleranceParamName,
LoanProductConstants.repaymentEveryParamName, LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName, LoanProductConstants.repaymentEveryParamName, LoanProductConstants.graceOnPrincipalAndInterestPaymentParamName,
LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME }; LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME };
@@ -161,10 +167,7 @@ public final class LoanProductDataValidator {
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@Autowired private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
public LoanProductDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) { public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { if (StringUtils.isBlank(json)) {
@@ -310,9 +313,19 @@ public final class LoanProductDataValidator {
final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(IN_ARREARS_TOLERANCE, element); final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(IN_ARREARS_TOLERANCE, element);
baseDataValidator.reset().parameter(IN_ARREARS_TOLERANCE).value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount(); baseDataValidator.reset().parameter(IN_ARREARS_TOLERANCE).value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed(TRANSACTION_PROCESSING_STRATEGY_ID, element); final String transactionProcessingStrategyCode = this.fromApiJsonHelper.extractStringNamed(TRANSACTION_PROCESSING_STRATEGY_CODE,
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_ID).value(transactionProcessingStrategyId).notNull() element);
.integerGreaterThanZero(); baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).value(transactionProcessingStrategyCode).notNull();
LoanRepaymentScheduleTransactionProcessor processor = loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(transactionProcessingStrategyCode);
if (processor == null) {
Object[] values = loanRepaymentScheduleTransactionProcessorFactory.getStrategies().stream()
.map(TransactionProcessingStrategyData::getCode).toList().toArray(new String[0]);
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).isOneOfTheseValues(values);
}
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, element)) { if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, element)) {
final Long delinquencyBucketId = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, final Long delinquencyBucketId = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME,
@@ -1077,10 +1090,10 @@ public final class LoanProductDataValidator {
baseDataValidator.reset().parameter(REPAYMENT_FREQUENCY_TYPE).value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3); baseDataValidator.reset().parameter(REPAYMENT_FREQUENCY_TYPE).value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
} }
if (this.fromApiJsonHelper.parameterExists(TRANSACTION_PROCESSING_STRATEGY_ID, element)) { if (this.fromApiJsonHelper.parameterExists(TRANSACTION_PROCESSING_STRATEGY_CODE, element)) {
final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed(TRANSACTION_PROCESSING_STRATEGY_ID, final String transactionProcessingStrategyCode = this.fromApiJsonHelper.extractStringNamed(TRANSACTION_PROCESSING_STRATEGY_CODE,
element); element);
baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_ID).value(transactionProcessingStrategyId).notNull() baseDataValidator.reset().parameter(TRANSACTION_PROCESSING_STRATEGY_CODE).value(transactionProcessingStrategyCode).notNull()
.integerGreaterThanZero(); .integerGreaterThanZero();
} }
@@ -34,15 +34,15 @@ import static org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations
import static org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType; import static org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType;
import static org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType; import static org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.common.domain.DayOfWeekType; import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
import org.apache.fineract.portfolio.common.domain.NthDayType; import org.apache.fineract.portfolio.common.domain.NthDayType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData; import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
@@ -51,22 +51,14 @@ import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCom
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy; import org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod; import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType; import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor
public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlatformService { public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlatformService {
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository; private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
@Autowired
public LoanDropdownReadPlatformServiceImpl(
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository) {
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
}
@Override @Override
public List<EnumOptionData> retrieveLoanAmortizationTypeOptions() { public List<EnumOptionData> retrieveLoanAmortizationTypeOptions() {
@@ -125,15 +117,7 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat
@Override @Override
public Collection<TransactionProcessingStrategyData> retrieveTransactionProcessingStrategies() { public Collection<TransactionProcessingStrategyData> retrieveTransactionProcessingStrategies() {
return loanRepaymentScheduleTransactionProcessorFactory.getStrategies();
final Collection<TransactionProcessingStrategyData> strategyOptions = new ArrayList<>();
Sort sort = Sort.by("sortOrder");
final List<LoanTransactionProcessingStrategy> strategies = this.loanTransactionProcessingStrategyRepository.findAll(sort);
for (final LoanTransactionProcessingStrategy strategy : strategies) {
strategyOptions.add(strategy.toData());
}
return strategyOptions;
} }
@Override @Override
@@ -202,7 +202,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
} }
public String loanProductSchema() { public String loanProductSchema() {
return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_id as transactionStrategyId, ltps.name as transactionStrategyName, " return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_code as transactionStrategyCode, lp.loan_transaction_strategy_name as transactionStrategyName, "
+ "lp.name as name, lp.short_name as shortName, lp.description as description, " + "lp.name as name, lp.short_name as shortName, lp.description as description, "
+ "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, lp.currency_multiplesof as inMultiplesOf, " + "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, lp.currency_multiplesof as inMultiplesOf, "
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, " + "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
@@ -236,7 +236,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lp.account_moves_out_of_npa_only_on_arrears_completion as accountMovesOutOfNPAOnlyOnArrearsCompletion, " + "lp.account_moves_out_of_npa_only_on_arrears_completion as accountMovesOutOfNPAOnlyOnArrearsCompletion, "
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId, " + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId, "
+ "lca.id as lcaId, lca.amortization_method_enum as amortizationBoolean, lca.interest_method_enum as interestMethodConfigBoolean, " + "lca.id as lcaId, lca.amortization_method_enum as amortizationBoolean, lca.interest_method_enum as interestMethodConfigBoolean, "
+ "lca.loan_transaction_strategy_id as transactionProcessingStrategyBoolean,lca.interest_calculated_in_period_enum as interestCalcPeriodBoolean, lca.arrearstolerance_amount as arrearsToleranceBoolean, " + "lca.loan_transaction_strategy_code as transactionProcessingStrategyBoolean,lca.interest_calculated_in_period_enum as interestCalcPeriodBoolean, lca.arrearstolerance_amount as arrearsToleranceBoolean, "
+ "lca.repay_every as repaymentFrequencyBoolean, lca.moratorium as graceOnPrincipalAndInterestBoolean, lca.grace_on_arrears_ageing as graceOnArrearsAgingBoolean, " + "lca.repay_every as repaymentFrequencyBoolean, lca.moratorium as graceOnPrincipalAndInterestBoolean, lca.grace_on_arrears_ageing as graceOnArrearsAgingBoolean, "
+ "lp.is_linked_to_floating_interest_rates as isLinkedToFloatingInterestRates, " + "lp.is_linked_to_floating_interest_rates as isLinkedToFloatingInterestRates, "
+ "lfr.floating_rates_id as floatingRateId, " + "fr.name as floatingRateName, " + "lfr.floating_rates_id as floatingRateId, " + "fr.name as floatingRateName, "
@@ -251,7 +251,6 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id " + " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id "
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id " + " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
+ " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id " + " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id "
+ " left join ref_loan_transaction_processing_strategy ltps on ltps.id = lp.loan_transaction_strategy_id"
+ " left join m_product_loan_configurable_attributes lca on lca.loan_product_id = lp.id " + " left join m_product_loan_configurable_attributes lca on lca.loan_product_id = lp.id "
+ " left join m_product_loan_floating_rates as lfr on lfr.loan_product_id = lp.id " + " left join m_product_loan_floating_rates as lfr on lfr.loan_product_id = lp.id "
+ " left join m_floating_rates as fr on lfr.floating_rates_id = fr.id " + " left join m_floating_rates as fr on lfr.floating_rates_id = fr.id "
@@ -270,7 +269,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final String description = rs.getString("description"); final String description = rs.getString("description");
final Long fundId = JdbcSupport.getLong(rs, "fundId"); final Long fundId = JdbcSupport.getLong(rs, "fundId");
final String fundName = rs.getString("fundName"); final String fundName = rs.getString("fundName");
final Long transactionStrategyId = JdbcSupport.getLong(rs, "transactionStrategyId"); final String transactionStrategyCode = rs.getString("transactionStrategyCode");
final String transactionStrategyName = rs.getString("transactionStrategyName"); final String transactionStrategyName = rs.getString("transactionStrategyName");
final String currencyCode = rs.getString("currencyCode"); final String currencyCode = rs.getString("currencyCode");
@@ -487,7 +486,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType,
allowPartialPeriodInterestCalcualtion, fundId, fundName, transactionStrategyId, transactionStrategyName, allowPartialPeriodInterestCalcualtion, fundId, fundName, transactionStrategyCode, transactionStrategyName,
graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged,
this.charges, accountingRuleType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, this.charges, accountingRuleType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId,
principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle,
@@ -25,6 +25,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.persistence.PersistenceException; import javax.persistence.PersistenceException;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -45,14 +46,12 @@ import org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepository
import org.apache.fineract.portfolio.fund.domain.Fund; import org.apache.fineract.portfolio.fund.domain.Fund;
import org.apache.fineract.portfolio.fund.domain.FundRepository; import org.apache.fineract.portfolio.fund.domain.FundRepository;
import org.apache.fineract.portfolio.fund.exception.FundNotFoundException; import org.apache.fineract.portfolio.fund.exception.FundNotFoundException;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants; import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException; import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductCannotBeModifiedDueToNonClosedLoansException; import org.apache.fineract.portfolio.loanproduct.exception.LoanProductCannotBeModifiedDueToNonClosedLoansException;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductDateException; import org.apache.fineract.portfolio.loanproduct.exception.LoanProductDateException;
@@ -62,13 +61,13 @@ import org.apache.fineract.portfolio.rate.domain.Rate;
import org.apache.fineract.portfolio.rate.domain.RateRepositoryWrapper; import org.apache.fineract.portfolio.rate.domain.RateRepositoryWrapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Service @Service
@RequiredArgsConstructor
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService { public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
private static final Logger LOG = LoggerFactory.getLogger(LoanProductWritePlatformServiceJpaRepositoryImpl.class); private static final Logger LOG = LoggerFactory.getLogger(LoanProductWritePlatformServiceJpaRepositoryImpl.class);
@@ -77,7 +76,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final LoanProductRepository loanProductRepository; private final LoanProductRepository loanProductRepository;
private final AprCalculator aprCalculator; private final AprCalculator aprCalculator;
private final FundRepository fundRepository; private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final ChargeRepositoryWrapper chargeRepository; private final ChargeRepositoryWrapper chargeRepository;
private final RateRepositoryWrapper rateRepository; private final RateRepositoryWrapper rateRepository;
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
@@ -86,32 +84,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final LoanRepositoryWrapper loanRepositoryWrapper; private final LoanRepositoryWrapper loanRepositoryWrapper;
private final BusinessEventNotifierService businessEventNotifierService; private final BusinessEventNotifierService businessEventNotifierService;
private final DelinquencyBucketRepository delinquencyBucketRepository; private final DelinquencyBucketRepository delinquencyBucketRepository;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
@Autowired
public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final LoanProductDataValidator fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
final AprCalculator aprCalculator, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final ChargeRepositoryWrapper chargeRepository, final RateRepositoryWrapper rateRepository,
final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
final FineractEntityAccessUtil fineractEntityAccessUtil, final FloatingRateRepositoryWrapper floatingRateRepository,
final LoanRepositoryWrapper loanRepositoryWrapper, final BusinessEventNotifierService businessEventNotifierService,
final DelinquencyBucketRepository delinquencyBucketRepository) {
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.loanProductRepository = loanProductRepository;
this.aprCalculator = aprCalculator;
this.fundRepository = fundRepository;
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
this.chargeRepository = chargeRepository;
this.rateRepository = rateRepository;
this.accountMappingWritePlatformService = accountMappingWritePlatformService;
this.fineractEntityAccessUtil = fineractEntityAccessUtil;
this.floatingRateRepository = floatingRateRepository;
this.loanRepositoryWrapper = loanRepositoryWrapper;
this.businessEventNotifierService = businessEventNotifierService;
this.delinquencyBucketRepository = delinquencyBucketRepository;
}
@Transactional @Transactional
@Override @Override
@@ -126,9 +99,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId")); final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId"));
final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId"); final String loanTransactionProcessingStrategyCode = command.stringValueOfParameterNamed("transactionProcessingStrategyCode");
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(
transactionProcessingStrategyId);
final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
final List<Charge> charges = assembleListOfProductCharges(command, currencyCode); final List<Charge> charges = assembleListOfProductCharges(command, currencyCode);
@@ -139,9 +110,11 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
floatingRate = this.floatingRateRepository floatingRate = this.floatingRateRepository
.findOneWithNotFoundDetection(command.longValueOfParameterNamed("floatingRatesId")); .findOneWithNotFoundDetection(command.longValueOfParameterNamed("floatingRatesId"));
} }
final LoanProduct loanProduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command, final LoanProduct loanProduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategyCode, charges, command,
this.aprCalculator, floatingRate, rates); this.aprCalculator, floatingRate, rates);
loanProduct.updateLoanProductInRelatedClasses(); loanProduct.updateLoanProductInRelatedClasses();
loanProduct.setTransactionProcessingStrategyName(
loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(loanTransactionProcessingStrategyCode).getName());
if (command.parameterExists("delinquencyBucketId")) { if (command.parameterExists("delinquencyBucketId")) {
DelinquencyBucket delinquencyBucket = this.delinquencyBucketRepository DelinquencyBucket delinquencyBucket = this.delinquencyBucketRepository
@@ -177,15 +150,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
} }
private LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
LoanTransactionProcessingStrategy strategy = null;
if (transactionProcessingStrategyId != null) {
return this.loanTransactionProcessingStrategyRepository.findById(transactionProcessingStrategyId)
.orElseThrow(() -> new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategyId));
}
return strategy;
}
private Fund findFundByIdIfProvided(final Long fundId) { private Fund findFundByIdIfProvided(final Long fundId) {
Fund fund = null; Fund fund = null;
if (fundId != null) { if (fundId != null) {
@@ -232,11 +196,12 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
product.setDelinquencyBucket(delinquencyBucket); product.setDelinquencyBucket(delinquencyBucket);
} }
if (changes.containsKey("transactionProcessingStrategyId")) { if (changes.containsKey("transactionProcessingStrategyCode")) {
final Long transactionProcessingStrategyId = (Long) changes.get("transactionProcessingStrategyId"); final String transactionProcessingStrategyCode = (String) changes.get("transactionProcessingStrategyCode");
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided( final String transactionProcessingStrategyName = loanRepaymentScheduleTransactionProcessorFactory
transactionProcessingStrategyId); .determineProcessor(transactionProcessingStrategyCode).getName();
product.update(loanTransactionProcessingStrategy); product.setTransactionProcessingStrategyCode(transactionProcessingStrategyCode);
product.setTransactionProcessingStrategyName(transactionProcessingStrategyName);
} }
if (changes.containsKey("charges")) { if (changes.containsKey("charges")) {
@@ -204,9 +204,9 @@ public class SelfLoansApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Calculate Loan Repayment Schedule | Submit a new Loan Application", description = "Calculate Loan Repayment Schedule:\n\n" @Operation(summary = "Calculate Loan Repayment Schedule | Submit a new Loan Application", description = "Calculate Loan Repayment Schedule:\n\n"
+ "Calculates Loan Repayment Schedule\n\n" + "Calculates Loan Repayment Schedule\n\n"
+ "Mandatory Fields: productId, principal, loanTermFrequency, loanTermFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, amortizationType, interestType, interestCalculationPeriodType, expectedDisbursementDate, transactionProcessingStrategyId\n\n" + "Mandatory Fields: productId, principal, loanTermFrequency, loanTermFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, amortizationType, interestType, interestCalculationPeriodType, expectedDisbursementDate, transactionProcessingStrategyCode\n\n"
+ "Submit a new Loan Application:\n\n" + "Submit a new Loan Application:\n\n"
+ "Mandatory Fields: clientId, productId, principal, loanTermFrequency, loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyId, expectedDisbursementDate, submittedOnDate, loanType\n\n" + "Mandatory Fields: clientId, productId, principal, loanTermFrequency, loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyCode, expectedDisbursementDate, submittedOnDate, loanType\n\n"
+ "Additional Mandatory Fields if interest recalculation is enabled for product and Rest frequency not same as repayment period: recalculationRestFrequencyDate\n\n" + "Additional Mandatory Fields if interest recalculation is enabled for product and Rest frequency not same as repayment period: recalculationRestFrequencyDate\n\n"
+ "Additional Mandatory Fields if interest recalculation with interest/fee compounding is enabled for product and compounding frequency not same as repayment period: recalculationCompoundingFrequencyDate\n\n" + "Additional Mandatory Fields if interest recalculation with interest/fee compounding is enabled for product and compounding frequency not same as repayment period: recalculationCompoundingFrequencyDate\n\n"
+ "Additional Mandatory Field if Entity-Datatable Check is enabled for the entity of type loan: datatables\n\n" + "Additional Mandatory Field if Entity-Datatable Check is enabled for the entity of type loan: datatables\n\n"
@@ -482,8 +482,8 @@ final class SelfLoansApiResourceSwagger {
public GetLoansLoanIdAmortizationType amortizationType; public GetLoansLoanIdAmortizationType amortizationType;
public GetLoansLoanIdInterestType interestType; public GetLoansLoanIdInterestType interestType;
public GetLoansLoanIdInterestCalculationPeriodType interestCalculationPeriodType; public GetLoansLoanIdInterestCalculationPeriodType interestCalculationPeriodType;
@Schema(example = "2") @Schema(example = "mifos-standard-strategy")
public Integer transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
public GetLoansLoanIdTimeline timeline; public GetLoansLoanIdTimeline timeline;
public GetLoansLoanIdSummary summary; public GetLoansLoanIdSummary summary;
} }
@@ -694,8 +694,8 @@ final class SelfLoansApiResourceSwagger {
public Integer interestType; public Integer interestType;
@Schema(example = "1") @Schema(example = "1")
public Integer interestCalculationPeriodType; public Integer interestCalculationPeriodType;
@Schema(example = "1") @Schema(example = "mifos-standard-strategy")
public Integer transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
@Schema(example = "10 Jun 2013") @Schema(example = "10 Jun 2013")
public String expectedDisbursementDate; public String expectedDisbursementDate;
@Schema(example = "10 Jun 2013") @Schema(example = "10 Jun 2013")
@@ -758,8 +758,8 @@ final class SelfLoansApiResourceSwagger {
public Integer amortizationType; public Integer amortizationType;
@Schema(example = "04 March 2014") @Schema(example = "04 March 2014")
public String expectedDisbursementDate; public String expectedDisbursementDate;
@Schema(example = "1") @Schema(example = "mifos-standard-strategy")
public Integer transactionProcessingStrategyId; public String transactionProcessingStrategyCode;
} }
@Schema(description = "PutSelfLoansLoanIdResponse") @Schema(description = "PutSelfLoansLoanIdResponse")
@@ -72,7 +72,7 @@ import org.springframework.stereotype.Component;
+ "The minimum number of days allowed between a Loan disbursal and its first repayment.\n" + "principalThresholdForLastInstalment\n" + "The minimum number of days allowed between a Loan disbursal and its first repayment.\n" + "principalThresholdForLastInstalment\n"
+ "Field represents percentage of current instalment principal amount for comparing against principal outstanding to add another repayment instalment. If the outstanding principal amount is less then calculated amount, remaining outstanding amount will be added to current instalment. Default value for multi disburse loan is 50% and non-multi disburse loan is 0%\n" + "Field represents percentage of current instalment principal amount for comparing against principal outstanding to add another repayment instalment. If the outstanding principal amount is less then calculated amount, remaining outstanding amount will be added to current instalment. Default value for multi disburse loan is 50% and non-multi disburse loan is 0%\n"
+ "canDefineInstallmentAmount\n" + "if provided as true, then fixed instalment amount can be provided from loan account.\n" + "canDefineInstallmentAmount\n" + "if provided as true, then fixed instalment amount can be provided from loan account.\n"
+ "transactionProcessingStrategyId\n" + "transactionProcessingStrategyCode\n"
+ "An enumeration that indicates the type of transaction processing strategy to be used. This relates to functionality that is also known as Payment Application Logic.\n" + "An enumeration that indicates the type of transaction processing strategy to be used. This relates to functionality that is also known as Payment Application Logic.\n"
+ "A number of out of the box approaches exist, some are custom to specific MFIs, some are more general and indicate the order in which payments are processed.\n" + "A number of out of the box approaches exist, some are custom to specific MFIs, some are more general and indicate the order in which payments are processed.\n"
+ "\n" + "\n"
@@ -61,6 +61,15 @@ fineract.events.external.producer.jms.broker-url=${FINERACT_EXTERNAL_EVENTS_PROD
fineract.idempotency-key-header-name=${FINERACT_IDEMPOTENCY_KEY_HEADER_NAME:Idempotency-Key} fineract.idempotency-key-header-name=${FINERACT_IDEMPOTENCY_KEY_HEADER_NAME:Idempotency-Key}
fineract.loan.transactionprocessor.creocore.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_CREOCORE_ENABLED:true}
fineract.loan.transactionprocessor.early-repayment.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_EARLY_REPAYMENT_ENABLED:true}
fineract.loan.transactionprocessor.mifos-standard.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_MIFOS_STANDARD_ENABLED:true}
fineract.loan.transactionprocessor.heavensfamily.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_HEAVENSFAMILY_ENABLED:true}
fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_INTEREST_PRINCIPAL_PENALTIES_FEES_ENABLED:true}
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_PRINCIPAL_INTEREST_PENALTIES_FEES_ENABLED:true}
fineract.loan.transactionprocessor.rbi-india.enabled=${FINERACT_LOAN_TRANSACTIONPROCESSOR_RBI_INDIA_ENABLED:true}
fineract.loan.transactionprocessor.error-not-found-fail=${FINERACT_LOAN_TRANSACTIONPROCESSOR_ERROR_NOT_FOUND_FAIL:true}
# Logging pattern for the console # Logging pattern for the console
logging.pattern.console=${CONSOLE_LOG_PATTERN:%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(%replace([%X{correlationId}]){'\\[\\]', ''}) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}} logging.pattern.console=${CONSOLE_LOG_PATTERN:%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(%replace([%X{correlationId}]){'\\[\\]', ''}) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}}
@@ -83,4 +83,5 @@
<include file="parts/0061_add_idempotency_key_to_command_source.xml" relativeToChangelogFile="true"/> <include file="parts/0061_add_idempotency_key_to_command_source.xml" relativeToChangelogFile="true"/>
<include file="parts/0062_add_fraud_attribute_to_loan.xml" relativeToChangelogFile="true"/> <include file="parts/0062_add_fraud_attribute_to_loan.xml" relativeToChangelogFile="true"/>
<include file="parts/0063_add_permissions_for_external_event_configuration.xml" relativeToChangelogFile="true"/> <include file="parts/0063_add_permissions_for_external_event_configuration.xml" relativeToChangelogFile="true"/>
<include file="parts/0064_refactor_loan_transaction_strategy.xml" relativeToChangelogFile="true"/>
</databaseChangeLog> </databaseChangeLog>
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet author="fineract" id="1">
<addColumn tableName="m_loan">
<column defaultValue="-" name="loan_transaction_strategy_code" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column defaultValue="-" name="loan_transaction_strategy_name" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
</addColumn>
<update tableName="m_loan">
<column name="loan_transaction_strategy_code" value="mifos-standard-strategy" />
<column name="loan_transaction_strategy_name" value="Penalties, Fees, Interest, Principal order" />
<where>loan_transaction_strategy_id = 1</where>
</update>
<update tableName="m_loan">
<column name="loan_transaction_strategy_code" value="heavensfamily-strategy" />
<column name="loan_transaction_strategy_name" value="HeavensFamily Unique" />
<where>loan_transaction_strategy_id = 2</where>
</update>
<update tableName="m_loan">
<column name="loan_transaction_strategy_code" value="creocore-strategy" />
<column name="loan_transaction_strategy_name" value="Creocore Unique" />
<where>loan_transaction_strategy_id = 3</where>
</update>
<update tableName="m_loan">
<column name="loan_transaction_strategy_code" value="rbi-india-strategy" />
<column name="loan_transaction_strategy_name" value="Overdue/Due Fee/Int,Principal" />
<where>loan_transaction_strategy_id = 4</where>
</update>
<update tableName="m_loan">
<column name="loan_transaction_strategy_code" value="principal-interest-penalties-fees-order-strategy" />
<column name="loan_transaction_strategy_name" value="Principal, Interest, Penalties, Fees Order" />
<where>loan_transaction_strategy_id = 5</where>
</update>
<update tableName="m_loan">
<column name="loan_transaction_strategy_code" value="interest-principal-penalties-fees-order-strategy" />
<column name="loan_transaction_strategy_name" value="Interest, Principal, Penalties, Fees Order" />
<where>loan_transaction_strategy_id = 6</where>
</update>
<update tableName="m_loan">
<column name="loan_transaction_strategy_code" value="early-repayment-strategy" />
<column name="loan_transaction_strategy_name" value="Early Repayment Strategy" />
<where>loan_transaction_strategy_id = 7</where>
</update>
<dropForeignKeyConstraint baseTableName="m_loan" constraintName="FK_loan_ltp_strategy" />
<!--
<dropColumn tableName="m_loan_product" columnName="loan_transaction_strategy_id" />
-->
</changeSet>
<changeSet author="fineract" id="2">
<addColumn tableName="m_product_loan">
<column defaultValue="-" name="loan_transaction_strategy_code" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column defaultValue="-" name="loan_transaction_strategy_name" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
</addColumn>
<update tableName="m_product_loan">
<column name="loan_transaction_strategy_code" value="mifos-standard-strategy" />
<column name="loan_transaction_strategy_name" value="Penalties, Fees, Interest, Principal order" />
<where>loan_transaction_strategy_id = 1</where>
</update>
<update tableName="m_product_loan">
<column name="loan_transaction_strategy_code" value="heavensfamily-strategy" />
<column name="loan_transaction_strategy_name" value="HeavensFamily Unique" />
<where>loan_transaction_strategy_id = 2</where>
</update>
<update tableName="m_product_loan">
<column name="loan_transaction_strategy_code" value="creocore-strategy" />
<column name="loan_transaction_strategy_name" value="Creocore Unique" />
<where>loan_transaction_strategy_id = 3</where>
</update>
<update tableName="m_product_loan">
<column name="loan_transaction_strategy_code" value="rbi-india-strategy" />
<column name="loan_transaction_strategy_name" value="Overdue/Due Fee/Int,Principal" />
<where>loan_transaction_strategy_id = 4</where>
</update>
<update tableName="m_product_loan">
<column name="loan_transaction_strategy_code" value="principal-interest-penalties-fees-order-strategy" />
<column name="loan_transaction_strategy_name" value="Principal, Interest, Penalties, Fees Order" />
<where>loan_transaction_strategy_id = 5</where>
</update>
<update tableName="m_product_loan">
<column name="loan_transaction_strategy_code" value="interest-principal-penalties-fees-order-strategy" />
<column name="loan_transaction_strategy_name" value="Interest, Principal, Penalties, Fees Order" />
<where>loan_transaction_strategy_id = 6</where>
</update>
<update tableName="m_product_loan">
<column name="loan_transaction_strategy_code" value="early-repayment-strategy" />
<column name="loan_transaction_strategy_name" value="Early Repayment Strategy" />
<where>loan_transaction_strategy_id = 7</where>
</update>
<dropForeignKeyConstraint baseTableName="m_product_loan" constraintName="FK_ltp_strategy" />
<!--
<dropColumn tableName="m_product_loan" columnName="loan_transaction_strategy_id" />
-->
</changeSet>
<changeSet author="fineract" id="3">
<renameColumn tableName="m_product_loan_configurable_attributes" oldColumnName="loan_transaction_strategy_id" newColumnName="loan_transaction_strategy_code" columnDataType="BOOLEAN" />
</changeSet>
<!--
<changeSet author="fineract" id="4">
<dropTable tableName="ref_loan_transaction_processing_strategy" />
</changeSet>
-->
</databaseChangeLog>
@@ -2266,7 +2266,6 @@ CREATE TABLE IF NOT EXISTS `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -2284,7 +2283,6 @@ CREATE TABLE IF NOT EXISTS `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -3971,9 +3969,7 @@ CREATE TABLE IF NOT EXISTS `m_product_loan` (
UNIQUE KEY `unq_short_name` (`short_name`), UNIQUE KEY `unq_short_name` (`short_name`),
UNIQUE KEY `external_id_UNIQUE` (`external_id`), UNIQUE KEY `external_id_UNIQUE` (`external_id`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;
-- Dumping data for table fineract_default.m_product_loan: ~0 rows (approximately) -- Dumping data for table fineract_default.m_product_loan: ~0 rows (approximately)
@@ -5494,30 +5490,6 @@ INSERT INTO `ppi_scores` (`id`, `score_from`, `score_to`) VALUES
/*!40000 ALTER TABLE `ppi_scores` ENABLE KEYS */; /*!40000 ALTER TABLE `ppi_scores` ENABLE KEYS */;
-- Dumping structure for table fineract_default.ref_loan_transaction_processing_strategy
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
CREATE TABLE IF NOT EXISTS `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`sort_order` INT DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=UTF8MB4;
-- Dumping data for table fineract_default.ref_loan_transaction_processing_strategy: ~7 rows (approximately)
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` (`id`, `code`, `name`, `sort_order`) VALUES
(1, 'mifos-standard-strategy', 'Penalties, Fees, Interest, Principal order', 1),
(2, 'heavensfamily-strategy', 'HeavensFamily Unique', 6),
(3, 'creocore-strategy', 'Creocore Unique', 7),
(4, 'rbi-india-strategy', 'Overdue/Due Fee/Int,Principal', 2),
(5, 'principal-interest-penalties-fees-order-strategy', 'Principal, Interest, Penalties, Fees Order', 3),
(6, 'interest-principal-penalties-fees-order-strategy', 'Interest, Principal, Penalties, Fees Order', 4),
(7, 'early-repayment-strategy', 'Early Repayment Strategy', 5);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
-- Dumping structure for table fineract_default.rpt_sequence -- Dumping structure for table fineract_default.rpt_sequence
DROP TABLE IF EXISTS `rpt_sequence`; DROP TABLE IF EXISTS `rpt_sequence`;
CREATE TABLE IF NOT EXISTS `rpt_sequence` ( CREATE TABLE IF NOT EXISTS `rpt_sequence` (
@@ -2406,7 +2406,6 @@ CREATE TABLE IF NOT EXISTS `m_loan` (
KEY `FKB6F935D87179A0CB` (`client_id`), KEY `FKB6F935D87179A0CB` (`client_id`),
KEY `FKB6F935D8C8D4B434` (`product_id`), KEY `FKB6F935D8C8D4B434` (`product_id`),
KEY `FK7C885877240145` (`fund_id`), KEY `FK7C885877240145` (`fund_id`),
KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
KEY `FK_m_loan_m_staff` (`loan_officer_id`), KEY `FK_m_loan_m_staff` (`loan_officer_id`),
KEY `group_id` (`group_id`), KEY `group_id` (`group_id`),
KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`), KEY `FK_m_loanpurpose_codevalue` (`loanpurpose_cv_id`),
@@ -2424,7 +2423,6 @@ CREATE TABLE IF NOT EXISTS `m_loan` (
CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_approvedon_userid` FOREIGN KEY (`approvedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_closedon_userid` FOREIGN KEY (`closedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_disbursedon_userid` FOREIGN KEY (`disbursedon_userid`) REFERENCES `m_appuser` (`id`),
CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`), CONSTRAINT `FK_m_loan_m_staff` FOREIGN KEY (`loan_officer_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`), CONSTRAINT `FK_m_loanpurpose_codevalue` FOREIGN KEY (`loanpurpose_cv_id`) REFERENCES `m_code_value` (`id`),
CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`), CONSTRAINT `FK_rejectedon_userid` FOREIGN KEY (`rejectedon_userid`) REFERENCES `m_appuser` (`id`),
@@ -4104,7 +4102,7 @@ INSERT INTO `m_portfolio_command_source` (`id`, `action_name`, `entity_name`, `o
(24, 'CREATE', 'USER', 1, NULL, NULL, NULL, NULL, '/users/template', 5, NULL, '{"sendPasswordToEmail":true,"officeId":1,"username":"janej","firstname":"Jane","lastname":"J","email":"janej@123.com","roles":["1"]}', 1, '2014-03-07 20:07:48', NULL, NULL, 1, NULL, NULL), (24, 'CREATE', 'USER', 1, NULL, NULL, NULL, NULL, '/users/template', 5, NULL, '{"sendPasswordToEmail":true,"officeId":1,"username":"janej","firstname":"Jane","lastname":"J","email":"janej@123.com","roles":["1"]}', 1, '2014-03-07 20:07:48', NULL, NULL, 1, NULL, NULL),
(25, 'CREATE', 'STAFF', 3, NULL, NULL, NULL, NULL, '/staff/template', 3, NULL, '{"isLoanOfficer":true,"officeId":3,"firstname":"John","lastname":"K"}', 1, '2014-03-07 20:08:28', NULL, NULL, 1, NULL, NULL), (25, 'CREATE', 'STAFF', 3, NULL, NULL, NULL, NULL, '/staff/template', 3, NULL, '{"isLoanOfficer":true,"officeId":3,"firstname":"John","lastname":"K"}', 1, '2014-03-07 20:08:28', NULL, NULL, 1, NULL, NULL),
(26, 'CREATE', 'FUND', NULL, NULL, NULL, NULL, NULL, '/funds/template', 1, NULL, '{"name":"Loan from Central Bank"}', 1, '2014-03-10 10:11:50', NULL, NULL, 1, NULL, NULL), (26, 'CREATE', 'FUND', NULL, NULL, NULL, NULL, NULL, '/funds/template', 1, NULL, '{"name":"Loan from Central Bank"}', 1, '2014-03-10 10:11:50', NULL, NULL, 1, NULL, NULL),
(27, 'CREATE', 'LOANPRODUCT', NULL, NULL, NULL, NULL, NULL, '/loanproducts/template', 1, NULL, '{"currencyCode":"USD","includeInBorrowerCycle":"false","useBorrowerCycle":"false","digitsAfterDecimal":"2","inMultiplesOf":"0","repaymentFrequencyType":1,"interestRateFrequencyType":3,"amortizationType":1,"interestType":1,"interestCalculationPeriodType":1,"transactionProcessingStrategyId":1,"principalVariationsForBorrowerCycle":[],"interestRateVariationsForBorrowerCycle":[],"numberOfRepaymentVariationsForBorrowerCycle":[],"multiDisburseLoan":false,"fundSourceAccountId":31,"loanPortfolioAccountId":32,"transfersInSuspenseAccountId":33,"interestOnLoanAccountId":36,"incomeFromFeeAccountId":37,"incomeFromPenaltyAccountId":38,"writeOffAccountId":41,"overpaymentLiabilityAccountId":30,"accountingRule":"2","name":"Income Generating Loan","shortName":"IGL","fundId":1,"minPrincipal":"10000","principal":"10000","maxPrincipal":"10000","minNumberOfRepayments":"25","numberOfRepayments":"25","maxNumberOfRepayments":"25","repaymentEvery":"1","minInterestRatePerPeriod":"26","interestRatePerPeriod":"26","maxInterestRatePerPeriod":"26","paymentChannelToFundSourceMappings":[],"feeToIncomeAccountMappings":[],"penaltyToIncomeAccountMappings":[],"charges":[],"dateFormat":"dd MMMM yyyy","locale":"en","startDate":"01 January 2010"}', 1, '2014-03-10 10:16:39', NULL, NULL, 1, NULL, NULL), (27, 'CREATE', 'LOANPRODUCT', NULL, NULL, NULL, NULL, NULL, '/loanproducts/template', 1, NULL, '{"currencyCode":"USD","includeInBorrowerCycle":"false","useBorrowerCycle":"false","digitsAfterDecimal":"2","inMultiplesOf":"0","repaymentFrequencyType":1,"interestRateFrequencyType":3,"amortizationType":1,"interestType":1,"interestCalculationPeriodType":1,"transactionProcessingStrategyCode":"mifos-standard-strategy","principalVariationsForBorrowerCycle":[],"interestRateVariationsForBorrowerCycle":[],"numberOfRepaymentVariationsForBorrowerCycle":[],"multiDisburseLoan":false,"fundSourceAccountId":31,"loanPortfolioAccountId":32,"transfersInSuspenseAccountId":33,"interestOnLoanAccountId":36,"incomeFromFeeAccountId":37,"incomeFromPenaltyAccountId":38,"writeOffAccountId":41,"overpaymentLiabilityAccountId":30,"accountingRule":"2","name":"Income Generating Loan","shortName":"IGL","fundId":1,"minPrincipal":"10000","principal":"10000","maxPrincipal":"10000","minNumberOfRepayments":"25","numberOfRepayments":"25","maxNumberOfRepayments":"25","repaymentEvery":"1","minInterestRatePerPeriod":"26","interestRatePerPeriod":"26","maxInterestRatePerPeriod":"26","paymentChannelToFundSourceMappings":[],"feeToIncomeAccountMappings":[],"penaltyToIncomeAccountMappings":[],"charges":[],"dateFormat":"dd MMMM yyyy","locale":"en","startDate":"01 January 2010"}', 1, '2014-03-10 10:16:39', NULL, NULL, 1, NULL, NULL),
(28, 'CREATE', 'SAVINGSPRODUCT', NULL, NULL, NULL, NULL, NULL, '/savingsproducts/template', 1, NULL, '{"currencyCode":"USD","digitsAfterDecimal":2,"interestCompoundingPeriodType":1,"interestPostingPeriodType":4,"interestCalculationType":1,"interestCalculationDaysInYearType":365,"savingsReferenceAccountId":31,"overdraftPortfolioControlId":32,"savingsControlAccountId":30,"incomeFromFeeAccountId":36,"incomeFromPenaltyAccountId":37,"incomeFromInterestId":38,"interestOnSavingsAccountId":41,"writeOffAccountId":42,"accountingRule":"2","name":"Voluntary savings","shortName":"VS","description":"Save money","inMultiplesOf":"0","nominalAnnualInterestRate":"9.5","minRequiredOpeningBalance":"1000","lockinPeriodFrequency":"1","lockinPeriodFrequencyType":1,"withdrawalFeeForTransfers":"false","paymentChannelToFundSourceMappings":[],"feeToIncomeAccountMappings":[],"penaltyToIncomeAccountMappings":[],"charges":[],"locale":"en","transfersInSuspenseAccountId":30}', 1, '2014-03-10 10:21:16', NULL, NULL, 1, NULL, NULL), (28, 'CREATE', 'SAVINGSPRODUCT', NULL, NULL, NULL, NULL, NULL, '/savingsproducts/template', 1, NULL, '{"currencyCode":"USD","digitsAfterDecimal":2,"interestCompoundingPeriodType":1,"interestPostingPeriodType":4,"interestCalculationType":1,"interestCalculationDaysInYearType":365,"savingsReferenceAccountId":31,"overdraftPortfolioControlId":32,"savingsControlAccountId":30,"incomeFromFeeAccountId":36,"incomeFromPenaltyAccountId":37,"incomeFromInterestId":38,"interestOnSavingsAccountId":41,"writeOffAccountId":42,"accountingRule":"2","name":"Voluntary savings","shortName":"VS","description":"Save money","inMultiplesOf":"0","nominalAnnualInterestRate":"9.5","minRequiredOpeningBalance":"1000","lockinPeriodFrequency":"1","lockinPeriodFrequencyType":1,"withdrawalFeeForTransfers":"false","paymentChannelToFundSourceMappings":[],"feeToIncomeAccountMappings":[],"penaltyToIncomeAccountMappings":[],"charges":[],"locale":"en","transfersInSuspenseAccountId":30}', 1, '2014-03-10 10:21:16', NULL, NULL, 1, NULL, NULL),
(29, 'UPDATE', 'SAVINGSPRODUCT', NULL, NULL, NULL, NULL, NULL, '/savingsproducts/1', 1, NULL, '{"shortName":"VS","penaltyToIncomeAccountMappings":"[]","paymentChannelToFundSourceMappings":"[]","feeToIncomeAccountMappings":"[]"}', 1, '2014-03-10 10:21:51', NULL, NULL, 1, NULL, NULL), (29, 'UPDATE', 'SAVINGSPRODUCT', NULL, NULL, NULL, NULL, NULL, '/savingsproducts/1', 1, NULL, '{"shortName":"VS","penaltyToIncomeAccountMappings":"[]","paymentChannelToFundSourceMappings":"[]","feeToIncomeAccountMappings":"[]"}', 1, '2014-03-10 10:21:51', NULL, NULL, 1, NULL, NULL),
(30, 'CREATE', 'CODEVALUE', NULL, NULL, NULL, NULL, NULL, '/codes/3/codevalues/template', 10, NULL, '{"name":"Cattle Rearing","position":"104"}', 1, '2014-03-10 10:27:02', NULL, NULL, 1, NULL, NULL), (30, 'CREATE', 'CODEVALUE', NULL, NULL, NULL, NULL, NULL, '/codes/3/codevalues/template', 10, NULL, '{"name":"Cattle Rearing","position":"104"}', 1, '2014-03-10 10:27:02', NULL, NULL, 1, NULL, NULL),
@@ -4145,7 +4143,7 @@ INSERT INTO `m_portfolio_command_source` (`id`, `action_name`, `entity_name`, `o
(65, 'UPDATE', 'GLACCOUNT', NULL, NULL, NULL, NULL, NULL, '/glaccounts/2', 2, NULL, '{"usage":1}', 1, '2014-05-01 16:25:56', NULL, NULL, 1, NULL, NULL), (65, 'UPDATE', 'GLACCOUNT', NULL, NULL, NULL, NULL, NULL, '/glaccounts/2', 2, NULL, '{"usage":1}', 1, '2014-05-01 16:25:56', NULL, NULL, 1, NULL, NULL),
(66, 'UPDATE', 'LOANPRODUCT', NULL, NULL, NULL, NULL, NULL, '/loanproducts/1', 1, NULL, '{"inMultiplesOf":0,"penaltyToIncomeAccountMappings":"[]","paymentChannelToFundSourceMappings":"[]","feeToIncomeAccountMappings":"[]"}', 1, '2014-05-01 16:26:06', NULL, NULL, 1, NULL, NULL), (66, 'UPDATE', 'LOANPRODUCT', NULL, NULL, NULL, NULL, NULL, '/loanproducts/1', 1, NULL, '{"inMultiplesOf":0,"penaltyToIncomeAccountMappings":"[]","paymentChannelToFundSourceMappings":"[]","feeToIncomeAccountMappings":"[]"}', 1, '2014-05-01 16:26:06', NULL, NULL, 1, NULL, NULL),
(67, 'UPDATE', 'LOANPRODUCT', NULL, NULL, NULL, NULL, NULL, '/loanproducts/1', 1, NULL, '{"inMultiplesOf":0,"overpaymentLiabilityAccountId":2,"penaltyToIncomeAccountMappings":"[]","paymentChannelToFundSourceMappings":"[]","transfersInSuspenseAccountId":17,"feeToIncomeAccountMappings":"[]"}', 1, '2014-05-01 16:26:37', NULL, NULL, 1, NULL, NULL), (67, 'UPDATE', 'LOANPRODUCT', NULL, NULL, NULL, NULL, NULL, '/loanproducts/1', 1, NULL, '{"inMultiplesOf":0,"overpaymentLiabilityAccountId":2,"penaltyToIncomeAccountMappings":"[]","paymentChannelToFundSourceMappings":"[]","transfersInSuspenseAccountId":17,"feeToIncomeAccountMappings":"[]"}', 1, '2014-05-01 16:26:37', NULL, NULL, 1, NULL, NULL),
(68, 'CREATE', 'LOAN', 2, NULL, 8, 1, NULL, '/loans', 1, NULL, '{"clientId":"8","productId":1,"disbursementData":[],"fundId":1,"principal":10000,"loanTermFrequency":25,"loanTermFrequencyType":1,"numberOfRepayments":25,"repaymentEvery":1,"repaymentFrequencyType":1,"interestRatePerPeriod":26,"amortizationType":1,"interestType":1,"interestCalculationPeriodType":1,"transactionProcessingStrategyId":1,"locale":"en","dateFormat":"dd MMMM yyyy","loanType":"individual","expectedDisbursementDate":"16 June 2014","submittedOnDate":"02 June 2014","charges":[{"chargeId":1,"amount":500}]}', 1, '2014-06-11 09:17:45', NULL, NULL, 1, NULL, NULL), (68, 'CREATE', 'LOAN', 2, NULL, 8, 1, NULL, '/loans', 1, NULL, '{"clientId":"8","productId":1,"disbursementData":[],"fundId":1,"principal":10000,"loanTermFrequency":25,"loanTermFrequencyType":1,"numberOfRepayments":25,"repaymentEvery":1,"repaymentFrequencyType":1,"interestRatePerPeriod":26,"amortizationType":1,"interestType":1,"interestCalculationPeriodType":1,"transactionProcessingStrategyCode":"mifos-standard-strategy","locale":"en","dateFormat":"dd MMMM yyyy","loanType":"individual","expectedDisbursementDate":"16 June 2014","submittedOnDate":"02 June 2014","charges":[{"chargeId":1,"amount":500}]}', 1, '2014-06-11 09:17:45', NULL, NULL, 1, NULL, NULL),
(69, 'APPROVE', 'LOAN', 2, NULL, 8, 1, NULL, '/loans/1', 1, NULL, '{"status":{"id":200,"code":"loanStatusType.approved","value":"Approved","pendingApproval":false,"waitingForDisbursal":true,"active":false,"closedObligationsMet":false,"closedWrittenOff":false,"closedRescheduled":false,"closed":false,"overpaid":false},"locale":"en","dateFormat":"dd MMMM yyyy","approvedOnDate":"11 June 2014"}', 1, '2014-06-11 09:18:16', NULL, NULL, 1, NULL, NULL), (69, 'APPROVE', 'LOAN', 2, NULL, 8, 1, NULL, '/loans/1', 1, NULL, '{"status":{"id":200,"code":"loanStatusType.approved","value":"Approved","pendingApproval":false,"waitingForDisbursal":true,"active":false,"closedObligationsMet":false,"closedWrittenOff":false,"closedRescheduled":false,"closed":false,"overpaid":false},"locale":"en","dateFormat":"dd MMMM yyyy","approvedOnDate":"11 June 2014"}', 1, '2014-06-11 09:18:16', NULL, NULL, 1, NULL, NULL),
(70, 'CREATE', 'FINANCIALACTIVITYACCOUNT', NULL, NULL, NULL, NULL, NULL, '/organizationglaccounts/template', 2, NULL, '{"financialActivityId":100,"glAccountId":33}', 1, '2014-06-16 16:54:20', NULL, NULL, 1, NULL, NULL), (70, 'CREATE', 'FINANCIALACTIVITYACCOUNT', NULL, NULL, NULL, NULL, NULL, '/organizationglaccounts/template', 2, NULL, '{"financialActivityId":100,"glAccountId":33}', 1, '2014-06-16 16:54:20', NULL, NULL, 1, NULL, NULL),
(71, 'UPDATE', 'FINANCIALACTIVITYACCOUNT', NULL, NULL, NULL, NULL, NULL, '/organizationglaccounts/2', 2, NULL, '{"glAccountId":32}', 1, '2014-06-16 16:57:17', NULL, NULL, 1, NULL, NULL), (71, 'UPDATE', 'FINANCIALACTIVITYACCOUNT', NULL, NULL, NULL, NULL, NULL, '/organizationglaccounts/2', 2, NULL, '{"glAccountId":32}', 1, '2014-06-16 16:57:17', NULL, NULL, 1, NULL, NULL),
@@ -4216,9 +4214,7 @@ CREATE TABLE IF NOT EXISTS `m_product_loan` (
UNIQUE KEY `unq_short_name` (`short_name`), UNIQUE KEY `unq_short_name` (`short_name`),
UNIQUE KEY `external_id_UNIQUE` (`external_id`), UNIQUE KEY `external_id_UNIQUE` (`external_id`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`)
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `m_fund` (`id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4; ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=UTF8MB4;
-- Dumping data for table mifostenant-reference.m_product_loan: ~1 rows (approximately) -- Dumping data for table mifostenant-reference.m_product_loan: ~1 rows (approximately)
@@ -5751,30 +5747,6 @@ INSERT INTO `ppi_scores` (`id`, `score_from`, `score_to`) VALUES
/*!40000 ALTER TABLE `ppi_scores` ENABLE KEYS */; /*!40000 ALTER TABLE `ppi_scores` ENABLE KEYS */;
-- Dumping structure for table mifostenant-reference.ref_loan_transaction_processing_strategy
DROP TABLE IF EXISTS `ref_loan_transaction_processing_strategy`;
CREATE TABLE IF NOT EXISTS `ref_loan_transaction_processing_strategy` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`sort_order` INT DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=UTF8MB4;
-- Dumping data for table mifostenant-reference.ref_loan_transaction_processing_strategy: ~7 rows (approximately)
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` DISABLE KEYS */;
INSERT INTO `ref_loan_transaction_processing_strategy` (`id`, `code`, `name`, `sort_order`) VALUES
(1, 'mifos-standard-strategy', 'Penalties, Fees, Interest, Principal order', 1),
(2, 'heavensfamily-strategy', 'HeavensFamily Unique', 6),
(3, 'creocore-strategy', 'Creocore Unique', 7),
(4, 'rbi-india-strategy', 'Overdue/Due Fee/Int,Principal', 2),
(5, 'principal-interest-penalties-fees-order-strategy', 'Principal, Interest, Penalties, Fees Order', 3),
(6, 'interest-principal-penalties-fees-order-strategy', 'Interest, Principal, Penalties, Fees Order', 4),
(7, 'early-repayment-strategy', 'Early Repayment Strategy', 5);
/*!40000 ALTER TABLE `ref_loan_transaction_processing_strategy` ENABLE KEYS */;
-- Dumping structure for table mifostenant-reference.rpt_sequence -- Dumping structure for table mifostenant-reference.rpt_sequence
DROP TABLE IF EXISTS `rpt_sequence`; DROP TABLE IF EXISTS `rpt_sequence`;
CREATE TABLE IF NOT EXISTS `rpt_sequence` ( CREATE TABLE IF NOT EXISTS `rpt_sequence` (
@@ -4740,7 +4740,7 @@ Content-Type: application/json Request Body:
\"amortizationType\": 1, \"amortizationType\": 1,
\"interestType\": 0, \"interestType\": 0,
\"interestCalculationPeriodType\": 1, \"interestCalculationPeriodType\": 1,
\"transactionProcessingStrategyId\": 1, \"transactionProcessingStrategyCode\": \"mifos-standard-strategy\",
\"expectedDisbursementDate\": \"10 Jun 2013\", \"expectedDisbursementDate\": \"10 Jun 2013\",
\"submittedOnDate\": \"10 Jun 2013\" \"submittedOnDate\": \"10 Jun 2013\"
}" }"
@@ -10339,7 +10339,7 @@ GET https://DomainName/fineract-provider/api/v1/groups/{groupId}/glimaccounts
</td> </td>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>transactionProcessingStrategyId</td> <td>transactionProcessingStrategyCode</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc> <td class=fielddesc>
@@ -10541,7 +10541,7 @@ GET https://DomainName/api/v1/loans/template?templateType=individual&clientId=1&
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 2, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"timeline": { "timeline": {
"expectedDisbursementDate": [ "expectedDisbursementDate": [
2013, 2013,
@@ -10973,7 +10973,7 @@ GET https://DomainName/api/v1/loans/{loanId}
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 2, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"timeline": { "timeline": {
"submittedOnDate": [ "submittedOnDate": [
2012, 2012,
@@ -11265,7 +11265,7 @@ GET https://DomainName/fineract-provider/api/v1/loans
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 2, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"timeline": { "timeline": {
"submittedOnDate": [ "submittedOnDate": [
2012, 2012,
@@ -11417,7 +11417,7 @@ No Request Body:
"interestCalculationPeriodType": 0, "interestCalculationPeriodType": 0,
"amortizationType": 1, "amortizationType": 1,
"expectedDisbursementDate": "04 March 2014", "expectedDisbursementDate": "04 March 2014",
"transactionProcessingStrategyId": 1 "transactionProcessingStrategyCode": "mifos-standard-strategy"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -11449,7 +11449,7 @@ No Request Body:
repaymentFrequencyType, interestRatePerPeriod, repaymentFrequencyType, interestRatePerPeriod,
amortizationType, interestType, amortizationType, interestType,
interestCalculationPeriodType, expectedDisbursementDate, interestCalculationPeriodType, expectedDisbursementDate,
transactionProcessingStrategyId</td> transactionProcessingStrategyCode</td>
</tr> </tr>
</table> </table>
</div> </div>
@@ -11476,7 +11476,7 @@ Request Body:
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"expectedDisbursementDate": "20 September 2011", "expectedDisbursementDate": "20 September 2011",
"transactionProcessingStrategyId": 2 "transactionProcessingStrategyCode": "mifos-standard-strategy"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -11598,7 +11598,7 @@ Request Body:
loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery, loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery,
repaymentFrequencyType, interestRatePerPeriod, repaymentFrequencyType, interestRatePerPeriod,
amortizationType, interestType, amortizationType, interestType,
interestCalculationPeriodType, transactionProcessingStrategyId, interestCalculationPeriodType, transactionProcessingStrategyCode,
expectedDisbursementDate, submittedOnDate, loanType</td> expectedDisbursementDate, submittedOnDate, loanType</td>
</tr> </tr>
</table> </table>
@@ -11663,7 +11663,7 @@ Request Body:
"amortizationType": 1, "amortizationType": 1,
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"transactionProcessingStrategyId": 1, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"expectedDisbursementDate": "10 Jun 2013", "expectedDisbursementDate": "10 Jun 2013",
"submittedOnDate": "10 Jun 2013", "submittedOnDate": "10 Jun 2013",
"linkAccountId" : "1", "linkAccountId" : "1",
@@ -12222,7 +12222,7 @@ Content-Type: application/json
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>interestRatePerPeriod, expectedDisbursementDate, clientId, groupId, productId, totalLoan, loanType, interestType, <td>interestRatePerPeriod, expectedDisbursementDate, clientId, groupId, productId, totalLoan, loanType, interestType,
principal, charges, transactionProcessingStrategyId, submittedOnDate, amortizationType, repaymentFrequencyType, principal, charges, transactionProcessingStrategyCode, submittedOnDate, amortizationType, repaymentFrequencyType,
loanTermFrequencyType, loanTermFrequency, repaymentEvery, numberOfRepayments, interestCalculationPeriodType, loanTermFrequencyType, loanTermFrequency, repaymentEvery, numberOfRepayments, interestCalculationPeriodType,
isParentAccount, locale,dateFormat</td> isParentAccount, locale,dateFormat</td>
</tr> </tr>
@@ -12250,7 +12250,7 @@ Request Body:
"locale":"en_GB", "locale":"en_GB",
"principal":"1000.00", "principal":"1000.00",
"charges":[], "charges":[],
"transactionProcessingStrategyId":"1", "transactionProcessingStrategyCode":"mifos-standard-strategy",
"submittedOnDate":"10 June 2020", "submittedOnDate":"10 June 2020",
"amortizationType":"1", "amortizationType":"1",
"repaymentFrequencyType":"2", "repaymentFrequencyType":"2",
@@ -15740,7 +15740,7 @@ Request Body:
<tr class=alt> <tr class=alt>
<td>transactionProcessingStrategyId</td> <td>transactionProcessingStrategyCode</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc> <td class=fielddesc>
@@ -15986,7 +15986,7 @@ GET https://DomainName/api/v1/loanproducts
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 1, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"transactionProcessingStrategyName": "Mifos style", "transactionProcessingStrategyName": "Mifos style",
"principalVariationsForBorrowerCycle": [], "principalVariationsForBorrowerCycle": [],
"interestRateVariationsForBorrowerCycle": [], "interestRateVariationsForBorrowerCycle": [],
@@ -16989,7 +16989,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 1, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"transactionProcessingStrategyName": "Mifos style", "transactionProcessingStrategyName": "Mifos style",
"charges": [], "charges": [],
"principalVariationsForBorrowerCycle": [ "principalVariationsForBorrowerCycle": [
@@ -17162,7 +17162,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
<td>name, shortName, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, <td>name, shortName, currencyCode, digitsAfterDecimal, inMultiplesOf, principal,
numberOfRepayments, repaymentEvery, repaymentFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType,
interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType,
interestCalculationPeriodType, transactionProcessingStrategyId, interestCalculationPeriodType, transactionProcessingStrategyCode,
accountingRule, isInterestRecalculationEnabled, daysInYearType, daysInMonthType</td> accountingRule, isInterestRecalculationEnabled, daysInYearType, daysInMonthType</td>
</tr> </tr>
</table> </table>
@@ -17277,7 +17277,7 @@ Request Body:
"amortizationType":1, "amortizationType":1,
"interestType":0, "interestType":0,
"interestCalculationPeriodType":1, "interestCalculationPeriodType":1,
"transactionProcessingStrategyId":1, "transactionProcessingStrategyCode":"mifos-standard-strategy",
"principalVariationsForBorrowerCycle":[], "principalVariationsForBorrowerCycle":[],
"interestRateVariationsForBorrowerCycle":[], "interestRateVariationsForBorrowerCycle":[],
"numberOfRepaymentVariationsForBorrowerCycle":[ "numberOfRepaymentVariationsForBorrowerCycle":[
@@ -17299,7 +17299,7 @@ Request Body:
"allowAttributeOverrides": "allowAttributeOverrides":
{amortizationType : true, {amortizationType : true,
interestType : true, interestType : true,
transactionProcessingStrategyId : false, transactionProcessingStrategyCode : false,
interestCalculationPeriodType : true, interestCalculationPeriodType : true,
inArrearsTolerance : false, inArrearsTolerance : false,
repaymentEvery : true, repaymentEvery : true,
@@ -17355,7 +17355,7 @@ Request Body:
"numberOfRepayments": "12", "numberOfRepayments": "12",
"repaymentEvery": "1", "repaymentEvery": "1",
"repaymentFrequencyType": 2, "repaymentFrequencyType": 2,
"transactionProcessingStrategyId": 1, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"interestRatePerPeriod": "1.75", "interestRatePerPeriod": "1.75",
"interestRateFrequencyType": 2, "interestRateFrequencyType": 2,
"amortizationType": 1, "amortizationType": 1,
@@ -17422,7 +17422,7 @@ Request Body:
"numberOfRepayments": "12", "numberOfRepayments": "12",
"repaymentEvery": "1", "repaymentEvery": "1",
"repaymentFrequencyType": 2, "repaymentFrequencyType": 2,
"transactionProcessingStrategyId": 1, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"interestRatePerPeriod": "1.75", "interestRatePerPeriod": "1.75",
"interestRateFrequencyType": 2, "interestRateFrequencyType": 2,
"amortizationType": 1, "amortizationType": 1,
@@ -47942,7 +47942,7 @@ GET https://DomainName/api/v1/self/loans/template?templateType=individual&client
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 2, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"timeline": { "timeline": {
"expectedDisbursementDate": [ "expectedDisbursementDate": [
2013, 2013,
@@ -48192,7 +48192,7 @@ GET https://DomainName/api/v1/self/loans/template?templateType=individual&client
repaymentFrequencyType, interestRatePerPeriod, repaymentFrequencyType, interestRatePerPeriod,
amortizationType, interestType, amortizationType, interestType,
interestCalculationPeriodType, expectedDisbursementDate, interestCalculationPeriodType, expectedDisbursementDate,
transactionProcessingStrategyId</td> transactionProcessingStrategyCode</td>
</tr> </tr>
</table> </table>
</div> </div>
@@ -48219,7 +48219,7 @@ Request Body:
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"expectedDisbursementDate": "20 September 2011", "expectedDisbursementDate": "20 September 2011",
"transactionProcessingStrategyId": 2 "transactionProcessingStrategyCode": "mifos-standard-strategy"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -48343,7 +48343,7 @@ Request Body:
loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery, loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery,
repaymentFrequencyType, interestRatePerPeriod, repaymentFrequencyType, interestRatePerPeriod,
amortizationType, interestType, amortizationType, interestType,
interestCalculationPeriodType, transactionProcessingStrategyId, interestCalculationPeriodType, transactionProcessingStrategyCode,
expectedDisbursementDate, submittedOnDate, loanType</td> expectedDisbursementDate, submittedOnDate, loanType</td>
</tr> </tr>
</table> </table>
@@ -48400,7 +48400,7 @@ Request Body:
"amortizationType": 1, "amortizationType": 1,
"interestType": 0, "interestType": 0,
"interestCalculationPeriodType": 1, "interestCalculationPeriodType": 1,
"transactionProcessingStrategyId": 1, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"expectedDisbursementDate": "10 Jun 2013", "expectedDisbursementDate": "10 Jun 2013",
"submittedOnDate": "10 Jun 2013", "submittedOnDate": "10 Jun 2013",
"linkAccountId" : "1", "linkAccountId" : "1",
@@ -48553,7 +48553,7 @@ GET https://DomainName/api/v1/self/loans/{loanId}
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 2, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"timeline": { "timeline": {
"submittedOnDate": [ "submittedOnDate": [
2012, 2012,
@@ -48722,7 +48722,7 @@ No Request Body:
"interestCalculationPeriodType": 0, "interestCalculationPeriodType": 0,
"amortizationType": 1, "amortizationType": 1,
"expectedDisbursementDate": "04 March 2014", "expectedDisbursementDate": "04 March 2014",
"transactionProcessingStrategyId": 1 "transactionProcessingStrategyCode": "mifos-standard-strategy"
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -51635,7 +51635,7 @@ No Request Body:
<tr class=alt> <tr class=alt>
<td>transactionProcessingStrategyId</td> <td>transactionProcessingStrategyCode</td>
</tr> </tr>
<tr> <tr>
<td class=fielddesc> <td class=fielddesc>
@@ -51929,7 +51929,7 @@ GET https://DomainName/api/v1/self/loanproducts/{productId}?clientId={clientId}
"code": "interestCalculationPeriodType.same.as.repayment.period", "code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period" "value": "Same as repayment period"
}, },
"transactionProcessingStrategyId": 1, "transactionProcessingStrategyCode": "mifos-standard-strategy",
"transactionProcessingStrategyName": "Mifos style", "transactionProcessingStrategyName": "Mifos style",
"charges": [], "charges": [],
"principalVariationsForBorrowerCycle": [ "principalVariationsForBorrowerCycle": [
@@ -51,6 +51,15 @@ fineract.events.external.producer.jms.enabled=${FINERACT_EXTERNAL_EVENTS_PRODUCE
fineract.events.external.producer.jms.event-queue-name=${FINERACT_EXTERNAL_EVENTS_PRODUCER_JMS_QUEUE_NAME:JMS-event-queue} fineract.events.external.producer.jms.event-queue-name=${FINERACT_EXTERNAL_EVENTS_PRODUCER_JMS_QUEUE_NAME:JMS-event-queue}
fineract.events.external.producer.jms.broker-url=${FINERACT_EXTERNAL_EVENTS_PRODUCER_JMS_BROKER_URL:tcp://127.0.0.1:61616} fineract.events.external.producer.jms.broker-url=${FINERACT_EXTERNAL_EVENTS_PRODUCER_JMS_BROKER_URL:tcp://127.0.0.1:61616}
fineract.loan.transactionprocessor.creocore.enabled=true
fineract.loan.transactionprocessor.early-repayment.enabled=true
fineract.loan.transactionprocessor.mifos-standard.enabled=true
fineract.loan.transactionprocessor.heavensfamily.enabled=true
fineract.loan.transactionprocessor.interest-principal-penalties-fees.enabled=true
fineract.loan.transactionprocessor.principal-interest-penalties-fees.enabled=true
fineract.loan.transactionprocessor.rbi-india.enabled=true
fineract.loan.transactionprocessor.error-not-found-fail=true
management.health.jms.enabled=false management.health.jms.enabled=false
+1 -1
View File
@@ -37,7 +37,7 @@ configurations {
driver driver
} }
dependencies { dependencies {
driver 'mysql:mysql-connector-java:8.0.30' driver 'mysql:mysql-connector-java:8.0.31'
} }
cargo { cargo {
+1 -1
View File
@@ -37,7 +37,7 @@ configurations {
driver driver
} }
dependencies { dependencies {
driver 'mysql:mysql-connector-java:8.0.30' driver 'mysql:mysql-connector-java:8.0.31'
} }
cargo { cargo {
@@ -6299,7 +6299,7 @@ public class ClientLoanIntegrationTest {
private void validateIfValuesAreNotOverridden(Integer loanID, Integer loanProductID) { private void validateIfValuesAreNotOverridden(Integer loanID, Integer loanProductID) {
String loanProductDetails = this.loanTransactionHelper.getLoanProductDetails(this.requestSpec, this.responseSpec, loanProductID); String loanProductDetails = this.loanTransactionHelper.getLoanProductDetails(this.requestSpec, this.responseSpec, loanProductID);
String loanDetails = this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, loanID); String loanDetails = this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, loanID);
List<String> comparisonAttributes = Arrays.asList("amortizationType", "interestType", "transactionProcessingStrategyId", List<String> comparisonAttributes = Arrays.asList("amortizationType", "interestType", "transactionProcessingStrategyCode",
"interestCalculationPeriodType", "repaymentFrequencyType", "graceOnPrincipalPayment", "graceOnInterestPayment", "interestCalculationPeriodType", "repaymentFrequencyType", "graceOnPrincipalPayment", "graceOnInterestPayment",
"inArrearsTolerance", "graceOnArrearsAgeing"); "inArrearsTolerance", "graceOnArrearsAgeing");
@@ -6313,7 +6313,7 @@ public class ClientLoanIntegrationTest {
private JsonObject createLoanProductConfigurationDetail(JsonObject loanProductConfiguration, Boolean bool) { private JsonObject createLoanProductConfigurationDetail(JsonObject loanProductConfiguration, Boolean bool) {
loanProductConfiguration.addProperty("amortizationType", bool); loanProductConfiguration.addProperty("amortizationType", bool);
loanProductConfiguration.addProperty("interestType", bool); loanProductConfiguration.addProperty("interestType", bool);
loanProductConfiguration.addProperty("transactionProcessingStrategyId", bool); loanProductConfiguration.addProperty("transactionProcessingStrategyCode", bool);
loanProductConfiguration.addProperty("interestCalculationPeriodType", bool); loanProductConfiguration.addProperty("interestCalculationPeriodType", bool);
loanProductConfiguration.addProperty("inArrearsTolerance", bool); loanProductConfiguration.addProperty("inArrearsTolerance", bool);
loanProductConfiguration.addProperty("repaymentEvery", bool); loanProductConfiguration.addProperty("repaymentEvery", bool);
@@ -275,7 +275,8 @@ public final class BatchHelper {
+ "\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10," + "\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10,"
+ "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10," + "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10,"
+ "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1," + "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1,"
+ "\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"" + dateString + "\","; + "\"transactionProcessingStrategyCode\": \"mifos-standard-strategy\", \"expectedDisbursementDate\": \"" + dateString
+ "\",";
if (clientCollateralId != null) { if (clientCollateralId != null) {
body = body + "\"collateral\": [{\"clientCollateralId\": \"" + clientCollateralId + "\", \"quantity\": \"1\"}],"; body = body + "\"collateral\": [{\"clientCollateralId\": \"" + clientCollateralId + "\", \"quantity\": \"1\"}],";
@@ -313,7 +314,7 @@ public final class BatchHelper {
+ "\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10," + "\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10,"
+ "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10," + "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10,"
+ "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1," + "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1,"
+ "\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"10 Jun 2013\"," + "\"transactionProcessingStrategyCode\": \"mifos-standard-strategy\", \"expectedDisbursementDate\": \"10 Jun 2013\","
+ "\"submittedOnDate\": \"10 Jun 2013\"}", clientId, productId); + "\"submittedOnDate\": \"10 Jun 2013\"}", clientId, productId);
br.setBody(body); br.setBody(body);
@@ -39,9 +39,9 @@ public class LoanApplicationTestBuilder {
private static final String EQUAL_INSTALLMENTS = "1"; private static final String EQUAL_INSTALLMENTS = "1";
private static final String CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD = "1"; private static final String CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD = "1";
private static final String LOCALE = "en_GB"; private static final String LOCALE = "en_GB";
public static final String DEFAULT_STRATEGY = "1"; public static final String DEFAULT_STRATEGY = "mifos-standard-strategy";
public static final String RBI_INDIA_STRATEGY = "4"; public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";
public static final String INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = "6"; public static final String INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = "interest-principal-penalties-fees-order-strategy";
private String externalId = null; private String externalId = null;
private String principal = "10,000"; private String principal = "10,000";
@@ -56,7 +56,7 @@ public class LoanApplicationTestBuilder {
private String interestType = FLAT_BALANCE; private String interestType = FLAT_BALANCE;
private String amortizationType = EQUAL_PRINCIPAL_PAYMENTS; private String amortizationType = EQUAL_PRINCIPAL_PAYMENTS;
private String interestCalculationPeriodType = CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD; private String interestCalculationPeriodType = CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD;
private String transactionProcessingID = DEFAULT_STRATEGY; private String transactionProcessingCode = DEFAULT_STRATEGY;
private String expectedDisbursmentDate = ""; private String expectedDisbursmentDate = "";
private String submittedOnDate = ""; private String submittedOnDate = "";
private String loanType = "individual"; private String loanType = "individual";
@@ -147,7 +147,7 @@ public class LoanApplicationTestBuilder {
map.put("fixedPrincipalPercentagePerInstallment", fixedPrincipalPercentagePerInstallment); map.put("fixedPrincipalPercentagePerInstallment", fixedPrincipalPercentagePerInstallment);
map.put("interestType", this.interestType); map.put("interestType", this.interestType);
map.put("interestCalculationPeriodType", this.interestCalculationPeriodType); map.put("interestCalculationPeriodType", this.interestCalculationPeriodType);
map.put("transactionProcessingStrategyId", this.transactionProcessingID); map.put("transactionProcessingStrategyCode", this.transactionProcessingCode);
map.put("expectedDisbursementDate", this.expectedDisbursmentDate); map.put("expectedDisbursementDate", this.expectedDisbursmentDate);
map.put("submittedOnDate", this.submittedOnDate); map.put("submittedOnDate", this.submittedOnDate);
map.put("loanType", this.loanType); map.put("loanType", this.loanType);
@@ -335,8 +335,8 @@ public class LoanApplicationTestBuilder {
return this; return this;
} }
public LoanApplicationTestBuilder withRepaymentStrategy(final String transactionProcessingStrategy) { public LoanApplicationTestBuilder withRepaymentStrategy(final String transactionProcessingStrategyCode) {
this.transactionProcessingID = transactionProcessingStrategy; this.transactionProcessingCode = transactionProcessingStrategyCode;
return this; return this;
} }
@@ -40,11 +40,11 @@ public class LoanProductTestBuilder {
private static final String EQUAL_INSTALLMENTS = "1"; private static final String EQUAL_INSTALLMENTS = "1";
private static final String DECLINING_BALANCE = "0"; private static final String DECLINING_BALANCE = "0";
private static final String FLAT_BALANCE = "1"; private static final String FLAT_BALANCE = "1";
public static final String DEFAULT_STRATEGY = "1"; public static final String DEFAULT_STRATEGY = "mifos-standard-strategy";
public static final String INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = "6"; public static final String INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = "interest-principal-penalties-fees-order-strategy";
// private static final String HEAVENS_FAMILY_STRATEGY ="2"; // private static final String HEAVENS_FAMILY_STRATEGY ="heavensfamily-strategy";
// private static final String CREO_CORE_STRATEGY ="3"; // private static final String CREO_CORE_STRATEGY ="creocore-strategy";
public static final String RBI_INDIA_STRATEGY = "4"; public static final String RBI_INDIA_STRATEGY = "rbi-india-strategy";
public static final String RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD = "1"; public static final String RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD = "1";
public static final String RECALCULATION_FREQUENCY_TYPE_DAILY = "2"; public static final String RECALCULATION_FREQUENCY_TYPE_DAILY = "2";
@@ -83,7 +83,7 @@ public class LoanProductTestBuilder {
private String overdueDaysForNPA = "5"; private String overdueDaysForNPA = "5";
private String interestCalculationPeriodType = CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD; private String interestCalculationPeriodType = CALCULATION_PERIOD_SAME_AS_REPAYMENT_PERIOD;
private String inArrearsTolerance = "0"; private String inArrearsTolerance = "0";
private String transactionProcessingStrategy = DEFAULT_STRATEGY; private String transactionProcessingStrategyCode = DEFAULT_STRATEGY;
private String accountingRule = NONE; private String accountingRule = NONE;
private final String currencyCode = USD; private final String currencyCode = USD;
private String amortizationType = EQUAL_INSTALLMENTS; private String amortizationType = EQUAL_INSTALLMENTS;
@@ -166,7 +166,7 @@ public class LoanProductTestBuilder {
map.put("interestType", this.interestType); map.put("interestType", this.interestType);
map.put("interestCalculationPeriodType", this.interestCalculationPeriodType); map.put("interestCalculationPeriodType", this.interestCalculationPeriodType);
map.put("inArrearsTolerance", this.inArrearsTolerance); map.put("inArrearsTolerance", this.inArrearsTolerance);
map.put("transactionProcessingStrategyId", this.transactionProcessingStrategy); map.put("transactionProcessingStrategyCode", this.transactionProcessingStrategyCode);
map.put("accountingRule", this.accountingRule); map.put("accountingRule", this.accountingRule);
map.put("minPrincipal", this.minPrincipal); map.put("minPrincipal", this.minPrincipal);
map.put("maxPrincipal", this.maxPrincipal); map.put("maxPrincipal", this.maxPrincipal);
@@ -488,7 +488,7 @@ public class LoanProductTestBuilder {
} }
public LoanProductTestBuilder withRepaymentStrategy(final String transactionProcessingStrategy) { public LoanProductTestBuilder withRepaymentStrategy(final String transactionProcessingStrategy) {
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategyCode = transactionProcessingStrategy;
return this; return this;
} }
@@ -22,8 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.linecorp.armeria.internal.shaded.guava.reflect.TypeToken;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -850,7 +850,7 @@ public class LoanTransactionHelper {
map.put("interestType", "0"); map.put("interestType", "0");
map.put("interestCalculationPeriodType", "1"); map.put("interestCalculationPeriodType", "1");
map.put("expectedDisbursementDate", "20 September 2011"); map.put("expectedDisbursementDate", "20 September 2011");
map.put("transactionProcessingStrategyId", "1"); map.put("transactionProcessingStrategyCode", "mifos-standard-strategy");
return new Gson().toJson(map); return new Gson().toJson(map);
} }
+1 -1
View File
@@ -37,7 +37,7 @@ configurations {
driver driver
} }
dependencies { dependencies {
driver 'mysql:mysql-connector-java:8.0.30' driver 'mysql:mysql-connector-java:8.0.31'
} }
cargo { cargo {
+1 -1
View File
@@ -37,7 +37,7 @@ configurations {
driver driver
} }
dependencies { dependencies {
driver 'mysql:mysql-connector-java:8.0.30' driver 'mysql:mysql-connector-java:8.0.31'
} }
cargo { cargo {