FINERACT-2418: create the loan origination module

This commit is contained in:
Attila Budai
2026-01-19 14:14:20 +01:00
committed by Adam Saghy
parent 2a064dcb09
commit f83ea7ea15
19 changed files with 959 additions and 0 deletions
+2
View File
@@ -37,6 +37,7 @@ buildscript {
'fineract-charge', 'fineract-charge',
'fineract-rates', 'fineract-rates',
'fineract-tax', 'fineract-tax',
'fineract-loan-origination',
'fineract-loan', 'fineract-loan',
'fineract-savings', 'fineract-savings',
'fineract-report', 'fineract-report',
@@ -67,6 +68,7 @@ buildscript {
'fineract-charge', 'fineract-charge',
'fineract-rates', 'fineract-rates',
'fineract-tax', 'fineract-tax',
'fineract-loan-origination',
'fineract-loan', 'fineract-loan',
'fineract-savings', 'fineract-savings',
'fineract-report', 'fineract-report',
@@ -600,6 +600,7 @@ public class FineractProperties {
private FineractInvestorModuleProperties investor; private FineractInvestorModuleProperties investor;
private FineractSelfServiceModuleProperties selfService; private FineractSelfServiceModuleProperties selfService;
private FineractLoanOriginationModuleProperties loanOrigination;
} }
@Getter @Getter
@@ -614,6 +615,12 @@ public class FineractProperties {
} }
@Getter
@Setter
public static class FineractLoanOriginationModuleProperties extends AbstractFineractModuleProperties {
}
@Getter @Getter
@Setter @Setter
public static class FineractSqlValidationProperties { public static class FineractSqlValidationProperties {
+81
View File
@@ -0,0 +1,81 @@
/**
* 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 = 'Fineract Loan Origination'
apply plugin: 'java'
apply plugin: 'eclipse'
compileJava {
dependsOn ':fineract-avro-schemas:buildJavaSdk'
}
configurations {
providedRuntime // needed for Spring Boot executable WAR
providedCompile
compile() {
exclude module: 'hibernate-entitymanager'
exclude module: 'hibernate-validator'
exclude module: 'activation'
exclude module: 'bcmail-jdk14'
exclude module: 'bcprov-jdk14'
exclude module: 'bctsp-jdk14'
exclude module: 'c3p0'
exclude module: 'stax-api'
exclude module: 'jaxb-api'
exclude module: 'jaxb-impl'
exclude module: 'jboss-logging'
exclude module: 'itext-rtf'
exclude module: 'classworlds'
}
runtime
}
apply from: 'dependencies.gradle'
// Configuration for the modernizer plugin
// https://github.com/andygoossens/gradle-modernizer-plugin
modernizer {
ignoreClassNamePatterns = [
'.*AbstractPersistableCustom',
'.*EntityTables',
'.*domain.*'
]
}
// If we are running Gradle within Eclipse to enhance classes with OpenJPA,
// set the classes directory to point to Eclipse's default build directory
if (project.hasProperty('env') && project.getProperty('env') == 'eclipse') {
sourceSets.main.java.outputDir = new File(rootProject.projectDir, "fineract-loan-origination/bin/main")
}
eclipse {
project {
buildCommand([ LaunchConfigHandle: "<project>/.externalToolBuilders/OpenJPA Enhance Builder.launch" ], 'org.eclipse.ui.externaltools.ExternalToolBuilder')
}
}
if (!(project.hasProperty('env') && project.getProperty('env') == 'dev')) {
sourceSets {
test {
java {
exclude '**/core/boot/tests/**'
}
}
}
}
@@ -0,0 +1,74 @@
/**
* 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.
*/
dependencies {
// Never use "compile" scope, but make all dependencies either 'implementation', 'runtimeOnly' or 'testCompile'.
// Note that we never use 'api', because Fineract at least currently is a simple monolithic application ("WAR"), not a library.
// We also (normally should have) no need to ever use 'compileOnly'.
// implementation dependencies are directly used (compiled against) in src/main (and src/test)
//
implementation(project(path: ':fineract-core'))
implementation(
'org.springframework.boot:spring-boot-starter-web',
'org.springframework.boot:spring-boot-starter-security',
'jakarta.ws.rs:jakarta.ws.rs-api',
'org.glassfish.jersey.media:jersey-media-multipart',
'com.google.guava:guava',
'com.google.code.gson:gson',
'org.apache.commons:commons-lang3',
'com.jayway.jsonpath:json-path',
'com.github.spotbugs:spotbugs-annotations',
'io.swagger.core.v3:swagger-annotations-jakarta',
'com.squareup.retrofit2:converter-gson',
'org.springdoc:springdoc-openapi-starter-webmvc-ui',
'org.mapstruct:mapstruct',
'io.github.resilience4j:resilience4j-spring-boot3',
'org.apache.httpcomponents:httpcore',
)
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.mapstruct:mapstruct-processor'
implementation ('org.springframework.boot:spring-boot-starter-data-jpa') {
exclude group: 'org.hibernate'
}
implementation('org.eclipse.persistence:org.eclipse.persistence.jpa') {
exclude group: 'org.eclipse.persistence', module: 'jakarta.persistence'
}
// testCompile dependencies are ONLY used in src/test, not src/main.
// Do NOT repeat dependencies which are ALREADY in implementation or runtimeOnly!
//
testImplementation( 'io.github.classgraph:classgraph' )
testImplementation ('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'com.jayway.jsonpath', module: 'json-path'
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
exclude group: 'jakarta.activation'
exclude group: 'javax.activation'
exclude group: 'org.skyscreamer'
}
testImplementation ('org.mockito:mockito-inline')
}
@@ -0,0 +1,30 @@
/**
* 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.loanorigination.config;
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
public class LoanOriginationModuleIsEnabledCondition extends PropertiesCondition {
@Override
protected boolean matches(FineractProperties properties) {
return properties.getModule().getLoanOrigination().isEnabled();
}
}
@@ -0,0 +1,78 @@
/**
* 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.loanorigination.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
@Getter
@Setter
@Entity
@NoArgsConstructor
@Table(name = "m_loan_originator")
public class LoanOriginator extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Column(name = "external_id", nullable = false, length = 100, unique = true)
private ExternalId externalId;
@Column(name = "name", length = 255)
private String name;
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false, length = 20)
private LoanOriginatorStatus status;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "originator_type_cv_id")
private CodeValue originatorType;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "channel_type_cv_id")
private CodeValue channelType;
public static LoanOriginator create(ExternalId externalId, String name, LoanOriginatorStatus status, CodeValue originatorType,
CodeValue channelType) {
LoanOriginator originator = new LoanOriginator();
originator.setExternalId(externalId);
originator.setName(name);
originator.setStatus(status);
originator.setOriginatorType(originatorType);
originator.setChannelType(channelType);
return originator;
}
public void update(String name, LoanOriginatorStatus status, CodeValue originatorType, CodeValue channelType) {
this.name = name;
this.status = status;
this.originatorType = originatorType;
this.channelType = channelType;
}
}
@@ -0,0 +1,52 @@
/**
* 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.loanorigination.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
@Getter
@Setter
@Entity
@NoArgsConstructor
@Table(name = "m_loan_originator_mapping")
public class LoanOriginatorMapping extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Column(name = "loan_id", nullable = false)
private Long loanId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "originator_id", nullable = false)
private LoanOriginator originator;
public static LoanOriginatorMapping create(Long loanId, LoanOriginator originator) {
LoanOriginatorMapping mapping = new LoanOriginatorMapping();
mapping.setLoanId(loanId);
mapping.setOriginator(originator);
return mapping;
}
}
@@ -0,0 +1,42 @@
/**
* 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.loanorigination.domain;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LoanOriginatorMappingRepository
extends JpaRepository<LoanOriginatorMapping, Long>, JpaSpecificationExecutor<LoanOriginatorMapping> {
List<LoanOriginatorMapping> findByLoanId(Long loanId);
boolean existsByLoanId(Long loanId);
boolean existsByOriginatorId(Long originatorId);
List<LoanOriginatorMapping> findByOriginatorId(Long originatorId);
Optional<LoanOriginatorMapping> findByLoanIdAndOriginatorId(Long loanId, Long originatorId);
boolean existsByLoanIdAndOriginatorId(Long loanId, Long originatorId);
void deleteByLoanIdAndOriginatorId(Long loanId, Long originatorId);
}
@@ -0,0 +1,34 @@
/**
* 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.loanorigination.domain;
import java.util.List;
import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LoanOriginatorRepository extends JpaRepository<LoanOriginator, Long>, JpaSpecificationExecutor<LoanOriginator> {
Optional<LoanOriginator> findByExternalId(ExternalId externalId);
boolean existsByExternalId(ExternalId externalId);
List<LoanOriginator> findByStatus(LoanOriginatorStatus status);
}
@@ -0,0 +1,43 @@
/**
* 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.loanorigination.domain;
public enum LoanOriginatorStatus {
ACTIVE("ACTIVE"), PENDING("PENDING");
private final String value;
LoanOriginatorStatus(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static LoanOriginatorStatus fromString(String text) {
for (LoanOriginatorStatus status : LoanOriginatorStatus.values()) {
if (status.value.equalsIgnoreCase(text)) {
return status;
}
}
throw new IllegalArgumentException("Unknown LoanOriginatorStatus: " + text);
}
}
@@ -0,0 +1,26 @@
<?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">
<include relativeToChangelogFile="true" file="parts/0001_initial_schema.xml"/>
</databaseChangeLog>
@@ -0,0 +1,436 @@
<?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">
<!-- Create Codes for Originator Type -->
<changeSet id="loan-origination-001" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code WHERE code_name = 'LoanOriginatorType'
</sqlCheck>
</preConditions>
<insert tableName="m_code">
<column name="code_name" value="LoanOriginatorType"/>
<column name="is_system_defined" valueBoolean="true"/>
</insert>
</changeSet>
<!-- Create Code Values for Originator Type -->
<changeSet id="loan-origination-002" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginatorType' AND cv.code_value = 'MERCHANT'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginatorType')"/>
<column name="code_value" value="MERCHANT"/>
<column name="order_position" valueNumeric="1"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<changeSet id="loan-origination-003" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginatorType' AND cv.code_value = 'BROKER'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginatorType')"/>
<column name="code_value" value="BROKER"/>
<column name="order_position" valueNumeric="2"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<changeSet id="loan-origination-004" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginatorType' AND cv.code_value = 'AFFILIATE'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginatorType')"/>
<column name="code_value" value="AFFILIATE"/>
<column name="order_position" valueNumeric="3"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<changeSet id="loan-origination-005" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginatorType' AND cv.code_value = 'PLATFORM'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginatorType')"/>
<column name="code_value" value="PLATFORM"/>
<column name="order_position" valueNumeric="4"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<!-- Create Codes for Channel Type -->
<changeSet id="loan-origination-006" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code WHERE code_name = 'LoanOriginationChannelType'
</sqlCheck>
</preConditions>
<insert tableName="m_code">
<column name="code_name" value="LoanOriginationChannelType"/>
<column name="is_system_defined" valueBoolean="true"/>
</insert>
</changeSet>
<!-- Create Code Values for Channel Type -->
<changeSet id="loan-origination-007" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginationChannelType' AND cv.code_value = 'ONLINE'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginationChannelType')"/>
<column name="code_value" value="ONLINE"/>
<column name="order_position" valueNumeric="1"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<changeSet id="loan-origination-008" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginationChannelType' AND cv.code_value = 'IN_STORE'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginationChannelType')"/>
<column name="code_value" value="IN_STORE"/>
<column name="order_position" valueNumeric="2"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<changeSet id="loan-origination-009" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginationChannelType' AND cv.code_value = 'API'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginationChannelType')"/>
<column name="code_value" value="API"/>
<column name="order_position" valueNumeric="3"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<changeSet id="loan-origination-010" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
SELECT COUNT(1) FROM m_code_value cv
JOIN m_code c ON cv.code_id = c.id
WHERE c.code_name = 'LoanOriginationChannelType' AND cv.code_value = 'AGGREGATOR'
</sqlCheck>
</preConditions>
<insert tableName="m_code_value">
<column name="code_id" valueComputed="(SELECT id FROM m_code WHERE code_name = 'LoanOriginationChannelType')"/>
<column name="code_value" value="AGGREGATOR"/>
<column name="order_position" valueNumeric="4"/>
<column name="is_active" valueBoolean="true"/>
<column name="is_mandatory" valueBoolean="false"/>
</insert>
</changeSet>
<!-- Create m_loan_originator table - MySQL -->
<changeSet id="loan-origination-011" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_loan_originator"/>
</not>
</preConditions>
<createTable tableName="m_loan_originator">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="external_id" type="VARCHAR(100)">
<constraints nullable="false" unique="true" uniqueConstraintName="UQ_loan_originator_external_id"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="status" type="VARCHAR(20)">
<constraints nullable="false"/>
</column>
<column name="originator_type_cv_id" type="INT">
<constraints nullable="true"/>
</column>
<column name="channel_type_cv_id" type="INT">
<constraints nullable="true"/>
</column>
<column name="created_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- Create m_loan_originator table - PostgreSQL -->
<changeSet id="loan-origination-011" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_loan_originator"/>
</not>
</preConditions>
<createTable tableName="m_loan_originator">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="external_id" type="VARCHAR(100)">
<constraints nullable="false" unique="true" uniqueConstraintName="UQ_loan_originator_external_id"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="status" type="VARCHAR(20)">
<constraints nullable="false"/>
</column>
<column name="originator_type_cv_id" type="INT">
<constraints nullable="true"/>
</column>
<column name="channel_type_cv_id" type="INT">
<constraints nullable="true"/>
</column>
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- Add foreign keys for m_loan_originator -->
<changeSet id="loan-origination-012" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator"/>
<columnExists tableName="m_loan_originator" columnName="originator_type_cv_id"/>
</preConditions>
<addForeignKeyConstraint baseTableName="m_loan_originator" baseColumnNames="originator_type_cv_id"
referencedTableName="m_code_value" referencedColumnNames="id"
constraintName="FK_loan_originator_type_cv" onDelete="RESTRICT" onUpdate="RESTRICT"/>
</changeSet>
<changeSet id="loan-origination-013" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator"/>
<columnExists tableName="m_loan_originator" columnName="channel_type_cv_id"/>
</preConditions>
<addForeignKeyConstraint baseTableName="m_loan_originator" baseColumnNames="channel_type_cv_id"
referencedTableName="m_code_value" referencedColumnNames="id"
constraintName="FK_loan_originator_channel_cv" onDelete="RESTRICT" onUpdate="RESTRICT"/>
</changeSet>
<!-- Add FK for audit columns on m_loan_originator -->
<changeSet id="loan-origination-014" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator"/>
</preConditions>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_loan_originator"
constraintName="FK_loan_originator_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT"
referencedColumnNames="id" referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_loan_originator"
constraintName="FK_loan_originator_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT"
referencedColumnNames="id" referencedTableName="m_appuser" validate="true"/>
</changeSet>
<!-- Create m_loan_originator_mapping table - MySQL -->
<changeSet id="loan-origination-015" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_loan_originator_mapping"/>
</not>
<tableExists tableName="m_loan"/>
</preConditions>
<createTable tableName="m_loan_originator_mapping">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="loan_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="originator_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- Create m_loan_originator_mapping table - PostgreSQL -->
<changeSet id="loan-origination-015" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_loan_originator_mapping"/>
</not>
<tableExists tableName="m_loan"/>
</preConditions>
<createTable tableName="m_loan_originator_mapping">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="loan_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="originator_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="last_modified_by" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<!-- Add foreign keys for m_loan_originator_mapping -->
<changeSet id="loan-origination-016" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator_mapping"/>
</preConditions>
<addForeignKeyConstraint baseTableName="m_loan_originator_mapping" baseColumnNames="loan_id"
referencedTableName="m_loan" referencedColumnNames="id"
constraintName="FK_loan_originator_mapping_loan" onDelete="RESTRICT" onUpdate="RESTRICT"/>
</changeSet>
<changeSet id="loan-origination-017" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator_mapping"/>
</preConditions>
<addForeignKeyConstraint baseTableName="m_loan_originator_mapping" baseColumnNames="originator_id"
referencedTableName="m_loan_originator" referencedColumnNames="id"
constraintName="FK_loan_originator_mapping_originator" onDelete="RESTRICT" onUpdate="RESTRICT"/>
</changeSet>
<!-- Add FK for audit columns on m_loan_originator_mapping -->
<changeSet id="loan-origination-018" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator_mapping"/>
</preConditions>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_loan_originator_mapping"
constraintName="FK_loan_originator_mapping_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT"
referencedColumnNames="id" referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_loan_originator_mapping"
constraintName="FK_loan_originator_mapping_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT"
referencedColumnNames="id" referencedTableName="m_appuser" validate="true"/>
</changeSet>
<!-- Add unique constraint for loan-originator mapping (prevent duplicate assignments) -->
<changeSet id="loan-origination-019" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator_mapping"/>
</preConditions>
<addUniqueConstraint tableName="m_loan_originator_mapping" columnNames="loan_id, originator_id"
constraintName="UQ_loan_originator_mapping_loan_originator"/>
</changeSet>
<!-- Add indexes -->
<changeSet id="loan-origination-020" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator"/>
</preConditions>
<createIndex tableName="m_loan_originator" indexName="idx_loan_originator_status">
<column name="status"/>
</createIndex>
</changeSet>
<changeSet id="loan-origination-021" author="fineract">
<preConditions onFail="MARK_RAN">
<tableExists tableName="m_loan_originator_mapping"/>
</preConditions>
<createIndex tableName="m_loan_originator_mapping" indexName="idx_loan_originator_mapping_originator">
<column name="originator_id"/>
</createIndex>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,47 @@
<?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.
-->
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<!-- This file is only used for static weaving, nothing more. -->
<!-- You can find the runtime configuration in the JPAConfig class -->
<persistence-unit name="jpa-pu" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<!-- Core entities needed for weaving -->
<class>org.apache.fineract.infrastructure.codes.domain.Code</class>
<class>org.apache.fineract.infrastructure.codes.domain.CodeValue</class>
<!-- Loan Origination Module Entities -->
<class>org.apache.fineract.portfolio.loanorigination.domain.LoanOriginator</class>
<class>org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMapping</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.weaving" value="static" />
</properties>
</persistence-unit>
</persistence>
+1
View File
@@ -39,6 +39,7 @@ dependencies {
implementation(project(path: ':fineract-progressive-loan')) implementation(project(path: ':fineract-progressive-loan'))
implementation(project(path: ':fineract-report')) implementation(project(path: ':fineract-report'))
implementation(project(path: ':fineract-tax')) implementation(project(path: ':fineract-tax'))
implementation(project(path: ':fineract-loan-origination'))
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
@@ -212,6 +212,7 @@ fineract.sampling.resetPeriodSec=${FINERACT_SAMPLING_RESET_PERIOD_IN_SEC:60}
#Modules #Modules
fineract.module.self-service.enabled=${FINERACT_MODULE_SELF_SERVICE_ENABLED:false} fineract.module.self-service.enabled=${FINERACT_MODULE_SELF_SERVICE_ENABLED:false}
fineract.module.investor.enabled=${FINERACT_MODULE_INVESTOR_ENABLED:true} fineract.module.investor.enabled=${FINERACT_MODULE_INVESTOR_ENABLED:true}
fineract.module.loan-origination.enabled=${FINERACT_MODULE_LOAN_ORIGINATION_ENABLED:false}
fineract.insecure-http-client=${FINERACT_INSECURE_HTTP_CLIENT:true} fineract.insecure-http-client=${FINERACT_INSECURE_HTTP_CLIENT:true}
fineract.client-connect-timeout=${FINERACT_CLIENT_CONNECT_TIMEOUT:30} fineract.client-connect-timeout=${FINERACT_CLIENT_CONNECT_TIMEOUT:30}
@@ -37,6 +37,7 @@
<include file="db/changelog/tenant/module/savings/parts/module-changelog-master.xml" context="tenant_db AND !initial_switch"/> <include file="db/changelog/tenant/module/savings/parts/module-changelog-master.xml" context="tenant_db AND !initial_switch"/>
<includeAll path="db/custom-changelog" errorIfMissingOrEmpty="false" context="tenant_db AND !initial_switch AND custom_changelog"/> <includeAll path="db/custom-changelog" errorIfMissingOrEmpty="false" context="tenant_db AND !initial_switch AND custom_changelog"/>
<include file="/db/changelog/tenant/module/progressiveloan/module-changelog-master.xml" context="tenant_db AND !initial_switch"/> <include file="/db/changelog/tenant/module/progressiveloan/module-changelog-master.xml" context="tenant_db AND !initial_switch"/>
<include file="db/changelog/tenant/module/loanorigination/module-changelog-master.xml" context="tenant_db AND !initial_switch"/>
<!-- Scripts to run after the modules were initialized --> <!-- Scripts to run after the modules were initialized -->
<include file="tenant/final-changelog-tenant.xml" relativeToChangelogFile="true" context="tenant_db AND !initial_switch"/> <include file="tenant/final-changelog-tenant.xml" relativeToChangelogFile="true" context="tenant_db AND !initial_switch"/>
</databaseChangeLog> </databaseChangeLog>
@@ -125,6 +125,8 @@ fineract.sampling.sampledClasses=
fineract.module.investor.enabled=true fineract.module.investor.enabled=true
fineract.module.self-service.enabled=true fineract.module.self-service.enabled=true
# TODO: activate this module when the implementation is ready
fineract.module.loan-origination.enabled=false
# sql validation # sql validation
+1
View File
@@ -86,6 +86,7 @@ dependencies {
implementation project(':fineract-charge') implementation project(':fineract-charge')
implementation project(':fineract-rates') implementation project(':fineract-rates')
implementation project(':fineract-loan') implementation project(':fineract-loan')
implementation project(':fineract-loan-origination')
implementation project(':fineract-progressive-loan') implementation project(':fineract-progressive-loan')
implementation project(':fineract-savings') implementation project(':fineract-savings')
implementation project(':fineract-provider') implementation project(':fineract-provider')
+1
View File
@@ -59,6 +59,7 @@ include ':fineract-investor'
include ':fineract-rates' include ':fineract-rates'
include ':fineract-charge' include ':fineract-charge'
include ':fineract-tax' include ':fineract-tax'
include ':fineract-loan-origination'
include ':fineract-loan' include ':fineract-loan'
include ':fineract-savings' include ':fineract-savings'
include ':fineract-report' include ':fineract-report'