Customer Self Service : APIs for Third Party Beneficiary management and Third Party Account Transfers

This commit is contained in:
Adi Narayana Raju
2016-05-04 13:01:25 +05:30
parent 9d4d90121a
commit 2bd3b0625a
32 changed files with 2620 additions and 504 deletions
+378 -1
View File
@@ -3312,6 +3312,38 @@
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td><a href="#selfbentemplate">Beneficiary for Third Party Transfer</a></td>
<td>/self/beneficiaries/tpt/template</td>
<td></td>
<td><a href="#selfbentemplate">Beneficiary TPT Template</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>/self/beneficiaries/tpt</td>
<td><a href="#selfaddtpt">Add TPT Beneficiary</a></td>
<td><a href="#selfgettpt">Get TPT Beneficiary List</a></td>
<td><a href="#selfputtpt">Update TPT Beneficiary</a></td>
<td><a href="#selfdeletetpt">Delete TPT Beneficiary</a></td>
</tr>
<tr>
<td><a href="#selftpttransfertemplate">Third Party Account Transfers</a></td>
<td>self/accounttransfers/template?type="tpt"</td>
<td></td>
<td><a href="#selftpttransfertemplate">Third Party Account Transfer Template</a></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>self/accounttransfers?type="tpt"</td>
<td><a href="#selftpttransfer">Third Party Account Transfer</a></td>
<td></td>
<td></td>
<td></td>
</tr>
</table> </table>
</div> </div>
</div> </div>
@@ -42193,7 +42225,35 @@ GET https://DomainName/api/v1/self/savingsaccounts/{accountId}/charges/{savingsA
</code> </code>
<code class="method-response"> <code class="method-response">
{ {
"accountOptions": [ "fromAccountOptions": [
{
"accountId": 1,
"accountNo": "00000001",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"clientId": 1,
"clientName": "ABC",
"officeId": 1,
"officeName": "HEAD OFFICE"
},
{
"accountId": 5,
"accountNo": "00000005",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"clientId": 2,
"clientName": "XYZ",
"officeId": 3,
"officeName": "REGIONAL OFFICE"
}
],
"toAccountOptions": [
{ {
"accountId": 1, "accountId": 1,
"accountNo": "00000001", "accountNo": "00000001",
@@ -42221,6 +42281,7 @@ GET https://DomainName/api/v1/self/savingsaccounts/{accountId}/charges/{savingsA
"officeName": "REGIONAL OFFICE" "officeName": "REGIONAL OFFICE"
} }
] ]
} }
</code> </code>
</div> </div>
@@ -42262,6 +42323,322 @@ No Request Body:
</div> </div>
</div> </div>
<a id="selfbentemplate" name="selfbentemplate" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Beneficiary Third Party Transfer Template</h2>
<p>Returns Account Type enumerations. Self User is expected to know office name and account number to be able to add beneficiary.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt/template</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://DomainName/api/v1/self/beneficiaries/tpt/template
</code>
<code class="method-response">
{
"accountTypeOptions":
[
{
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
{
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
}
]
}
</code>
</div>
</div>
<a id="selfaddtpt" name="selfaddtpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Add TPT Beneficiary</h2>
<p>Api to add third party beneficiary linked to current user.</p>
<p>Parameter Definitions</p>
<p>name : Nick name for beneficiary, should be unique for an self service user</p>
<p>officeName : Office Name of beneficiary(not id)</p>
<p>accountNumber : Account Number of beneficiary(not id)</p>
<p>transferLimit : Each transfer initiated to this account will not exceed this amount</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt</div>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="fineractHeading2">Mandatory Fields</div></td>
</tr>
<tr class=alt>
<td>name, officeName, accountNumber, accountType</td>
</tr>
</table>
<br/>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="fineractHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>transferLimit
</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">POST https://DomainName/api/v1/self/beneficiaries/tpt
</code>
<code class="method-request">
POST self/beneficiaries/tpt
Content-Type: application/json
Request Body:
{
"locale": "en_GB",
"name": "beneficiary nick name",
"officeName": "HEAD OFFICE",
"accountNumber": "0000001",
"accountType": 1,
"transferLimit": 1000
}
</code>
<code class="method-response">
{
"resourceId": 5
}
</code>
</div>
</div>
<a id="selfgettpt" name="selfgettpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Get All TPT Beneficiary</h2>
<p>Api to get all third party beneficiary linked to current user.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://DomainName/api/v1/self/beneficiaries/tpt
</code>
<code class="method-request">
GET self/beneficiaries/tpt
Content-Type: application/json
</code>
<code class="method-response">
{
[
{
"id": 1,
"name": "Client2Savings",
"officeName": "Test Office",
"clientName": "FN2 LN2",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"accountNumber": "000000002",
"transferLimit": 0
},
{
"id": 4,
"name": "Client2Loan",
"officeName": "Test Office",
"clientName": "FN2 LN2",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"accountNumber": "000000002",
"transferLimit": 1000
}]
}
</code>
</div>
</div>
<a id="selfputtpt" name="selfputtpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Update TPT Beneficiary</h2>
<p>Api to update third party beneficiary linked to current user.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt/{beneficiaryId}</div>
<br/>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="fineractHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>name, transferLimit
</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">PUT https://DomainName/api/v1/self/beneficiaries/tpt/{beneficiaryId}
</code>
<code class="method-request">
PUT self/beneficiaries/tpt/5
Content-Type: application/json
Request Body:
{
"name": "beneficiary nick name",
"transferLimit": 1000
}
</code>
<code class="method-response">
{
"resourceId": 5,
"changes": {
"transferLimit": 1000,
"name": "Client22"
}
}
</code>
</div>
</div>
<a id="selfdeletetpt" name="selfdeletetpt" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Delete TPT Beneficiary</h2>
<p>Api to delete third party beneficiary linked to current user.</p>
<p>Example Requests:</p>
<div class=apiClick>/self/beneficiaries/tpt/{beneficiaryId}</div>
</div>
<div class="method-example">
<code class="method-declaration">DELETE https://DomainName/api/v1/self/beneficiaries/tpt/{beneficiaryId}
</code>
<code class="method-request">
DELETE self/beneficiaries/tpt/5
Content-Type: application/json
</code>
<code class="method-response">
{
"resourceId": 5
}
</code>
</div>
</div>
<a id="selftpttransfertemplate" name="selftpttransfertemplate" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Third Party Account Transfer Template</h2>
<p>Returns list of loan/savings accounts that can be used for third party account transfer</p>
<p>Example Requests:</p>
<div class=apiClick>self/accounttransfers/template?type="tpt"</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://DomainName/api/v1/self/accounttransfers/template?type="tpt"
</code>
<code class="method-response">
{
"fromAccountOptions": [
{
"accountId": 1,
"accountNo": "00000001",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"clientId": 1,
"clientName": "ABC",
"officeId": 1,
"officeName": "HEAD OFFICE"
},
{
"accountId": 5,
"accountNo": "00000005",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"clientId": 2,
"clientName": "XYZ",
"officeId": 3,
"officeName": "REGIONAL OFFICE"
}
],
"toAccountOptions": [
{
"accountId": 2,
"accountNo": "00000002",
"accountType": {
"id": 2,
"code": "accountType.savings",
"value": "Savings Account"
},
"clientId": 2,
"clientName": "ABC",
"officeId": 1,
"officeName": "HEAD OFFICE"
},
{
"accountId": 6,
"accountNo": "00000006",
"accountType": {
"id": 1,
"code": "accountType.loan",
"value": "Loan Account"
},
"clientId": 3,
"clientName": "XYZ",
"officeId": 4,
"officeName": "REGIONAL OFFICE"
}
]
}
</code>
</div>
</div>
<a id="selftpttransfer" name="selftpttransfer" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h2>Third Party Account Transfer</h2>
<p>Ability to create new third party transfer of monetary funds from one account to another.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://DomainName/api/v1/self/accounttransfers?type="tpt"</code>
<code class="method-request">POST self/accounttransfers?type="tpt"
Content-Type: application/json
No Request Body:
{
"fromOfficeId": 1,
"fromClientId": 1,
"fromAccountType": 2,
"fromAccountId": 1,
"toOfficeId": 1,
"toClientId": 2,
"toAccountType": 2,
"toAccountId": 2,
"dateFormat": "dd MMMM yyyy",
"locale": "en",
"transferDate": "01 August 2011",
"transferAmount": "112.45",
"transferDescription": "A description of the transfer"
}
</code>
<code class="method-response">
{
"savingsId": 1,
"resourceId": 1
}
</code>
</div>
</div>
<!-- end of Customer Self Service APIs--> <!-- end of Customer Self Service APIs-->
</div> </div>
<!-- main-content-wrapper --> <!-- main-content-wrapper -->
@@ -2702,4 +2702,29 @@ public class CommandWrapperBuilder {
this.href = "/savingsaccounts/" + accountId + "?commad=updateTaxWithHoldTax"; this.href = "/savingsaccounts/" + accountId + "?commad=updateTaxWithHoldTax";
return this; return this;
} }
public CommandWrapperBuilder addSelfServiceBeneficiaryTPT() {
this.actionName = "CREATE";
this.entityName = "SSBENEFICIARYTPT";
this.entityId = null;
this.href = "/self/beneficiaries/tpt";
return this;
}
public CommandWrapperBuilder updateSelfServiceBeneficiaryTPT(final Long beneficiaryId) {
this.actionName = "UPDATE";
this.entityName = "SSBENEFICIARYTPT";
this.entityId = beneficiaryId;
this.href = "/self/beneficiaries/tpt/"+beneficiaryId;
return this;
}
public CommandWrapperBuilder deleteSelfServiceBeneficiaryTPT(final Long beneficiaryId) {
this.actionName = "DELETE";
this.entityName = "SSBENEFICIARYTPT";
this.entityId = beneficiaryId;
this.href = "/self/beneficiaries/tpt/"+beneficiaryId;
return this;
}
} }
@@ -79,4 +79,8 @@ public interface ConfigurationDomainService {
boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled(); boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
boolean isDailyTPTLimitEnabled();
Long getDailyTPTLimit();
} }
@@ -19,6 +19,7 @@
package org.apache.fineract.infrastructure.configuration.domain; package org.apache.fineract.infrastructure.configuration.domain;
import java.util.Date; import java.util.Date;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.cache.domain.CacheType; import org.apache.fineract.infrastructure.cache.domain.CacheType;
import org.apache.fineract.infrastructure.cache.domain.PlatformCache; import org.apache.fineract.infrastructure.cache.domain.PlatformCache;
@@ -209,6 +210,7 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
return defaultValue; return defaultValue;
} }
@Override
public boolean isBackdatePenaltiesEnabled() { public boolean isBackdatePenaltiesEnabled() {
final String propertyName = "backdate-penalties-enabled"; final String propertyName = "backdate-penalties-enabled";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName); final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
@@ -263,5 +265,19 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
return property.isEnabled(); return property.isEnabled();
} }
@Override
public boolean isDailyTPTLimitEnabled() {
final String propertyName = "daily-tpt-limit";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
return property.isEnabled();
}
@Override
public Long getDailyTPTLimit() {
final String propertyName = "daily-tpt-limit";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
return property.getValue();
}
} }
@@ -74,7 +74,7 @@ public class TenantDatabaseUpgradeService {
flyway.migrate(); flyway.migrate();
} catch (FlywayException e) { } catch (FlywayException e) {
String betterMessage = e.getMessage() + "; for Tenant DB URL: " + connectionProtocol + ", username: " String betterMessage = e.getMessage() + "; for Tenant DB URL: " + connectionProtocol + ", username: "
+ connection.getSchemaPassword(); + connection.getSchemaUsername();
throw new FlywayException(betterMessage, e.getCause()); throw new FlywayException(betterMessage, e.getCause());
} }
} }
@@ -136,11 +136,11 @@ public class SchedularWritePlatformServiceJpaRepositoryImpl implements Schedular
boolean isStopExecution = false; boolean isStopExecution = false;
final ScheduledJobDetail scheduledJobDetail = this.scheduledJobDetailsRepository.findByJobKeyWithLock(jobKey); final ScheduledJobDetail scheduledJobDetail = this.scheduledJobDetailsRepository.findByJobKeyWithLock(jobKey);
if (scheduledJobDetail.isCurrentlyRunning() if (scheduledJobDetail.isCurrentlyRunning()
|| (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON && (scheduledJobDetail.getNextRunTime().after(new Date())))) { || (triggerType.equals(SchedulerServiceConstants.TRIGGER_TYPE_CRON) && (scheduledJobDetail.getNextRunTime().after(new Date())))) {
isStopExecution = true; isStopExecution = true;
} }
final SchedulerDetail schedulerDetail = retriveSchedulerDetail(); final SchedulerDetail schedulerDetail = retriveSchedulerDetail();
if (triggerType == SchedulerServiceConstants.TRIGGER_TYPE_CRON && schedulerDetail.isSuspended()) { if (triggerType.equals(SchedulerServiceConstants.TRIGGER_TYPE_CRON) && schedulerDetail.isSuspended()) {
scheduledJobDetail.updateTriggerMisfired(true); scheduledJobDetail.updateTriggerMisfired(true);
isStopExecution = true; isStopExecution = true;
} else if (!isStopExecution) { } else if (!isStopExecution) {
@@ -18,12 +18,14 @@
*/ */
package org.apache.fineract.portfolio.account.service; package org.apache.fineract.portfolio.account.service;
import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
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.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.AccountTransferData; import org.apache.fineract.portfolio.account.data.AccountTransferData;
import org.joda.time.LocalDate;
public interface AccountTransfersReadPlatformService { public interface AccountTransfersReadPlatformService {
@@ -42,4 +44,7 @@ public interface AccountTransfersReadPlatformService {
AccountTransferData retrieveRefundByTransferTemplate(Long fromOfficeId, Long fromClientId, Long fromAccountId, Integer fromAccountType, AccountTransferData retrieveRefundByTransferTemplate(Long fromOfficeId, Long fromClientId, Long fromAccountId, Integer fromAccountType,
Long toOfficeId, Long toClientId, Long toAccountId, Integer toAccountType); Long toOfficeId, Long toClientId, Long toAccountId, Integer toAccountType);
BigDecimal getTotalTransactionAmount(Long accountId, Integer accountType,
LocalDate transactionDate);
} }
@@ -45,6 +45,8 @@ import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundEx
import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@@ -53,7 +55,8 @@ import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@Service @Service
public class AccountTransfersReadPlatformServiceImpl implements AccountTransfersReadPlatformService { public class AccountTransfersReadPlatformServiceImpl implements
AccountTransfersReadPlatformService {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final ClientReadPlatformService clientReadPlatformService; private final ClientReadPlatformService clientReadPlatformService;
@@ -65,10 +68,14 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
// pagination // pagination
private final PaginationHelper<AccountTransferData> paginationHelper = new PaginationHelper<>(); private final PaginationHelper<AccountTransferData> paginationHelper = new PaginationHelper<>();
private final DateTimeFormatter formatter = DateTimeFormat
.forPattern("yyyy-MM-dd");
@Autowired @Autowired
public AccountTransfersReadPlatformServiceImpl(final RoutingDataSource dataSource, public AccountTransfersReadPlatformServiceImpl(
final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, final RoutingDataSource dataSource,
final ClientReadPlatformService clientReadPlatformService,
final OfficeReadPlatformService officeReadPlatformService,
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService) { final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService) {
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
this.clientReadPlatformService = clientReadPlatformService; this.clientReadPlatformService = clientReadPlatformService;
@@ -79,25 +86,35 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
} }
@Override @Override
public AccountTransferData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, public AccountTransferData retrieveTemplate(final Long fromOfficeId,
final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, final Integer toAccountType) { final Long fromClientId, final Long fromAccountId,
final Integer fromAccountType, final Long toOfficeId,
final Long toClientId, final Long toAccountId,
final Integer toAccountType) {
final EnumOptionData loanAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); final EnumOptionData loanAccountType = AccountTransferEnumerations
final EnumOptionData savingsAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); .accountType(PortfolioAccountType.LOAN);
final EnumOptionData savingsAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.SAVINGS);
final Integer mostRelevantFromAccountType = fromAccountType; final Integer mostRelevantFromAccountType = fromAccountType;
final Collection<EnumOptionData> fromAccountTypeOptions = Arrays.asList(savingsAccountType, loanAccountType); final Collection<EnumOptionData> fromAccountTypeOptions = Arrays
.asList(savingsAccountType, loanAccountType);
final Collection<EnumOptionData> toAccountTypeOptions; final Collection<EnumOptionData> toAccountTypeOptions;
if (mostRelevantFromAccountType != null && mostRelevantFromAccountType == 1) { if (mostRelevantFromAccountType != null
&& mostRelevantFromAccountType == 1) {
// overpaid loan amt transfer to savings account // overpaid loan amt transfer to savings account
toAccountTypeOptions = Arrays.asList(savingsAccountType); toAccountTypeOptions = Arrays.asList(savingsAccountType);
} else { } else {
toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType); toAccountTypeOptions = Arrays.asList(loanAccountType,
savingsAccountType);
} }
final Integer mostRelevantToAccountType = toAccountType; final Integer mostRelevantToAccountType = toAccountType;
final EnumOptionData fromAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantFromAccountType); final EnumOptionData fromAccountTypeData = AccountTransferEnumerations
final EnumOptionData toAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantToAccountType); .accountType(mostRelevantFromAccountType);
final EnumOptionData toAccountTypeData = AccountTransferEnumerations
.accountType(mostRelevantToAccountType);
// from settings // from settings
OfficeData fromOffice = null; OfficeData fromOffice = null;
@@ -125,30 +142,37 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
} else { } else {
accountType = PortfolioAccountType.SAVINGS.getValue(); accountType = PortfolioAccountType.SAVINGS.getValue();
} }
fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, accountType); fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(
fromAccountId, accountType);
// override provided fromClient with client of account // override provided fromClient with client of account
mostRelevantFromClientId = fromAccount.clientId(); mostRelevantFromClientId = fromAccount.clientId();
} }
if (mostRelevantFromClientId != null) { if (mostRelevantFromClientId != null) {
fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); fromClient = this.clientReadPlatformService
.retrieveOne(mostRelevantFromClientId);
mostRelevantFromOfficeId = fromClient.officeId(); mostRelevantFromOfficeId = fromClient.officeId();
long[] loanStatus = null; long[] loanStatus = null;
if (mostRelevantFromAccountType == 1) { if (mostRelevantFromAccountType == 1) {
loanStatus = new long[] { 300, 700 }; loanStatus = new long[] { 300, 700 };
} }
PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(mostRelevantFromAccountType, mostRelevantFromClientId, PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(
mostRelevantFromAccountType, mostRelevantFromClientId,
loanStatus); loanStatus);
fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(portfolioAccountDTO); fromAccountOptions = this.portfolioAccountReadPlatformService
.retrieveAllForLookup(portfolioAccountDTO);
} }
Collection<OfficeData> fromOfficeOptions = null; Collection<OfficeData> fromOfficeOptions = null;
Collection<ClientData> fromClientOptions = null; Collection<ClientData> fromClientOptions = null;
if (mostRelevantFromOfficeId != null) { if (mostRelevantFromOfficeId != null) {
fromOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantFromOfficeId); fromOffice = this.officeReadPlatformService
fromOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); .retrieveOffice(mostRelevantFromOfficeId);
fromClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId); fromOfficeOptions = this.officeReadPlatformService
.retrieveAllOfficesForDropdown();
fromClientOptions = this.clientReadPlatformService
.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId);
} }
// defaults // defaults
@@ -157,43 +181,57 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
Collection<ClientData> toClientOptions = null; Collection<ClientData> toClientOptions = null;
if (toAccountId != null && fromAccount != null) { if (toAccountId != null && fromAccount != null) {
toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, toAccount = this.portfolioAccountReadPlatformService.retrieveOne(
toAccountId, mostRelevantToAccountType,
fromAccount.currencyCode()); fromAccount.currencyCode());
mostRelevantToClientId = toAccount.clientId(); mostRelevantToClientId = toAccount.clientId();
} }
if (mostRelevantToClientId != null) { if (mostRelevantToClientId != null) {
toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); toClient = this.clientReadPlatformService
.retrieveOne(mostRelevantToClientId);
mostRelevantToOfficeId = toClient.officeId(); mostRelevantToOfficeId = toClient.officeId();
toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); toClientOptions = this.clientReadPlatformService
.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId);
toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); toAccountOptions = retrieveToAccounts(fromAccount,
mostRelevantToAccountType, mostRelevantToClientId);
} }
if (mostRelevantToOfficeId != null) { if (mostRelevantToOfficeId != null) {
toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId); toOffice = this.officeReadPlatformService
toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); .retrieveOffice(mostRelevantToOfficeId);
toOfficeOptions = this.officeReadPlatformService
.retrieveAllOfficesForDropdown();
toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); toClientOptions = this.clientReadPlatformService
.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId);
if (toClientOptions != null && toClientOptions.size() == 1) { if (toClientOptions != null && toClientOptions.size() == 1) {
toClient = new ArrayList<>(toClientOptions).get(0); toClient = new ArrayList<>(toClientOptions).get(0);
toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); toAccountOptions = retrieveToAccounts(fromAccount,
mostRelevantToAccountType, mostRelevantToClientId);
} }
} }
return AccountTransferData.template(fromOffice, fromClient, fromAccountTypeData, fromAccount, transferDate, toOffice, toClient, return AccountTransferData.template(fromOffice, fromClient,
toAccountTypeData, toAccount, fromOfficeOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions, fromAccountTypeData, fromAccount, transferDate, toOffice,
toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions); toClient, toAccountTypeData, toAccount, fromOfficeOptions,
fromClientOptions, fromAccountTypeOptions, fromAccountOptions,
toOfficeOptions, toClientOptions, toAccountTypeOptions,
toAccountOptions);
} }
private Collection<PortfolioAccountData> retrieveToAccounts(final PortfolioAccountData excludeThisAccountFromOptions, private Collection<PortfolioAccountData> retrieveToAccounts(
final PortfolioAccountData excludeThisAccountFromOptions,
final Integer toAccountType, final Long toClientId) { final Integer toAccountType, final Long toClientId) {
final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions.currencyCode() : null; final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions
.currencyCode() : null;
PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(toAccountType, toClientId, currencyCode, null, null); PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(
toAccountType, toClientId, currencyCode, null, null);
Collection<PortfolioAccountData> accountOptions = this.portfolioAccountReadPlatformService Collection<PortfolioAccountData> accountOptions = this.portfolioAccountReadPlatformService
.retrieveAllForLookup(portfolioAccountDTO); .retrieveAllForLookup(portfolioAccountDTO);
if (!CollectionUtils.isEmpty(accountOptions)) { if (!CollectionUtils.isEmpty(accountOptions)) {
@@ -206,7 +244,8 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
} }
@Override @Override
public Page<AccountTransferData> retrieveAll(final SearchParameters searchParameters, final Long accountDetailId) { public Page<AccountTransferData> retrieveAll(
final SearchParameters searchParameters, final Long accountDetailId) {
final StringBuilder sqlBuilder = new StringBuilder(200); final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
@@ -218,7 +257,8 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
} }
if (searchParameters.isOrderByRequested()) { if (searchParameters.isOrderByRequested()) {
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); sqlBuilder.append(" order by ").append(
searchParameters.getOrderBy());
if (searchParameters.isSortOrderProvided()) { if (searchParameters.isSortOrderProvided()) {
sqlBuilder.append(' ').append(searchParameters.getSortOrder()); sqlBuilder.append(' ').append(searchParameters.getSortOrder());
@@ -228,12 +268,14 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
if (searchParameters.isLimited()) { if (searchParameters.isLimited()) {
sqlBuilder.append(" limit ").append(searchParameters.getLimit()); sqlBuilder.append(" limit ").append(searchParameters.getLimit());
if (searchParameters.isOffset()) { if (searchParameters.isOffset()) {
sqlBuilder.append(" offset ").append(searchParameters.getOffset()); sqlBuilder.append(" offset ").append(
searchParameters.getOffset());
} }
} }
final String sqlCountRows = "SELECT FOUND_ROWS()"; final String sqlCountRows = "SELECT FOUND_ROWS()";
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows,
sqlBuilder.toString(), finalObjectArray,
this.accountTransfersMapper); this.accountTransfersMapper);
} }
@@ -241,9 +283,11 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
public AccountTransferData retrieveOne(final Long transferId) { public AccountTransferData retrieveOne(final Long transferId) {
try { try {
final String sql = "select " + this.accountTransfersMapper.schema() + " where att.id = ?"; final String sql = "select " + this.accountTransfersMapper.schema()
+ " where att.id = ?";
return this.jdbcTemplate.queryForObject(sql, this.accountTransfersMapper, new Object[] { transferId }); return this.jdbcTemplate.queryForObject(sql,
this.accountTransfersMapper, new Object[] { transferId });
} catch (final EmptyResultDataAccessException e) { } catch (final EmptyResultDataAccessException e) {
throw new AccountTransferNotFoundException(transferId); throw new AccountTransferNotFoundException(transferId);
} }
@@ -253,52 +297,82 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
public Collection<Long> fetchPostInterestTransactionIds(final Long accountId) { public Collection<Long> fetchPostInterestTransactionIds(final Long accountId) {
final String sql = "select att.from_savings_transaction_id from m_account_transfer_transaction att inner join m_account_transfer_details atd on atd.id = att.account_transfer_details_id where atd.from_savings_account_id=? and att.is_reversed =0 and atd.transfer_type = ?"; final String sql = "select att.from_savings_transaction_id from m_account_transfer_transaction att inner join m_account_transfer_details atd on atd.id = att.account_transfer_details_id where atd.from_savings_account_id=? and att.is_reversed =0 and atd.transfer_type = ?";
final List<Long> transactionId = this.jdbcTemplate.queryForList(sql, Long.class, accountId, final List<Long> transactionId = this.jdbcTemplate.queryForList(sql,
Long.class, accountId,
AccountTransferType.INTEREST_TRANSFER.getValue()); AccountTransferType.INTEREST_TRANSFER.getValue());
return transactionId; return transactionId;
} }
private static final class AccountTransfersMapper implements RowMapper<AccountTransferData> { private static final class AccountTransfersMapper implements
RowMapper<AccountTransferData> {
private final String schemaSql; private final String schemaSql;
public AccountTransfersMapper() { public AccountTransfersMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400); final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("att.id as id, att.is_reversed as isReversed,"); sqlBuilder.append("att.id as id, att.is_reversed as isReversed,");
sqlBuilder.append("att.transaction_date as transferDate, att.amount as transferAmount,"); sqlBuilder
.append("att.transaction_date as transferDate, att.amount as transferAmount,");
sqlBuilder.append("att.description as transferDescription,"); sqlBuilder.append("att.description as transferDescription,");
sqlBuilder.append("att.currency_code as currencyCode, att.currency_digits as currencyDigits,"); sqlBuilder
.append("att.currency_code as currencyCode, att.currency_digits as currencyDigits,");
sqlBuilder.append("att.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("att.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder
.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); sqlBuilder
sqlBuilder.append("tooff.id as toOfficeId, tooff.name as toOfficeName,"); .append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,");
sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); sqlBuilder
sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); .append("tooff.id as toOfficeId, tooff.name as toOfficeName,");
sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); sqlBuilder
sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); .append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,");
sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); sqlBuilder
sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo,"); .append("toclient.id as toClientId, toclient.display_name as toClientName,");
sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); sqlBuilder
sqlBuilder.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,"); .append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,");
sqlBuilder
.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,");
sqlBuilder
.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,");
sqlBuilder
.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo,");
sqlBuilder
.append("fromsavtran.id as fromSavingsAccountTransactionId,");
sqlBuilder
.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,");
sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,"); sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,");
sqlBuilder.append("tosavtran.transaction_type_enum as toSavingsAccountTransactionType"); sqlBuilder
.append("tosavtran.transaction_type_enum as toSavingsAccountTransactionType");
sqlBuilder.append(" FROM m_account_transfer_transaction att "); sqlBuilder.append(" FROM m_account_transfer_transaction att ");
sqlBuilder.append("left join m_account_transfer_details atd on atd.id = att.account_transfer_details_id "); sqlBuilder
sqlBuilder.append("join m_currency curr on curr.code = att.currency_code "); .append("left join m_account_transfer_details atd on atd.id = att.account_transfer_details_id ");
sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id "); sqlBuilder
sqlBuilder.append("join m_office tooff on tooff.id = atd.to_office_id "); .append("join m_currency curr on curr.code = att.currency_code ");
sqlBuilder.append("join m_client fromclient on fromclient.id = atd.from_client_id "); sqlBuilder
sqlBuilder.append("join m_client toclient on toclient.id = atd.to_client_id "); .append("join m_office fromoff on fromoff.id = atd.from_office_id ");
sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id "); sqlBuilder
sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id "); .append("join m_office tooff on tooff.id = atd.to_office_id ");
sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id "); sqlBuilder
sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id "); .append("join m_client fromclient on fromclient.id = atd.from_client_id ");
sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = att.from_savings_transaction_id "); sqlBuilder
sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = att.to_savings_transaction_id "); .append("join m_client toclient on toclient.id = atd.to_client_id ");
sqlBuilder.append("left join m_loan_transaction fromloantran on fromloantran.id = att.from_savings_transaction_id "); sqlBuilder
sqlBuilder.append("left join m_loan_transaction toloantran on toloantran.id = att.to_savings_transaction_id "); .append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id ");
sqlBuilder
.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id ");
sqlBuilder
.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id ");
sqlBuilder
.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id ");
sqlBuilder
.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = att.from_savings_transaction_id ");
sqlBuilder
.append("left join m_savings_account_transaction tosavtran on tosavtran.id = att.to_savings_transaction_id ");
sqlBuilder
.append("left join m_loan_transaction fromloantran on fromloantran.id = att.from_savings_transaction_id ");
sqlBuilder
.append("left join m_loan_transaction toloantran on toloantran.id = att.to_savings_transaction_id ");
this.schemaSql = sqlBuilder.toString(); this.schemaSql = sqlBuilder.toString();
} }
@@ -308,81 +382,115 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
} }
@Override @Override
public AccountTransferData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { public AccountTransferData mapRow(final ResultSet rs,
@SuppressWarnings("unused") final int rowNum)
throws SQLException {
final Long id = rs.getLong("id"); final Long id = rs.getLong("id");
final boolean reversed = rs.getBoolean("isReversed"); final boolean reversed = rs.getBoolean("isReversed");
final LocalDate transferDate = JdbcSupport.getLocalDate(rs, "transferDate"); final LocalDate transferDate = JdbcSupport.getLocalDate(rs,
final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transferAmount"); "transferDate");
final String transferDescription = rs.getString("transferDescription"); final BigDecimal transferAmount = JdbcSupport
.getBigDecimalDefaultToZeroIfNull(rs, "transferAmount");
final String transferDescription = rs
.getString("transferDescription");
final String currencyCode = rs.getString("currencyCode"); final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName"); final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final String currencyDisplaySymbol = rs
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); .getString("currencyDisplaySymbol");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); final Integer currencyDigits = JdbcSupport.getInteger(rs,
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs,
"inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode,
currencyName, currencyDigits, inMultiplesOf,
currencyDisplaySymbol, currencyNameCode); currencyDisplaySymbol, currencyNameCode);
final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId");
final String fromOfficeName = rs.getString("fromOfficeName"); final String fromOfficeName = rs.getString("fromOfficeName");
final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId,
fromOfficeName, null);
final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId");
final String toOfficeName = rs.getString("toOfficeName"); final String toOfficeName = rs.getString("toOfficeName");
final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); final OfficeData toOffice = OfficeData.dropdown(toOfficeId,
toOfficeName, null);
final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId");
final String fromClientName = rs.getString("fromClientName"); final String fromClientName = rs.getString("fromClientName");
final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); final ClientData fromClient = ClientData.lookup(fromClientId,
fromClientName, fromOfficeId, fromOfficeName);
final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); final Long toClientId = JdbcSupport.getLong(rs, "toClientId");
final String toClientName = rs.getString("toClientName"); final String toClientName = rs.getString("toClientName");
final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); final ClientData toClient = ClientData.lookup(toClientId,
toClientName, toOfficeId, toOfficeName);
final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); final Long fromSavingsAccountId = JdbcSupport.getLong(rs,
final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); "fromSavingsAccountId");
final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); final String fromSavingsAccountNo = rs
.getString("fromSavingsAccountNo");
final Long fromLoanAccountId = JdbcSupport.getLong(rs,
"fromLoanAccountId");
final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); final String fromLoanAccountNo = rs.getString("fromLoanAccountNo");
PortfolioAccountData fromAccount = null; PortfolioAccountData fromAccount = null;
EnumOptionData fromAccountType = null; EnumOptionData fromAccountType = null;
if (fromSavingsAccountId != null) { if (fromSavingsAccountId != null) {
fromAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); fromAccount = PortfolioAccountData.lookup(fromSavingsAccountId,
fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); fromSavingsAccountNo);
fromAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.SAVINGS);
} else if (fromLoanAccountId != null) { } else if (fromLoanAccountId != null) {
fromAccount = PortfolioAccountData.lookup(fromLoanAccountId, fromLoanAccountNo); fromAccount = PortfolioAccountData.lookup(fromLoanAccountId,
fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); fromLoanAccountNo);
fromAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.LOAN);
} }
PortfolioAccountData toAccount = null; PortfolioAccountData toAccount = null;
EnumOptionData toAccountType = null; EnumOptionData toAccountType = null;
final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); final Long toSavingsAccountId = JdbcSupport.getLong(rs,
final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); "toSavingsAccountId");
final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId"); final String toSavingsAccountNo = rs
.getString("toSavingsAccountNo");
final Long toLoanAccountId = JdbcSupport.getLong(rs,
"toLoanAccountId");
final String toLoanAccountNo = rs.getString("toLoanAccountNo"); final String toLoanAccountNo = rs.getString("toLoanAccountNo");
if (toSavingsAccountId != null) { if (toSavingsAccountId != null) {
toAccount = PortfolioAccountData.lookup(toSavingsAccountId, toSavingsAccountNo); toAccount = PortfolioAccountData.lookup(toSavingsAccountId,
toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); toSavingsAccountNo);
toAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.SAVINGS);
} else if (toLoanAccountId != null) { } else if (toLoanAccountId != null) {
toAccount = PortfolioAccountData.lookup(toLoanAccountId, toLoanAccountNo); toAccount = PortfolioAccountData.lookup(toLoanAccountId,
toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); toLoanAccountNo);
toAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.LOAN);
} }
return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, return AccountTransferData.instance(id, reversed, transferDate,
toOffice, fromClient, toClient, fromAccountType, fromAccount, toAccountType, toAccount); currency, transferAmount, transferDescription, fromOffice,
toOffice, fromClient, toClient, fromAccountType,
fromAccount, toAccountType, toAccount);
} }
} }
@Override @Override
public boolean isAccountTransfer(final Long transactionId, final PortfolioAccountType accountType) { public boolean isAccountTransfer(final Long transactionId,
final StringBuilder sql = new StringBuilder("select count(*) from m_account_transfer_transaction at where "); final PortfolioAccountType accountType) {
final StringBuilder sql = new StringBuilder(
"select count(*) from m_account_transfer_transaction at where ");
if (accountType.isLoanAccount()) { if (accountType.isLoanAccount()) {
sql.append("at.from_loan_transaction_id=").append(transactionId).append(" or at.to_loan_transaction_id=").append(transactionId); sql.append("at.from_loan_transaction_id=").append(transactionId)
.append(" or at.to_loan_transaction_id=")
.append(transactionId);
} else { } else {
sql.append("at.from_savings_transaction_id=").append(transactionId).append(" or at.to_savings_transaction_id=") sql.append("at.from_savings_transaction_id=").append(transactionId)
.append(" or at.to_savings_transaction_id=")
.append(transactionId); .append(transactionId);
} }
@@ -392,7 +500,8 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
} }
@Override @Override
public Page<AccountTransferData> retrieveByStandingInstruction(final Long id, final SearchParameters searchParameters) { public Page<AccountTransferData> retrieveByStandingInstruction(
final Long id, final SearchParameters searchParameters) {
final StringBuilder sqlBuilder = new StringBuilder(200); final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
@@ -403,47 +512,61 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
if (searchParameters != null) { if (searchParameters != null) {
if (searchParameters.isOrderByRequested()) { if (searchParameters.isOrderByRequested()) {
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); sqlBuilder.append(" order by ").append(
searchParameters.getOrderBy());
if (searchParameters.isSortOrderProvided()) { if (searchParameters.isSortOrderProvided()) {
sqlBuilder.append(' ').append(searchParameters.getSortOrder()); sqlBuilder.append(' ').append(
searchParameters.getSortOrder());
} }
} }
if (searchParameters.isLimited()) { if (searchParameters.isLimited()) {
sqlBuilder.append(" limit ").append(searchParameters.getLimit()); sqlBuilder.append(" limit ")
.append(searchParameters.getLimit());
if (searchParameters.isOffset()) { if (searchParameters.isOffset()) {
sqlBuilder.append(" offset ").append(searchParameters.getOffset()); sqlBuilder.append(" offset ").append(
searchParameters.getOffset());
} }
} }
} }
final Object[] finalObjectArray = { id }; final Object[] finalObjectArray = { id };
final String sqlCountRows = "SELECT FOUND_ROWS()"; final String sqlCountRows = "SELECT FOUND_ROWS()";
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray, return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows,
sqlBuilder.toString(), finalObjectArray,
this.accountTransfersMapper); this.accountTransfersMapper);
} }
@Override @Override
public AccountTransferData retrieveRefundByTransferTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, public AccountTransferData retrieveRefundByTransferTemplate(
final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, final Integer toAccountType) { final Long fromOfficeId, final Long fromClientId,
final Long fromAccountId, final Integer fromAccountType,
final Long toOfficeId, final Long toClientId,
final Long toAccountId, final Integer toAccountType) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
final EnumOptionData loanAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); final EnumOptionData loanAccountType = AccountTransferEnumerations
final EnumOptionData savingsAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); .accountType(PortfolioAccountType.LOAN);
final EnumOptionData savingsAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.SAVINGS);
final Integer mostRelevantFromAccountType = fromAccountType; final Integer mostRelevantFromAccountType = fromAccountType;
final Collection<EnumOptionData> fromAccountTypeOptions = Arrays.asList(savingsAccountType, loanAccountType); final Collection<EnumOptionData> fromAccountTypeOptions = Arrays
.asList(savingsAccountType, loanAccountType);
final Collection<EnumOptionData> toAccountTypeOptions; final Collection<EnumOptionData> toAccountTypeOptions;
if (mostRelevantFromAccountType == 1) { if (mostRelevantFromAccountType == 1) {
// overpaid loan amt transfer to savings account // overpaid loan amt transfer to savings account
toAccountTypeOptions = Arrays.asList(savingsAccountType); toAccountTypeOptions = Arrays.asList(savingsAccountType);
} else { } else {
toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType); toAccountTypeOptions = Arrays.asList(loanAccountType,
savingsAccountType);
} }
final Integer mostRelevantToAccountType = toAccountType; final Integer mostRelevantToAccountType = toAccountType;
final EnumOptionData fromAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantFromAccountType); final EnumOptionData fromAccountTypeData = AccountTransferEnumerations
final EnumOptionData toAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantToAccountType); .accountType(mostRelevantFromAccountType);
final EnumOptionData toAccountTypeData = AccountTransferEnumerations
.accountType(mostRelevantToAccountType);
// from settings // from settings
OfficeData fromOffice = null; OfficeData fromOffice = null;
@@ -471,30 +594,37 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
} else { } else {
accountType = PortfolioAccountType.SAVINGS.getValue(); accountType = PortfolioAccountType.SAVINGS.getValue();
} }
fromAccount = this.portfolioAccountReadPlatformService.retrieveOneByPaidInAdvance(fromAccountId, accountType); fromAccount = this.portfolioAccountReadPlatformService
.retrieveOneByPaidInAdvance(fromAccountId, accountType);
// override provided fromClient with client of account // override provided fromClient with client of account
mostRelevantFromClientId = fromAccount.clientId(); mostRelevantFromClientId = fromAccount.clientId();
} }
if (mostRelevantFromClientId != null) { if (mostRelevantFromClientId != null) {
fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId); fromClient = this.clientReadPlatformService
.retrieveOne(mostRelevantFromClientId);
mostRelevantFromOfficeId = fromClient.officeId(); mostRelevantFromOfficeId = fromClient.officeId();
long[] loanStatus = null; long[] loanStatus = null;
if (mostRelevantFromAccountType == 1) { if (mostRelevantFromAccountType == 1) {
loanStatus = new long[] { 300, 700 }; loanStatus = new long[] { 300, 700 };
} }
PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(mostRelevantFromAccountType, mostRelevantFromClientId, PortfolioAccountDTO portfolioAccountDTO = new PortfolioAccountDTO(
mostRelevantFromAccountType, mostRelevantFromClientId,
loanStatus); loanStatus);
fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(portfolioAccountDTO); fromAccountOptions = this.portfolioAccountReadPlatformService
.retrieveAllForLookup(portfolioAccountDTO);
} }
Collection<OfficeData> fromOfficeOptions = null; Collection<OfficeData> fromOfficeOptions = null;
Collection<ClientData> fromClientOptions = null; Collection<ClientData> fromClientOptions = null;
if (mostRelevantFromOfficeId != null) { if (mostRelevantFromOfficeId != null) {
fromOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantFromOfficeId); fromOffice = this.officeReadPlatformService
fromOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); .retrieveOffice(mostRelevantFromOfficeId);
fromClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId); fromOfficeOptions = this.officeReadPlatformService
.retrieveAllOfficesForDropdown();
fromClientOptions = this.clientReadPlatformService
.retrieveAllForLookupByOfficeId(mostRelevantFromOfficeId);
} }
// defaults // defaults
@@ -503,35 +633,65 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
Collection<ClientData> toClientOptions = null; Collection<ClientData> toClientOptions = null;
if (toAccountId != null && fromAccount != null) { if (toAccountId != null && fromAccount != null) {
toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, toAccount = this.portfolioAccountReadPlatformService.retrieveOne(
toAccountId, mostRelevantToAccountType,
fromAccount.currencyCode()); fromAccount.currencyCode());
mostRelevantToClientId = toAccount.clientId(); mostRelevantToClientId = toAccount.clientId();
} }
if (mostRelevantToClientId != null) { if (mostRelevantToClientId != null) {
toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId); toClient = this.clientReadPlatformService
.retrieveOne(mostRelevantToClientId);
mostRelevantToOfficeId = toClient.officeId(); mostRelevantToOfficeId = toClient.officeId();
toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); toClientOptions = this.clientReadPlatformService
.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId);
toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); toAccountOptions = retrieveToAccounts(fromAccount,
mostRelevantToAccountType, mostRelevantToClientId);
} }
if (mostRelevantToOfficeId != null) { if (mostRelevantToOfficeId != null) {
toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId); toOffice = this.officeReadPlatformService
toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); .retrieveOffice(mostRelevantToOfficeId);
toOfficeOptions = this.officeReadPlatformService
.retrieveAllOfficesForDropdown();
toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId); toClientOptions = this.clientReadPlatformService
.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId);
if (toClientOptions != null && toClientOptions.size() == 1) { if (toClientOptions != null && toClientOptions.size() == 1) {
toClient = new ArrayList<>(toClientOptions).get(0); toClient = new ArrayList<>(toClientOptions).get(0);
toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId); toAccountOptions = retrieveToAccounts(fromAccount,
mostRelevantToAccountType, mostRelevantToClientId);
} }
} }
return AccountTransferData.template(fromOffice, fromClient, fromAccountTypeData, fromAccount, transferDate, toOffice, toClient, return AccountTransferData.template(fromOffice, fromClient,
toAccountTypeData, toAccount, fromOfficeOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions, fromAccountTypeData, fromAccount, transferDate, toOffice,
toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions); toClient, toAccountTypeData, toAccount, fromOfficeOptions,
fromClientOptions, fromAccountTypeOptions, fromAccountOptions,
toOfficeOptions, toClientOptions, toAccountTypeOptions,
toAccountOptions);
}
@Override
public BigDecimal getTotalTransactionAmount(Long accountId,
Integer accountType, LocalDate transactionDate) {
StringBuilder sqlBuilder = new StringBuilder(
" select sum(trans.amount) as totalTransactionAmount ");
sqlBuilder.append(" from m_account_transfer_details as det ");
sqlBuilder
.append(" inner join m_account_transfer_transaction as trans ");
sqlBuilder.append(" on det.id = trans.account_transfer_details_id ");
sqlBuilder.append(" where trans.is_reversed = 0 ");
sqlBuilder.append(" and trans.transaction_date = ? ");
sqlBuilder
.append(" and IF(1=?, det.from_loan_account_id = ?, det.from_savings_account_id = ?) ");
return this.jdbcTemplate.queryForObject(sqlBuilder.toString(),
new Object[] { this.formatter.print(transactionDate),
accountType, accountId, accountId }, BigDecimal.class);
} }
} }
@@ -69,6 +69,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
public static final String DOES_PRODUCT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in (100,200,300,303,304,700)"; public static final String DOES_PRODUCT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in (100,200,300,303,304,700)";
public static final String FIND_BY_ACCOUNT_NUMBER = "from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in (100,200,300,303,304)";
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER) @Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId, List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType); @Param("loanType") Integer loanType);
@@ -145,4 +147,6 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS) @Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS)
boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId); boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId);
@Query(FIND_BY_ACCOUNT_NUMBER)
Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber);
} }
@@ -45,4 +45,7 @@ public interface SavingsAccountRepository extends JpaRepository<SavingsAccount,
@Query("from SavingsAccount sa where sa.id = :accountId and sa.depositType = :depositAccountTypeId") @Query("from SavingsAccount sa where sa.id = :accountId and sa.depositType = :depositAccountTypeId")
SavingsAccount findByIdAndDepositAccountType(@Param("accountId") Long accountId, SavingsAccount findByIdAndDepositAccountType(@Param("accountId") Long accountId,
@Param("depositAccountTypeId") Integer depositAccountTypeId); @Param("depositAccountTypeId") Integer depositAccountTypeId);
@Query("from SavingsAccount sa where sa.accountNumber = :accountNumber and sa.status in (100, 200, 300, 303, 304) ")
SavingsAccount findNonClosedAccountByAccountNumber(@Param("accountNumber") String accountNumber);
} }
@@ -18,27 +18,37 @@
*/ */
package org.apache.fineract.portfolio.self.account.api; package org.apache.fineract.portfolio.self.account.api;
import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.account.api.AccountTransfersApiResource; import org.apache.fineract.portfolio.account.api.AccountTransfersApiResource;
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData; import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTransferData; import org.apache.fineract.portfolio.self.account.data.SelfAccountTransferData;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTransferDataValidator; import org.apache.fineract.portfolio.self.account.data.SelfAccountTransferDataValidator;
import org.apache.fineract.portfolio.self.account.exception.BeneficiaryTransferLimitExceededException;
import org.apache.fineract.portfolio.self.account.exception.DailyTPTTransactionAmountLimitExceededException;
import org.apache.fineract.portfolio.self.account.service.SelfAccountTransferReadService; import org.apache.fineract.portfolio.self.account.service.SelfAccountTransferReadService;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTReadPlatformService;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -54,6 +64,9 @@ public class SelfAccountTransferApiResource {
private final SelfAccountTransferReadService selfAccountTransferReadService; private final SelfAccountTransferReadService selfAccountTransferReadService;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final ApiRequestParameterHelper apiRequestParameterHelper;
private final SelfAccountTransferDataValidator dataValidator; private final SelfAccountTransferDataValidator dataValidator;
private final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService;
private final ConfigurationDomainService configurationDomainService;
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
@Autowired @Autowired
public SelfAccountTransferApiResource( public SelfAccountTransferApiResource(
@@ -62,37 +75,101 @@ public class SelfAccountTransferApiResource {
final AccountTransfersApiResource accountTransfersApiResource, final AccountTransfersApiResource accountTransfersApiResource,
final SelfAccountTransferReadService selfAccountTransferReadService, final SelfAccountTransferReadService selfAccountTransferReadService,
final ApiRequestParameterHelper apiRequestParameterHelper, final ApiRequestParameterHelper apiRequestParameterHelper,
final SelfAccountTransferDataValidator dataValidator) { final SelfAccountTransferDataValidator dataValidator,
final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService,
final ConfigurationDomainService configurationDomainService,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.context = context; this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer; this.toApiJsonSerializer = toApiJsonSerializer;
this.accountTransfersApiResource = accountTransfersApiResource; this.accountTransfersApiResource = accountTransfersApiResource;
this.selfAccountTransferReadService = selfAccountTransferReadService; this.selfAccountTransferReadService = selfAccountTransferReadService;
this.apiRequestParameterHelper = apiRequestParameterHelper; this.apiRequestParameterHelper = apiRequestParameterHelper;
this.dataValidator = dataValidator; this.dataValidator = dataValidator;
this.tptBeneficiaryReadPlatformService = tptBeneficiaryReadPlatformService;
this.configurationDomainService = configurationDomainService;
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
} }
@GET @GET
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String template(@Context final UriInfo uriInfo) { public String template(
@DefaultValue("") @QueryParam("type") final String type,
@Context final UriInfo uriInfo) {
AppUser user = this.context.authenticatedUser(); AppUser user = this.context.authenticatedUser();
Collection<SelfAccountTemplateData> templateData = this.selfAccountTransferReadService
.retrieveSelfAccountTemplateData(user);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
.process(uriInfo.getQueryParameters()); .process(uriInfo.getQueryParameters());
Collection<SelfAccountTemplateData> selfTemplateData = this.selfAccountTransferReadService
.retrieveSelfAccountTemplateData(user);
if (type.equals("tpt")) {
Collection<SelfAccountTemplateData> tptTemplateData = this.tptBeneficiaryReadPlatformService
.retrieveTPTSelfAccountTemplateData(user);
return this.toApiJsonSerializer.serialize(settings, return this.toApiJsonSerializer.serialize(settings,
new SelfAccountTransferData(templateData)); new SelfAccountTransferData(selfTemplateData,
tptTemplateData));
}
return this.toApiJsonSerializer
.serialize(settings, new SelfAccountTransferData(
selfTemplateData, selfTemplateData));
} }
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String create(final String apiRequestBodyAsJson) { public String create(
this.dataValidator.validateCreate(apiRequestBodyAsJson); @DefaultValue("") @QueryParam("type") final String type,
final String apiRequestBodyAsJson) {
Map<String, Object> params = this.dataValidator.validateCreate(type,
apiRequestBodyAsJson);
if (type.equals("tpt")) {
checkForLimits(params);
}
return this.accountTransfersApiResource.create(apiRequestBodyAsJson); return this.accountTransfersApiResource.create(apiRequestBodyAsJson);
} }
private void checkForLimits(Map<String, Object> params) {
SelfAccountTemplateData fromAccount = (SelfAccountTemplateData) params
.get("fromAccount");
SelfAccountTemplateData toAccount = (SelfAccountTemplateData) params
.get("toAccount");
LocalDate transactionDate = (LocalDate) params.get("transactionDate");
BigDecimal transactionAmount = (BigDecimal) params
.get("transactionAmount");
AppUser user = this.context.authenticatedUser();
Long transferLimit = this.tptBeneficiaryReadPlatformService
.getTransferLimit(user.getId(), toAccount.getAccountId(),
toAccount.getAccountType());
if (transferLimit != null && transferLimit > 0) {
if (transactionAmount.compareTo(new BigDecimal(transferLimit)) > 0) {
throw new BeneficiaryTransferLimitExceededException();
}
}
if (this.configurationDomainService.isDailyTPTLimitEnabled()) {
Long dailyTPTLimit = this.configurationDomainService
.getDailyTPTLimit();
if (dailyTPTLimit != null && dailyTPTLimit > 0) {
BigDecimal dailyTPTLimitBD = new BigDecimal(dailyTPTLimit);
BigDecimal totTransactionAmount = this.accountTransfersReadPlatformService
.getTotalTransactionAmount(fromAccount.getAccountId(),
fromAccount.getAccountType(), transactionDate);
if (totTransactionAmount != null
&& totTransactionAmount.compareTo(BigDecimal.ZERO) > 0) {
if (dailyTPTLimitBD.compareTo(totTransactionAmount) <= 0
|| dailyTPTLimitBD.compareTo(totTransactionAmount
.add(transactionAmount)) < 0) {
throw new DailyTPTTransactionAmountLimitExceededException(
fromAccount.getAccountId(),
fromAccount.getAccountType());
}
}
}
}
}
} }
@@ -0,0 +1,53 @@
/**
* 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.self.account.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface SelfBeneficiariesTPTApiConstants {
public static final String BENEFICIARY_ENTITY_NAME = "SSBENEFICIARYTPT";
public static final String RESOURCE_NAME = "beneficiary";
public static final String LOCALE = "locale";
public static final String NAME_PARAM_NAME = "name";
public static final String OFFICE_NAME_PARAM_NAME = "officeName";
public static final String ACCOUNT_TYPE_PARAM_NAME = "accountType";
public static final String ACCOUNT_NUMBER_PARAM_NAME = "accountNumber";
public static final String TRANSFER_LIMIT_PARAM_NAME = "transferLimit";
public static final String ID_PARAM_NAME = "id";
public static final String CLIENT_NAME_PARAM_NAME = "clientName";
public static final String ACCOUNT_TYPE_OPTIONS_PARAM_NAME = "accountTypeOptions";
public static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(LOCALE, NAME_PARAM_NAME, OFFICE_NAME_PARAM_NAME,
ACCOUNT_NUMBER_PARAM_NAME, ACCOUNT_TYPE_PARAM_NAME,
TRANSFER_LIMIT_PARAM_NAME));
public static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(NAME_PARAM_NAME, TRANSFER_LIMIT_PARAM_NAME));
public static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(NAME_PARAM_NAME, OFFICE_NAME_PARAM_NAME,
ACCOUNT_NUMBER_PARAM_NAME, ACCOUNT_TYPE_PARAM_NAME,
TRANSFER_LIMIT_PARAM_NAME, ID_PARAM_NAME,
CLIENT_NAME_PARAM_NAME, ACCOUNT_TYPE_OPTIONS_PARAM_NAME));
}
@@ -0,0 +1,161 @@
/**
* 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.self.account.api;
import java.util.Arrays;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
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.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.service.AccountTransferEnumerations;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTData;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/self/beneficiaries/tpt")
@Component
@Scope("singleton")
public class SelfBeneficiariesTPTApiResource {
private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<SelfBeneficiariesTPTData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final SelfBeneficiariesTPTReadPlatformService readPlatformService;
@Autowired
public SelfBeneficiariesTPTApiResource(
final PlatformSecurityContext context,
final DefaultToApiJsonSerializer<SelfBeneficiariesTPTData> toApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final ApiRequestParameterHelper apiRequestParameterHelper,
final SelfBeneficiariesTPTReadPlatformService readPlatformService) {
this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.readPlatformService = readPlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String template(@Context final UriInfo uriInfo) {
final EnumOptionData loanAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.LOAN);
final EnumOptionData savingsAccountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.SAVINGS);
final Collection<EnumOptionData> accountTypeOptions = Arrays.asList(
savingsAccountType, loanAccountType);
SelfBeneficiariesTPTData templateData = new SelfBeneficiariesTPTData(
accountTypeOptions);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, templateData,
SelfBeneficiariesTPTApiConstants.RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String add(final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder()
.addSelfServiceBeneficiaryTPT().withJson(apiRequestBodyAsJson)
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService
.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@PUT
@Path("{beneficiaryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String update(@PathParam("beneficiaryId") final Long beneficiaryId,
final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder()
.updateSelfServiceBeneficiaryTPT(beneficiaryId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService
.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@DELETE
@Path("{beneficiaryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String delete(@PathParam("beneficiaryId") final Long beneficiaryId,
final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder()
.deleteSelfServiceBeneficiaryTPT(beneficiaryId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService
.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(
SelfBeneficiariesTPTApiConstants.BENEFICIARY_ENTITY_NAME);
final Collection<SelfBeneficiariesTPTData> beneficiaries = this.readPlatformService
.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, beneficiaries,
SelfBeneficiariesTPTApiConstants.RESPONSE_DATA_PARAMETERS);
}
}
@@ -103,4 +103,13 @@ public class SelfAccountTemplateData implements
.toHashCode(); .toHashCode();
} }
public Long getAccountId() {
return this.accountId;
}
public Integer getAccountType() {
return this.accountType.getId().intValue();
}
} }
@@ -23,11 +23,14 @@ import java.util.Collection;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class SelfAccountTransferData { public class SelfAccountTransferData {
private final Collection<SelfAccountTemplateData> accountOptions; private final Collection<SelfAccountTemplateData> fromAccountOptions;
private final Collection<SelfAccountTemplateData> toAccountOptions;
public SelfAccountTransferData( public SelfAccountTransferData(
final Collection<SelfAccountTemplateData> accountOptions) { final Collection<SelfAccountTemplateData> fromAccountOptions,
this.accountOptions = accountOptions; Collection<SelfAccountTemplateData> toAccountOptions) {
this.fromAccountOptions = fromAccountOptions;
this.toAccountOptions = toAccountOptions;
} }
} }
@@ -27,10 +27,16 @@ import static org.apache.fineract.portfolio.account.AccountDetailConstants.toAcc
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.api.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME; import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME;
import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferAmountParamName;
import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferDateParamName;
import static org.apache.fineract.portfolio.account.api.AccountTransfersApiConstants.transferDescriptionParamName;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
@@ -40,7 +46,9 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; 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.self.account.service.SelfAccountTransferReadService; import org.apache.fineract.portfolio.self.account.service.SelfAccountTransferReadService;
import org.apache.fineract.portfolio.self.account.service.SelfBeneficiariesTPTReadPlatformService;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -51,19 +59,22 @@ public class SelfAccountTransferDataValidator {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final SelfAccountTransferReadService selfAccountTransferReadService; private final SelfAccountTransferReadService selfAccountTransferReadService;
private final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService;
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@Autowired @Autowired
public SelfAccountTransferDataValidator( public SelfAccountTransferDataValidator(
final PlatformSecurityContext context, final PlatformSecurityContext context,
final SelfAccountTransferReadService selfAccountTransferReadService, final SelfAccountTransferReadService selfAccountTransferReadService,
final SelfBeneficiariesTPTReadPlatformService tptBeneficiaryReadPlatformService,
final FromJsonHelper fromApiJsonHelper) { final FromJsonHelper fromApiJsonHelper) {
this.context = context; this.context = context;
this.selfAccountTransferReadService = selfAccountTransferReadService; this.selfAccountTransferReadService = selfAccountTransferReadService;
this.tptBeneficiaryReadPlatformService = tptBeneficiaryReadPlatformService;
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
} }
public void validateCreate(String apiRequestBodyAsJson) { public Map<String,Object> validateCreate(String type, String apiRequestBodyAsJson) {
if (StringUtils.isBlank(apiRequestBodyAsJson)) { if (StringUtils.isBlank(apiRequestBodyAsJson)) {
throw new InvalidJsonException(); throw new InvalidJsonException();
} }
@@ -126,6 +137,16 @@ public class SelfAccountTransferDataValidator {
"Cannot transfer from Loan account to another Loan account."); "Cannot transfer from Loan account to another Loan account.");
} }
final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(transferDateParamName, element);
baseDataValidator.reset().parameter(transferDateParamName).value(transactionDate).notNull();
final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(transferAmountParamName, element);
baseDataValidator.reset().parameter(transferAmountParamName).value(transactionAmount).notNull().positiveAmount();
final String transactionDescription = this.fromApiJsonHelper.extractStringNamed(transferDescriptionParamName, element);
baseDataValidator.reset().parameter(transferDescriptionParamName).value(transactionDescription).notBlank()
.notExceedingLengthOf(200);
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
SelfAccountTemplateData fromAccount = new SelfAccountTemplateData( SelfAccountTemplateData fromAccount = new SelfAccountTemplateData(
@@ -133,21 +154,35 @@ public class SelfAccountTransferDataValidator {
SelfAccountTemplateData toAccount = new SelfAccountTemplateData( SelfAccountTemplateData toAccount = new SelfAccountTemplateData(
toAccountId, toAccountType, toClientId, toOfficeId); toAccountId, toAccountType, toClientId, toOfficeId);
validateSelfUserAccounts(fromAccount, toAccount, baseDataValidator); validateUserAccounts(fromAccount, toAccount, baseDataValidator, type);
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
Map<String, Object> ret = new HashMap<>();
ret.put("fromAccount", fromAccount);
ret.put("toAccount", toAccount);
ret.put("transactionDate", transactionDate);
ret.put("transactionAmount", transactionAmount);
return ret;
} }
private void validateSelfUserAccounts( private void validateUserAccounts(
final SelfAccountTemplateData fromAccount, final SelfAccountTemplateData fromAccount,
final SelfAccountTemplateData toAccount, final SelfAccountTemplateData toAccount,
final DataValidatorBuilder baseDataValidator) { final DataValidatorBuilder baseDataValidator, final String type) {
AppUser user = this.context.authenticatedUser(); AppUser user = this.context.authenticatedUser();
Collection<SelfAccountTemplateData> userValidAccounts = this.selfAccountTransferReadService Collection<SelfAccountTemplateData> validFromAccounts = this.selfAccountTransferReadService
.retrieveSelfAccountTemplateData(user); .retrieveSelfAccountTemplateData(user);
Collection<SelfAccountTemplateData> validToAccounts = validFromAccounts;
if (type.equals("tpt")) {
validToAccounts = this.tptBeneficiaryReadPlatformService
.retrieveTPTSelfAccountTemplateData(user);
}
boolean validFromAccount = false; boolean validFromAccount = false;
for (SelfAccountTemplateData validAccount : userValidAccounts) { for (SelfAccountTemplateData validAccount : validFromAccounts) {
if (validAccount.equals(fromAccount)) { if (validAccount.equals(fromAccount)) {
validFromAccount = true; validFromAccount = true;
break; break;
@@ -155,7 +190,7 @@ public class SelfAccountTransferDataValidator {
} }
boolean validToAccount = false; boolean validToAccount = false;
for (SelfAccountTemplateData validAccount : userValidAccounts) { for (SelfAccountTemplateData validAccount : validToAccounts) {
if (validAccount.equals(toAccount)) { if (validAccount.equals(toAccount)) {
validToAccount = true; validToAccount = true;
break; break;
@@ -0,0 +1,69 @@
/**
* 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.self.account.data;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public class SelfBeneficiariesTPTData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final String name;
@SuppressWarnings("unused")
private final String officeName;
@SuppressWarnings("unused")
private final String clientName;
@SuppressWarnings("unused")
private final EnumOptionData accountType;
@SuppressWarnings("unused")
private final String accountNumber;
@SuppressWarnings("unused")
private final Long transferLimit;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> accountTypeOptions;
public SelfBeneficiariesTPTData(
final Collection<EnumOptionData> accountTypeOptions) {
this.accountTypeOptions = accountTypeOptions;
this.id = null;
this.name = null;
this.officeName = null;
this.clientName = null;
this.accountType = null;
this.accountNumber = null;
this.transferLimit = null;
}
public SelfBeneficiariesTPTData(final Long id, final String name,
final String officeName, final String clientName,
final EnumOptionData accountType, final String accountNumber,
final Long transferLimit) {
this.accountTypeOptions = null;
this.id = id;
this.name = name;
this.officeName = officeName;
this.clientName = clientName;
this.accountType = accountType;
this.accountNumber = accountNumber;
this.transferLimit = transferLimit;
}
}
@@ -0,0 +1,165 @@
/**
* 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.self.account.data;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.RESOURCE_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.OFFICE_NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_TYPE_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_NUMBER_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.TRANSFER_LIMIT_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.CREATE_REQUEST_DATA_PARAMETERS;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.UPDATE_REQUEST_DATA_PARAMETERS;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public class SelfBeneficiariesTPTDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public SelfBeneficiariesTPTDataValidator(
final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public HashMap<String, Object> validateForCreate(String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
CREATE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
dataValidationErrors).resource(RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String name = this.fromApiJsonHelper.extractStringNamed(
NAME_PARAM_NAME, element);
baseDataValidator.reset().parameter(NAME_PARAM_NAME).value(name)
.notBlank().notExceedingLengthOf(50);
final String officeName = this.fromApiJsonHelper.extractStringNamed(
OFFICE_NAME_PARAM_NAME, element);
baseDataValidator.reset().parameter(OFFICE_NAME_PARAM_NAME)
.value(officeName).notBlank()
.notExceedingLengthOf(50);
final String accountNo = this.fromApiJsonHelper.extractStringNamed(
ACCOUNT_NUMBER_PARAM_NAME, element);
baseDataValidator.reset().parameter(ACCOUNT_NUMBER_PARAM_NAME)
.value(accountNo).notBlank().notExceedingLengthOf(20);
final Integer accountType = this.fromApiJsonHelper.extractIntegerNamed(
ACCOUNT_TYPE_PARAM_NAME, element, this.fromApiJsonHelper
.extractLocaleParameter(element.getAsJsonObject()));
baseDataValidator
.reset()
.parameter(ACCOUNT_TYPE_PARAM_NAME)
.value(accountType)
.notNull()
.isOneOfTheseValues(PortfolioAccountType.LOAN.getValue(),
PortfolioAccountType.SAVINGS.getValue());
final Long transferLimit = this.fromApiJsonHelper.extractLongNamed(
TRANSFER_LIMIT_PARAM_NAME, element);
baseDataValidator.reset().parameter(TRANSFER_LIMIT_PARAM_NAME)
.value(transferLimit).ignoreIfNull().longGreaterThanZero();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
HashMap<String, Object> ret = new HashMap<>();
ret.put(NAME_PARAM_NAME, name);
ret.put(OFFICE_NAME_PARAM_NAME, officeName);
ret.put(ACCOUNT_NUMBER_PARAM_NAME, accountNo);
ret.put(ACCOUNT_TYPE_PARAM_NAME, accountType);
ret.put(TRANSFER_LIMIT_PARAM_NAME, transferLimit);
return ret;
}
public HashMap<String, Object> validateForUpdate(String json) {
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 List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
dataValidationErrors).resource(RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
HashMap<String, Object> ret = new HashMap<>();
if (this.fromApiJsonHelper.parameterExists(NAME_PARAM_NAME, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(
NAME_PARAM_NAME, element);
baseDataValidator.reset().parameter(NAME_PARAM_NAME).value(name)
.notBlank().notExceedingLengthOf(50);
ret.put(NAME_PARAM_NAME, name);
}
if (this.fromApiJsonHelper.parameterExists(TRANSFER_LIMIT_PARAM_NAME,
element)) {
final Long transferLimit = this.fromApiJsonHelper.extractLongNamed(
TRANSFER_LIMIT_PARAM_NAME, element);
baseDataValidator.reset().parameter(TRANSFER_LIMIT_PARAM_NAME)
.value(transferLimit).ignoreIfNull().longGreaterThanZero();
ret.put(TRANSFER_LIMIT_PARAM_NAME, transferLimit);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
return ret;
}
private void throwExceptionIfValidationWarningsExist(
final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -0,0 +1,136 @@
/**
* 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.self.account.domain;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.TRANSFER_LIMIT_PARAM_NAME;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_selfservice_beneficiaries_tpt", uniqueConstraints = { @UniqueConstraint(columnNames = {
"name", "app_user_id", "is_active" }, name = "name") })
public class SelfBeneficiariesTPT extends AbstractPersistable<Long> {
@Column(name = "app_user_id", nullable = false)
private Long appUserId;
@Column(name = "name", length = 50, nullable = false)
private String name;
@Column(name = "office_id", nullable = false)
private Long officeId;
@Column(name = "client_id", nullable = false)
private Long clientId;
@Column(name = "account_id", nullable = false)
private Long accountId;
@Column(name = "account_type", nullable = false)
private Integer accountType;
@Column(name = "transfer_limit", nullable = true)
private Long transferLimit;
@Column(name = "is_active", nullable = false)
private boolean isActive = true;
protected SelfBeneficiariesTPT() {
//
}
public SelfBeneficiariesTPT(Long appUserId, String name, Long officeId,
Long clientId, Long accountId, Integer accountType,
Long transferLimit) {
this.appUserId = appUserId;
this.name = name;
this.officeId = officeId;
this.clientId = clientId;
this.accountId = accountId;
this.accountType = accountType;
this.transferLimit = transferLimit;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Long getTransferLimit() {
return this.transferLimit;
}
public void setTransferLimit(Long transferLimit) {
this.transferLimit = transferLimit;
}
public boolean isActive() {
return this.isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
public Long getAppUserId() {
return this.appUserId;
}
public Long getOfficeId() {
return this.officeId;
}
public Long getClientId() {
return this.clientId;
}
public Long getAccountId() {
return this.accountId;
}
public Integer getAccountType() {
return this.accountType;
}
public Map<String, Object> update(String newName, Long newTransferLimit) {
Map<String, Object> changes = new HashMap<>();
if (!this.name.equals(newName)) {
this.name = newName;
changes.put(NAME_PARAM_NAME, newName);
}
if ((this.transferLimit !=null && !this.transferLimit.equals(newTransferLimit))
|| (this.transferLimit == null && newTransferLimit != null)) {
this.transferLimit = newTransferLimit;
changes.put(TRANSFER_LIMIT_PARAM_NAME, newTransferLimit);
}
return changes;
}
}
@@ -0,0 +1,28 @@
/**
* 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.self.account.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface SelfBeneficiariesTPTRepository extends
JpaRepository<SelfBeneficiariesTPT, Long>,
JpaSpecificationExecutor<SelfBeneficiariesTPT> {
}
@@ -0,0 +1,30 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.self.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class BeneficiaryTransferLimitExceededException extends AbstractPlatformDomainRuleException {
public BeneficiaryTransferLimitExceededException() {
super("error.msg.beneficiary.transfer.amount.limit.for.beneficiary.exceeded",
"Transfer amount limit for beneficiary exceeded");
}
}
@@ -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.portfolio.self.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class DailyTPTTransactionAmountLimitExceededException extends AbstractPlatformDomainRuleException {
public DailyTPTTransactionAmountLimitExceededException(Long accountId,
Integer accountType) {
super("error.msg.beneficiary.daily.tpt.transfer.limit.for.fromaccountid."+accountId+".fromaccounttype."+accountType+".exceeded",
"Daily third party transfer limit for the source account excceeded");
}
}
@@ -0,0 +1,32 @@
/**
* 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.self.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class InvalidAccountInformationException extends
AbstractPlatformDomainRuleException {
public InvalidAccountInformationException(final String officeName,
final String accountNumber, final String accountType) {
super("error.msg.beneficiary.invalid.account.details.with.officeName."
+ officeName + ".accountNumber." + accountNumber
+ ".accountType." + accountType,
"Invalid Office Name, Account Number, Account Type combination");
}
}
@@ -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.self.account.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class InvalidBeneficiaryException extends
AbstractPlatformDomainRuleException {
public InvalidBeneficiaryException(final Long beneficiaryId) {
super("error.msg.beneficiary.invalid.beneficiary.id." + beneficiaryId,
"Beneficiary ID doesn't belong to the User");
}
}
@@ -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.self.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.self.account.service.SelfBeneficiariesTPTWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "SSBENEFICIARYTPT", action = "CREATE")
public class AddSelfBeneficiariesTPTCommandHandler implements
NewCommandSourceHandler {
private final SelfBeneficiariesTPTWritePlatformService writePlatformService;
@Autowired
public AddSelfBeneficiariesTPTCommandHandler(
final SelfBeneficiariesTPTWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.add(command);
}
}
@@ -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.self.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.self.account.service.SelfBeneficiariesTPTWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "SSBENEFICIARYTPT", action = "DELETE")
public class DeleteSelfBeneficiariesTPTCommandHandler implements
NewCommandSourceHandler {
private final SelfBeneficiariesTPTWritePlatformService writePlatformService;
@Autowired
public DeleteSelfBeneficiariesTPTCommandHandler(
final SelfBeneficiariesTPTWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.delete(command);
}
}
@@ -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.self.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.self.account.service.SelfBeneficiariesTPTWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "SSBENEFICIARYTPT", action = "UPDATE")
public class UpdateSelfBeneficiariesTPTCommandHandler implements
NewCommandSourceHandler {
private final SelfBeneficiariesTPTWritePlatformService writePlatformService;
@Autowired
public UpdateSelfBeneficiariesTPTCommandHandler(
final SelfBeneficiariesTPTWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.update(command);
}
}
@@ -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.portfolio.self.account.service;
import java.util.Collection;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTData;
import org.apache.fineract.useradministration.domain.AppUser;
public interface SelfBeneficiariesTPTReadPlatformService {
Collection<SelfBeneficiariesTPTData> retrieveAll();
Collection<SelfAccountTemplateData> retrieveTPTSelfAccountTemplateData(
AppUser user);
Long getTransferLimit(Long id, Long accountId, Integer accountType);
}
@@ -0,0 +1,222 @@
/**
* 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.self.account.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.service.AccountTransferEnumerations;
import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTData;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class SelfBeneficiariesTPTReadPlatformServiceImpl implements
SelfBeneficiariesTPTReadPlatformService {
private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate;
private final BeneficiaryMapper mapper;
private final AccountTemplateMapper accountTemplateMapper;
@Autowired
public SelfBeneficiariesTPTReadPlatformServiceImpl(
final PlatformSecurityContext context,
final RoutingDataSource dataSource) {
this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.mapper = new BeneficiaryMapper();
this.accountTemplateMapper = new AccountTemplateMapper();
}
@Override
public Collection<SelfBeneficiariesTPTData> retrieveAll() {
AppUser user = this.context.authenticatedUser();
return this.jdbcTemplate.query(this.mapper.schema(), this.mapper,
new Object[] { user.getId(), user.getId() });
}
@Override
public Collection<SelfAccountTemplateData> retrieveTPTSelfAccountTemplateData(
AppUser user) {
return this.jdbcTemplate.query(this.accountTemplateMapper.schema(),
this.accountTemplateMapper,
new Object[] { user.getId(), user.getId() });
}
private static final class BeneficiaryMapper implements
RowMapper<SelfBeneficiariesTPTData> {
private final String schemaSql;
public BeneficiaryMapper() {
final StringBuilder sqlBuilder = new StringBuilder(
"(select b.id as id, ");
sqlBuilder.append(" b.name as name, ");
sqlBuilder.append(" o.name as officeName, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" s.account_no as accountNumber, ");
sqlBuilder.append(" b.transfer_limit as transferLimit ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_savings_account as s on b.account_id = s.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 2 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
sqlBuilder.append(" union all ");
sqlBuilder.append(" (select b.id as id, ");
sqlBuilder.append(" b.name as name, ");
sqlBuilder.append(" o.name as officeName, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" l.account_no as accountNumber, ");
sqlBuilder.append(" b.transfer_limit as transferLimit ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_loan as l on b.account_id = l.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 1 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SelfBeneficiariesTPTData mapRow(final ResultSet rs,
@SuppressWarnings("unused") final int rowNum)
throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final String officeName = rs.getString("officeName");
final String clientName = rs.getString("clientName");
final Integer accountTypeId = rs.getInt("accountType");
final EnumOptionData accountType = AccountTransferEnumerations
.accountType(PortfolioAccountType.fromInt(accountTypeId));
final String accountNumber = rs.getString("accountNumber");
final Long transferLimit = rs.getLong("transferLimit");
return new SelfBeneficiariesTPTData(id, name, officeName,
clientName, accountType, accountNumber, transferLimit);
}
}
private static final class AccountTemplateMapper implements
RowMapper<SelfAccountTemplateData> {
private final String schemaSql;
public AccountTemplateMapper() {
final StringBuilder sqlBuilder = new StringBuilder(
"(select o.name as officeName, ");
sqlBuilder.append(" o.id as officeId, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" c.id as clientId, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" s.account_no as accountNumber, ");
sqlBuilder.append(" s.id as accountId ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_savings_account as s on b.account_id = s.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 2 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
sqlBuilder.append(" union all ");
sqlBuilder.append(" (select o.name as officeName, ");
sqlBuilder.append(" o.id as officeId, ");
sqlBuilder.append(" c.display_name as clientName, ");
sqlBuilder.append(" c.id as clientId, ");
sqlBuilder.append(" b.account_type as accountType, ");
sqlBuilder.append(" l.account_no as accountNumber, ");
sqlBuilder.append(" l.id as accountId ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder
.append(" inner join m_office as o on b.office_id = o.id ");
sqlBuilder
.append(" inner join m_client as c on b.client_id = c.id ");
sqlBuilder
.append(" inner join m_loan as l on b.account_id = l.id ");
sqlBuilder.append(" where b.is_active = 1 ");
sqlBuilder.append(" and b.account_type = 1 ");
sqlBuilder.append(" and b.app_user_id = ?) ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SelfAccountTemplateData mapRow(final ResultSet rs,
@SuppressWarnings("unused") final int rowNum)
throws SQLException {
final String officeName = rs.getString("officeName");
final Long officeId = rs.getLong("officeId");
final String clientName = rs.getString("clientName");
final Long clientId = rs.getLong("clientId");
final Integer accountTypeId = rs.getInt("accountType");
final String accountNumber = rs.getString("accountNumber");
final Long accountId = rs.getLong("accountId");
return new SelfAccountTemplateData(accountId, accountNumber,
accountTypeId, clientId, clientName, officeId, officeName);
}
}
@Override
public Long getTransferLimit(Long appUserId, Long accountId, Integer accountType) {
final StringBuilder sqlBuilder = new StringBuilder("select b.transfer_limit ");
sqlBuilder.append(" from m_selfservice_beneficiaries_tpt as b ");
sqlBuilder.append(" where b.app_user_id = ? ");
sqlBuilder.append(" and b.account_id = ? ");
sqlBuilder.append(" and b.account_type = ? ");
sqlBuilder.append(" and b.is_active = 1; ");
return this.jdbcTemplate.queryForObject(sqlBuilder.toString(),
new Object[]{appUserId, accountId, accountType}, Long.class);
}
}
@@ -0,0 +1,32 @@
/**
* 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.self.account.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface SelfBeneficiariesTPTWritePlatformService {
CommandProcessingResult add(JsonCommand command);
CommandProcessingResult update(JsonCommand command);
CommandProcessingResult delete(JsonCommand command);
}
@@ -0,0 +1,212 @@
/**
* 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.self.account.service;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_NUMBER_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.ACCOUNT_TYPE_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.OFFICE_NAME_PARAM_NAME;
import static org.apache.fineract.portfolio.self.account.api.SelfBeneficiariesTPTApiConstants.TRANSFER_LIMIT_PARAM_NAME;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
import org.apache.fineract.portfolio.self.account.data.SelfBeneficiariesTPTDataValidator;
import org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPT;
import org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPTRepository;
import org.apache.fineract.portfolio.self.account.exception.InvalidAccountInformationException;
import org.apache.fineract.portfolio.self.account.exception.InvalidBeneficiaryException;
import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SelfBeneficiariesTPTWritePlatformServiceImpl implements
SelfBeneficiariesTPTWritePlatformService {
private final Logger logger;
private final PlatformSecurityContext context;
private final SelfBeneficiariesTPTRepository repository;
private final SelfBeneficiariesTPTDataValidator validator;
private final LoanRepository loanRepo;
private final SavingsAccountRepository savingRepo;
@Autowired
public SelfBeneficiariesTPTWritePlatformServiceImpl(
final PlatformSecurityContext context,
final SelfBeneficiariesTPTRepository repository,
final SelfBeneficiariesTPTDataValidator validator,
final LoanRepository loanRepo,
final SavingsAccountRepository savingRepo) {
this.context = context;
this.repository = repository;
this.validator = validator;
this.loanRepo = loanRepo;
this.savingRepo = savingRepo;
this.logger = LoggerFactory
.getLogger(SelfBeneficiariesTPTWritePlatformServiceImpl.class);
}
@Transactional
@Override
public CommandProcessingResult add(JsonCommand command) {
HashMap<String, Object> params = this.validator
.validateForCreate(command.json());
String name = (String) params.get(NAME_PARAM_NAME);
Integer accountType = (Integer) params.get(ACCOUNT_TYPE_PARAM_NAME);
String accountNumber = (String) params.get(ACCOUNT_NUMBER_PARAM_NAME);
String officeName = (String) params.get(OFFICE_NAME_PARAM_NAME);
Long transferLimit = (Long) params.get(TRANSFER_LIMIT_PARAM_NAME);
Long accountId = null;
Long clientId = null;
Long officeId = null;
boolean validAccountDetails = true;
if (accountType.equals(PortfolioAccountType.LOAN)) {
Loan loan = this.loanRepo
.findNonClosedLoanByAccountNumber(accountNumber);
if (loan != null && loan.getClientId() != null
&& loan.getOffice().getName().equals(officeName)) {
accountId = loan.getId();
officeId = loan.getOfficeId();
clientId = loan.getClientId();
} else {
validAccountDetails = false;
}
} else {
SavingsAccount savings = this.savingRepo
.findNonClosedAccountByAccountNumber(accountNumber);
if (savings != null
&& savings.getClient() != null
&& savings.getClient().getOffice().getName()
.equals(officeName)) {
accountId = savings.getId();
clientId = savings.getClient().getId();
officeId = savings.getClient().getOffice().getId();
} else {
validAccountDetails = false;
}
}
if (validAccountDetails) {
try {
AppUser user = this.context.authenticatedUser();
SelfBeneficiariesTPT beneficiary = new SelfBeneficiariesTPT(
user.getId(), name, officeId, clientId, accountId,
accountType, transferLimit);
this.repository.save(beneficiary);
return new CommandProcessingResultBuilder().withEntityId(
beneficiary.getId()).build();
} catch (DataAccessException dae) {
handleDataIntegrityIssues(command, dae);
}
}
throw new InvalidAccountInformationException(officeName, accountNumber,
PortfolioAccountType.fromInt(accountType).getCode());
}
@Transactional
@Override
public CommandProcessingResult update(JsonCommand command) {
HashMap<String, Object> params = this.validator
.validateForUpdate(command.json());
AppUser user = this.context.authenticatedUser();
Long beneficiaryId = command.entityId();
SelfBeneficiariesTPT beneficiary = this.repository
.findOne(beneficiaryId);
if (beneficiary != null
&& beneficiary.getAppUserId().equals(user.getId())) {
String name = (String) params.get(NAME_PARAM_NAME);
Long transferLimit = (Long) params.get(TRANSFER_LIMIT_PARAM_NAME);
Map<String, Object> changes = beneficiary.update(name,
transferLimit);
if (!changes.isEmpty()) {
try {
this.repository.save(beneficiary);
return new CommandProcessingResultBuilder() //
.withEntityId(beneficiary.getId()) //
.with(changes).build();
} catch (DataAccessException dae) {
handleDataIntegrityIssues(command, dae);
}
}
}
throw new InvalidBeneficiaryException(beneficiaryId);
}
@Transactional
@Override
public CommandProcessingResult delete(JsonCommand command) {
AppUser user = this.context.authenticatedUser();
Long beneficiaryId = command.entityId();
SelfBeneficiariesTPT beneficiary = this.repository
.findOne(beneficiaryId);
if (beneficiary != null
&& beneficiary.getAppUserId().equals(user.getId())) {
beneficiary.setActive(false);
this.repository.save(beneficiary);
return new CommandProcessingResultBuilder() //
.withEntityId(beneficiary.getId()) //
.build();
}
throw new InvalidBeneficiaryException(beneficiaryId);
}
private void handleDataIntegrityIssues(final JsonCommand command,
final DataAccessException dae) {
final Throwable realCause = dae.getMostSpecificCause();
if (realCause.getMessage().contains("name")) {
final String name = command
.stringValueOfParameterNamed(NAME_PARAM_NAME);
throw new PlatformDataIntegrityException(
"error.msg.beneficiary.duplicate.name",
"Beneficiary with name `" + name + "` already exists",
NAME_PARAM_NAME, name);
}
this.logger.error(dae.getMessage(), dae);
throw new PlatformDataIntegrityException(
"error.msg.beneficiary.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
}
@@ -0,0 +1,24 @@
INSERT INTO `c_configuration` (`name`, `value`, `date_value`, `enabled`, `is_trap_door`, `description`) VALUES ( 'daily-tpt-limit', 0, NULL, 0, 0, 'Daily limit for third party transfers');
CREATE TABLE `m_selfservice_beneficiaries_tpt` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`app_user_id` BIGINT NOT NULL,
`name` VARCHAR(50) NOT NULL,
`office_id` BIGINT NOT NULL,
`client_id` BIGINT NOT NULL,
`account_id` BIGINT NOT NULL,
`account_type` SMALLINT(4) NOT NULL,
`transfer_limit` BIGINT NULL DEFAULT 0,
`is_active` BIT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `name` (`name`, `app_user_id`, `is_active`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
INSERT INTO `m_permission`(`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES
('SSBENEFICIARYTPT', 'READ_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'READ', 0),
('SSBENEFICIARYTPT', 'CREATE_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'CREATE', 0),
('SSBENEFICIARYTPT', 'UPDATE_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'UPDATE', 0),
('SSBENEFICIARYTPT', 'DELETE_SSBENEFICIARYTPT', 'SSBENEFICIARYTPT', 'DELETE', 0);