Compare commits
79
Commits
ea2dfd789d
...
383dfdc6e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
383dfdc6e9 | ||
|
|
5ed95c27e8 | ||
|
|
fde670e644 | ||
|
|
3bb7fc8050 | ||
|
|
04d65d857f | ||
|
|
9d09d9ebd4 | ||
|
|
eb88fb0813 | ||
|
|
f13a9c6bba | ||
|
|
0e076889e9 | ||
|
|
87b308b527 | ||
|
|
934a51c9ee | ||
|
|
9980310c9e | ||
|
|
cfcd24efad | ||
|
|
73db4ee114 | ||
|
|
e72b9359c1 | ||
|
|
a1a31aff25 | ||
|
|
2606427203 | ||
|
|
b0bfa386ce | ||
|
|
00d3a2e333 | ||
|
|
048a4ecfde | ||
|
|
7ce7353e7a | ||
|
|
99c153830c | ||
|
|
ba199d78e7 | ||
|
|
99cc972804 | ||
|
|
99a89d5d81 | ||
|
|
388af0a6a3 | ||
|
|
1bd41cff43 | ||
|
|
34f84e1fc6 | ||
|
|
69513f25f3 | ||
|
|
3d30bdb788 | ||
|
|
ed139d6e68 | ||
|
|
db41c2b606 | ||
|
|
fc80869d4b | ||
|
|
8e55b6747c | ||
|
|
21878939b5 | ||
|
|
b5c9101fa4 | ||
|
|
1ff3f9c0c9 | ||
|
|
1eba2c50ed | ||
|
|
3367b7d935 | ||
|
|
5899d2ca07 | ||
|
|
c1e23080fc | ||
|
|
7354da8c1e | ||
|
|
bf5ce421df | ||
|
|
d002fc0c9b | ||
|
|
a6eae3d6e8 | ||
|
|
3e1abaef03 | ||
|
|
40449ef0f7 | ||
|
|
33ea10689f | ||
|
|
8d5b4d1ce9 | ||
|
|
db94056144 | ||
|
|
19639c9e02 | ||
|
|
53b6a94bf8 | ||
|
|
6d0c1fe7f6 | ||
|
|
edaeb674d5 | ||
|
|
2371ad98b1 | ||
|
|
9d944d0196 | ||
|
|
6e31ac9acd | ||
|
|
a051faba5d | ||
|
|
7c4cf6260c | ||
|
|
6bdcac7b77 | ||
|
|
8bf28a8033 | ||
|
|
d4262ebf6f | ||
|
|
656a0b4f13 | ||
|
|
ae7017ca81 | ||
|
|
6413befe07 | ||
|
|
eae5b03d7e | ||
|
|
49d64004cc | ||
|
|
ada24d7497 | ||
|
|
b89e653bcb | ||
|
|
9a30ede278 | ||
|
|
ee915e395a | ||
|
|
7d4f409202 | ||
|
|
2251fd2259 | ||
|
|
45585ecc10 | ||
|
|
7dea88d30f | ||
|
|
d32953ed76 | ||
|
|
5a98a89839 | ||
|
|
d5157ef701 | ||
|
|
c6f130e116 |
@@ -1 +1 @@
|
|||||||
{"sessionId":"0fda6e48-a79d-4980-a9ee-31a6d5f735e1","pid":15548,"acquiredAt":1776168814856}
|
{"sessionId":"b5222d29-c337-4c5d-afde-70cde98cdf46","pid":426448,"acquiredAt":1780052566378}
|
||||||
@@ -52,7 +52,32 @@
|
|||||||
"Bash(git -c commit.gpgsign=false commit -m ' *)",
|
"Bash(git -c commit.gpgsign=false commit -m ' *)",
|
||||||
"Bash(git pull *)",
|
"Bash(git pull *)",
|
||||||
"Bash(./gradlew :fineract-security:compileJava :fineract-provider:compileJava -q)",
|
"Bash(./gradlew :fineract-security:compileJava :fineract-provider:compileJava -q)",
|
||||||
"Bash(git ls-remote *)"
|
"Bash(git ls-remote *)",
|
||||||
|
"Bash(PGPASSWORD=root psql -h localhost -p 5432 -U postgres -d fineract_default -c \"SELECT cv.id, cv.code_value FROM m_code_value cv JOIN m_code c ON cv.code_id=c.id WHERE c.code_name='Constitution' ORDER BY cv.id;\")",
|
||||||
|
"Bash(curl -s -w '\\\\n--- HTTP %{http_code} | content-type: %{content_type} | %{size_download} bytes | %{time_total}s ---\\\\n' -X POST https://ekyc.lincpay.in/EKYC/ekyc/validate-pan -H 'Content-Type: application/json' -d '{\"apikey\":\"api_7c9e3a1f6d4b2a8c5e1f3d7b9a2c6e4f\",\"panNumber\":\"AQRPV8345E\",\"name\":\"Aditya Vyas\",\"dob\":\"19/12/1988\",\"secrete\":\"secret_4e8a2d7c1f9b3a6e5d2c8f1a7b4e9c3d\"}')",
|
||||||
|
"Bash(curl -s -w '\\\\n--- HTTP %{http_code} | content-type: %{content_type} | %{size_download} bytes | %{time_total}s ---\\\\n' -X POST https://ekyc.lincpay.in/EKYC/ekyc/validate-pan -H 'Content-Type: application/json' -d '{\"apikey\":\"api_7c9e3a1f6d4b2a8c5e1f3d7b9a2c6e4f\",\"panNumber\":\"AAMCG5406B\",\"name\":\"GIZMOGLADE PRIVATE LIMITED\",\"dob\":\"01/12/2025\",\"secrete\":\"secret_4e8a2d7c1f9b3a6e5d2c8f1a7b4e9c3d\"}')",
|
||||||
|
"Bash(curl -s -w '\\\\n--- HTTP %{http_code} | content-type: %{content_type} | %{size_download} bytes ---\\\\n' -X POST https://ekyc.lincpay.in/EKYC/ekyc/validate-pan -H 'Content-Type: application/json' -d '{\"apikey\":\"api_7c9e3a1f6d4b2a8c5e1f3d7b9a2c6e4f\",\"panNumber\":\"AAMCG5406B\",\"name\":\"\",\"dob\":\"\",\"secrete\":\"secret_4e8a2d7c1f9b3a6e5d2c8f1a7b4e9c3d\"}')",
|
||||||
|
"Bash(curl -s -w '\\\\n--- HTTP %{http_code} | content-type: %{content_type} | %{size_download} bytes ---\\\\n' -X POST https://ekyc.lincpay.in/EKYC/ekyc/validate-gstn -H 'Content-Type: application/json' -d '{\"apikey\":\"api_7c9e3a1f6d4b2a8c5e1f3d7b9a2c6e4f\",\"gstNumber\":\"21AAMCG5406BIZ6\",\"secrete\":\"secret_4e8a2d7c1f9b3a6e5d2c8f1a7b4e9c3d\"}')",
|
||||||
|
"Bash(curl -s -w '\\\\n--- HTTP %{http_code} | content-type: %{content_type} | %{size_download} bytes | %{time_total}s ---\\\\n' -X POST https://ekyc.lincpay.in/EKYC/ekyc/validate-gstn -H 'Content-Type: application/json' -d '{\"apikey\":\"api_7c9e3a1f6d4b2a8c5e1f3d7b9a2c6e4f\",\"gstNumber\":\"23AADCL9984A1ZH\",\"secrete\":\"secret_4e8a2d7c1f9b3a6e5d2c8f1a7b4e9c3d\"}')",
|
||||||
|
"PowerShell(Get-Process | Where-Object { $_.ProcessName -match '^\\(java|gradle\\)' } | Select-Object Id, ProcessName, StartTime, @{n='CPU\\(s\\)';e={[math]::Round\\($_.CPU,1\\)}}, @{n='WS\\(MB\\)';e={[math]::Round\\($_.WorkingSet64/1MB,0\\)}} | Format-Table -AutoSize)",
|
||||||
|
"PowerShell(Get-NetTCPConnection -LocalPort 8443 -ErrorAction SilentlyContinue)",
|
||||||
|
"PowerShell(Stop-Process -Id 3468,3864,39964 -Force)",
|
||||||
|
"PowerShell(Get-Process -Id 3468,3864,39964 -ErrorAction SilentlyContinue)",
|
||||||
|
"PowerShell(foreach \\($id in 3468,3864,39964\\) { try { Stop-Process -Id $id -Force -ErrorAction Stop; \"killed $id\" } catch { \"skip $id - $\\($_.Exception.Message\\)\" } })",
|
||||||
|
"PowerShell(Get-Process java -ErrorAction SilentlyContinue)",
|
||||||
|
"PowerShell(if \\(-not \\(Test-Path .\\\\fineract-provider\\\\logs\\)\\) { New-Item -ItemType Directory -Force .\\\\fineract-provider\\\\logs | Out-Null }; Remove-Item .\\\\fineract-provider\\\\logs\\\\devrun.out -ErrorAction SilentlyContinue; \"ready\")",
|
||||||
|
"PowerShell($j = Get-Process java -ErrorAction SilentlyContinue | Select-Object Id, @{n='CPU\\(s\\)';e={[math]::Round\\($_.CPU,1\\)}}, @{n='WS\\(MB\\)';e={[math]::Round\\($_.WorkingSet64/1MB,0\\)}}, StartTime; $j | Format-Table -AutoSize; \"---\"; Get-NetTCPConnection -LocalPort 8443 -State Listen -ErrorAction SilentlyContinue | Select-Object LocalAddress, OwningProcess)",
|
||||||
|
"PowerShell(if \\(Test-Path .\\\\fineract-provider\\\\logs\\\\devrun.out\\) { $f = Get-Item .\\\\fineract-provider\\\\logs\\\\devrun.out; \"size=$\\($f.Length\\) bytes mtime=$\\($f.LastWriteTime\\)\" } else { \"devrun.out missing\" })",
|
||||||
|
"PowerShell(netstat -ano)",
|
||||||
|
"PowerShell(Get-Process java -ErrorAction SilentlyContinue | Stop-Process -Force; Start-Sleep -Milliseconds 800; $left = Get-Process java -ErrorAction SilentlyContinue; if \\($left\\) { $left | Select-Object Id, ProcessName } else { \"no java processes\" }; Get-NetTCPConnection -LocalPort 8443 -State Listen -ErrorAction SilentlyContinue | Measure-Object | ForEach-Object { \"8443 listeners: $\\($_.Count\\)\" })",
|
||||||
|
"Bash(curl -sk -o /tmp/resp.txt -w \"STATUS=%{http_code}\\\\nCONTENT_TYPE=%{content_type}\\\\n\" -X POST 'https://localhost:8443/fineract-provider/api/v1/authentication' -H 'Content-Type: application/json' -H 'Fineract-Platform-TenantId: default' -d '{\"username\":\"mifos\",\"password\":\"password\"}')",
|
||||||
|
"Bash(curl -sk -o /tmp/r1.txt -w \"STATUS=%{http_code}\\\\n\" -X POST 'https://localhost:8443/fineract-provider/api/v1/authentication' -H 'Content-Type: application/json' -d '{\"username\":\"mifos\",\"password\":\"password\"}')",
|
||||||
|
"Bash(curl -s -o /tmp/r2.txt -w \"STATUS=%{http_code}\\\\n\" -X POST 'http://localhost:4200/fineract-provider/api/v1/authentication' -H 'Content-Type: application/json' -d '{\"username\":\"mifos\",\"password\":\"password\"}')",
|
||||||
|
"Bash(curl -s -o /tmp/r3.txt -w \"STATUS=%{http_code}\\\\n\" -X POST 'http://localhost:4200/fineract-provider/api/v1/authentication' -H 'Content-Type: application/json' -H 'Origin: http://localhost:4200' -H 'Fineract-Platform-TenantId: default' -d '{\"username\":\"mifos\",\"password\":\"password\"}')",
|
||||||
|
"Bash(curl -s -o /tmp/r4.txt -w \"STATUS=%{http_code}\\\\n\" -X OPTIONS 'http://localhost:4200/fineract-provider/api/v1/authentication' -H 'Origin: http://localhost:4200' -H 'Access-Control-Request-Method: POST' -H 'Access-Control-Request-Headers: content-type,fineract-platform-tenantid' -i)",
|
||||||
|
"Bash(git cherry-pick *)",
|
||||||
|
"PowerShell($parent = 'C:\\\\Users\\\\adity\\\\Music\\\\CBS'; if \\(Test-Path $parent\\) { \"parent exists: $parent\"; Get-ChildItem $parent -Directory | Select-Object -ExpandProperty Name } else { \"MISSING parent: $parent\" }; \"---\"; $target = Join-Path $parent 'LincpayEKYC'; if \\(Test-Path $target\\) { \"TARGET ALREADY EXISTS: $target\" } else { \"target free: $target\" })",
|
||||||
|
"Bash(git clone *)"
|
||||||
],
|
],
|
||||||
"additionalDirectories": [
|
"additionalDirectories": [
|
||||||
"c:\\Users\\adity\\Music\\CBS\\cfcfrms-bank-api",
|
"c:\\Users\\adity\\Music\\CBS\\cfcfrms-bank-api",
|
||||||
|
|||||||
+40
-37
@@ -1,37 +1,40 @@
|
|||||||
*.class
|
*.class
|
||||||
*.jar
|
*.jar
|
||||||
*.war
|
*.war
|
||||||
*.ear
|
*.ear
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
.gradle
|
.gradle
|
||||||
build
|
build
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
*.swp
|
*.swp
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
catalina.base_IS_UNDEFINED/
|
catalina.base_IS_UNDEFINED/
|
||||||
keystore.jks
|
keystore.jks
|
||||||
bin/
|
bin/
|
||||||
.lock
|
.lock
|
||||||
fineract-provider/out/
|
fineract-provider/out/
|
||||||
fineract-provider/config/swagger/config.json
|
fineract-provider/config/swagger/config.json
|
||||||
fineract-provider/config/swagger/fineract-input.yaml
|
fineract-provider/config/swagger/fineract-input.yaml
|
||||||
licenses
|
licenses
|
||||||
**/.asciidoctor/images/*
|
**/.asciidoctor/images/*
|
||||||
**/.asciidoctor/diagram/*
|
**/.asciidoctor/diagram/*
|
||||||
**/images/diag-*.svg
|
**/images/diag-*.svg
|
||||||
|
|
||||||
fineract-provider/src/main/generated/
|
fineract-provider/src/main/generated/
|
||||||
|
|
||||||
**/out/
|
**/out/
|
||||||
gradleExp/
|
gradleExp/
|
||||||
|
|
||||||
.run/
|
.run/
|
||||||
.java-version
|
.java-version
|
||||||
|
|
||||||
.windsurf/
|
.windsurf/
|
||||||
|
|
||||||
|
# Application logs
|
||||||
|
fineract-provider/logs/
|
||||||
+284
-272
@@ -1,272 +1,284 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.accounting.glaccount.service;
|
package org.apache.fineract.accounting.glaccount.service;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
import org.apache.fineract.accounting.glaccount.data.GLAccountDataForLookup;
|
import org.apache.fineract.accounting.glaccount.data.GLAccountDataForLookup;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountUsage;
|
import org.apache.fineract.accounting.glaccount.domain.GLAccountUsage;
|
||||||
import org.apache.fineract.accounting.glaccount.exception.GLAccountInvalidClassificationException;
|
import org.apache.fineract.accounting.glaccount.exception.GLAccountInvalidClassificationException;
|
||||||
import org.apache.fineract.accounting.glaccount.exception.GLAccountNotFoundException;
|
import org.apache.fineract.accounting.glaccount.exception.GLAccountNotFoundException;
|
||||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
||||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformService {
|
public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformService {
|
||||||
|
|
||||||
private static final String NAME_DECORATED_BASE_ON_HIERARCHY = "concat(substring('........................................', 1, ((LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '.', '')) - 1) * 4)), name)";
|
private static final String NAME_DECORATED_BASE_ON_HIERARCHY = "concat(substring('........................................', 1, ((LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '.', '')) - 1) * 4)), name)";
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
private static final String SQL_AND = " and ";
|
private static final String SQL_AND = " and ";
|
||||||
|
|
||||||
private static final class GLAccountMapper implements RowMapper<GLAccountData> {
|
private static final class GLAccountMapper implements RowMapper<GLAccountData> {
|
||||||
|
|
||||||
private final JournalEntryAssociationParametersData associationParametersData;
|
private final JournalEntryAssociationParametersData associationParametersData;
|
||||||
|
|
||||||
GLAccountMapper(final JournalEntryAssociationParametersData associationParametersData) {
|
GLAccountMapper(final JournalEntryAssociationParametersData associationParametersData) {
|
||||||
if (associationParametersData == null) {
|
if (associationParametersData == null) {
|
||||||
this.associationParametersData = new JournalEntryAssociationParametersData();
|
this.associationParametersData = new JournalEntryAssociationParametersData();
|
||||||
} else {
|
} else {
|
||||||
this.associationParametersData = associationParametersData;
|
this.associationParametersData = associationParametersData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String schema() {
|
public String schema() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(
|
sb.append(
|
||||||
" gl.id as id, name as name, parent_id as parentId, gl_code as glCode, disabled as disabled, manual_journal_entries_allowed as manualEntriesAllowed, ")
|
" gl.id as id, name as name, parent_id as parentId, gl_code as glCode, disabled as disabled, manual_journal_entries_allowed as manualEntriesAllowed, ")
|
||||||
.append("classification_enum as classification, account_usage as accountUsage, gl.description as description, ")
|
.append("classification_enum as classification, account_usage as accountUsage, gl.description as description, ")
|
||||||
.append(NAME_DECORATED_BASE_ON_HIERARCHY).append(" as nameDecorated, ")
|
.append(NAME_DECORATED_BASE_ON_HIERARCHY).append(" as nameDecorated, ")
|
||||||
.append("cv.id as codeId, cv.code_value as codeValue ");
|
.append("cv.id as codeId, cv.code_value as codeValue ");
|
||||||
if (this.associationParametersData.isRunningBalanceRequired()) {
|
if (this.associationParametersData.isRunningBalanceRequired()) {
|
||||||
sb.append(",gl_j.organization_running_balance as organizationRunningBalance ");
|
sb.append(",gl_j.organization_running_balance as organizationRunningBalance ");
|
||||||
}
|
}
|
||||||
sb.append("from acc_gl_account gl left join m_code_value cv on tag_id=cv.id ");
|
sb.append("from acc_gl_account gl left join m_code_value cv on tag_id=cv.id ");
|
||||||
if (this.associationParametersData.isRunningBalanceRequired()) {
|
if (this.associationParametersData.isRunningBalanceRequired()) {
|
||||||
sb.append("left outer Join acc_gl_journal_entry gl_j on gl_j.account_id = gl.id");
|
sb.append("left outer join acc_gl_journal_entry gl_j on gl_j.id = (")
|
||||||
}
|
.append("select id from acc_gl_journal_entry ")
|
||||||
return sb.toString();
|
.append("where account_id = gl.id ")
|
||||||
}
|
.append("order by id desc limit 1) ");
|
||||||
|
}
|
||||||
@Override
|
return sb.toString();
|
||||||
public GLAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
}
|
||||||
|
|
||||||
final Long id = rs.getLong("id");
|
@Override
|
||||||
final String name = rs.getString("name");
|
public GLAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
final Long parentId = JdbcSupport.getLong(rs, "parentId");
|
|
||||||
final String glCode = rs.getString("glCode");
|
final Long id = rs.getLong("id");
|
||||||
final boolean disabled = rs.getBoolean("disabled");
|
final String name = rs.getString("name");
|
||||||
final boolean manualEntriesAllowed = rs.getBoolean("manualEntriesAllowed");
|
final Long parentId = JdbcSupport.getLong(rs, "parentId");
|
||||||
final int accountTypeId = JdbcSupport.getInteger(rs, "classification");
|
final String glCode = rs.getString("glCode");
|
||||||
final EnumOptionData accountType = AccountingEnumerations.gLAccountType(accountTypeId);
|
final boolean disabled = rs.getBoolean("disabled");
|
||||||
final int usageId = JdbcSupport.getInteger(rs, "accountUsage");
|
final boolean manualEntriesAllowed = rs.getBoolean("manualEntriesAllowed");
|
||||||
final EnumOptionData usage = AccountingEnumerations.gLAccountUsage(usageId);
|
final int accountTypeId = JdbcSupport.getInteger(rs, "classification");
|
||||||
final String description = rs.getString("description");
|
final EnumOptionData accountType = AccountingEnumerations.gLAccountType(accountTypeId);
|
||||||
final String nameDecorated = rs.getString("nameDecorated");
|
final int usageId = JdbcSupport.getInteger(rs, "accountUsage");
|
||||||
final Long codeId = rs.wasNull() ? null : rs.getLong("codeId");
|
final EnumOptionData usage = AccountingEnumerations.gLAccountUsage(usageId);
|
||||||
final String codeValue = rs.getString("codeValue");
|
final String description = rs.getString("description");
|
||||||
final CodeValueData tagId = CodeValueData.instance(codeId, codeValue);
|
final String nameDecorated = rs.getString("nameDecorated");
|
||||||
Long organizationRunningBalance = null;
|
final Long codeId = rs.wasNull() ? null : rs.getLong("codeId");
|
||||||
if (associationParametersData.isRunningBalanceRequired()) {
|
final String codeValue = rs.getString("codeValue");
|
||||||
organizationRunningBalance = rs.getLong("organizationRunningBalance");
|
final CodeValueData tagId = CodeValueData.instance(codeId, codeValue);
|
||||||
}
|
Long organizationRunningBalance = null;
|
||||||
return new GLAccountData().setId(id).setName(name).setParentId(parentId).setGlCode(glCode).setDisabled(disabled)
|
if (associationParametersData.isRunningBalanceRequired()) {
|
||||||
.setManualEntriesAllowed(manualEntriesAllowed).setType(accountType).setUsage(usage).setDescription(description)
|
organizationRunningBalance = rs.getLong("organizationRunningBalance");
|
||||||
.setNameDecorated(nameDecorated).setTagId(tagId).setOrganizationRunningBalance(organizationRunningBalance);
|
}
|
||||||
}
|
return new GLAccountData().setId(id).setName(name).setParentId(parentId).setGlCode(glCode).setDisabled(disabled)
|
||||||
}
|
.setManualEntriesAllowed(manualEntriesAllowed).setType(accountType).setUsage(usage).setDescription(description)
|
||||||
|
.setNameDecorated(nameDecorated).setTagId(tagId).setOrganizationRunningBalance(organizationRunningBalance);
|
||||||
@Override
|
}
|
||||||
public List<GLAccountData> retrieveAllGLAccounts(final Integer accountClassification, final String searchParam, final Integer usage,
|
}
|
||||||
final Boolean manualTransactionsAllowed, final Boolean disabled,
|
|
||||||
JournalEntryAssociationParametersData associationParametersData) {
|
@Override
|
||||||
if (accountClassification != null && !checkValidGLAccountType(accountClassification)) {
|
public List<GLAccountData> retrieveAllGLAccounts(final Integer accountClassification, final String searchParam, final Integer usage,
|
||||||
throw new GLAccountInvalidClassificationException(accountClassification);
|
final Boolean manualTransactionsAllowed, final Boolean disabled,
|
||||||
}
|
JournalEntryAssociationParametersData associationParametersData) {
|
||||||
|
if (accountClassification != null && !checkValidGLAccountType(accountClassification)) {
|
||||||
if (usage != null && !checkValidGLAccountUsage(usage)) {
|
throw new GLAccountInvalidClassificationException(accountClassification);
|
||||||
throw new GLAccountInvalidClassificationException(accountClassification);
|
}
|
||||||
}
|
|
||||||
|
if (usage != null && !checkValidGLAccountUsage(usage)) {
|
||||||
final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
|
throw new GLAccountInvalidClassificationException(accountClassification);
|
||||||
String sql = "select " + rm.schema();
|
}
|
||||||
// append SQL statement for fetching account totals
|
|
||||||
if (associationParametersData != null && associationParametersData.isRunningBalanceRequired()) {
|
final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
|
||||||
sql = sql + " and gl_j.id in (select t1.id from (select t2.account_id, max(t2.id) as id from "
|
String sql = "select " + rm.schema();
|
||||||
+ "(select id, max(entry_date) as entry_date, account_id from acc_gl_journal_entry where is_running_balance_calculated = true "
|
// append SQL statement for fetching account totals
|
||||||
+ "group by account_id desc, id) t3 inner join acc_gl_journal_entry t2 on t2.account_id = t3.account_id and t2.entry_date = t3.entry_date "
|
if (associationParametersData != null && associationParametersData.isRunningBalanceRequired()) {
|
||||||
+ "group by t2.account_id desc) t1)";
|
sql = sql + " and gl_j.id in (select t1.id from (select t2.account_id, max(t2.id) as id from "
|
||||||
}
|
+ "(select id, max(entry_date) as entry_date, account_id from acc_gl_journal_entry where is_running_balance_calculated = true "
|
||||||
final Object[] parameterArray = new Object[3];
|
+ "group by account_id desc, id) t3 inner join acc_gl_journal_entry t2 on t2.account_id = t3.account_id and t2.entry_date = t3.entry_date "
|
||||||
int arrayPos = 0;
|
+ "group by t2.account_id desc) t1)";
|
||||||
boolean filtersPresent = false;
|
}
|
||||||
if ((accountClassification != null) || StringUtils.isNotBlank(searchParam) || (usage != null) || (manualTransactionsAllowed != null)
|
final Object[] parameterArray = new Object[3];
|
||||||
|| (disabled != null)) {
|
int arrayPos = 0;
|
||||||
filtersPresent = true;
|
boolean filtersPresent = false;
|
||||||
sql += " where";
|
if ((accountClassification != null) || StringUtils.isNotBlank(searchParam) || (usage != null) || (manualTransactionsAllowed != null)
|
||||||
}
|
|| (disabled != null)) {
|
||||||
|
filtersPresent = true;
|
||||||
if (filtersPresent) {
|
sql += " where";
|
||||||
boolean firstWhereConditionAdded = false;
|
}
|
||||||
if (accountClassification != null) {
|
|
||||||
sql += " classification_enum = ?";
|
if (filtersPresent) {
|
||||||
parameterArray[arrayPos] = accountClassification.shortValue();
|
boolean firstWhereConditionAdded = false;
|
||||||
arrayPos = arrayPos + 1;
|
if (accountClassification != null) {
|
||||||
firstWhereConditionAdded = true;
|
sql += " classification_enum = ?";
|
||||||
}
|
parameterArray[arrayPos] = accountClassification.shortValue();
|
||||||
if (StringUtils.isNotBlank(searchParam)) {
|
arrayPos = arrayPos + 1;
|
||||||
if (firstWhereConditionAdded) {
|
firstWhereConditionAdded = true;
|
||||||
sql += SQL_AND;
|
}
|
||||||
}
|
if (StringUtils.isNotBlank(searchParam)) {
|
||||||
sql += " ( name like %?% or gl_code like %?% )";
|
if (firstWhereConditionAdded) {
|
||||||
parameterArray[arrayPos] = searchParam;
|
sql += SQL_AND;
|
||||||
arrayPos = arrayPos + 1;
|
}
|
||||||
parameterArray[arrayPos] = searchParam;
|
sql += " ( name like ? or gl_code like ? )";
|
||||||
arrayPos = arrayPos + 1;
|
parameterArray[arrayPos] = "%" + searchParam + "%";
|
||||||
firstWhereConditionAdded = true;
|
arrayPos = arrayPos + 1;
|
||||||
}
|
parameterArray[arrayPos] = "%" + searchParam + "%";
|
||||||
if (usage != null) {
|
arrayPos = arrayPos + 1;
|
||||||
if (firstWhereConditionAdded) {
|
firstWhereConditionAdded = true;
|
||||||
sql += SQL_AND;
|
}
|
||||||
}
|
if (usage != null) {
|
||||||
if (GLAccountUsage.HEADER.getValue().equals(usage)) {
|
if (firstWhereConditionAdded) {
|
||||||
sql += " account_usage = 2 ";
|
sql += SQL_AND;
|
||||||
} else if (GLAccountUsage.DETAIL.getValue().equals(usage)) {
|
}
|
||||||
sql += " account_usage = 1 ";
|
if (GLAccountUsage.HEADER.getValue().equals(usage)) {
|
||||||
}
|
sql += " account_usage = 2 ";
|
||||||
firstWhereConditionAdded = true;
|
} else if (GLAccountUsage.DETAIL.getValue().equals(usage)) {
|
||||||
}
|
sql += " account_usage = 1 ";
|
||||||
if (manualTransactionsAllowed != null) {
|
}
|
||||||
if (firstWhereConditionAdded) {
|
firstWhereConditionAdded = true;
|
||||||
sql += SQL_AND;
|
}
|
||||||
}
|
if (manualTransactionsAllowed != null) {
|
||||||
|
if (firstWhereConditionAdded) {
|
||||||
sql += " manual_journal_entries_allowed = " + manualTransactionsAllowed;
|
sql += SQL_AND;
|
||||||
firstWhereConditionAdded = true;
|
}
|
||||||
}
|
|
||||||
if (disabled != null) {
|
sql += " manual_journal_entries_allowed = " + manualTransactionsAllowed;
|
||||||
if (firstWhereConditionAdded) {
|
firstWhereConditionAdded = true;
|
||||||
sql += SQL_AND;
|
}
|
||||||
}
|
if (disabled != null) {
|
||||||
|
if (firstWhereConditionAdded) {
|
||||||
sql += " disabled = " + disabled;
|
sql += SQL_AND;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
sql += " disabled = " + disabled;
|
||||||
sql += " ORDER BY gl_code ASC";
|
}
|
||||||
|
}
|
||||||
final Object[] finalObjectArray = Arrays.copyOf(parameterArray, arrayPos);
|
|
||||||
return this.jdbcTemplate.query(sql, rm, (Object[]) finalObjectArray);// NOSONAR
|
sql += " ORDER BY gl_code ASC";
|
||||||
}
|
|
||||||
|
final Object[] finalObjectArray = Arrays.copyOf(parameterArray, arrayPos);
|
||||||
@Override
|
return this.jdbcTemplate.query(sql, rm, (Object[]) finalObjectArray);// NOSONAR
|
||||||
public GLAccountData retrieveGLAccountById(final long glAccountId, JournalEntryAssociationParametersData associationParametersData) {
|
}
|
||||||
try {
|
|
||||||
final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
|
@Override
|
||||||
final StringBuilder sql = new StringBuilder();
|
public GLAccountData retrieveGLAccountById(final long glAccountId, JournalEntryAssociationParametersData associationParametersData) {
|
||||||
sql.append("select ").append(rm.schema());
|
try {
|
||||||
sql.append(" where gl.id = ?");
|
final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
|
||||||
if (associationParametersData.isRunningBalanceRequired()) {
|
final StringBuilder sql = new StringBuilder();
|
||||||
sql.append(" and gl_j.is_running_balance_calculated = true ")
|
sql.append("select ").append(rm.schema());
|
||||||
.append(" ORDER BY gl_j.entry_date DESC,gl_j.id DESC LIMIT 1");
|
sql.append(" where gl.id = ?");
|
||||||
}
|
|
||||||
|
final GLAccountData glAccount = this.jdbcTemplate.queryForObject(sql.toString(), rm, new Object[] { glAccountId });
|
||||||
return this.jdbcTemplate.queryForObject(sql.toString(), rm, new Object[] { glAccountId });
|
|
||||||
} catch (final EmptyResultDataAccessException e) {
|
// Live balance: sum all non-reversed journal entries so cashier sees real-time balance
|
||||||
throw new GLAccountNotFoundException(glAccountId, e);
|
// without waiting for the "Update Accounting Running Balances" scheduled job.
|
||||||
}
|
if (associationParametersData != null && associationParametersData.isRunningBalanceRequired() && glAccount != null) {
|
||||||
}
|
final String liveBalanceSql = "SELECT COALESCE(ABS(SUM(CASE WHEN type_enum = 2 THEN amount ELSE -amount END)), 0) "
|
||||||
|
+ "FROM acc_gl_journal_entry "
|
||||||
@Override
|
+ "WHERE account_id = ? AND (reversed = false OR reversed IS NULL)";
|
||||||
public List<GLAccountData> retrieveAllEnabledDetailGLAccounts(final GLAccountType accountType) {
|
final java.math.BigDecimal liveBalance = this.jdbcTemplate.queryForObject(liveBalanceSql,
|
||||||
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.DETAIL.getValue(), null, false,
|
java.math.BigDecimal.class, glAccountId);
|
||||||
new JournalEntryAssociationParametersData());
|
glAccount.setOrganizationRunningBalance(liveBalance != null ? liveBalance.longValue() : 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return glAccount;
|
||||||
public List<GLAccountData> retrieveAllEnabledDetailGLAccounts() {
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
return retrieveAllGLAccounts(null, null, GLAccountUsage.DETAIL.getValue(), null, false,
|
throw new GLAccountNotFoundException(glAccountId, e);
|
||||||
new JournalEntryAssociationParametersData());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkValidGLAccountType(final int type) {
|
@Override
|
||||||
for (final GLAccountType accountType : GLAccountType.values()) {
|
public List<GLAccountData> retrieveAllEnabledDetailGLAccounts(final GLAccountType accountType) {
|
||||||
if (accountType.getValue().equals(type)) {
|
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.DETAIL.getValue(), null, false,
|
||||||
return true;
|
new JournalEntryAssociationParametersData());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
@Override
|
||||||
}
|
public List<GLAccountData> retrieveAllEnabledDetailGLAccounts() {
|
||||||
|
return retrieveAllGLAccounts(null, null, GLAccountUsage.DETAIL.getValue(), null, false,
|
||||||
private static boolean checkValidGLAccountUsage(final int type) {
|
new JournalEntryAssociationParametersData());
|
||||||
for (final GLAccountUsage accountUsage : GLAccountUsage.values()) {
|
}
|
||||||
if (accountUsage.getValue().equals(type)) {
|
|
||||||
return true;
|
private static boolean checkValidGLAccountType(final int type) {
|
||||||
}
|
for (final GLAccountType accountType : GLAccountType.values()) {
|
||||||
}
|
if (accountType.getValue().equals(type)) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@Override
|
return false;
|
||||||
public GLAccountData retrieveNewGLAccountDetails(final Integer type) {
|
}
|
||||||
return GLAccountData.sensibleDefaultsForNewGLAccountCreation(type);
|
|
||||||
}
|
private static boolean checkValidGLAccountUsage(final int type) {
|
||||||
|
for (final GLAccountUsage accountUsage : GLAccountUsage.values()) {
|
||||||
@Override
|
if (accountUsage.getValue().equals(type)) {
|
||||||
public List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(final GLAccountType accountType) {
|
return true;
|
||||||
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.HEADER.getValue(), null, false,
|
}
|
||||||
new JournalEntryAssociationParametersData());
|
}
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType) {
|
@Override
|
||||||
final GLAccountDataLookUpMapper mapper = new GLAccountDataLookUpMapper();
|
public GLAccountData retrieveNewGLAccountDetails(final Integer type) {
|
||||||
final String sql = "Select " + GLAccountDataLookUpMapper.LOOKUP_SCHEMA + " where rule.id=? and tags.acc_type_enum=?";
|
return GLAccountData.sensibleDefaultsForNewGLAccountCreation(type);
|
||||||
return this.jdbcTemplate.query(sql, mapper, (Object[]) new Object[] { ruleId, transactionType });// NOSONAR
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
private static final class GLAccountDataLookUpMapper implements RowMapper<GLAccountDataForLookup> {
|
public List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(final GLAccountType accountType) {
|
||||||
|
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.HEADER.getValue(), null, false,
|
||||||
private static final String LOOKUP_SCHEMA = " gl.id as id, gl.name as name, gl.gl_code as glCode from acc_accounting_rule rule join acc_rule_tags tags on tags.acc_rule_id = rule.id join acc_gl_account gl on gl.tag_id=tags.tag_id";
|
new JournalEntryAssociationParametersData());
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public GLAccountDataForLookup mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
@Override
|
||||||
final Long id = JdbcSupport.getLong(rs, "id");
|
public List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType) {
|
||||||
final String name = rs.getString("name");
|
final GLAccountDataLookUpMapper mapper = new GLAccountDataLookUpMapper();
|
||||||
final String glCode = rs.getString("glCode");
|
final String sql = "Select " + GLAccountDataLookUpMapper.LOOKUP_SCHEMA + " where rule.id=? and tags.acc_type_enum=?";
|
||||||
|
return this.jdbcTemplate.query(sql, mapper, (Object[]) new Object[] { ruleId, transactionType });// NOSONAR
|
||||||
return new GLAccountDataForLookup().setId(id).setName(name).setGlCode(glCode);
|
}
|
||||||
}
|
|
||||||
}
|
private static final class GLAccountDataLookUpMapper implements RowMapper<GLAccountDataForLookup> {
|
||||||
}
|
|
||||||
|
private static final String LOOKUP_SCHEMA = " gl.id as id, gl.name as name, gl.gl_code as glCode from acc_accounting_rule rule join acc_rule_tags tags on tags.acc_rule_id = rule.id join acc_gl_account gl on gl.tag_id=tags.tag_id";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GLAccountDataForLookup mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
|
final Long id = JdbcSupport.getLong(rs, "id");
|
||||||
|
final String name = rs.getString("name");
|
||||||
|
final String glCode = rs.getString("glCode");
|
||||||
|
|
||||||
|
return new GLAccountDataForLookup().setId(id).setName(name).setGlCode(glCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+11
@@ -76,6 +76,9 @@ public class Teller extends AbstractPersistableCustom<Long> {
|
|||||||
@Column(name = "state", nullable = false)
|
@Column(name = "state", nullable = false)
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
@Column(name = "is_deleted", nullable = false)
|
||||||
|
private boolean deleted = false;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "teller", fetch = FetchType.LAZY)
|
@OneToMany(mappedBy = "teller", fetch = FetchType.LAZY)
|
||||||
private Set<Cashier> cashiers;
|
private Set<Cashier> cashiers;
|
||||||
|
|
||||||
@@ -161,4 +164,12 @@ public class Teller extends AbstractPersistableCustom<Long> {
|
|||||||
this.office.getId();
|
this.office.getId();
|
||||||
this.cashiers.size();
|
this.cashiers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
this.deleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return this.deleted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -31,4 +31,6 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
|||||||
public interface TellerRepository extends JpaRepository<Teller, Long>, JpaSpecificationExecutor<Teller> {
|
public interface TellerRepository extends JpaRepository<Teller, Long>, JpaSpecificationExecutor<Teller> {
|
||||||
|
|
||||||
Collection<Teller> findTellerByOfficeId(Long officeId);
|
Collection<Teller> findTellerByOfficeId(Long officeId);
|
||||||
|
|
||||||
|
long countByDeletedFalse();
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -36,6 +36,9 @@ public class TellerRepositoryWrapper {
|
|||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Teller findOneWithNotFoundDetection(final Long id) {
|
public Teller findOneWithNotFoundDetection(final Long id) {
|
||||||
final Teller teller = this.repository.findById(id).orElseThrow(() -> new TellerNotFoundException(id));
|
final Teller teller = this.repository.findById(id).orElseThrow(() -> new TellerNotFoundException(id));
|
||||||
|
if (teller.isDeleted()) {
|
||||||
|
throw new TellerNotFoundException(id);
|
||||||
|
}
|
||||||
teller.initializeLazyCollections();
|
teller.initializeLazyCollections();
|
||||||
return teller;
|
return teller;
|
||||||
}
|
}
|
||||||
@@ -51,4 +54,8 @@ public class TellerRepositoryWrapper {
|
|||||||
public void delete(final Teller teller) {
|
public void delete(final Teller teller) {
|
||||||
this.repository.delete(teller);
|
this.repository.delete(teller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean existsAny() {
|
||||||
|
return this.repository.countByDeletedFalse() > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.organisation.teller.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class TellerAlreadyExistsException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
private static final String ERROR_MESSAGE_CODE = "error.msg.teller.already.exists";
|
||||||
|
private static final String DEFAULT_ERROR_MESSAGE = "Cannot create teller as a teller is already present.";
|
||||||
|
|
||||||
|
public TellerAlreadyExistsException() {
|
||||||
|
super(ERROR_MESSAGE_CODE, DEFAULT_ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-1
@@ -34,7 +34,8 @@ public enum CommandProcessingResultType {
|
|||||||
AWAITING_APPROVAL(2, "commandProcessingResultType.awaiting.approval"), //
|
AWAITING_APPROVAL(2, "commandProcessingResultType.awaiting.approval"), //
|
||||||
REJECTED(3, "commandProcessingResultType.rejected"), //
|
REJECTED(3, "commandProcessingResultType.rejected"), //
|
||||||
UNDER_PROCESSING(4, "commandProcessingResultType.underProcessing"), //
|
UNDER_PROCESSING(4, "commandProcessingResultType.underProcessing"), //
|
||||||
ERROR(5, "commandProcessingResultType.error");
|
ERROR(5, "commandProcessingResultType.error"), //
|
||||||
|
TERMINATE(6, "commandProcessingResultType.terminate");
|
||||||
|
|
||||||
private static final Map<Integer, CommandProcessingResultType> BY_ID = Arrays.stream(values())
|
private static final Map<Integer, CommandProcessingResultType> BY_ID = Arrays.stream(values())
|
||||||
.collect(Collectors.toMap(CommandProcessingResultType::getValue, v -> v));
|
.collect(Collectors.toMap(CommandProcessingResultType::getValue, v -> v));
|
||||||
@@ -59,4 +60,8 @@ public enum CommandProcessingResultType {
|
|||||||
public boolean isRejected() {
|
public boolean isRejected() {
|
||||||
return this == REJECTED;
|
return this == REJECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTerminate() {
|
||||||
|
return this == TERMINATE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+169
-1
@@ -71,6 +71,166 @@ public class CommandWrapperBuilder {
|
|||||||
this.sanitizeJsonKeys);
|
this.sanitizeJsonKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createChequeSeries() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "CHEQUESERIES";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/chequeseries";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder updateChequeSeries(final Long seriesId) {
|
||||||
|
this.actionName = "UPDATE";
|
||||||
|
this.entityName = "CHEQUESERIES";
|
||||||
|
this.entityId = seriesId;
|
||||||
|
this.href = "/chequeseries/" + seriesId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder deleteChequeSeries(final Long seriesId) {
|
||||||
|
this.actionName = "DELETE";
|
||||||
|
this.entityName = "CHEQUESERIES";
|
||||||
|
this.entityId = seriesId;
|
||||||
|
this.href = "/chequeseries/" + seriesId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createChequeStockLot() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "CHEQUESTOCKLOT";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/chequestocklots";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder reportLostChequeStockLot(final Long lotId) {
|
||||||
|
this.actionName = "REPORTLOST";
|
||||||
|
this.entityName = "CHEQUESTOCKLOT";
|
||||||
|
this.entityId = lotId;
|
||||||
|
this.href = "/chequestocklots/" + lotId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createChequeStockTransfer() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "CHEQUESTOCKTRANSFER";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/chequestocktransfers";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder receiveChequeStockTransfer(final Long transferId) {
|
||||||
|
this.actionName = "RECEIVE";
|
||||||
|
this.entityName = "CHEQUESTOCKTRANSFER";
|
||||||
|
this.entityId = transferId;
|
||||||
|
this.href = "/chequestocktransfers/" + transferId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder rejectChequeStockTransfer(final Long transferId) {
|
||||||
|
this.actionName = "REJECT";
|
||||||
|
this.entityName = "CHEQUESTOCKTRANSFER";
|
||||||
|
this.entityId = transferId;
|
||||||
|
this.href = "/chequestocktransfers/" + transferId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createChequeBook() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/chequebooks";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder issueChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "ISSUE";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder rejectChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "REJECT";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder withdrawChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "WITHDRAW";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder stopPaymentChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "STOPPAYMENT";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder revokeStopPaymentChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "REVOKESTOPPAYMENT";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder reportLostChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "REPORTLOST";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder cancelLeafChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "CANCELLEAF";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder markUsedChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "MARKUSED";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder returnChequeBook(final Long chequeBookId) {
|
||||||
|
this.actionName = "RETURN";
|
||||||
|
this.entityName = "CHEQUEBOOK";
|
||||||
|
this.entityId = chequeBookId;
|
||||||
|
this.href = "/chequebooks/" + chequeBookId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder approveChequeBookPendingRequest(final Long pendingRequestId) {
|
||||||
|
this.actionName = "APPROVE";
|
||||||
|
this.entityName = "CHEQUEBOOKPENDINGREQUEST";
|
||||||
|
this.entityId = pendingRequestId;
|
||||||
|
this.href = "/chequebook/pending-requests/" + pendingRequestId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder rejectChequeBookPendingRequest(final Long pendingRequestId) {
|
||||||
|
this.actionName = "REJECT";
|
||||||
|
this.entityName = "CHEQUEBOOKPENDINGREQUEST";
|
||||||
|
this.entityId = pendingRequestId;
|
||||||
|
this.href = "/chequebook/pending-requests/" + pendingRequestId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder updateCreditBureau() {
|
public CommandWrapperBuilder updateCreditBureau() {
|
||||||
this.actionName = "UPDATE";
|
this.actionName = "UPDATE";
|
||||||
this.entityName = "ORGANISATIONCREDITBUREAU";
|
this.entityName = "ORGANISATIONCREDITBUREAU";
|
||||||
@@ -1611,6 +1771,14 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder terminateAccount(final Long accountId) {
|
||||||
|
this.actionName = "TERMINATE";
|
||||||
|
this.entityName = "ACCOUNT";
|
||||||
|
this.entityId = accountId;
|
||||||
|
this.href = "/accounts/" + accountId + "/terminate";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder approveGSIMAccountApplication(final Long accountId) {
|
public CommandWrapperBuilder approveGSIMAccountApplication(final Long accountId) {
|
||||||
this.actionName = "APPROVE";
|
this.actionName = "APPROVE";
|
||||||
this.entityName = "GSIMACCOUNT";
|
this.entityName = "GSIMACCOUNT";
|
||||||
@@ -3565,7 +3733,7 @@ public class CommandWrapperBuilder {
|
|||||||
this.actionName = "ACTIVATE";
|
this.actionName = "ACTIVATE";
|
||||||
this.entityName = "CURRENTACCOUNT";
|
this.entityName = "CURRENTACCOUNT";
|
||||||
this.savingsId = accountId;
|
this.savingsId = accountId;
|
||||||
this.entityId = null;
|
this.entityId = accountId;
|
||||||
this.href = "/currentaccounts/" + accountId + "?command=activate";
|
this.href = "/currentaccounts/" + accountId + "?command=activate";
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -20,6 +20,7 @@ package org.apache.fineract.commands.service;
|
|||||||
|
|
||||||
import static org.apache.fineract.commands.domain.CommandProcessingResultType.ERROR;
|
import static org.apache.fineract.commands.domain.CommandProcessingResultType.ERROR;
|
||||||
import static org.apache.fineract.commands.domain.CommandProcessingResultType.PROCESSED;
|
import static org.apache.fineract.commands.domain.CommandProcessingResultType.PROCESSED;
|
||||||
|
import static org.apache.fineract.commands.domain.CommandProcessingResultType.TERMINATE;
|
||||||
import static org.apache.http.HttpStatus.SC_OK;
|
import static org.apache.http.HttpStatus.SC_OK;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@@ -181,7 +182,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
|||||||
currentSource.setResultStatusCode(SC_OK);
|
currentSource.setResultStatusCode(SC_OK);
|
||||||
currentSource.updateForAudit(result);
|
currentSource.updateForAudit(result);
|
||||||
currentSource.setResult(toApiResultJsonSerializer.serializeResult(result));
|
currentSource.setResult(toApiResultJsonSerializer.serializeResult(result));
|
||||||
currentSource.setStatus(PROCESSED);
|
currentSource.setStatus("TERMINATE".equals(wrapper.getActionName()) ? TERMINATE : PROCESSED);
|
||||||
|
|
||||||
// Return saved command source
|
// Return saved command source
|
||||||
return commandSourceService.saveResultSameTransaction(currentSource);
|
return commandSourceService.saveResultSameTransaction(currentSource);
|
||||||
|
|||||||
+150
-116
@@ -1,116 +1,150 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.infrastructure.core.serialization;
|
package org.apache.fineract.infrastructure.core.serialization;
|
||||||
|
|
||||||
import com.google.gson.ExclusionStrategy;
|
import com.google.gson.ExclusionStrategy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import java.time.LocalDate;
|
import com.google.gson.TypeAdapter;
|
||||||
import java.time.LocalDateTime;
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import java.time.LocalTime;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import java.time.MonthDay;
|
import com.google.gson.stream.JsonReader;
|
||||||
import java.time.OffsetDateTime;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import java.time.ZonedDateTime;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.time.LocalDate;
|
||||||
import java.util.HashSet;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Set;
|
import java.time.LocalTime;
|
||||||
import org.apache.fineract.infrastructure.core.api.DateAdapter;
|
import java.time.MonthDay;
|
||||||
import org.apache.fineract.infrastructure.core.api.ExternalIdAdapter;
|
import java.time.OffsetDateTime;
|
||||||
import org.apache.fineract.infrastructure.core.api.JodaDateTimeAdapter;
|
import java.time.ZonedDateTime;
|
||||||
import org.apache.fineract.infrastructure.core.api.JodaMonthDayAdapter;
|
import java.util.ArrayList;
|
||||||
import org.apache.fineract.infrastructure.core.api.LocalDateAdapter;
|
import java.util.HashSet;
|
||||||
import org.apache.fineract.infrastructure.core.api.LocalDateTimeAdapter;
|
import java.util.Set;
|
||||||
import org.apache.fineract.infrastructure.core.api.LocalTimeAdapter;
|
import org.apache.fineract.infrastructure.core.api.DateAdapter;
|
||||||
import org.apache.fineract.infrastructure.core.api.OffsetDateTimeAdapter;
|
import org.apache.fineract.infrastructure.core.api.ExternalIdAdapter;
|
||||||
import org.apache.fineract.infrastructure.core.api.ParameterListExclusionStrategy;
|
import org.apache.fineract.infrastructure.core.api.JodaDateTimeAdapter;
|
||||||
import org.apache.fineract.infrastructure.core.api.ParameterListInclusionStrategy;
|
import org.apache.fineract.infrastructure.core.api.JodaMonthDayAdapter;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.api.LocalDateAdapter;
|
||||||
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
|
import org.apache.fineract.infrastructure.core.api.LocalDateTimeAdapter;
|
||||||
import org.springframework.stereotype.Service;
|
import org.apache.fineract.infrastructure.core.api.LocalTimeAdapter;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.OffsetDateTimeAdapter;
|
||||||
/**
|
import org.apache.fineract.infrastructure.core.api.ParameterListExclusionStrategy;
|
||||||
* Helper class for serialization of Java objects into JSON using Google's GSON.
|
import org.apache.fineract.infrastructure.core.api.ParameterListInclusionStrategy;
|
||||||
*/
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
@Service
|
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
|
||||||
public final class GoogleGsonSerializerHelper {
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
public Gson createGsonBuilderForPartialResponseFiltering(final Set<String> responseParameters) {
|
/**
|
||||||
final ExclusionStrategy strategy = new ParameterListInclusionStrategy(responseParameters);
|
* Helper class for serialization of Java objects into JSON using Google's GSON.
|
||||||
|
*/
|
||||||
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
|
@Service
|
||||||
registerTypeAdapters(builder);
|
public final class GoogleGsonSerializerHelper {
|
||||||
return builder.create();
|
|
||||||
}
|
public Gson createGsonBuilderForPartialResponseFiltering(final Set<String> responseParameters) {
|
||||||
|
final ExclusionStrategy strategy = new ParameterListInclusionStrategy(responseParameters);
|
||||||
public Gson createGsonBuilderWithParameterExclusionSerializationStrategy(final Set<String> supportedParameters,
|
|
||||||
final Set<String> responseParameters) {
|
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
|
||||||
|
registerTypeAdapters(builder);
|
||||||
final Set<String> parameterNamesToSkip = new HashSet<>();
|
return builder.create();
|
||||||
|
}
|
||||||
if (!responseParameters.isEmpty()) {
|
|
||||||
// strip out all known support parameters from expected response to
|
public Gson createGsonBuilderWithParameterExclusionSerializationStrategy(final Set<String> supportedParameters,
|
||||||
// see if unsupported parameters requested for response.
|
final Set<String> responseParameters) {
|
||||||
final Set<String> differentParametersDetectedSet = new HashSet<>(responseParameters);
|
|
||||||
differentParametersDetectedSet.removeAll(supportedParameters);
|
final Set<String> parameterNamesToSkip = new HashSet<>();
|
||||||
|
|
||||||
if (!differentParametersDetectedSet.isEmpty()) {
|
if (!responseParameters.isEmpty()) {
|
||||||
throw new UnsupportedParameterException(new ArrayList<>(differentParametersDetectedSet));
|
// strip out all known support parameters from expected response to
|
||||||
}
|
// see if unsupported parameters requested for response.
|
||||||
|
final Set<String> differentParametersDetectedSet = new HashSet<>(responseParameters);
|
||||||
parameterNamesToSkip.addAll(supportedParameters);
|
differentParametersDetectedSet.removeAll(supportedParameters);
|
||||||
parameterNamesToSkip.removeAll(responseParameters);
|
|
||||||
}
|
if (!differentParametersDetectedSet.isEmpty()) {
|
||||||
|
throw new UnsupportedParameterException(new ArrayList<>(differentParametersDetectedSet));
|
||||||
final ExclusionStrategy strategy = new ParameterListExclusionStrategy(parameterNamesToSkip);
|
}
|
||||||
|
|
||||||
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
|
parameterNamesToSkip.addAll(supportedParameters);
|
||||||
registerTypeAdapters(builder);
|
parameterNamesToSkip.removeAll(responseParameters);
|
||||||
return builder.create();
|
}
|
||||||
}
|
|
||||||
|
final ExclusionStrategy strategy = new ParameterListExclusionStrategy(parameterNamesToSkip);
|
||||||
public String serializedJsonFrom(final Gson serializer, final Object[] dataObjects) {
|
|
||||||
return serializer.toJson(dataObjects);
|
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
|
||||||
}
|
registerTypeAdapters(builder);
|
||||||
|
return builder.create();
|
||||||
public String serializedJsonFrom(final Gson serializer, final Object singleDataObject) {
|
}
|
||||||
return serializer.toJson(singleDataObject);
|
|
||||||
}
|
public String serializedJsonFrom(final Gson serializer, final Object[] dataObjects) {
|
||||||
|
return serializer.toJson(dataObjects);
|
||||||
public static Gson createSimpleGson() {
|
}
|
||||||
return createGsonBuilder().create();
|
|
||||||
}
|
public String serializedJsonFrom(final Gson serializer, final Object singleDataObject) {
|
||||||
|
return serializer.toJson(singleDataObject);
|
||||||
public static GsonBuilder createGsonBuilder() {
|
}
|
||||||
final GsonBuilder builder = new GsonBuilder();
|
|
||||||
registerTypeAdapters(builder);
|
public static Gson createSimpleGson() {
|
||||||
return builder;
|
return createGsonBuilder().create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerTypeAdapters(final GsonBuilder builder) {
|
public static GsonBuilder createGsonBuilder() {
|
||||||
builder.registerTypeAdapter(java.util.Date.class, new DateAdapter());
|
final GsonBuilder builder = new GsonBuilder();
|
||||||
builder.registerTypeAdapter(LocalDate.class, new LocalDateAdapter());
|
registerTypeAdapters(builder);
|
||||||
// NOTE: was missing, necessary for GSON serialization with JDK 21's restrictive module access
|
return builder;
|
||||||
builder.registerTypeAdapter(LocalTime.class, new LocalTimeAdapter());
|
}
|
||||||
builder.registerTypeAdapter(ZonedDateTime.class, new JodaDateTimeAdapter());
|
|
||||||
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
|
public static void registerTypeAdapters(final GsonBuilder builder) {
|
||||||
builder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter());
|
builder.registerTypeAdapter(java.util.Date.class, new DateAdapter());
|
||||||
builder.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter());
|
builder.registerTypeAdapter(LocalDate.class, new LocalDateAdapter());
|
||||||
builder.registerTypeAdapter(ExternalId.class, new ExternalIdAdapter());
|
// NOTE: was missing, necessary for GSON serialization with JDK 21's restrictive module access
|
||||||
}
|
builder.registerTypeAdapter(LocalTime.class, new LocalTimeAdapter());
|
||||||
}
|
builder.registerTypeAdapter(ZonedDateTime.class, new JodaDateTimeAdapter());
|
||||||
|
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
|
||||||
|
builder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter());
|
||||||
|
builder.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter());
|
||||||
|
builder.registerTypeAdapter(ExternalId.class, new ExternalIdAdapter());
|
||||||
|
// Use runtime type when serializing interface-typed fields (e.g. Collection<? extends IGuarantor>)
|
||||||
|
builder.registerTypeAdapterFactory(new TypeAdapterFactory() {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
|
||||||
|
if (!type.getRawType().isInterface()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final TypeAdapterFactory self = this;
|
||||||
|
return new TypeAdapter<T>() {
|
||||||
|
@Override
|
||||||
|
public void write(final JsonWriter out, final T value) throws IOException {
|
||||||
|
if (value == null) {
|
||||||
|
out.nullValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TypeAdapter<T> delegate = (TypeAdapter<T>) gson.getDelegateAdapter(self,
|
||||||
|
TypeToken.get((Class<T>) value.getClass()));
|
||||||
|
delegate.write(out, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(final JsonReader in) throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+3
@@ -61,6 +61,9 @@ public enum JobName {
|
|||||||
ADD_PERIODIC_ACCRUAL_ENTRIES_FOR_SAVINGS_WITH_INCOME_POSTED_AS_TRANSACTIONS("Add Accrual Transactions For Savings"), //
|
ADD_PERIODIC_ACCRUAL_ENTRIES_FOR_SAVINGS_WITH_INCOME_POSTED_AS_TRANSACTIONS("Add Accrual Transactions For Savings"), //
|
||||||
JOURNAL_ENTRY_AGGREGATION("Journal Entry Aggregation"), //
|
JOURNAL_ENTRY_AGGREGATION("Journal Entry Aggregation"), //
|
||||||
FETCH_RBI_RSS_FEEDS("Fetch RBI RSS Feeds"), //
|
FETCH_RBI_RSS_FEEDS("Fetch RBI RSS Feeds"), //
|
||||||
|
IMPS_DEEMED_RECONCILE("IMPS DEEMED Reconciliation"), //
|
||||||
|
NEFT_BATCH_PROCESS("NEFT Batch Process"), //
|
||||||
|
VERIFICATION_TOKEN_CLEANUP("Verification Token Cleanup"), //
|
||||||
; //
|
; //
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|||||||
+79
-71
@@ -1,71 +1,79 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account;
|
package org.apache.fineract.portfolio.account;
|
||||||
|
|
||||||
public enum PortfolioAccountType {
|
public enum PortfolioAccountType {
|
||||||
|
|
||||||
INVALID(0, "accountType.invalid"), //
|
INVALID(0, "accountType.invalid"), //
|
||||||
LOAN(1, "accountType.loan"), //
|
LOAN(1, "accountType.loan"), //
|
||||||
SAVINGS(2, "accountType.savings"), //
|
SAVINGS(2, "accountType.savings"), //
|
||||||
;
|
CURRENT(3, "accountType.current"), //
|
||||||
|
;
|
||||||
private final Integer value;
|
|
||||||
private final String code;
|
private final Integer value;
|
||||||
|
private final String code;
|
||||||
PortfolioAccountType(final Integer value, final String code) {
|
|
||||||
this.value = value;
|
PortfolioAccountType(final Integer value, final String code) {
|
||||||
this.code = code;
|
this.value = value;
|
||||||
}
|
this.code = code;
|
||||||
|
}
|
||||||
public Integer getValue() {
|
|
||||||
return this.value;
|
public Integer getValue() {
|
||||||
}
|
return this.value;
|
||||||
|
}
|
||||||
public String getCode() {
|
|
||||||
return this.code;
|
public String getCode() {
|
||||||
}
|
return this.code;
|
||||||
|
}
|
||||||
public static PortfolioAccountType fromInt(final Integer type) {
|
|
||||||
|
public static PortfolioAccountType fromInt(final Integer type) {
|
||||||
PortfolioAccountType enumType = INVALID;
|
|
||||||
if (type != null) {
|
PortfolioAccountType enumType = INVALID;
|
||||||
switch (type) {
|
if (type != null) {
|
||||||
case 1:
|
switch (type) {
|
||||||
enumType = LOAN;
|
case 1:
|
||||||
break;
|
enumType = LOAN;
|
||||||
case 2:
|
break;
|
||||||
enumType = SAVINGS;
|
case 2:
|
||||||
break;
|
enumType = SAVINGS;
|
||||||
default:
|
break;
|
||||||
enumType = INVALID;
|
case 3:
|
||||||
}
|
enumType = CURRENT;
|
||||||
}
|
break;
|
||||||
return enumType;
|
default:
|
||||||
}
|
enumType = INVALID;
|
||||||
|
}
|
||||||
// TODO: bad practice and unnecessary code! why not just use the enum values themselves!?!
|
}
|
||||||
public boolean isSavingsAccount() {
|
return enumType;
|
||||||
return this.equals(SAVINGS);
|
}
|
||||||
}
|
|
||||||
|
// TODO: bad practice and unnecessary code! why not just use the enum values themselves!?!
|
||||||
// TODO: bad practice and unnecessary code! why not just use the enum values themselves!?!
|
public boolean isSavingsAccount() {
|
||||||
public boolean isLoanAccount() {
|
return this.equals(SAVINGS);
|
||||||
return this.equals(LOAN);
|
}
|
||||||
}
|
|
||||||
}
|
// TODO: bad practice and unnecessary code! why not just use the enum values themselves!?!
|
||||||
|
public boolean isLoanAccount() {
|
||||||
|
return this.equals(LOAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCurrentAccount() {
|
||||||
|
return this.equals(CURRENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+6
-1
@@ -224,7 +224,7 @@ public final class DepositsApiConstants {
|
|||||||
* Deposit Product Parameters
|
* Deposit Product Parameters
|
||||||
*/
|
*/
|
||||||
private static final Set<String> DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
|
private static final Set<String> DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
|
||||||
monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName,
|
dateFormatParamName, monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName,
|
||||||
digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName,
|
digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName,
|
||||||
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
|
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
|
||||||
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, accountingRuleParamName, chargesParamName,
|
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, accountingRuleParamName, chargesParamName,
|
||||||
@@ -512,6 +512,11 @@ public final class DepositsApiConstants {
|
|||||||
fixedDepositInterestCalculationData.add(tenureInMonthsParamName);
|
fixedDepositInterestCalculationData.add(tenureInMonthsParamName);
|
||||||
fixedDepositInterestCalculationData.add(interestPostingPeriodInMonthsParamName);
|
fixedDepositInterestCalculationData.add(interestPostingPeriodInMonthsParamName);
|
||||||
fixedDepositInterestCalculationData.add(interestCompoundingPeriodInMonthsParamName);
|
fixedDepositInterestCalculationData.add(interestCompoundingPeriodInMonthsParamName);
|
||||||
|
// Exact calendar-based calculation params
|
||||||
|
fixedDepositInterestCalculationData.add("submittedOnDate");
|
||||||
|
fixedDepositInterestCalculationData.add("dateFormat");
|
||||||
|
fixedDepositInterestCalculationData.add("locale");
|
||||||
|
fixedDepositInterestCalculationData.add("daysInYear");
|
||||||
|
|
||||||
return fixedDepositInterestCalculationData;
|
return fixedDepositInterestCalculationData;
|
||||||
|
|
||||||
|
|||||||
+1
@@ -170,6 +170,7 @@ public final class SavingsAccountData implements Serializable {
|
|||||||
private Boolean balanceSwapOption;
|
private Boolean balanceSwapOption;
|
||||||
private Boolean msme;
|
private Boolean msme;
|
||||||
private Boolean autoDebitFacility;
|
private Boolean autoDebitFacility;
|
||||||
|
private Boolean terminateTrue;
|
||||||
private String avgBalanceCalcPeriod;
|
private String avgBalanceCalcPeriod;
|
||||||
private String validForPeriod;
|
private String validForPeriod;
|
||||||
private Boolean odAccountClosedZeroBalanceInDayClosing;
|
private Boolean odAccountClosedZeroBalanceInDayClosing;
|
||||||
|
|||||||
+720
-714
File diff suppressed because it is too large
Load Diff
+49
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* 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.savings.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-side view of a single pending Savings Product edit row (m_savings_product_temp), for the Approvals ->
|
||||||
|
* Pending Savings Product Edits screen.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SavingsProductTempData implements Serializable {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long savingsProductId;
|
||||||
|
private String savingsProductName;
|
||||||
|
private Long makerId;
|
||||||
|
private String makerUsername;
|
||||||
|
private Long checkerId;
|
||||||
|
private String checkerUsername;
|
||||||
|
private LocalDateTime createdOn;
|
||||||
|
private LocalDateTime checkedOn;
|
||||||
|
private String status;
|
||||||
|
private String rejectReason;
|
||||||
|
private Map<String, Object> proposedChanges;
|
||||||
|
|
||||||
|
public SavingsProductTempData() {}
|
||||||
|
}
|
||||||
+7
-1
@@ -36,7 +36,8 @@ public enum SavingsAccountStatusType {
|
|||||||
REJECTED(500, "savingsAccountStatusType.rejected"), //
|
REJECTED(500, "savingsAccountStatusType.rejected"), //
|
||||||
CLOSED(600, "savingsAccountStatusType.closed"), //
|
CLOSED(600, "savingsAccountStatusType.closed"), //
|
||||||
PRE_MATURE_CLOSURE(700, "savingsAccountStatusType.pre.mature.closure"), //
|
PRE_MATURE_CLOSURE(700, "savingsAccountStatusType.pre.mature.closure"), //
|
||||||
MATURED(800, "savingsAccountStatusType.matured"); //
|
MATURED(800, "savingsAccountStatusType.matured"), //
|
||||||
|
TERMINATED(900, "savingsAccountStatusType.terminated"); //
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
private final String code;
|
private final String code;
|
||||||
@@ -53,6 +54,7 @@ public enum SavingsAccountStatusType {
|
|||||||
case 600 -> SavingsAccountStatusType.CLOSED;
|
case 600 -> SavingsAccountStatusType.CLOSED;
|
||||||
case 700 -> SavingsAccountStatusType.PRE_MATURE_CLOSURE;
|
case 700 -> SavingsAccountStatusType.PRE_MATURE_CLOSURE;
|
||||||
case 800 -> SavingsAccountStatusType.MATURED;
|
case 800 -> SavingsAccountStatusType.MATURED;
|
||||||
|
case 900 -> SavingsAccountStatusType.TERMINATED;
|
||||||
default -> SavingsAccountStatusType.INVALID;
|
default -> SavingsAccountStatusType.INVALID;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -113,4 +115,8 @@ public enum SavingsAccountStatusType {
|
|||||||
public boolean isPreMatureClosure() {
|
public boolean isPreMatureClosure() {
|
||||||
return this.value.equals(SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue());
|
return this.value.equals(SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTerminated() {
|
||||||
|
return this.value.equals(SavingsAccountStatusType.TERMINATED.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -285,6 +285,11 @@ public final class SavingsEnumerations {
|
|||||||
SavingsAccountStatusType.MATURED.getCode(), "Matured", submittedAndPendingApproval, isApproved, isRejected,
|
SavingsAccountStatusType.MATURED.getCode(), "Matured", submittedAndPendingApproval, isApproved, isRejected,
|
||||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||||
break;
|
break;
|
||||||
|
case TERMINATED:
|
||||||
|
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TERMINATED.getValue().longValue(),
|
||||||
|
SavingsAccountStatusType.TERMINATED.getCode(), "Terminated", submittedAndPendingApproval, isApproved, isRejected,
|
||||||
|
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return optionData;
|
return optionData;
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-5
@@ -39,6 +39,7 @@ import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
|||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.organisation.staff.domain.Staff;
|
import org.apache.fineract.organisation.staff.domain.Staff;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "m_current_account", uniqueConstraints = {
|
@Table(name = "m_current_account", uniqueConstraints = {
|
||||||
@@ -102,6 +103,30 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
|||||||
@Column(name = "closedon_date")
|
@Column(name = "closedon_date")
|
||||||
private LocalDate closedOnDate;
|
private LocalDate closedOnDate;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "submittedon_userid")
|
||||||
|
private AppUser submittedOnByUser;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "approvedon_userid")
|
||||||
|
private AppUser approvedOnByUser;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "activatedon_userid")
|
||||||
|
private AppUser activatedOnByUser;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "rejectedon_userid")
|
||||||
|
private AppUser rejectedOnByUser;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "withdrawnon_userid")
|
||||||
|
private AppUser withdrawnOnByUser;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "closedon_userid")
|
||||||
|
private AppUser closedOnByUser;
|
||||||
|
|
||||||
// Currency (copied from product at account creation)
|
// Currency (copied from product at account creation)
|
||||||
@Column(name = "currency_code", nullable = false, length = 3)
|
@Column(name = "currency_code", nullable = false, length = 3)
|
||||||
private String currencyCode;
|
private String currencyCode;
|
||||||
@@ -304,6 +329,12 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
|||||||
@Column(name = "od_account_closed_zero_balance_in_day_closing")
|
@Column(name = "od_account_closed_zero_balance_in_day_closing")
|
||||||
private boolean odAccountClosedZeroBalanceInDayClosing = false;
|
private boolean odAccountClosedZeroBalanceInDayClosing = false;
|
||||||
|
|
||||||
|
@Column(name = "is_linking_enable", nullable = false)
|
||||||
|
private boolean shareLinkingEnabled = false;
|
||||||
|
|
||||||
|
@Column(name = "terminate_true", nullable = false, columnDefinition = "boolean default false")
|
||||||
|
private boolean terminateTrue = false;
|
||||||
|
|
||||||
|
|
||||||
// Relationships
|
// Relationships
|
||||||
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
@OneToMany(mappedBy = "currentAccount", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
@@ -373,29 +404,34 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
|||||||
|
|
||||||
public void updateAccountNo(String accountNo) { this.accountNo = accountNo; }
|
public void updateAccountNo(String accountNo) { this.accountNo = accountNo; }
|
||||||
|
|
||||||
public void approve(LocalDate approvedOnDate) {
|
public void approve(LocalDate approvedOnDate, AppUser approvedOnByUser) {
|
||||||
this.statusEnum = 200;
|
this.statusEnum = 200;
|
||||||
this.approvedOnDate = approvedOnDate;
|
this.approvedOnDate = approvedOnDate;
|
||||||
|
this.approvedOnByUser = approvedOnByUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void activate(LocalDate activatedOnDate) {
|
public void activate(LocalDate activatedOnDate, AppUser activatedOnByUser) {
|
||||||
this.statusEnum = 300;
|
this.statusEnum = 300;
|
||||||
this.activatedOnDate = activatedOnDate;
|
this.activatedOnDate = activatedOnDate;
|
||||||
|
this.activatedOnByUser = activatedOnByUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reject(LocalDate rejectedOnDate) {
|
public void reject(LocalDate rejectedOnDate, AppUser rejectedOnByUser) {
|
||||||
this.statusEnum = 500;
|
this.statusEnum = 500;
|
||||||
this.rejectedOnDate = rejectedOnDate;
|
this.rejectedOnDate = rejectedOnDate;
|
||||||
|
this.rejectedOnByUser = rejectedOnByUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void withdraw(LocalDate withdrawnOnDate) {
|
public void withdraw(LocalDate withdrawnOnDate, AppUser withdrawnOnByUser) {
|
||||||
this.statusEnum = 400;
|
this.statusEnum = 400;
|
||||||
this.withdrawnOnDate = withdrawnOnDate;
|
this.withdrawnOnDate = withdrawnOnDate;
|
||||||
|
this.withdrawnOnByUser = withdrawnOnByUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close(LocalDate closedOnDate) {
|
public void close(LocalDate closedOnDate, AppUser closedOnByUser) {
|
||||||
this.statusEnum = 600;
|
this.statusEnum = 600;
|
||||||
this.closedOnDate = closedOnDate;
|
this.closedOnDate = closedOnDate;
|
||||||
|
this.closedOnByUser = closedOnByUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMinRequiredOpeningBalance(java.math.BigDecimal val) { this.minRequiredOpeningBalance = val; }
|
public void setMinRequiredOpeningBalance(java.math.BigDecimal val) { this.minRequiredOpeningBalance = val; }
|
||||||
@@ -406,4 +442,15 @@ public class CurrentAccount extends AbstractAuditableCustom {
|
|||||||
public void setNominalAnnualInterestRateOverdraft(java.math.BigDecimal val) { this.nominalAnnualInterestRateOverdraft = val; }
|
public void setNominalAnnualInterestRateOverdraft(java.math.BigDecimal val) { this.nominalAnnualInterestRateOverdraft = val; }
|
||||||
public void setWithdrawalFeeForTransfer(boolean val) { this.withdrawalFeeForTransfer = val; }
|
public void setWithdrawalFeeForTransfer(boolean val) { this.withdrawalFeeForTransfer = val; }
|
||||||
public void setMinOverdraftForInterestCalculation(java.math.BigDecimal val) { this.minOverdraftForInterestCalculation = val; }
|
public void setMinOverdraftForInterestCalculation(java.math.BigDecimal val) { this.minOverdraftForInterestCalculation = val; }
|
||||||
|
|
||||||
|
public boolean isShareLinkingEnabled() { return shareLinkingEnabled; }
|
||||||
|
public void setShareLinkingEnabled(boolean shareLinkingEnabled) { this.shareLinkingEnabled = shareLinkingEnabled; }
|
||||||
|
|
||||||
|
public boolean isTerminateTrue() { return terminateTrue; }
|
||||||
|
public void setTerminateTrue(boolean terminateTrue) { this.terminateTrue = terminateTrue; }
|
||||||
|
|
||||||
|
public void terminate() {
|
||||||
|
this.terminateTrue = true;
|
||||||
|
this.statusEnum = 900;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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.currentaccount.domain;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Wrapper for {@link CurrentAccountRepository} that is responsible for checking if {@link CurrentAccount} is
|
||||||
|
* returned when using <code>findOne</code> repository method and throwing an appropriate not found exception.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is to avoid need for checking and throwing in multiple areas of code base where
|
||||||
|
* {@link CurrentAccountRepository} is required.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CurrentAccountRepositoryWrapper {
|
||||||
|
|
||||||
|
private final CurrentAccountRepository repository;
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public CurrentAccount findOneWithNotFoundDetection(final Long id) {
|
||||||
|
return this.repository.findById(id).orElseThrow(() -> new CurrentAccountNotFoundException(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
-10
@@ -37,6 +37,31 @@ public class CurrentAccountNumberGenerator {
|
|||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
private String getNextSequence() {
|
||||||
|
final java.util.List<String> accountNos = this.jdbcTemplate.query(
|
||||||
|
"SELECT account_no FROM m_current_account WHERE account_no IS NOT NULL",
|
||||||
|
(rs, rowNum) -> rs.getString(1));
|
||||||
|
|
||||||
|
long maxVal = 0;
|
||||||
|
for (String accNo : accountNos) {
|
||||||
|
if (accNo != null && accNo.length() >= 5) {
|
||||||
|
String suffix = accNo.substring(accNo.length() - 5);
|
||||||
|
try {
|
||||||
|
long val = Long.parseLong(suffix);
|
||||||
|
if (val > maxVal) {
|
||||||
|
maxVal = val;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String nextSeq = StringUtils.leftPad(String.valueOf(maxVal + 1), 5, '0');
|
||||||
|
if (nextSeq.length() > 5) {
|
||||||
|
nextSeq = nextSeq.substring(nextSeq.length() - 5);
|
||||||
|
}
|
||||||
|
return nextSeq;
|
||||||
|
}
|
||||||
|
|
||||||
public String generate(CurrentAccount account) {
|
public String generate(CurrentAccount account) {
|
||||||
Long productId = account.getProductId();
|
Long productId = account.getProductId();
|
||||||
|
|
||||||
@@ -59,11 +84,10 @@ public class CurrentAccountNumberGenerator {
|
|||||||
String officeId = StringUtils.leftPad(account.getOffice().getId().toString(), 2, '0');
|
String officeId = StringUtils.leftPad(account.getOffice().getId().toString(), 2, '0');
|
||||||
if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2);
|
if (officeId.length() > 2) officeId = officeId.substring(officeId.length() - 2);
|
||||||
|
|
||||||
// Account ID — 5 digits
|
// Next sequence from existing account numbers
|
||||||
String accountId = StringUtils.leftPad(account.getId().toString(), 5, '0');
|
String nextSeq = getNextSequence();
|
||||||
if (accountId.length() > 5) accountId = accountId.substring(accountId.length() - 5);
|
|
||||||
|
|
||||||
return CBS_PREFIX + glCode + officeId + accountId;
|
return CBS_PREFIX + glCode + officeId + nextSeq;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String preview(Long productId, Long officeId) {
|
public String preview(Long productId, Long officeId) {
|
||||||
@@ -80,13 +104,10 @@ public class CurrentAccountNumberGenerator {
|
|||||||
String officeStr = StringUtils.leftPad(officeId != null ? officeId.toString() : "0", 2, '0');
|
String officeStr = StringUtils.leftPad(officeId != null ? officeId.toString() : "0", 2, '0');
|
||||||
if (officeStr.length() > 2) officeStr = officeStr.substring(officeStr.length() - 2);
|
if (officeStr.length() > 2) officeStr = officeStr.substring(officeStr.length() - 2);
|
||||||
|
|
||||||
// Next account ID estimate from m_current_account
|
// Next sequence from existing account numbers
|
||||||
Long maxId = jdbcTemplate.query(
|
String nextSeq = getNextSequence();
|
||||||
"SELECT MAX(id) FROM m_current_account",
|
|
||||||
rs -> rs.next() ? rs.getLong(1) : 0L);
|
|
||||||
String nextSeq = StringUtils.leftPad(String.valueOf((maxId != null ? maxId : 0L) + 1), 5, '0');
|
|
||||||
if (nextSeq.length() > 5) nextSeq = nextSeq.substring(nextSeq.length() - 5);
|
|
||||||
|
|
||||||
return CBS_PREFIX + glCode + officeStr + nextSeq;
|
return CBS_PREFIX + glCode + officeStr + nextSeq;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -26,6 +26,8 @@ public interface CurrentAccountReadPlatformService {
|
|||||||
|
|
||||||
Map<String, Object> retrieveOne(Long accountId);
|
Map<String, Object> retrieveOne(Long accountId);
|
||||||
|
|
||||||
|
Map<String, Object> retrieveOneTransaction(Long accountId, Long transactionId);
|
||||||
|
|
||||||
List<Map<String, Object>> retrieveAll();
|
List<Map<String, Object>> retrieveAll();
|
||||||
|
|
||||||
List<Map<String, Object>> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName);
|
List<Map<String, Object>> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName);
|
||||||
|
|||||||
+2
@@ -40,4 +40,6 @@ public interface CurrentAccountWritePlatformService {
|
|||||||
CommandProcessingResult modifyApplication(Long accountId, JsonCommand command);
|
CommandProcessingResult modifyApplication(Long accountId, JsonCommand command);
|
||||||
|
|
||||||
CommandProcessingResult deleteApplication(Long accountId);
|
CommandProcessingResult deleteApplication(Long accountId);
|
||||||
|
|
||||||
|
CommandProcessingResult terminateAccount(Long accountId, JsonCommand command);
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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.currentproduct.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class CurrentProductEditPendingApprovalException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public CurrentProductEditPendingApprovalException(final Long productId) {
|
||||||
|
super("error.msg.currentproduct.edit.pending.approval",
|
||||||
|
"Current product with identifier " + productId + " already has an edit pending approval", productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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.currentproduct.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class CurrentProductNoPendingEditException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public CurrentProductNoPendingEditException(final Long productId) {
|
||||||
|
super("error.msg.currentproduct.no.pending.edit",
|
||||||
|
"Current product with identifier " + productId + " has no edit pending approval", productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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.currentproduct.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class CurrentProductSelfApprovalNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public CurrentProductSelfApprovalNotAllowedException(final Long productId) {
|
||||||
|
super("error.msg.currentproduct.self.approval.not.allowed",
|
||||||
|
"The maker of the pending edit for current product " + productId + " cannot also approve or reject it", productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.currentproduct.service;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||||
|
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||||
|
|
||||||
public interface CurrentProductReadPlatformService {
|
public interface CurrentProductReadPlatformService {
|
||||||
|
|
||||||
@@ -28,4 +29,8 @@ public interface CurrentProductReadPlatformService {
|
|||||||
Collection<SavingsProductData> retrieveAllForLookup();
|
Collection<SavingsProductData> retrieveAllForLookup();
|
||||||
|
|
||||||
SavingsProductData retrieveOne(Long productId);
|
SavingsProductData retrieveOne(Long productId);
|
||||||
|
|
||||||
|
Collection<SavingsProductTempData> retrieveAllPendingEdits();
|
||||||
|
|
||||||
|
SavingsProductTempData retrievePendingEdit(Long productId);
|
||||||
}
|
}
|
||||||
|
|||||||
+77
-1
@@ -42,13 +42,23 @@ import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilit
|
|||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository;
|
||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
|
||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductNoPendingEditException;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||||
|
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CurrentProductReadPlatformServiceImpl implements CurrentProductReadPlatformService {
|
public class CurrentProductReadPlatformServiceImpl implements CurrentProductReadPlatformService {
|
||||||
@@ -58,6 +68,11 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
|||||||
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
|
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
|
||||||
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
|
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
|
||||||
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
|
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
|
||||||
|
private final ProductPendingEditRepository productPendingEditRepository;
|
||||||
|
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||||
|
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||||
|
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||||
|
private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository;
|
||||||
|
|
||||||
private final CurrentProductMapper currentProductRowMapper = new CurrentProductMapper();
|
private final CurrentProductMapper currentProductRowMapper = new CurrentProductMapper();
|
||||||
private final CurrentProductLookupMapper currentProductLookupsRowMapper = new CurrentProductLookupMapper();
|
private final CurrentProductLookupMapper currentProductLookupsRowMapper = new CurrentProductLookupMapper();
|
||||||
@@ -147,6 +162,64 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String PENDING_EDIT_SCHEMA = "spt.id as tempId, spt.product_id as savingsProductId, "
|
||||||
|
+ "cp.name as savingsProductName, spt.createdby_id as makerId, maker.username as makerUsername, "
|
||||||
|
+ "spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, spt.created_date as createdOn, "
|
||||||
|
+ "spt.lastmodified_date as checkedOn, spt.status as status, spt.reject_reason as rejectReason "
|
||||||
|
+ "from m_saving_product_temp spt "
|
||||||
|
+ "join m_current_product cp on cp.id = spt.product_id "
|
||||||
|
+ "join m_appuser maker on maker.id = spt.createdby_id "
|
||||||
|
+ "left join m_appuser checker on checker.id = spt.lastmodifiedby_id "
|
||||||
|
+ "where spt.product_type = 'CURRENT' ";
|
||||||
|
|
||||||
|
private RowMapper<SavingsProductTempData> getPendingEditRowMapper() {
|
||||||
|
return (rs, rowNum) -> {
|
||||||
|
final SavingsProductTempData data = new SavingsProductTempData();
|
||||||
|
data.setId(rs.getLong("tempId"));
|
||||||
|
data.setSavingsProductId(rs.getLong("savingsProductId"));
|
||||||
|
data.setSavingsProductName(rs.getString("savingsProductName"));
|
||||||
|
data.setMakerId(rs.getLong("makerId"));
|
||||||
|
data.setMakerUsername(rs.getString("makerUsername"));
|
||||||
|
data.setCheckerId(JdbcSupport.getLong(rs, "checkerId"));
|
||||||
|
data.setCheckerUsername(rs.getString("checkerUsername"));
|
||||||
|
data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime());
|
||||||
|
data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime());
|
||||||
|
data.setStatus(rs.getString("status"));
|
||||||
|
data.setRejectReason(rs.getString("rejectReason"));
|
||||||
|
|
||||||
|
final Long tempId = data.getId();
|
||||||
|
final java.util.Optional<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||||
|
if (opt.isPresent()) {
|
||||||
|
final ProductPendingEdit pendingEdit = opt.get();
|
||||||
|
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||||
|
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||||
|
final Map<String, Object> proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap(
|
||||||
|
pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository,
|
||||||
|
this.pendingEditFacilityLimitRepository, GSON);
|
||||||
|
data.setProposedChanges(proposedChanges);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<SavingsProductTempData> retrieveAllPendingEdits() {
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_date desc";
|
||||||
|
return this.jdbcTemplate.query(sql, getPendingEditRowMapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SavingsProductTempData retrievePendingEdit(final Long productId) {
|
||||||
|
this.context.authenticatedUser();
|
||||||
|
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?";
|
||||||
|
try {
|
||||||
|
return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
throw new CurrentProductNoPendingEditException(productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class CurrentProductMapper implements RowMapper<SavingsProductData> {
|
private static final class CurrentProductMapper implements RowMapper<SavingsProductData> {
|
||||||
|
|
||||||
private final String schemaSql;
|
private final String schemaSql;
|
||||||
@@ -197,7 +270,9 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
|||||||
sqlBuilder.append("cp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
sqlBuilder.append("cp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||||
sqlBuilder.append("cp.balance_swap_option as balanceSwapOption, cp.msme as msme, cp.auto_debit_facility as autoDebitFacility, ");
|
sqlBuilder.append("cp.balance_swap_option as balanceSwapOption, cp.msme as msme, cp.auto_debit_facility as autoDebitFacility, ");
|
||||||
sqlBuilder.append("cp.avg_balance_calc_period as avgBalanceCalcPeriod, cp.valid_for_period as validForPeriod, ");
|
sqlBuilder.append("cp.avg_balance_calc_period as avgBalanceCalcPeriod, cp.valid_for_period as validForPeriod, ");
|
||||||
sqlBuilder.append("cp.od_account_closed_zero_balance_in_day_closing as odAccountClosedZeroBalanceInDayClosing ");
|
sqlBuilder.append("cp.od_account_closed_zero_balance_in_day_closing as odAccountClosedZeroBalanceInDayClosing, ");
|
||||||
|
sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = cp.id "
|
||||||
|
+ "and spt.product_type = 'CURRENT' and spt.status = 'PENDING') as isPendingApproval ");
|
||||||
|
|
||||||
sqlBuilder.append("from m_current_product cp ");
|
sqlBuilder.append("from m_current_product cp ");
|
||||||
sqlBuilder.append("join m_currency curr on curr.code = cp.currency_code ");
|
sqlBuilder.append("join m_currency curr on curr.code = cp.currency_code ");
|
||||||
@@ -321,6 +396,7 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead
|
|||||||
productData.setAvgBalanceCalcPeriod(rs.getString("avgBalanceCalcPeriod"));
|
productData.setAvgBalanceCalcPeriod(rs.getString("avgBalanceCalcPeriod"));
|
||||||
productData.setValidForPeriod(rs.getString("validForPeriod"));
|
productData.setValidForPeriod(rs.getString("validForPeriod"));
|
||||||
productData.setOdAccountClosedZeroBalanceInDayClosing(rs.getBoolean("odAccountClosedZeroBalanceInDayClosing"));
|
productData.setOdAccountClosedZeroBalanceInDayClosing(rs.getBoolean("odAccountClosedZeroBalanceInDayClosing"));
|
||||||
|
productData.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||||
|
|
||||||
return productData;
|
return productData;
|
||||||
}
|
}
|
||||||
|
|||||||
+17
@@ -28,4 +28,21 @@ public interface CurrentProductWritePlatformService {
|
|||||||
CommandProcessingResult update(Long productId, JsonCommand command);
|
CommandProcessingResult update(Long productId, JsonCommand command);
|
||||||
|
|
||||||
CommandProcessingResult delete(Long productId);
|
CommandProcessingResult delete(Long productId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits an edit for approval instead of applying it directly. Fails if a pending edit already exists for this
|
||||||
|
* product (that pending row is itself the lock).
|
||||||
|
*/
|
||||||
|
CommandProcessingResult submitEditForApproval(Long productId, JsonCommand command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replays the stored pending edit through the normal update path and marks it approved. Fails if the approving
|
||||||
|
* user is the same user who submitted the edit.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult approveEdit(Long productId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discards the pending edit. Fails if the rejecting user is the same user who submitted the edit.
|
||||||
|
*/
|
||||||
|
CommandProcessingResult rejectEdit(Long productId, String rejectReason);
|
||||||
}
|
}
|
||||||
|
|||||||
+245
@@ -31,6 +31,9 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
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.commands.domain.CommandProcessingResultType;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSource;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
@@ -38,6 +41,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
|||||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||||
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
|
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
|
||||||
@@ -50,10 +55,26 @@ import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilit
|
|||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimit;
|
||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository;
|
||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductEditPendingApprovalException;
|
||||||
|
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductNoPendingEditException;
|
||||||
|
import org.apache.fineract.portfolio.currentproduct.exception.CurrentProductSelfApprovalNotAllowedException;
|
||||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimit;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -61,6 +82,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements CurrentProductWritePlatformService {
|
public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements CurrentProductWritePlatformService {
|
||||||
|
|
||||||
|
private static final com.google.gson.Gson GSON = new com.google.gson.Gson();
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final CurrentProductRepository currentProductRepository;
|
private final CurrentProductRepository currentProductRepository;
|
||||||
private final CurrentProductDataValidator fromApiJsonDataValidator;
|
private final CurrentProductDataValidator fromApiJsonDataValidator;
|
||||||
@@ -69,6 +91,13 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr
|
|||||||
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
|
private final CurrentProductFacilityConfigRepository facilityConfigRepository;
|
||||||
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
|
private final CurrentProductFacilityLimitRepository facilityLimitRepository;
|
||||||
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
|
private final CurrentProductDepositLimitKycRepository depositLimitKycRepository;
|
||||||
|
private final ProductPendingEditRepository productPendingEditRepository;
|
||||||
|
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||||
|
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||||
|
private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository;
|
||||||
|
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
private final CommandSourceRepository commandSourceRepository;
|
||||||
|
|
||||||
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dae) {
|
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dae) {
|
||||||
String msgCode = "error.msg." + DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME;
|
String msgCode = "error.msg." + DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME;
|
||||||
@@ -251,6 +280,222 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||||
|
|
||||||
|
final AppUser maker = this.context.authenticatedUser();
|
||||||
|
final CurrentProduct product = this.currentProductRepository.findById(productId)
|
||||||
|
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
|
||||||
|
|
||||||
|
if (this.productPendingEditRepository
|
||||||
|
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_CURRENT, productId, ProductPendingEdit.STATUS_PENDING)
|
||||||
|
.isPresent()) {
|
||||||
|
throw new CurrentProductEditPendingApprovalException(productId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fromApiJsonDataValidator.validateForUpdate(command.json(), product);
|
||||||
|
|
||||||
|
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_CURRENT, productId, maker);
|
||||||
|
|
||||||
|
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||||
|
final java.util.Set<String> currentOnlySubmitted = ProductPendingEditFieldMapper.populateCurrentOnlyFields(command, pendingEdit.getCurrentOnlyFields());
|
||||||
|
final boolean facilityConfigSubmitted = ProductPendingEditFieldMapper.populateFacilityConfig(command, pendingEdit.getFacilityConfigFields());
|
||||||
|
final boolean depositLimitKycSubmitted = ProductPendingEditFieldMapper.populateDepositLimitKyc(command, pendingEdit.getDepositLimitKycFields());
|
||||||
|
final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields());
|
||||||
|
|
||||||
|
if (!currentOnlySubmitted.isEmpty()) {
|
||||||
|
submitted.add("avgBalanceCalcPeriod");
|
||||||
|
submitted.add("validForPeriod");
|
||||||
|
submitted.add("odAccountClosedZeroBalanceInDayClosing");
|
||||||
|
}
|
||||||
|
if (facilityConfigSubmitted) {
|
||||||
|
submitted.add("accountFacilityConfig");
|
||||||
|
}
|
||||||
|
if (depositLimitKycSubmitted) {
|
||||||
|
submitted.add("depositLimitKyc");
|
||||||
|
}
|
||||||
|
if (glMappingSubmitted) {
|
||||||
|
submitted.add("glAccountMapping");
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandSource auditEntry = CommandSource.builder() //
|
||||||
|
.actionName("UPDATE") //
|
||||||
|
.entityName("CURRENTPRODUCT") //
|
||||||
|
.resourceGetUrl("/currentproducts/" + productId) //
|
||||||
|
.resourceId(productId) //
|
||||||
|
.commandAsJson(command.json()) //
|
||||||
|
.maker(maker) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build();
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||||
|
|
||||||
|
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
for (final String fieldName : submitted) {
|
||||||
|
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Long> chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command);
|
||||||
|
if (chargeIds != null) {
|
||||||
|
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges"));
|
||||||
|
for (final Long chargeId : chargeIds) {
|
||||||
|
this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<ProductPendingEditFacilityLimit> facilityLimits = ProductPendingEditFieldMapper.extractFacilityLimits(command, pendingEdit);
|
||||||
|
if (facilityLimits != null) {
|
||||||
|
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "accountFacilityLimits"));
|
||||||
|
this.pendingEditFacilityLimitRepository.saveAll(facilityLimits);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<ProductPendingEditGlMapping> glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit);
|
||||||
|
if (glMappings != null) {
|
||||||
|
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings"));
|
||||||
|
this.pendingEditGlMappingRepository.saveAll(glMappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withEntityId(product.getId()) //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult approveEdit(final Long productId) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||||
|
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_CURRENT, productId, ProductPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new CurrentProductNoPendingEditException(productId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new CurrentProductSelfApprovalNotAllowedException(productId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||||
|
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||||
|
final JsonObject root = new JsonObject();
|
||||||
|
root.addProperty("locale", "en");
|
||||||
|
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||||
|
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||||
|
ProductPendingEditFieldMapper.writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields);
|
||||||
|
ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(),
|
||||||
|
submittedFields.contains("accountFacilityConfig"));
|
||||||
|
ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(),
|
||||||
|
submittedFields.contains("depositLimitKyc"));
|
||||||
|
if (submittedFields.contains("glAccountMapping")) {
|
||||||
|
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||||
|
}
|
||||||
|
if (submittedFields.contains("charges")) {
|
||||||
|
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||||
|
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||||
|
}
|
||||||
|
if (submittedFields.contains("accountFacilityLimits")) {
|
||||||
|
ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root,
|
||||||
|
this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId()));
|
||||||
|
}
|
||||||
|
if (submittedFields.contains("glArrayMappings")) {
|
||||||
|
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||||
|
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||||
|
}
|
||||||
|
final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper);
|
||||||
|
final CommandProcessingResult result = update(productId, replayedCommand);
|
||||||
|
|
||||||
|
pendingEdit.markApproved(checker);
|
||||||
|
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||||
|
auditEntry.markAsChecked(checker);
|
||||||
|
auditEntry.updateForAudit(result);
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||||
|
if (pendingEdit.getCommandSourceId() != null) {
|
||||||
|
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
return existing.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||||
|
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||||
|
final JsonObject root = new JsonObject();
|
||||||
|
root.addProperty("locale", "en");
|
||||||
|
root.addProperty("dateFormat", "yyyy-MM-dd");
|
||||||
|
ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields);
|
||||||
|
ProductPendingEditFieldMapper.writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields);
|
||||||
|
ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(),
|
||||||
|
submittedFields.contains("accountFacilityConfig"));
|
||||||
|
ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(),
|
||||||
|
submittedFields.contains("depositLimitKyc"));
|
||||||
|
if (submittedFields.contains("glAccountMapping")) {
|
||||||
|
ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields());
|
||||||
|
}
|
||||||
|
if (submittedFields.contains("charges")) {
|
||||||
|
ProductPendingEditFieldMapper.writeChargesToJson(root,
|
||||||
|
this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId()));
|
||||||
|
}
|
||||||
|
if (submittedFields.contains("accountFacilityLimits")) {
|
||||||
|
ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root,
|
||||||
|
this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId()));
|
||||||
|
}
|
||||||
|
if (submittedFields.contains("glArrayMappings")) {
|
||||||
|
ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root,
|
||||||
|
this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId()));
|
||||||
|
}
|
||||||
|
final String commandJson = GSON.toJson(root);
|
||||||
|
|
||||||
|
final CommandSource auditEntry = CommandSource.builder() //
|
||||||
|
.actionName("UPDATE") //
|
||||||
|
.entityName("CURRENTPRODUCT") //
|
||||||
|
.resourceGetUrl("/currentproducts/" + productId) //
|
||||||
|
.resourceId(productId) //
|
||||||
|
.commandAsJson(commandJson) //
|
||||||
|
.maker(pendingEdit.getMaker()) //
|
||||||
|
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||||
|
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||||
|
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||||
|
.sanitized(false) //
|
||||||
|
.build();
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||||
|
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
return auditEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||||
|
|
||||||
|
final AppUser checker = this.context.authenticatedUser();
|
||||||
|
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||||
|
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_CURRENT, productId, ProductPendingEdit.STATUS_PENDING)
|
||||||
|
.orElseThrow(() -> new CurrentProductNoPendingEditException(productId));
|
||||||
|
|
||||||
|
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||||
|
throw new CurrentProductSelfApprovalNotAllowedException(productId);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingEdit.markRejected(checker, rejectReason);
|
||||||
|
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||||
|
|
||||||
|
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||||
|
auditEntry.markAsRejected(checker);
|
||||||
|
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withEntityId(productId) //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult delete(final Long productId) {
|
public CommandProcessingResult delete(final Long productId) {
|
||||||
|
|||||||
+28
-4
@@ -19,7 +19,14 @@
|
|||||||
package org.apache.fineract.portfolio.currentproduct.starter;
|
package org.apache.fineract.portfolio.currentproduct.starter;
|
||||||
|
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||||
|
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository;
|
||||||
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
|
import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator;
|
||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductAssembler;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductAssembler;
|
||||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository;
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository;
|
||||||
@@ -43,9 +50,17 @@ public class CurrentProductConfiguration {
|
|||||||
public CurrentProductReadPlatformService currentProductReadPlatformService(PlatformSecurityContext context,
|
public CurrentProductReadPlatformService currentProductReadPlatformService(PlatformSecurityContext context,
|
||||||
JdbcTemplate jdbcTemplate, CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
|
JdbcTemplate jdbcTemplate, CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
|
||||||
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
|
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
|
||||||
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository) {
|
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository,
|
||||||
|
ProductPendingEditRepository productPendingEditRepository,
|
||||||
|
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||||
|
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||||
|
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||||
|
ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository) {
|
||||||
return new CurrentProductReadPlatformServiceImpl(context, jdbcTemplate, currentFacilityConfigRepository,
|
return new CurrentProductReadPlatformServiceImpl(context, jdbcTemplate, currentFacilityConfigRepository,
|
||||||
currentFacilityLimitRepository, currentDepositLimitKycRepository);
|
currentFacilityLimitRepository, currentDepositLimitKycRepository,
|
||||||
|
productPendingEditRepository, pendingEditSubmittedFieldRepository,
|
||||||
|
pendingEditChargeRepository, pendingEditGlMappingRepository,
|
||||||
|
pendingEditFacilityLimitRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -56,9 +71,18 @@ public class CurrentProductConfiguration {
|
|||||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||||
CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
|
CurrentProductFacilityConfigRepository currentFacilityConfigRepository,
|
||||||
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
|
CurrentProductFacilityLimitRepository currentFacilityLimitRepository,
|
||||||
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository) {
|
CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository,
|
||||||
|
ProductPendingEditRepository productPendingEditRepository,
|
||||||
|
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||||
|
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||||
|
ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository,
|
||||||
|
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||||
|
FromJsonHelper fromApiJsonHelper,
|
||||||
|
CommandSourceRepository commandSourceRepository) {
|
||||||
return new CurrentProductWritePlatformServiceJpaRepositoryImpl(context, currentProductRepository, currentProductDataValidator,
|
return new CurrentProductWritePlatformServiceJpaRepositoryImpl(context, currentProductRepository, currentProductDataValidator,
|
||||||
currentProductAssembler, accountMappingWritePlatformService, currentFacilityConfigRepository,
|
currentProductAssembler, accountMappingWritePlatformService, currentFacilityConfigRepository,
|
||||||
currentFacilityLimitRepository, currentDepositLimitKycRepository);
|
currentFacilityLimitRepository, currentDepositLimitKycRepository, productPendingEditRepository,
|
||||||
|
pendingEditSubmittedFieldRepository, pendingEditChargeRepository, pendingEditFacilityLimitRepository,
|
||||||
|
pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+719
-719
File diff suppressed because it is too large
Load Diff
@@ -99,6 +99,10 @@ dependencies {
|
|||||||
// Bundles ZXing transitively for QR PNG output (used by ZxingPngQrGenerator).
|
// Bundles ZXing transitively for QR PNG output (used by ZxingPngQrGenerator).
|
||||||
'dev.samstevens.totp:totp:1.7.1',
|
'dev.samstevens.totp:totp:1.7.1',
|
||||||
|
|
||||||
|
// Nimbus JOSE+JWT — RS256 sign/verify for the IMPS_BANK_API_SPEC_CBS_1.0 envelope
|
||||||
|
// (see org.apache.fineract.infrastructure.imps.crypto.ImpsJwtUtil).
|
||||||
|
'com.nimbusds:nimbus-jose-jwt:9.37',
|
||||||
|
|
||||||
'org.apache.commons:commons-lang3',
|
'org.apache.commons:commons-lang3',
|
||||||
'commons-io:commons-io',
|
'commons-io:commons-io',
|
||||||
'org.apache.poi:poi',
|
'org.apache.poi:poi',
|
||||||
|
|||||||
@@ -0,0 +1,975 @@
|
|||||||
|
> Task :buildSrc:extractPluginRequests UP-TO-DATE
|
||||||
|
> Task :buildSrc:generatePluginAdapters UP-TO-DATE
|
||||||
|
> Task :buildSrc:compileJava UP-TO-DATE
|
||||||
|
> Task :buildSrc:compileGroovy UP-TO-DATE
|
||||||
|
> Task :buildSrc:compileGroovyPlugins UP-TO-DATE
|
||||||
|
> Task :buildSrc:pluginDescriptors UP-TO-DATE
|
||||||
|
> Task :buildSrc:processResources UP-TO-DATE
|
||||||
|
> Task :buildSrc:classes UP-TO-DATE
|
||||||
|
> Task :buildSrc:jar UP-TO-DATE
|
||||||
|
|
||||||
|
> Configure project :
|
||||||
|
matching ref: COMMIT - 7dea88d30f72365945bed2160d2ba72dccdc80b7
|
||||||
|
ref configuration: COMMIT - pattern: null
|
||||||
|
version: ${describe.tag.version.major}.${describe.tag.version.minor.next}.0-SNAPSHOT
|
||||||
|
describeTagPattern: .*(\d+\.\d+\.\d+).*
|
||||||
|
describeTagFirstParent: false
|
||||||
|
|
||||||
|
project version: 1.15.0-SNAPSHOT
|
||||||
|
|
||||||
|
> Task :spotlessInternalRegisterDependencies UP-TO-DATE
|
||||||
|
> Task :fineract-current-product:processResources NO-SOURCE
|
||||||
|
> Task :fineract-mix:processResources NO-SOURCE
|
||||||
|
> Task :fineract-rates:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-core:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-cob:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-branch:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-security:processResources NO-SOURCE
|
||||||
|
> Task :fineract-loan-origination:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-savings:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-accounting:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-report:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-charge:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-investor:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-loan:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-client:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-progressive-loan:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-working-capital-loan:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-document:processResources NO-SOURCE
|
||||||
|
> Task :fineract-tax:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:preprocessAvroSchemas UP-TO-DATE
|
||||||
|
> Task :fineract-validation:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-command:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-validation:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-validation:classes UP-TO-DATE
|
||||||
|
> Task :fineract-command:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-command:classes UP-TO-DATE
|
||||||
|
> Task :fineract-command:jar UP-TO-DATE
|
||||||
|
> Task :fineract-validation:jar UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:spotlessJson UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:spotlessJsonApply UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:buildJavaSdk UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:processResources UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:classes UP-TO-DATE
|
||||||
|
> Task :fineract-avro-schemas:jar UP-TO-DATE
|
||||||
|
> Task :fineract-working-capital-loan:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-working-capital-loan:classes UP-TO-DATE
|
||||||
|
> Task :fineract-working-capital-loan:jar UP-TO-DATE
|
||||||
|
> Task :fineract-core:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-core:classes UP-TO-DATE
|
||||||
|
> Task :fineract-core:jar UP-TO-DATE
|
||||||
|
> Task :fineract-mix:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-tax:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-mix:classes UP-TO-DATE
|
||||||
|
> Task :fineract-report:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-tax:classes UP-TO-DATE
|
||||||
|
> Task :fineract-mix:jar UP-TO-DATE
|
||||||
|
> Task :fineract-rates:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-report:classes UP-TO-DATE
|
||||||
|
> Task :fineract-cob:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-rates:classes UP-TO-DATE
|
||||||
|
> Task :fineract-cob:classes UP-TO-DATE
|
||||||
|
> Task :fineract-tax:jar UP-TO-DATE
|
||||||
|
> Task :fineract-report:jar UP-TO-DATE
|
||||||
|
> Task :fineract-cob:jar UP-TO-DATE
|
||||||
|
> Task :fineract-rates:jar UP-TO-DATE
|
||||||
|
> Task :fineract-document:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-document:classes UP-TO-DATE
|
||||||
|
> Task :fineract-security:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-document:jar UP-TO-DATE
|
||||||
|
> Task :fineract-security:classes UP-TO-DATE
|
||||||
|
> Task :fineract-security:jar UP-TO-DATE
|
||||||
|
> Task :fineract-charge:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-charge:classes UP-TO-DATE
|
||||||
|
> Task :fineract-charge:jar UP-TO-DATE
|
||||||
|
> Task :fineract-accounting:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-accounting:classes UP-TO-DATE
|
||||||
|
> Task :fineract-accounting:jar UP-TO-DATE
|
||||||
|
> Task :fineract-branch:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-branch:classes UP-TO-DATE
|
||||||
|
> Task :fineract-loan:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-loan:classes UP-TO-DATE
|
||||||
|
> Task :fineract-branch:jar UP-TO-DATE
|
||||||
|
> Task :fineract-loan:jar UP-TO-DATE
|
||||||
|
> Task :fineract-savings:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-savings:classes UP-TO-DATE
|
||||||
|
> Task :fineract-savings:jar UP-TO-DATE
|
||||||
|
> Task :fineract-loan-origination:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-investor:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-progressive-loan:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-loan-origination:classes UP-TO-DATE
|
||||||
|
> Task :fineract-progressive-loan:classes UP-TO-DATE
|
||||||
|
> Task :fineract-investor:classes UP-TO-DATE
|
||||||
|
> Task :fineract-progressive-loan:jar UP-TO-DATE
|
||||||
|
> Task :fineract-loan-origination:jar UP-TO-DATE
|
||||||
|
> Task :fineract-investor:jar UP-TO-DATE
|
||||||
|
> Task :fineract-current-product:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-current-product:classes UP-TO-DATE
|
||||||
|
> Task :fineract-current-product:jar UP-TO-DATE
|
||||||
|
> Task :fineract-provider:generateGitProperties
|
||||||
|
> Task :fineract-provider:compileJava UP-TO-DATE
|
||||||
|
> Task :fineract-provider:generateRebelMain UP-TO-DATE
|
||||||
|
> Task :fineract-provider:processResources
|
||||||
|
> Task :fineract-provider:classes
|
||||||
|
> Task :fineract-provider:compileTestJava UP-TO-DATE
|
||||||
|
> Task :fineract-provider:prepareInputYaml UP-TO-DATE
|
||||||
|
> Task :fineract-provider:processTestResources UP-TO-DATE
|
||||||
|
> Task :fineract-provider:testClasses UP-TO-DATE
|
||||||
|
> Task :fineract-provider:resolve
|
||||||
|
> Task :fineract-provider:resolveMainClassName
|
||||||
|
|
||||||
|
> Task :fineract-provider:bootRun
|
||||||
|
19:17:05,132 |-WARN in ch.qos.logback.core.joran.action.ConversionRuleAction - [converterClass] attribute is deprecated and replaced by [class]. See element [conversionRule] near line 30
|
||||||
|
|
||||||
|
_ _ _____ _ _
|
||||||
|
/ \ _ __ __ _ ___| |__ ___ | ___(_)_ __ ___ _ __ __ _ ___| |_
|
||||||
|
/ _ \ | '_ \ / _` |/ __| '_ \ / _ \ | |_ | | '_ \ / _ \ '__/ _` |/ __| __|
|
||||||
|
/ ___ \| |_) | (_| | (__| | | | __/ | _| | | | | | __/ | | (_| | (__| |_
|
||||||
|
/_/ \_\ .__/ \__,_|\___|_| |_|\___| |_| |_|_| |_|\___|_| \__,_|\___|\__|
|
||||||
|
|_|
|
||||||
|
|
||||||
|
[2m[37m(c) 2015-2024 Apache Fineract (https://fineract.apache.org)[30m
|
||||||
|
|
||||||
|
[2m[37mPowered by Spring Boot 3.5.6[30m
|
||||||
|
|
||||||
|
|
||||||
|
[2m2026-05-29 19:17:05.618[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36morg.apache.fineract.ServerApplication [0;39m [2m:[0;39m Starting ServerApplication using Java 21.0.9 with PID 7760 (C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main started by adity in C:\Users\adity\Music\CBS\fineract\fineract-provider)
|
||||||
|
[2m2026-05-29 19:17:05.622[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36morg.apache.fineract.ServerApplication [0;39m [2m:[0;39m No active profile set, falling back to 1 default profile: "default"
|
||||||
|
[2m2026-05-29 19:17:10.836[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Multiple Spring Data modules found, entering strict repository configuration mode
|
||||||
|
[2m2026-05-29 19:17:10.836[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Bootstrapping Spring Data JPA repositories in DEFAULT mode.
|
||||||
|
[2m2026-05-29 19:17:11.794[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.DocumentRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
[2m2026-05-29 19:17:11.797[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.ImageRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
[2m2026-05-29 19:17:11.811[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixReportXBRLNamespaceRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
[2m2026-05-29 19:17:11.813[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyMappingRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
[2m2026-05-29 19:17:11.815[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
[2m2026-05-29 19:17:11.887[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.command.persistence.domain.CommandRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
[2m2026-05-29 19:17:13.120[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Finished Spring Data repository scanning in 2267 ms. Found 224 JPA repository interfaces.
|
||||||
|
[2m2026-05-29 19:17:13.130[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.f.s.DefaultListableBeanFactory [0;39m [2m:[0;39m Overriding bean definition for bean 'jpaAuditingHandler' with a different definition: replacing [Root bean: class=org.springframework.data.auditing.AuditingHandler; scope=; abstract=false; lazyInit=null; autowireMode=2; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=from; initMethodNames=null; destroyMethodNames=null] with [Root bean: class=org.apache.fineract.infrastructure.core.auditing.CustomAuditingHandler; scope=prototype; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null]
|
||||||
|
[2m2026-05-29 19:17:13.204[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.f.s.DefaultListableBeanFactory [0;39m [2m:[0;39m Overriding bean definition for bean 'transactionManager' with a different definition: replacing [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=JPAConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/core/config/jpa/JPAConfig.class]] with [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=scheduledJobRunnerConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.class]]
|
||||||
|
[2m2026-05-29 19:17:13.206[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Bean jobRepository already defined in the application context, skipping the registration of a jobRepository
|
||||||
|
[2m2026-05-29 19:17:13.206[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Bean jobExplorer already defined in the application context, skipping the registration of a jobExplorer
|
||||||
|
[2m2026-05-29 19:17:13.206[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Bean jobLauncher already defined in the application context, skipping the registration of a jobLauncher
|
||||||
|
[2m2026-05-29 19:17:13.208[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.c.c.annotation.BatchRegistrar [0;39m [2m:[0;39m Finished Spring Batch infrastructure beans configuration in 3 ms.
|
||||||
|
[2m2026-05-29 19:17:14.107[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Multiple Spring Data modules found, entering strict repository configuration mode
|
||||||
|
[2m2026-05-29 19:17:14.108[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Bootstrapping Spring Data JDBC repositories in DEFAULT mode.
|
||||||
|
[2m2026-05-29 19:17:14.408[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.provisioning.domain.ProvisioningEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.409[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.adhocquery.domain.AdHocRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.409[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.LoanAccountLockRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.bulkimport.domain.ImportDocumentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.410[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.ExternalServicesPropertiesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.412[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.412[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauLoanProductMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.412[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.413[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.413[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.OrganisationCreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.413[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.TokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.EntityDatatableChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.RegisteredDatatableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterUsageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.414[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityToEntityMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.415[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookTemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.imps.domain.ImpsTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.416[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.SchedulerDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntryAggregationTrackingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntrySummaryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.417[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.419[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.419[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.420[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.otp.domain.OtpLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.sms.domain.SmsMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.survey.domain.LikelihoodRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.interoperation.domain.InteropIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.421[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationMapperRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.422[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.422[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.holiday.domain.HolidayRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.422[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.423[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCategoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.423[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaDefinitionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.423[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.424[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.staff.domain.StaffRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.424[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.424[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.425[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.425[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.426[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.426[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.AddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.426[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.FieldConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.ckyc.domain.CkycBatchRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientAddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.427[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientFamilyMembersRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNonPersonRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.428[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransferDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.429[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.430[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomainRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.430[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateral.domain.LoanCollateralRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.431[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.BankIfscRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.431[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.CustomerTransferLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.431[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.FundTransferEventRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.FundTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.TransferLimitConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fundtransfer.domain.VerificationTokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fund.domain.FundRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupLevelRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.432[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.433[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.productmix.domain.ProductMixRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.attendance.domain.ClientAttendanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.domain.MeetingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.note.domain.NoteRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.rate.domain.RateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.434[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.435[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.438[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.438[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.438[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPTRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.device.domain.DeviceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.439[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.registration.domain.SelfServiceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.440[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.441[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.441[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.LookupTableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.442[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.ScorecardRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.444[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.SurveyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.444[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.template.domain.TemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.445[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserPreviousPasswordRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.445[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PasswordValidationPolicyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PermissionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.RoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.446[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerLoanProductAttributesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.447[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.448[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.448[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.448[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.449[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.449[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.449[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAccrualActivityRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.450[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidByRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.452[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanStatusChangeHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgingParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.453[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.454[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.455[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlabRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FacilityChargeMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.456[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.BatchBusinessStepRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.457[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.closure.domain.GLClosureRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.458[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.rule.domain.AccountingRuleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.459[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.charge.domain.ChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxComponentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxGroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.domain.TFAccessTokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.462[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.domain.TwoFactorConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.464[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.GLAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.commands.domain.CommandSourceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.cache.domain.PlatformCacheRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.466[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.rbifeed.domain.RbiFeedItemRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.CfcfrmsSimulatorLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.467[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintAuditLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.468[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintTicketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.468[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientForm60Repository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.470[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.470[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNomineeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientEouRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsmeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.471[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrowerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndiaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.mobilelookup.domain.MobileLookupLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.472[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsScreeningLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.DscChallengeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.473[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.RepositoryConfigurationExtensionSupport[0;39m [2m:[0;39m Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
|
||||||
|
[2m2026-05-29 19:17:14.768[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Finished Spring Data repository scanning in 657 ms. Found 6 JDBC repository interfaces.
|
||||||
|
[2m2026-05-29 19:17:16.886[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
|
||||||
|
[2m2026-05-29 19:17:16.895[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mfaultConfiguringBeanFactoryPostProcessor[0;39m [2m:[0;39m No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
|
||||||
|
[2m2026-05-29 19:17:18.104[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'fineract-org.apache.fineract.infrastructure.core.config.FineractProperties' of type [org.apache.fineract.infrastructure.core.config.FineractProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:18.117[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'cacheConfig' of type [org.apache.fineract.infrastructure.core.config.cache.CacheConfig$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:18.335[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36morg.reflections.Reflections [0;39m [2m:[0;39m Reflections took 7 ms to scan 1 urls, producing 0 keys and 0 values
|
||||||
|
[2m2026-05-29 19:17:18.339[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.a.f.i.core.config.cache.CacheConfig [0;39m [2m:[0;39m The following cache configurations are defined but cache does not exists: [userTFAccessToken]
|
||||||
|
[2m2026-05-29 19:17:18.343[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'ehCacheManager' of type [org.springframework.cache.jcache.JCacheCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:18.354[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'defaultCacheManager' of type [org.apache.fineract.infrastructure.core.config.cache.TransactionBoundCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:18.360[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'runtimeDelegatingCacheManager' of type [org.apache.fineract.infrastructure.cache.service.RuntimeDelegatingCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:18.364[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'platformCacheConfiguration' of type [org.apache.fineract.infrastructure.cache.PlatformCacheConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:18.407[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'samplingConfiguration' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:18.415[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mtrationDelegate$BeanPostProcessorChecker[0;39m [2m:[0;39m Bean 'samplingServiceFactory' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingServiceFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE.
|
||||||
|
[2m2026-05-29 19:17:19.255[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.w.embedded.tomcat.TomcatWebServer [0;39m [2m:[0;39m Tomcat initialized with port 8443 (https)
|
||||||
|
[2m2026-05-29 19:17:19.278[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.apache.catalina.core.StandardService [0;39m [2m:[0;39m Starting service [Tomcat]
|
||||||
|
[2m2026-05-29 19:17:19.279[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.apache.catalina.core.StandardEngine [0;39m [2m:[0;39m Starting Servlet engine: [Apache Tomcat/10.1.49]
|
||||||
|
[2m2026-05-29 19:17:19.328[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.a.c.c.C.[.[.[/fineract-provider] [0;39m [2m:[0;39m Initializing Spring embedded WebApplicationContext
|
||||||
|
[2m2026-05-29 19:17:19.329[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mw.s.c.ServletWebServerApplicationContext[0;39m [2m:[0;39m Root WebApplicationContext: initialization completed in 13673 ms
|
||||||
|
[2m2026-05-29 19:17:19.577[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Starting...
|
||||||
|
[2m2026-05-29 19:17:19.860[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.pool.HikariPool [0;39m [2m:[0;39m HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@5917b984
|
||||||
|
[2m2026-05-29 19:17:19.861[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Start completed.
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Upgrading tenant store DB at localhost:5432
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.username: postgres
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.password: ****
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.parameters:
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.timezone: Asia/Kolkata
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.description: Default Demo Tenant
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.identifier: default
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - fineract.tenant.name: fineract_default
|
||||||
|
[2m2026-05-29 19:17:20.050[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m - liquibase.analytics.enabled: false
|
||||||
|
[2m2026-05-29 19:17:25.487[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Tenant store upgrade finished
|
||||||
|
[2m2026-05-29 19:17:25.487[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Upgrading all tenants
|
||||||
|
[2m2026-05-29 19:17:25.501[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Upgrade for tenant default has started
|
||||||
|
[2m2026-05-29 19:17:25.842[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Starting...
|
||||||
|
[2m2026-05-29 19:17:25.946[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.pool.HikariPool [0;39m [2m:[0;39m HikariPool-2 - Added connection org.postgresql.jdbc.PgConnection@625805c1
|
||||||
|
[2m2026-05-29 19:17:25.946[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Start completed.
|
||||||
|
[2m2026-05-29 19:17:31.700[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mliquibase.changelog [0;39m [2m:[0;39m ChangeSet db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract encountered an exception.
|
||||||
|
|
||||||
|
liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:520)
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:85)
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:188)
|
||||||
|
at liquibase.executor.AbstractExecutor.execute(AbstractExecutor.java:148)
|
||||||
|
at liquibase.database.AbstractJdbcDatabase.executeStatements(AbstractJdbcDatabase.java:1198)
|
||||||
|
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:816)
|
||||||
|
at liquibase.changelog.visitor.UpdateVisitor.executeAcceptedChange(UpdateVisitor.java:127)
|
||||||
|
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:71)
|
||||||
|
at liquibase.changelog.ChangeLogIterator.lambda$run$0(ChangeLogIterator.java:133)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.changelog.ChangeLogIterator.lambda$run$1(ChangeLogIterator.java:122)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.Scope.child(Scope.java:282)
|
||||||
|
at liquibase.Scope.child(Scope.java:286)
|
||||||
|
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:91)
|
||||||
|
at liquibase.command.core.AbstractUpdateCommandStep.lambda$run$0(AbstractUpdateCommandStep.java:114)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.command.core.AbstractUpdateCommandStep.run(AbstractUpdateCommandStep.java:112)
|
||||||
|
at liquibase.command.core.UpdateCommandStep.run(UpdateCommandStep.java:100)
|
||||||
|
at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:263)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:201)
|
||||||
|
at liquibase.command.CommandScope.execute(CommandScope.java:251)
|
||||||
|
at liquibase.Liquibase.lambda$update$0(Liquibase.java:216)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.Liquibase.runInScope(Liquibase.java:1366)
|
||||||
|
at liquibase.Liquibase.update(Liquibase.java:205)
|
||||||
|
at liquibase.Liquibase.update(Liquibase.java:188)
|
||||||
|
at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:327)
|
||||||
|
at liquibase.integration.spring.SpringLiquibase.lambda$afterPropertiesSet$0(SpringLiquibase.java:279)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:272)
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:205)
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.lambda$upgradeIndividualTenants$1(TenantDatabaseUpgradeService.java:146)
|
||||||
|
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||||
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
|
||||||
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null).
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2736)
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2421)
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:525)
|
||||||
|
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:435)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:357)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:342)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318)
|
||||||
|
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:313)
|
||||||
|
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95)
|
||||||
|
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java)
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:512)
|
||||||
|
... 52 common frames omitted
|
||||||
|
|
||||||
|
[2m2026-05-29 19:17:31.748[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Shutdown initiated...
|
||||||
|
[2m2026-05-29 19:17:31.750[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ default] [2m---[0;39m [2m[lTaskExecutor-1][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-2 - Shutdown completed.
|
||||||
|
[2m2026-05-29 19:17:31.752[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36ma.f.i.c.s.m.TenantDatabaseUpgradeService[0;39m [2m:[0;39m Exception:
|
||||||
|
|
||||||
|
java.util.concurrent.ExecutionException: java.lang.RuntimeException: Exception while upgrading tenant default
|
||||||
|
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
|
||||||
|
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenants(TenantDatabaseUpgradeService.java:155)
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.afterPropertiesSet(TenantDatabaseUpgradeService.java:93)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:691)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:513)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1725)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1474)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:606)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1698)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:796)
|
||||||
|
at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1440)
|
||||||
|
at org.apache.fineract.infrastructure.core.jersey.JerseyConfig.setup(JerseyConfig.java:58)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMethod.invoke(InitDestroyAnnotationBeanPostProcessor.java:457)
|
||||||
|
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:401)
|
||||||
|
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:219)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:429)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1818)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:230)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:221)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:106)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:95)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:271)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:245)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52)
|
||||||
|
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4452)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
||||||
|
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||||
|
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
||||||
|
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
||||||
|
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
||||||
|
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:770)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
||||||
|
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||||
|
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
||||||
|
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
||||||
|
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
||||||
|
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:201)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:410)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:868)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:436)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:128)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:107)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:517)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:219)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:193)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:167)
|
||||||
|
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
|
||||||
|
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
|
||||||
|
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
|
||||||
|
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
|
||||||
|
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149)
|
||||||
|
at org.apache.fineract.ServerApplication.main(ServerApplication.java:57)
|
||||||
|
Caused by: java.lang.RuntimeException: Exception while upgrading tenant default
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:208)
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.lambda$upgradeIndividualTenants$1(TenantDatabaseUpgradeService.java:146)
|
||||||
|
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||||
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
|
||||||
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
Caused by: liquibase.exception.LiquibaseException: liquibase.exception.CommandExecutionException: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
||||||
|
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
||||||
|
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:289)
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:205)
|
||||||
|
... 5 common frames omitted
|
||||||
|
Caused by: liquibase.exception.CommandExecutionException: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
||||||
|
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
||||||
|
at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:310)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:201)
|
||||||
|
at liquibase.command.CommandScope.execute(CommandScope.java:251)
|
||||||
|
at liquibase.Liquibase.lambda$update$0(Liquibase.java:216)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.Liquibase.runInScope(Liquibase.java:1366)
|
||||||
|
at liquibase.Liquibase.update(Liquibase.java:205)
|
||||||
|
at liquibase.Liquibase.update(Liquibase.java:188)
|
||||||
|
at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:327)
|
||||||
|
at liquibase.integration.spring.SpringLiquibase.lambda$afterPropertiesSet$0(SpringLiquibase.java:279)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:272)
|
||||||
|
... 6 common frames omitted
|
||||||
|
Caused by: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
||||||
|
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
||||||
|
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:155)
|
||||||
|
at liquibase.command.core.AbstractUpdateCommandStep.lambda$run$0(AbstractUpdateCommandStep.java:114)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.command.core.AbstractUpdateCommandStep.run(AbstractUpdateCommandStep.java:112)
|
||||||
|
at liquibase.command.core.UpdateCommandStep.run(UpdateCommandStep.java:100)
|
||||||
|
at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:263)
|
||||||
|
... 24 common frames omitted
|
||||||
|
Caused by: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0309_phase3_jobs_and_recall_permission.xml::0309_imps_deemed_reconcile_job::fineract:
|
||||||
|
Reason: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
||||||
|
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:873)
|
||||||
|
at liquibase.changelog.visitor.UpdateVisitor.executeAcceptedChange(UpdateVisitor.java:127)
|
||||||
|
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:71)
|
||||||
|
at liquibase.changelog.ChangeLogIterator.lambda$run$0(ChangeLogIterator.java:133)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.changelog.ChangeLogIterator.lambda$run$1(ChangeLogIterator.java:122)
|
||||||
|
at liquibase.Scope.lambda$child$0(Scope.java:216)
|
||||||
|
at liquibase.Scope.child(Scope.java:225)
|
||||||
|
at liquibase.Scope.child(Scope.java:215)
|
||||||
|
at liquibase.Scope.child(Scope.java:194)
|
||||||
|
at liquibase.Scope.child(Scope.java:282)
|
||||||
|
at liquibase.Scope.child(Scope.java:286)
|
||||||
|
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:91)
|
||||||
|
... 32 common frames omitted
|
||||||
|
Caused by: liquibase.exception.DatabaseException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null). [Failed SQL: (0) INSERT INTO public.job (name, display_name, cron_expression, create_time, task_priority, group_name, previous_run_start_time, job_key, initializing_errorlog, is_active, currently_running, updates_allowed, scheduler_group, is_misfired, node_id, is_mismatched_job) VALUES ('IMPS DEEMED Reconciliation', 'IMPS DEEMED Reconciliation', '0/30 * * * * ?', NOW(), 5, NULL, NULL, 'IMPS DEEMED ReconciliationDetail1 _ DEFAULT', NULL, FALSE, FALSE, TRUE, 0, FALSE, 1, TRUE)]
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:520)
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:85)
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:188)
|
||||||
|
at liquibase.executor.AbstractExecutor.execute(AbstractExecutor.java:148)
|
||||||
|
at liquibase.database.AbstractJdbcDatabase.executeStatements(AbstractJdbcDatabase.java:1198)
|
||||||
|
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:816)
|
||||||
|
... 47 common frames omitted
|
||||||
|
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "short_name" of relation "job" violates not-null constraint
|
||||||
|
Detail: Failing row contains (45, IMPS DEEMED Reconciliation, IMPS DEEMED Reconciliation, 0/30 * * * * ?, 2026-05-29 19:17:31.688453, 5, null, null, null, IMPS DEEMED ReconciliationDetail1 _ DEFAULT, null, f, f, t, 0, f, 1, t, null).
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2736)
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2421)
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:525)
|
||||||
|
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:435)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:357)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:342)
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318)
|
||||||
|
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:313)
|
||||||
|
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95)
|
||||||
|
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java)
|
||||||
|
at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:512)
|
||||||
|
... 52 common frames omitted
|
||||||
|
|
||||||
|
[2m2026-05-29 19:17:31.761[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.b.web.embedded.tomcat.TomcatStarter [0;39m [2m:[0;39m Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'jerseyConfig': Invocation of init method failed
|
||||||
|
[2m2026-05-29 19:17:31.812[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.apache.catalina.core.StandardService [0;39m [2m:[0;39m Stopping service [Tomcat]
|
||||||
|
[2m2026-05-29 19:17:31.817[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.a.c.loader.WebappClassLoaderBase [0;39m [2m:[0;39m The web application [fineract-provider] appears to have started a thread named [HikariPool-1:housekeeper] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
|
||||||
|
java.base/jdk.internal.misc.Unsafe.park(Native Method)
|
||||||
|
java.base/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:269)
|
||||||
|
java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1797)
|
||||||
|
java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182)
|
||||||
|
java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899)
|
||||||
|
java.base/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1070)
|
||||||
|
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
|
||||||
|
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
||||||
|
java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
[2m2026-05-29 19:17:31.820[0;39m [33m- WARN[0;39m [35m7760[0;39m [33m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mConfigServletWebServerApplicationContext[0;39m [2m:[0;39m Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server
|
||||||
|
[2m2026-05-29 19:17:31.820[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Shutdown initiated...
|
||||||
|
[2m2026-05-29 19:17:31.822[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Shutdown completed.
|
||||||
|
[2m2026-05-29 19:17:31.847[0;39m [32m- INFO[0;39m [35m7760[0;39m [32m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36m.s.b.a.l.ConditionEvaluationReportLogger[0;39m [2m:[0;39m
|
||||||
|
|
||||||
|
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
|
||||||
|
[2m2026-05-29 19:17:31.875[0;39m [31m-ERROR[0;39m [35m7760[0;39m [31m[0;39m [ no-tenant] [2m---[0;39m [2m[ main][0;39m [36mo.s.boot.SpringApplication [0;39m [2m:[0;39m Application run failed
|
||||||
|
|
||||||
|
org.springframework.context.ApplicationContextException: Unable to start web server
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:170)
|
||||||
|
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
|
||||||
|
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
|
||||||
|
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
|
||||||
|
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
|
||||||
|
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149)
|
||||||
|
at org.apache.fineract.ServerApplication.main(ServerApplication.java:57)
|
||||||
|
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:147)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:107)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:517)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:219)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:193)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:167)
|
||||||
|
... 7 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'jerseyConfig': Invocation of init method failed
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:230)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:221)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:106)
|
||||||
|
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:95)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:271)
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:245)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52)
|
||||||
|
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4452)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
||||||
|
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||||
|
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
||||||
|
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
||||||
|
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
||||||
|
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:770)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201)
|
||||||
|
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191)
|
||||||
|
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||||
|
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81)
|
||||||
|
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145)
|
||||||
|
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747)
|
||||||
|
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:201)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:410)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:868)
|
||||||
|
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164)
|
||||||
|
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:436)
|
||||||
|
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:128)
|
||||||
|
... 12 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jerseyConfig': Invocation of init method failed
|
||||||
|
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:222)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:429)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1818)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
... 61 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'journalEntriesApiResource' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\accounting\journalentry\api\JournalEntriesApiResource.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'springSecurityPlatformSecurityContext' defined in URL [jar:file:/C:/Users/adity/Music/CBS/fineract/fineract-security/build/libs/fineract-security-1.15.0-SNAPSHOT.jar!/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'configurationDomainServiceJpa' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:796)
|
||||||
|
at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1440)
|
||||||
|
at org.apache.fineract.infrastructure.core.jersey.JerseyConfig.setup(JerseyConfig.java:58)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMethod.invoke(InitDestroyAnnotationBeanPostProcessor.java:457)
|
||||||
|
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:401)
|
||||||
|
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:219)
|
||||||
|
... 74 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springSecurityPlatformSecurityContext' defined in URL [jar:file:/C:/Users/adity/Music/CBS/fineract/fineract-security/build/libs/fineract-security-1.15.0-SNAPSHOT.jar!/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'configurationDomainServiceJpa' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
... 91 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configurationDomainServiceJpa' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
... 105 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:377)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1725)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1474)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:606)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1698)
|
||||||
|
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
|
||||||
|
... 119 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaSharedEM_entityManagerFactory': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:377)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:691)
|
||||||
|
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:513)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
||||||
|
... 132 common frames omitted
|
||||||
|
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tenantDatabaseUpgradeService' defined in file [C:\Users\adity\Music\CBS\fineract\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\core\service\migration\TenantDatabaseUpgradeService.class]: Tenant upgrades had exceptions
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1826)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
|
||||||
|
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
|
||||||
|
... 144 common frames omitted
|
||||||
|
Caused by: java.lang.RuntimeException: Tenant upgrades had exceptions
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenants(TenantDatabaseUpgradeService.java:167)
|
||||||
|
at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.afterPropertiesSet(TenantDatabaseUpgradeService.java:93)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873)
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822)
|
||||||
|
... 153 common frames omitted
|
||||||
|
|
||||||
|
|
||||||
|
> Task :fineract-provider:bootRun FAILED
|
||||||
|
|
||||||
|
[Incubating] Problems report is available at: file:///C:/Users/adity/Music/CBS/fineract/build/reports/problems/problems-report.html
|
||||||
|
|
||||||
|
FAILURE: Build failed with an exception.
|
||||||
|
|
||||||
|
* What went wrong:
|
||||||
|
Execution failed for task ':fineract-provider:bootRun'.
|
||||||
|
> Process 'command 'C:\Program Files\Eclipse Adoptium\jdk-21.0.9.10-hotspot\bin\java.exe'' finished with non-zero exit value 1
|
||||||
|
|
||||||
|
* Try:
|
||||||
|
> Run with --stacktrace option to get the stack trace.
|
||||||
|
> Run with --info or --debug option to get more log output.
|
||||||
|
> Get more help at https://help.gradle.org.
|
||||||
|
|
||||||
|
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
|
||||||
|
|
||||||
|
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
|
||||||
|
|
||||||
|
For more on this, please refer to https://docs.gradle.org/8.14.3/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
|
||||||
|
|
||||||
|
BUILD FAILED in 1m 30s
|
||||||
|
83 actionable tasks: 5 executed, 78 up-to-date
|
||||||
|
Resource usage collection shutdown not awaited
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+11464
-14684
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+40386
-37900
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+21
@@ -600,6 +600,27 @@ public class AccountingProcessorHelper {
|
|||||||
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a direct transfer journal entry between two savings-type accounts (savings or current),
|
||||||
|
* each with their own product GL mapping and entity ID so the correct account number appears per entry.
|
||||||
|
*
|
||||||
|
* @param debitProductId product ID whose SAVINGS_CONTROL GL is debited
|
||||||
|
* @param debitEntityId entity (account) ID shown on the debit entry
|
||||||
|
* @param creditProductId product ID whose SAVINGS_CONTROL GL is credited
|
||||||
|
* @param creditEntityId entity (account) ID shown on the credit entry
|
||||||
|
*/
|
||||||
|
public void createDirectTransferJournalEntriesForSavings(final Office office, final String currencyCode,
|
||||||
|
final Long debitProductId, final Long debitEntityId,
|
||||||
|
final Long creditProductId, final Long creditEntityId,
|
||||||
|
final String transactionId, final LocalDate transactionDate, final BigDecimal amount) {
|
||||||
|
final GLAccount debitAccount = getLinkedGLAccountForSavingsProduct(debitProductId,
|
||||||
|
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), null);
|
||||||
|
final GLAccount creditAccount = getLinkedGLAccountForSavingsProduct(creditProductId,
|
||||||
|
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), null);
|
||||||
|
createDebitJournalEntryForSavings(office, currencyCode, debitAccount, debitEntityId, transactionId, transactionDate, amount);
|
||||||
|
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, creditEntityId, transactionId, transactionDate, amount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method that creates a pair of related Debits and Credits for Cash Based accounting.
|
* Convenience method that creates a pair of related Debits and Credits for Cash Based accounting.
|
||||||
*
|
*
|
||||||
|
|||||||
+11
-8
@@ -106,8 +106,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
.append(" curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf, ")
|
.append(" curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf, ")
|
||||||
.append(" eao.external_id as externalAssetOwner, ")
|
.append(" eao.external_id as externalAssetOwner, ")
|
||||||
.append(" CASE ")
|
.append(" CASE ")
|
||||||
.append(" WHEN journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id LIKE 'CA%' THEN ca.account_no ")
|
.append(" WHEN journalEntry.entity_type_enum = 2 THEN COALESCE(ca.account_no, sa.account_no) ")
|
||||||
.append(" WHEN journalEntry.entity_type_enum = 2 THEN sa.account_no ")
|
|
||||||
.append(" ELSE NULL ")
|
.append(" ELSE NULL ")
|
||||||
.append(" END as savingsAccountNo ");
|
.append(" END as savingsAccountNo ");
|
||||||
if (associationParametersData.isRunningBalanceRequired()) {
|
if (associationParametersData.isRunningBalanceRequired()) {
|
||||||
@@ -130,8 +129,8 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
.append(" join m_currency curr on curr.code = journalEntry.currency_code ")
|
.append(" join m_currency curr on curr.code = journalEntry.currency_code ")
|
||||||
.append(" left join m_external_asset_owner_journal_entry_mapping eajem on eajem.journal_entry_id = journalEntry.id ")
|
.append(" left join m_external_asset_owner_journal_entry_mapping eajem on eajem.journal_entry_id = journalEntry.id ")
|
||||||
.append(" left join m_external_asset_owner eao on eao.id = eajem.owner_id ")
|
.append(" left join m_external_asset_owner eao on eao.id = eajem.owner_id ")
|
||||||
.append(" left join m_savings_account sa on sa.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id NOT LIKE 'CA%' ")
|
.append(" left join m_savings_account sa on sa.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 ")
|
||||||
.append(" left join m_current_account ca on ca.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id LIKE 'CA%' ");
|
.append(" left join m_current_account ca on ca.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 ");
|
||||||
if (associationParametersData.isTransactionDetailsRequired()) {
|
if (associationParametersData.isTransactionDetailsRequired()) {
|
||||||
sb.append(" left join m_loan_transaction as lt on journalEntry.loan_transaction_id = lt.id ")
|
sb.append(" left join m_loan_transaction as lt on journalEntry.loan_transaction_id = lt.id ")
|
||||||
.append(" left join m_savings_account_transaction as st on journalEntry.savings_transaction_id = st.id ")
|
.append(" left join m_savings_account_transaction as st on journalEntry.savings_transaction_id = st.id ")
|
||||||
@@ -265,6 +264,10 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
int arrayPos = 0;
|
int arrayPos = 0;
|
||||||
String whereClose = " where ";
|
String whereClose = " where ";
|
||||||
|
|
||||||
|
// Exclude intermediary/reversed entries from all GL views
|
||||||
|
sqlBuilder.append(whereClose).append("(journalEntry.reversed IS NULL OR journalEntry.reversed = false)");
|
||||||
|
whereClose = " and ";
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(transactionId)) {
|
if (StringUtils.isNotBlank(transactionId)) {
|
||||||
sqlBuilder.append(whereClose).append(" journalEntry.transaction_id = ?");
|
sqlBuilder.append(whereClose).append(" journalEntry.transaction_id = ?");
|
||||||
objectArray[arrayPos] = transactionId;
|
objectArray[arrayPos] = transactionId;
|
||||||
@@ -581,7 +584,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
"SELECT COALESCE(SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE 0 END), 0) AS ob_debit, "
|
"SELECT COALESCE(SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE 0 END), 0) AS ob_debit, "
|
||||||
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS ob_credit "
|
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS ob_credit "
|
||||||
+ "FROM acc_gl_journal_entry je "
|
+ "FROM acc_gl_journal_entry je "
|
||||||
+ "WHERE je.account_id = ? AND je.entry_date < ?");
|
+ "WHERE je.account_id = ? AND je.entry_date < ? AND (je.reversed IS NULL OR je.reversed = false)");
|
||||||
obParams.add(glAccountId);
|
obParams.add(glAccountId);
|
||||||
obParams.add(fromDate);
|
obParams.add(fromDate);
|
||||||
if (officeId != null && officeId > 0) {
|
if (officeId != null && officeId > 0) {
|
||||||
@@ -608,7 +611,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS dw_credit, "
|
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS dw_credit, "
|
||||||
+ "COUNT(*) AS dw_count "
|
+ "COUNT(*) AS dw_count "
|
||||||
+ "FROM acc_gl_journal_entry je "
|
+ "FROM acc_gl_journal_entry je "
|
||||||
+ "WHERE 1=1");
|
+ "WHERE (je.reversed IS NULL OR je.reversed = false)");
|
||||||
if (glAccountId != null && glAccountId > 0) {
|
if (glAccountId != null && glAccountId > 0) {
|
||||||
dwSql.append(" AND je.account_id = ?");
|
dwSql.append(" AND je.account_id = ?");
|
||||||
dwParams.add(glAccountId);
|
dwParams.add(glAccountId);
|
||||||
@@ -684,7 +687,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
final StringBuilder totSql = new StringBuilder(
|
final StringBuilder totSql = new StringBuilder(
|
||||||
"SELECT COALESCE(SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE 0 END), 0) AS totalDebit, "
|
"SELECT COALESCE(SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE 0 END), 0) AS totalDebit, "
|
||||||
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS totalCredit "
|
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS totalCredit "
|
||||||
+ "FROM acc_gl_journal_entry je WHERE 1=1");
|
+ "FROM acc_gl_journal_entry je WHERE (je.reversed IS NULL OR je.reversed = false)");
|
||||||
if (glAccountId != null && glAccountId > 0) {
|
if (glAccountId != null && glAccountId > 0) {
|
||||||
totSql.append(" AND je.account_id = ?");
|
totSql.append(" AND je.account_id = ?");
|
||||||
totParams.add(glAccountId);
|
totParams.add(glAccountId);
|
||||||
@@ -723,7 +726,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
{
|
{
|
||||||
final List<Object> cntParams = new ArrayList<>();
|
final List<Object> cntParams = new ArrayList<>();
|
||||||
final StringBuilder cntSql = new StringBuilder(
|
final StringBuilder cntSql = new StringBuilder(
|
||||||
"SELECT COUNT(*) FROM acc_gl_journal_entry je WHERE 1=1");
|
"SELECT COUNT(*) FROM acc_gl_journal_entry je WHERE (je.reversed IS NULL OR je.reversed = false)");
|
||||||
if (glAccountId != null && glAccountId > 0) {
|
if (glAccountId != null && glAccountId > 0) {
|
||||||
cntSql.append(" AND je.account_id = ?");
|
cntSql.append(" AND je.account_id = ?");
|
||||||
cntParams.add(glAccountId);
|
cntParams.add(glAccountId);
|
||||||
|
|||||||
+8
@@ -146,6 +146,14 @@ public class SecurityConfig {
|
|||||||
auth.requestMatchers(API_MATCHER.matcher(HttpMethod.OPTIONS, "/api/**")).permitAll()
|
auth.requestMatchers(API_MATCHER.matcher(HttpMethod.OPTIONS, "/api/**")).permitAll()
|
||||||
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/echo")).permitAll()
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/echo")).permitAll()
|
||||||
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/authentication")).permitAll()
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/authentication")).permitAll()
|
||||||
|
// Meeting vote public endpoints — secured by UUID token in the URL path
|
||||||
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/public/meeting-vote/**")).permitAll()
|
||||||
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/public/meeting-vote/**")).permitAll()
|
||||||
|
// IMPS receiver — Fineract acts as the bank-side endpoint under
|
||||||
|
// IMPS_BANK_API_SPEC_CBS_1.0 §6.2.x. LincPay (the switch) calls these without
|
||||||
|
// a Fineract Bearer token; security is enforced at the JWT envelope
|
||||||
|
// (signature + encryption) by ImpsJwtUtil / ImpsCryptoUtil inside the handler.
|
||||||
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/imps/**")).permitAll()
|
||||||
// Login OTP — same posture as PAN/GSTIN public verification: provider-credentialed,
|
// Login OTP — same posture as PAN/GSTIN public verification: provider-credentialed,
|
||||||
// no Fineract auth required.
|
// no Fineract auth required.
|
||||||
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/otp/generate")).permitAll()
|
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/otp/generate")).permitAll()
|
||||||
|
|||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.api;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.imps.crypto.ImpsCryptoUtil;
|
||||||
|
import org.apache.fineract.infrastructure.imps.crypto.ImpsEnvelope;
|
||||||
|
import org.apache.fineract.infrastructure.imps.crypto.ImpsJwtUtil;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsRequestData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsResponseData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.service.ImpsCbsService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receiver-side IMPS CBS endpoints — Fineract acting as the bank under
|
||||||
|
* IMPS_BANK_API_SPEC_CBS_1.0 §6. Each endpoint:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Reads the {@link ImpsEnvelope} ({@code data}, {@code signature}).</li>
|
||||||
|
* <li>Verifies the inbound JWT via {@link ImpsJwtUtil}.</li>
|
||||||
|
* <li>Decrypts {@code data} via {@link ImpsCryptoUtil}.</li>
|
||||||
|
* <li>Hands the typed {@link ImpsRequestData} to {@link ImpsCbsService}.</li>
|
||||||
|
* <li>Encrypts the response, signs an outbound JWT, returns the envelope.</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
@Path("/v1/imps")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "IMPS CBS (Bank Side)", description = "Receiver-side endpoints for IMPS_BANK_API_SPEC_CBS_1.0 §6.2.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ImpsCbsApiResource {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ImpsCbsApiResource.class);
|
||||||
|
|
||||||
|
private final ImpsCryptoUtil cryptoUtil;
|
||||||
|
private final ImpsJwtUtil jwtUtil;
|
||||||
|
private final ImpsCbsService cbsService;
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/ReqPay/v1/urn:txnid:{txnid}")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "IMPS ReqPay (§6.2.1)")
|
||||||
|
public Response reqPay(@PathParam("txnid") String txnid, ImpsEnvelope body) {
|
||||||
|
return handle("ReqPay", txnid, body, cbsService::reqPay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/ReqValAdd/v1/urn:txnid:{txnid}")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "IMPS ReqValAdd (§6.2.3)")
|
||||||
|
public Response reqValAdd(@PathParam("txnid") String txnid, ImpsEnvelope body) {
|
||||||
|
return handle("ReqValAdd", txnid, body, cbsService::reqValAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/ReqChkTxn/v1/urn:txnid:{txnid}")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Operation(summary = "IMPS ReqChkTxn (§6.2.2)")
|
||||||
|
public Response reqChkTxn(@PathParam("txnid") String txnid, ImpsEnvelope body) {
|
||||||
|
return handle("ReqChkTxn", txnid, body, cbsService::reqChkTxn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response handle(String api, String txnid, ImpsEnvelope body, Function<ImpsRequestData, ImpsResponseData> handler) {
|
||||||
|
log.info("[ImpsCbs] Inbound {} txnid={}", api, txnid);
|
||||||
|
try {
|
||||||
|
if (!jwtUtil.verifyJwtSignature(body.getSignature())) {
|
||||||
|
log.warn("[ImpsCbs] {} txnid={} — JWT verification FAILED", api, txnid);
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(new ImpsEnvelope("invalid-signature", "")).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
String decrypted = cryptoUtil.decryptJson(body.getData());
|
||||||
|
if (decrypted == null) {
|
||||||
|
log.warn("[ImpsCbs] {} txnid={} — decrypt FAILED", api, txnid);
|
||||||
|
return Response.status(Response.Status.BAD_REQUEST).entity(new ImpsEnvelope("decrypt-failed", "")).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImpsRequestData request = objectMapper.readValue(decrypted, ImpsRequestData.class);
|
||||||
|
ImpsResponseData response = handler.apply(request);
|
||||||
|
String respJson = objectMapper.writeValueAsString(response);
|
||||||
|
log.info("[ImpsCbs] {} txnid={} responding status={} responseCode={}", api, txnid, response.getStatus(),
|
||||||
|
response.getResponseCode());
|
||||||
|
|
||||||
|
String encResp = cryptoUtil.encryptJson(respJson);
|
||||||
|
String jwt = jwtUtil.signPayloadAsJwt(respJson);
|
||||||
|
return Response.ok(new ImpsEnvelope(encResp, jwt)).build();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[ImpsCbs] {} txnid={} unhandled error: {}", api, txnid, e.toString(), e);
|
||||||
|
return Response.serverError().entity(new ImpsEnvelope("error: " + e.getMessage(), "")).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.client;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import org.apache.fineract.infrastructure.imps.crypto.ImpsCryptoUtil;
|
||||||
|
import org.apache.fineract.infrastructure.imps.crypto.ImpsEnvelope;
|
||||||
|
import org.apache.fineract.infrastructure.imps.crypto.ImpsJwtUtil;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsRequestData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsResponseData;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outbound IMPS calls — Fineract acting as the bank initiating ReqPay / ReqValAdd /
|
||||||
|
* ReqChkTxn against the LincPay switch.
|
||||||
|
*
|
||||||
|
* <p>Each call: build {@link ImpsRequestData} → encrypt with LincPay public key →
|
||||||
|
* sign JWT with CBS private key → POST envelope → verify response JWT → decrypt
|
||||||
|
* response body → parse to {@link ImpsResponseData}.
|
||||||
|
*
|
||||||
|
* <p>The crypto utilities are symmetric across inbound and outbound: encryptJson
|
||||||
|
* uses the peer's public key (LincPay), decryptJson uses our private key (CBS).
|
||||||
|
* No additional key configuration is needed beyond what's already set for the
|
||||||
|
* inbound receiver.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ImpsSwitchClient {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ImpsSwitchClient.class);
|
||||||
|
private static final MediaType JSON = MediaType.get("application/json");
|
||||||
|
|
||||||
|
@Value("${fineract.imps.switch.base-url:}")
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
@Value("${fineract.imps.switch.timeout-ms:10000}")
|
||||||
|
private int timeoutMs;
|
||||||
|
|
||||||
|
private final ImpsCryptoUtil cryptoUtil;
|
||||||
|
private final ImpsJwtUtil jwtUtil;
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private OkHttpClient httpClient;
|
||||||
|
|
||||||
|
public ImpsSwitchClient(ImpsCryptoUtil cryptoUtil, ImpsJwtUtil jwtUtil) {
|
||||||
|
this.cryptoUtil = cryptoUtil;
|
||||||
|
this.jwtUtil = jwtUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OkHttpClient client() {
|
||||||
|
if (httpClient == null) {
|
||||||
|
httpClient = new OkHttpClient.Builder().connectTimeout(Duration.ofMillis(timeoutMs))
|
||||||
|
.readTimeout(timeoutMs, TimeUnit.MILLISECONDS).writeTimeout(timeoutMs, TimeUnit.MILLISECONDS).build();
|
||||||
|
}
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpsResponseData reqPay(String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke("IMPS", "ReqPay", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpsResponseData reqValAdd(String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke("IMPS", "ReqValAdd", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpsResponseData reqChkTxn(String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke("IMPS", "ReqChkTxn", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpsResponseData neftPay(String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke("NEFT", "ReqPay", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpsResponseData neftChkTxn(String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke("NEFT", "ReqChkTxn", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpsResponseData rtgsPay(String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke("RTGS", "ReqPay", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpsResponseData rtgsChkTxn(String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke("RTGS", "ReqChkTxn", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Best-effort recall on the switch. Real-world semantics vary by channel:
|
||||||
|
* IMPS uses a dispute / chargeback flow rather than a synchronous recall;
|
||||||
|
* NEFT supports {@code ReqDirectRet} for return-on-no-credit; RTGS recall
|
||||||
|
* is rare and usually manual. Callers should treat exceptions as soft
|
||||||
|
* failures and proceed with local reversal.
|
||||||
|
*/
|
||||||
|
public ImpsResponseData recall(String channel, String txnId, ImpsRequestData payload) {
|
||||||
|
return invoke(channel, "ReqRecall", txnId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImpsResponseData invoke(String channel, String api, String txnId, ImpsRequestData payload) {
|
||||||
|
if (baseUrl == null || baseUrl.isBlank()) {
|
||||||
|
throw new IllegalStateException("fineract.imps.switch.base-url is not set");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String plainBody = objectMapper.writeValueAsString(payload);
|
||||||
|
String encrypted = cryptoUtil.encryptJson(plainBody);
|
||||||
|
String signature = jwtUtil.signPayloadAsJwt(plainBody);
|
||||||
|
ImpsEnvelope env = new ImpsEnvelope(encrypted, signature);
|
||||||
|
String envJson = objectMapper.writeValueAsString(env);
|
||||||
|
|
||||||
|
String url = baseUrl.replaceAll("/+$", "") + "/" + channel + "/" + api + "/v1/urn:txnid:" + txnId;
|
||||||
|
log.info("[ImpsSwitchClient] {} {} txnId={} → POST {}", channel, api, txnId, url);
|
||||||
|
|
||||||
|
Request req = new Request.Builder().url(url).post(RequestBody.create(envJson, JSON)).build();
|
||||||
|
try (Response resp = client().newCall(req).execute()) {
|
||||||
|
String respBody = bodyAsString(resp.body());
|
||||||
|
if (!resp.isSuccessful()) {
|
||||||
|
log.warn("[ImpsSwitchClient] {} {} txnId={} HTTP {} body={}", channel, api, txnId, resp.code(), respBody);
|
||||||
|
throw new IOException("Switch returned HTTP " + resp.code());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImpsEnvelope respEnv = objectMapper.readValue(respBody, ImpsEnvelope.class);
|
||||||
|
if (!jwtUtil.verifyJwtSignature(respEnv.getSignature())) {
|
||||||
|
throw new IllegalStateException("Response JWT signature verification failed");
|
||||||
|
}
|
||||||
|
String decrypted = cryptoUtil.decryptJson(respEnv.getData());
|
||||||
|
if (decrypted == null) {
|
||||||
|
throw new IllegalStateException("Response payload decrypt failed");
|
||||||
|
}
|
||||||
|
log.info("[ImpsSwitchClient] {} {} txnId={} ← decrypted response", channel, api, txnId);
|
||||||
|
return objectMapper.readValue(decrypted, ImpsResponseData.class);
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException(channel + " " + api + " call failed: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String bodyAsString(ResponseBody body) throws IOException {
|
||||||
|
return body == null ? "" : body.string();
|
||||||
|
}
|
||||||
|
}
|
||||||
+249
@@ -0,0 +1,249 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.crypto;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.GCMParameterSpec;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encryption / decryption matching IMPS_BANK_API_SPEC_CBS_1.0 §4.3.
|
||||||
|
*
|
||||||
|
* <p>Wire format: {@code <base64(wrappedAESKey)>:<base64(iv)>:<base64(cipherText)>}.
|
||||||
|
* AES-256-GCM with a 12-byte IV and a 128-bit tag; the AES key is wrapped via
|
||||||
|
* RSA-OAEP-SHA256.
|
||||||
|
*
|
||||||
|
* <h3>Key directions</h3>
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Inbound</b>: {@link #decryptJson(String)} — unwrap the AES key with
|
||||||
|
* the CBS private key loaded from {@code fineract.imps.cbs.private-key-path}.</li>
|
||||||
|
* <li><b>Outbound</b>: {@link #encryptJson(String)} — wrap the AES key with
|
||||||
|
* the LincPay public key loaded from
|
||||||
|
* {@code fineract.imps.lincpay.public-key-path}.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Keys are loaded once at startup in {@link #init()}.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ImpsCryptoUtil {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ImpsCryptoUtil.class);
|
||||||
|
|
||||||
|
private static final String AES = "AES";
|
||||||
|
private static final String AES_GCM = "AES/GCM/NoPadding";
|
||||||
|
private static final String RSA = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
|
||||||
|
private static final int GCM_TAG_LENGTH = 128;
|
||||||
|
private static final int AES_KEY_SIZE = 256;
|
||||||
|
private static final int IV_SIZE = 12;
|
||||||
|
|
||||||
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
@Value("${fineract.imps.cbs.private-key-path:}")
|
||||||
|
private String cbsPrivateKeyPath;
|
||||||
|
|
||||||
|
@Value("${fineract.imps.cbs.private-key-password:}")
|
||||||
|
private String cbsPrivateKeyPassword;
|
||||||
|
|
||||||
|
@Value("${fineract.imps.lincpay.public-key-path:}")
|
||||||
|
private String lincpayPublicKeyPath;
|
||||||
|
|
||||||
|
private PrivateKey cbsPrivateKey;
|
||||||
|
private PublicKey lincpayPublicKey;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
void init() {
|
||||||
|
if (cbsPrivateKeyPath == null || cbsPrivateKeyPath.isBlank()) {
|
||||||
|
log.info("[ImpsCryptoUtil] fineract.imps.cbs.private-key-path not set — CBS private key NOT loaded");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
cbsPrivateKey = loadFirstPrivateKey(cbsPrivateKeyPath, cbsPrivateKeyPassword);
|
||||||
|
log.info("[ImpsCryptoUtil] CBS private key loaded from {}", cbsPrivateKeyPath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[ImpsCryptoUtil] Failed to load CBS private key from {}: {}", cbsPrivateKeyPath, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lincpayPublicKeyPath == null || lincpayPublicKeyPath.isBlank()) {
|
||||||
|
log.info("[ImpsCryptoUtil] fineract.imps.lincpay.public-key-path not set — LincPay public key NOT loaded");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
lincpayPublicKey = loadPublicKey(lincpayPublicKeyPath);
|
||||||
|
log.info("[ImpsCryptoUtil] LincPay public key loaded from {}", lincpayPublicKeyPath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[ImpsCryptoUtil] Failed to load LincPay public key from {}: {}", lincpayPublicKeyPath, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decrypt inbound payload using the CBS private key. */
|
||||||
|
public String decryptJson(String encryptedPayload) {
|
||||||
|
if (cbsPrivateKey == null) {
|
||||||
|
throw new IllegalStateException("CBS private key not loaded — check fineract.imps.cbs.private-key-path");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String[] parts = encryptedPayload.split(":");
|
||||||
|
if (parts.length != 3) {
|
||||||
|
log.error("Invalid encrypted payload format — expected 3 colon-separated parts");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] iv = Base64.getDecoder().decode(parts[1]);
|
||||||
|
SecretKey aesKey = unwrapAESKey(parts[0], cbsPrivateKey);
|
||||||
|
if (aesKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return decryptData(parts[2], aesKey, iv);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error decrypting inbound payload", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Encrypt outbound payload using the LincPay public key. */
|
||||||
|
public String encryptJson(String json) {
|
||||||
|
if (lincpayPublicKey == null) {
|
||||||
|
throw new IllegalStateException("LincPay public key not loaded — check fineract.imps.lincpay.public-key-path");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SecretKey aesKey = generateAESKey();
|
||||||
|
if (aesKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] iv = generateIV();
|
||||||
|
String cipherText = encryptData(json, aesKey, iv);
|
||||||
|
String wrappedKey = wrapAESKey(aesKey, lincpayPublicKey);
|
||||||
|
if (cipherText == null || wrappedKey == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return wrappedKey + ":" + Base64.getEncoder().encodeToString(iv) + ":" + cipherText;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error encrypting outbound payload", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Exposed for {@link ImpsJwtUtil} — sign outbound JWT with the same CBS private key. */
|
||||||
|
public PrivateKey getCbsPrivateKey() {
|
||||||
|
return cbsPrivateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Exposed for {@link ImpsJwtUtil} — verify inbound JWT with the LincPay public key. */
|
||||||
|
public PublicKey getLincpayPublicKey() {
|
||||||
|
return lincpayPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SecretKey generateAESKey() throws Exception {
|
||||||
|
KeyGenerator keyGen = KeyGenerator.getInstance(AES);
|
||||||
|
keyGen.init(AES_KEY_SIZE);
|
||||||
|
return keyGen.generateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] generateIV() {
|
||||||
|
byte[] iv = new byte[IV_SIZE];
|
||||||
|
SECURE_RANDOM.nextBytes(iv);
|
||||||
|
return iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String wrapAESKey(SecretKey aesKey, PublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA);
|
||||||
|
cipher.init(Cipher.WRAP_MODE, publicKey);
|
||||||
|
return Base64.getEncoder().encodeToString(cipher.wrap(aesKey));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error wrapping AES key", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SecretKey unwrapAESKey(String wrappedBase64, PrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
byte[] wrapped = Base64.getDecoder().decode(wrappedBase64);
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA);
|
||||||
|
cipher.init(Cipher.UNWRAP_MODE, privateKey);
|
||||||
|
return (SecretKey) cipher.unwrap(wrapped, AES, Cipher.SECRET_KEY);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error unwrapping AES key", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String encryptData(String plain, SecretKey aesKey, byte[] iv) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(AES_GCM);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
|
||||||
|
byte[] enc = cipher.doFinal(plain.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64.getEncoder().encodeToString(enc);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error encrypting data", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String decryptData(String encBase64, SecretKey aesKey, byte[] iv) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(AES_GCM);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(GCM_TAG_LENGTH, iv));
|
||||||
|
byte[] dec = cipher.doFinal(Base64.getDecoder().decode(encBase64));
|
||||||
|
return new String(dec, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error decrypting data", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PrivateKey loadFirstPrivateKey(String p12Path, String password) throws Exception {
|
||||||
|
char[] pwd = password != null ? password.toCharArray() : null;
|
||||||
|
try (FileInputStream fis = new FileInputStream(p12Path)) {
|
||||||
|
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||||
|
ks.load(fis, pwd);
|
||||||
|
Enumeration<String> aliases = ks.aliases();
|
||||||
|
while (aliases.hasMoreElements()) {
|
||||||
|
String alias = aliases.nextElement();
|
||||||
|
if (ks.isKeyEntry(alias)) {
|
||||||
|
return (PrivateKey) ks.getKey(alias, pwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("No private-key entry in keystore at " + p12Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PublicKey loadPublicKey(String crtPath) throws Exception {
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
try (InputStream is = new BufferedInputStream(new FileInputStream(crtPath))) {
|
||||||
|
Certificate cert = cf.generateCertificate(is);
|
||||||
|
return cert.getPublicKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.crypto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wire envelope shared with the LincPay IMPS gateway:
|
||||||
|
* {@code { "data": "<encrypted JSON>", "signature": "<RS256 JWT>" } }.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class ImpsEnvelope {
|
||||||
|
|
||||||
|
private String data;
|
||||||
|
private String signature;
|
||||||
|
}
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.crypto;
|
||||||
|
|
||||||
|
import com.nimbusds.jose.JOSEObjectType;
|
||||||
|
import com.nimbusds.jose.JWSAlgorithm;
|
||||||
|
import com.nimbusds.jose.JWSHeader;
|
||||||
|
import com.nimbusds.jose.JWSSigner;
|
||||||
|
import com.nimbusds.jose.JWSVerifier;
|
||||||
|
import com.nimbusds.jose.crypto.RSASSASigner;
|
||||||
|
import com.nimbusds.jose.crypto.RSASSAVerifier;
|
||||||
|
import com.nimbusds.jwt.JWTClaimsSet;
|
||||||
|
import com.nimbusds.jwt.SignedJWT;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.util.Date;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT helpers for IMPS_BANK_API_SPEC_CBS_1.0 §4.2.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li><b>Verify</b> inbound LincPay-signed JWT using the LincPay public key
|
||||||
|
* loaded by {@link ImpsCryptoUtil}.</li>
|
||||||
|
* <li><b>Sign</b> outbound JWT (RS256, 1h TTL, payload in {@code "data"}
|
||||||
|
* claim) using the CBS private key loaded by {@link ImpsCryptoUtil}.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>{@link #verifyJwtSignature(String)} returns {@code true} when the LincPay
|
||||||
|
* public key is not configured — useful for smoke testing the encrypt/decrypt
|
||||||
|
* path before both certs are wired up.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ImpsJwtUtil {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ImpsJwtUtil.class);
|
||||||
|
|
||||||
|
private static final long JWT_TTL_MILLIS = 3600_000L;
|
||||||
|
|
||||||
|
private final ImpsCryptoUtil cryptoUtil;
|
||||||
|
|
||||||
|
public ImpsJwtUtil(ImpsCryptoUtil cryptoUtil) {
|
||||||
|
this.cryptoUtil = cryptoUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sign a JSON payload — payload carried in the {@code "data"} claim. */
|
||||||
|
public String signPayloadAsJwt(String jsonPayload) {
|
||||||
|
PrivateKey privateKey = cryptoUtil.getCbsPrivateKey();
|
||||||
|
if (privateKey == null) {
|
||||||
|
log.warn("[ImpsJwtUtil] CBS private key not loaded — outbound JWT signing SKIPPED");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JWSSigner signer = new RSASSASigner(privateKey);
|
||||||
|
JWTClaimsSet claims = new JWTClaimsSet.Builder().claim("data", jsonPayload).issueTime(new Date())
|
||||||
|
.expirationTime(new Date(System.currentTimeMillis() + JWT_TTL_MILLIS)).build();
|
||||||
|
SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT).build(), claims);
|
||||||
|
signedJWT.sign(signer);
|
||||||
|
return signedJWT.serialize();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[ImpsJwtUtil] Error signing outbound JWT: {}", e.toString(), e);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verify an inbound JWT against the LincPay public key. */
|
||||||
|
public boolean verifyJwtSignature(String jwtToken) {
|
||||||
|
if (jwtToken == null || jwtToken.isBlank()) {
|
||||||
|
log.warn("[ImpsJwtUtil] Inbound JWT is blank — verification skipped (signature missing)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PublicKey pub = cryptoUtil.getLincpayPublicKey();
|
||||||
|
if (pub == null) {
|
||||||
|
log.warn("[ImpsJwtUtil] LincPay public key not loaded — verification skipped (DEV MODE)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(pub instanceof RSAPublicKey rsaPub)) {
|
||||||
|
log.error("[ImpsJwtUtil] LincPay public key is not RSA (type={}) — verification FAILED", pub.getAlgorithm());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SignedJWT signedJWT = SignedJWT.parse(jwtToken);
|
||||||
|
JWSVerifier verifier = new RSASSAVerifier(rsaPub);
|
||||||
|
return signedJWT.verify(verifier);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[ImpsJwtUtil] Verification error: {}", e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/** Account block under {@code payer.account} / {@code payee.account}. */
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class AccountData {
|
||||||
|
|
||||||
|
private String accountNumber;
|
||||||
|
private String ifsc;
|
||||||
|
private String accountType;
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union of the decrypted inbound request fields across the three IMPS APIs
|
||||||
|
* (ReqPay §6.2.1, ReqValAdd §6.2.3, ReqChkTxn §6.2.2). Fields not present in a
|
||||||
|
* given API stay {@code null}.
|
||||||
|
*
|
||||||
|
* <p>Covers the fields the simulator extracted via {@code JsonNode} dotted paths.
|
||||||
|
* Extend with additional spec fields (e.g. {@code remarks}, {@code merchantCode},
|
||||||
|
* {@code signature}) as the IMPS_BANK_API_SPEC_CBS_1.0 §6 schemas are consulted.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class ImpsRequestData {
|
||||||
|
|
||||||
|
private String txnId;
|
||||||
|
private String orgTxnId;
|
||||||
|
private String refId;
|
||||||
|
private String utr;
|
||||||
|
private String amount;
|
||||||
|
private PartyData payer;
|
||||||
|
private PartyData payee;
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union of CBS response fields across RespPay / RespValAdd / RespChkTxn per
|
||||||
|
* IMPS_BANK_API_SPEC_CBS_1.0 §6.2.x. {@code @JsonInclude(NON_NULL)} so each API
|
||||||
|
* only emits the fields its spec section requires.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class ImpsResponseData {
|
||||||
|
|
||||||
|
private String txnId;
|
||||||
|
private String orgTxnId;
|
||||||
|
private String refId;
|
||||||
|
private String timeStamp;
|
||||||
|
private String utr;
|
||||||
|
private String amount;
|
||||||
|
private String status;
|
||||||
|
private String responseCode;
|
||||||
|
private String accountNumber;
|
||||||
|
private String ifsc;
|
||||||
|
private String maskName;
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.data;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/** Payer / payee block on an IMPS request. */
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class PartyData {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String mobile;
|
||||||
|
private AccountData account;
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Lob;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One row per inbound IMPS request received by Fineract acting as the CBS.
|
||||||
|
* Records audit + idempotency state — a retried {@code txnId} should resolve
|
||||||
|
* back to the same {@code status} / {@code responseCode} via {@code unique(txn_id, api)}.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_imps_transaction")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ImpsTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
/** Which spec endpoint produced this row — REQ_PAY / REQ_VAL_ADD / REQ_CHK_TXN. */
|
||||||
|
@Column(name = "api", nullable = false, length = 32)
|
||||||
|
private String api;
|
||||||
|
|
||||||
|
@Column(name = "txn_id", nullable = false, length = 64)
|
||||||
|
private String txnId;
|
||||||
|
|
||||||
|
@Column(name = "org_txn_id", length = 64)
|
||||||
|
private String orgTxnId;
|
||||||
|
|
||||||
|
@Column(name = "ref_id", length = 64)
|
||||||
|
private String refId;
|
||||||
|
|
||||||
|
@Column(name = "utr", length = 64)
|
||||||
|
private String utr;
|
||||||
|
|
||||||
|
@Column(name = "amount", length = 32)
|
||||||
|
private String amount;
|
||||||
|
|
||||||
|
@Column(name = "payee_account_number", length = 64)
|
||||||
|
private String payeeAccountNumber;
|
||||||
|
|
||||||
|
@Column(name = "payee_ifsc", length = 32)
|
||||||
|
private String payeeIfsc;
|
||||||
|
|
||||||
|
/** Resolved scenario — SUCCESS / DEEMED / FAILED. */
|
||||||
|
@Column(name = "status", length = 16)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** IMPS response code — 00 / 91 / XC / 8 / etc. */
|
||||||
|
@Column(name = "response_code", length = 8)
|
||||||
|
private String responseCode;
|
||||||
|
|
||||||
|
/** Decrypted request JSON, kept for audit / replay. */
|
||||||
|
@Lob
|
||||||
|
@Column(name = "request_json")
|
||||||
|
private String requestJson;
|
||||||
|
|
||||||
|
/** Outbound response JSON before encryption, kept for audit. */
|
||||||
|
@Lob
|
||||||
|
@Column(name = "response_json")
|
||||||
|
private String responseJson;
|
||||||
|
|
||||||
|
public static ImpsTransaction newRow(String api, String txnId) {
|
||||||
|
ImpsTransaction t = new ImpsTransaction();
|
||||||
|
t.api = api;
|
||||||
|
t.txnId = txnId;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.domain;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface ImpsTransactionRepository extends JpaRepository<ImpsTransaction, Long> {
|
||||||
|
|
||||||
|
Optional<ImpsTransaction> findFirstByApiAndTxnId(String api, String txnId);
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.service;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsRequestData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsResponseData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receiver-side IMPS handler. Each method takes the decrypted request and
|
||||||
|
* returns the unencrypted response body — the controller layer is responsible
|
||||||
|
* for envelope encryption / signing around these calls.
|
||||||
|
*/
|
||||||
|
public interface ImpsCbsService {
|
||||||
|
|
||||||
|
ImpsResponseData reqPay(ImpsRequestData request);
|
||||||
|
|
||||||
|
ImpsResponseData reqValAdd(ImpsRequestData request);
|
||||||
|
|
||||||
|
ImpsResponseData reqChkTxn(ImpsRequestData request);
|
||||||
|
}
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.imps.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.AccountData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsRequestData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.ImpsResponseData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.data.PartyData;
|
||||||
|
import org.apache.fineract.infrastructure.imps.domain.ImpsTransaction;
|
||||||
|
import org.apache.fineract.infrastructure.imps.domain.ImpsTransactionRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STUB implementation — returns hard-coded SUCCESS for every request and
|
||||||
|
* persists an audit row per call. Replace the {@link #buildResponse} body
|
||||||
|
* with real Fineract account lookup / debit-credit logic when that scope is
|
||||||
|
* agreed.
|
||||||
|
*
|
||||||
|
* <p>Idempotency: a retried {@code (api, txnId)} short-circuits to the
|
||||||
|
* previously-recorded response instead of running the handler twice.
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ImpsCbsServiceImpl implements ImpsCbsService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ImpsCbsServiceImpl.class);
|
||||||
|
private static final DateTimeFormatter ISO_OFFSET = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
||||||
|
|
||||||
|
private final ImpsTransactionRepository repository;
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ImpsResponseData reqPay(ImpsRequestData request) {
|
||||||
|
return handle("REQ_PAY", request, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ImpsResponseData reqValAdd(ImpsRequestData request) {
|
||||||
|
return handle("REQ_VAL_ADD", request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ImpsResponseData reqChkTxn(ImpsRequestData request) {
|
||||||
|
return handle("REQ_CHK_TXN", request, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImpsResponseData handle(String api, ImpsRequestData request, boolean echoIfsc) {
|
||||||
|
Optional<ImpsTransaction> prior = repository.findFirstByApiAndTxnId(api, request.getTxnId());
|
||||||
|
if (prior.isPresent()) {
|
||||||
|
log.info("[ImpsCbsService] {} txnId={} idempotent replay — returning prior response", api, request.getTxnId());
|
||||||
|
return readResponse(prior.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImpsResponseData response = buildResponse(api, request, echoIfsc);
|
||||||
|
|
||||||
|
ImpsTransaction row = ImpsTransaction.newRow(api, request.getTxnId());
|
||||||
|
row.setOrgTxnId(request.getOrgTxnId());
|
||||||
|
row.setRefId(request.getRefId());
|
||||||
|
row.setUtr(request.getUtr());
|
||||||
|
row.setAmount(request.getAmount());
|
||||||
|
AccountData payeeAcc = Optional.ofNullable(request.getPayee()).map(PartyData::getAccount).orElse(null);
|
||||||
|
if (payeeAcc != null) {
|
||||||
|
row.setPayeeAccountNumber(payeeAcc.getAccountNumber());
|
||||||
|
row.setPayeeIfsc(payeeAcc.getIfsc());
|
||||||
|
}
|
||||||
|
row.setStatus(response.getStatus());
|
||||||
|
row.setResponseCode(response.getResponseCode());
|
||||||
|
row.setRequestJson(writeJson(request));
|
||||||
|
row.setResponseJson(writeJson(response));
|
||||||
|
repository.save(row);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** STUB — replace with real account lookup + debit/credit. */
|
||||||
|
private ImpsResponseData buildResponse(String api, ImpsRequestData request, boolean echoIfsc) {
|
||||||
|
ImpsResponseData resp = new ImpsResponseData();
|
||||||
|
resp.setTxnId(request.getTxnId());
|
||||||
|
resp.setOrgTxnId(request.getOrgTxnId());
|
||||||
|
resp.setRefId(request.getRefId());
|
||||||
|
resp.setUtr(request.getUtr());
|
||||||
|
resp.setAmount(request.getAmount());
|
||||||
|
resp.setTimeStamp(OffsetDateTime.now().format(ISO_OFFSET));
|
||||||
|
resp.setStatus("SUCCESS");
|
||||||
|
resp.setResponseCode("00");
|
||||||
|
AccountData payeeAcc = Optional.ofNullable(request.getPayee()).map(PartyData::getAccount).orElse(null);
|
||||||
|
if (payeeAcc != null) {
|
||||||
|
resp.setAccountNumber(payeeAcc.getAccountNumber());
|
||||||
|
if (echoIfsc) {
|
||||||
|
resp.setIfsc(payeeAcc.getIfsc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String writeJson(Object o) {
|
||||||
|
try {
|
||||||
|
return objectMapper.writeValueAsString(o);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn("[ImpsCbsService] failed to serialize {} for audit: {}", o.getClass().getSimpleName(), e.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImpsResponseData readResponse(ImpsTransaction row) {
|
||||||
|
if (row.getResponseJson() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(row.getResponseJson(), ImpsResponseData.class);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn("[ImpsCbsService] failed to deserialize stored response for txnId={}: {}", row.getTxnId(), e.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -120,7 +120,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final TellerMapper tm = new TellerMapper();
|
final TellerMapper tm = new TellerMapper();
|
||||||
final String sql = "select " + tm.schema() + " where t.id = ?";
|
final String sql = "select " + tm.schema() + " where t.id = ? and t.is_deleted = false";
|
||||||
|
|
||||||
return this.jdbcTemplate.queryForObject(sql, tm, new Object[] { tellerId }); // NOSONAR
|
return this.jdbcTemplate.queryForObject(sql, tm, new Object[] { tellerId }); // NOSONAR
|
||||||
} catch (final EmptyResultDataAccessException e) {
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
@@ -198,7 +198,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
|
|||||||
hierarchySearchString = hierarchy + "%";
|
hierarchySearchString = hierarchy + "%";
|
||||||
}
|
}
|
||||||
final TellerMapper tm = new TellerMapper();
|
final TellerMapper tm = new TellerMapper();
|
||||||
final String sql = "select " + tm.schema() + "where o.hierarchy like ? order by o.hierarchy";
|
final String sql = "select " + tm.schema() + "where o.hierarchy like ? and t.is_deleted = false order by o.hierarchy";
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, tm, new Object[] { hierarchySearchString }); // NOSONAR
|
return this.jdbcTemplate.query(sql, tm, new Object[] { hierarchySearchString }); // NOSONAR
|
||||||
}
|
}
|
||||||
|
|||||||
+394
-451
@@ -1,451 +1,394 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.organisation.teller.service;
|
package org.apache.fineract.organisation.teller.service;
|
||||||
|
|
||||||
import jakarta.persistence.PersistenceException;
|
import jakarta.persistence.PersistenceException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccount;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
|
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.organisation.staff.domain.Staff;
|
||||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
import org.apache.fineract.organisation.staff.domain.StaffRepository;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.apache.fineract.organisation.staff.exception.StaffNotFoundException;
|
||||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
import org.apache.fineract.organisation.teller.data.CashierTransactionDataValidator;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.organisation.teller.domain.Cashier;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.teller.domain.CashierRepository;
|
||||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
import org.apache.fineract.organisation.teller.domain.CashierTransaction;
|
||||||
import org.apache.fineract.organisation.staff.domain.Staff;
|
import org.apache.fineract.organisation.teller.domain.CashierTransactionRepository;
|
||||||
import org.apache.fineract.organisation.staff.domain.StaffRepository;
|
import org.apache.fineract.organisation.teller.domain.CashierTxnType;
|
||||||
import org.apache.fineract.organisation.staff.exception.StaffNotFoundException;
|
import org.apache.fineract.organisation.teller.domain.Teller;
|
||||||
import org.apache.fineract.organisation.teller.data.CashierTransactionDataValidator;
|
import org.apache.fineract.organisation.teller.domain.TellerRepositoryWrapper;
|
||||||
import org.apache.fineract.organisation.teller.domain.Cashier;
|
import org.apache.fineract.organisation.teller.exception.CashierExistForTellerException;
|
||||||
import org.apache.fineract.organisation.teller.domain.CashierRepository;
|
import org.apache.fineract.organisation.teller.exception.CashierNotFoundException;
|
||||||
import org.apache.fineract.organisation.teller.domain.CashierTransaction;
|
import org.apache.fineract.organisation.teller.exception.TellerAlreadyExistsException;
|
||||||
import org.apache.fineract.organisation.teller.domain.CashierTransactionRepository;
|
import org.apache.fineract.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
|
||||||
import org.apache.fineract.organisation.teller.domain.CashierTxnType;
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
import org.apache.fineract.organisation.teller.domain.Teller;
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
import org.apache.fineract.organisation.teller.domain.TellerRepositoryWrapper;
|
import org.springframework.orm.jpa.JpaSystemException;
|
||||||
import org.apache.fineract.organisation.teller.exception.CashierExistForTellerException;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.apache.fineract.organisation.teller.exception.CashierNotFoundException;
|
|
||||||
import org.apache.fineract.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
|
@AllArgsConstructor
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
@Slf4j
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformService {
|
||||||
import org.springframework.orm.jpa.JpaSystemException;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
private final PlatformSecurityContext context;
|
||||||
|
private final TellerCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||||
@AllArgsConstructor
|
private final TellerRepositoryWrapper tellerRepositoryWrapper;
|
||||||
@Slf4j
|
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
||||||
public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformService {
|
private final StaffRepository staffRepository;
|
||||||
|
private final CashierRepository cashierRepository;
|
||||||
private final PlatformSecurityContext context;
|
private final CashierTransactionRepository cashierTxnRepository;
|
||||||
private final TellerCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
private final CashierTransactionDataValidator cashierTransactionDataValidator;
|
||||||
private final TellerRepositoryWrapper tellerRepositoryWrapper;
|
|
||||||
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
@Override
|
||||||
private final StaffRepository staffRepository;
|
@Transactional
|
||||||
private final CashierRepository cashierRepository;
|
public CommandProcessingResult createTeller(JsonCommand command) {
|
||||||
private final CashierTransactionRepository cashierTxnRepository;
|
try {
|
||||||
private final JournalEntryRepository glJournalEntryRepository;
|
this.context.authenticatedUser();
|
||||||
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper;
|
|
||||||
private final CashierTransactionDataValidator cashierTransactionDataValidator;
|
if (this.tellerRepositoryWrapper.existsAny()) {
|
||||||
|
throw new TellerAlreadyExistsException();
|
||||||
@Override
|
}
|
||||||
@Transactional
|
|
||||||
public CommandProcessingResult createTeller(JsonCommand command) {
|
final Long officeId = command.longValueOfParameterNamed("officeId");
|
||||||
try {
|
|
||||||
this.context.authenticatedUser();
|
this.fromApiJsonDeserializer.validateForCreateAndUpdateTeller(command.json());
|
||||||
|
|
||||||
final Long officeId = command.longValueOfParameterNamed("officeId");
|
// final Office parent =
|
||||||
|
// validateUserPriviledgeOnOfficeAndRetrieve(currentUser, officeId);
|
||||||
this.fromApiJsonDeserializer.validateForCreateAndUpdateTeller(command.json());
|
final Office tellerOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
||||||
|
final Teller teller = Teller.fromJson(tellerOffice, command);
|
||||||
// final Office parent =
|
|
||||||
// validateUserPriviledgeOnOfficeAndRetrieve(currentUser, officeId);
|
// pre save to generate id for use in office hierarchy
|
||||||
final Office tellerOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
this.tellerRepositoryWrapper.saveAndFlush(teller);
|
||||||
final Teller teller = Teller.fromJson(tellerOffice, command);
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
// pre save to generate id for use in office hierarchy
|
.withCommandId(command.commandId()) //
|
||||||
this.tellerRepositoryWrapper.saveAndFlush(teller);
|
.withEntityId(teller.getId()) //
|
||||||
|
.withOfficeId(teller.getOffice().getId()) //
|
||||||
return new CommandProcessingResultBuilder() //
|
.build();
|
||||||
.withCommandId(command.commandId()) //
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
.withEntityId(teller.getId()) //
|
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||||
.withOfficeId(teller.getOffice().getId()) //
|
return CommandProcessingResult.empty();
|
||||||
.build();
|
} catch (final PersistenceException dve) {
|
||||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||||
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
handleTellerDataIntegrityIssues(command, throwable, dve);
|
||||||
return CommandProcessingResult.empty();
|
return CommandProcessingResult.empty();
|
||||||
} catch (final PersistenceException dve) {
|
}
|
||||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
}
|
||||||
handleTellerDataIntegrityIssues(command, throwable, dve);
|
|
||||||
return CommandProcessingResult.empty();
|
@Override
|
||||||
}
|
@Transactional
|
||||||
}
|
public CommandProcessingResult modifyTeller(Long tellerId, JsonCommand command) {
|
||||||
|
try {
|
||||||
@Override
|
|
||||||
@Transactional
|
final Long officeId = command.longValueOfParameterNamed("officeId");
|
||||||
public CommandProcessingResult modifyTeller(Long tellerId, JsonCommand command) {
|
final Office tellerOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
||||||
try {
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
|
|
||||||
final Long officeId = command.longValueOfParameterNamed("officeId");
|
this.fromApiJsonDeserializer.validateForCreateAndUpdateTeller(command.json());
|
||||||
final Office tellerOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
|
||||||
final AppUser currentUser = this.context.authenticatedUser();
|
final Teller teller = validateUserPriviledgeOnTellerAndRetrieve(currentUser, tellerId);
|
||||||
|
|
||||||
this.fromApiJsonDeserializer.validateForCreateAndUpdateTeller(command.json());
|
final Map<String, Object> changes = teller.update(tellerOffice, command);
|
||||||
|
|
||||||
final Teller teller = validateUserPriviledgeOnTellerAndRetrieve(currentUser, tellerId);
|
if (!changes.isEmpty()) {
|
||||||
|
this.tellerRepositoryWrapper.saveAndFlush(teller);
|
||||||
final Map<String, Object> changes = teller.update(tellerOffice, command);
|
}
|
||||||
|
|
||||||
if (!changes.isEmpty()) {
|
return new CommandProcessingResultBuilder() //
|
||||||
this.tellerRepositoryWrapper.saveAndFlush(teller);
|
.withCommandId(command.commandId()) //
|
||||||
}
|
.withEntityId(teller.getId()) //
|
||||||
|
.withOfficeId(teller.officeId()) //
|
||||||
return new CommandProcessingResultBuilder() //
|
.with(changes) //
|
||||||
.withCommandId(command.commandId()) //
|
.build();
|
||||||
.withEntityId(teller.getId()) //
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
.withOfficeId(teller.officeId()) //
|
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||||
.with(changes) //
|
return CommandProcessingResult.empty();
|
||||||
.build();
|
} catch (final PersistenceException dve) {
|
||||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||||
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
handleTellerDataIntegrityIssues(command, throwable, dve);
|
||||||
return CommandProcessingResult.empty();
|
return CommandProcessingResult.empty();
|
||||||
} catch (final PersistenceException dve) {
|
}
|
||||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
}
|
||||||
handleTellerDataIntegrityIssues(command, throwable, dve);
|
|
||||||
return CommandProcessingResult.empty();
|
/*
|
||||||
}
|
* used to restrict modifying operations to office that are either the users office or lower (child) in the office
|
||||||
}
|
* hierarchy
|
||||||
|
*/
|
||||||
/*
|
private Teller validateUserPriviledgeOnTellerAndRetrieve(final AppUser currentUser, final Long tellerId) {
|
||||||
* used to restrict modifying operations to office that are either the users office or lower (child) in the office
|
|
||||||
* hierarchy
|
final Long userOfficeId = currentUser.getOffice().getId();
|
||||||
*/
|
final Office userOffice = this.officeRepositoryWrapper.findOfficeHierarchy(userOfficeId);
|
||||||
private Teller validateUserPriviledgeOnTellerAndRetrieve(final AppUser currentUser, final Long tellerId) {
|
final Teller tellerToReturn = this.tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
||||||
|
final Long tellerOfficeId = tellerToReturn.officeId();
|
||||||
final Long userOfficeId = currentUser.getOffice().getId();
|
if (userOffice.doesNotHaveAnOfficeInHierarchyWithId(tellerOfficeId)) {
|
||||||
final Office userOffice = this.officeRepositoryWrapper.findOfficeHierarchy(userOfficeId);
|
throw new NoAuthorizationException("User does not have sufficient priviledges to act on the provided office.");
|
||||||
final Teller tellerToReturn = this.tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
}
|
||||||
final Long tellerOfficeId = tellerToReturn.officeId();
|
return tellerToReturn;
|
||||||
if (userOffice.doesNotHaveAnOfficeInHierarchyWithId(tellerOfficeId)) {
|
}
|
||||||
throw new NoAuthorizationException("User does not have sufficient priviledges to act on the provided office.");
|
|
||||||
}
|
@Override
|
||||||
return tellerToReturn;
|
@Transactional
|
||||||
}
|
public CommandProcessingResult deleteTeller(Long tellerId) {
|
||||||
|
Teller teller = tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
||||||
@Override
|
teller.delete();
|
||||||
@Transactional
|
tellerRepositoryWrapper.saveAndFlush(teller);
|
||||||
public CommandProcessingResult deleteTeller(Long tellerId) {
|
return new CommandProcessingResultBuilder() //
|
||||||
// TODO Auto-generated method stub
|
.withEntityId(teller.getId()) //
|
||||||
|
.build();
|
||||||
Teller teller = tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
}
|
||||||
Set<Cashier> isTellerIdPresentInCashier = teller.getCashiers();
|
|
||||||
|
/*
|
||||||
for (final Cashier tellerIdInCashier : isTellerIdPresentInCashier) {
|
* Guaranteed to throw an exception no matter what the data integrity issue is.
|
||||||
if (tellerIdInCashier.getTeller().getId().toString().equalsIgnoreCase(tellerId.toString())) {
|
*/
|
||||||
throw new CashierExistForTellerException(tellerId);
|
private void handleTellerDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dve) {
|
||||||
}
|
if (realCause.getMessage().contains("m_tellers_name_unq")) {
|
||||||
|
final String name = command.stringValueOfParameterNamed("name");
|
||||||
}
|
throw new PlatformDataIntegrityException("error.msg.teller.duplicate.name", "Teller with name `" + name + "` already exists",
|
||||||
tellerRepositoryWrapper.delete(teller);
|
"name", name);
|
||||||
return new CommandProcessingResultBuilder() //
|
}
|
||||||
.withEntityId(teller.getId()) //
|
|
||||||
.build();
|
log.error("Error occured.", dve);
|
||||||
|
throw ErrorHandler.getMappable(dve, "error.msg.teller.unknown.data.integrity.issue",
|
||||||
}
|
"Unknown data integrity issue with resource: " + realCause.getMessage());
|
||||||
|
}
|
||||||
/*
|
|
||||||
* Guaranteed to throw an exception no matter what the data integrity issue is.
|
@Override
|
||||||
*/
|
public CommandProcessingResult allocateCashierToTeller(final Long tellerId, JsonCommand command) {
|
||||||
private void handleTellerDataIntegrityIssues(final JsonCommand command, final Throwable realCause, final Exception dve) {
|
try {
|
||||||
if (realCause.getMessage().contains("m_tellers_name_unq")) {
|
this.context.authenticatedUser();
|
||||||
final String name = command.stringValueOfParameterNamed("name");
|
Long hourStartTime;
|
||||||
throw new PlatformDataIntegrityException("error.msg.teller.duplicate.name", "Teller with name `" + name + "` already exists",
|
Long minStartTime;
|
||||||
"name", name);
|
Long hourEndTime;
|
||||||
}
|
Long minEndTime;
|
||||||
|
String startTime = " ";
|
||||||
log.error("Error occured.", dve);
|
String endTime = " ";
|
||||||
throw ErrorHandler.getMappable(dve, "error.msg.teller.unknown.data.integrity.issue",
|
final Teller teller = this.tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
||||||
"Unknown data integrity issue with resource: " + realCause.getMessage());
|
final Office tellerOffice = teller.getOffice();
|
||||||
}
|
|
||||||
|
final Long staffId = command.longValueOfParameterNamed("staffId");
|
||||||
@Override
|
|
||||||
public CommandProcessingResult allocateCashierToTeller(final Long tellerId, JsonCommand command) {
|
this.fromApiJsonDeserializer.validateForAllocateCashier(command.json());
|
||||||
try {
|
|
||||||
this.context.authenticatedUser();
|
final Staff staff = this.staffRepository.findById(staffId).orElseThrow(() -> new StaffNotFoundException(staffId));
|
||||||
Long hourStartTime;
|
final Boolean isFullDay = command.booleanObjectValueOfParameterNamed("isFullDay");
|
||||||
Long minStartTime;
|
if (!isFullDay) {
|
||||||
Long hourEndTime;
|
hourStartTime = command.longValueOfParameterNamed("hourStartTime");
|
||||||
Long minEndTime;
|
minStartTime = command.longValueOfParameterNamed("minStartTime");
|
||||||
String startTime = " ";
|
|
||||||
String endTime = " ";
|
if (minStartTime == 0) {
|
||||||
final Teller teller = this.tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
startTime = hourStartTime.toString() + ":" + minStartTime.toString() + "0";
|
||||||
final Office tellerOffice = teller.getOffice();
|
} else {
|
||||||
|
startTime = hourStartTime.toString() + ":" + minStartTime.toString();
|
||||||
final Long staffId = command.longValueOfParameterNamed("staffId");
|
}
|
||||||
|
|
||||||
this.fromApiJsonDeserializer.validateForAllocateCashier(command.json());
|
hourEndTime = command.longValueOfParameterNamed("hourEndTime");
|
||||||
|
minEndTime = command.longValueOfParameterNamed("minEndTime");
|
||||||
final Staff staff = this.staffRepository.findById(staffId).orElseThrow(() -> new StaffNotFoundException(staffId));
|
if (minEndTime == 0) {
|
||||||
final Boolean isFullDay = command.booleanObjectValueOfParameterNamed("isFullDay");
|
endTime = hourEndTime.toString() + ":" + minEndTime.toString() + "0";
|
||||||
if (!isFullDay) {
|
} else {
|
||||||
hourStartTime = command.longValueOfParameterNamed("hourStartTime");
|
endTime = hourEndTime.toString() + ":" + minEndTime.toString();
|
||||||
minStartTime = command.longValueOfParameterNamed("minStartTime");
|
}
|
||||||
|
|
||||||
if (minStartTime == 0) {
|
}
|
||||||
startTime = hourStartTime.toString() + ":" + minStartTime.toString() + "0";
|
final Cashier cashier = Cashier.fromJson(tellerOffice, teller, staff, startTime, endTime, command);
|
||||||
} else {
|
this.cashierTransactionDataValidator.validateCashierAllowedDateAndTime(cashier, teller);
|
||||||
startTime = hourStartTime.toString() + ":" + minStartTime.toString();
|
|
||||||
}
|
this.cashierRepository.save(cashier);
|
||||||
|
|
||||||
hourEndTime = command.longValueOfParameterNamed("hourEndTime");
|
return new CommandProcessingResultBuilder() //
|
||||||
minEndTime = command.longValueOfParameterNamed("minEndTime");
|
.withCommandId(command.commandId()) //
|
||||||
if (minEndTime == 0) {
|
.withEntityId(teller.getId()) //
|
||||||
endTime = hourEndTime.toString() + ":" + minEndTime.toString() + "0";
|
.withSubEntityId(cashier.getId()) //
|
||||||
} else {
|
.build();
|
||||||
endTime = hourEndTime.toString() + ":" + minEndTime.toString();
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
}
|
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||||
|
return CommandProcessingResult.empty();
|
||||||
}
|
} catch (final PersistenceException dve) {
|
||||||
final Cashier cashier = Cashier.fromJson(tellerOffice, teller, staff, startTime, endTime, command);
|
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||||
this.cashierTransactionDataValidator.validateCashierAllowedDateAndTime(cashier, teller);
|
handleTellerDataIntegrityIssues(command, throwable, dve);
|
||||||
|
return CommandProcessingResult.empty();
|
||||||
this.cashierRepository.save(cashier);
|
}
|
||||||
|
}
|
||||||
return new CommandProcessingResultBuilder() //
|
|
||||||
.withCommandId(command.commandId()) //
|
@Override
|
||||||
.withEntityId(teller.getId()) //
|
@Transactional
|
||||||
.withSubEntityId(cashier.getId()) //
|
public CommandProcessingResult updateCashierAllocation(Long tellerId, Long cashierId, JsonCommand command) {
|
||||||
.build();
|
try {
|
||||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
|
||||||
return CommandProcessingResult.empty();
|
this.fromApiJsonDeserializer.validateForAllocateCashier(command.json());
|
||||||
} catch (final PersistenceException dve) {
|
|
||||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
final Long staffId = command.longValueOfParameterNamed("staffId");
|
||||||
handleTellerDataIntegrityIssues(command, throwable, dve);
|
final Staff staff = this.staffRepository.findById(staffId).orElseThrow(() -> new StaffNotFoundException(staffId));
|
||||||
return CommandProcessingResult.empty();
|
|
||||||
}
|
final Cashier cashier = validateUserPriviledgeOnCashierAndRetrieve(currentUser, tellerId, cashierId);
|
||||||
}
|
|
||||||
|
cashier.setStaff(staff);
|
||||||
@Override
|
|
||||||
@Transactional
|
// TODO - check if staff office and teller office match
|
||||||
public CommandProcessingResult updateCashierAllocation(Long tellerId, Long cashierId, JsonCommand command) {
|
|
||||||
try {
|
final Map<String, Object> changes = cashier.update(command);
|
||||||
final AppUser currentUser = this.context.authenticatedUser();
|
|
||||||
|
if (!changes.isEmpty()) {
|
||||||
this.fromApiJsonDeserializer.validateForAllocateCashier(command.json());
|
this.cashierRepository.saveAndFlush(cashier);
|
||||||
|
}
|
||||||
final Long staffId = command.longValueOfParameterNamed("staffId");
|
|
||||||
final Staff staff = this.staffRepository.findById(staffId).orElseThrow(() -> new StaffNotFoundException(staffId));
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withCommandId(command.commandId()) //
|
||||||
final Cashier cashier = validateUserPriviledgeOnCashierAndRetrieve(currentUser, tellerId, cashierId);
|
.withEntityId(cashier.getTeller().getId()) //
|
||||||
|
.withSubEntityId(cashier.getId()) //
|
||||||
cashier.setStaff(staff);
|
.with(changes) //
|
||||||
|
.build();
|
||||||
// TODO - check if staff office and teller office match
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
|
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||||
final Map<String, Object> changes = cashier.update(command);
|
return CommandProcessingResult.empty();
|
||||||
|
} catch (final PersistenceException dve) {
|
||||||
if (!changes.isEmpty()) {
|
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||||
this.cashierRepository.saveAndFlush(cashier);
|
handleTellerDataIntegrityIssues(command, throwable, dve);
|
||||||
}
|
return CommandProcessingResult.empty();
|
||||||
|
}
|
||||||
return new CommandProcessingResultBuilder() //
|
}
|
||||||
.withCommandId(command.commandId()) //
|
|
||||||
.withEntityId(cashier.getTeller().getId()) //
|
private Cashier validateUserPriviledgeOnCashierAndRetrieve(final AppUser currentUser, final Long tellerId, final Long cashierId) {
|
||||||
.withSubEntityId(cashier.getId()) //
|
|
||||||
.with(changes) //
|
validateUserPriviledgeOnTellerAndRetrieve(currentUser, tellerId);
|
||||||
.build();
|
|
||||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
return this.cashierRepository.findById(cashierId).orElse(null);
|
||||||
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
}
|
||||||
return CommandProcessingResult.empty();
|
|
||||||
} catch (final PersistenceException dve) {
|
@Override
|
||||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
@Transactional
|
||||||
handleTellerDataIntegrityIssues(command, throwable, dve);
|
public CommandProcessingResult deleteCashierAllocation(Long tellerId, Long cashierId, JsonCommand command) {
|
||||||
return CommandProcessingResult.empty();
|
try {
|
||||||
}
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
}
|
final Cashier cashier = validateUserPriviledgeOnCashierAndRetrieve(currentUser, tellerId, cashierId);
|
||||||
|
this.cashierRepository.delete(cashier);
|
||||||
private Cashier validateUserPriviledgeOnCashierAndRetrieve(final AppUser currentUser, final Long tellerId, final Long cashierId) {
|
|
||||||
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
validateUserPriviledgeOnTellerAndRetrieve(currentUser, tellerId);
|
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||||
|
return CommandProcessingResult.empty();
|
||||||
return this.cashierRepository.findById(cashierId).orElse(null);
|
} catch (final PersistenceException dve) {
|
||||||
}
|
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||||
|
handleTellerDataIntegrityIssues(command, throwable, dve);
|
||||||
@Override
|
return CommandProcessingResult.empty();
|
||||||
@Transactional
|
}
|
||||||
public CommandProcessingResult deleteCashierAllocation(Long tellerId, Long cashierId, JsonCommand command) {
|
|
||||||
try {
|
return new CommandProcessingResultBuilder() //
|
||||||
final AppUser currentUser = this.context.authenticatedUser();
|
.withEntityId(cashierId) //
|
||||||
final Cashier cashier = validateUserPriviledgeOnCashierAndRetrieve(currentUser, tellerId, cashierId);
|
.build();
|
||||||
this.cashierRepository.delete(cashier);
|
}
|
||||||
|
|
||||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
/*
|
||||||
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
* @Override public CommandProcessingResult inwardCashToCashier (final Long cashierId, final CashierTransaction
|
||||||
return CommandProcessingResult.empty();
|
* cashierTxn) { CashierTxnType txnType = CashierTxnType.INWARD_CASH_TXN; // pre save to generate id for use in
|
||||||
} catch (final PersistenceException dve) {
|
* office hierarchy this.cashierTxnRepository.save(cashierTxn); }
|
||||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
*/
|
||||||
handleTellerDataIntegrityIssues(command, throwable, dve);
|
|
||||||
return CommandProcessingResult.empty();
|
@Override
|
||||||
}
|
public CommandProcessingResult allocateCashToCashier(final Long cashierId, JsonCommand command) {
|
||||||
|
return doTransactionForCashier(cashierId, CashierTxnType.ALLOCATE, command); // For
|
||||||
return new CommandProcessingResultBuilder() //
|
// fund
|
||||||
.withEntityId(cashierId) //
|
// allocation
|
||||||
.build();
|
// to
|
||||||
}
|
// cashier
|
||||||
|
}
|
||||||
/*
|
|
||||||
* @Override public CommandProcessingResult inwardCashToCashier (final Long cashierId, final CashierTransaction
|
@Override
|
||||||
* cashierTxn) { CashierTxnType txnType = CashierTxnType.INWARD_CASH_TXN; // pre save to generate id for use in
|
public CommandProcessingResult settleCashFromCashier(final Long cashierId, JsonCommand command) {
|
||||||
* office hierarchy this.cashierTxnRepository.save(cashierTxn); }
|
|
||||||
*/
|
this.cashierTransactionDataValidator.validateSettleCashAndCashOutTransactions(cashierId, command);
|
||||||
|
|
||||||
@Override
|
return doTransactionForCashier(cashierId, CashierTxnType.SETTLE, command); // For
|
||||||
public CommandProcessingResult allocateCashToCashier(final Long cashierId, JsonCommand command) {
|
// fund
|
||||||
return doTransactionForCashier(cashierId, CashierTxnType.ALLOCATE, command); // For
|
// settlement
|
||||||
// fund
|
// from
|
||||||
// allocation
|
// cashier
|
||||||
// to
|
}
|
||||||
// cashier
|
|
||||||
}
|
private CommandProcessingResult doTransactionForCashier(final Long cashierId, final CashierTxnType txnType, JsonCommand command) {
|
||||||
|
try {
|
||||||
@Override
|
final AppUser currentUser = this.context.authenticatedUser();
|
||||||
public CommandProcessingResult settleCashFromCashier(final Long cashierId, JsonCommand command) {
|
|
||||||
|
final Cashier cashier = this.cashierRepository.findById(cashierId).orElseThrow(() -> new CashierNotFoundException(cashierId));
|
||||||
this.cashierTransactionDataValidator.validateSettleCashAndCashOutTransactions(cashierId, command);
|
|
||||||
|
this.fromApiJsonDeserializer.validateForCashTxnForCashier(command.json());
|
||||||
return doTransactionForCashier(cashierId, CashierTxnType.SETTLE, command); // For
|
|
||||||
// fund
|
// TODO: can we please remove this whole block?!? this is 20 lines of dead code!!!
|
||||||
// settlement
|
final String entityType = command.stringValueOfParameterNamed("entityType");
|
||||||
// from
|
if (entityType != null) {
|
||||||
// cashier
|
if (entityType.equals("loan account")) {
|
||||||
}
|
// TODO : Check if loan account exists
|
||||||
|
// LoanAccount loan = null;
|
||||||
private CommandProcessingResult doTransactionForCashier(final Long cashierId, final CashierTxnType txnType, JsonCommand command) {
|
// if (loan == null) { throw new
|
||||||
try {
|
// LoanAccountFoundException(entityId); }
|
||||||
final AppUser currentUser = this.context.authenticatedUser();
|
} else if (entityType.equals("savings account")) {
|
||||||
|
// TODO : Check if loan account exists
|
||||||
final Cashier cashier = this.cashierRepository.findById(cashierId).orElseThrow(() -> new CashierNotFoundException(cashierId));
|
// SavingsAccount savingsaccount = null;
|
||||||
|
// if (savingsaccount == null) { throw new
|
||||||
this.fromApiJsonDeserializer.validateForCashTxnForCashier(command.json());
|
// SavingsAccountNotFoundException(entityId); }
|
||||||
|
|
||||||
// TODO: can we please remove this whole block?!? this is 20 lines of dead code!!!
|
}
|
||||||
final String entityType = command.stringValueOfParameterNamed("entityType");
|
if (entityType.equals("client")) {
|
||||||
if (entityType != null) {
|
// TODO: Check if client exists
|
||||||
if (entityType.equals("loan account")) {
|
// Client client = null;
|
||||||
// TODO : Check if loan account exists
|
// if (client == null) { throw new
|
||||||
// LoanAccount loan = null;
|
// ClientNotFoundException(entityId); }
|
||||||
// if (loan == null) { throw new
|
} else {
|
||||||
// LoanAccountFoundException(entityId); }
|
// TODO : Invalid type handling
|
||||||
} else if (entityType.equals("savings account")) {
|
}
|
||||||
// TODO : Check if loan account exists
|
}
|
||||||
// SavingsAccount savingsaccount = null;
|
|
||||||
// if (savingsaccount == null) { throw new
|
final CashierTransaction cashierTxn = CashierTransaction.fromJson(cashier, command);
|
||||||
// SavingsAccountNotFoundException(entityId); }
|
cashierTxn.setTxnType(txnType.getId());
|
||||||
|
|
||||||
}
|
this.cashierTxnRepository.save(cashierTxn);
|
||||||
if (entityType.equals("client")) {
|
|
||||||
// TODO: Check if client exists
|
// GL entries skipped for vault-to-cashier (ALLOCATE) and cashier-to-vault (SETTLE)
|
||||||
// Client client = null;
|
// transfers. Cash is internal bank physical movement — denomination tracking is
|
||||||
// if (client == null) { throw new
|
// handled operationally via m_cash_transfer / m_cash_settlement tables.
|
||||||
// ClientNotFoundException(entityId); }
|
// GL impact happens only on customer deposit/withdrawal transactions.
|
||||||
} else {
|
|
||||||
// TODO : Invalid type handling
|
return new CommandProcessingResultBuilder() //
|
||||||
}
|
.withCommandId(command.commandId()) //
|
||||||
}
|
.withEntityId(cashier.getId()) //
|
||||||
|
.withSubEntityId(cashierTxn.getId()) //
|
||||||
final CashierTransaction cashierTxn = CashierTransaction.fromJson(cashier, command);
|
.build();
|
||||||
cashierTxn.setTxnType(txnType.getId());
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
|
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||||
this.cashierTxnRepository.save(cashierTxn);
|
return CommandProcessingResult.empty();
|
||||||
|
} catch (final PersistenceException dve) {
|
||||||
// Pass the journal entries
|
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||||
FinancialActivityAccount mainVaultFinancialActivityAccount = this.financialActivityAccountRepositoryWrapper
|
handleTellerDataIntegrityIssues(command, throwable, dve);
|
||||||
.findByFinancialActivityTypeWithNotFoundDetection(FinancialActivity.CASH_AT_MAINVAULT.getValue());
|
return CommandProcessingResult.empty();
|
||||||
FinancialActivityAccount tellerCashFinancialActivityAccount = this.financialActivityAccountRepositoryWrapper
|
}
|
||||||
.findByFinancialActivityTypeWithNotFoundDetection(FinancialActivity.CASH_AT_TELLER.getValue());
|
}
|
||||||
GLAccount creditAccount = null;
|
|
||||||
GLAccount debitAccount = null;
|
}
|
||||||
if (txnType.equals(CashierTxnType.ALLOCATE)) {
|
|
||||||
debitAccount = tellerCashFinancialActivityAccount.getGlAccount();
|
|
||||||
creditAccount = mainVaultFinancialActivityAccount.getGlAccount();
|
|
||||||
} else if (txnType.equals(CashierTxnType.SETTLE)) {
|
|
||||||
debitAccount = mainVaultFinancialActivityAccount.getGlAccount();
|
|
||||||
creditAccount = tellerCashFinancialActivityAccount.getGlAccount();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Office cashierOffice = cashier.getTeller().getOffice();
|
|
||||||
|
|
||||||
final Long time = System.currentTimeMillis();
|
|
||||||
final String uniqueVal = String.valueOf(time) + currentUser.getId() + cashierOffice.getId();
|
|
||||||
final String transactionId = Long.toHexString(Long.parseLong(uniqueVal));
|
|
||||||
|
|
||||||
final JournalEntry debitJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
|
|
||||||
// detail
|
|
||||||
debitAccount, cashierTxn.getCurrencyCode(),
|
|
||||||
|
|
||||||
transactionId, false, // manual entry
|
|
||||||
cashierTxn.getTxnDate(), JournalEntryType.DEBIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
|
|
||||||
null, null, null, // entity Type, entityId, reference number
|
|
||||||
null, null, null, null); // Loan
|
|
||||||
// and
|
|
||||||
// Savings
|
|
||||||
// Txn
|
|
||||||
|
|
||||||
final JournalEntry creditJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
|
|
||||||
// detail
|
|
||||||
creditAccount, cashierTxn.getCurrencyCode(),
|
|
||||||
|
|
||||||
transactionId, false, // manual entry
|
|
||||||
cashierTxn.getTxnDate(), JournalEntryType.CREDIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
|
|
||||||
null, null, null, // entity Type, entityId, reference number
|
|
||||||
null, null, null, null); // Loan
|
|
||||||
// and
|
|
||||||
// Savings
|
|
||||||
// Txn
|
|
||||||
|
|
||||||
this.glJournalEntryRepository.saveAndFlush(debitJournalEntry);
|
|
||||||
this.glJournalEntryRepository.saveAndFlush(creditJournalEntry);
|
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
|
||||||
.withCommandId(command.commandId()) //
|
|
||||||
.withEntityId(cashier.getId()) //
|
|
||||||
.withSubEntityId(cashierTxn.getId()) //
|
|
||||||
.build();
|
|
||||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
|
||||||
handleTellerDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
|
||||||
return CommandProcessingResult.empty();
|
|
||||||
} catch (final PersistenceException dve) {
|
|
||||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
|
||||||
handleTellerDataIntegrityIssues(command, throwable, dve);
|
|
||||||
return CommandProcessingResult.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
+67
-71
@@ -1,71 +1,67 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.organisation.teller.starter;
|
package org.apache.fineract.organisation.teller.starter;
|
||||||
|
|
||||||
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
|
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
||||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||||
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
||||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
import org.apache.fineract.organisation.staff.domain.StaffRepository;
|
||||||
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
|
||||||
import org.apache.fineract.organisation.staff.domain.StaffRepository;
|
import org.apache.fineract.organisation.teller.data.CashierTransactionDataValidator;
|
||||||
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
|
import org.apache.fineract.organisation.teller.domain.CashierRepository;
|
||||||
import org.apache.fineract.organisation.teller.data.CashierTransactionDataValidator;
|
import org.apache.fineract.organisation.teller.domain.CashierTransactionRepository;
|
||||||
import org.apache.fineract.organisation.teller.domain.CashierRepository;
|
import org.apache.fineract.organisation.teller.domain.TellerRepositoryWrapper;
|
||||||
import org.apache.fineract.organisation.teller.domain.CashierTransactionRepository;
|
import org.apache.fineract.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
|
||||||
import org.apache.fineract.organisation.teller.domain.TellerRepositoryWrapper;
|
import org.apache.fineract.organisation.teller.service.TellerManagementReadPlatformService;
|
||||||
import org.apache.fineract.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
|
import org.apache.fineract.organisation.teller.service.TellerManagementReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.organisation.teller.service.TellerManagementReadPlatformService;
|
import org.apache.fineract.organisation.teller.service.TellerWritePlatformService;
|
||||||
import org.apache.fineract.organisation.teller.service.TellerManagementReadPlatformServiceImpl;
|
import org.apache.fineract.organisation.teller.service.TellerWritePlatformServiceJpaImpl;
|
||||||
import org.apache.fineract.organisation.teller.service.TellerWritePlatformService;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.apache.fineract.organisation.teller.service.TellerWritePlatformServiceJpaImpl;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
@Configuration
|
||||||
|
public class OrganisationTellerConfiguration {
|
||||||
@Configuration
|
|
||||||
public class OrganisationTellerConfiguration {
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(TellerManagementReadPlatformService.class)
|
||||||
@Bean
|
public TellerManagementReadPlatformService tellerManagementReadPlatformService(JdbcTemplate jdbcTemplate,
|
||||||
@ConditionalOnMissingBean(TellerManagementReadPlatformService.class)
|
PlatformSecurityContext context, OfficeReadPlatformService officeReadPlatformService,
|
||||||
public TellerManagementReadPlatformService tellerManagementReadPlatformService(JdbcTemplate jdbcTemplate,
|
StaffReadPlatformService staffReadPlatformService, CurrencyReadPlatformService currencyReadPlatformService,
|
||||||
PlatformSecurityContext context, OfficeReadPlatformService officeReadPlatformService,
|
DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper, SqlValidator sqlValidator) {
|
||||||
StaffReadPlatformService staffReadPlatformService, CurrencyReadPlatformService currencyReadPlatformService,
|
return new TellerManagementReadPlatformServiceImpl(jdbcTemplate, context, officeReadPlatformService, staffReadPlatformService,
|
||||||
DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper, SqlValidator sqlValidator) {
|
currencyReadPlatformService, sqlGenerator, paginationHelper, sqlValidator);
|
||||||
return new TellerManagementReadPlatformServiceImpl(jdbcTemplate, context, officeReadPlatformService, staffReadPlatformService,
|
}
|
||||||
currencyReadPlatformService, sqlGenerator, paginationHelper, sqlValidator);
|
|
||||||
}
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(TellerWritePlatformService.class)
|
||||||
@Bean
|
public TellerWritePlatformService tellerWritePlatformService(PlatformSecurityContext context,
|
||||||
@ConditionalOnMissingBean(TellerWritePlatformService.class)
|
TellerCommandFromApiJsonDeserializer fromApiJsonDeserializer, TellerRepositoryWrapper tellerRepositoryWrapper,
|
||||||
public TellerWritePlatformService tellerWritePlatformService(PlatformSecurityContext context,
|
OfficeRepositoryWrapper officeRepositoryWrapper, StaffRepository staffRepository, CashierRepository cashierRepository,
|
||||||
TellerCommandFromApiJsonDeserializer fromApiJsonDeserializer, TellerRepositoryWrapper tellerRepositoryWrapper,
|
CashierTransactionRepository cashierTxnRepository,
|
||||||
OfficeRepositoryWrapper officeRepositoryWrapper, StaffRepository staffRepository, CashierRepository cashierRepository,
|
CashierTransactionDataValidator cashierTransactionDataValidator) {
|
||||||
CashierTransactionRepository cashierTxnRepository, JournalEntryRepository glJournalEntryRepository,
|
return new TellerWritePlatformServiceJpaImpl(context, fromApiJsonDeserializer, tellerRepositoryWrapper, officeRepositoryWrapper,
|
||||||
FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper,
|
staffRepository, cashierRepository, cashierTxnRepository, cashierTransactionDataValidator);
|
||||||
CashierTransactionDataValidator cashierTransactionDataValidator) {
|
}
|
||||||
return new TellerWritePlatformServiceJpaImpl(context, fromApiJsonDeserializer, tellerRepositoryWrapper, officeRepositoryWrapper,
|
}
|
||||||
staffRepository, cashierRepository, cashierTxnRepository, glJournalEntryRepository,
|
|
||||||
financialActivityAccountRepositoryWrapper, cashierTransactionDataValidator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* 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.account.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/accounts/{accountId}/terminate")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Account Terminate", description = "Unified terminate endpoint for Savings, Current, FD and RD accounts.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AccountTerminateApiResource {
|
||||||
|
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
|
||||||
|
private final FromJsonHelper fromJsonHelper;
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Terminate an account", description = "Terminates a Savings, Current, Fixed Deposit or Recurring Deposit account. Pass accountType in the request body: SAVINGS | CURRENT | FD | RD.")
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK")
|
||||||
|
public String terminate(@PathParam("accountId") final Long accountId,
|
||||||
|
final String apiRequestBodyAsJson) {
|
||||||
|
|
||||||
|
final String jsonBody = StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson;
|
||||||
|
|
||||||
|
final JsonElement root = this.fromJsonHelper.parse(jsonBody);
|
||||||
|
final String accountType = this.fromJsonHelper.extractStringNamed("accountType", root);
|
||||||
|
String entityName = "SAVINGSACCOUNT";
|
||||||
|
if ("CURRENT".equalsIgnoreCase(accountType)) {
|
||||||
|
entityName = "CURRENTACCOUNT";
|
||||||
|
} else if ("FD".equalsIgnoreCase(accountType)) {
|
||||||
|
entityName = "FIXEDDEPOSITACCOUNT";
|
||||||
|
} else if ("RD".equalsIgnoreCase(accountType)) {
|
||||||
|
entityName = "RECURRINGDEPOSITACCOUNT";
|
||||||
|
}
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||||
|
.withJson(jsonBody)
|
||||||
|
.terminateAccount(accountId)
|
||||||
|
.withEntityName(entityName)
|
||||||
|
.build();
|
||||||
|
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
return toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
+220
-220
@@ -1,220 +1,220 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.api;
|
package org.apache.fineract.portfolio.account.api;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.ws.rs.BeanParam;
|
import jakarta.ws.rs.BeanParam;
|
||||||
import jakarta.ws.rs.Consumes;
|
import jakarta.ws.rs.Consumes;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.POST;
|
import jakarta.ws.rs.POST;
|
||||||
import jakarta.ws.rs.PUT;
|
import jakarta.ws.rs.PUT;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.PathParam;
|
import jakarta.ws.rs.PathParam;
|
||||||
import jakarta.ws.rs.Produces;
|
import jakarta.ws.rs.Produces;
|
||||||
import jakarta.ws.rs.QueryParam;
|
import jakarta.ws.rs.QueryParam;
|
||||||
import jakarta.ws.rs.core.Context;
|
import jakarta.ws.rs.core.Context;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.batch.command.CommandHandlerRegistry;
|
import org.apache.fineract.batch.command.CommandHandlerRegistry;
|
||||||
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;
|
||||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
|
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
|
||||||
import org.apache.fineract.infrastructure.core.service.Page;
|
import org.apache.fineract.infrastructure.core.service.Page;
|
||||||
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
import org.apache.fineract.infrastructure.core.service.SearchParameters;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||||
import org.apache.fineract.portfolio.account.data.AccountTransferData;
|
import org.apache.fineract.portfolio.account.data.AccountTransferData;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDTO;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionDTO;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionData;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionData;
|
||||||
import org.apache.fineract.portfolio.account.data.request.StandingInstructionCreationRequest;
|
import org.apache.fineract.portfolio.account.data.request.StandingInstructionCreationRequest;
|
||||||
import org.apache.fineract.portfolio.account.data.request.StandingInstructionSearchParam;
|
import org.apache.fineract.portfolio.account.data.request.StandingInstructionSearchParam;
|
||||||
import org.apache.fineract.portfolio.account.data.request.StandingInstructionUpdatesRequest;
|
import org.apache.fineract.portfolio.account.data.request.StandingInstructionUpdatesRequest;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Path("/v1/standinginstructions")
|
@Path("/v1/standinginstructions")
|
||||||
@Component
|
@Component
|
||||||
@Tag(name = "Standing Instructions", description = "Standing instructions (or standing orders) refer to instructions a bank account holder (\"the payer\") gives to his or her bank to pay a set amount at regular intervals to another's (\"the payee's\") account.\n"
|
@Tag(name = "Standing Instructions", description = "Standing instructions (or standing orders) refer to instructions a bank account holder (\"the payer\") gives to his or her bank to pay a set amount at regular intervals to another's (\"the payee's\") account.\n"
|
||||||
+ "\n" + "Note: At present only savings account to savings account and savings account to Loan account transfers are permitted.")
|
+ "\n" + "Note: At present only savings account to savings account and savings account to Loan account transfers are permitted.")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class StandingInstructionApiResource {
|
public class StandingInstructionApiResource {
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final DefaultToApiJsonSerializer<StandingInstructionData> toApiJsonSerializer;
|
private final DefaultToApiJsonSerializer<StandingInstructionData> toApiJsonSerializer;
|
||||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
private final StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
private final StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
||||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||||
private final SqlValidator sqlValidator;
|
private final SqlValidator sqlValidator;
|
||||||
|
|
||||||
private static final CommandHandlerRegistry<String, Long, String, CommandWrapper> COMMAND_HANDLER_REGISTRY = new CommandHandlerRegistry<>(
|
private static final CommandHandlerRegistry<String, Long, String, CommandWrapper> COMMAND_HANDLER_REGISTRY = new CommandHandlerRegistry<>(
|
||||||
Map.of(CommandParameterUtil.UPDATE_COMMAND_VALUE,
|
Map.of(CommandParameterUtil.UPDATE_COMMAND_VALUE,
|
||||||
(id, json) -> new CommandWrapperBuilder().updateStandingInstruction(id).withJson(json).build(),
|
(id, json) -> new CommandWrapperBuilder().updateStandingInstruction(id).withJson(json).build(),
|
||||||
CommandParameterUtil.DELETE_COMMAND_VALUE,
|
CommandParameterUtil.DELETE_COMMAND_VALUE,
|
||||||
(id, json) -> new CommandWrapperBuilder().deleteStandingInstruction(id).withJson(json).build()));
|
(id, json) -> new CommandWrapperBuilder().deleteStandingInstruction(id).withJson(json).build()));
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("template")
|
@Path("template")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
@Operation(summary = "Retrieve Standing Instruction Template", description = "This is a convenience resource. "
|
@Operation(summary = "Retrieve Standing Instruction Template", description = "This is a convenience resource. "
|
||||||
+ "It can be useful when building maintenance user interface screens for client applications. "
|
+ "It can be useful when building maintenance user interface screens for client applications. "
|
||||||
+ "The template data returned consists of any or all of:\n" + "\n" + "Field Defaults\n" + "Allowed Value Lists\n"
|
+ "The template data returned consists of any or all of:\n" + "\n" + "Field Defaults\n" + "Allowed Value Lists\n"
|
||||||
+ "Example Requests:\n" + "\n" + "standinginstructions/template?fromAccountType=2&fromOfficeId=1\n" + "\n"
|
+ "Example Requests:\n" + "\n" + "standinginstructions/template?fromAccountType=2&fromOfficeId=1\n" + "\n"
|
||||||
+ "standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1\n" + "\n"
|
+ "standinginstructions/template?fromAccountType=2&fromOfficeId=1&fromClientId=1&transferType=1\n" + "\n"
|
||||||
+ "standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1")
|
+ "standinginstructions/template?fromClientId=1&fromAccountType=2&fromAccountId=1&transferType=1")
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsTemplateResponse.class)))
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsTemplateResponse.class)))
|
||||||
public StandingInstructionData template(@BeanParam StandingInstructionSearchParam instructionParam) {
|
public StandingInstructionData template(@BeanParam StandingInstructionSearchParam instructionParam) {
|
||||||
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||||
|
|
||||||
return standingInstructionReadPlatformService.retrieveTemplate(instructionParam.getFromOfficeId(),
|
return standingInstructionReadPlatformService.retrieveTemplate(instructionParam.getFromOfficeId(),
|
||||||
instructionParam.getFromClientId(), instructionParam.getFromAccountId(), instructionParam.getFromAccountType(),
|
instructionParam.getFromClientId(), instructionParam.getFromAccountId(), instructionParam.getFromAccountType(),
|
||||||
instructionParam.getToOfficeId(), instructionParam.getToClientId(), instructionParam.getToAccountId(),
|
instructionParam.getToOfficeId(), instructionParam.getToClientId(), instructionParam.getToAccountId(),
|
||||||
instructionParam.getToAccountType(), instructionParam.getTransferType());
|
instructionParam.getToAccountType(), instructionParam.getTransferType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
@Operation(summary = "Create new Standing Instruction", description = "Ability to create new instruction for transfer of monetary funds from one account to another")
|
@Operation(summary = "Create new Standing Instruction", description = "Ability to create new instruction for transfer of monetary funds from one account to another")
|
||||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = StandingInstructionCreationRequest.class)))
|
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = StandingInstructionCreationRequest.class)))
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsResponse.class)))
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PostStandingInstructionsResponse.class)))
|
||||||
public CommandProcessingResult create(@Parameter(hidden = true) StandingInstructionCreationRequest creationRequest) {
|
public CommandProcessingResult create(@Parameter(hidden = true) StandingInstructionCreationRequest creationRequest) {
|
||||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction()
|
final CommandWrapper commandRequest = new CommandWrapperBuilder().createStandingInstruction()
|
||||||
.withJson(toApiJsonSerializer.serialize(creationRequest)).build();
|
.withJson(toApiJsonSerializer.serialize(creationRequest)).build();
|
||||||
|
|
||||||
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("{standingInstructionId}")
|
@Path("{standingInstructionId}")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
@Operation(summary = "Update Standing Instruction | Delete Standing Instruction", description = "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n"
|
@Operation(summary = "Update Standing Instruction | Delete Standing Instruction", description = "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n"
|
||||||
+ "\n" + "PUT https://DomainName/api/v1/standinginstructions/1?command=update\n" + "\n\n"
|
+ "\n" + "PUT https://DomainName/api/v1/standinginstructions/1?command=update\n" + "\n\n"
|
||||||
+ "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n" + "\n"
|
+ "Ability to modify existing instruction for transfer of monetary funds from one account to another.\n" + "\n"
|
||||||
+ "PUT https://DomainName/api/v1/standinginstructions/1?command=delete")
|
+ "PUT https://DomainName/api/v1/standinginstructions/1?command=delete")
|
||||||
@RequestBody(content = @Content(schema = @Schema(implementation = StandingInstructionUpdatesRequest.class)))
|
@RequestBody(content = @Content(schema = @Schema(implementation = StandingInstructionUpdatesRequest.class)))
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdResponse.class)))
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.PutStandingInstructionsStandingInstructionIdResponse.class)))
|
||||||
public CommandProcessingResult update(
|
public CommandProcessingResult update(
|
||||||
@PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId,
|
@PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId,
|
||||||
@Parameter(hidden = true) StandingInstructionUpdatesRequest updatesRequest,
|
@Parameter(hidden = true) StandingInstructionUpdatesRequest updatesRequest,
|
||||||
@QueryParam("command") @Parameter(description = "command") final String commandParam) {
|
@QueryParam("command") @Parameter(description = "command") final String commandParam) {
|
||||||
|
|
||||||
final String serializedUpdatesRequest = toApiJsonSerializer.serialize(updatesRequest);
|
final String serializedUpdatesRequest = toApiJsonSerializer.serialize(updatesRequest);
|
||||||
final CommandWrapper commandRequest = COMMAND_HANDLER_REGISTRY.execute(commandParam, standingInstructionId,
|
final CommandWrapper commandRequest = COMMAND_HANDLER_REGISTRY.execute(commandParam, standingInstructionId,
|
||||||
serializedUpdatesRequest, new UnrecognizedQueryParamException("command", commandParam));
|
serializedUpdatesRequest, new UnrecognizedQueryParamException("command", commandParam));
|
||||||
|
|
||||||
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
@Operation(summary = "List Standing Instructions", description = "Example Requests:\n" + "\n" + "standinginstructions")
|
@Operation(summary = "List Standing Instructions", description = "Example Requests:\n" + "\n" + "standinginstructions")
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsResponse.class)))
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsResponse.class)))
|
||||||
public Page<StandingInstructionData> retrieveAll(
|
public Page<StandingInstructionData> retrieveAll(
|
||||||
@QueryParam("externalId") @Parameter(description = "externalId") final String externalId,
|
@QueryParam("externalId") @Parameter(description = "externalId") final String externalId,
|
||||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
||||||
@QueryParam("transferType") @Parameter(description = "transferType") final Integer transferType,
|
@QueryParam("transferType") @Parameter(description = "transferType") final Integer transferType,
|
||||||
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
||||||
@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
@QueryParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||||
@QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccount,
|
@QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccount,
|
||||||
@QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType) {
|
@QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType) {
|
||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||||
|
|
||||||
sqlValidator.validate(orderBy);
|
sqlValidator.validate(orderBy);
|
||||||
sqlValidator.validate(sortOrder);
|
sqlValidator.validate(sortOrder);
|
||||||
sqlValidator.validate(externalId);
|
sqlValidator.validate(externalId);
|
||||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||||
|
|
||||||
final LocalDate startDateRange = null;
|
final LocalDate startDateRange = null;
|
||||||
final LocalDate endDateRange = null;
|
final LocalDate endDateRange = null;
|
||||||
StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId,
|
StandingInstructionDTO standingInstructionDTO = new StandingInstructionDTO(searchParameters, transferType, clientName, clientId,
|
||||||
fromAccount, fromAccountType, startDateRange, endDateRange);
|
fromAccount, fromAccountType, startDateRange, endDateRange);
|
||||||
|
|
||||||
return standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO);
|
return standingInstructionReadPlatformService.retrieveAll(standingInstructionDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{standingInstructionId}")
|
@Path("{standingInstructionId}")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
@Operation(summary = "Retrieve Standing Instruction", description = "Example Requests :\n" + "\n" + "standinginstructions/1")
|
@Operation(summary = "Retrieve Standing Instruction", description = "Example Requests :\n" + "\n" + "standinginstructions/1")
|
||||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsStandingInstructionIdResponse.class)))
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StandingInstructionApiResourceSwagger.GetStandingInstructionsStandingInstructionIdResponse.class)))
|
||||||
public StandingInstructionData retrieveOne(
|
public StandingInstructionData retrieveOne(
|
||||||
@PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId,
|
@PathParam("standingInstructionId") @Parameter(description = "standingInstructionId") final Long standingInstructionId,
|
||||||
@Context final UriInfo uriInfo, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId,
|
@Context final UriInfo uriInfo, @QueryParam("externalId") @Parameter(description = "externalId") final String externalId,
|
||||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
context.authenticatedUser().validateHasReadPermission(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||||
|
|
||||||
sqlValidator.validate(orderBy);
|
sqlValidator.validate(orderBy);
|
||||||
sqlValidator.validate(sortOrder);
|
sqlValidator.validate(sortOrder);
|
||||||
StandingInstructionData standingInstructionData = standingInstructionReadPlatformService.retrieveOne(standingInstructionId);
|
StandingInstructionData standingInstructionData = standingInstructionReadPlatformService.retrieveOne(standingInstructionId);
|
||||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||||
if (!associationParameters.isEmpty()) {
|
if (!associationParameters.isEmpty()) {
|
||||||
if (associationParameters.contains("all")) {
|
if (associationParameters.contains("all")) {
|
||||||
associationParameters.addAll(Arrays.asList("transactions", "template"));
|
associationParameters.addAll(Arrays.asList("transactions", "template"));
|
||||||
}
|
}
|
||||||
if (associationParameters.contains("transactions")) {
|
if (associationParameters.contains("transactions")) {
|
||||||
Page<AccountTransferData> transfers = accountTransfersReadPlatformService
|
Page<AccountTransferData> transfers = accountTransfersReadPlatformService
|
||||||
.retrieveByStandingInstruction(standingInstructionId, searchParameters);
|
.retrieveByStandingInstruction(standingInstructionId, searchParameters);
|
||||||
standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers);
|
standingInstructionData = StandingInstructionData.withTransferData(standingInstructionData, transfers);
|
||||||
}
|
}
|
||||||
if (associationParameters.contains("template")) {
|
if (associationParameters.contains("template")) {
|
||||||
final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate(
|
final StandingInstructionData templateData = standingInstructionReadPlatformService.retrieveTemplate(
|
||||||
standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(),
|
standingInstructionData.getFromClient().getOfficeId(), standingInstructionData.getFromClient().getId(),
|
||||||
standingInstructionData.getFromAccount().getId(), standingInstructionData.getFromAccountType().getValue(),
|
standingInstructionData.getFromAccount().getId(), standingInstructionData.fromAccountTypeEnum().getValue(),
|
||||||
standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(),
|
standingInstructionData.getToClient().getOfficeId(), standingInstructionData.getToClient().getId(),
|
||||||
standingInstructionData.getToAccount().getId(), standingInstructionData.getToAccountType().getValue(),
|
standingInstructionData.getToAccount().getId(), standingInstructionData.toAccountTypeEnum().getValue(),
|
||||||
standingInstructionData.getTransferType().getValue());
|
standingInstructionData.transferTypeEnum().getValue());
|
||||||
standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData);
|
standingInstructionData = StandingInstructionData.withTemplateData(standingInstructionData, templateData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return standingInstructionData;
|
return standingInstructionData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-79
@@ -1,79 +1,79 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.data;
|
package org.apache.fineract.portfolio.account.data;
|
||||||
|
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class AccountTransfersDetailDataValidator {
|
public class AccountTransfersDetailDataValidator {
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AccountTransfersDetailDataValidator(final FromJsonHelper fromApiJsonHelper) {
|
public AccountTransfersDetailDataValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
|
public void validate(final JsonCommand command, final DataValidatorBuilder baseDataValidator) {
|
||||||
|
|
||||||
final JsonElement element = command.parsedJson();
|
final JsonElement element = command.parsedJson();
|
||||||
|
|
||||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter(fromOfficeIdParamName).value(fromOfficeId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter(fromClientIdParamName).value(fromClientId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element);
|
final Long fromAccountId = this.fromApiJsonHelper.extractLongNamed(fromAccountIdParamName, element);
|
||||||
baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter(fromAccountIdParamName).value(fromAccountId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
|
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
|
||||||
baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull()
|
baseDataValidator.reset().parameter(fromAccountTypeParamName).value(fromAccountType).notNull()
|
||||||
.isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2));
|
.isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
|
||||||
|
|
||||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter(toOfficeIdParamName).value(toOfficeId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter(toClientIdParamName).value(toClientId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element);
|
final Long toAccountId = this.fromApiJsonHelper.extractLongNamed(toAccountIdParamName, element);
|
||||||
baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter(toAccountIdParamName).value(toAccountId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
||||||
baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1),
|
baseDataValidator.reset().parameter(toAccountTypeParamName).value(toAccountType).notNull().isOneOfTheseValues(Integer.valueOf(1),
|
||||||
Integer.valueOf(2));
|
Integer.valueOf(2), Integer.valueOf(3));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+359
-329
@@ -1,329 +1,359 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.data;
|
package org.apache.fineract.portfolio.account.data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
import org.apache.fineract.infrastructure.core.service.Page;
|
import org.apache.fineract.infrastructure.core.service.Page;
|
||||||
import org.apache.fineract.organisation.office.data.OfficeData;
|
import org.apache.fineract.organisation.office.data.OfficeData;
|
||||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
||||||
import org.apache.fineract.portfolio.client.data.ClientData;
|
import org.apache.fineract.portfolio.client.data.ClientData;
|
||||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable data object representing a savings account.
|
* Immutable data object representing a savings account.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class StandingInstructionData {
|
public final class StandingInstructionData {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Long id;
|
private final Long id;
|
||||||
@Getter
|
@Getter
|
||||||
private final Long accountDetailId;
|
private final Long accountDetailId;
|
||||||
@Getter
|
@Getter
|
||||||
private final String name;
|
private final String name;
|
||||||
private final OfficeData fromOffice;
|
@Getter
|
||||||
@Getter
|
private final OfficeData fromOffice;
|
||||||
private final ClientData fromClient;
|
@Getter
|
||||||
private final EnumOptionData fromAccountType;
|
private final ClientData fromClient;
|
||||||
@Getter
|
@Getter
|
||||||
private final PortfolioAccountData fromAccount;
|
private final EnumOptionData fromAccountType;
|
||||||
private final OfficeData toOffice;
|
@Getter
|
||||||
@Getter
|
private final PortfolioAccountData fromAccount;
|
||||||
private final ClientData toClient;
|
@Getter
|
||||||
private final EnumOptionData toAccountType;
|
private final OfficeData toOffice;
|
||||||
@Getter
|
@Getter
|
||||||
private final PortfolioAccountData toAccount;
|
private final ClientData toClient;
|
||||||
private final EnumOptionData transferType;
|
@Getter
|
||||||
private final EnumOptionData priority;
|
private final EnumOptionData toAccountType;
|
||||||
private final EnumOptionData instructionType;
|
@Getter
|
||||||
@Getter
|
private final PortfolioAccountData toAccount;
|
||||||
private final EnumOptionData status;
|
@Getter
|
||||||
@Getter
|
private final EnumOptionData transferType;
|
||||||
private final BigDecimal amount;
|
@Getter
|
||||||
@Getter
|
private final EnumOptionData priority;
|
||||||
private final LocalDate validFrom;
|
@Getter
|
||||||
private final LocalDate validTill;
|
private final EnumOptionData instructionType;
|
||||||
private final EnumOptionData recurrenceType;
|
@Getter
|
||||||
private final EnumOptionData recurrenceFrequency;
|
private final EnumOptionData status;
|
||||||
@Getter
|
@Getter
|
||||||
private final Integer recurrenceInterval;
|
private final BigDecimal amount;
|
||||||
private final MonthDay recurrenceOnMonthDay;
|
@Getter
|
||||||
private final Page<AccountTransferData> transactions;
|
private final LocalDate validFrom;
|
||||||
|
@Getter
|
||||||
private final Collection<OfficeData> fromOfficeOptions;
|
private final LocalDate validTill;
|
||||||
private final Collection<ClientData> fromClientOptions;
|
@Getter
|
||||||
private final Collection<EnumOptionData> fromAccountTypeOptions;
|
private final EnumOptionData recurrenceType;
|
||||||
private final Collection<PortfolioAccountData> fromAccountOptions;
|
@Getter
|
||||||
private final Collection<OfficeData> toOfficeOptions;
|
private final EnumOptionData recurrenceFrequency;
|
||||||
private final Collection<ClientData> toClientOptions;
|
@Getter
|
||||||
private final Collection<EnumOptionData> toAccountTypeOptions;
|
private final Integer recurrenceInterval;
|
||||||
private final Collection<PortfolioAccountData> toAccountOptions;
|
@Getter
|
||||||
private final Collection<EnumOptionData> transferTypeOptions;
|
private final MonthDay recurrenceOnMonthDay;
|
||||||
private final Collection<EnumOptionData> statusOptions;
|
@Getter
|
||||||
private final Collection<EnumOptionData> instructionTypeOptions;
|
private final Page<AccountTransferData> transactions;
|
||||||
private final Collection<EnumOptionData> priorityOptions;
|
|
||||||
private final Collection<EnumOptionData> recurrenceTypeOptions;
|
@Getter
|
||||||
private final Collection<EnumOptionData> recurrenceFrequencyOptions;
|
private final Collection<OfficeData> fromOfficeOptions;
|
||||||
|
@Getter
|
||||||
public static StandingInstructionData template(final Collection<OfficeData> fromOfficeOptions,
|
private final Collection<ClientData> fromClientOptions;
|
||||||
final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions,
|
@Getter
|
||||||
final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions,
|
private final Collection<EnumOptionData> fromAccountTypeOptions;
|
||||||
final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions,
|
@Getter
|
||||||
final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions,
|
private final Collection<PortfolioAccountData> fromAccountOptions;
|
||||||
final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions,
|
@Getter
|
||||||
final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions,
|
private final Collection<OfficeData> toOfficeOptions;
|
||||||
final Collection<EnumOptionData> recurrenceFrequencyOptions) {
|
@Getter
|
||||||
|
private final Collection<ClientData> toClientOptions;
|
||||||
final Long id = null;
|
@Getter
|
||||||
final Long accountDetailId = null;
|
private final Collection<EnumOptionData> toAccountTypeOptions;
|
||||||
final String name = null;
|
@Getter
|
||||||
final OfficeData fromOffice = null;
|
private final Collection<PortfolioAccountData> toAccountOptions;
|
||||||
final OfficeData toOffice = null;
|
@Getter
|
||||||
final ClientData fromClient = null;
|
private final Collection<EnumOptionData> transferTypeOptions;
|
||||||
final ClientData toClient = null;
|
@Getter
|
||||||
final EnumOptionData fromAccountType = null;
|
private final Collection<EnumOptionData> statusOptions;
|
||||||
final PortfolioAccountData fromAccount = null;
|
@Getter
|
||||||
final EnumOptionData toAccountType = null;
|
private final Collection<EnumOptionData> instructionTypeOptions;
|
||||||
final PortfolioAccountData toAccount = null;
|
@Getter
|
||||||
final EnumOptionData transferType = null;
|
private final Collection<EnumOptionData> priorityOptions;
|
||||||
final EnumOptionData priority = null;
|
@Getter
|
||||||
final EnumOptionData instructionType = null;
|
private final Collection<EnumOptionData> recurrenceTypeOptions;
|
||||||
final EnumOptionData status = null;
|
@Getter
|
||||||
final BigDecimal amount = null;
|
private final Collection<EnumOptionData> recurrenceFrequencyOptions;
|
||||||
final LocalDate validFrom = null;
|
|
||||||
final LocalDate validTill = null;
|
public static StandingInstructionData template(final Collection<OfficeData> fromOfficeOptions,
|
||||||
final EnumOptionData recurrenceType = null;
|
final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions,
|
||||||
final EnumOptionData recurrenceFrequency = null;
|
final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions,
|
||||||
final Integer recurrenceInterval = null;
|
final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions,
|
||||||
final MonthDay recurrenceOnMonthDay = null;
|
final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions,
|
||||||
final Page<AccountTransferData> transactions = null;
|
final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions,
|
||||||
|
final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions,
|
||||||
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
|
final Collection<EnumOptionData> recurrenceFrequencyOptions) {
|
||||||
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
|
|
||||||
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
|
final Long id = null;
|
||||||
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
|
final Long accountDetailId = null;
|
||||||
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
|
final String name = null;
|
||||||
recurrenceFrequencyOptions);
|
final OfficeData fromOffice = null;
|
||||||
}
|
final OfficeData toOffice = null;
|
||||||
|
final ClientData fromClient = null;
|
||||||
public static StandingInstructionData instance(final Long id, final Long accountDetailId, final String name,
|
final ClientData toClient = null;
|
||||||
final OfficeData fromOffice, final OfficeData toOffice, final ClientData fromClient, final ClientData toClient,
|
final EnumOptionData fromAccountType = null;
|
||||||
final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final EnumOptionData toAccountType,
|
final PortfolioAccountData fromAccount = null;
|
||||||
final PortfolioAccountData toAccount, final EnumOptionData transferType, final EnumOptionData priority,
|
final EnumOptionData toAccountType = null;
|
||||||
final EnumOptionData instructionType, final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom,
|
final PortfolioAccountData toAccount = null;
|
||||||
final LocalDate validTill, final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency,
|
final EnumOptionData transferType = null;
|
||||||
final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) {
|
final EnumOptionData priority = null;
|
||||||
final Page<AccountTransferData> transactions = null;
|
final EnumOptionData instructionType = null;
|
||||||
final Collection<OfficeData> fromOfficeOptions = null;
|
final EnumOptionData status = null;
|
||||||
final Collection<ClientData> fromClientOptions = null;
|
final BigDecimal amount = null;
|
||||||
final Collection<EnumOptionData> fromAccountTypeOptions = null;
|
final LocalDate validFrom = null;
|
||||||
final Collection<PortfolioAccountData> fromAccountOptions = null;
|
final LocalDate validTill = null;
|
||||||
final Collection<OfficeData> toOfficeOptions = null;
|
final EnumOptionData recurrenceType = null;
|
||||||
final Collection<ClientData> toClientOptions = null;
|
final EnumOptionData recurrenceFrequency = null;
|
||||||
final Collection<EnumOptionData> toAccountTypeOptions = null;
|
final Integer recurrenceInterval = null;
|
||||||
final Collection<PortfolioAccountData> toAccountOptions = null;
|
final MonthDay recurrenceOnMonthDay = null;
|
||||||
final Collection<EnumOptionData> transferTypeOptions = null;
|
final Page<AccountTransferData> transactions = null;
|
||||||
final Collection<EnumOptionData> statusOptions = null;
|
|
||||||
final Collection<EnumOptionData> instructionTypeOptions = null;
|
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
|
||||||
final Collection<EnumOptionData> priorityOptions = null;
|
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
|
||||||
final Collection<EnumOptionData> recurrenceTypeOptions = null;
|
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
|
||||||
final Collection<EnumOptionData> recurrenceFrequencyOptions = null;
|
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
|
||||||
|
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
|
||||||
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
|
recurrenceFrequencyOptions);
|
||||||
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
|
}
|
||||||
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
|
|
||||||
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
|
public static StandingInstructionData instance(final Long id, final Long accountDetailId, final String name,
|
||||||
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
|
final OfficeData fromOffice, final OfficeData toOffice, final ClientData fromClient, final ClientData toClient,
|
||||||
recurrenceFrequencyOptions);
|
final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final EnumOptionData toAccountType,
|
||||||
}
|
final PortfolioAccountData toAccount, final EnumOptionData transferType, final EnumOptionData priority,
|
||||||
|
final EnumOptionData instructionType, final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom,
|
||||||
public static StandingInstructionData withTemplateData(StandingInstructionData instructionData, StandingInstructionData templateData) {
|
final LocalDate validTill, final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency,
|
||||||
return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name,
|
final Integer recurrenceInterval, final MonthDay recurrenceOnMonthDay) {
|
||||||
instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount,
|
final Page<AccountTransferData> transactions = null;
|
||||||
instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount,
|
final Collection<OfficeData> fromOfficeOptions = null;
|
||||||
instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status,
|
final Collection<ClientData> fromClientOptions = null;
|
||||||
instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType,
|
final Collection<EnumOptionData> fromAccountTypeOptions = null;
|
||||||
instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay,
|
final Collection<PortfolioAccountData> fromAccountOptions = null;
|
||||||
instructionData.transactions, templateData.fromOfficeOptions, templateData.fromClientOptions,
|
final Collection<OfficeData> toOfficeOptions = null;
|
||||||
templateData.fromAccountTypeOptions, templateData.fromAccountOptions, templateData.toOfficeOptions,
|
final Collection<ClientData> toClientOptions = null;
|
||||||
templateData.toClientOptions, templateData.toAccountTypeOptions, templateData.toAccountOptions,
|
final Collection<EnumOptionData> toAccountTypeOptions = null;
|
||||||
templateData.transferTypeOptions, templateData.statusOptions, templateData.instructionTypeOptions,
|
final Collection<PortfolioAccountData> toAccountOptions = null;
|
||||||
templateData.priorityOptions, templateData.recurrenceTypeOptions, templateData.recurrenceFrequencyOptions);
|
final Collection<EnumOptionData> transferTypeOptions = null;
|
||||||
}
|
final Collection<EnumOptionData> statusOptions = null;
|
||||||
|
final Collection<EnumOptionData> instructionTypeOptions = null;
|
||||||
private StandingInstructionData(final Long id, final Long accountDetailId, final String name, final OfficeData fromOffice,
|
final Collection<EnumOptionData> priorityOptions = null;
|
||||||
final ClientData fromClient, final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount,
|
final Collection<EnumOptionData> recurrenceTypeOptions = null;
|
||||||
final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount,
|
final Collection<EnumOptionData> recurrenceFrequencyOptions = null;
|
||||||
final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData instructionType,
|
|
||||||
final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, LocalDate validTill,
|
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
|
||||||
final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final Integer recurrenceInterval,
|
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
|
||||||
final MonthDay recurrenceOnMonthDay, final Page<AccountTransferData> transactions,
|
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
|
||||||
final Collection<OfficeData> fromOfficeOptions, final Collection<ClientData> fromClientOptions,
|
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
|
||||||
final Collection<EnumOptionData> fromAccountTypeOptions, final Collection<PortfolioAccountData> fromAccountOptions,
|
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
|
||||||
final Collection<OfficeData> toOfficeOptions, final Collection<ClientData> toClientOptions,
|
recurrenceFrequencyOptions);
|
||||||
final Collection<EnumOptionData> toAccountTypeOptions, final Collection<PortfolioAccountData> toAccountOptions,
|
}
|
||||||
final Collection<EnumOptionData> transferTypeOptions, final Collection<EnumOptionData> statusOptions,
|
|
||||||
final Collection<EnumOptionData> instructionTypeOptions, final Collection<EnumOptionData> priorityOptions,
|
public static StandingInstructionData withTemplateData(StandingInstructionData instructionData, StandingInstructionData templateData) {
|
||||||
final Collection<EnumOptionData> recurrenceTypeOptions, final Collection<EnumOptionData> recurrenceFrequencyOptions) {
|
return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name,
|
||||||
this.id = id;
|
instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount,
|
||||||
this.accountDetailId = accountDetailId;
|
instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount,
|
||||||
this.name = name;
|
instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status,
|
||||||
this.fromOffice = fromOffice;
|
instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType,
|
||||||
this.fromClient = fromClient;
|
instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay,
|
||||||
this.fromAccountType = fromAccountType;
|
instructionData.transactions, templateData.fromOfficeOptions, templateData.fromClientOptions,
|
||||||
this.fromAccount = fromAccount;
|
templateData.fromAccountTypeOptions, templateData.fromAccountOptions, templateData.toOfficeOptions,
|
||||||
this.toOffice = toOffice;
|
templateData.toClientOptions, templateData.toAccountTypeOptions, templateData.toAccountOptions,
|
||||||
this.toClient = toClient;
|
templateData.transferTypeOptions, templateData.statusOptions, templateData.instructionTypeOptions,
|
||||||
this.toAccountType = toAccountType;
|
templateData.priorityOptions, templateData.recurrenceTypeOptions, templateData.recurrenceFrequencyOptions);
|
||||||
this.toAccount = toAccount;
|
}
|
||||||
this.transferType = transferType;
|
|
||||||
|
private StandingInstructionData(final Long id, final Long accountDetailId, final String name, final OfficeData fromOffice,
|
||||||
this.priority = priority;
|
final ClientData fromClient, final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount,
|
||||||
this.instructionType = instructionType;
|
final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount,
|
||||||
this.status = status;
|
final EnumOptionData transferType, final EnumOptionData priority, final EnumOptionData instructionType,
|
||||||
this.amount = amount;
|
final EnumOptionData status, final BigDecimal amount, final LocalDate validFrom, LocalDate validTill,
|
||||||
this.validFrom = validFrom;
|
final EnumOptionData recurrenceType, final EnumOptionData recurrenceFrequency, final Integer recurrenceInterval,
|
||||||
this.validTill = validTill;
|
final MonthDay recurrenceOnMonthDay, final Page<AccountTransferData> transactions,
|
||||||
this.recurrenceType = recurrenceType;
|
final Collection<OfficeData> fromOfficeOptions, final Collection<ClientData> fromClientOptions,
|
||||||
this.recurrenceFrequency = recurrenceFrequency;
|
final Collection<EnumOptionData> fromAccountTypeOptions, final Collection<PortfolioAccountData> fromAccountOptions,
|
||||||
this.recurrenceInterval = recurrenceInterval;
|
final Collection<OfficeData> toOfficeOptions, final Collection<ClientData> toClientOptions,
|
||||||
this.recurrenceOnMonthDay = recurrenceOnMonthDay;
|
final Collection<EnumOptionData> toAccountTypeOptions, final Collection<PortfolioAccountData> toAccountOptions,
|
||||||
|
final Collection<EnumOptionData> transferTypeOptions, final Collection<EnumOptionData> statusOptions,
|
||||||
this.fromOfficeOptions = fromOfficeOptions;
|
final Collection<EnumOptionData> instructionTypeOptions, final Collection<EnumOptionData> priorityOptions,
|
||||||
this.fromClientOptions = fromClientOptions;
|
final Collection<EnumOptionData> recurrenceTypeOptions, final Collection<EnumOptionData> recurrenceFrequencyOptions) {
|
||||||
this.fromAccountTypeOptions = fromAccountTypeOptions;
|
this.id = id;
|
||||||
this.fromAccountOptions = fromAccountOptions;
|
this.accountDetailId = accountDetailId;
|
||||||
this.toOfficeOptions = toOfficeOptions;
|
this.name = name;
|
||||||
this.toClientOptions = toClientOptions;
|
this.fromOffice = fromOffice;
|
||||||
this.toAccountTypeOptions = toAccountTypeOptions;
|
this.fromClient = fromClient;
|
||||||
this.toAccountOptions = toAccountOptions;
|
this.fromAccountType = fromAccountType;
|
||||||
this.transferTypeOptions = transferTypeOptions;
|
this.fromAccount = fromAccount;
|
||||||
this.statusOptions = statusOptions;
|
this.toOffice = toOffice;
|
||||||
this.instructionTypeOptions = instructionTypeOptions;
|
this.toClient = toClient;
|
||||||
this.priorityOptions = priorityOptions;
|
this.toAccountType = toAccountType;
|
||||||
this.recurrenceTypeOptions = recurrenceTypeOptions;
|
this.toAccount = toAccount;
|
||||||
this.recurrenceFrequencyOptions = recurrenceFrequencyOptions;
|
this.transferType = transferType;
|
||||||
this.transactions = transactions;
|
|
||||||
}
|
this.priority = priority;
|
||||||
|
this.instructionType = instructionType;
|
||||||
public static StandingInstructionData template(OfficeData fromOffice, ClientData fromClient, EnumOptionData fromAccountType,
|
this.status = status;
|
||||||
PortfolioAccountData fromAccount, LocalDate transferDate, OfficeData toOffice, ClientData toClient,
|
this.amount = amount;
|
||||||
EnumOptionData toAccountType, PortfolioAccountData toAccount, final Collection<OfficeData> fromOfficeOptions,
|
this.validFrom = validFrom;
|
||||||
final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions,
|
this.validTill = validTill;
|
||||||
final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions,
|
this.recurrenceType = recurrenceType;
|
||||||
final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions,
|
this.recurrenceFrequency = recurrenceFrequency;
|
||||||
final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions,
|
this.recurrenceInterval = recurrenceInterval;
|
||||||
final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions,
|
this.recurrenceOnMonthDay = recurrenceOnMonthDay;
|
||||||
final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions,
|
|
||||||
final Collection<EnumOptionData> recurrenceFrequencyOptions) {
|
this.fromOfficeOptions = fromOfficeOptions;
|
||||||
final Long id = null;
|
this.fromClientOptions = fromClientOptions;
|
||||||
final Long accountDetailId = null;
|
this.fromAccountTypeOptions = fromAccountTypeOptions;
|
||||||
final String name = null;
|
this.fromAccountOptions = fromAccountOptions;
|
||||||
final EnumOptionData transferType = null;
|
this.toOfficeOptions = toOfficeOptions;
|
||||||
final EnumOptionData priority = null;
|
this.toClientOptions = toClientOptions;
|
||||||
final EnumOptionData instructionType = null;
|
this.toAccountTypeOptions = toAccountTypeOptions;
|
||||||
final EnumOptionData status = null;
|
this.toAccountOptions = toAccountOptions;
|
||||||
final BigDecimal amount = null;
|
this.transferTypeOptions = transferTypeOptions;
|
||||||
final LocalDate validFrom = null;
|
this.statusOptions = statusOptions;
|
||||||
final LocalDate validTill = null;
|
this.instructionTypeOptions = instructionTypeOptions;
|
||||||
final EnumOptionData recurrenceType = null;
|
this.priorityOptions = priorityOptions;
|
||||||
final EnumOptionData recurrenceFrequency = null;
|
this.recurrenceTypeOptions = recurrenceTypeOptions;
|
||||||
final Integer recurrenceInterval = null;
|
this.recurrenceFrequencyOptions = recurrenceFrequencyOptions;
|
||||||
final MonthDay recurrenceOnMonthDay = null;
|
this.transactions = transactions;
|
||||||
final Page<AccountTransferData> transactions = null;
|
}
|
||||||
|
|
||||||
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
|
public static StandingInstructionData template(OfficeData fromOffice, ClientData fromClient, EnumOptionData fromAccountType,
|
||||||
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
|
PortfolioAccountData fromAccount, LocalDate transferDate, OfficeData toOffice, ClientData toClient,
|
||||||
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
|
EnumOptionData toAccountType, PortfolioAccountData toAccount, final Collection<OfficeData> fromOfficeOptions,
|
||||||
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
|
final Collection<ClientData> fromClientOptions, final Collection<EnumOptionData> fromAccountTypeOptions,
|
||||||
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
|
final Collection<PortfolioAccountData> fromAccountOptions, final Collection<OfficeData> toOfficeOptions,
|
||||||
recurrenceFrequencyOptions);
|
final Collection<ClientData> toClientOptions, final Collection<EnumOptionData> toAccountTypeOptions,
|
||||||
}
|
final Collection<PortfolioAccountData> toAccountOptions, final Collection<EnumOptionData> transferTypeOptions,
|
||||||
|
final Collection<EnumOptionData> statusOptions, final Collection<EnumOptionData> instructionTypeOptions,
|
||||||
public static StandingInstructionData withTransferData(StandingInstructionData instructionData,
|
final Collection<EnumOptionData> priorityOptions, final Collection<EnumOptionData> recurrenceTypeOptions,
|
||||||
final Page<AccountTransferData> transactions) {
|
final Collection<EnumOptionData> recurrenceFrequencyOptions) {
|
||||||
return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name,
|
final Long id = null;
|
||||||
instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount,
|
final Long accountDetailId = null;
|
||||||
instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount,
|
final String name = null;
|
||||||
instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status,
|
final EnumOptionData transferType = null;
|
||||||
instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType,
|
final EnumOptionData priority = null;
|
||||||
instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, transactions,
|
final EnumOptionData instructionType = null;
|
||||||
instructionData.fromOfficeOptions, instructionData.fromClientOptions, instructionData.fromAccountTypeOptions,
|
final EnumOptionData status = null;
|
||||||
instructionData.fromAccountOptions, instructionData.toOfficeOptions, instructionData.toClientOptions,
|
final BigDecimal amount = null;
|
||||||
instructionData.toAccountTypeOptions, instructionData.toAccountOptions, instructionData.transferTypeOptions,
|
final LocalDate validFrom = null;
|
||||||
instructionData.statusOptions, instructionData.instructionTypeOptions, instructionData.priorityOptions,
|
final LocalDate validTill = null;
|
||||||
instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions);
|
final EnumOptionData recurrenceType = null;
|
||||||
}
|
final EnumOptionData recurrenceFrequency = null;
|
||||||
|
final Integer recurrenceInterval = null;
|
||||||
public StandingInstructionType getInstructionType() {
|
final MonthDay recurrenceOnMonthDay = null;
|
||||||
return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null);
|
final Page<AccountTransferData> transactions = null;
|
||||||
|
|
||||||
}
|
return new StandingInstructionData(id, accountDetailId, name, fromOffice, fromClient, fromAccountType, fromAccount, toOffice,
|
||||||
|
toClient, toAccountType, toAccount, transferType, priority, instructionType, status, amount, validFrom, validTill,
|
||||||
public AccountTransferRecurrenceType getRecurrenceType() {
|
recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay, transactions, fromOfficeOptions,
|
||||||
return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null);
|
fromClientOptions, fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions,
|
||||||
}
|
toAccountOptions, transferTypeOptions, statusOptions, instructionTypeOptions, priorityOptions, recurrenceTypeOptions,
|
||||||
|
recurrenceFrequencyOptions);
|
||||||
public PeriodFrequencyType getRecurrenceFrequency() {
|
}
|
||||||
return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null);
|
|
||||||
}
|
public static StandingInstructionData withTransferData(StandingInstructionData instructionData,
|
||||||
|
final Page<AccountTransferData> transactions) {
|
||||||
public PortfolioAccountType getFromAccountType() {
|
return new StandingInstructionData(instructionData.id, instructionData.accountDetailId, instructionData.name,
|
||||||
return Optional.ofNullable(this.fromAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null);
|
instructionData.fromOffice, instructionData.fromClient, instructionData.fromAccountType, instructionData.fromAccount,
|
||||||
}
|
instructionData.toOffice, instructionData.toClient, instructionData.toAccountType, instructionData.toAccount,
|
||||||
|
instructionData.transferType, instructionData.priority, instructionData.instructionType, instructionData.status,
|
||||||
public PortfolioAccountType getToAccountType() {
|
instructionData.amount, instructionData.validFrom, instructionData.validTill, instructionData.recurrenceType,
|
||||||
return Optional.ofNullable(this.toAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null);
|
instructionData.recurrenceFrequency, instructionData.recurrenceInterval, instructionData.recurrenceOnMonthDay, transactions,
|
||||||
}
|
instructionData.fromOfficeOptions, instructionData.fromClientOptions, instructionData.fromAccountTypeOptions,
|
||||||
|
instructionData.fromAccountOptions, instructionData.toOfficeOptions, instructionData.toClientOptions,
|
||||||
public AccountTransferType getTransferType() {
|
instructionData.toAccountTypeOptions, instructionData.toAccountOptions, instructionData.transferTypeOptions,
|
||||||
return Optional.ofNullable(this.transferType).map(e -> AccountTransferType.fromInt(e.getId().intValue())).orElse(null);
|
instructionData.statusOptions, instructionData.instructionTypeOptions, instructionData.priorityOptions,
|
||||||
}
|
instructionData.recurrenceTypeOptions, instructionData.recurrenceFrequencyOptions);
|
||||||
|
}
|
||||||
public Integer getRecurrenceOnDay() {
|
|
||||||
return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.DAY_OF_MONTH)).orElse(0);
|
/**
|
||||||
}
|
* Returns the domain enum for instruction type — used internally by business logic.
|
||||||
|
* NOTE: renamed from getInstructionType() to avoid overriding Lombok @Getter (which must
|
||||||
public Integer getRecurrenceOnMonth() {
|
* return EnumOptionData so Jackson serializes it as an object, not a plain string).
|
||||||
return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.MONTH_OF_YEAR)).orElse(0);
|
*/
|
||||||
}
|
public StandingInstructionType instructionTypeEnum() {
|
||||||
|
return Optional.ofNullable(this.instructionType).map(e -> StandingInstructionType.fromInt(e.getId().intValue())).orElse(null);
|
||||||
public Integer toTransferType() {
|
}
|
||||||
Integer transferType = null;
|
|
||||||
AccountTransferType accountTransferType = getTransferType();
|
public AccountTransferRecurrenceType recurrenceTypeEnum() {
|
||||||
if (accountTransferType.isChargePayment()) {
|
return Optional.ofNullable(this.recurrenceType).map(e -> AccountTransferRecurrenceType.fromInt(e.getId().intValue())).orElse(null);
|
||||||
transferType = LoanTransactionType.CHARGE_PAYMENT.getValue();
|
}
|
||||||
} else if (accountTransferType.isLoanRepayment()) {
|
|
||||||
transferType = LoanTransactionType.REPAYMENT.getValue();
|
public PeriodFrequencyType recurrenceFrequencyEnum() {
|
||||||
}
|
return Optional.ofNullable(this.recurrenceFrequency).map(e -> PeriodFrequencyType.fromInt(e.getId().intValue())).orElse(null);
|
||||||
return transferType;
|
}
|
||||||
}
|
|
||||||
|
public PortfolioAccountType fromAccountTypeEnum() {
|
||||||
}
|
return Optional.ofNullable(this.fromAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortfolioAccountType toAccountTypeEnum() {
|
||||||
|
return Optional.ofNullable(this.toAccountType).map(e -> PortfolioAccountType.fromInt(e.getId().intValue())).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferType transferTypeEnum() {
|
||||||
|
return Optional.ofNullable(this.transferType).map(e -> AccountTransferType.fromInt(e.getId().intValue())).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getRecurrenceOnDay() {
|
||||||
|
return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.DAY_OF_MONTH)).orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getRecurrenceOnMonth() {
|
||||||
|
return Optional.ofNullable(this.recurrenceOnMonthDay).map(monthDay -> monthDay.get(ChronoField.MONTH_OF_YEAR)).orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer toTransferType() {
|
||||||
|
Integer transferType = null;
|
||||||
|
AccountTransferType accountTransferType = transferTypeEnum();
|
||||||
|
if (accountTransferType != null && accountTransferType.isChargePayment()) {
|
||||||
|
transferType = LoanTransactionType.CHARGE_PAYMENT.getValue();
|
||||||
|
} else if (accountTransferType != null && accountTransferType.isLoanRepayment()) {
|
||||||
|
transferType = LoanTransactionType.REPAYMENT.getValue();
|
||||||
|
}
|
||||||
|
return transferType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+296
-294
@@ -1,294 +1,296 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.data;
|
package org.apache.fineract.portfolio.account.data;
|
||||||
|
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
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 org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
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;
|
||||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.apache.fineract.portfolio.account.AccountDetailConstants;
|
import org.apache.fineract.portfolio.account.AccountDetailConstants;
|
||||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||||
import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants;
|
import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
||||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class StandingInstructionDataValidator {
|
public class StandingInstructionDataValidator {
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator;
|
private final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator;
|
||||||
private static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName,
|
private static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName,
|
||||||
AccountDetailConstants.dateFormatParamName, AccountDetailConstants.fromOfficeIdParamName,
|
AccountDetailConstants.dateFormatParamName, AccountDetailConstants.fromOfficeIdParamName,
|
||||||
AccountDetailConstants.fromClientIdParamName, AccountDetailConstants.fromAccountTypeParamName,
|
AccountDetailConstants.fromClientIdParamName, AccountDetailConstants.fromAccountTypeParamName,
|
||||||
AccountDetailConstants.fromAccountIdParamName, AccountDetailConstants.toOfficeIdParamName,
|
AccountDetailConstants.fromAccountIdParamName, AccountDetailConstants.toOfficeIdParamName,
|
||||||
AccountDetailConstants.toClientIdParamName, AccountDetailConstants.toAccountTypeParamName,
|
AccountDetailConstants.toClientIdParamName, AccountDetailConstants.toAccountTypeParamName,
|
||||||
AccountDetailConstants.toAccountIdParamName, AccountDetailConstants.transferTypeParamName,
|
AccountDetailConstants.toAccountIdParamName, AccountDetailConstants.transferTypeParamName,
|
||||||
StandingInstructionApiConstants.priorityParamName, StandingInstructionApiConstants.instructionTypeParamName,
|
StandingInstructionApiConstants.priorityParamName, StandingInstructionApiConstants.instructionTypeParamName,
|
||||||
StandingInstructionApiConstants.statusParamName, StandingInstructionApiConstants.amountParamName,
|
StandingInstructionApiConstants.statusParamName, StandingInstructionApiConstants.amountParamName,
|
||||||
StandingInstructionApiConstants.validFromParamName, StandingInstructionApiConstants.validTillParamName,
|
StandingInstructionApiConstants.validFromParamName, StandingInstructionApiConstants.validTillParamName,
|
||||||
StandingInstructionApiConstants.recurrenceTypeParamName, StandingInstructionApiConstants.recurrenceFrequencyParamName,
|
StandingInstructionApiConstants.recurrenceTypeParamName, StandingInstructionApiConstants.recurrenceFrequencyParamName,
|
||||||
StandingInstructionApiConstants.recurrenceIntervalParamName, StandingInstructionApiConstants.recurrenceOnMonthDayParamName,
|
StandingInstructionApiConstants.recurrenceIntervalParamName, StandingInstructionApiConstants.recurrenceOnMonthDayParamName,
|
||||||
StandingInstructionApiConstants.nameParamName, StandingInstructionApiConstants.monthDayFormatParamName));
|
StandingInstructionApiConstants.nameParamName, StandingInstructionApiConstants.monthDayFormatParamName));
|
||||||
|
|
||||||
private static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName,
|
private static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(AccountDetailConstants.localeParamName,
|
||||||
AccountDetailConstants.dateFormatParamName, StandingInstructionApiConstants.priorityParamName,
|
AccountDetailConstants.dateFormatParamName, StandingInstructionApiConstants.priorityParamName,
|
||||||
StandingInstructionApiConstants.instructionTypeParamName, StandingInstructionApiConstants.statusParamName,
|
StandingInstructionApiConstants.instructionTypeParamName, StandingInstructionApiConstants.statusParamName,
|
||||||
StandingInstructionApiConstants.amountParamName, StandingInstructionApiConstants.validFromParamName,
|
StandingInstructionApiConstants.amountParamName, StandingInstructionApiConstants.validFromParamName,
|
||||||
StandingInstructionApiConstants.validTillParamName, StandingInstructionApiConstants.recurrenceTypeParamName,
|
StandingInstructionApiConstants.validTillParamName, StandingInstructionApiConstants.recurrenceTypeParamName,
|
||||||
StandingInstructionApiConstants.recurrenceFrequencyParamName, StandingInstructionApiConstants.recurrenceIntervalParamName,
|
StandingInstructionApiConstants.recurrenceFrequencyParamName, StandingInstructionApiConstants.recurrenceIntervalParamName,
|
||||||
StandingInstructionApiConstants.recurrenceOnMonthDayParamName, StandingInstructionApiConstants.monthDayFormatParamName));
|
StandingInstructionApiConstants.recurrenceOnMonthDayParamName, StandingInstructionApiConstants.monthDayFormatParamName));
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public StandingInstructionDataValidator(final FromJsonHelper fromApiJsonHelper,
|
public StandingInstructionDataValidator(final FromJsonHelper fromApiJsonHelper,
|
||||||
final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) {
|
final AccountTransfersDetailDataValidator accountTransfersDetailDataValidator) {
|
||||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator;
|
this.accountTransfersDetailDataValidator = accountTransfersDetailDataValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateForCreate(final JsonCommand command) {
|
public void validateForCreate(final JsonCommand command) {
|
||||||
final String json = command.json();
|
final String json = command.json();
|
||||||
|
|
||||||
if (StringUtils.isBlank(json)) {
|
if (StringUtils.isBlank(json)) {
|
||||||
throw new InvalidJsonException();
|
throw new InvalidJsonException();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CREATE_REQUEST_DATA_PARAMETERS);
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CREATE_REQUEST_DATA_PARAMETERS);
|
||||||
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||||
.resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
.resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||||
this.accountTransfersDetailDataValidator.validate(command, baseDataValidator);
|
this.accountTransfersDetailDataValidator.validate(command, baseDataValidator);
|
||||||
|
|
||||||
final JsonElement element = command.parsedJson();
|
final JsonElement element = command.parsedJson();
|
||||||
|
|
||||||
final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element,
|
final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element,
|
||||||
Locale.getDefault());
|
Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, 2);
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1, 2);
|
||||||
|
|
||||||
final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName,
|
final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName,
|
||||||
element);
|
element);
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull();
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull();
|
||||||
|
|
||||||
final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName,
|
final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName,
|
||||||
element);
|
element);
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill)
|
||||||
.validateDateAfter(validFrom);
|
.validateDateAfter(validFrom);
|
||||||
|
|
||||||
final BigDecimal transferAmount = this.fromApiJsonHelper
|
final BigDecimal transferAmount = this.fromApiJsonHelper
|
||||||
.extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element);
|
.extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element);
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount();
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount();
|
||||||
|
|
||||||
final Integer transferType = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
final Integer transferType = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter(transferTypeParamName).value(transferType).notNull().inMinMaxRange(1, 3);
|
baseDataValidator.reset().parameter(transferTypeParamName).value(transferType).notNull().inMinMaxRange(1, 3);
|
||||||
|
|
||||||
final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element,
|
final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element,
|
||||||
Locale.getDefault());
|
Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull().inMinMaxRange(1,
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull().inMinMaxRange(1,
|
||||||
4);
|
4);
|
||||||
|
|
||||||
final Integer standingInstructionType = this.fromApiJsonHelper
|
final Integer standingInstructionType = this.fromApiJsonHelper
|
||||||
.extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault());
|
.extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
||||||
.notNull().inMinMaxRange(1, 2);
|
.notNull().inMinMaxRange(1, 2);
|
||||||
|
|
||||||
final Integer recurrenceType = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName,
|
final Integer recurrenceType = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName,
|
||||||
element, Locale.getDefault());
|
element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
||||||
.inMinMaxRange(1, 2);
|
.inMinMaxRange(1, 2);
|
||||||
boolean isPeriodic = false;
|
boolean isPeriodic = false;
|
||||||
if (recurrenceType != null) {
|
if (recurrenceType != null) {
|
||||||
isPeriodic = AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence();
|
isPeriodic = AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Integer recurrenceFrequency = this.fromApiJsonHelper
|
final Integer recurrenceFrequency = this.fromApiJsonHelper
|
||||||
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault());
|
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
|
||||||
.inMinMaxRange(0, 3);
|
.inMinMaxRange(0, 3);
|
||||||
|
|
||||||
if (recurrenceFrequency != null) {
|
if (recurrenceFrequency != null) {
|
||||||
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(recurrenceFrequency);
|
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(recurrenceFrequency);
|
||||||
if (frequencyType.isMonthly() || frequencyType.isYearly()) {
|
if (frequencyType.isMonthly() || frequencyType.isYearly()) {
|
||||||
final MonthDay monthDay = this.fromApiJsonHelper
|
final MonthDay monthDay = this.fromApiJsonHelper
|
||||||
.extractMonthDayNamed(StandingInstructionApiConstants.recurrenceOnMonthDayParamName, element);
|
.extractMonthDayNamed(StandingInstructionApiConstants.recurrenceOnMonthDayParamName, element);
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceOnMonthDayParamName).value(monthDay)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceOnMonthDayParamName).value(monthDay)
|
||||||
.notNull();
|
.notNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Integer recurrenceInterval = this.fromApiJsonHelper
|
final Integer recurrenceInterval = this.fromApiJsonHelper
|
||||||
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault());
|
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault());
|
||||||
if (isPeriodic) {
|
if (isPeriodic) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
|
||||||
.notNull();
|
.notNull();
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
|
||||||
.notNull();
|
.notNull();
|
||||||
}
|
}
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
|
||||||
.integerGreaterThanZero();
|
.integerGreaterThanZero();
|
||||||
|
|
||||||
final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element);
|
final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element);
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
|
||||||
|
|
||||||
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
final Integer toAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(toAccountTypeParamName, element);
|
||||||
if (toAccountType != null && PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()) {
|
if (toAccountType != null && (PortfolioAccountType.fromInt(toAccountType).isSavingsAccount()
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
|| PortfolioAccountType.fromInt(toAccountType).isCurrentAccount())) {
|
||||||
.notNull().inMinMaxRange(1, 1);
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
.notNull().inMinMaxRange(1, 1);
|
||||||
.inMinMaxRange(1, 1);
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
||||||
|
.inMinMaxRange(1, 1);
|
||||||
}
|
|
||||||
if (standingInstructionType != null && StandingInstructionType.fromInt(standingInstructionType).isFixedAmoutTransfer()) {
|
}
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).notNull();
|
if (standingInstructionType != null && StandingInstructionType.fromInt(standingInstructionType).isFixedAmoutTransfer()) {
|
||||||
}
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).notNull();
|
||||||
|
}
|
||||||
String errorCode = null;
|
|
||||||
AccountTransferType accountTransferType = AccountTransferType.fromInt(transferType);
|
String errorCode = null;
|
||||||
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
|
AccountTransferType accountTransferType = AccountTransferType.fromInt(transferType);
|
||||||
if (fromAccountType != null && toAccountType != null) {
|
final Integer fromAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(fromAccountTypeParamName, element);
|
||||||
PortfolioAccountType fromPortfolioAccountType = PortfolioAccountType.fromInt(fromAccountType);
|
if (fromAccountType != null && toAccountType != null) {
|
||||||
PortfolioAccountType toPortfolioAccountType = PortfolioAccountType.fromInt(toAccountType);
|
PortfolioAccountType fromPortfolioAccountType = PortfolioAccountType.fromInt(fromAccountType);
|
||||||
if (accountTransferType.isAccountTransfer()
|
PortfolioAccountType toPortfolioAccountType = PortfolioAccountType.fromInt(toAccountType);
|
||||||
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) {
|
if (accountTransferType.isAccountTransfer()
|
||||||
errorCode = "not.account.transfer";
|
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isLoanAccount())) {
|
||||||
} else if (accountTransferType.isLoanRepayment()
|
errorCode = "not.account.transfer";
|
||||||
&& (fromPortfolioAccountType.isLoanAccount() || toPortfolioAccountType.isSavingsAccount())) {
|
} else if (accountTransferType.isLoanRepayment()
|
||||||
errorCode = "not.loan.repayment";
|
&& (fromPortfolioAccountType.isLoanAccount()
|
||||||
}
|
|| (toPortfolioAccountType.isSavingsAccount() || toPortfolioAccountType.isCurrentAccount()))) {
|
||||||
if (errorCode != null) {
|
errorCode = "not.loan.repayment";
|
||||||
baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode);
|
}
|
||||||
}
|
if (errorCode != null) {
|
||||||
}
|
baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode);
|
||||||
|
}
|
||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
}
|
||||||
}
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
public void validateForUpdate(final JsonCommand command) {
|
}
|
||||||
final String json = command.json();
|
|
||||||
|
public void validateForUpdate(final JsonCommand command) {
|
||||||
if (StringUtils.isBlank(json)) {
|
final String json = command.json();
|
||||||
throw new InvalidJsonException();
|
|
||||||
}
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
}
|
||||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_REQUEST_DATA_PARAMETERS);
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_REQUEST_DATA_PARAMETERS);
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
|
||||||
.resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||||
final JsonElement element = command.parsedJson();
|
.resource(StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validFromParamName, element)) {
|
|
||||||
final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName,
|
final JsonElement element = command.parsedJson();
|
||||||
element);
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validFromParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull();
|
final LocalDate validFrom = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validFromParamName,
|
||||||
}
|
element);
|
||||||
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validFromParamName).value(validFrom).notNull();
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validTillParamName, element)) {
|
}
|
||||||
final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName,
|
|
||||||
element);
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.validTillParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill).notNull();
|
final LocalDate validTill = this.fromApiJsonHelper.extractLocalDateNamed(StandingInstructionApiConstants.validTillParamName,
|
||||||
}
|
element);
|
||||||
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.validTillParamName).value(validTill).notNull();
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.amountParamName, element)) {
|
}
|
||||||
final BigDecimal transferAmount = this.fromApiJsonHelper
|
|
||||||
.extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element);
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.amountParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount();
|
final BigDecimal transferAmount = this.fromApiJsonHelper
|
||||||
}
|
.extractBigDecimalWithLocaleNamed(StandingInstructionApiConstants.amountParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.amountParamName).value(transferAmount).positiveAmount();
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.statusParamName, element)) {
|
}
|
||||||
final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element,
|
|
||||||
Locale.getDefault());
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.statusParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1,
|
final Integer status = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.statusParamName, element,
|
||||||
2);
|
Locale.getDefault());
|
||||||
}
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.statusParamName).value(status).notNull().inMinMaxRange(1,
|
||||||
|
2);
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.priorityParamName, element)) {
|
}
|
||||||
final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element,
|
|
||||||
Locale.getDefault());
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.priorityParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull()
|
final Integer priority = this.fromApiJsonHelper.extractIntegerNamed(StandingInstructionApiConstants.priorityParamName, element,
|
||||||
.inMinMaxRange(1, 4);
|
Locale.getDefault());
|
||||||
}
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.priorityParamName).value(priority).notNull()
|
||||||
|
.inMinMaxRange(1, 4);
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.instructionTypeParamName, element)) {
|
}
|
||||||
final Integer standingInstructionType = this.fromApiJsonHelper
|
|
||||||
.extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault());
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.instructionTypeParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
final Integer standingInstructionType = this.fromApiJsonHelper
|
||||||
.notNull().inMinMaxRange(1, 2);
|
.extractIntegerNamed(StandingInstructionApiConstants.instructionTypeParamName, element, Locale.getDefault());
|
||||||
}
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.instructionTypeParamName).value(standingInstructionType)
|
||||||
|
.notNull().inMinMaxRange(1, 2);
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceTypeParamName, element)) {
|
}
|
||||||
final Integer recurrenceType = this.fromApiJsonHelper
|
|
||||||
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName, element, Locale.getDefault());
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceTypeParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
final Integer recurrenceType = this.fromApiJsonHelper
|
||||||
.inMinMaxRange(1, 2);
|
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceTypeParamName, element, Locale.getDefault());
|
||||||
}
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceTypeParamName).value(recurrenceType).notNull()
|
||||||
|
.inMinMaxRange(1, 2);
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceFrequencyParamName, element)) {
|
}
|
||||||
final Integer recurrenceFrequency = this.fromApiJsonHelper
|
|
||||||
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault());
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceFrequencyParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
|
final Integer recurrenceFrequency = this.fromApiJsonHelper
|
||||||
.inMinMaxRange(0, 3);
|
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceFrequencyParamName, element, Locale.getDefault());
|
||||||
}
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceFrequencyParamName).value(recurrenceFrequency)
|
||||||
|
.inMinMaxRange(0, 3);
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceIntervalParamName, element)) {
|
}
|
||||||
final Integer recurrenceInterval = this.fromApiJsonHelper
|
|
||||||
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault());
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.recurrenceIntervalParamName, element)) {
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
|
final Integer recurrenceInterval = this.fromApiJsonHelper
|
||||||
.integerGreaterThanZero();
|
.extractIntegerNamed(StandingInstructionApiConstants.recurrenceIntervalParamName, element, Locale.getDefault());
|
||||||
}
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.recurrenceIntervalParamName).value(recurrenceInterval)
|
||||||
|
.integerGreaterThanZero();
|
||||||
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.nameParamName, element)) {
|
}
|
||||||
final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element);
|
|
||||||
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
|
if (this.fromApiJsonHelper.parameterExists(StandingInstructionApiConstants.nameParamName, element)) {
|
||||||
}
|
final String name = this.fromApiJsonHelper.extractStringNamed(StandingInstructionApiConstants.nameParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(StandingInstructionApiConstants.nameParamName).value(name).notNull();
|
||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
}
|
||||||
}
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
}
|
||||||
if (!dataValidationErrors.isEmpty()) {
|
|
||||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||||
}
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
}
|
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+314
-214
@@ -1,214 +1,314 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.domain;
|
package org.apache.fineract.portfolio.account.domain;
|
||||||
|
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromOfficeIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toClientIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toOfficeIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
import org.springframework.stereotype.Service;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@Service
|
import org.springframework.stereotype.Service;
|
||||||
public class AccountTransferDetailAssembler {
|
|
||||||
|
@Service
|
||||||
private final ClientRepositoryWrapper clientRepository;
|
public class AccountTransferDetailAssembler {
|
||||||
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
|
||||||
private final SavingsAccountAssembler savingsAccountAssembler;
|
private final ClientRepositoryWrapper clientRepository;
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
||||||
private final LoanAssembler loanAccountAssembler;
|
private final SavingsAccountAssembler savingsAccountAssembler;
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
@Autowired
|
private final LoanAssembler loanAccountAssembler;
|
||||||
public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository,
|
private final CurrentAccountRepository currentAccountRepository;
|
||||||
final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler,
|
|
||||||
final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler) {
|
@Autowired
|
||||||
this.clientRepository = clientRepository;
|
public AccountTransferDetailAssembler(final ClientRepositoryWrapper clientRepository,
|
||||||
this.officeRepositoryWrapper = officeRepositoryWrapper;
|
final OfficeRepositoryWrapper officeRepositoryWrapper, final SavingsAccountAssembler savingsAccountAssembler,
|
||||||
this.savingsAccountAssembler = savingsAccountAssembler;
|
final FromJsonHelper fromApiJsonHelper, final LoanAssembler loanAccountAssembler,
|
||||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
final CurrentAccountRepository currentAccountRepository) {
|
||||||
this.loanAccountAssembler = loanAccountAssembler;
|
this.clientRepository = clientRepository;
|
||||||
}
|
this.officeRepositoryWrapper = officeRepositoryWrapper;
|
||||||
|
this.savingsAccountAssembler = savingsAccountAssembler;
|
||||||
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
|
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||||
|
this.loanAccountAssembler = loanAccountAssembler;
|
||||||
final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
this.currentAccountRepository = currentAccountRepository;
|
||||||
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false);
|
}
|
||||||
|
|
||||||
final boolean backdatedTxnsAllowedTill = false;
|
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
|
||||||
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
|
|
||||||
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill);
|
final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||||
|
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false);
|
||||||
return assembleSavingsToSavingsTransfer(command, fromSavingsAccount, toSavingsAccount);
|
|
||||||
|
final boolean backdatedTxnsAllowedTill = false;
|
||||||
}
|
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
|
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill);
|
||||||
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
|
|
||||||
|
return assembleSavingsToSavingsTransfer(command, fromSavingsAccount, toSavingsAccount);
|
||||||
final Long fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
|
||||||
final boolean backdatedTxnsAllowedTill = false;
|
}
|
||||||
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, backdatedTxnsAllowedTill);
|
|
||||||
|
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
|
||||||
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
|
||||||
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
|
final Long fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||||
|
final boolean backdatedTxnsAllowedTill = false;
|
||||||
return assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount);
|
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId, backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
}
|
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
|
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
|
||||||
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) {
|
|
||||||
|
return assembleSavingsToLoanTransfer(command, fromSavingsAccount, toLoanAccount);
|
||||||
final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
|
||||||
final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId);
|
}
|
||||||
final boolean backdatedTxnsAllowedTill = false;
|
|
||||||
final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) {
|
||||||
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill);
|
|
||||||
|
final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||||
return assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount);
|
final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId);
|
||||||
}
|
final boolean backdatedTxnsAllowedTill = false;
|
||||||
|
final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount,
|
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId, backdatedTxnsAllowedTill);
|
||||||
final SavingsAccount toSavingsAccount) {
|
|
||||||
|
return assembleLoanToSavingsTransfer(command, fromLoanAccount, toSavingsAccount);
|
||||||
final JsonElement element = command.parsedJson();
|
}
|
||||||
|
|
||||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount,
|
||||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
final SavingsAccount toSavingsAccount) {
|
||||||
|
|
||||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
final JsonElement element = command.parsedJson();
|
||||||
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
|
||||||
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
|
||||||
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||||
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
|
||||||
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||||
|
|
||||||
return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
toSavingsAccount, transfertype);
|
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||||
|
|
||||||
}
|
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
|
|
||||||
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount,
|
return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
|
||||||
final Loan toLoanAccount) {
|
toSavingsAccount, transfertype);
|
||||||
|
|
||||||
final JsonElement element = command.parsedJson();
|
}
|
||||||
|
|
||||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount,
|
||||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
final Loan toLoanAccount) {
|
||||||
|
|
||||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
final JsonElement element = command.parsedJson();
|
||||||
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
|
||||||
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
|
||||||
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||||
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
|
||||||
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||||
|
|
||||||
return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
transfertype);
|
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||||
|
|
||||||
}
|
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
|
|
||||||
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount,
|
return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
|
||||||
final SavingsAccount toSavingsAccount) {
|
transfertype);
|
||||||
|
|
||||||
final JsonElement element = command.parsedJson();
|
}
|
||||||
|
|
||||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount,
|
||||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
final SavingsAccount toSavingsAccount) {
|
||||||
|
|
||||||
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
final JsonElement element = command.parsedJson();
|
||||||
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
|
||||||
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
|
||||||
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||||
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
|
||||||
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
|
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||||
return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
|
|
||||||
transfertype);
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
}
|
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||||
|
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
public AccountTransferDetails assembleSavingsToLoanTransfer(final SavingsAccount fromSavingsAccount, final Loan toLoanAccount,
|
|
||||||
Integer transferType) {
|
return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
|
||||||
final Office fromOffice = fromSavingsAccount.office();
|
transfertype);
|
||||||
final Client fromClient = fromSavingsAccount.getClient();
|
}
|
||||||
final Office toOffice = toLoanAccount.getOffice();
|
|
||||||
final Client toClient = toLoanAccount.client();
|
public AccountTransferDetails assembleSavingsToLoanTransfer(final SavingsAccount fromSavingsAccount, final Loan toLoanAccount,
|
||||||
|
Integer transferType) {
|
||||||
return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
|
final Office fromOffice = fromSavingsAccount.office();
|
||||||
transferType);
|
final Client fromClient = fromSavingsAccount.getClient();
|
||||||
|
final Office toOffice = toLoanAccount.getOffice();
|
||||||
}
|
final Client toClient = toLoanAccount.client();
|
||||||
|
|
||||||
public AccountTransferDetails assembleSavingsToSavingsTransfer(final SavingsAccount fromSavingsAccount,
|
return AccountTransferDetails.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
|
||||||
final SavingsAccount toSavingsAccount, Integer transferType) {
|
transferType);
|
||||||
final Office fromOffice = fromSavingsAccount.office();
|
|
||||||
final Client fromClient = fromSavingsAccount.getClient();
|
}
|
||||||
final Office toOffice = toSavingsAccount.office();
|
|
||||||
final Client toClient = toSavingsAccount.getClient();
|
public AccountTransferDetails assembleSavingsToSavingsTransfer(final SavingsAccount fromSavingsAccount,
|
||||||
|
final SavingsAccount toSavingsAccount, Integer transferType) {
|
||||||
return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
|
final Office fromOffice = fromSavingsAccount.office();
|
||||||
toSavingsAccount, transferType);
|
final Client fromClient = fromSavingsAccount.getClient();
|
||||||
}
|
final Office toOffice = toSavingsAccount.office();
|
||||||
|
final Client toClient = toSavingsAccount.getClient();
|
||||||
public AccountTransferDetails assembleLoanToSavingsTransfer(final Loan fromLoanAccount, final SavingsAccount toSavingsAccount,
|
|
||||||
Integer transferType) {
|
return AccountTransferDetails.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
|
||||||
final Office fromOffice = fromLoanAccount.getOffice();
|
toSavingsAccount, transferType);
|
||||||
final Client fromClient = fromLoanAccount.client();
|
}
|
||||||
final Office toOffice = toSavingsAccount.office();
|
|
||||||
final Client toClient = toSavingsAccount.getClient();
|
public AccountTransferDetails assembleLoanToSavingsTransfer(final Loan fromLoanAccount, final SavingsAccount toSavingsAccount,
|
||||||
|
Integer transferType) {
|
||||||
return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
|
final Office fromOffice = fromLoanAccount.getOffice();
|
||||||
transferType);
|
final Client fromClient = fromLoanAccount.client();
|
||||||
}
|
final Office toOffice = toSavingsAccount.office();
|
||||||
|
final Client toClient = toSavingsAccount.getClient();
|
||||||
public AccountTransferDetails assembleLoanToLoanTransfer(Loan fromLoanAccount, Loan toLoanAccount, Integer transferType) {
|
|
||||||
final Office fromOffice = fromLoanAccount.getOffice();
|
return AccountTransferDetails.loanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
|
||||||
final Client fromClient = fromLoanAccount.client();
|
transferType);
|
||||||
final Office toOffice = toLoanAccount.getOffice();
|
}
|
||||||
final Client toClient = toLoanAccount.client();
|
|
||||||
|
public AccountTransferDetails assembleLoanToLoanTransfer(Loan fromLoanAccount, Loan toLoanAccount, Integer transferType) {
|
||||||
return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount,
|
final Office fromOffice = fromLoanAccount.getOffice();
|
||||||
transferType);
|
final Client fromClient = fromLoanAccount.client();
|
||||||
}
|
final Office toOffice = toLoanAccount.getOffice();
|
||||||
}
|
final Client toClient = toLoanAccount.client();
|
||||||
|
|
||||||
|
return AccountTransferDetails.loanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount,
|
||||||
|
transferType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Current account assembly methods ---
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleCurrentToCurrentTransfer(final JsonCommand command) {
|
||||||
|
final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||||
|
final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId));
|
||||||
|
|
||||||
|
final Long toCurrentAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
|
final CurrentAccount toCurrentAccount = currentAccountRepository.findById(toCurrentAccountId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("To current account not found: " + toCurrentAccountId));
|
||||||
|
|
||||||
|
final JsonElement element = command.parsedJson();
|
||||||
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
|
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||||
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
|
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||||
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
|
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||||
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
|
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||||
|
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
|
|
||||||
|
return AccountTransferDetails.currentToCurrentTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient,
|
||||||
|
toCurrentAccount, transfertype);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleCurrentToSavingsTransfer(final JsonCommand command) {
|
||||||
|
final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||||
|
final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId));
|
||||||
|
|
||||||
|
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
|
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, false);
|
||||||
|
|
||||||
|
final JsonElement element = command.parsedJson();
|
||||||
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
|
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||||
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
|
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||||
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
|
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||||
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
|
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||||
|
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
|
|
||||||
|
return AccountTransferDetails.currentToSavingsTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient,
|
||||||
|
toSavingsAccount, transfertype);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleSavingsToCurrentTransfer(final JsonCommand command) {
|
||||||
|
final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||||
|
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId, false);
|
||||||
|
|
||||||
|
final Long toCurrentAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
|
final CurrentAccount toCurrentAccount = currentAccountRepository.findById(toCurrentAccountId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("To current account not found: " + toCurrentAccountId));
|
||||||
|
|
||||||
|
final JsonElement element = command.parsedJson();
|
||||||
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
|
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||||
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
|
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||||
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
|
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||||
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
|
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||||
|
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
|
|
||||||
|
return AccountTransferDetails.savingsToCurrentTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient,
|
||||||
|
toCurrentAccount, transfertype);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleCurrentToLoanTransfer(final JsonCommand command) {
|
||||||
|
final Long fromCurrentAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
|
||||||
|
final CurrentAccount fromCurrentAccount = currentAccountRepository.findById(fromCurrentAccountId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("From current account not found: " + fromCurrentAccountId));
|
||||||
|
|
||||||
|
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
|
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
|
||||||
|
|
||||||
|
final JsonElement element = command.parsedJson();
|
||||||
|
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
|
||||||
|
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||||
|
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
|
||||||
|
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
|
||||||
|
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
|
||||||
|
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||||
|
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
|
||||||
|
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
|
||||||
|
final Integer transfertype = this.fromApiJsonHelper.extractIntegerNamed(transferTypeParamName, element, Locale.getDefault());
|
||||||
|
|
||||||
|
return AccountTransferDetails.currentToLoanTransfer(fromOffice, fromClient, fromCurrentAccount, toOffice, toClient,
|
||||||
|
toLoanAccount, transfertype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+216
-165
@@ -1,165 +1,216 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.domain;
|
package org.apache.fineract.portfolio.account.domain;
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.OneToOne;
|
import jakarta.persistence.OneToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
@Entity
|
|
||||||
@Table(name = "m_account_transfer_details")
|
@Entity
|
||||||
public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
@Table(name = "m_account_transfer_details")
|
||||||
|
public class AccountTransferDetails extends AbstractPersistableCustom<Long> {
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "from_office_id", nullable = false)
|
@ManyToOne
|
||||||
private Office fromOffice;
|
@JoinColumn(name = "from_office_id", nullable = false)
|
||||||
|
private Office fromOffice;
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "from_client_id", nullable = false)
|
@ManyToOne
|
||||||
private Client fromClient;
|
@JoinColumn(name = "from_client_id", nullable = false)
|
||||||
|
private Client fromClient;
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "from_savings_account_id", nullable = true)
|
@ManyToOne
|
||||||
private SavingsAccount fromSavingsAccount;
|
@JoinColumn(name = "from_savings_account_id", nullable = true)
|
||||||
|
private SavingsAccount fromSavingsAccount;
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "to_office_id", nullable = false)
|
@ManyToOne
|
||||||
private Office toOffice;
|
@JoinColumn(name = "to_office_id", nullable = false)
|
||||||
|
private Office toOffice;
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "to_client_id", nullable = false)
|
@ManyToOne
|
||||||
private Client toClient;
|
@JoinColumn(name = "to_client_id", nullable = false)
|
||||||
|
private Client toClient;
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "to_savings_account_id", nullable = true)
|
@ManyToOne
|
||||||
private SavingsAccount toSavingsAccount;
|
@JoinColumn(name = "to_savings_account_id", nullable = true)
|
||||||
|
private SavingsAccount toSavingsAccount;
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "to_loan_account_id", nullable = true)
|
@ManyToOne
|
||||||
private Loan toLoanAccount;
|
@JoinColumn(name = "to_loan_account_id", nullable = true)
|
||||||
|
private Loan toLoanAccount;
|
||||||
@ManyToOne
|
|
||||||
@JoinColumn(name = "from_loan_account_id", nullable = true)
|
@ManyToOne
|
||||||
private Loan fromLoanAccount;
|
@JoinColumn(name = "from_loan_account_id", nullable = true)
|
||||||
|
private Loan fromLoanAccount;
|
||||||
@Column(name = "transfer_type")
|
|
||||||
private Integer transferType;
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "from_current_account_id", nullable = true)
|
||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountTransferDetails", orphanRemoval = true, fetch = FetchType.EAGER)
|
private CurrentAccount fromCurrentAccount;
|
||||||
private List<AccountTransferTransaction> accountTransferTransactions = new ArrayList<>();
|
|
||||||
|
@ManyToOne
|
||||||
@OneToOne(mappedBy = "accountTransferDetails", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
|
@JoinColumn(name = "to_current_account_id", nullable = true)
|
||||||
private AccountTransferStandingInstruction accountTransferStandingInstruction;
|
private CurrentAccount toCurrentAccount;
|
||||||
|
|
||||||
public static AccountTransferDetails savingsToSavingsTransfer(final Office fromOffice, final Client fromClient,
|
@Column(name = "transfer_type")
|
||||||
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
private Integer transferType;
|
||||||
Integer transferType) {
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountTransferDetails", orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null,
|
private List<AccountTransferTransaction> accountTransferTransactions = new ArrayList<>();
|
||||||
transferType, null);
|
|
||||||
}
|
@OneToOne(mappedBy = "accountTransferDetails", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
|
private AccountTransferStandingInstruction accountTransferStandingInstruction;
|
||||||
public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
|
|
||||||
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount,
|
public static AccountTransferDetails savingsToSavingsTransfer(final Office fromOffice, final Client fromClient,
|
||||||
Integer transferType) {
|
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount,
|
Integer transferType) {
|
||||||
transferType, null);
|
|
||||||
}
|
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, toSavingsAccount,
|
||||||
|
null, null, transferType, null);
|
||||||
public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount,
|
}
|
||||||
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) {
|
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, toSavingsAccount, null,
|
public static AccountTransferDetails savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
|
||||||
transferType, null);
|
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount,
|
||||||
}
|
Integer transferType) {
|
||||||
|
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, null, toLoanAccount,
|
||||||
protected AccountTransferDetails() {
|
null, transferType, null);
|
||||||
//
|
}
|
||||||
}
|
|
||||||
|
public static AccountTransferDetails loanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount,
|
||||||
private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount,
|
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, Integer transferType) {
|
||||||
final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, toSavingsAccount, null,
|
||||||
final Loan toLoanAccount, final Integer transferType,
|
null, transferType, null);
|
||||||
final AccountTransferStandingInstruction accountTransferStandingInstruction) {
|
}
|
||||||
this.fromOffice = fromOffice;
|
|
||||||
this.fromClient = fromClient;
|
protected AccountTransferDetails() {
|
||||||
this.fromSavingsAccount = fromSavingsAccount;
|
//
|
||||||
this.fromLoanAccount = fromLoanAccount;
|
}
|
||||||
this.toOffice = toOffice;
|
|
||||||
this.toClient = toClient;
|
private AccountTransferDetails(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount,
|
||||||
this.toSavingsAccount = toSavingsAccount;
|
final Loan fromLoanAccount, final CurrentAccount fromCurrentAccount,
|
||||||
this.toLoanAccount = toLoanAccount;
|
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
|
||||||
this.transferType = transferType;
|
final Loan toLoanAccount, final CurrentAccount toCurrentAccount,
|
||||||
this.accountTransferStandingInstruction = accountTransferStandingInstruction;
|
final Integer transferType,
|
||||||
}
|
final AccountTransferStandingInstruction accountTransferStandingInstruction) {
|
||||||
|
this.fromOffice = fromOffice;
|
||||||
public SavingsAccount toSavingsAccount() {
|
this.fromClient = fromClient;
|
||||||
return this.toSavingsAccount;
|
this.fromSavingsAccount = fromSavingsAccount;
|
||||||
}
|
this.fromLoanAccount = fromLoanAccount;
|
||||||
|
this.fromCurrentAccount = fromCurrentAccount;
|
||||||
public SavingsAccount fromSavingsAccount() {
|
this.toOffice = toOffice;
|
||||||
return this.fromSavingsAccount;
|
this.toClient = toClient;
|
||||||
}
|
this.toSavingsAccount = toSavingsAccount;
|
||||||
|
this.toLoanAccount = toLoanAccount;
|
||||||
public void addAccountTransferTransaction(AccountTransferTransaction accountTransferTransaction) {
|
this.toCurrentAccount = toCurrentAccount;
|
||||||
this.accountTransferTransactions.add(accountTransferTransaction);
|
this.transferType = transferType;
|
||||||
}
|
this.accountTransferStandingInstruction = accountTransferStandingInstruction;
|
||||||
|
}
|
||||||
public void updateAccountTransferStandingInstruction(final AccountTransferStandingInstruction accountTransferStandingInstruction) {
|
|
||||||
this.accountTransferStandingInstruction = accountTransferStandingInstruction;
|
public SavingsAccount toSavingsAccount() {
|
||||||
}
|
return this.toSavingsAccount;
|
||||||
|
}
|
||||||
public Loan toLoanAccount() {
|
|
||||||
return this.toLoanAccount;
|
public SavingsAccount fromSavingsAccount() {
|
||||||
}
|
return this.fromSavingsAccount;
|
||||||
|
}
|
||||||
public Loan fromLoanAccount() {
|
|
||||||
return this.fromLoanAccount;
|
public void addAccountTransferTransaction(AccountTransferTransaction accountTransferTransaction) {
|
||||||
}
|
this.accountTransferTransactions.add(accountTransferTransaction);
|
||||||
|
}
|
||||||
public AccountTransferStandingInstruction accountTransferStandingInstruction() {
|
|
||||||
return this.accountTransferStandingInstruction;
|
public void updateAccountTransferStandingInstruction(final AccountTransferStandingInstruction accountTransferStandingInstruction) {
|
||||||
}
|
this.accountTransferStandingInstruction = accountTransferStandingInstruction;
|
||||||
|
}
|
||||||
public AccountTransferType transferType() {
|
|
||||||
return AccountTransferType.fromInt(this.transferType);
|
public Loan toLoanAccount() {
|
||||||
}
|
return this.toLoanAccount;
|
||||||
|
}
|
||||||
public static AccountTransferDetails loanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice,
|
|
||||||
Client toClient, Loan toLoanAccount, Integer transferType) {
|
public Loan fromLoanAccount() {
|
||||||
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, null, toLoanAccount,
|
return this.fromLoanAccount;
|
||||||
transferType, null);
|
}
|
||||||
}
|
|
||||||
|
public CurrentAccount fromCurrentAccount() {
|
||||||
public List<AccountTransferTransaction> getAccountTransferTransactions() {
|
return this.fromCurrentAccount;
|
||||||
return accountTransferTransactions;
|
}
|
||||||
}
|
|
||||||
}
|
public CurrentAccount toCurrentAccount() {
|
||||||
|
return this.toCurrentAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferStandingInstruction accountTransferStandingInstruction() {
|
||||||
|
return this.accountTransferStandingInstruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferType transferType() {
|
||||||
|
return AccountTransferType.fromInt(this.transferType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountTransferDetails loanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice,
|
||||||
|
Client toClient, Loan toLoanAccount, Integer transferType) {
|
||||||
|
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, null, toOffice, toClient, null, toLoanAccount,
|
||||||
|
null, transferType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Current account factory methods ---
|
||||||
|
|
||||||
|
public static AccountTransferDetails currentToCurrentTransfer(final Office fromOffice, final Client fromClient,
|
||||||
|
final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient,
|
||||||
|
final CurrentAccount toCurrentAccount, Integer transferType) {
|
||||||
|
return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, null, null,
|
||||||
|
toCurrentAccount, transferType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountTransferDetails currentToSavingsTransfer(final Office fromOffice, final Client fromClient,
|
||||||
|
final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient,
|
||||||
|
final SavingsAccount toSavingsAccount, Integer transferType) {
|
||||||
|
return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, toSavingsAccount,
|
||||||
|
null, null, transferType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountTransferDetails savingsToCurrentTransfer(final Office fromOffice, final Client fromClient,
|
||||||
|
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient,
|
||||||
|
final CurrentAccount toCurrentAccount, Integer transferType) {
|
||||||
|
return new AccountTransferDetails(fromOffice, fromClient, fromSavingsAccount, null, null, toOffice, toClient, null, null,
|
||||||
|
toCurrentAccount, transferType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountTransferDetails currentToLoanTransfer(final Office fromOffice, final Client fromClient,
|
||||||
|
final CurrentAccount fromCurrentAccount, final Office toOffice, final Client toClient,
|
||||||
|
final Loan toLoanAccount, Integer transferType) {
|
||||||
|
return new AccountTransferDetails(fromOffice, fromClient, null, null, fromCurrentAccount, toOffice, toClient, null, toLoanAccount,
|
||||||
|
null, transferType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AccountTransferTransaction> getAccountTransferTransactions() {
|
||||||
|
return accountTransferTransactions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+302
-290
@@ -1,290 +1,302 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.domain;
|
package org.apache.fineract.portfolio.account.domain;
|
||||||
|
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.transferTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.STANDING_INSTRUCTION_RESOURCE_NAME;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.persistence.UniqueConstraint;
|
import jakarta.persistence.UniqueConstraint;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
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;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "m_account_transfer_standing_instructions", uniqueConstraints = {
|
@Table(name = "m_account_transfer_standing_instructions", uniqueConstraints = {
|
||||||
@UniqueConstraint(columnNames = { "name" }, name = "name") })
|
@UniqueConstraint(columnNames = { "name" }, name = "name") })
|
||||||
public class AccountTransferStandingInstruction extends AbstractPersistableCustom<Long> {
|
public class AccountTransferStandingInstruction extends AbstractPersistableCustom<Long> {
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "account_transfer_details_id", nullable = true)
|
@JoinColumn(name = "account_transfer_details_id", nullable = true)
|
||||||
private AccountTransferDetails accountTransferDetails;
|
private AccountTransferDetails accountTransferDetails;
|
||||||
|
|
||||||
@Column(name = "name")
|
@Column(name = "name")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Column(name = "priority")
|
@Column(name = "priority")
|
||||||
private Integer priority;
|
private Integer priority;
|
||||||
|
|
||||||
@Column(name = "instruction_type")
|
@Column(name = "instruction_type")
|
||||||
private Integer instructionType;
|
private Integer instructionType;
|
||||||
|
|
||||||
@Column(name = "status")
|
@Column(name = "status")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
@Column(name = "amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "amount", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
|
|
||||||
@Column(name = "valid_from")
|
@Column(name = "valid_from")
|
||||||
private LocalDate validFrom;
|
private LocalDate validFrom;
|
||||||
|
|
||||||
@Column(name = "valid_till")
|
@Column(name = "valid_till")
|
||||||
private LocalDate validTill;
|
private LocalDate validTill;
|
||||||
|
|
||||||
@Column(name = "recurrence_type")
|
@Column(name = "recurrence_type")
|
||||||
private Integer recurrenceType;
|
private Integer recurrenceType;
|
||||||
|
|
||||||
@Column(name = "recurrence_frequency")
|
@Column(name = "recurrence_frequency")
|
||||||
private Integer recurrenceFrequency;
|
private Integer recurrenceFrequency;
|
||||||
|
|
||||||
@Column(name = "recurrence_interval")
|
@Column(name = "recurrence_interval")
|
||||||
private Integer recurrenceInterval;
|
private Integer recurrenceInterval;
|
||||||
|
|
||||||
@Column(name = "recurrence_on_day")
|
@Column(name = "recurrence_on_day")
|
||||||
private Integer recurrenceOnDay;
|
private Integer recurrenceOnDay;
|
||||||
|
|
||||||
@Column(name = "recurrence_on_month")
|
@Column(name = "recurrence_on_month")
|
||||||
private Integer recurrenceOnMonth;
|
private Integer recurrenceOnMonth;
|
||||||
|
|
||||||
@Column(name = "last_run_date")
|
@Column(name = "last_run_date")
|
||||||
private LocalDate latsRunDate;
|
private LocalDate latsRunDate;
|
||||||
|
|
||||||
protected AccountTransferStandingInstruction() {
|
protected AccountTransferStandingInstruction() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AccountTransferStandingInstruction create(final AccountTransferDetails accountTransferDetails, final String name,
|
public static AccountTransferStandingInstruction create(final AccountTransferDetails accountTransferDetails, final String name,
|
||||||
final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom,
|
final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom,
|
||||||
final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval,
|
final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval,
|
||||||
final MonthDay recurrenceOnMonthDay) {
|
final MonthDay recurrenceOnMonthDay) {
|
||||||
Integer recurrenceOnDay = null;
|
Integer recurrenceOnDay = null;
|
||||||
Integer recurrenceOnMonth = null;
|
Integer recurrenceOnMonth = null;
|
||||||
if (recurrenceOnMonthDay != null) {
|
if (recurrenceOnMonthDay != null) {
|
||||||
recurrenceOnDay = recurrenceOnMonthDay.getDayOfMonth();
|
recurrenceOnDay = recurrenceOnMonthDay.getDayOfMonth();
|
||||||
recurrenceOnMonth = recurrenceOnMonthDay.getMonthValue();
|
recurrenceOnMonth = recurrenceOnMonthDay.getMonthValue();
|
||||||
}
|
}
|
||||||
return new AccountTransferStandingInstruction(accountTransferDetails, name, priority, instructionType, status, amount, validFrom,
|
return new AccountTransferStandingInstruction(accountTransferDetails, name, priority, instructionType, status, amount, validFrom,
|
||||||
validTill, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnDay, recurrenceOnMonth);
|
validTill, recurrenceType, recurrenceFrequency, recurrenceInterval, recurrenceOnDay, recurrenceOnMonth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccountTransferStandingInstruction(final AccountTransferDetails accountTransferDetails, final String name,
|
private AccountTransferStandingInstruction(final AccountTransferDetails accountTransferDetails, final String name,
|
||||||
final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom,
|
final Integer priority, final Integer instructionType, final Integer status, final BigDecimal amount, final LocalDate validFrom,
|
||||||
final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval,
|
final LocalDate validTill, final Integer recurrenceType, final Integer recurrenceFrequency, final Integer recurrenceInterval,
|
||||||
final Integer recurrenceOnDay, final Integer recurrenceOnMonth) {
|
final Integer recurrenceOnDay, final Integer recurrenceOnMonth) {
|
||||||
this.accountTransferDetails = accountTransferDetails;
|
this.accountTransferDetails = accountTransferDetails;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.instructionType = instructionType;
|
this.instructionType = instructionType;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.validFrom = validFrom;
|
this.validFrom = validFrom;
|
||||||
this.validTill = validTill;
|
this.validTill = validTill;
|
||||||
this.recurrenceType = recurrenceType;
|
this.recurrenceType = recurrenceType;
|
||||||
this.recurrenceFrequency = recurrenceFrequency;
|
this.recurrenceFrequency = recurrenceFrequency;
|
||||||
this.recurrenceInterval = recurrenceInterval;
|
this.recurrenceInterval = recurrenceInterval;
|
||||||
this.recurrenceOnDay = recurrenceOnDay;
|
this.recurrenceOnDay = recurrenceOnDay;
|
||||||
this.recurrenceOnMonth = recurrenceOnMonth;
|
this.recurrenceOnMonth = recurrenceOnMonth;
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||||
.resource(STANDING_INSTRUCTION_RESOURCE_NAME);
|
.resource(STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||||
|
|
||||||
validateDependencies(baseDataValidator);
|
validateDependencies(baseDataValidator);
|
||||||
if (!dataValidationErrors.isEmpty()) {
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> update(JsonCommand command) {
|
public Map<String, Object> update(JsonCommand command) {
|
||||||
final Map<String, Object> actualChanges = new HashMap<>();
|
final Map<String, Object> actualChanges = new HashMap<>();
|
||||||
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||||
.resource(STANDING_INSTRUCTION_RESOURCE_NAME);
|
.resource(STANDING_INSTRUCTION_RESOURCE_NAME);
|
||||||
|
|
||||||
if (StandingInstructionStatus.fromInt(this.status).isDeleted()) {
|
if (StandingInstructionStatus.fromInt(this.status).isDeleted()) {
|
||||||
baseDataValidator.reset().parameter(statusParamName).failWithCode("can.not.modify.once.deleted");
|
baseDataValidator.reset().parameter(statusParamName).failWithCode("can.not.modify.once.deleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInDateParameterNamed(validFromParamName, this.validFrom)) {
|
if (command.isChangeInDateParameterNamed(validFromParamName, this.validFrom)) {
|
||||||
this.validFrom = command.localDateValueOfParameterNamed(validFromParamName);
|
this.validFrom = command.localDateValueOfParameterNamed(validFromParamName);
|
||||||
actualChanges.put(validFromParamName, this.validFrom);
|
actualChanges.put(validFromParamName, this.validFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInDateParameterNamed(validTillParamName, this.validTill)) {
|
if (command.isChangeInDateParameterNamed(validTillParamName, this.validTill)) {
|
||||||
this.validTill = command.localDateValueOfParameterNamed(validTillParamName);
|
this.validTill = command.localDateValueOfParameterNamed(validTillParamName);
|
||||||
actualChanges.put(validTillParamName, this.validTill);
|
actualChanges.put(validTillParamName, this.validTill);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) {
|
if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) {
|
||||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName);
|
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||||
actualChanges.put(amountParamName, newValue);
|
actualChanges.put(amountParamName, newValue);
|
||||||
this.amount = newValue;
|
this.amount = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInIntegerParameterNamed(statusParamName, this.status)) {
|
if (command.isChangeInIntegerParameterNamed(statusParamName, this.status)) {
|
||||||
final Integer newValue = command.integerValueOfParameterNamed(statusParamName);
|
final Integer newValue = command.integerValueOfParameterNamed(statusParamName);
|
||||||
actualChanges.put(statusParamName, newValue);
|
actualChanges.put(statusParamName, newValue);
|
||||||
this.status = newValue;
|
this.status = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInIntegerParameterNamed(priorityParamName, this.priority)) {
|
if (command.isChangeInIntegerParameterNamed(priorityParamName, this.priority)) {
|
||||||
final Integer newValue = command.integerValueOfParameterNamed(priorityParamName);
|
final Integer newValue = command.integerValueOfParameterNamed(priorityParamName);
|
||||||
actualChanges.put(priorityParamName, newValue);
|
actualChanges.put(priorityParamName, newValue);
|
||||||
this.priority = newValue;
|
this.priority = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInIntegerParameterNamed(instructionTypeParamName, this.instructionType)) {
|
if (command.isChangeInIntegerParameterNamed(instructionTypeParamName, this.instructionType)) {
|
||||||
final Integer newValue = command.integerValueOfParameterNamed(instructionTypeParamName);
|
final Integer newValue = command.integerValueOfParameterNamed(instructionTypeParamName);
|
||||||
actualChanges.put(instructionTypeParamName, newValue);
|
actualChanges.put(instructionTypeParamName, newValue);
|
||||||
this.instructionType = newValue;
|
this.instructionType = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInIntegerParameterNamed(recurrenceTypeParamName, this.recurrenceType)) {
|
if (command.isChangeInIntegerParameterNamed(recurrenceTypeParamName, this.recurrenceType)) {
|
||||||
final Integer newValue = command.integerValueOfParameterNamed(recurrenceTypeParamName);
|
final Integer newValue = command.integerValueOfParameterNamed(recurrenceTypeParamName);
|
||||||
actualChanges.put(recurrenceTypeParamName, newValue);
|
actualChanges.put(recurrenceTypeParamName, newValue);
|
||||||
this.recurrenceType = newValue;
|
this.recurrenceType = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInIntegerParameterNamed(recurrenceFrequencyParamName, this.recurrenceFrequency)) {
|
if (command.isChangeInIntegerParameterNamed(recurrenceFrequencyParamName, this.recurrenceFrequency)) {
|
||||||
final Integer newValue = command.integerValueOfParameterNamed(recurrenceFrequencyParamName);
|
final Integer newValue = command.integerValueOfParameterNamed(recurrenceFrequencyParamName);
|
||||||
actualChanges.put(recurrenceFrequencyParamName, newValue);
|
actualChanges.put(recurrenceFrequencyParamName, newValue);
|
||||||
this.recurrenceFrequency = newValue;
|
this.recurrenceFrequency = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.hasParameter(recurrenceOnMonthDayParamName)) {
|
if (command.hasParameter(recurrenceOnMonthDayParamName)) {
|
||||||
final MonthDay monthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName);
|
final MonthDay monthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName);
|
||||||
final String actualValueEntered = command.stringValueOfParameterNamed(recurrenceOnMonthDayParamName);
|
final String actualValueEntered = command.stringValueOfParameterNamed(recurrenceOnMonthDayParamName);
|
||||||
final Integer dayOfMonthValue = monthDay.getDayOfMonth();
|
final Integer dayOfMonthValue = monthDay.getDayOfMonth();
|
||||||
if (!this.recurrenceOnDay.equals(dayOfMonthValue)) {
|
if (!this.recurrenceOnDay.equals(dayOfMonthValue)) {
|
||||||
actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered);
|
actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered);
|
||||||
this.recurrenceOnDay = dayOfMonthValue;
|
this.recurrenceOnDay = dayOfMonthValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Integer monthOfYear = monthDay.getMonthValue();
|
final Integer monthOfYear = monthDay.getMonthValue();
|
||||||
if (!this.recurrenceOnMonth.equals(monthOfYear)) {
|
if (!this.recurrenceOnMonth.equals(monthOfYear)) {
|
||||||
actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered);
|
actualChanges.put(recurrenceOnMonthDayParamName, actualValueEntered);
|
||||||
this.recurrenceOnMonth = monthOfYear;
|
this.recurrenceOnMonth = monthOfYear;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChangeInIntegerParameterNamed(recurrenceIntervalParamName, this.recurrenceInterval)) {
|
if (command.isChangeInIntegerParameterNamed(recurrenceIntervalParamName, this.recurrenceInterval)) {
|
||||||
final Integer newValue = command.integerValueOfParameterNamed(recurrenceIntervalParamName);
|
final Integer newValue = command.integerValueOfParameterNamed(recurrenceIntervalParamName);
|
||||||
actualChanges.put(recurrenceIntervalParamName, newValue);
|
actualChanges.put(recurrenceIntervalParamName, newValue);
|
||||||
this.recurrenceInterval = newValue;
|
this.recurrenceInterval = newValue;
|
||||||
}
|
}
|
||||||
validateDependencies(baseDataValidator);
|
validateDependencies(baseDataValidator);
|
||||||
if (!dataValidationErrors.isEmpty()) {
|
if (!dataValidationErrors.isEmpty()) {
|
||||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||||
}
|
}
|
||||||
return actualChanges;
|
return actualChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateDependencies(final DataValidatorBuilder baseDataValidator) {
|
private void validateDependencies(final DataValidatorBuilder baseDataValidator) {
|
||||||
|
|
||||||
if (this.validTill != null && this.validFrom != null) {
|
if (this.validTill != null && this.validFrom != null) {
|
||||||
baseDataValidator.reset().parameter(validTillParamName).value(this.validTill).validateDateAfter(this.validFrom);
|
baseDataValidator.reset().parameter(validTillParamName).value(this.validTill).validateDateAfter(this.validFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence()) {
|
if (AccountTransferRecurrenceType.fromInt(recurrenceType).isPeriodicRecurrence()) {
|
||||||
baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(this.recurrenceFrequency).notNull();
|
baseDataValidator.reset().parameter(recurrenceFrequencyParamName).value(this.recurrenceFrequency).notNull();
|
||||||
baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(this.recurrenceInterval).notNull();
|
baseDataValidator.reset().parameter(recurrenceIntervalParamName).value(this.recurrenceInterval).notNull();
|
||||||
if (this.recurrenceFrequency != null) {
|
if (this.recurrenceFrequency != null) {
|
||||||
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency);
|
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(this.recurrenceFrequency);
|
||||||
if (frequencyType.isMonthly()) {
|
if (frequencyType.isMonthly()) {
|
||||||
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull();
|
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull();
|
||||||
} else if (frequencyType.isYearly()) {
|
} else if (frequencyType.isYearly()) {
|
||||||
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull();
|
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnDay).notNull();
|
||||||
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnMonth).notNull();
|
baseDataValidator.reset().parameter(recurrenceOnMonthDayParamName).value(this.recurrenceOnMonth).notNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.accountTransferDetails.toSavingsAccount() != null) {
|
// For savings-to-savings or current-to-savings: enforce Fixed instruction + Periodic recurrence
|
||||||
baseDataValidator.reset().parameter(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1);
|
if (this.accountTransferDetails.toSavingsAccount() != null
|
||||||
baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).notNull().inMinMaxRange(1, 1);
|
|| this.accountTransferDetails.toCurrentAccount() != null) {
|
||||||
}
|
baseDataValidator.reset().parameter(instructionTypeParamName).value(this.instructionType).notNull().inMinMaxRange(1, 1);
|
||||||
|
baseDataValidator.reset().parameter(recurrenceTypeParamName).value(this.recurrenceType).notNull().inMinMaxRange(1, 1);
|
||||||
if (StandingInstructionType.fromInt(this.instructionType).isFixedAmoutTransfer()) {
|
}
|
||||||
baseDataValidator.reset().parameter(amountParamName).value(this.amount).notNull();
|
|
||||||
}
|
if (StandingInstructionType.fromInt(this.instructionType).isFixedAmoutTransfer()) {
|
||||||
|
baseDataValidator.reset().parameter(amountParamName).value(this.amount).notNull();
|
||||||
String errorCode = null;
|
}
|
||||||
if (this.accountTransferDetails.transferType().isAccountTransfer()
|
|
||||||
&& (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toSavingsAccount() == null)) {
|
// Account Transfer: from must be savings OR current; to must be savings OR current
|
||||||
errorCode = "not.account.transfer";
|
boolean fromAccountValid = this.accountTransferDetails.fromSavingsAccount() != null
|
||||||
} else if (this.accountTransferDetails.transferType().isLoanRepayment()
|
|| this.accountTransferDetails.fromCurrentAccount() != null;
|
||||||
&& (this.accountTransferDetails.fromSavingsAccount() == null || this.accountTransferDetails.toLoanAccount() == null)) {
|
boolean toAccountValid = this.accountTransferDetails.toSavingsAccount() != null
|
||||||
errorCode = "not.loan.repayment";
|
|| this.accountTransferDetails.toCurrentAccount() != null;
|
||||||
}
|
|
||||||
if (errorCode != null) {
|
// Loan Repayment: from must be savings OR current; to must be loan
|
||||||
baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode);
|
boolean fromAccountValidForLoan = this.accountTransferDetails.fromSavingsAccount() != null
|
||||||
}
|
|| this.accountTransferDetails.fromCurrentAccount() != null;
|
||||||
|
|
||||||
}
|
String errorCode = null;
|
||||||
|
if (this.accountTransferDetails.transferType().isAccountTransfer()
|
||||||
public void updateLatsRunDate(LocalDate latsRunDate) {
|
&& !(fromAccountValid && toAccountValid)) {
|
||||||
this.latsRunDate = latsRunDate;
|
errorCode = "not.account.transfer";
|
||||||
}
|
} else if (this.accountTransferDetails.transferType().isLoanRepayment()
|
||||||
|
&& !(fromAccountValidForLoan && this.accountTransferDetails.toLoanAccount() != null)) {
|
||||||
public void updateStatus(Integer status) {
|
errorCode = "not.loan.repayment";
|
||||||
this.status = status;
|
}
|
||||||
}
|
if (errorCode != null) {
|
||||||
|
baseDataValidator.reset().parameter(transferTypeParamName).failWithCode(errorCode);
|
||||||
/**
|
}
|
||||||
* delete the standing instruction by setting the status to 3 and appending "_deleted_" and the id to the name
|
|
||||||
**/
|
}
|
||||||
public void delete() {
|
|
||||||
this.status = StandingInstructionStatus.DELETED.getValue();
|
public void updateLatsRunDate(LocalDate latsRunDate) {
|
||||||
this.name = this.name + "_deleted_" + this.getId();
|
this.latsRunDate = latsRunDate;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void updateStatus(Integer status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete the standing instruction by setting the status to 3 and appending "_deleted_" and the id to the name
|
||||||
|
**/
|
||||||
|
public void delete() {
|
||||||
|
this.status = StandingInstructionStatus.DELETED.getValue();
|
||||||
|
this.name = this.name + "_deleted_" + this.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+143
-93
@@ -1,93 +1,143 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.domain;
|
package org.apache.fineract.portfolio.account.domain;
|
||||||
|
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.amountParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.instructionTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.nameParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.nameParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.priorityParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceFrequencyParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceIntervalParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceOnMonthDayParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.recurrenceTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validFromParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.validTillParamName;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.springframework.stereotype.Service;
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
@Service
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
public class StandingInstructionAssembler {
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
private final AccountTransferDetailAssembler accountTransferDetailAssembler;
|
@Service
|
||||||
|
public class StandingInstructionAssembler {
|
||||||
@Autowired
|
|
||||||
public StandingInstructionAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) {
|
private final AccountTransferDetailAssembler accountTransferDetailAssembler;
|
||||||
|
|
||||||
this.accountTransferDetailAssembler = accountTransferDetailAssembler;
|
@Autowired
|
||||||
}
|
public StandingInstructionAssembler(final AccountTransferDetailAssembler accountTransferDetailAssembler) {
|
||||||
|
|
||||||
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
|
this.accountTransferDetailAssembler = accountTransferDetailAssembler;
|
||||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command);
|
}
|
||||||
assembleStandingInstruction(command, accountTransferDetails);
|
|
||||||
return accountTransferDetails;
|
public AccountTransferDetails assembleSavingsToSavingsTransfer(final JsonCommand command) {
|
||||||
}
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToSavingsTransfer(command);
|
||||||
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
public void assembleStandingInstruction(final JsonCommand command, final AccountTransferDetails accountTransferDetails) {
|
return accountTransferDetails;
|
||||||
final LocalDate validFrom = command.localDateValueOfParameterNamed(validFromParamName);
|
}
|
||||||
final LocalDate validTill = command.localDateValueOfParameterNamed(validTillParamName);
|
|
||||||
BigDecimal amount = null;
|
public void assembleStandingInstruction(final JsonCommand command, final AccountTransferDetails accountTransferDetails) {
|
||||||
final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
final LocalDate validFrom = command.localDateValueOfParameterNamed(validFromParamName);
|
||||||
if (transferAmount != null) {
|
final LocalDate validTill = command.localDateValueOfParameterNamed(validTillParamName);
|
||||||
final Money monetaryAmount = Money.of(accountTransferDetails.fromSavingsAccount().getCurrency(), transferAmount);
|
BigDecimal amount = null;
|
||||||
amount = monetaryAmount.getAmount();
|
final BigDecimal transferAmount = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||||
}
|
if (transferAmount != null) {
|
||||||
final Integer status = command.integerValueOfParameterNamed(statusParamName);
|
final MonetaryCurrency currency = resolveCurrency(accountTransferDetails);
|
||||||
final Integer priority = command.integerValueOfParameterNamed(priorityParamName);
|
if (currency != null) {
|
||||||
final Integer standingInstructionType = command.integerValueOfParameterNamed(instructionTypeParamName);
|
final Money monetaryAmount = Money.of(currency, transferAmount);
|
||||||
final Integer recurrenceType = command.integerValueOfParameterNamed(recurrenceTypeParamName);
|
amount = monetaryAmount.getAmount();
|
||||||
final Integer recurrenceFrequency = command.integerValueOfParameterNamed(recurrenceFrequencyParamName);
|
} else {
|
||||||
final MonthDay recurrenceOnMonthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName);
|
amount = transferAmount;
|
||||||
final Integer recurrenceInterval = command.integerValueOfParameterNamed(recurrenceIntervalParamName);
|
}
|
||||||
final String name = command.stringValueOfParameterNamed(nameParamName);
|
}
|
||||||
AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create(
|
final Integer status = command.integerValueOfParameterNamed(statusParamName);
|
||||||
accountTransferDetails, name, priority, standingInstructionType, status, amount, validFrom, validTill, recurrenceType,
|
final Integer priority = command.integerValueOfParameterNamed(priorityParamName);
|
||||||
recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay);
|
final Integer standingInstructionType = command.integerValueOfParameterNamed(instructionTypeParamName);
|
||||||
accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction);
|
final Integer recurrenceType = command.integerValueOfParameterNamed(recurrenceTypeParamName);
|
||||||
}
|
final Integer recurrenceFrequency = command.integerValueOfParameterNamed(recurrenceFrequencyParamName);
|
||||||
|
final MonthDay recurrenceOnMonthDay = command.extractMonthDayNamed(recurrenceOnMonthDayParamName);
|
||||||
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
|
final Integer recurrenceInterval = command.integerValueOfParameterNamed(recurrenceIntervalParamName);
|
||||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command);
|
final String name = command.stringValueOfParameterNamed(nameParamName);
|
||||||
assembleStandingInstruction(command, accountTransferDetails);
|
AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create(
|
||||||
return accountTransferDetails;
|
accountTransferDetails, name, priority, standingInstructionType, status, amount, validFrom, validTill, recurrenceType,
|
||||||
}
|
recurrenceFrequency, recurrenceInterval, recurrenceOnMonthDay);
|
||||||
|
accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction);
|
||||||
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) {
|
}
|
||||||
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(command);
|
|
||||||
assembleStandingInstruction(command, accountTransferDetails);
|
/**
|
||||||
return accountTransferDetails;
|
* Resolves the currency from the "from" account — savings, loan (null), or current account.
|
||||||
}
|
*/
|
||||||
|
private MonetaryCurrency resolveCurrency(final AccountTransferDetails details) {
|
||||||
}
|
final SavingsAccount fromSavings = details.fromSavingsAccount();
|
||||||
|
if (fromSavings != null) {
|
||||||
|
return fromSavings.getCurrency();
|
||||||
|
}
|
||||||
|
final CurrentAccount fromCurrent = details.fromCurrentAccount();
|
||||||
|
if (fromCurrent != null) {
|
||||||
|
return new MonetaryCurrency(fromCurrent.getCurrencyCode(), fromCurrent.getCurrencyDigits(),
|
||||||
|
fromCurrent.getCurrencyMultiplesOf());
|
||||||
|
}
|
||||||
|
return null; // loan accounts don't have a direct currency on AccountTransferDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleSavingsToLoanTransfer(final JsonCommand command) {
|
||||||
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToLoanTransfer(command);
|
||||||
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
|
return accountTransferDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleLoanToSavingsTransfer(final JsonCommand command) {
|
||||||
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToSavingsTransfer(command);
|
||||||
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
|
return accountTransferDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Current account methods ---
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleCurrentToCurrentTransfer(final JsonCommand command) {
|
||||||
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToCurrentTransfer(command);
|
||||||
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
|
return accountTransferDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleCurrentToSavingsTransfer(final JsonCommand command) {
|
||||||
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToSavingsTransfer(command);
|
||||||
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
|
return accountTransferDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleSavingsToCurrentTransfer(final JsonCommand command) {
|
||||||
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleSavingsToCurrentTransfer(command);
|
||||||
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
|
return accountTransferDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountTransferDetails assembleCurrentToLoanTransfer(final JsonCommand command) {
|
||||||
|
final AccountTransferDetails accountTransferDetails = this.accountTransferDetailAssembler.assembleCurrentToLoanTransfer(command);
|
||||||
|
assembleStandingInstruction(command, accountTransferDetails);
|
||||||
|
return accountTransferDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* 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.account.handler;
|
||||||
|
|
||||||
|
import org.apache.fineract.commands.annotation.CommandType;
|
||||||
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.account.service.AccountTerminateWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "ACCOUNT", action = "TERMINATE")
|
||||||
|
public class AccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final AccountTerminateWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public AccountTerminateCommandHandler(final AccountTerminateWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.terminate(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
-70
@@ -1,70 +1,73 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.jobs.executestandinginstructions;
|
package org.apache.fineract.portfolio.account.jobs.executestandinginstructions;
|
||||||
|
|
||||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||||
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||||
import org.springframework.batch.core.Job;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
||||||
import org.springframework.batch.core.Step;
|
import org.springframework.batch.core.Job;
|
||||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
import org.springframework.batch.core.Step;
|
||||||
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
import org.springframework.batch.core.repository.JobRepository;
|
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
||||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
@Configuration
|
|
||||||
public class ExecuteStandingInstructionsConfig {
|
@Configuration
|
||||||
|
public class ExecuteStandingInstructionsConfig {
|
||||||
@Autowired
|
|
||||||
private JobRepository jobRepository;
|
@Autowired
|
||||||
@Autowired
|
private JobRepository jobRepository;
|
||||||
private PlatformTransactionManager transactionManager;
|
@Autowired
|
||||||
@Autowired
|
private PlatformTransactionManager transactionManager;
|
||||||
private StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
@Autowired
|
||||||
@Autowired
|
private StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
||||||
private JdbcTemplate jdbcTemplate;
|
@Autowired
|
||||||
@Autowired
|
private JdbcTemplate jdbcTemplate;
|
||||||
private DatabaseSpecificSQLGenerator sqlGenerator;
|
@Autowired
|
||||||
@Autowired
|
private DatabaseSpecificSQLGenerator sqlGenerator;
|
||||||
private AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
@Autowired
|
||||||
|
private AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||||
@Bean
|
@Autowired
|
||||||
protected Step executeStandingInstructionsStep() {
|
private AccountTransferDetailRepository accountTransferDetailRepository;
|
||||||
return new StepBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository)
|
|
||||||
.tasklet(executeStandingInstructionsTasklet(), transactionManager).build();
|
@Bean
|
||||||
}
|
protected Step executeStandingInstructionsStep() {
|
||||||
|
return new StepBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository)
|
||||||
@Bean
|
.tasklet(executeStandingInstructionsTasklet(), transactionManager).build();
|
||||||
public Job executeStandingInstructionsJob() {
|
}
|
||||||
return new JobBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository).start(executeStandingInstructionsStep())
|
|
||||||
.incrementer(new RunIdIncrementer()).build();
|
@Bean
|
||||||
}
|
public Job executeStandingInstructionsJob() {
|
||||||
|
return new JobBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository).start(executeStandingInstructionsStep())
|
||||||
@Bean
|
.incrementer(new RunIdIncrementer()).build();
|
||||||
public ExecuteStandingInstructionsTasklet executeStandingInstructionsTasklet() {
|
}
|
||||||
return new ExecuteStandingInstructionsTasklet(standingInstructionReadPlatformService, jdbcTemplate, sqlGenerator,
|
|
||||||
accountTransfersWritePlatformService);
|
@Bean
|
||||||
}
|
public ExecuteStandingInstructionsTasklet executeStandingInstructionsTasklet() {
|
||||||
}
|
return new ExecuteStandingInstructionsTasklet(standingInstructionReadPlatformService, jdbcTemplate, sqlGenerator,
|
||||||
|
accountTransfersWritePlatformService, accountTransferDetailRepository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+180
-172
@@ -1,172 +1,180 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.jobs.executestandinginstructions;
|
package org.apache.fineract.portfolio.account.jobs.executestandinginstructions;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformServiceUnavailableException;
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformServiceUnavailableException;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionData;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionData;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
||||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||||
import org.apache.fineract.portfolio.savings.exception.InsufficientAccountBalanceException;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
|
||||||
import org.springframework.batch.core.StepContribution;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
import org.apache.fineract.portfolio.savings.exception.InsufficientAccountBalanceException;
|
||||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
import org.springframework.batch.core.StepContribution;
|
||||||
import org.springframework.batch.repeat.RepeatStatus;
|
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
|
import org.springframework.batch.repeat.RepeatStatus;
|
||||||
@Slf4j
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
private final StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
||||||
private final JdbcTemplate jdbcTemplate;
|
|
||||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
private final StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
||||||
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||||
@Override
|
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
||||||
Collection<StandingInstructionData> instructionData = standingInstructionReadPlatformService
|
|
||||||
.retrieveAll(StandingInstructionStatus.ACTIVE.getValue());
|
@Override
|
||||||
List<Throwable> errors = new ArrayList<>();
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||||
for (StandingInstructionData data : instructionData) {
|
Collection<StandingInstructionData> instructionData = standingInstructionReadPlatformService
|
||||||
boolean isDueForTransfer = false;
|
.retrieveAll(StandingInstructionStatus.ACTIVE.getValue());
|
||||||
AccountTransferRecurrenceType recurrenceType = data.getRecurrenceType();
|
List<Throwable> errors = new ArrayList<>();
|
||||||
StandingInstructionType instructionType = data.getInstructionType();
|
for (StandingInstructionData data : instructionData) {
|
||||||
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
boolean isDueForTransfer = false;
|
||||||
if (recurrenceType.isPeriodicRecurrence()) {
|
AccountTransferRecurrenceType recurrenceType = data.recurrenceTypeEnum();
|
||||||
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
StandingInstructionType instructionType = data.instructionTypeEnum();
|
||||||
PeriodFrequencyType frequencyType = data.getRecurrenceFrequency();
|
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||||
LocalDate startDate = data.getValidFrom();
|
if (recurrenceType.isPeriodicRecurrence()) {
|
||||||
if (frequencyType.isMonthly()) {
|
final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||||
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay());
|
PeriodFrequencyType frequencyType = data.recurrenceFrequencyEnum();
|
||||||
if (DateUtils.isBefore(startDate, data.getValidFrom())) {
|
LocalDate startDate = data.getValidFrom();
|
||||||
startDate = startDate.plusMonths(1);
|
if (frequencyType.isMonthly()) {
|
||||||
}
|
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay());
|
||||||
} else if (frequencyType.isYearly()) {
|
if (DateUtils.isBefore(startDate, data.getValidFrom())) {
|
||||||
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()).withMonth(data.getRecurrenceOnMonth());
|
startDate = startDate.plusMonths(1);
|
||||||
if (DateUtils.isBefore(startDate, data.getValidFrom())) {
|
}
|
||||||
startDate = startDate.plusYears(1);
|
} else if (frequencyType.isYearly()) {
|
||||||
}
|
startDate = startDate.withDayOfMonth(data.getRecurrenceOnDay()).withMonth(data.getRecurrenceOnMonth());
|
||||||
}
|
if (DateUtils.isBefore(startDate, data.getValidFrom())) {
|
||||||
isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.getRecurrenceInterval(), startDate,
|
startDate = startDate.plusYears(1);
|
||||||
transactionDate);
|
}
|
||||||
|
}
|
||||||
}
|
isDueForTransfer = scheduledDateGenerator.isDateFallsInSchedule(frequencyType, data.getRecurrenceInterval(), startDate,
|
||||||
BigDecimal transactionAmount = data.getAmount();
|
transactionDate);
|
||||||
if (data.getToAccountType().isLoanAccount()
|
|
||||||
&& (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) {
|
}
|
||||||
StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService
|
BigDecimal transactionAmount = data.getAmount();
|
||||||
.retriveLoanDuesData(data.getToAccount().getId());
|
if (data.toAccountTypeEnum() != null && data.toAccountTypeEnum().isLoanAccount()
|
||||||
if (data.getInstructionType().isDuesAmoutTransfer()) {
|
&& (recurrenceType.isDuesRecurrence() || (isDueForTransfer && instructionType.isDuesAmoutTransfer()))) {
|
||||||
transactionAmount = standingInstructionDuesData.totalDueAmount();
|
StandingInstructionDuesData standingInstructionDuesData = standingInstructionReadPlatformService
|
||||||
}
|
.retriveLoanDuesData(data.getToAccount().getId());
|
||||||
if (recurrenceType.isDuesRecurrence()) {
|
if (data.instructionTypeEnum().isDuesAmoutTransfer()) {
|
||||||
isDueForTransfer = isDueForTransfer(standingInstructionDuesData);
|
transactionAmount = standingInstructionDuesData.totalDueAmount();
|
||||||
}
|
}
|
||||||
}
|
if (recurrenceType.isDuesRecurrence()) {
|
||||||
|
isDueForTransfer = isDueForTransfer(standingInstructionDuesData);
|
||||||
if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) {
|
}
|
||||||
final SavingsAccount fromSavingsAccount = null;
|
}
|
||||||
final boolean isRegularTransaction = true;
|
|
||||||
final boolean isExceptionForBalanceCheck = false;
|
if (isDueForTransfer && transactionAmount != null && transactionAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount,
|
final SavingsAccount fromSavingsAccount = null;
|
||||||
data.getFromAccountType(), data.getToAccountType(), data.getFromAccount().getId(), data.getToAccount().getId(),
|
final boolean isRegularTransaction = true;
|
||||||
data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null,
|
final boolean isExceptionForBalanceCheck = false;
|
||||||
data.getTransferType().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount,
|
// Load existing AccountTransferDetails so transactions are linked to
|
||||||
isRegularTransaction, isExceptionForBalanceCheck);
|
// this standing instruction and appear in its transaction history
|
||||||
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
|
final AccountTransferDetails existingDetails = data.getAccountDetailId() != null
|
||||||
|
? accountTransferDetailRepository.findById(data.getAccountDetailId()).orElse(null)
|
||||||
if (transferCompleted) {
|
: null;
|
||||||
final String updateQuery = "UPDATE m_account_transfer_standing_instructions SET last_run_date = ? where id = ?";
|
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount,
|
||||||
jdbcTemplate.update(updateQuery, transactionDate, data.getId());
|
data.fromAccountTypeEnum(), data.toAccountTypeEnum(), data.getFromAccount().getId(), data.getToAccount().getId(),
|
||||||
}
|
data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null,
|
||||||
|
data.transferTypeEnum().getValue(), existingDetails, null, ExternalId.empty(), null, null, fromSavingsAccount,
|
||||||
}
|
isRegularTransaction, isExceptionForBalanceCheck);
|
||||||
}
|
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
throw new JobExecutionException(errors);
|
if (transferCompleted) {
|
||||||
}
|
final String updateQuery = "UPDATE m_account_transfer_standing_instructions SET last_run_date = ? where id = ?";
|
||||||
return RepeatStatus.FINISHED;
|
jdbcTemplate.update(updateQuery, transactionDate, data.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean transferAmount(final List<Throwable> errors, final AccountTransferDTO accountTransferDTO, final Long instructionId) {
|
}
|
||||||
boolean transferCompleted = true;
|
}
|
||||||
StringBuilder errorLog = new StringBuilder();
|
if (!errors.isEmpty()) {
|
||||||
StringBuilder updateQuery = new StringBuilder(
|
throw new JobExecutionException(errors);
|
||||||
"INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, " + sqlGenerator.escape("status")
|
}
|
||||||
+ ", amount,execution_time, error_log) VALUES (");
|
return RepeatStatus.FINISHED;
|
||||||
try {
|
}
|
||||||
accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
|
||||||
} catch (final PlatformApiDataValidationException e) {
|
private boolean transferAmount(final List<Throwable> errors, final AccountTransferDTO accountTransferDTO, final Long instructionId) {
|
||||||
errors.add(new Exception("Validation exception while transfering funds for standing Instruction id" + instructionId + " from "
|
boolean transferCompleted = true;
|
||||||
+ accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
StringBuilder errorLog = new StringBuilder();
|
||||||
errorLog.append("Validation exception while trasfering funds ").append(e.getDefaultUserMessage());
|
StringBuilder updateQuery = new StringBuilder(
|
||||||
} catch (final InsufficientAccountBalanceException e) {
|
"INSERT INTO m_account_transfer_standing_instructions_history (standing_instruction_id, " + sqlGenerator.escape("status")
|
||||||
errors.add(new Exception("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id"
|
+ ", amount,execution_time, error_log) VALUES (");
|
||||||
+ instructionId + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
try {
|
||||||
errorLog.append("InsufficientAccountBalance Exception ");
|
accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
} catch (final AbstractPlatformServiceUnavailableException e) {
|
} catch (final PlatformApiDataValidationException e) {
|
||||||
errors.add(new Exception("Platform exception while trasfering funds for standing Instruction id" + instructionId + " from "
|
errors.add(new Exception("Validation exception while transfering funds for standing Instruction id" + instructionId + " from "
|
||||||
+ accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
+ accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
||||||
errorLog.append("Platform exception while trasfering funds ").append(e.getDefaultUserMessage());
|
errorLog.append("Validation exception while trasfering funds ").append(e.getDefaultUserMessage());
|
||||||
} catch (Exception e) {
|
} catch (final InsufficientAccountBalanceException e) {
|
||||||
errors.add(new Exception("Unhandled System Exception while trasfering funds for standing Instruction id" + instructionId
|
errors.add(new Exception("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id"
|
||||||
+ " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
+ instructionId + " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
||||||
errorLog.append("Exception while trasfering funds ").append(e.getMessage());
|
errorLog.append("InsufficientAccountBalance Exception ");
|
||||||
|
} catch (final AbstractPlatformServiceUnavailableException e) {
|
||||||
}
|
errors.add(new Exception("Platform exception while trasfering funds for standing Instruction id" + instructionId + " from "
|
||||||
updateQuery.append(instructionId).append(",");
|
+ accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
||||||
if (errorLog.length() > 0) {
|
errorLog.append("Platform exception while trasfering funds ").append(e.getDefaultUserMessage());
|
||||||
transferCompleted = false;
|
} catch (Exception e) {
|
||||||
updateQuery.append("'failed'").append(",");
|
errors.add(new Exception("Unhandled System Exception while trasfering funds for standing Instruction id" + instructionId
|
||||||
} else {
|
+ " from " + accountTransferDTO.getFromAccountId() + " to " + accountTransferDTO.getToAccountId(), e));
|
||||||
updateQuery.append("'success'").append(",");
|
errorLog.append("Exception while trasfering funds ").append(e.getMessage());
|
||||||
}
|
|
||||||
updateQuery.append(accountTransferDTO.getTransactionAmount().doubleValue());
|
}
|
||||||
updateQuery.append(", now(),");
|
updateQuery.append(instructionId).append(",");
|
||||||
updateQuery.append("'").append(errorLog).append("')");
|
if (errorLog.length() > 0) {
|
||||||
jdbcTemplate.update(updateQuery.toString());
|
transferCompleted = false;
|
||||||
return transferCompleted;
|
updateQuery.append("'failed'").append(",");
|
||||||
}
|
} else {
|
||||||
|
updateQuery.append("'success'").append(",");
|
||||||
public boolean isDueForTransfer(StandingInstructionDuesData standingInstructionDuesData) {
|
}
|
||||||
return standingInstructionDuesData.dueDate() != null
|
updateQuery.append(accountTransferDTO.getTransactionAmount().doubleValue());
|
||||||
&& !standingInstructionDuesData.dueDate().isAfter(LocalDate.now(DateUtils.getDateTimeZoneOfTenant()));
|
updateQuery.append(", now(),");
|
||||||
}
|
updateQuery.append("'").append(errorLog).append("')");
|
||||||
}
|
jdbcTemplate.update(updateQuery.toString());
|
||||||
|
return transferCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDueForTransfer(StandingInstructionDuesData standingInstructionDuesData) {
|
||||||
|
return standingInstructionDuesData.dueDate() != null
|
||||||
|
&& !standingInstructionDuesData.dueDate().isAfter(LocalDate.now(DateUtils.getDateTimeZoneOfTenant()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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.account.service;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
|
public interface AccountTerminateWritePlatformService {
|
||||||
|
|
||||||
|
CommandProcessingResult terminate(Long accountId, JsonCommand command);
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* 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.account.service;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routes terminate to the correct account service based on the
|
||||||
|
* "accountType" field in the JSON body:
|
||||||
|
* SAVINGS → m_savings_account (depositType = savings)
|
||||||
|
* FD → m_savings_account (depositType = 200)
|
||||||
|
* RD → m_savings_account (depositType = 300)
|
||||||
|
* CURRENT → m_current_account
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AccountTerminateWritePlatformServiceImpl implements AccountTerminateWritePlatformService {
|
||||||
|
|
||||||
|
private static final String PARAM_ACCOUNT_TYPE = "accountType";
|
||||||
|
|
||||||
|
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
|
||||||
|
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommandProcessingResult terminate(final Long accountId, final JsonCommand command) {
|
||||||
|
final String accountType = command.stringValueOfParameterNamed(PARAM_ACCOUNT_TYPE);
|
||||||
|
|
||||||
|
if (accountType == null || accountType.isBlank()) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.account.terminate.accountType.required",
|
||||||
|
"accountType is required: SAVINGS, CURRENT, FD or RD");
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (accountType.toUpperCase()) {
|
||||||
|
case "CURRENT" -> currentAccountWritePlatformService.terminateAccount(accountId, command);
|
||||||
|
case "SAVINGS", "FD", "RD" -> savingsAccountWritePlatformService.terminate(accountId, command);
|
||||||
|
default -> throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.account.terminate.accountType.invalid",
|
||||||
|
"Unknown accountType '" + accountType + "'. Expected SAVINGS, CURRENT, FD or RD.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+206
-202
@@ -1,202 +1,206 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.service;
|
package org.apache.fineract.portfolio.account.service;
|
||||||
|
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferType;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionPriority;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
||||||
|
|
||||||
public final class AccountTransferEnumerations {
|
public final class AccountTransferEnumerations {
|
||||||
|
|
||||||
private AccountTransferEnumerations() {
|
private AccountTransferEnumerations() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnumOptionData accountType(final Integer type) {
|
public static EnumOptionData accountType(final Integer type) {
|
||||||
return accountType(PortfolioAccountType.fromInt(type));
|
return accountType(PortfolioAccountType.fromInt(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnumOptionData accountType(final PortfolioAccountType type) {
|
public static EnumOptionData accountType(final PortfolioAccountType type) {
|
||||||
|
|
||||||
EnumOptionData optionData = null;
|
EnumOptionData optionData = null;
|
||||||
|
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case INVALID:
|
case INVALID:
|
||||||
break;
|
break;
|
||||||
case LOAN:
|
case LOAN:
|
||||||
optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(),
|
optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(),
|
||||||
"Loan Account");
|
"Loan Account");
|
||||||
break;
|
break;
|
||||||
case SAVINGS:
|
case SAVINGS:
|
||||||
optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(),
|
optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(),
|
||||||
PortfolioAccountType.SAVINGS.getCode(), "Savings Account");
|
PortfolioAccountType.SAVINGS.getCode(), "Savings Account");
|
||||||
break;
|
break;
|
||||||
}
|
case CURRENT:
|
||||||
}
|
optionData = new EnumOptionData(PortfolioAccountType.CURRENT.getValue().longValue(),
|
||||||
|
PortfolioAccountType.CURRENT.getCode(), "Current Account");
|
||||||
return optionData;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static EnumOptionData recurrenceType(final Integer type) {
|
|
||||||
return recurrenceType(AccountTransferRecurrenceType.fromInt(type));
|
return optionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnumOptionData recurrenceType(final AccountTransferRecurrenceType type) {
|
public static EnumOptionData recurrenceType(final Integer type) {
|
||||||
EnumOptionData optionData = null;
|
return recurrenceType(AccountTransferRecurrenceType.fromInt(type));
|
||||||
if (type != null) {
|
}
|
||||||
switch (type) {
|
|
||||||
case INVALID:
|
public static EnumOptionData recurrenceType(final AccountTransferRecurrenceType type) {
|
||||||
break;
|
EnumOptionData optionData = null;
|
||||||
case PERIODIC:
|
if (type != null) {
|
||||||
optionData = new EnumOptionData(AccountTransferRecurrenceType.PERIODIC.getValue().longValue(),
|
switch (type) {
|
||||||
AccountTransferRecurrenceType.PERIODIC.getCode(), "Periodic Recurrence");
|
case INVALID:
|
||||||
break;
|
break;
|
||||||
case AS_PER_DUES:
|
case PERIODIC:
|
||||||
optionData = new EnumOptionData(AccountTransferRecurrenceType.AS_PER_DUES.getValue().longValue(),
|
optionData = new EnumOptionData(AccountTransferRecurrenceType.PERIODIC.getValue().longValue(),
|
||||||
AccountTransferRecurrenceType.AS_PER_DUES.getCode(), "As Per Dues Recurrence");
|
AccountTransferRecurrenceType.PERIODIC.getCode(), "Periodic Recurrence");
|
||||||
break;
|
break;
|
||||||
}
|
case AS_PER_DUES:
|
||||||
}
|
optionData = new EnumOptionData(AccountTransferRecurrenceType.AS_PER_DUES.getValue().longValue(),
|
||||||
return optionData;
|
AccountTransferRecurrenceType.AS_PER_DUES.getCode(), "As Per Dues Recurrence");
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
public static EnumOptionData transferType(final Integer type) {
|
}
|
||||||
return transferType(AccountTransferType.fromInt(type));
|
return optionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnumOptionData transferType(final AccountTransferType type) {
|
public static EnumOptionData transferType(final Integer type) {
|
||||||
EnumOptionData optionData = null;
|
return transferType(AccountTransferType.fromInt(type));
|
||||||
if (type != null) {
|
}
|
||||||
switch (type) {
|
|
||||||
case INVALID:
|
public static EnumOptionData transferType(final AccountTransferType type) {
|
||||||
break;
|
EnumOptionData optionData = null;
|
||||||
case ACCOUNT_TRANSFER:
|
if (type != null) {
|
||||||
optionData = new EnumOptionData(AccountTransferType.ACCOUNT_TRANSFER.getValue().longValue(),
|
switch (type) {
|
||||||
AccountTransferType.ACCOUNT_TRANSFER.getCode(), "Account Transfer");
|
case INVALID:
|
||||||
break;
|
break;
|
||||||
case LOAN_REPAYMENT:
|
case ACCOUNT_TRANSFER:
|
||||||
optionData = new EnumOptionData(AccountTransferType.LOAN_REPAYMENT.getValue().longValue(),
|
optionData = new EnumOptionData(AccountTransferType.ACCOUNT_TRANSFER.getValue().longValue(),
|
||||||
AccountTransferType.LOAN_REPAYMENT.getCode(), "Loan Repayment");
|
AccountTransferType.ACCOUNT_TRANSFER.getCode(), "Account Transfer");
|
||||||
break;
|
break;
|
||||||
case CHARGE_PAYMENT:
|
case LOAN_REPAYMENT:
|
||||||
optionData = new EnumOptionData(AccountTransferType.CHARGE_PAYMENT.getValue().longValue(),
|
optionData = new EnumOptionData(AccountTransferType.LOAN_REPAYMENT.getValue().longValue(),
|
||||||
AccountTransferType.CHARGE_PAYMENT.getCode(), "Charge Payment");
|
AccountTransferType.LOAN_REPAYMENT.getCode(), "Loan Repayment");
|
||||||
break;
|
break;
|
||||||
case INTEREST_TRANSFER:
|
case CHARGE_PAYMENT:
|
||||||
optionData = new EnumOptionData(AccountTransferType.INTEREST_TRANSFER.getValue().longValue(),
|
optionData = new EnumOptionData(AccountTransferType.CHARGE_PAYMENT.getValue().longValue(),
|
||||||
AccountTransferType.INTEREST_TRANSFER.getCode(), "Interest Transfer");
|
AccountTransferType.CHARGE_PAYMENT.getCode(), "Charge Payment");
|
||||||
break;
|
break;
|
||||||
case LOAN_DOWN_PAYMENT:
|
case INTEREST_TRANSFER:
|
||||||
optionData = new EnumOptionData(AccountTransferType.LOAN_DOWN_PAYMENT.getValue().longValue(),
|
optionData = new EnumOptionData(AccountTransferType.INTEREST_TRANSFER.getValue().longValue(),
|
||||||
AccountTransferType.LOAN_DOWN_PAYMENT.getCode(), "Loan Down Payment");
|
AccountTransferType.INTEREST_TRANSFER.getCode(), "Interest Transfer");
|
||||||
break;
|
break;
|
||||||
|
case LOAN_DOWN_PAYMENT:
|
||||||
}
|
optionData = new EnumOptionData(AccountTransferType.LOAN_DOWN_PAYMENT.getValue().longValue(),
|
||||||
}
|
AccountTransferType.LOAN_DOWN_PAYMENT.getCode(), "Loan Down Payment");
|
||||||
return optionData;
|
break;
|
||||||
}
|
|
||||||
|
}
|
||||||
public static EnumOptionData standingInstructionPriority(final Integer type) {
|
}
|
||||||
return standingInstructionPriority(StandingInstructionPriority.fromInt(type));
|
return optionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnumOptionData standingInstructionPriority(final StandingInstructionPriority type) {
|
public static EnumOptionData standingInstructionPriority(final Integer type) {
|
||||||
EnumOptionData optionData = null;
|
return standingInstructionPriority(StandingInstructionPriority.fromInt(type));
|
||||||
if (type != null) {
|
}
|
||||||
switch (type) {
|
|
||||||
case INVALID:
|
public static EnumOptionData standingInstructionPriority(final StandingInstructionPriority type) {
|
||||||
break;
|
EnumOptionData optionData = null;
|
||||||
case HIGH:
|
if (type != null) {
|
||||||
optionData = new EnumOptionData(StandingInstructionPriority.HIGH.getValue().longValue(),
|
switch (type) {
|
||||||
StandingInstructionPriority.HIGH.getCode(), "High Priority");
|
case INVALID:
|
||||||
break;
|
break;
|
||||||
case LOW:
|
case HIGH:
|
||||||
optionData = new EnumOptionData(StandingInstructionPriority.LOW.getValue().longValue(),
|
optionData = new EnumOptionData(StandingInstructionPriority.HIGH.getValue().longValue(),
|
||||||
StandingInstructionPriority.LOW.getCode(), "Low Priority");
|
StandingInstructionPriority.HIGH.getCode(), "High Priority");
|
||||||
break;
|
break;
|
||||||
case MEDIUM:
|
case LOW:
|
||||||
optionData = new EnumOptionData(StandingInstructionPriority.MEDIUM.getValue().longValue(),
|
optionData = new EnumOptionData(StandingInstructionPriority.LOW.getValue().longValue(),
|
||||||
StandingInstructionPriority.MEDIUM.getCode(), "Medium Priority");
|
StandingInstructionPriority.LOW.getCode(), "Low Priority");
|
||||||
break;
|
break;
|
||||||
case URGENT:
|
case MEDIUM:
|
||||||
optionData = new EnumOptionData(StandingInstructionPriority.URGENT.getValue().longValue(),
|
optionData = new EnumOptionData(StandingInstructionPriority.MEDIUM.getValue().longValue(),
|
||||||
StandingInstructionPriority.URGENT.getCode(), "Urgent Priority");
|
StandingInstructionPriority.MEDIUM.getCode(), "Medium Priority");
|
||||||
break;
|
break;
|
||||||
}
|
case URGENT:
|
||||||
}
|
optionData = new EnumOptionData(StandingInstructionPriority.URGENT.getValue().longValue(),
|
||||||
return optionData;
|
StandingInstructionPriority.URGENT.getCode(), "Urgent Priority");
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
public static EnumOptionData standingInstructionStatus(final Integer type) {
|
}
|
||||||
return standingInstructionStatus(StandingInstructionStatus.fromInt(type));
|
return optionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnumOptionData standingInstructionStatus(final StandingInstructionStatus type) {
|
public static EnumOptionData standingInstructionStatus(final Integer type) {
|
||||||
EnumOptionData optionData = null;
|
return standingInstructionStatus(StandingInstructionStatus.fromInt(type));
|
||||||
if (type != null) {
|
}
|
||||||
switch (type) {
|
|
||||||
case INVALID:
|
public static EnumOptionData standingInstructionStatus(final StandingInstructionStatus type) {
|
||||||
break;
|
EnumOptionData optionData = null;
|
||||||
case ACTIVE:
|
if (type != null) {
|
||||||
optionData = new EnumOptionData(StandingInstructionStatus.ACTIVE.getValue().longValue(),
|
switch (type) {
|
||||||
StandingInstructionStatus.ACTIVE.getCode(), "Active");
|
case INVALID:
|
||||||
break;
|
break;
|
||||||
case DELETED:
|
case ACTIVE:
|
||||||
optionData = new EnumOptionData(StandingInstructionStatus.DELETED.getValue().longValue(),
|
optionData = new EnumOptionData(StandingInstructionStatus.ACTIVE.getValue().longValue(),
|
||||||
StandingInstructionStatus.DELETED.getCode(), "Deleted");
|
StandingInstructionStatus.ACTIVE.getCode(), "Active");
|
||||||
break;
|
break;
|
||||||
case DISABLED:
|
case DELETED:
|
||||||
optionData = new EnumOptionData(StandingInstructionStatus.DISABLED.getValue().longValue(),
|
optionData = new EnumOptionData(StandingInstructionStatus.DELETED.getValue().longValue(),
|
||||||
StandingInstructionStatus.DISABLED.getCode(), "Disabled");
|
StandingInstructionStatus.DELETED.getCode(), "Deleted");
|
||||||
break;
|
break;
|
||||||
}
|
case DISABLED:
|
||||||
}
|
optionData = new EnumOptionData(StandingInstructionStatus.DISABLED.getValue().longValue(),
|
||||||
return optionData;
|
StandingInstructionStatus.DISABLED.getCode(), "Disabled");
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
public static EnumOptionData standingInstructionType(final Integer type) {
|
}
|
||||||
return standingInstructionType(StandingInstructionType.fromInt(type));
|
return optionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnumOptionData standingInstructionType(final StandingInstructionType type) {
|
public static EnumOptionData standingInstructionType(final Integer type) {
|
||||||
EnumOptionData optionData = null;
|
return standingInstructionType(StandingInstructionType.fromInt(type));
|
||||||
if (type != null) {
|
}
|
||||||
switch (type) {
|
|
||||||
case INVALID:
|
public static EnumOptionData standingInstructionType(final StandingInstructionType type) {
|
||||||
break;
|
EnumOptionData optionData = null;
|
||||||
case DUES:
|
if (type != null) {
|
||||||
optionData = new EnumOptionData(StandingInstructionType.DUES.getValue().longValue(),
|
switch (type) {
|
||||||
StandingInstructionType.DUES.getCode(), "Dues");
|
case INVALID:
|
||||||
break;
|
break;
|
||||||
case FIXED:
|
case DUES:
|
||||||
optionData = new EnumOptionData(StandingInstructionType.FIXED.getValue().longValue(),
|
optionData = new EnumOptionData(StandingInstructionType.DUES.getValue().longValue(),
|
||||||
StandingInstructionType.FIXED.getCode(), "Fixed");
|
StandingInstructionType.DUES.getCode(), "Dues");
|
||||||
break;
|
break;
|
||||||
|
case FIXED:
|
||||||
}
|
optionData = new EnumOptionData(StandingInstructionType.FIXED.getValue().longValue(),
|
||||||
}
|
StandingInstructionType.FIXED.getCode(), "Fixed");
|
||||||
return optionData;
|
break;
|
||||||
}
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return optionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+865
-561
File diff suppressed because it is too large
Load Diff
+482
-396
@@ -1,396 +1,482 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.service;
|
package org.apache.fineract.portfolio.account.service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||||
import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO;
|
import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO;
|
||||||
import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
|
import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
|
||||||
import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException;
|
import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
|
||||||
public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService {
|
public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService {
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
// mapper
|
// mapper
|
||||||
private final PortfolioSavingsAccountMapper savingsAccountMapper;
|
private final PortfolioSavingsAccountMapper savingsAccountMapper;
|
||||||
private final PortfolioLoanAccountMapper loanAccountMapper;
|
private final PortfolioLoanAccountMapper loanAccountMapper;
|
||||||
private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper;
|
private final PortfolioLoanAccountRefundByTransferMapper accountRefundByTransferMapper;
|
||||||
|
private final PortfolioCurrentAccountMapper currentAccountMapper;
|
||||||
public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) {
|
|
||||||
this.jdbcTemplate = jdbcTemplate;
|
public PortfolioAccountReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||||
this.savingsAccountMapper = new PortfolioSavingsAccountMapper();
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
this.loanAccountMapper = new PortfolioLoanAccountMapper();
|
this.savingsAccountMapper = new PortfolioSavingsAccountMapper();
|
||||||
this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator);
|
this.loanAccountMapper = new PortfolioLoanAccountMapper();
|
||||||
}
|
this.accountRefundByTransferMapper = new PortfolioLoanAccountRefundByTransferMapper(sqlGenerator);
|
||||||
|
this.currentAccountMapper = new PortfolioCurrentAccountMapper();
|
||||||
@Override
|
}
|
||||||
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId) {
|
|
||||||
return retrieveOne(accountId, accountTypeId, null);
|
@Override
|
||||||
}
|
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId) {
|
||||||
|
return retrieveOne(accountId, accountTypeId, null);
|
||||||
@Override
|
}
|
||||||
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) {
|
|
||||||
|
@Override
|
||||||
Object[] sqlParams = new Object[] { accountId };
|
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) {
|
||||||
PortfolioAccountData accountData = null;
|
|
||||||
try {
|
Object[] sqlParams = new Object[] { accountId };
|
||||||
String sql = null;
|
PortfolioAccountData accountData = null;
|
||||||
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId);
|
try {
|
||||||
switch (accountType) {
|
String sql = null;
|
||||||
case INVALID:
|
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId);
|
||||||
break;
|
switch (accountType) {
|
||||||
case LOAN:
|
case INVALID:
|
||||||
|
break;
|
||||||
sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?";
|
case LOAN:
|
||||||
if (currencyCode != null) {
|
|
||||||
sql += " and la.currency_code = ?";
|
sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?";
|
||||||
sqlParams = new Object[] { accountId, currencyCode };
|
if (currencyCode != null) {
|
||||||
}
|
sql += " and la.currency_code = ?";
|
||||||
|
sqlParams = new Object[] { accountId, currencyCode };
|
||||||
accountData = this.jdbcTemplate.queryForObject(sql, this.loanAccountMapper, sqlParams);
|
}
|
||||||
break;
|
|
||||||
case SAVINGS:
|
accountData = this.jdbcTemplate.queryForObject(sql, this.loanAccountMapper, sqlParams);
|
||||||
sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?";
|
break;
|
||||||
if (currencyCode != null) {
|
case SAVINGS:
|
||||||
sql += " and sa.currency_code = ?";
|
sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?";
|
||||||
sqlParams = new Object[] { accountId, currencyCode };
|
if (currencyCode != null) {
|
||||||
}
|
sql += " and sa.currency_code = ?";
|
||||||
|
sqlParams = new Object[] { accountId, currencyCode };
|
||||||
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
|
}
|
||||||
break;
|
|
||||||
}
|
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
|
||||||
} catch (final EmptyResultDataAccessException e) {
|
break;
|
||||||
throw new AccountTransferNotFoundException(accountId, e);
|
case CURRENT:
|
||||||
}
|
sql = "select " + this.currentAccountMapper.schema() + " where ca.id = ?";
|
||||||
|
if (currencyCode != null) {
|
||||||
return accountData;
|
sql += " and ca.currency_code = ?";
|
||||||
}
|
sqlParams = new Object[] { accountId, currencyCode };
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public Collection<PortfolioAccountData> retrieveAllForLookup(final PortfolioAccountDTO portfolioAccountDTO) {
|
accountData = this.jdbcTemplate.queryForObject(sql, this.currentAccountMapper, sqlParams);
|
||||||
|
break;
|
||||||
final List<Object> sqlParams = new ArrayList<>();
|
}
|
||||||
// sqlParams.add(portfolioAccountDTO.getClientId());
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
Collection<PortfolioAccountData> accounts = null;
|
throw new AccountTransferNotFoundException(accountId, e);
|
||||||
String sql = null;
|
}
|
||||||
long defaultAccountStatus = 300; // Active Status
|
|
||||||
if (portfolioAccountDTO.getAccountStatus() != null) {
|
return accountData;
|
||||||
defaultAccountStatus = portfolioAccountDTO.getFirstAccountStatus();
|
}
|
||||||
}
|
|
||||||
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(portfolioAccountDTO.getAccountTypeId());
|
@Override
|
||||||
switch (accountType) {
|
public Collection<PortfolioAccountData> retrieveAllForLookup(final PortfolioAccountDTO portfolioAccountDTO) {
|
||||||
case INVALID:
|
|
||||||
break;
|
final List<Object> sqlParams = new ArrayList<>();
|
||||||
case LOAN:
|
// sqlParams.add(portfolioAccountDTO.getClientId());
|
||||||
sql = "select " + this.loanAccountMapper.schema() + " where ";
|
Collection<PortfolioAccountData> accounts = null;
|
||||||
if (portfolioAccountDTO.getClientId() != null) {
|
String sql = null;
|
||||||
sql += " la.client_id = ? and la.loan_status_id in (?) ";
|
long defaultAccountStatus = 300; // Active Status
|
||||||
sqlParams.add(portfolioAccountDTO.getClientId());
|
if (portfolioAccountDTO.getAccountStatus() != null) {
|
||||||
sqlParams.add(defaultAccountStatus);
|
defaultAccountStatus = portfolioAccountDTO.getFirstAccountStatus();
|
||||||
} else {
|
}
|
||||||
sql += " la.loan_status_id in (?) ";
|
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(portfolioAccountDTO.getAccountTypeId());
|
||||||
sqlParams.add(defaultAccountStatus);
|
switch (accountType) {
|
||||||
}
|
case INVALID:
|
||||||
if (portfolioAccountDTO.getCurrencyCode() != null) {
|
break;
|
||||||
sql += " and la.currency_code = ?";
|
case LOAN:
|
||||||
sqlParams.add(portfolioAccountDTO.getCurrencyCode());
|
sql = "select " + this.loanAccountMapper.schema() + " where ";
|
||||||
}
|
if (portfolioAccountDTO.getClientId() != null) {
|
||||||
|
sql += " la.client_id = ? and la.loan_status_id in (?) ";
|
||||||
accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams.toArray()); // NOSONAR
|
sqlParams.add(portfolioAccountDTO.getClientId());
|
||||||
break;
|
sqlParams.add(defaultAccountStatus);
|
||||||
case SAVINGS:
|
} else {
|
||||||
sql = "select " + this.savingsAccountMapper.schema() + " where ";
|
sql += " la.loan_status_id in (?) ";
|
||||||
if (portfolioAccountDTO.getClientId() != null) {
|
sqlParams.add(defaultAccountStatus);
|
||||||
sql += " sa.client_id = ? and sa.status_enum in (?) ";
|
}
|
||||||
sqlParams.add(portfolioAccountDTO.getClientId());
|
if (portfolioAccountDTO.getCurrencyCode() != null) {
|
||||||
sqlParams.add(defaultAccountStatus);
|
sql += " and la.currency_code = ?";
|
||||||
} else {
|
sqlParams.add(portfolioAccountDTO.getCurrencyCode());
|
||||||
sql += " sa.status_enum in (?) ";
|
}
|
||||||
sqlParams.add(defaultAccountStatus);
|
|
||||||
}
|
accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams.toArray()); // NOSONAR
|
||||||
if (portfolioAccountDTO.getCurrencyCode() != null) {
|
break;
|
||||||
sql += " and sa.currency_code = ?";
|
case SAVINGS:
|
||||||
sqlParams.add(portfolioAccountDTO.getCurrencyCode());
|
sql = "select " + this.savingsAccountMapper.schema() + " where ";
|
||||||
}
|
if (portfolioAccountDTO.getClientId() != null) {
|
||||||
|
sql += " sa.client_id = ? and sa.status_enum in (?) ";
|
||||||
if (portfolioAccountDTO.getDepositType() != null) {
|
sqlParams.add(portfolioAccountDTO.getClientId());
|
||||||
sql += " and sa.deposit_type_enum = ?";
|
sqlParams.add(defaultAccountStatus);
|
||||||
sqlParams.add(portfolioAccountDTO.getDepositType().shortValue());
|
} else {
|
||||||
}
|
sql += " sa.status_enum in (?) ";
|
||||||
|
sqlParams.add(defaultAccountStatus);
|
||||||
if (portfolioAccountDTO.isExcludeOverDraftAccounts()) {
|
}
|
||||||
sql += " and sa.allow_overdraft = false";
|
if (portfolioAccountDTO.getCurrencyCode() != null) {
|
||||||
}
|
sql += " and sa.currency_code = ?";
|
||||||
|
sqlParams.add(portfolioAccountDTO.getCurrencyCode());
|
||||||
if (portfolioAccountDTO.getClientId() == null && portfolioAccountDTO.getGroupId() != null) {
|
}
|
||||||
sql += " and sa.group_id = ? ";
|
|
||||||
sqlParams.add(portfolioAccountDTO.getGroupId());
|
if (portfolioAccountDTO.getDepositType() != null) {
|
||||||
}
|
sql += " and sa.deposit_type_enum = ?";
|
||||||
|
sqlParams.add(portfolioAccountDTO.getDepositType().shortValue());
|
||||||
accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR
|
}
|
||||||
break;
|
|
||||||
}
|
if (portfolioAccountDTO.isExcludeOverDraftAccounts()) {
|
||||||
|
sql += " and sa.allow_overdraft = false";
|
||||||
return accounts;
|
}
|
||||||
}
|
|
||||||
|
if (portfolioAccountDTO.getClientId() == null && portfolioAccountDTO.getGroupId() != null) {
|
||||||
private static final class PortfolioSavingsAccountMapper implements RowMapper<PortfolioAccountData> {
|
sql += " and sa.group_id = ? ";
|
||||||
|
sqlParams.add(portfolioAccountDTO.getGroupId());
|
||||||
private final String schemaSql;
|
}
|
||||||
|
|
||||||
PortfolioSavingsAccountMapper() {
|
accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams.toArray()); // NOSONAR
|
||||||
|
break;
|
||||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
case CURRENT:
|
||||||
sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, ");
|
sql = "select " + this.currentAccountMapper.schema() + " where ";
|
||||||
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
if (portfolioAccountDTO.getClientId() != null) {
|
||||||
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
|
sql += " ca.client_id = ? and ca.status_enum in (?) ";
|
||||||
sqlBuilder.append("sp.id as productId, sp.name as productName, ");
|
sqlParams.add(portfolioAccountDTO.getClientId());
|
||||||
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
|
sqlParams.add(defaultAccountStatus);
|
||||||
sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,");
|
} else {
|
||||||
sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, ");
|
sql += " ca.status_enum in (?) ";
|
||||||
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
sqlParams.add(defaultAccountStatus);
|
||||||
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
|
}
|
||||||
sqlBuilder.append("from m_savings_account sa ");
|
if (portfolioAccountDTO.getCurrencyCode() != null) {
|
||||||
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
|
sql += " and ca.currency_code = ?";
|
||||||
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
sqlParams.add(portfolioAccountDTO.getCurrencyCode());
|
||||||
sqlBuilder.append("left join m_client c ON c.id = sa.client_id ");
|
}
|
||||||
sqlBuilder.append("left join m_group g ON g.id = sa.group_id ");
|
|
||||||
sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id ");
|
accounts = this.jdbcTemplate.query(sql, this.currentAccountMapper, sqlParams.toArray()); // NOSONAR
|
||||||
|
break;
|
||||||
this.schemaSql = sqlBuilder.toString();
|
}
|
||||||
}
|
|
||||||
|
return accounts;
|
||||||
public String schema() {
|
}
|
||||||
return this.schemaSql;
|
|
||||||
}
|
private static final class PortfolioSavingsAccountMapper implements RowMapper<PortfolioAccountData> {
|
||||||
|
|
||||||
@Override
|
private final String schemaSql;
|
||||||
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
|
||||||
|
PortfolioSavingsAccountMapper() {
|
||||||
final Long id = rs.getLong("id");
|
|
||||||
final String accountNo = rs.getString("accountNo");
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
final String externalId = rs.getString("externalId");
|
sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, ");
|
||||||
|
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
||||||
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
|
||||||
final String groupName = rs.getString("groupName");
|
sqlBuilder.append("sp.id as productId, sp.name as productName, ");
|
||||||
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
|
||||||
final String clientName = rs.getString("clientName");
|
sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,");
|
||||||
|
sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, ");
|
||||||
final Long productId = rs.getLong("productId");
|
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
||||||
final String productName = rs.getString("productName");
|
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
|
||||||
|
sqlBuilder.append("from m_savings_account sa ");
|
||||||
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
|
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
|
||||||
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
||||||
|
sqlBuilder.append("left join m_client c ON c.id = sa.client_id ");
|
||||||
final String currencyCode = rs.getString("currencyCode");
|
sqlBuilder.append("left join m_group g ON g.id = sa.group_id ");
|
||||||
final String currencyName = rs.getString("currencyName");
|
sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id ");
|
||||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
|
||||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
this.schemaSql = sqlBuilder.toString();
|
||||||
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
}
|
||||||
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
|
||||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
|
public String schema() {
|
||||||
currencyNameCode);
|
return this.schemaSql;
|
||||||
|
}
|
||||||
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
|
||||||
fieldOfficerId, fieldOfficerName, currency);
|
@Override
|
||||||
}
|
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
}
|
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
private static final class PortfolioLoanAccountMapper implements RowMapper<PortfolioAccountData> {
|
final String accountNo = rs.getString("accountNo");
|
||||||
|
final String externalId = rs.getString("externalId");
|
||||||
private final String schemaSql;
|
|
||||||
|
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
||||||
PortfolioLoanAccountMapper() {
|
final String groupName = rs.getString("groupName");
|
||||||
|
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
||||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
final String clientName = rs.getString("clientName");
|
||||||
sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, ");
|
|
||||||
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
final Long productId = rs.getLong("productId");
|
||||||
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
|
final String productName = rs.getString("productName");
|
||||||
sqlBuilder.append("lp.id as productId, lp.name as productName, ");
|
|
||||||
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
|
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
|
||||||
sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
|
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
||||||
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
|
|
||||||
sqlBuilder.append("la.total_overpaid_derived as totalOverpaid, ");
|
final String currencyCode = rs.getString("currencyCode");
|
||||||
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
final String currencyName = rs.getString("currencyName");
|
||||||
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
|
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||||
sqlBuilder.append("from m_loan la ");
|
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||||
sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id ");
|
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||||
sqlBuilder.append("join m_currency curr on curr.code = la.currency_code ");
|
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||||
sqlBuilder.append("left join m_client c ON c.id = la.client_id ");
|
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
|
||||||
sqlBuilder.append("left join m_group g ON g.id = la.group_id ");
|
currencyNameCode);
|
||||||
sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
|
|
||||||
|
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||||
this.schemaSql = sqlBuilder.toString();
|
fieldOfficerId, fieldOfficerName, currency);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public String schema() {
|
|
||||||
return this.schemaSql;
|
private static final class PortfolioLoanAccountMapper implements RowMapper<PortfolioAccountData> {
|
||||||
}
|
|
||||||
|
private final String schemaSql;
|
||||||
@Override
|
|
||||||
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
PortfolioLoanAccountMapper() {
|
||||||
|
|
||||||
final Long id = rs.getLong("id");
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
final String accountNo = rs.getString("accountNo");
|
sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, ");
|
||||||
final String externalId = rs.getString("externalId");
|
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
||||||
|
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
|
||||||
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
sqlBuilder.append("lp.id as productId, lp.name as productName, ");
|
||||||
final String groupName = rs.getString("groupName");
|
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
|
||||||
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
|
||||||
final String clientName = rs.getString("clientName");
|
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
|
||||||
|
sqlBuilder.append("la.total_overpaid_derived as totalOverpaid, ");
|
||||||
final Long productId = rs.getLong("productId");
|
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
||||||
final String productName = rs.getString("productName");
|
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
|
||||||
|
sqlBuilder.append("from m_loan la ");
|
||||||
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
|
sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id ");
|
||||||
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
sqlBuilder.append("join m_currency curr on curr.code = la.currency_code ");
|
||||||
|
sqlBuilder.append("left join m_client c ON c.id = la.client_id ");
|
||||||
final String currencyCode = rs.getString("currencyCode");
|
sqlBuilder.append("left join m_group g ON g.id = la.group_id ");
|
||||||
final String currencyName = rs.getString("currencyName");
|
sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
|
||||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
|
||||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
this.schemaSql = sqlBuilder.toString();
|
||||||
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
}
|
||||||
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
|
||||||
final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
|
public String schema() {
|
||||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
|
return this.schemaSql;
|
||||||
currencyNameCode);
|
}
|
||||||
|
|
||||||
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
@Override
|
||||||
fieldOfficerId, fieldOfficerName, currency, amtForTransfer);
|
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
}
|
|
||||||
}
|
final Long id = rs.getLong("id");
|
||||||
|
final String accountNo = rs.getString("accountNo");
|
||||||
private static final class PortfolioLoanAccountRefundByTransferMapper implements RowMapper<PortfolioAccountData> {
|
final String externalId = rs.getString("externalId");
|
||||||
|
|
||||||
private String schemaSql;
|
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
||||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
final String groupName = rs.getString("groupName");
|
||||||
|
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
||||||
PortfolioLoanAccountRefundByTransferMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
|
final String clientName = rs.getString("clientName");
|
||||||
this.sqlGenerator = sqlGenerator;
|
|
||||||
}
|
final Long productId = rs.getLong("productId");
|
||||||
|
final String productName = rs.getString("productName");
|
||||||
public String schema() {
|
|
||||||
final StringBuilder amountQueryString = new StringBuilder(400);
|
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
|
||||||
amountQueryString.append("(select (SUM(COALESCE(mr.principal_completed_derived, 0)) +");
|
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
||||||
amountQueryString.append("SUM(COALESCE(mr.interest_completed_derived, 0)) + ");
|
|
||||||
amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + ");
|
final String currencyCode = rs.getString("currencyCode");
|
||||||
amountQueryString.append(" SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived");
|
final String currencyName = rs.getString("currencyName");
|
||||||
amountQueryString.append(" from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id");
|
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||||
amountQueryString.append(" where ml.id=? and ml.loan_status_id = 300");
|
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||||
amountQueryString.append(" and mr.duedate >= " + sqlGenerator.currentBusinessDate() + " group by ml.id having");
|
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||||
amountQueryString.append(" (SUM(COALESCE(mr.principal_completed_derived, 0)) + ");
|
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||||
amountQueryString.append(" SUM(COALESCE(mr.interest_completed_derived, 0)) + ");
|
final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
|
||||||
amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + ");
|
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
|
||||||
amountQueryString.append("SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0) as totalOverpaid ");
|
currencyNameCode);
|
||||||
|
|
||||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||||
sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, ");
|
fieldOfficerId, fieldOfficerName, currency, amtForTransfer);
|
||||||
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
}
|
||||||
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
|
}
|
||||||
sqlBuilder.append("lp.id as productId, lp.name as productName, ");
|
|
||||||
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
|
private static final class PortfolioLoanAccountRefundByTransferMapper implements RowMapper<PortfolioAccountData> {
|
||||||
sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
|
|
||||||
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
|
private String schemaSql;
|
||||||
sqlBuilder.append(amountQueryString.toString());
|
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||||
sqlBuilder.append(", ");
|
|
||||||
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
PortfolioLoanAccountRefundByTransferMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||||
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
|
this.sqlGenerator = sqlGenerator;
|
||||||
sqlBuilder.append("from m_loan la ");
|
}
|
||||||
sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id ");
|
|
||||||
sqlBuilder.append("join m_currency curr on curr.code = la.currency_code ");
|
public String schema() {
|
||||||
sqlBuilder.append("left join m_client c ON c.id = la.client_id ");
|
final StringBuilder amountQueryString = new StringBuilder(400);
|
||||||
sqlBuilder.append("left join m_group g ON g.id = la.group_id ");
|
amountQueryString.append("(select (SUM(COALESCE(mr.principal_completed_derived, 0)) +");
|
||||||
sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
|
amountQueryString.append("SUM(COALESCE(mr.interest_completed_derived, 0)) + ");
|
||||||
|
amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + ");
|
||||||
this.schemaSql = sqlBuilder.toString();
|
amountQueryString.append(" SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived");
|
||||||
|
amountQueryString.append(" from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id");
|
||||||
return this.schemaSql;
|
amountQueryString.append(" where ml.id=? and ml.loan_status_id = 300");
|
||||||
}
|
amountQueryString.append(" and mr.duedate >= " + sqlGenerator.currentBusinessDate() + " group by ml.id having");
|
||||||
|
amountQueryString.append(" (SUM(COALESCE(mr.principal_completed_derived, 0)) + ");
|
||||||
@Override
|
amountQueryString.append(" SUM(COALESCE(mr.interest_completed_derived, 0)) + ");
|
||||||
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
amountQueryString.append("SUM(COALESCE(mr.fee_charges_completed_derived, 0)) + ");
|
||||||
|
amountQueryString.append("SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0) as totalOverpaid ");
|
||||||
final Long id = rs.getLong("id");
|
|
||||||
final String accountNo = rs.getString("accountNo");
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
final String externalId = rs.getString("externalId");
|
sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, ");
|
||||||
|
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
||||||
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
|
||||||
final String groupName = rs.getString("groupName");
|
sqlBuilder.append("lp.id as productId, lp.name as productName, ");
|
||||||
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
|
||||||
final String clientName = rs.getString("clientName");
|
sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
|
||||||
|
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
|
||||||
final Long productId = rs.getLong("productId");
|
sqlBuilder.append(amountQueryString.toString());
|
||||||
final String productName = rs.getString("productName");
|
sqlBuilder.append(", ");
|
||||||
|
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
||||||
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
|
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
|
||||||
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
sqlBuilder.append("from m_loan la ");
|
||||||
|
sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id ");
|
||||||
final String currencyCode = rs.getString("currencyCode");
|
sqlBuilder.append("join m_currency curr on curr.code = la.currency_code ");
|
||||||
final String currencyName = rs.getString("currencyName");
|
sqlBuilder.append("left join m_client c ON c.id = la.client_id ");
|
||||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
sqlBuilder.append("left join m_group g ON g.id = la.group_id ");
|
||||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
|
||||||
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
|
||||||
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
this.schemaSql = sqlBuilder.toString();
|
||||||
final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
|
|
||||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
|
return this.schemaSql;
|
||||||
currencyNameCode);
|
}
|
||||||
|
|
||||||
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
@Override
|
||||||
fieldOfficerId, fieldOfficerName, currency, amtForTransfer);
|
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
}
|
|
||||||
}
|
final Long id = rs.getLong("id");
|
||||||
|
final String accountNo = rs.getString("accountNo");
|
||||||
@Override
|
final String externalId = rs.getString("externalId");
|
||||||
public PortfolioAccountData retrieveOneByPaidInAdvance(Long accountId, Integer accountTypeId) {
|
|
||||||
// TODO Auto-generated method stub
|
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
||||||
Object[] sqlParams = new Object[] { accountId, accountId };
|
final String groupName = rs.getString("groupName");
|
||||||
PortfolioAccountData accountData = null;
|
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
||||||
// String currencyCode = null;
|
final String clientName = rs.getString("clientName");
|
||||||
try {
|
|
||||||
String sql = null;
|
final Long productId = rs.getLong("productId");
|
||||||
// final PortfolioAccountType accountType =
|
final String productName = rs.getString("productName");
|
||||||
// PortfolioAccountType.fromInt(accountTypeId);
|
|
||||||
|
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
|
||||||
sql = "select " + this.accountRefundByTransferMapper.schema() + " where la.id = ?";
|
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
||||||
/*
|
|
||||||
* if (currencyCode != null) { sql += " and la.currency_code = ?"; sqlParams = new Object[] {accountId ,
|
final String currencyCode = rs.getString("currencyCode");
|
||||||
* accountId,currencyCode }; }
|
final String currencyName = rs.getString("currencyName");
|
||||||
*/
|
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||||
|
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||||
accountData = this.jdbcTemplate.queryForObject(sql, this.accountRefundByTransferMapper, sqlParams);
|
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||||
|
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||||
} catch (final EmptyResultDataAccessException e) {
|
final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
|
||||||
throw new AccountTransferNotFoundException(accountId, e);
|
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf, currencyDisplaySymbol,
|
||||||
}
|
currencyNameCode);
|
||||||
|
|
||||||
return accountData;
|
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||||
}
|
fieldOfficerId, fieldOfficerName, currency, amtForTransfer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PortfolioAccountData retrieveOneByPaidInAdvance(Long accountId, Integer accountTypeId) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
Object[] sqlParams = new Object[] { accountId, accountId };
|
||||||
|
PortfolioAccountData accountData = null;
|
||||||
|
// String currencyCode = null;
|
||||||
|
try {
|
||||||
|
String sql = null;
|
||||||
|
// final PortfolioAccountType accountType =
|
||||||
|
// PortfolioAccountType.fromInt(accountTypeId);
|
||||||
|
|
||||||
|
sql = "select " + this.accountRefundByTransferMapper.schema() + " where la.id = ?";
|
||||||
|
/*
|
||||||
|
* if (currencyCode != null) { sql += " and la.currency_code = ?"; sqlParams = new Object[] {accountId ,
|
||||||
|
* accountId,currencyCode }; }
|
||||||
|
*/
|
||||||
|
|
||||||
|
accountData = this.jdbcTemplate.queryForObject(sql, this.accountRefundByTransferMapper, sqlParams);
|
||||||
|
|
||||||
|
} catch (final EmptyResultDataAccessException e) {
|
||||||
|
throw new AccountTransferNotFoundException(accountId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class PortfolioCurrentAccountMapper implements RowMapper<PortfolioAccountData> {
|
||||||
|
|
||||||
|
private final String schemaSql;
|
||||||
|
|
||||||
|
PortfolioCurrentAccountMapper() {
|
||||||
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
|
sqlBuilder.append("ca.id as id, ca.account_no as accountNo, ca.external_id as externalId, ");
|
||||||
|
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
|
||||||
|
sqlBuilder.append("null as groupId, null as groupName, ");
|
||||||
|
sqlBuilder.append("cp.id as productId, cp.name as productName, ");
|
||||||
|
sqlBuilder.append("null as fieldOfficerId, null as fieldOfficerName, ");
|
||||||
|
sqlBuilder.append("ca.currency_code as currencyCode, ca.currency_digits as currencyDigits, ");
|
||||||
|
sqlBuilder.append("ca.currency_multiplesof as inMultiplesOf, ");
|
||||||
|
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
||||||
|
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
|
||||||
|
sqlBuilder.append("from m_current_account ca ");
|
||||||
|
sqlBuilder.append("join m_current_product cp ON ca.product_id = cp.id ");
|
||||||
|
sqlBuilder.append("join m_currency curr on curr.code = ca.currency_code ");
|
||||||
|
sqlBuilder.append("left join m_client c ON c.id = ca.client_id ");
|
||||||
|
|
||||||
|
this.schemaSql = sqlBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return this.schemaSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
|
final Long id = rs.getLong("id");
|
||||||
|
final String accountNo = rs.getString("accountNo");
|
||||||
|
final String externalId = rs.getString("externalId");
|
||||||
|
|
||||||
|
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
||||||
|
final String groupName = rs.getString("groupName");
|
||||||
|
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
||||||
|
final String clientName = rs.getString("clientName");
|
||||||
|
|
||||||
|
final Long productId = rs.getLong("productId");
|
||||||
|
final String productName = rs.getString("productName");
|
||||||
|
|
||||||
|
final Long fieldOfficerId = JdbcSupport.getLong(rs, "fieldOfficerId");
|
||||||
|
final String fieldOfficerName = rs.getString("fieldOfficerName");
|
||||||
|
|
||||||
|
final String currencyCode = rs.getString("currencyCode");
|
||||||
|
final String currencyName = rs.getString("currencyName");
|
||||||
|
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||||
|
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||||
|
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
|
||||||
|
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
|
||||||
|
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
|
||||||
|
currencyDisplaySymbol, currencyNameCode);
|
||||||
|
|
||||||
|
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||||
|
fieldOfficerId, fieldOfficerName, currency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+626
-591
File diff suppressed because it is too large
Load Diff
+179
-145
@@ -1,145 +1,179 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.service;
|
package org.apache.fineract.portfolio.account.service;
|
||||||
|
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromAccountTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.fromClientIdParamName;
|
||||||
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName;
|
import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAccountTypeParamName;
|
||||||
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName;
|
import static org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants.statusParamName;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||||
import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants;
|
import org.apache.fineract.portfolio.account.api.StandingInstructionApiConstants;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferStandingInstruction;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
||||||
import org.apache.fineract.portfolio.account.exception.StandingInstructionNotFoundException;
|
import org.apache.fineract.portfolio.account.exception.StandingInstructionNotFoundException;
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
import org.springframework.dao.NonTransientDataAccessException;
|
import org.springframework.dao.NonTransientDataAccessException;
|
||||||
import org.springframework.orm.jpa.JpaSystemException;
|
import org.springframework.orm.jpa.JpaSystemException;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class StandingInstructionWritePlatformServiceImpl implements StandingInstructionWritePlatformService {
|
public class StandingInstructionWritePlatformServiceImpl implements StandingInstructionWritePlatformService {
|
||||||
|
|
||||||
private final StandingInstructionDataValidator standingInstructionDataValidator;
|
private final StandingInstructionDataValidator standingInstructionDataValidator;
|
||||||
private final StandingInstructionAssembler standingInstructionAssembler;
|
private final StandingInstructionAssembler standingInstructionAssembler;
|
||||||
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
||||||
private final StandingInstructionRepository standingInstructionRepository;
|
private final StandingInstructionRepository standingInstructionRepository;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult create(final JsonCommand command) {
|
public CommandProcessingResult create(final JsonCommand command) {
|
||||||
|
|
||||||
this.standingInstructionDataValidator.validateForCreate(command);
|
this.standingInstructionDataValidator.validateForCreate(command);
|
||||||
|
|
||||||
final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName);
|
final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName);
|
||||||
final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId);
|
final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId);
|
||||||
|
|
||||||
final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName);
|
final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName);
|
||||||
final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId);
|
final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId);
|
||||||
|
|
||||||
final Long fromClientId = command.longValueOfParameterNamed(fromClientIdParamName);
|
final Long fromClientId = command.longValueOfParameterNamed(fromClientIdParamName);
|
||||||
|
|
||||||
Long standingInstructionId = null;
|
Long standingInstructionId = null;
|
||||||
try {
|
try {
|
||||||
if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler
|
||||||
.assembleSavingsToSavingsTransfer(command);
|
.assembleSavingsToSavingsTransfer(command);
|
||||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
} else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) {
|
} else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToLoanTransfer(command);
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToLoanTransfer(command);
|
||||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command);
|
||||||
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleLoanToSavingsTransfer(command);
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
} else if (isCurrentToCurrentAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToCurrentTransfer(command);
|
||||||
}
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
final Throwable throwable = dve.getMostSpecificCause();
|
} else if (isCurrentToSavingsAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
handleDataIntegrityIssues(command, throwable, dve);
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToSavingsTransfer(command);
|
||||||
return CommandProcessingResult.empty();
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
}
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(standingInstructionId)
|
} else if (isSavingsToCurrentAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
.withClientId(fromClientId);
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleSavingsToCurrentTransfer(command);
|
||||||
return builder.build();
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
}
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
|
} else if (isCurrentToLoanAccountTransfer(fromAccountType, toAccountType)) {
|
||||||
private void handleDataIntegrityIssues(final JsonCommand command, Throwable realCause, final NonTransientDataAccessException dve) {
|
final AccountTransferDetails standingInstruction = this.standingInstructionAssembler.assembleCurrentToLoanTransfer(command);
|
||||||
if (realCause.getMessage().contains("name")) {
|
this.accountTransferDetailRepository.saveAndFlush(standingInstruction);
|
||||||
final String name = command.stringValueOfParameterNamed(StandingInstructionApiConstants.nameParamName);
|
standingInstructionId = standingInstruction.accountTransferStandingInstruction().getId();
|
||||||
throw new PlatformDataIntegrityException("error.msg.standinginstruction.duplicate.name",
|
}
|
||||||
"Standinginstruction with name `" + name + "` already exists", "name", name);
|
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||||
}
|
final Throwable throwable = dve.getMostSpecificCause();
|
||||||
log.error("Error occured.", dve);
|
handleDataIntegrityIssues(command, throwable, dve);
|
||||||
throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue",
|
return CommandProcessingResult.empty();
|
||||||
"Unknown data integrity issue with resource: " + realCause.getMessage());
|
}
|
||||||
}
|
final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(standingInstructionId)
|
||||||
|
.withClientId(fromClientId);
|
||||||
private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
|
return builder.build();
|
||||||
return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount();
|
}
|
||||||
}
|
|
||||||
|
private void handleDataIntegrityIssues(final JsonCommand command, Throwable realCause, final NonTransientDataAccessException dve) {
|
||||||
private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
|
if (realCause.getMessage().contains("name")) {
|
||||||
return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount();
|
final String name = command.stringValueOfParameterNamed(StandingInstructionApiConstants.nameParamName);
|
||||||
}
|
throw new PlatformDataIntegrityException("error.msg.standinginstruction.duplicate.name",
|
||||||
|
"Standinginstruction with name `" + name + "` already exists", "name", name);
|
||||||
private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType,
|
}
|
||||||
final PortfolioAccountType toAccountType) {
|
log.error("Error occured.", dve);
|
||||||
return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount();
|
throw ErrorHandler.getMappable(dve, "error.msg.client.unknown.data.integrity.issue",
|
||||||
}
|
"Unknown data integrity issue with resource: " + realCause.getMessage());
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public CommandProcessingResult update(final Long id, final JsonCommand command) {
|
private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
|
||||||
this.standingInstructionDataValidator.validateForUpdate(command);
|
return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount();
|
||||||
AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id)
|
}
|
||||||
.orElseThrow(() -> new StandingInstructionNotFoundException(id));
|
|
||||||
final Map<String, Object> actualChanges = standingInstructionsForUpdate.update(command);
|
private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
|
||||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(id).with(actualChanges).build();
|
return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||||
public CommandProcessingResult delete(final Long id) {
|
final PortfolioAccountType toAccountType) {
|
||||||
AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id).orElseThrow();
|
return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount();
|
||||||
// update the "deleted" and "name" properties of the standing
|
}
|
||||||
// instruction
|
|
||||||
standingInstructionsForUpdate.delete();
|
private boolean isCurrentToCurrentAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||||
|
final PortfolioAccountType toAccountType) {
|
||||||
final Map<String, Object> actualChanges = new HashMap<>();
|
return fromAccountType.isCurrentAccount() && toAccountType.isCurrentAccount();
|
||||||
actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue());
|
}
|
||||||
return new CommandProcessingResultBuilder().withEntityId(id).with(actualChanges).build();
|
|
||||||
}
|
private boolean isCurrentToSavingsAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||||
}
|
final PortfolioAccountType toAccountType) {
|
||||||
|
return fromAccountType.isCurrentAccount() && toAccountType.isSavingsAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSavingsToCurrentAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||||
|
final PortfolioAccountType toAccountType) {
|
||||||
|
return fromAccountType.isSavingsAccount() && toAccountType.isCurrentAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCurrentToLoanAccountTransfer(final PortfolioAccountType fromAccountType,
|
||||||
|
final PortfolioAccountType toAccountType) {
|
||||||
|
return fromAccountType.isCurrentAccount() && toAccountType.isLoanAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult update(final Long id, final JsonCommand command) {
|
||||||
|
this.standingInstructionDataValidator.validateForUpdate(command);
|
||||||
|
AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new StandingInstructionNotFoundException(id));
|
||||||
|
final Map<String, Object> actualChanges = standingInstructionsForUpdate.update(command);
|
||||||
|
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(id).with(actualChanges).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult delete(final Long id) {
|
||||||
|
AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id).orElseThrow();
|
||||||
|
// update the "deleted" and "name" properties of the standing
|
||||||
|
// instruction
|
||||||
|
standingInstructionsForUpdate.delete();
|
||||||
|
|
||||||
|
final Map<String, Object> actualChanges = new HashMap<>();
|
||||||
|
actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue());
|
||||||
|
return new CommandProcessingResultBuilder().withEntityId(id).with(actualChanges).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+140
-134
@@ -1,134 +1,140 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.starter;
|
package org.apache.fineract.portfolio.account.starter;
|
||||||
|
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.accounting.journalentry.service.AccountingProcessorHelper;
|
||||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||||
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
||||||
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||||
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||||
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
|
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler;
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionDataValidator;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferAssembler;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||||
import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformServiceImpl;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformService;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionHistoryReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformServiceImpl;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionWritePlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.savings.domain.GSIMRepositoy;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
@Configuration
|
import org.springframework.context.annotation.Configuration;
|
||||||
public class AccountConfiguration {
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
@Bean
|
@Configuration
|
||||||
@ConditionalOnMissingBean(AccountAssociationsReadPlatformService.class)
|
public class AccountConfiguration {
|
||||||
public AccountAssociationsReadPlatformService accountAssociationsReadPlatformService(JdbcTemplate jdbcTemplate) {
|
|
||||||
return new AccountAssociationsReadPlatformServiceImpl(jdbcTemplate);
|
@Bean
|
||||||
}
|
@ConditionalOnMissingBean(AccountAssociationsReadPlatformService.class)
|
||||||
|
public AccountAssociationsReadPlatformService accountAssociationsReadPlatformService(JdbcTemplate jdbcTemplate) {
|
||||||
@Bean
|
return new AccountAssociationsReadPlatformServiceImpl(jdbcTemplate);
|
||||||
@ConditionalOnMissingBean(AccountTransfersReadPlatformService.class)
|
}
|
||||||
public AccountTransfersReadPlatformService accountTransfersReadPlatformService(JdbcTemplate jdbcTemplate,
|
|
||||||
ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService,
|
@Bean
|
||||||
PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, ColumnValidator columnValidator,
|
@ConditionalOnMissingBean(AccountTransfersReadPlatformService.class)
|
||||||
DatabaseSpecificSQLGenerator sqlGenerator, AccountTransfersMapper accountTransfersMapper, PaginationHelper paginationHelper,
|
public AccountTransfersReadPlatformService accountTransfersReadPlatformService(JdbcTemplate jdbcTemplate,
|
||||||
SqlValidator sqlValidator) {
|
ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService,
|
||||||
return new AccountTransfersReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService,
|
PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, ColumnValidator columnValidator,
|
||||||
portfolioAccountReadPlatformService, columnValidator, sqlGenerator, accountTransfersMapper, paginationHelper, sqlValidator);
|
DatabaseSpecificSQLGenerator sqlGenerator, AccountTransfersMapper accountTransfersMapper, PaginationHelper paginationHelper,
|
||||||
}
|
SqlValidator sqlValidator) {
|
||||||
|
return new AccountTransfersReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService,
|
||||||
@Bean
|
portfolioAccountReadPlatformService, columnValidator, sqlGenerator, accountTransfersMapper, paginationHelper, sqlValidator);
|
||||||
@ConditionalOnMissingBean(AccountTransfersWritePlatformService.class)
|
}
|
||||||
public AccountTransfersWritePlatformService accountTransfersWritePlatformService(
|
|
||||||
AccountTransfersDataValidator accountTransfersDataValidator, AccountTransferAssembler accountTransferAssembler,
|
@Bean
|
||||||
AccountTransferRepository accountTransferRepository, SavingsAccountAssembler savingsAccountAssembler,
|
@ConditionalOnMissingBean(AccountTransfersWritePlatformService.class)
|
||||||
SavingsAccountDomainService savingsAccountDomainService, LoanAssembler loanAccountAssembler,
|
public AccountTransfersWritePlatformService accountTransfersWritePlatformService(
|
||||||
LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService,
|
AccountTransfersDataValidator accountTransfersDataValidator, AccountTransferAssembler accountTransferAssembler,
|
||||||
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
|
AccountTransferRepository accountTransferRepository, SavingsAccountAssembler savingsAccountAssembler,
|
||||||
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
|
SavingsAccountDomainService savingsAccountDomainService, LoanAssembler loanAccountAssembler,
|
||||||
FineractProperties fineractProperties) {
|
LoanAccountDomainService loanAccountDomainService, SavingsAccountWritePlatformService savingsAccountWritePlatformService,
|
||||||
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
|
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
|
||||||
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
|
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
|
||||||
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
|
FineractProperties fineractProperties, CurrentAccountRepository currentAccountRepository,
|
||||||
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties);
|
CurrentAccountTransactionRepository currentAccountTransactionRepository,
|
||||||
}
|
AccountingProcessorHelper accountingProcessorHelper) {
|
||||||
|
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
|
||||||
@Bean
|
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
|
||||||
@ConditionalOnMissingBean(PortfolioAccountReadPlatformService.class)
|
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
|
||||||
public PortfolioAccountReadPlatformService portfolioAccountReadPlatformService(JdbcTemplate jdbcTemplate,
|
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties,
|
||||||
DatabaseSpecificSQLGenerator sqlGenerator) {
|
currentAccountRepository, currentAccountTransactionRepository, accountingProcessorHelper);
|
||||||
return new PortfolioAccountReadPlatformServiceImpl(jdbcTemplate, sqlGenerator);
|
}
|
||||||
}
|
|
||||||
|
@Bean
|
||||||
@Bean
|
@ConditionalOnMissingBean(PortfolioAccountReadPlatformService.class)
|
||||||
@ConditionalOnMissingBean(StandingInstructionHistoryReadPlatformService.class)
|
public PortfolioAccountReadPlatformService portfolioAccountReadPlatformService(JdbcTemplate jdbcTemplate,
|
||||||
public StandingInstructionHistoryReadPlatformService standingInstructionHistoryReadPlatformService(JdbcTemplate jdbcTemplate,
|
DatabaseSpecificSQLGenerator sqlGenerator) {
|
||||||
ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
|
return new PortfolioAccountReadPlatformServiceImpl(jdbcTemplate, sqlGenerator);
|
||||||
return new StandingInstructionHistoryReadPlatformServiceImpl(jdbcTemplate, columnValidator, sqlGenerator, paginationHelper);
|
}
|
||||||
}
|
|
||||||
|
@Bean
|
||||||
@Bean
|
@ConditionalOnMissingBean(StandingInstructionHistoryReadPlatformService.class)
|
||||||
@ConditionalOnMissingBean(StandingInstructionReadPlatformService.class)
|
public StandingInstructionHistoryReadPlatformService standingInstructionHistoryReadPlatformService(JdbcTemplate jdbcTemplate,
|
||||||
public StandingInstructionReadPlatformService standingInstructionReadPlatformService(JdbcTemplate jdbcTemplate,
|
ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
|
||||||
ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService,
|
return new StandingInstructionHistoryReadPlatformServiceImpl(jdbcTemplate, columnValidator, sqlGenerator, paginationHelper);
|
||||||
PortfolioAccountReadPlatformService portfolioAccountReadPlatformService,
|
}
|
||||||
DropdownReadPlatformService dropdownReadPlatformService, ColumnValidator columnValidator,
|
|
||||||
DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
|
@Bean
|
||||||
return new StandingInstructionReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService,
|
@ConditionalOnMissingBean(StandingInstructionReadPlatformService.class)
|
||||||
portfolioAccountReadPlatformService, dropdownReadPlatformService, columnValidator, sqlGenerator, paginationHelper);
|
public StandingInstructionReadPlatformService standingInstructionReadPlatformService(JdbcTemplate jdbcTemplate,
|
||||||
}
|
ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService,
|
||||||
|
PortfolioAccountReadPlatformService portfolioAccountReadPlatformService,
|
||||||
@Bean
|
DropdownReadPlatformService dropdownReadPlatformService, ColumnValidator columnValidator,
|
||||||
@ConditionalOnMissingBean(StandingInstructionWritePlatformService.class)
|
DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
|
||||||
public StandingInstructionWritePlatformService standingInstructionWritePlatformService(
|
return new StandingInstructionReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService,
|
||||||
StandingInstructionDataValidator standingInstructionDataValidator, StandingInstructionAssembler standingInstructionAssembler,
|
portfolioAccountReadPlatformService, dropdownReadPlatformService, columnValidator, sqlGenerator, paginationHelper);
|
||||||
AccountTransferDetailRepository accountTransferDetailRepository, StandingInstructionRepository standingInstructionRepository) {
|
}
|
||||||
return new StandingInstructionWritePlatformServiceImpl(standingInstructionDataValidator, standingInstructionAssembler,
|
|
||||||
accountTransferDetailRepository, standingInstructionRepository);
|
@Bean
|
||||||
}
|
@ConditionalOnMissingBean(StandingInstructionWritePlatformService.class)
|
||||||
}
|
public StandingInstructionWritePlatformService standingInstructionWritePlatformService(
|
||||||
|
StandingInstructionDataValidator standingInstructionDataValidator, StandingInstructionAssembler standingInstructionAssembler,
|
||||||
|
AccountTransferDetailRepository accountTransferDetailRepository, StandingInstructionRepository standingInstructionRepository) {
|
||||||
|
return new StandingInstructionWritePlatformServiceImpl(standingInstructionDataValidator, standingInstructionAssembler,
|
||||||
|
accountTransferDetailRepository, standingInstructionRepository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+30
-1
@@ -19,6 +19,7 @@
|
|||||||
package org.apache.fineract.portfolio.accountdetails.data;
|
package org.apache.fineract.portfolio.accountdetails.data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
|
||||||
|
|
||||||
@@ -44,12 +45,28 @@ public class GuarantorAccountSummaryData {
|
|||||||
private final Boolean isActive;
|
private final Boolean isActive;
|
||||||
private final String relationship;
|
private final String relationship;
|
||||||
private final BigDecimal onHoldAmount;
|
private final BigDecimal onHoldAmount;
|
||||||
|
private final String savingsAccountNo;
|
||||||
|
private final BigDecimal amountReleased;
|
||||||
|
private final String borrowerName;
|
||||||
|
private final BigDecimal totalOverdue;
|
||||||
|
private final LocalDate overdueSinceDate;
|
||||||
|
private final LocalDate submittedOnDate;
|
||||||
|
private final LocalDate approvedOnDate;
|
||||||
|
private final LocalDate disbursedOnDate;
|
||||||
|
private final LocalDate expectedMaturityDate;
|
||||||
|
private final BigDecimal amountRemaining;
|
||||||
|
private final BigDecimal savingsBalance;
|
||||||
|
private final Integer missedInstallments;
|
||||||
|
|
||||||
public GuarantorAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId,
|
public GuarantorAccountSummaryData(final Long id, final String accountNo, final String externalId, final Long productId,
|
||||||
final String loanProductName, final String shortLoanProductName, final LoanStatusEnumData loanStatus,
|
final String loanProductName, final String shortLoanProductName, final LoanStatusEnumData loanStatus,
|
||||||
final EnumOptionData loanType, final Integer loanCycle, final Boolean inArrears, final BigDecimal originalLoan,
|
final EnumOptionData loanType, final Integer loanCycle, final Boolean inArrears, final BigDecimal originalLoan,
|
||||||
final BigDecimal loanBalance, final BigDecimal amountPaid, final Boolean isActive, final String relationship,
|
final BigDecimal loanBalance, final BigDecimal amountPaid, final Boolean isActive, final String relationship,
|
||||||
final BigDecimal onHoldAmount) {
|
final BigDecimal onHoldAmount, final String savingsAccountNo, final BigDecimal amountReleased,
|
||||||
|
final String borrowerName, final BigDecimal totalOverdue, final LocalDate overdueSinceDate,
|
||||||
|
final LocalDate submittedOnDate, final LocalDate approvedOnDate, final LocalDate disbursedOnDate,
|
||||||
|
final LocalDate expectedMaturityDate, final BigDecimal amountRemaining, final BigDecimal savingsBalance,
|
||||||
|
final Integer missedInstallments) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.accountNo = accountNo;
|
this.accountNo = accountNo;
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
@@ -66,5 +83,17 @@ public class GuarantorAccountSummaryData {
|
|||||||
this.isActive = isActive;
|
this.isActive = isActive;
|
||||||
this.relationship = relationship;
|
this.relationship = relationship;
|
||||||
this.onHoldAmount = onHoldAmount;
|
this.onHoldAmount = onHoldAmount;
|
||||||
|
this.savingsAccountNo = savingsAccountNo;
|
||||||
|
this.amountReleased = amountReleased;
|
||||||
|
this.borrowerName = borrowerName;
|
||||||
|
this.totalOverdue = totalOverdue;
|
||||||
|
this.overdueSinceDate = overdueSinceDate;
|
||||||
|
this.submittedOnDate = submittedOnDate;
|
||||||
|
this.approvedOnDate = approvedOnDate;
|
||||||
|
this.disbursedOnDate = disbursedOnDate;
|
||||||
|
this.expectedMaturityDate = expectedMaturityDate;
|
||||||
|
this.amountRemaining = amountRemaining;
|
||||||
|
this.savingsBalance = savingsBalance;
|
||||||
|
this.missedInstallments = missedInstallments;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+812
-785
File diff suppressed because it is too large
Load Diff
+298
@@ -0,0 +1,298 @@
|
|||||||
|
package org.apache.fineract.portfolio.cashexchange.api;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.cashreport.service.CashierRoleHelper;
|
||||||
|
import org.apache.fineract.portfolio.cashexchange.domain.CashExchange;
|
||||||
|
import org.apache.fineract.portfolio.denominationmaster.service.DenominationBreakdownWriter;
|
||||||
|
import org.apache.fineract.portfolio.cashexchange.domain.CashExchangeDetail;
|
||||||
|
import org.apache.fineract.portfolio.cashexchange.domain.CashExchangeDetailRepository;
|
||||||
|
import org.apache.fineract.portfolio.cashexchange.domain.CashExchangeRepository;
|
||||||
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Path("/v1/cashexchange")
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public class CashExchangeApiResource {
|
||||||
|
|
||||||
|
private final CashExchangeRepository repository;
|
||||||
|
private final CashExchangeDetailRepository detailRepository;
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
private final DenominationBreakdownWriter denominationBreakdownWriter;
|
||||||
|
private final CashierRoleHelper cashierRoleHelper;
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Transactional
|
||||||
|
public String create(String body) {
|
||||||
|
JsonObject json = JsonParser.parseString(body).getAsJsonObject();
|
||||||
|
|
||||||
|
final Long accountId = json.has("accountId") && !json.get("accountId").isJsonNull() ? json.get("accountId").getAsLong() : null;
|
||||||
|
final String accountNo = json.has("accountNo") && !json.get("accountNo").isJsonNull() ? json.get("accountNo").getAsString()
|
||||||
|
: null;
|
||||||
|
final Long clientId = json.has("clientId") && !json.get("clientId").isJsonNull() ? json.get("clientId").getAsLong() : null;
|
||||||
|
final LocalDate exchangeDate = json.has("exchangeDate") && !json.get("exchangeDate").isJsonNull()
|
||||||
|
? LocalDate.parse(json.get("exchangeDate").getAsString())
|
||||||
|
: null;
|
||||||
|
final String note = json.has("note") && !json.get("note").isJsonNull() ? json.get("note").getAsString() : null;
|
||||||
|
final String customerIdProof = json.has("customerIdProof") && !json.get("customerIdProof").isJsonNull()
|
||||||
|
? json.get("customerIdProof").getAsString() : null;
|
||||||
|
|
||||||
|
final JsonArray received = json.has("received") && json.get("received").isJsonArray() ? json.getAsJsonArray("received")
|
||||||
|
: new JsonArray();
|
||||||
|
final JsonArray given = json.has("given") && json.get("given").isJsonArray() ? json.getAsJsonArray("given") : new JsonArray();
|
||||||
|
|
||||||
|
final BigDecimal receivedTotal = sumTotal(received);
|
||||||
|
final BigDecimal givenTotal = sumTotal(given);
|
||||||
|
|
||||||
|
if (!hasPositiveCount(received) || !hasPositiveCount(given)) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.cashexchange.empty",
|
||||||
|
"Both received and given denominations are required.");
|
||||||
|
}
|
||||||
|
if (receivedTotal.compareTo(givenTotal) != 0) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.cashexchange.mismatch",
|
||||||
|
"Received total (" + receivedTotal + ") must equal given total (" + givenTotal + ").");
|
||||||
|
}
|
||||||
|
if (receivedTotal.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.cashexchange.empty",
|
||||||
|
"Both received and given denominations are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CashExchange header = new CashExchange();
|
||||||
|
header.setAccountId(accountId);
|
||||||
|
header.setAccountNo(accountNo);
|
||||||
|
header.setClientId(clientId);
|
||||||
|
header.setExchangeAmount(receivedTotal);
|
||||||
|
header.setExchangeDate(exchangeDate);
|
||||||
|
header.setNote(note);
|
||||||
|
header.setCustomerIdProof(customerIdProof);
|
||||||
|
header.setIsReversed(false);
|
||||||
|
header.setCreatedAt(LocalDateTime.now());
|
||||||
|
try {
|
||||||
|
AppUser user = this.context.authenticatedUser();
|
||||||
|
if (user != null) {
|
||||||
|
header.setPerformedBy(user.getId());
|
||||||
|
header.setPerformedByUsername(user.getUsername());
|
||||||
|
// Auto-detect cashier linked to this user
|
||||||
|
try {
|
||||||
|
Long cashierId = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT mc.id FROM m_cashiers mc JOIN m_staff ms ON ms.id = mc.staff_id "
|
||||||
|
+ "JOIN m_appuser au ON au.staff_id = ms.id WHERE au.id = ?",
|
||||||
|
Long.class, user.getId());
|
||||||
|
header.setCashierId(cashierId);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// resolving the current user must never block recording the exchange
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that cashier has enough of each denomination being GIVEN to the customer
|
||||||
|
if (header.getCashierId() != null) {
|
||||||
|
final java.util.Map<Long, Long> stock = denominationBreakdownWriter.getCashierNetPosition(header.getCashierId());
|
||||||
|
final java.util.List<String> shortages = new java.util.ArrayList<>();
|
||||||
|
for (JsonElement el : given) {
|
||||||
|
JsonObject item = el.getAsJsonObject();
|
||||||
|
int count = item.has("count") && !item.get("count").isJsonNull() ? item.get("count").getAsInt() : 0;
|
||||||
|
if (count <= 0) continue;
|
||||||
|
if (!item.has("denominationId") || item.get("denominationId").isJsonNull()) continue;
|
||||||
|
long denomId = item.get("denominationId").getAsLong();
|
||||||
|
long available = stock.getOrDefault(denomId, 0L);
|
||||||
|
if (count > available) {
|
||||||
|
int denomValue = item.has("denominationValue") && !item.get("denominationValue").isJsonNull()
|
||||||
|
? item.get("denominationValue").getAsInt() : 0;
|
||||||
|
shortages.add("₹" + denomValue + " (need " + count + ", available " + available + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!shortages.isEmpty()) {
|
||||||
|
throw new PlatformDataIntegrityException(
|
||||||
|
"error.msg.cashier.denomination.insufficient",
|
||||||
|
"Insufficient denomination stock for exchange — cannot give: " + String.join("; ", shortages));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CashExchange saved = repository.saveAndFlush(header);
|
||||||
|
|
||||||
|
saveDetails(saved.getId(), "RECEIVED", received);
|
||||||
|
saveDetails(saved.getId(), "GIVEN", given);
|
||||||
|
|
||||||
|
JsonObject result = new JsonObject();
|
||||||
|
result.addProperty("resourceId", saved.getId());
|
||||||
|
return gson.toJson(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{id}/reverse")
|
||||||
|
public String reverse(@PathParam("id") Long id, String body) {
|
||||||
|
CashExchange exchange = repository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Cash exchange not found: " + id));
|
||||||
|
if (Boolean.TRUE.equals(exchange.getIsReversed())) {
|
||||||
|
throw new PlatformDataIntegrityException("error.msg.cashexchange.already.reversed",
|
||||||
|
"Cash exchange " + id + " is already reversed.");
|
||||||
|
}
|
||||||
|
String reason = null;
|
||||||
|
try {
|
||||||
|
JsonObject json = JsonParser.parseString(body).getAsJsonObject();
|
||||||
|
reason = json.has("reason") && !json.get("reason").isJsonNull() ? json.get("reason").getAsString() : null;
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
exchange.setIsReversed(true);
|
||||||
|
exchange.setReversedAt(LocalDateTime.now());
|
||||||
|
exchange.setReverseReason(reason);
|
||||||
|
try {
|
||||||
|
AppUser user = this.context.authenticatedUser();
|
||||||
|
if (user != null) exchange.setReversedBy(user.getId());
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
repository.save(exchange);
|
||||||
|
JsonObject result = new JsonObject();
|
||||||
|
result.addProperty("resourceId", id);
|
||||||
|
result.addProperty("reversed", true);
|
||||||
|
return gson.toJson(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String getAll(
|
||||||
|
@QueryParam("accountId") Long accountId,
|
||||||
|
@QueryParam("offset") @jakarta.ws.rs.DefaultValue("0") int offset,
|
||||||
|
@QueryParam("limit") @jakarta.ws.rs.DefaultValue("20") int limit) {
|
||||||
|
final Long scopedCashierId = cashierRoleHelper.getCashierIdForFiltering();
|
||||||
|
List<CashExchange> all = accountId != null
|
||||||
|
? repository.findByAccountIdOrderByCreatedAtDesc(accountId)
|
||||||
|
: repository.findAllByOrderByCreatedAtDesc();
|
||||||
|
if (scopedCashierId != null) {
|
||||||
|
all = all.stream()
|
||||||
|
.filter(e -> scopedCashierId.equals(e.getCashierId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
final int total = all.size();
|
||||||
|
final int safeOffset = Math.max(0, Math.min(offset, total));
|
||||||
|
final int safeTo = Math.min(safeOffset + Math.max(1, limit), total);
|
||||||
|
final List<Map<String, Object>> page = all.subList(safeOffset, safeTo)
|
||||||
|
.stream().map(this::toMap).collect(Collectors.toList());
|
||||||
|
final Map<String, Object> resp = new LinkedHashMap<>();
|
||||||
|
resp.put("totalFilteredRecords", total);
|
||||||
|
resp.put("offset", safeOffset);
|
||||||
|
resp.put("limit", limit);
|
||||||
|
resp.put("pageItems", page);
|
||||||
|
return gson.toJson(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{id}")
|
||||||
|
public String getOne(@PathParam("id") Long id) {
|
||||||
|
CashExchange header = repository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Cash exchange not found: " + id));
|
||||||
|
Map<String, Object> map = toMap(header);
|
||||||
|
List<Map<String, Object>> details = detailRepository.findByExchangeId(id).stream().map(this::detailToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
map.put("details", details);
|
||||||
|
return gson.toJson(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal sumTotal(JsonArray items) {
|
||||||
|
BigDecimal total = BigDecimal.ZERO;
|
||||||
|
for (JsonElement el : items) {
|
||||||
|
JsonObject item = el.getAsJsonObject();
|
||||||
|
int value = item.has("denominationValue") && !item.get("denominationValue").isJsonNull()
|
||||||
|
? item.get("denominationValue").getAsInt()
|
||||||
|
: 0;
|
||||||
|
int count = item.has("count") && !item.get("count").isJsonNull() ? item.get("count").getAsInt() : 0;
|
||||||
|
if (count > 0) {
|
||||||
|
total = total.add(BigDecimal.valueOf((long) value * count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPositiveCount(JsonArray items) {
|
||||||
|
for (JsonElement el : items) {
|
||||||
|
JsonObject item = el.getAsJsonObject();
|
||||||
|
int count = item.has("count") && !item.get("count").isJsonNull() ? item.get("count").getAsInt() : 0;
|
||||||
|
if (count > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveDetails(Long exchangeId, String direction, JsonArray items) {
|
||||||
|
for (JsonElement el : items) {
|
||||||
|
JsonObject item = el.getAsJsonObject();
|
||||||
|
int count = item.has("count") && !item.get("count").isJsonNull() ? item.get("count").getAsInt() : 0;
|
||||||
|
if (count <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int value = item.has("denominationValue") && !item.get("denominationValue").isJsonNull()
|
||||||
|
? item.get("denominationValue").getAsInt()
|
||||||
|
: 0;
|
||||||
|
Long denominationId = item.has("denominationId") && !item.get("denominationId").isJsonNull()
|
||||||
|
? item.get("denominationId").getAsLong()
|
||||||
|
: null;
|
||||||
|
CashExchangeDetail detail = new CashExchangeDetail();
|
||||||
|
detail.setExchangeId(exchangeId);
|
||||||
|
detail.setDirection(direction);
|
||||||
|
detail.setDenominationId(denominationId);
|
||||||
|
detail.setDenominationValue(value);
|
||||||
|
detail.setCount(count);
|
||||||
|
detail.setTotalAmount(BigDecimal.valueOf((long) value * count));
|
||||||
|
detailRepository.save(detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> toMap(CashExchange e) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", e.getId());
|
||||||
|
map.put("cashierId", e.getCashierId());
|
||||||
|
map.put("accountId", e.getAccountId());
|
||||||
|
map.put("accountNo", e.getAccountNo());
|
||||||
|
map.put("clientId", e.getClientId());
|
||||||
|
map.put("customerIdProof", e.getCustomerIdProof());
|
||||||
|
map.put("exchangeAmount", e.getExchangeAmount());
|
||||||
|
map.put("exchangeDate", e.getExchangeDate() != null ? e.getExchangeDate().toString() : null);
|
||||||
|
map.put("note", e.getNote());
|
||||||
|
map.put("performedBy", e.getPerformedBy());
|
||||||
|
map.put("performedByUsername", e.getPerformedByUsername());
|
||||||
|
map.put("createdAt", e.getCreatedAt() != null ? e.getCreatedAt().toString() : null);
|
||||||
|
map.put("isReversed", Boolean.TRUE.equals(e.getIsReversed()));
|
||||||
|
map.put("reversedBy", e.getReversedBy());
|
||||||
|
map.put("reversedAt", e.getReversedAt() != null ? e.getReversedAt().toString() : null);
|
||||||
|
map.put("reverseReason", e.getReverseReason());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> detailToMap(CashExchangeDetail d) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", d.getId());
|
||||||
|
map.put("direction", d.getDirection());
|
||||||
|
map.put("denominationId", d.getDenominationId());
|
||||||
|
map.put("denominationValue", d.getDenominationValue());
|
||||||
|
map.put("count", d.getCount());
|
||||||
|
map.put("totalAmount", d.getTotalAmount());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.cashexchange.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_cash_exchange")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CashExchange {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private Long accountId;
|
||||||
|
|
||||||
|
@Column(name = "account_no", length = 50)
|
||||||
|
private String accountNo;
|
||||||
|
|
||||||
|
@Column(name = "client_id")
|
||||||
|
private Long clientId;
|
||||||
|
|
||||||
|
@Column(name = "exchange_amount", nullable = false)
|
||||||
|
private BigDecimal exchangeAmount;
|
||||||
|
|
||||||
|
@Column(name = "exchange_date")
|
||||||
|
private LocalDate exchangeDate;
|
||||||
|
|
||||||
|
@Column(name = "note", length = 500)
|
||||||
|
private String note;
|
||||||
|
|
||||||
|
@Column(name = "cashier_id")
|
||||||
|
private Long cashierId;
|
||||||
|
|
||||||
|
@Column(name = "customer_id_proof", length = 50)
|
||||||
|
private String customerIdProof;
|
||||||
|
|
||||||
|
@Column(name = "performed_by")
|
||||||
|
private Long performedBy;
|
||||||
|
|
||||||
|
@Column(name = "performed_by_username", length = 100)
|
||||||
|
private String performedByUsername;
|
||||||
|
|
||||||
|
@Column(name = "created_at")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "is_reversed")
|
||||||
|
private Boolean isReversed;
|
||||||
|
|
||||||
|
@Column(name = "reversed_by")
|
||||||
|
private Long reversedBy;
|
||||||
|
|
||||||
|
@Column(name = "reversed_at")
|
||||||
|
private LocalDateTime reversedAt;
|
||||||
|
|
||||||
|
@Column(name = "reverse_reason", length = 500)
|
||||||
|
private String reverseReason;
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.cashexchange.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_cash_exchange_detail")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CashExchangeDetail {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "exchange_id", nullable = false)
|
||||||
|
private Long exchangeId;
|
||||||
|
|
||||||
|
@Column(name = "direction", nullable = false, length = 10)
|
||||||
|
private String direction;
|
||||||
|
|
||||||
|
@Column(name = "denomination_id")
|
||||||
|
private Long denominationId;
|
||||||
|
|
||||||
|
@Column(name = "denomination_value", nullable = false)
|
||||||
|
private Integer denominationValue;
|
||||||
|
|
||||||
|
@Column(name = "count", nullable = false)
|
||||||
|
private Integer count;
|
||||||
|
|
||||||
|
@Column(name = "total_amount", nullable = false)
|
||||||
|
private BigDecimal totalAmount;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user