Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4522f1305d | ||
|
|
ac563badaa | ||
|
|
861e2e196e | ||
|
|
8ad90f0b00 | ||
|
|
5606b88fbc | ||
|
|
4bb71382a1 | ||
|
|
859cabd07d | ||
|
|
6380e0d7d2 | ||
|
|
4ad508ee96 | ||
|
|
6eb562548c | ||
|
|
41aeb2b89a | ||
|
|
2569e0f836 | ||
|
|
81b5850b62 |
+192
@@ -95,6 +95,198 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createInvestmentParty() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTPARTY";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/parties";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder updateInvestmentParty(final Long partyId) {
|
||||||
|
this.actionName = "UPDATE";
|
||||||
|
this.entityName = "INVESTMENTPARTY";
|
||||||
|
this.entityId = partyId;
|
||||||
|
this.href = "/investment/parties/" + partyId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder deleteInvestmentParty(final Long partyId) {
|
||||||
|
this.actionName = "DELETE";
|
||||||
|
this.entityName = "INVESTMENTPARTY";
|
||||||
|
this.entityId = partyId;
|
||||||
|
this.href = "/investment/parties/" + partyId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createInvestmentScript() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTSCRIPT";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/scripts";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder updateInvestmentScript(final Long scriptId) {
|
||||||
|
this.actionName = "UPDATE";
|
||||||
|
this.entityName = "INVESTMENTSCRIPT";
|
||||||
|
this.entityId = scriptId;
|
||||||
|
this.href = "/investment/scripts/" + scriptId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createInvestmentPurchase() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTPURCHASE";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/purchases";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder deleteInvestmentPurchase(final Long purchaseId) {
|
||||||
|
this.actionName = "DELETE";
|
||||||
|
this.entityName = "INVESTMENTPURCHASE";
|
||||||
|
this.entityId = purchaseId;
|
||||||
|
this.href = "/investment/purchases/" + purchaseId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder settleInvestmentPurchase(final Long purchaseId) {
|
||||||
|
this.actionName = "SETTLE";
|
||||||
|
this.entityName = "INVESTMENTPURCHASE";
|
||||||
|
this.entityId = purchaseId;
|
||||||
|
this.href = "/investment/purchases/" + purchaseId + "/settle";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createInvestmentSale() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTSALE";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/sales";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder settleInvestmentSale(final Long saleId) {
|
||||||
|
this.actionName = "SETTLE";
|
||||||
|
this.entityName = "INVESTMENTSALE";
|
||||||
|
this.entityId = saleId;
|
||||||
|
this.href = "/investment/sales/" + saleId + "/settle";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder deleteInvestmentSale(final Long saleId) {
|
||||||
|
this.actionName = "DELETE";
|
||||||
|
this.entityName = "INVESTMENTSALE";
|
||||||
|
this.entityId = saleId;
|
||||||
|
this.href = "/investment/sales/" + saleId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createInvestmentFd() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTFD";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/fds";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder matureInvestmentFd(final Long fdId) {
|
||||||
|
this.actionName = "MATURE";
|
||||||
|
this.entityName = "INVESTMENTFD";
|
||||||
|
this.entityId = fdId;
|
||||||
|
this.href = "/investment/fds/" + fdId + "/mature";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder prematureInvestmentFd(final Long fdId) {
|
||||||
|
this.actionName = "PREMATURE";
|
||||||
|
this.entityName = "INVESTMENTFD";
|
||||||
|
this.entityId = fdId;
|
||||||
|
this.href = "/investment/fds/" + fdId + "/premature";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder renewInvestmentFd(final Long fdId) {
|
||||||
|
this.actionName = "RENEW";
|
||||||
|
this.entityName = "INVESTMENTFD";
|
||||||
|
this.entityId = fdId;
|
||||||
|
this.href = "/investment/fds/" + fdId + "/renew";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder postInvestmentFdInterest(final Long fdId) {
|
||||||
|
this.actionName = "POSTINTEREST";
|
||||||
|
this.entityName = "INVESTMENTFD";
|
||||||
|
this.entityId = fdId;
|
||||||
|
this.href = "/investment/fds/" + fdId + "/interest";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder setInvestmentDepositBase() {
|
||||||
|
this.actionName = "SETBASE";
|
||||||
|
this.entityName = "INVESTMENTFD";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/fds/deposit-base";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder amortiseInvestmentPremium() {
|
||||||
|
this.actionName = "AMORTISE";
|
||||||
|
this.entityName = "INVESTMENTSALE";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/amortisation";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder writeOffInvestmentPurchase(final Long purchaseId) {
|
||||||
|
this.actionName = "WRITEOFF";
|
||||||
|
this.entityName = "INVESTMENTSALE";
|
||||||
|
this.entityId = purchaseId;
|
||||||
|
this.href = "/investment/purchases/" + purchaseId + "/write-off";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder redeemInvestmentPurchase(final Long purchaseId) {
|
||||||
|
this.actionName = "REDEEM";
|
||||||
|
this.entityName = "INVESTMENTSALE";
|
||||||
|
this.entityId = purchaseId;
|
||||||
|
this.href = "/investment/purchases/" + purchaseId + "/redeem";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder transferInvestmentSecurity(final Long purchaseId) {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTTRANSFER";
|
||||||
|
this.entityId = purchaseId;
|
||||||
|
this.href = "/investment/purchases/" + purchaseId + "/transfer";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder postInvestmentInterest(final Long purchaseId) {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTINTEREST";
|
||||||
|
this.entityId = purchaseId;
|
||||||
|
this.href = "/investment/purchases/" + purchaseId + "/interest-postings";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder createInvestmentDailyRate() {
|
||||||
|
this.actionName = "CREATE";
|
||||||
|
this.entityName = "INVESTMENTDAILYRATE";
|
||||||
|
this.entityId = null;
|
||||||
|
this.href = "/investment/daily-rates";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder deleteInvestmentDailyRate(final Long rateId) {
|
||||||
|
this.actionName = "DELETE";
|
||||||
|
this.entityName = "INVESTMENTDAILYRATE";
|
||||||
|
this.entityId = rateId;
|
||||||
|
this.href = "/investment/daily-rates/" + rateId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder createChequeStockLot() {
|
public CommandWrapperBuilder createChequeStockLot() {
|
||||||
this.actionName = "CREATE";
|
this.actionName = "CREATE";
|
||||||
this.entityName = "CHEQUESTOCKLOT";
|
this.entityName = "CHEQUESTOCKLOT";
|
||||||
|
|||||||
@@ -81,8 +81,6 @@ dependencies {
|
|||||||
'org.apache.avro:avro',
|
'org.apache.avro:avro',
|
||||||
'jakarta.ws.rs:jakarta.ws.rs-api',
|
'jakarta.ws.rs:jakarta.ws.rs-api',
|
||||||
'org.glassfish.jersey.media:jersey-media-multipart',
|
'org.glassfish.jersey.media:jersey-media-multipart',
|
||||||
// Server-Sent Events (SseEventSink, Sse): the Approvals badge is pushed to the screen over one.
|
|
||||||
'org.glassfish.jersey.media:jersey-media-sse',
|
|
||||||
|
|
||||||
'org.springframework:spring-jms',
|
'org.springframework:spring-jms',
|
||||||
'org.springframework:spring-context-support',
|
'org.springframework:spring-context-support',
|
||||||
|
|||||||
-67
@@ -1570,76 +1570,9 @@ public class AccountingProcessorHelper {
|
|||||||
return mapping == null ? null : mapping.getGlAccount();
|
return mapping == null ? null : mapping.getGlAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The clearing account for the savings-to-savings account transfer running on this thread, taken from a product's
|
|
||||||
* own Transfers In Suspense mapping (see {@link #useProductTransferSuspense}). Null outside such a transfer.
|
|
||||||
*/
|
|
||||||
private static final ThreadLocal<GLAccount> TRANSFER_CLEARING_ACCOUNT = new ThreadLocal<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes both legs of a savings-to-savings account transfer (savings to RD by auto debit, savings to savings, a
|
|
||||||
* deposit paying out) clear through the Transfers In Suspense account set on the product's Accounting tab, rather
|
|
||||||
* than the organisation-wide Liability Transfer financial activity.
|
|
||||||
* <p>
|
|
||||||
* The receiving product's mapping is used first, then the paying product's: a savings product screen does not
|
|
||||||
* offer Transfers In Suspense, so for savings to RD it is the RD product's mapping. Both legs use the same account,
|
|
||||||
* so it nets to nil. Where neither product has one, nothing is set and the transfer clears through Liability
|
|
||||||
* Transfer as before.
|
|
||||||
* <p>
|
|
||||||
* Must be paired with {@link #clearProductTransferSuspense()} in a finally block.
|
|
||||||
*
|
|
||||||
* @return true when a product mapping was found and is now in use
|
|
||||||
*/
|
|
||||||
public boolean useProductTransferSuspense(final Long toSavingsProductId, final Long fromSavingsProductId) {
|
|
||||||
final int transfersSuspense = CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue();
|
|
||||||
GLAccount account = optionalLinkedGLAccountForSavingsProduct(toSavingsProductId, transfersSuspense, null);
|
|
||||||
if (account == null) {
|
|
||||||
account = optionalLinkedGLAccountForSavingsProduct(fromSavingsProductId, transfersSuspense, null);
|
|
||||||
}
|
|
||||||
if (account == null) {
|
|
||||||
TRANSFER_CLEARING_ACCOUNT.remove();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
TRANSFER_CLEARING_ACCOUNT.set(account);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearProductTransferSuspense() {
|
|
||||||
TRANSFER_CLEARING_ACCOUNT.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For undoing one leg of an account transfer: reverses through the clearing account that leg was actually posted
|
|
||||||
* to, whether a product's Transfers In Suspense or the Liability Transfer financial activity. A reversal works the
|
|
||||||
* accounts out again rather than copying the original rows, so without this a transfer posted to a product's
|
|
||||||
* suspense account would be reversed against Liability Transfer and leave both accounts open.
|
|
||||||
* <p>
|
|
||||||
* The clearing row is the credit of a withdrawal and the debit of a deposit (the largest, should a fee sit beside
|
|
||||||
* it). Nothing is set where the leg has no journal entries. Must be paired with
|
|
||||||
* {@link #clearProductTransferSuspense()} in a finally block.
|
|
||||||
*/
|
|
||||||
public void useClearingAccountPostedFor(final Long savingsTransactionId, final boolean isWithdrawal) {
|
|
||||||
TRANSFER_CLEARING_ACCOUNT.remove();
|
|
||||||
if (savingsTransactionId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final List<Long> found = jdbcTemplate.queryForList("""
|
|
||||||
SELECT account_id FROM acc_gl_journal_entry
|
|
||||||
WHERE savings_transaction_id = ? AND type_enum = ? AND reversed = false
|
|
||||||
ORDER BY amount DESC, id
|
|
||||||
LIMIT 1
|
|
||||||
""", Long.class, savingsTransactionId, isWithdrawal ? JournalEntryType.CREDIT.getValue() : JournalEntryType.DEBIT.getValue());
|
|
||||||
if (!found.isEmpty()) {
|
|
||||||
glAccountRepository.findById(found.get(0)).ifPresent(TRANSFER_CLEARING_ACCOUNT::set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private GLAccount getLinkedGLAccountForSavingsProduct(final Long savingsProductId, final int accountMappingTypeId,
|
private GLAccount getLinkedGLAccountForSavingsProduct(final Long savingsProductId, final int accountMappingTypeId,
|
||||||
final Long paymentTypeId) {
|
final Long paymentTypeId) {
|
||||||
GLAccount glAccount;
|
GLAccount glAccount;
|
||||||
if (accountMappingTypeId == FinancialActivity.LIABILITY_TRANSFER.getValue() && TRANSFER_CLEARING_ACCOUNT.get() != null) {
|
|
||||||
return TRANSFER_CLEARING_ACCOUNT.get();
|
|
||||||
}
|
|
||||||
if (isOrganizationAccount(accountMappingTypeId)) {
|
if (isOrganizationAccount(accountMappingTypeId)) {
|
||||||
FinancialActivityAccount financialActivityAccount = this.financialActivityAccountRepository
|
FinancialActivityAccount financialActivityAccount = this.financialActivityAccountRepository
|
||||||
.findByFinancialActivityTypeWithNotFoundDetection(accountMappingTypeId);
|
.findByFinancialActivityTypeWithNotFoundDetection(accountMappingTypeId);
|
||||||
|
|||||||
+13
-43
@@ -144,22 +144,14 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
|
|
||||||
final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer,
|
final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer,
|
||||||
isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), isInterestTransfer, isWithdrawBalance);
|
isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), isInterestTransfer, isWithdrawBalance);
|
||||||
|
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt,
|
||||||
|
transactionDate, transactionAmount, paymentDetail, transactionBooleanValues, backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
|
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||||
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill);
|
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId, backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
// As in transferFunds: both legs clear through the product's Transfers In Suspense where one is mapped.
|
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate,
|
||||||
final SavingsAccountTransaction withdrawal;
|
transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill);
|
||||||
final SavingsAccountTransaction deposit;
|
|
||||||
this.accountingProcessorHelper.useProductTransferSuspense(toSavingsAccount.productId(), fromSavingsAccount.productId());
|
|
||||||
try {
|
|
||||||
withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt, transactionDate, transactionAmount,
|
|
||||||
paymentDetail, transactionBooleanValues, backdatedTxnsAllowedTill);
|
|
||||||
deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate, transactionAmount,
|
|
||||||
paymentDetail, isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill);
|
|
||||||
} finally {
|
|
||||||
this.accountingProcessorHelper.clearProductTransferSuspense();
|
|
||||||
}
|
|
||||||
applyToRecurringDepositSchedule(toSavingsAccount, deposit);
|
applyToRecurringDepositSchedule(toSavingsAccount, deposit);
|
||||||
|
|
||||||
if (!fromSavingsAccount.getCurrency().getCode().equals(toSavingsAccount.getCurrency().getCode())) {
|
if (!fromSavingsAccount.getCurrency().getCode().equals(toSavingsAccount.getCurrency().getCode())) {
|
||||||
@@ -285,27 +277,14 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
if (accountTransfer.getToLoanTransaction() != null) {
|
if (accountTransfer.getToLoanTransaction() != null) {
|
||||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction());
|
this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction());
|
||||||
}
|
}
|
||||||
// Each leg is reversed through the clearing account it was posted to (a product's Transfers In Suspense or
|
|
||||||
// Liability Transfer), so the reversal clears that same account.
|
|
||||||
if (accountTransfer.getFromTransaction() != null) {
|
if (accountTransfer.getFromTransaction() != null) {
|
||||||
this.accountingProcessorHelper.useClearingAccountPostedFor(accountTransfer.getFromTransaction().getId(), true);
|
|
||||||
try {
|
|
||||||
this.savingsAccountWritePlatformService.undoTransaction(
|
this.savingsAccountWritePlatformService.undoTransaction(
|
||||||
accountTransfer.accountTransferDetails().fromSavingsAccount().getId(),
|
accountTransfer.accountTransferDetails().fromSavingsAccount().getId(), accountTransfer.getFromTransaction().getId(),
|
||||||
accountTransfer.getFromTransaction().getId(), true);
|
true);
|
||||||
} finally {
|
|
||||||
this.accountingProcessorHelper.clearProductTransferSuspense();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (accountTransfer.getToSavingsTransaction() != null) {
|
if (accountTransfer.getToSavingsTransaction() != null) {
|
||||||
this.accountingProcessorHelper.useClearingAccountPostedFor(accountTransfer.getToSavingsTransaction().getId(), false);
|
this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.accountTransferDetails().toSavingsAccount().getId(),
|
||||||
try {
|
|
||||||
this.savingsAccountWritePlatformService.undoTransaction(
|
|
||||||
accountTransfer.accountTransferDetails().toSavingsAccount().getId(),
|
|
||||||
accountTransfer.getToSavingsTransaction().getId(), true);
|
accountTransfer.getToSavingsTransaction().getId(), true);
|
||||||
} finally {
|
|
||||||
this.accountingProcessorHelper.clearProductTransferSuspense();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
accountTransfer.reverse();
|
accountTransfer.reverse();
|
||||||
this.accountTransferRepository.save(accountTransfer);
|
this.accountTransferRepository.save(accountTransfer);
|
||||||
@@ -428,22 +407,13 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
transactionDate = transactionDate.plusDays(1);
|
transactionDate = transactionDate.plusDays(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both legs clear through the Transfers In Suspense account on the product's Accounting tab (the receiving
|
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount,
|
||||||
// product's, else the paying one's) instead of the organisation-wide Liability Transfer mapping.
|
accountTransferDTO.getFmt(), transactionDate, accountTransferDTO.getTransactionAmount(),
|
||||||
final SavingsAccountTransaction withdrawal;
|
accountTransferDTO.getPaymentDetail(), transactionBooleanValues, backdatedTxnsAllowedTill);
|
||||||
final SavingsAccountTransaction deposit;
|
|
||||||
this.accountingProcessorHelper.useProductTransferSuspense(toSavingsAccount.productId(), fromSavingsAccount.productId());
|
|
||||||
try {
|
|
||||||
withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, accountTransferDTO.getFmt(),
|
|
||||||
transactionDate, accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
|
|
||||||
transactionBooleanValues, backdatedTxnsAllowedTill);
|
|
||||||
|
|
||||||
deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, accountTransferDTO.getFmt(), transactionDate,
|
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount,
|
||||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), isAccountTransfer,
|
accountTransferDTO.getFmt(), transactionDate, accountTransferDTO.getTransactionAmount(),
|
||||||
isRegularTransaction, backdatedTxnsAllowedTill);
|
accountTransferDTO.getPaymentDetail(), isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill);
|
||||||
} finally {
|
|
||||||
this.accountingProcessorHelper.clearProductTransferSuspense();
|
|
||||||
}
|
|
||||||
applyToRecurringDepositSchedule(toSavingsAccount, deposit);
|
applyToRecurringDepositSchedule(toSavingsAccount, deposit);
|
||||||
|
|
||||||
accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(accountTransferDTO, fromSavingsAccount,
|
accountTransferDetails = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(accountTransferDTO, fromSavingsAccount,
|
||||||
|
|||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.api;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import jakarta.ws.rs.DefaultValue;
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.QueryParam;
|
|
||||||
import jakarta.ws.rs.core.CacheControl;
|
|
||||||
import jakarta.ws.rs.core.Context;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
import jakarta.ws.rs.core.Response;
|
|
||||||
import jakarta.ws.rs.sse.Sse;
|
|
||||||
import jakarta.ws.rs.sse.SseEventSink;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
|
||||||
import org.apache.fineract.portfolio.approvals.service.ApprovalCountsReadPlatformService;
|
|
||||||
import org.apache.fineract.portfolio.approvals.service.ApprovalCountsStreamService;
|
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How many requests wait on every Approval Center tab, for the badge on each tab and the Total on the Approvals menu.
|
|
||||||
* Numbers only: opening a tab still loads that tab's own list. The counting is in
|
|
||||||
* {@link ApprovalCountsReadPlatformService}; the live stream is in {@link ApprovalCountsStreamService}.
|
|
||||||
*/
|
|
||||||
@Path("/v1/approvals/counts")
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
|
||||||
public class ApprovalCountsApiResource {
|
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
|
||||||
private final ApprovalCountsReadPlatformService readService;
|
|
||||||
private final ApprovalCountsStreamService streamService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The counts as they are now, answered at once. {@code fresh=true} skips the few seconds the last answer is held
|
|
||||||
* for. Screens that can stream use {@link #stream} instead; this is for a one-off read and as a fallback.
|
|
||||||
*/
|
|
||||||
@GET
|
|
||||||
public Response counts(@QueryParam("fresh") @DefaultValue("false") final boolean fresh) {
|
|
||||||
final AppUser user = this.context.authenticatedUser();
|
|
||||||
final CacheControl noStore = new CacheControl();
|
|
||||||
noStore.setNoStore(true);
|
|
||||||
return Response.ok(this.readService.retrieveHeld(user, fresh).toJson()).cacheControl(noStore).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Server-Sent Events: the connection stays open and a {@code counts} event is written the moment the numbers change,
|
|
||||||
* so a screen neither polls nor reloads. The first event carries the numbers as they stand.
|
|
||||||
*/
|
|
||||||
@GET
|
|
||||||
@Path("stream")
|
|
||||||
@Produces(MediaType.SERVER_SENT_EVENTS)
|
|
||||||
public void stream(@Context final SseEventSink sink, @Context final Sse sse, @Context final HttpServletResponse response) {
|
|
||||||
// Ask reverse proxies not to hold the events back.
|
|
||||||
response.setHeader("X-Accel-Buffering", "no");
|
|
||||||
response.setHeader("Cache-Control", "no-cache, no-store");
|
|
||||||
try {
|
|
||||||
this.streamService.open(this.readService.planFor(this.context.authenticatedUser()), sink, sse);
|
|
||||||
} catch (final RuntimeException e) {
|
|
||||||
sink.close();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One Approval Center tab and the question that counts what waits on it.
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* the tab's route segment, which is also the key its badge reads on the screen
|
|
||||||
* @param permission
|
|
||||||
* the permission the tab is shown under (READ_* is also met by ALL_FUNCTIONS_READ); null means every caller sees
|
|
||||||
* the tab
|
|
||||||
* @param sql
|
|
||||||
* a scalar sub-select answering "how many", written with the named parameters of {@link ApprovalCountsPlan}
|
|
||||||
*/
|
|
||||||
public record ApprovalCounter(String key, String permission, String sql) {}
|
|
||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.data;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What is waiting for approval: the total, the number for each tab that has anything, and a version that is equal
|
|
||||||
* exactly when the numbers are, which is how a change is told from a repeat.
|
|
||||||
*/
|
|
||||||
public record ApprovalCountsData(long total, Map<String, Long> counts, String version) {
|
|
||||||
|
|
||||||
private static final Gson GSON = new Gson();
|
|
||||||
|
|
||||||
public static ApprovalCountsData of(final Map<String, Long> counts) {
|
|
||||||
final long total = counts.values().stream().mapToLong(Long::longValue).sum();
|
|
||||||
return new ApprovalCountsData(total, counts, Integer.toHexString(counts.hashCode()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toJson() {
|
|
||||||
final Map<String, Object> json = new LinkedHashMap<>();
|
|
||||||
json.put("total", this.total);
|
|
||||||
json.put("counts", this.counts);
|
|
||||||
json.put("version", this.version);
|
|
||||||
return GSON.toJson(json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
|
||||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What one caller is counted for: the tabs they may see and the values (office scope, user, checker rights) those
|
|
||||||
* tabs' queries are asked with. It is worked out on the request thread, because it reads the user's roles, and can then
|
|
||||||
* be used from any thread.
|
|
||||||
*
|
|
||||||
* @param tenant
|
|
||||||
* the tenant the counts are read from
|
|
||||||
* @param visible
|
|
||||||
* the tabs this caller may see, in the order the Approval Center shows them
|
|
||||||
* @param params
|
|
||||||
* the named parameters the tabs' queries use
|
|
||||||
* @param group
|
|
||||||
* equal for callers who are counted identically, so they can share one query
|
|
||||||
*/
|
|
||||||
public record ApprovalCountsPlan(FineractPlatformTenant tenant, List<ApprovalCounter> visible, MapSqlParameterSource params,
|
|
||||||
String group) {}
|
|
||||||
-122
@@ -1,122 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.service;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCounter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Every Approval Center tab that carries a badge, and the question that counts what waits on it. Each question is the
|
|
||||||
* one the tab asks of its own API (same table, same status, same office scope, same entries left out). To badge a new
|
|
||||||
* tab, add one line here and one badge on the screen.
|
|
||||||
*
|
|
||||||
* The queries use these named parameters, supplied per caller: {@code :h} (office hierarchy pattern), {@code :ho} (head
|
|
||||||
* office), {@code :uid} (user) and {@code :superChecker} (may check every maker-checker command).
|
|
||||||
*/
|
|
||||||
public final class ApprovalCounterRegistry {
|
|
||||||
|
|
||||||
private ApprovalCounterRegistry() {}
|
|
||||||
|
|
||||||
/** Entities whose maker-checker requests are shown under Product Approval, not in the Checker Inbox. */
|
|
||||||
private static final String PRODUCT_ENTITIES = "'LOANPRODUCT','SAVINGSPRODUCT','CURRENTPRODUCT','SHAREPRODUCT',"
|
|
||||||
+ "'FIXEDDEPOSITPRODUCT','RECURRINGDEPOSITPRODUCT','CHARGE'";
|
|
||||||
|
|
||||||
/** Product edits that are staged in m_saving_product_temp, so Product Approval already lists them from there. */
|
|
||||||
private static final String STAGED_PRODUCT_ENTITIES = "'SAVINGSPRODUCT','CURRENTPRODUCT','SHAREPRODUCT',"
|
|
||||||
+ "'FIXEDDEPOSITPRODUCT','RECURRINGDEPOSITPRODUCT'";
|
|
||||||
|
|
||||||
/** Accounts whose UPDATE is staged for Pending Account Edits, so the Checker Inbox leaves it out. */
|
|
||||||
private static final String STAGED_ACCOUNT_ENTITIES = "'SAVINGSACCOUNT','CURRENTACCOUNT'";
|
|
||||||
|
|
||||||
private static final String DDS_ENTITIES = "'PARTY','AGENT','PARTYTRANSACTION','DDSACCOUNTTYPE','COMMISSIONSLAB',"
|
|
||||||
+ "'DDSBATCH','DDSCOLLECTIONENTRY'";
|
|
||||||
|
|
||||||
/** Maker-checker commands awaiting a checker, limited to the offices and checker rights the caller has. */
|
|
||||||
private static final String MC_PENDING = "FROM m_portfolio_command_source aud WHERE aud.status = 2 "
|
|
||||||
+ "AND (:ho OR EXISTS (SELECT 1 FROM m_office o2 WHERE o2.id = aud.office_id AND o2.hierarchy LIKE :h)) "
|
|
||||||
+ "AND (:superChecker OR EXISTS (SELECT 1 FROM m_permission p JOIN m_role_permission rp ON rp.permission_id = p.id "
|
|
||||||
+ "JOIN m_appuser_role ur ON ur.role_id = rp.role_id AND ur.appuser_id = :uid "
|
|
||||||
+ "WHERE p.code LIKE '%\\_CHECKER' AND REPLACE(p.action_name, '_CHECKER', '') = aud.action_name "
|
|
||||||
+ "AND p.entity_name = aud.entity_name)) ";
|
|
||||||
|
|
||||||
/** Where the person who raised a loan or account sits, for the office scope the tab applies. */
|
|
||||||
private static final String CLIENT_OFFICE = "EXISTS (SELECT 1 FROM m_office o WHERE o.id = COALESCE(c.office_id, g.office_id) "
|
|
||||||
+ "AND o.hierarchy LIKE :h)";
|
|
||||||
|
|
||||||
/** The one tab whose list is open to managers and head cashiers only. */
|
|
||||||
public static final String DISBURSEMENT_REQUESTS = "disbursement-requests";
|
|
||||||
|
|
||||||
/** The tabs, in the order the Approval Center shows them. A null permission means every caller sees the tab. */
|
|
||||||
public static final List<ApprovalCounter> ALL = List.of(
|
|
||||||
// ── Fineract's own maker-checker and pending lists ────────────────────────────
|
|
||||||
new ApprovalCounter("checker-inbox", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) " + MC_PENDING + "AND upper(aud.entity_name) NOT IN (" + PRODUCT_ENTITIES + ") "
|
|
||||||
+ "AND NOT (upper(aud.entity_name) IN (" + STAGED_ACCOUNT_ENTITIES + ") AND upper(aud.action_name) = 'UPDATE')"),
|
|
||||||
new ApprovalCounter("client-approval", "READ_CLIENT",
|
|
||||||
"SELECT count(*) FROM m_client c WHERE c.status_enum = 100 "
|
|
||||||
+ "AND EXISTS (SELECT 1 FROM m_office o WHERE o.id = c.office_id AND o.hierarchy LIKE :h)"),
|
|
||||||
new ApprovalCounter("loan-approval", "READ_LOAN",
|
|
||||||
"SELECT count(*) FROM m_loan l LEFT JOIN m_client c ON c.id = l.client_id LEFT JOIN m_group g ON g.id = l.group_id "
|
|
||||||
+ "WHERE l.loan_status_id = 100 AND " + CLIENT_OFFICE),
|
|
||||||
new ApprovalCounter("loan-disbursal", "READ_LOAN",
|
|
||||||
"SELECT count(*) FROM m_loan l LEFT JOIN m_client c ON c.id = l.client_id LEFT JOIN m_group g ON g.id = l.group_id "
|
|
||||||
+ "WHERE l.loan_status_id = 200 AND " + CLIENT_OFFICE),
|
|
||||||
new ApprovalCounter("reschedule-loan", "RESCHEDULE_LOAN",
|
|
||||||
"SELECT count(*) FROM m_loan_reschedule_request WHERE status_enum = 100"),
|
|
||||||
new ApprovalCounter("pending-account", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT (SELECT count(*) FROM m_savings_account s LEFT JOIN m_client c ON c.id = s.client_id "
|
|
||||||
+ "LEFT JOIN m_group g ON g.id = s.group_id WHERE s.status_enum = 100 "
|
|
||||||
+ "AND COALESCE(s.terminate_true, false) = false AND " + CLIENT_OFFICE
|
|
||||||
+ ") + (SELECT count(*) FROM m_current_account ca WHERE ca.status_enum = 100 "
|
|
||||||
+ "AND COALESCE(ca.terminate_true, false) = false "
|
|
||||||
+ "AND EXISTS (SELECT 1 FROM m_office o WHERE o.id = ca.office_id AND o.hierarchy LIKE :h))"),
|
|
||||||
new ApprovalCounter("pending-share-accounts", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_share_account WHERE status_enum = 100"),
|
|
||||||
new ApprovalCounter("product-approval", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT (SELECT count(*) FROM m_saving_product_temp WHERE status = 'PENDING' "
|
|
||||||
+ "AND product_type IN ('SAVINGS','CURRENT','SHARE','FD','RD')) + (SELECT count(*) " + MC_PENDING
|
|
||||||
+ "AND upper(aud.entity_name) IN (" + PRODUCT_ENTITIES + ") "
|
|
||||||
+ "AND NOT (upper(aud.entity_name) IN (" + STAGED_PRODUCT_ENTITIES + ") AND upper(aud.action_name) = 'UPDATE'))"),
|
|
||||||
new ApprovalCounter("pending-savings-account-edits", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT (SELECT count(*) FROM m_savings_account_pending_edit WHERE status = 'PENDING') "
|
|
||||||
+ "+ (SELECT count(*) FROM m_current_account_pending_edit WHERE status = 'PENDING') "
|
|
||||||
+ "+ (SELECT count(*) FROM m_share_account_pending_edit WHERE status = 'PENDING')"),
|
|
||||||
new ApprovalCounter("pending-client-edits", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_client_pending_edit pe JOIN m_client c ON c.id = pe.client_id WHERE pe.status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("pending-user-edits", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_user_pending_request WHERE status = 'PENDING'"),
|
|
||||||
// ── Requests raised by clerks, cashiers and managers ──────────────────────────
|
|
||||||
new ApprovalCounter("withdrawal-requests", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_clerk_withdrawal_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("cash-transfers", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_cash_transfer_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("voucher-corrections", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_voucher_correction WHERE status IN ('UNPASS_PENDING','CORRECTION_PENDING')"),
|
|
||||||
new ApprovalCounter("share-withdrawal-requests", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_clerk_share_withdrawal_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("dividend-requests", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_share_product_dividend_pay_out p JOIN m_share_product sp ON sp.id = p.product_id "
|
|
||||||
+ "WHERE p.status = 100"),
|
|
||||||
new ApprovalCounter("share-addition-requests", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_share_account_transactions t JOIN m_share_account sa ON sa.id = t.account_id "
|
|
||||||
+ "JOIN m_client c ON c.id = sa.client_id JOIN m_share_product sp ON sp.id = sa.product_id "
|
|
||||||
+ "WHERE t.is_active = true AND t.status_enum = 100 AND t.type_enum = 500"),
|
|
||||||
new ApprovalCounter("deposit-requests", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_cashier_deposit_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter(DISBURSEMENT_REQUESTS, "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_clerk_disbursement_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("dds-approvals", null,
|
|
||||||
"SELECT count(*) FROM m_portfolio_command_source c JOIN m_permission p "
|
|
||||||
+ "ON p.code = c.action_name || '_' || c.entity_name AND p.can_maker_checker = true "
|
|
||||||
+ "WHERE c.status = 2 AND c.entity_name IN (" + DDS_ENTITIES + ")"),
|
|
||||||
new ApprovalCounter("chequebook-requests", "READ_CHEQUEBOOKPENDINGREQUEST",
|
|
||||||
"SELECT count(*) FROM m_chequebook_pending_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("closure-requests", "ALL_FUNCTIONS_READ",
|
|
||||||
"SELECT count(*) FROM m_clerk_closure_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("dd-issue-approvals", null,
|
|
||||||
"SELECT count(*) FROM m_sponsor_dd_issue_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("dd-reprint-approvals", null,
|
|
||||||
"SELECT count(*) FROM m_sponsor_dd_reprint_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("dd-revoke-approvals", null,
|
|
||||||
"SELECT count(*) FROM m_sponsor_dd_revoke_request WHERE status = 'PENDING'"),
|
|
||||||
new ApprovalCounter("risk-change-approvals", null,
|
|
||||||
"SELECT count(*) FROM m_client_risk_change_request WHERE status = 'PENDING'"));
|
|
||||||
}
|
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.service;
|
|
||||||
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCountsData;
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCountsPlan;
|
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
|
||||||
|
|
||||||
/** Reads how many requests wait on each Approval Center tab, in one statement. */
|
|
||||||
public interface ApprovalCountsReadPlatformService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Works out what the caller is counted for. Call it on the request thread: it reads the user's roles and the
|
|
||||||
* tenant of the current request.
|
|
||||||
*/
|
|
||||||
ApprovalCountsPlan planFor(AppUser user);
|
|
||||||
|
|
||||||
/** Counts now, for the tabs in the plan. The plan's tenant must be the current tenant of the calling thread. */
|
|
||||||
ApprovalCountsData retrieve(ApprovalCountsPlan plan);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Counts for the caller, from the answer held for a few seconds when there is one, so many screens asking together
|
|
||||||
* cost the database one query. {@code fresh} skips the held answer.
|
|
||||||
*/
|
|
||||||
ApprovalCountsData retrieveHeld(AppUser user, boolean fresh);
|
|
||||||
}
|
|
||||||
-114
@@ -1,114 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.service;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCounter;
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCountsData;
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCountsPlan;
|
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
|
||||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All the visible tabs' questions go to the database as one statement (one scalar sub-select per tab), so the cost is
|
|
||||||
* one connection and one plan instead of a call per tab. A tab the caller may not see is simply not asked.
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ApprovalCountsReadPlatformServiceImpl implements ApprovalCountsReadPlatformService {
|
|
||||||
|
|
||||||
/** Screens asking together read one answer instead of each running the statement. */
|
|
||||||
private static final long HOLD_MILLIS = 3_000L;
|
|
||||||
|
|
||||||
private record Held(long at, ApprovalCountsData data) {}
|
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
|
||||||
private final Map<String, Held> held = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApprovalCountsPlan planFor(final AppUser user) {
|
|
||||||
final List<ApprovalCounter> visible = new ArrayList<>();
|
|
||||||
for (final ApprovalCounter counter : ApprovalCounterRegistry.ALL) {
|
|
||||||
if (isVisible(user, counter)) {
|
|
||||||
visible.add(counter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final String hierarchy = user.getOffice().getHierarchy();
|
|
||||||
final boolean superChecker = !user.hasNotPermissionForAnyOf("ALL_FUNCTIONS", "CHECKER_SUPER_USER");
|
|
||||||
final MapSqlParameterSource params = new MapSqlParameterSource().addValue("h", hierarchy + "%")
|
|
||||||
.addValue("ho", ".".equals(hierarchy)).addValue("uid", user.getId()).addValue("superChecker", superChecker);
|
|
||||||
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
|
|
||||||
// Callers counted the same way share one query: only a limited checker's numbers depend on who they are.
|
|
||||||
final String group = tenant.getTenantIdentifier() + '|' + hierarchy + '|' + superChecker + '|'
|
|
||||||
+ (superChecker ? "" : user.getId()) + '|' + visible.stream().map(ApprovalCounter::key).collect(Collectors.joining(","));
|
|
||||||
return new ApprovalCountsPlan(tenant, visible, params, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApprovalCountsData retrieve(final ApprovalCountsPlan plan) {
|
|
||||||
final Map<String, Long> counts = new LinkedHashMap<>();
|
|
||||||
final List<ApprovalCounter> visible = plan.visible();
|
|
||||||
if (!visible.isEmpty()) {
|
|
||||||
final StringBuilder sql = new StringBuilder("SELECT ");
|
|
||||||
for (int i = 0; i < visible.size(); i++) {
|
|
||||||
sql.append(i == 0 ? "" : ", ").append('(').append(visible.get(i).sql()).append(") AS c").append(i);
|
|
||||||
}
|
|
||||||
final Map<String, Object> row = new NamedParameterJdbcTemplate(this.jdbcTemplate).queryForMap(sql.toString(),
|
|
||||||
plan.params());
|
|
||||||
for (int i = 0; i < visible.size(); i++) {
|
|
||||||
final long n = row.get("c" + i) instanceof Number number ? number.longValue() : 0L;
|
|
||||||
if (n > 0) {
|
|
||||||
counts.put(visible.get(i).key(), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ApprovalCountsData.of(counts);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ApprovalCountsData retrieveHeld(final AppUser user, final boolean fresh) {
|
|
||||||
final ApprovalCountsPlan plan = planFor(user);
|
|
||||||
final String holdKey = plan.tenant().getTenantIdentifier() + ':' + user.getId();
|
|
||||||
final long now = System.currentTimeMillis();
|
|
||||||
final Held recent = this.held.get(holdKey);
|
|
||||||
if (!fresh && recent != null && now - recent.at() < HOLD_MILLIS) {
|
|
||||||
return recent.data();
|
|
||||||
}
|
|
||||||
final ApprovalCountsData data = retrieve(plan);
|
|
||||||
this.held.put(holdKey, new Held(now, data));
|
|
||||||
if (this.held.size() > 500) {
|
|
||||||
this.held.entrySet().removeIf(e -> now - e.getValue().at() >= HOLD_MILLIS);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The tab's own visibility rule: its permission (READ_* is also met by ALL_FUNCTIONS_READ) and, for one tab, its role. */
|
|
||||||
private boolean isVisible(final AppUser user, final ApprovalCounter counter) {
|
|
||||||
if (counter.permission() != null) {
|
|
||||||
final boolean allowed = counter.permission().startsWith("READ_")
|
|
||||||
? user.hasAnyPermission(counter.permission(), "ALL_FUNCTIONS_READ")
|
|
||||||
: user.hasAnyPermission(counter.permission());
|
|
||||||
if (!allowed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !ApprovalCounterRegistry.DISBURSEMENT_REQUESTS.equals(counter.key()) || isChecker(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isChecker(final AppUser user) {
|
|
||||||
if (user.hasAnyPermission("ALL_FUNCTIONS")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return user.getRoles().stream().map(r -> r.getName() == null ? "" : r.getName().toLowerCase().trim())
|
|
||||||
.anyMatch(n -> n.contains("manager") || n.contains("head cashier"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-145
@@ -1,145 +0,0 @@
|
|||||||
package org.apache.fineract.portfolio.approvals.service;
|
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
|
||||||
import jakarta.annotation.PreDestroy;
|
|
||||||
import jakarta.ws.rs.sse.OutboundSseEvent;
|
|
||||||
import jakarta.ws.rs.sse.Sse;
|
|
||||||
import jakarta.ws.rs.sse.SseEventSink;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCountsData;
|
|
||||||
import org.apache.fineract.portfolio.approvals.data.ApprovalCountsPlan;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps the Approvals badge live. A screen listens on a Server-Sent Events stream and is written to the moment its
|
|
||||||
* numbers change, so it neither polls nor reloads.
|
|
||||||
*
|
|
||||||
* One shared check, every {@value #CHECK_MILLIS} ms, serves every open screen, and screens counted the same way share
|
|
||||||
* its query. A comment line every {@value #PING_MILLIS} ms keeps proxies from closing an idle connection and tells the
|
|
||||||
* server when a screen has gone. A stream ends after {@value #LIFETIME_MILLIS} ms so the screen reconnects with a clean
|
|
||||||
* connection, which bounds what a browser accumulates on one.
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ApprovalCountsStreamService {
|
|
||||||
|
|
||||||
/** How often the open screens are looked at: the longest a change waits before it is pushed. */
|
|
||||||
private static final long CHECK_MILLIS = 1_000L;
|
|
||||||
|
|
||||||
private static final long PING_MILLIS = 15_000L;
|
|
||||||
|
|
||||||
private static final long LIFETIME_MILLIS = 300_000L;
|
|
||||||
|
|
||||||
/** A screen listening, and the version of the numbers it was last sent. */
|
|
||||||
private static final class Listener {
|
|
||||||
|
|
||||||
private final ApprovalCountsPlan plan;
|
|
||||||
private final SseEventSink sink;
|
|
||||||
private final Sse sse;
|
|
||||||
private final long expiresAt;
|
|
||||||
private volatile String version;
|
|
||||||
private volatile long lastSent;
|
|
||||||
|
|
||||||
private Listener(final ApprovalCountsPlan plan, final SseEventSink sink, final Sse sse, final long expiresAt) {
|
|
||||||
this.plan = plan;
|
|
||||||
this.sink = sink;
|
|
||||||
this.sse = sse;
|
|
||||||
this.expiresAt = expiresAt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ApprovalCountsReadPlatformService readService;
|
|
||||||
private final Set<Listener> listeners = ConcurrentHashMap.newKeySet();
|
|
||||||
private final ScheduledExecutorService ticker = Executors.newSingleThreadScheduledExecutor(runnable -> {
|
|
||||||
final Thread thread = new Thread(runnable, "approval-counts-stream");
|
|
||||||
thread.setDaemon(true);
|
|
||||||
return thread;
|
|
||||||
});
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
void startTicker() {
|
|
||||||
this.ticker.scheduleWithFixedDelay(this::tick, CHECK_MILLIS, CHECK_MILLIS, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
void stopTicker() {
|
|
||||||
this.ticker.shutdownNow();
|
|
||||||
this.listeners.forEach(this::close);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sends the numbers as they stand, then keeps the screen informed. Call it on the request thread. */
|
|
||||||
public void open(final ApprovalCountsPlan plan, final SseEventSink sink, final Sse sse) {
|
|
||||||
final Listener listener = new Listener(plan, sink, sse, System.currentTimeMillis() + LIFETIME_MILLIS);
|
|
||||||
try {
|
|
||||||
publish(listener, this.readService.retrieve(plan));
|
|
||||||
} catch (final RuntimeException e) {
|
|
||||||
sink.close();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
this.listeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Looks at the numbers for every open screen and writes to the ones whose numbers moved. */
|
|
||||||
private void tick() {
|
|
||||||
if (this.listeners.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final long now = System.currentTimeMillis();
|
|
||||||
final Map<String, ApprovalCountsData> computed = new HashMap<>();
|
|
||||||
for (final Listener listener : this.listeners) {
|
|
||||||
try {
|
|
||||||
if (listener.sink.isClosed() || now >= listener.expiresAt) {
|
|
||||||
close(listener);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ApprovalCountsData data = computed.get(listener.plan.group());
|
|
||||||
if (data == null) {
|
|
||||||
ThreadLocalContextUtil.setTenant(listener.plan.tenant());
|
|
||||||
data = this.readService.retrieve(listener.plan);
|
|
||||||
computed.put(listener.plan.group(), data);
|
|
||||||
}
|
|
||||||
if (!data.version().equals(listener.version)) {
|
|
||||||
publish(listener, data);
|
|
||||||
} else if (now - listener.lastSent >= PING_MILLIS) {
|
|
||||||
listener.lastSent = now;
|
|
||||||
send(listener, listener.sse.newEventBuilder().comment("ping").build());
|
|
||||||
}
|
|
||||||
} catch (final RuntimeException e) {
|
|
||||||
close(listener);
|
|
||||||
} finally {
|
|
||||||
ThreadLocalContextUtil.clearTenant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void publish(final Listener listener, final ApprovalCountsData data) {
|
|
||||||
listener.version = data.version();
|
|
||||||
listener.lastSent = System.currentTimeMillis();
|
|
||||||
send(listener, listener.sse.newEvent("counts", data.toJson()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void send(final Listener listener, final OutboundSseEvent event) {
|
|
||||||
listener.sink.send(event).whenComplete((sent, failure) -> {
|
|
||||||
if (failure != null) {
|
|
||||||
close(listener);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close(final Listener listener) {
|
|
||||||
this.listeners.remove(listener);
|
|
||||||
try {
|
|
||||||
listener.sink.close();
|
|
||||||
} catch (final RuntimeException ignored) {
|
|
||||||
// The screen is already gone.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+17
@@ -46,6 +46,8 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
|||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.portfolio.cashreport.service.VaultPositionService;
|
import org.apache.fineract.portfolio.cashreport.service.VaultPositionService;
|
||||||
import org.apache.fineract.portfolio.denominationmaster.service.CountedCashLedger;
|
import org.apache.fineract.portfolio.denominationmaster.service.CountedCashLedger;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentFdInterestService;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.savings.dailyinterest.SavingsDailyInterestOrchestrator;
|
import org.apache.fineract.portfolio.savings.dailyinterest.SavingsDailyInterestOrchestrator;
|
||||||
import org.apache.fineract.portfolio.savings.dailyinterest.SavingsInterestPostingReportService;
|
import org.apache.fineract.portfolio.savings.dailyinterest.SavingsInterestPostingReportService;
|
||||||
import org.apache.fineract.portfolio.savings.fdinterest.FixedDepositInterestPostingOrchestrator;
|
import org.apache.fineract.portfolio.savings.fdinterest.FixedDepositInterestPostingOrchestrator;
|
||||||
@@ -95,8 +97,16 @@ public class BranchDayRegisterApiResource {
|
|||||||
private final SavingsDailyInterestOrchestrator savingsDailyInterestOrchestrator;
|
private final SavingsDailyInterestOrchestrator savingsDailyInterestOrchestrator;
|
||||||
private final FixedDepositInterestPostingReportService fdInterestPostingReportService;
|
private final FixedDepositInterestPostingReportService fdInterestPostingReportService;
|
||||||
private final FixedDepositInterestPostingOrchestrator fdInterestPostingOrchestrator;
|
private final FixedDepositInterestPostingOrchestrator fdInterestPostingOrchestrator;
|
||||||
|
private final InvestmentPurchaseReadPlatformService investmentPurchaseReadPlatformService;
|
||||||
|
private final InvestmentFdInterestService investmentFdInterestService;
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
|
private boolean isHeadOffice(final Long officeId) {
|
||||||
|
final Integer topLevel = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM m_office WHERE id = ? AND parent_id IS NULL",
|
||||||
|
Integer.class, officeId);
|
||||||
|
return topLevel != null && topLevel > 0;
|
||||||
|
}
|
||||||
|
|
||||||
// ── GET /v1/branchday ── the register itself ──────────────────────────────
|
// ── GET /v1/branchday ── the register itself ──────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,6 +270,13 @@ public class BranchDayRegisterApiResource {
|
|||||||
final Map<String, Object> fdInterest = fdInterestPostingReportService.dayCloseInterestView(officeId, businessDate);
|
final Map<String, Object> fdInterest = fdInterestPostingReportService.dayCloseInterestView(officeId, businessDate);
|
||||||
result.put("fdInterestPending", fdInterest.get("pending"));
|
result.put("fdInterestPending", fdInterest.get("pending"));
|
||||||
result.put("fdInterestDue", fdInterest.get("due"));
|
result.put("fdInterestDue", fdInterest.get("due"));
|
||||||
|
// Investment interest vouchers (coupon / provision) whose date has come but which are not posted yet.
|
||||||
|
// A reminder only: it never stops the close (Jitendra Sir). Investments are booked at Head Office, so
|
||||||
|
// only the Head Office close shows it.
|
||||||
|
result.put("investmentInterestPending",
|
||||||
|
isHeadOffice(officeId) ? investmentPurchaseReadPlatformService.retrieveDueInterest(0) : List.of());
|
||||||
|
// The same for FDs placed with other banks: interest vouchers and maturities due. A reminder only.
|
||||||
|
result.put("investmentFdPending", isHeadOffice(officeId) ? investmentFdInterestService.due(0) : List.of());
|
||||||
|
|
||||||
return gson.toJson(result);
|
return gson.toJson(result);
|
||||||
}
|
}
|
||||||
|
|||||||
-7
@@ -56,7 +56,6 @@ 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.client.domain.KycExpiryStage;
|
import org.apache.fineract.portfolio.client.domain.KycExpiryStage;
|
||||||
import org.apache.fineract.portfolio.client.service.KycRestrictionGuard;
|
import org.apache.fineract.portfolio.client.service.KycRestrictionGuard;
|
||||||
import org.apache.fineract.portfolio.savings.service.FixedDepositFundingValidator;
|
|
||||||
import org.apache.fineract.portfolio.voucher.service.VoucherLine;
|
import org.apache.fineract.portfolio.voucher.service.VoucherLine;
|
||||||
import org.apache.fineract.portfolio.voucher.service.VoucherService;
|
import org.apache.fineract.portfolio.voucher.service.VoucherService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -99,7 +98,6 @@ public class CashierDepositRequestApiResource {
|
|||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
private final VoucherService voucherService;
|
private final VoucherService voucherService;
|
||||||
private final FixedDepositFundingValidator fixedDepositFundingValidator;
|
|
||||||
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||||
|
|
||||||
// ── POST /v1/cashier/deposit-requests ── Cashier submits new deposit request ──
|
// ── POST /v1/cashier/deposit-requests ── Cashier submits new deposit request ──
|
||||||
@@ -157,11 +155,6 @@ public class CashierDepositRequestApiResource {
|
|||||||
throw apiError(400, "Amount must be greater than zero");
|
throw apiError(400, "Amount must be greater than zero");
|
||||||
}
|
}
|
||||||
kycRestrictionGuard.check(savingsAccountId, KycRestrictionGuard.Operation.CREDIT);
|
kycRestrictionGuard.check(savingsAccountId, KycRestrictionGuard.Operation.CREDIT);
|
||||||
// An FD is funded once, with exactly its principal. Told here at the counter rather than when the
|
|
||||||
// manager approves; the FD deposit command checks the same again at posting.
|
|
||||||
if (!isGlType && "FIXED_DEPOSIT".equals(resolveDepositAccountCategory(savingsAccountId, accountNo))) {
|
|
||||||
fixedDepositFundingValidator.validateFirstFunding(savingsAccountId, amount);
|
|
||||||
}
|
|
||||||
validateDenominationTotal(denominationData, amount);
|
validateDenominationTotal(denominationData, amount);
|
||||||
validateDdLimit(ddFaceValue);
|
validateDdLimit(ddFaceValue);
|
||||||
|
|
||||||
|
|||||||
+1
-14
@@ -20,7 +20,6 @@ import org.apache.fineract.portfolio.cashreport.service.CashierRoleHelper;
|
|||||||
import org.apache.fineract.portfolio.cashreport.service.VaultPositionService;
|
import org.apache.fineract.portfolio.cashreport.service.VaultPositionService;
|
||||||
import org.apache.fineract.portfolio.denominationmaster.service.CountedCashLedger;
|
import org.apache.fineract.portfolio.denominationmaster.service.CountedCashLedger;
|
||||||
import org.apache.fineract.portfolio.denominationmaster.service.DenominationBreakdownWriter;
|
import org.apache.fineract.portfolio.denominationmaster.service.DenominationBreakdownWriter;
|
||||||
import org.apache.fineract.portfolio.voucher.service.VoucherService;
|
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
@@ -37,7 +36,6 @@ public class CashReportApiResource {
|
|||||||
private final DenominationBreakdownWriter denominationBreakdownWriter;
|
private final DenominationBreakdownWriter denominationBreakdownWriter;
|
||||||
private final CashierRoleHelper cashierRoleHelper;
|
private final CashierRoleHelper cashierRoleHelper;
|
||||||
private final VaultPositionService vaultPositionService;
|
private final VaultPositionService vaultPositionService;
|
||||||
private final VoucherService voucherService;
|
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -331,18 +329,7 @@ public class CashReportApiResource {
|
|||||||
BigDecimal priorCreditTotal = BigDecimal.ZERO;
|
BigDecimal priorCreditTotal = BigDecimal.ZERO;
|
||||||
BigDecimal priorDebitTotal = BigDecimal.ZERO;
|
BigDecimal priorDebitTotal = BigDecimal.ZERO;
|
||||||
|
|
||||||
// Where each voucher stands after a correction: a revoked voucher is listed, marked, but its money now sits on
|
|
||||||
// the voucher that replaced it, so it must not be counted in the day's totals a second time.
|
|
||||||
final Map<String, Map<String, Object>> marks = voucherService.voucherMarks(
|
|
||||||
rows.stream().map(r -> (String) r.get("voucherNo")).toList());
|
|
||||||
|
|
||||||
for (final Map<String, Object> row : rows) {
|
for (final Map<String, Object> row : rows) {
|
||||||
final Map<String, Object> mark = marks.get((String) row.get("voucherNo"));
|
|
||||||
row.put("voucherStatus", mark == null ? "ACTIVE" : mark.get("voucherStatus"));
|
|
||||||
row.put("revokedByVoucherNo", mark == null ? null : mark.get("revokedByVoucherNo"));
|
|
||||||
row.put("replacesVoucherNo", mark == null ? null : mark.get("replacesVoucherNo"));
|
|
||||||
row.put("correctionNote", mark == null ? null : mark.get("correctionNote"));
|
|
||||||
final boolean revoked = "REVOKED".equals(row.get("voucherStatus"));
|
|
||||||
if (sideFilter != null && !sideFilter.equals(row.get("side"))) {
|
if (sideFilter != null && !sideFilter.equals(row.get("side"))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -363,7 +350,7 @@ public class CashReportApiResource {
|
|||||||
// was already earned, and the day it belongs to has been counted and signed off. Adding
|
// was already earned, and the day it belongs to has been counted and signed off. Adding
|
||||||
// it to this day's totals would make them disagree with the ledger for either date.
|
// it to this day's totals would make them disagree with the ledger for either date.
|
||||||
final boolean priorDay = "SYSTEM_PRIOR".equals(row.get("section"));
|
final boolean priorDay = "SYSTEM_PRIOR".equals(row.get("section"));
|
||||||
if (!priorDay && !revoked && ("APPROVED".equalsIgnoreCase(rowStatus) || "COMPLETED".equalsIgnoreCase(rowStatus)
|
if (!priorDay && ("APPROVED".equalsIgnoreCase(rowStatus) || "COMPLETED".equalsIgnoreCase(rowStatus)
|
||||||
|| "WITHDRAWN".equalsIgnoreCase(rowStatus) || "CLOSED".equalsIgnoreCase(rowStatus))) {
|
|| "WITHDRAWN".equalsIgnoreCase(rowStatus) || "CLOSED".equalsIgnoreCase(rowStatus))) {
|
||||||
final BigDecimal amount = (BigDecimal) row.get("amount");
|
final BigDecimal amount = (BigDecimal) row.get("amount");
|
||||||
if (amount != null) {
|
if (amount != null) {
|
||||||
|
|||||||
+10
-186
@@ -57,23 +57,9 @@ public class CashTransferApiResource {
|
|||||||
private final VaultPositionService vaultPositionService;
|
private final VaultPositionService vaultPositionService;
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
/**
|
|
||||||
* Cash no longer leaves the vault on one person's say-so: the allocation is raised as a request (see
|
|
||||||
* {@link #submitRequest}) and only a different person's approval posts it. The posting itself is
|
|
||||||
* {@link #postTransfer}.
|
|
||||||
*/
|
|
||||||
@POST
|
@POST
|
||||||
public String create(String body) {
|
public String create(String body) {
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.required",
|
this.context.authenticatedUser().validateHasPermissionTo("CREATE_CASHTRANSFER");
|
||||||
"A cash transfer is raised as a request and posted when a manager approves it.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a transfer and, unless {@code dryRun}, posts it: the header and its denominations, then the GL entry.
|
|
||||||
* A dry run stops after every check has passed, which is what a request is held to when it is raised and again
|
|
||||||
* when it is approved, since the vault can change in between.
|
|
||||||
*/
|
|
||||||
private String postTransfer(final String body, final Long performedBy, final String performedByUsername, final boolean dryRun) {
|
|
||||||
JsonObject json = JsonParser.parseString(body).getAsJsonObject();
|
JsonObject json = JsonParser.parseString(body).getAsJsonObject();
|
||||||
|
|
||||||
final LocalDate transferDate = json.has("transferDate") && !json.get("transferDate").isJsonNull()
|
final LocalDate transferDate = json.has("transferDate") && !json.get("transferDate").isJsonNull()
|
||||||
@@ -116,9 +102,6 @@ public class CashTransferApiResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateVaultHasSufficientCash(fromTellerId, total, denominations, transferDate);
|
validateVaultHasSufficientCash(fromTellerId, total, denominations, transferDate);
|
||||||
if (dryRun) {
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
|
|
||||||
CashTransfer header = new CashTransfer();
|
CashTransfer header = new CashTransfer();
|
||||||
header.setTransferDate(transferDate);
|
header.setTransferDate(transferDate);
|
||||||
@@ -129,9 +112,15 @@ public class CashTransferApiResource {
|
|||||||
header.setTransferAmount(total);
|
header.setTransferAmount(total);
|
||||||
header.setNote(note);
|
header.setNote(note);
|
||||||
header.setCreatedAt(LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()));
|
header.setCreatedAt(LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()));
|
||||||
// Recorded against the person who raised the request, not the one who approved it.
|
try {
|
||||||
header.setPerformedBy(performedBy);
|
AppUser user = this.context.authenticatedUser();
|
||||||
header.setPerformedByUsername(performedByUsername);
|
if (user != null) {
|
||||||
|
header.setPerformedBy(user.getId());
|
||||||
|
header.setPerformedByUsername(user.getUsername());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// resolving the current user must never block recording the transfer
|
||||||
|
}
|
||||||
|
|
||||||
// Save header + details in one committed transaction, then GL dispatch separately.
|
// Save header + details in one committed transaction, then GL dispatch separately.
|
||||||
// This prevents EclipseLink from flushing pending detail rows inside Fineract's
|
// This prevents EclipseLink from flushing pending detail rows inside Fineract's
|
||||||
@@ -170,171 +159,6 @@ public class CashTransferApiResource {
|
|||||||
return gson.toJson(result);
|
return gson.toJson(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Maker-checker: the allocation is a request until a different person approves it ────────────
|
|
||||||
|
|
||||||
/** The maker raises the allocation. Nothing moves yet: every check runs, and the request waits for a checker. */
|
|
||||||
@POST
|
|
||||||
@Path("requests")
|
|
||||||
public String submitRequest(final String body) {
|
|
||||||
final AppUser user = this.context.authenticatedUser();
|
|
||||||
user.validateHasPermissionTo("CREATE_CASHTRANSFER");
|
|
||||||
postTransfer(body, user.getId(), user.getUsername(), true);
|
|
||||||
|
|
||||||
// The same request, from the same person, is not raised twice while the first is still waiting: a second tap or
|
|
||||||
// a second click would otherwise release the same cash twice once both were approved.
|
|
||||||
final Integer identical = jdbcTemplate.queryForObject(
|
|
||||||
"SELECT COUNT(*) FROM m_cash_transfer_request WHERE status = ? AND maker_id = ? AND request_body = ?", Integer.class,
|
|
||||||
REQUEST_PENDING, user.getId(), body);
|
|
||||||
if (identical != null && identical > 0) {
|
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.duplicate",
|
|
||||||
"An identical cash transfer request from you is already waiting for approval.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final JsonObject json = JsonParser.parseString(body).getAsJsonObject();
|
|
||||||
final JsonArray denominations = json.getAsJsonArray("denominations");
|
|
||||||
final Long id = jdbcTemplate.queryForObject("""
|
|
||||||
INSERT INTO m_cash_transfer_request
|
|
||||||
(transfer_date, from_teller_id, to_cashier_id, from_party, to_party, note, total_amount, request_body,
|
|
||||||
status, maker_id, maker_name, maker_at)
|
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
|
||||||
RETURNING id
|
|
||||||
""", Long.class, textOrNull(json, "transferDate") == null ? null : java.sql.Date.valueOf(textOrNull(json, "transferDate")),
|
|
||||||
json.get("fromTellerId").getAsLong(), json.get("toCashierId").getAsLong(), textOrNull(json, "fromParty"),
|
|
||||||
textOrNull(json, "toParty"), textOrNull(json, "note"), sumTotal(denominations), body, REQUEST_PENDING, user.getId(),
|
|
||||||
user.getDisplayName(), java.sql.Timestamp.from(java.time.Instant.now()));
|
|
||||||
final JsonObject result = new JsonObject();
|
|
||||||
result.addProperty("resourceId", id);
|
|
||||||
result.addProperty("status", REQUEST_PENDING);
|
|
||||||
return gson.toJson(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Requests by status (comma separated); {@code mine} narrows them to the ones the caller raised. */
|
|
||||||
@GET
|
|
||||||
@Path("requests")
|
|
||||||
public String listRequests(@QueryParam("status") final String status,
|
|
||||||
@QueryParam("mine") @jakarta.ws.rs.DefaultValue("false") final boolean mine) {
|
|
||||||
final AppUser user = this.context.authenticatedUser();
|
|
||||||
final StringBuilder sql = new StringBuilder("SELECT * FROM m_cash_transfer_request WHERE 1=1");
|
|
||||||
final List<Object> params = new java.util.ArrayList<>();
|
|
||||||
if (status != null && !status.isBlank()) {
|
|
||||||
final String[] statuses = status.split(",");
|
|
||||||
sql.append(" AND status IN (").append("?,".repeat(statuses.length), 0, statuses.length * 2 - 1).append(")");
|
|
||||||
for (final String s : statuses) {
|
|
||||||
params.add(s.trim().toUpperCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mine) {
|
|
||||||
sql.append(" AND maker_id = ?");
|
|
||||||
params.add(user.getId());
|
|
||||||
}
|
|
||||||
sql.append(" ORDER BY id DESC LIMIT 200");
|
|
||||||
final List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql.toString(), params.toArray());
|
|
||||||
for (final Map<String, Object> row : rows) {
|
|
||||||
// The notes asked for, so the approver sees what they are releasing without opening anything.
|
|
||||||
final Object requestBody = row.remove("request_body");
|
|
||||||
row.put("denominations", requestBody == null ? new JsonArray()
|
|
||||||
: JsonParser.parseString(requestBody.toString()).getAsJsonObject().getAsJsonArray("denominations"));
|
|
||||||
}
|
|
||||||
return gson.toJson(rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The checker releases the cash: re-checks the vault, then posts the transfer exactly as a direct one used to. */
|
|
||||||
@POST
|
|
||||||
@Path("requests/{id}/approve")
|
|
||||||
public String approveRequest(@PathParam("id") final Long id) {
|
|
||||||
final AppUser user = this.context.authenticatedUser();
|
|
||||||
requireChecker(user);
|
|
||||||
final Map<String, Object> r = loadRequest(id);
|
|
||||||
requirePending(r, "approved");
|
|
||||||
final Long makerId = r.get("maker_id") instanceof Number n ? n.longValue() : null;
|
|
||||||
if (makerId != null && makerId.equals(user.getId())) {
|
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.same.user",
|
|
||||||
"Maker-checker: you cannot approve a cash transfer you raised yourself. Ask another manager.");
|
|
||||||
}
|
|
||||||
final String posted = postTransfer((String) r.get("request_body"), makerId, (String) r.get("maker_name"), false);
|
|
||||||
final JsonObject postedJson = JsonParser.parseString(posted).getAsJsonObject();
|
|
||||||
final Long transferId = postedJson.has("resourceId") ? postedJson.get("resourceId").getAsLong() : null;
|
|
||||||
|
|
||||||
final int updated = jdbcTemplate.update("""
|
|
||||||
UPDATE m_cash_transfer_request SET status = ?, checker_id = ?, checker_name = ?, checker_at = ?, cash_transfer_id = ?
|
|
||||||
WHERE id = ? AND status = ?
|
|
||||||
""", REQUEST_APPROVED, user.getId(), user.getDisplayName(), java.sql.Timestamp.from(java.time.Instant.now()),
|
|
||||||
transferId, id, REQUEST_PENDING);
|
|
||||||
if (updated == 0) {
|
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.actioned",
|
|
||||||
"This request was already actioned by another manager.");
|
|
||||||
}
|
|
||||||
final JsonObject result = new JsonObject();
|
|
||||||
result.addProperty("resourceId", id);
|
|
||||||
result.addProperty("status", REQUEST_APPROVED);
|
|
||||||
if (transferId != null) {
|
|
||||||
result.addProperty("cashTransferId", transferId);
|
|
||||||
}
|
|
||||||
if (postedJson.has("glWarning")) {
|
|
||||||
result.add("glWarning", postedJson.get("glWarning"));
|
|
||||||
}
|
|
||||||
return gson.toJson(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("requests/{id}/reject")
|
|
||||||
public String rejectRequest(@PathParam("id") final Long id, final String body) {
|
|
||||||
final AppUser user = this.context.authenticatedUser();
|
|
||||||
requireChecker(user);
|
|
||||||
final Map<String, Object> r = loadRequest(id);
|
|
||||||
requirePending(r, "rejected");
|
|
||||||
final JsonObject json = body == null || body.isBlank() ? new JsonObject() : JsonParser.parseString(body).getAsJsonObject();
|
|
||||||
final String reason = textOrNull(json, "reason");
|
|
||||||
if (reason == null || reason.isBlank()) {
|
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.reason", "A reason is required to reject.");
|
|
||||||
}
|
|
||||||
jdbcTemplate.update("""
|
|
||||||
UPDATE m_cash_transfer_request SET status = ?, checker_id = ?, checker_name = ?, checker_at = ?, rejection_reason = ?
|
|
||||||
WHERE id = ? AND status = ?
|
|
||||||
""", REQUEST_REJECTED, user.getId(), user.getDisplayName(), java.sql.Timestamp.from(java.time.Instant.now()),
|
|
||||||
reason.trim(), id, REQUEST_PENDING);
|
|
||||||
final JsonObject result = new JsonObject();
|
|
||||||
result.addProperty("resourceId", id);
|
|
||||||
result.addProperty("status", REQUEST_REJECTED);
|
|
||||||
return gson.toJson(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String REQUEST_PENDING = "PENDING";
|
|
||||||
private static final String REQUEST_APPROVED = "APPROVED";
|
|
||||||
private static final String REQUEST_REJECTED = "REJECTED";
|
|
||||||
|
|
||||||
private Map<String, Object> loadRequest(final Long id) {
|
|
||||||
final List<Map<String, Object>> rows = jdbcTemplate.queryForList("SELECT * FROM m_cash_transfer_request WHERE id = ?", id);
|
|
||||||
if (rows.isEmpty()) {
|
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.not.found", "Cash transfer request " + id + " not found.");
|
|
||||||
}
|
|
||||||
return rows.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requirePending(final Map<String, Object> request, final String action) {
|
|
||||||
if (!REQUEST_PENDING.equals(request.get("status"))) {
|
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.status",
|
|
||||||
"Only PENDING requests can be " + action + ". Current status: " + request.get("status"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checker authorization, the same rule the withdrawal and deposit request registers use. */
|
|
||||||
private void requireChecker(final AppUser user) {
|
|
||||||
if (user.hasAnyPermission("ALL_FUNCTIONS")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final boolean isChecker = user.getRoles().stream().map(role -> role.getName() == null ? "" : role.getName().toLowerCase().trim())
|
|
||||||
.anyMatch(n -> n.contains("manager") || n.contains("head cashier"));
|
|
||||||
if (!isChecker) {
|
|
||||||
throw new PlatformDataIntegrityException("error.msg.cashtransfer.request.checker",
|
|
||||||
"Only a Bank Manager (or Head Cashier) can approve or reject a cash transfer.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String textOrNull(final JsonObject json, final String key) {
|
|
||||||
return json.has(key) && !json.get(key).isJsonNull() ? json.get(key).getAsString() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
public String getAll(
|
public String getAll(
|
||||||
@QueryParam("offset") @jakarta.ws.rs.DefaultValue("0") int offset,
|
@QueryParam("offset") @jakarta.ws.rs.DefaultValue("0") int offset,
|
||||||
|
|||||||
-81
@@ -69,8 +69,6 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
|||||||
import org.apache.fineract.portfolio.PortfolioProductType;
|
import org.apache.fineract.portfolio.PortfolioProductType;
|
||||||
import org.apache.fineract.portfolio.voucher.service.VoucherLine;
|
import org.apache.fineract.portfolio.voucher.service.VoucherLine;
|
||||||
import org.apache.fineract.portfolio.client.service.KycRestrictionGuard;
|
import org.apache.fineract.portfolio.client.service.KycRestrictionGuard;
|
||||||
import org.apache.fineract.portfolio.savings.service.DepositAccountWritePlatformService;
|
|
||||||
import org.apache.fineract.portfolio.savings.service.FixedDepositFundingValidator;
|
|
||||||
import org.apache.fineract.portfolio.voucher.service.VoucherService;
|
import org.apache.fineract.portfolio.voucher.service.VoucherService;
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
@@ -92,8 +90,6 @@ public class ClerkDisbursementRequestApiResource {
|
|||||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
private final FineractRequestContextHolder fineractRequestContextHolder;
|
private final FineractRequestContextHolder fineractRequestContextHolder;
|
||||||
private final VoucherService voucherService;
|
private final VoucherService voucherService;
|
||||||
private final FixedDepositFundingValidator fixedDepositFundingValidator;
|
|
||||||
private final DepositAccountWritePlatformService depositAccountWritePlatformService;
|
|
||||||
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||||
|
|
||||||
// ── POST /v1/clerk/disbursement-requests ── Clerk submits new request ────────
|
// ── POST /v1/clerk/disbursement-requests ── Clerk submits new request ────────
|
||||||
@@ -206,9 +202,6 @@ public class ClerkDisbursementRequestApiResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixed deposit legs: credit-only, funded once with exactly the principal, from the customer's own account.
|
|
||||||
validateFixedDepositLegs(creditEntries, requestType);
|
|
||||||
|
|
||||||
// Validate balance when both DR and CR entries present (collection mode)
|
// Validate balance when both DR and CR entries present (collection mode)
|
||||||
if (totalDr.compareTo(BigDecimal.ZERO) > 0 && totalCr.compareTo(BigDecimal.ZERO) > 0
|
if (totalDr.compareTo(BigDecimal.ZERO) > 0 && totalCr.compareTo(BigDecimal.ZERO) > 0
|
||||||
&& totalDr.subtract(totalCr).abs().compareTo(new BigDecimal("0.01")) > 0) {
|
&& totalDr.subtract(totalCr).abs().compareTo(new BigDecimal("0.01")) > 0) {
|
||||||
@@ -498,10 +491,6 @@ public class ClerkDisbursementRequestApiResource {
|
|||||||
rejectRecurringDepositLeg(entry, entryType);
|
rejectRecurringDepositLeg(entry, entryType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Re-checked here, not only at submit: another request (or a counter deposit) may have funded the
|
|
||||||
// same FD while this one waited, and a refusal now still leaves the request PENDING.
|
|
||||||
final Set<Long> fundedFixedDeposits = validateFixedDepositLegs(creditEntries,
|
|
||||||
record.get("request_type") != null ? (String) record.get("request_type") : "DISBURSEMENT");
|
|
||||||
|
|
||||||
// Atomic PENDING → PROCESSING lock: prevents two managers from concurrently
|
// Atomic PENDING → PROCESSING lock: prevents two managers from concurrently
|
||||||
// posting duplicate Fineract transactions for the same request.
|
// posting duplicate Fineract transactions for the same request.
|
||||||
@@ -784,12 +773,6 @@ public class ClerkDisbursementRequestApiResource {
|
|||||||
}
|
}
|
||||||
} // end else (disbursement/collection mode)
|
} // end else (disbursement/collection mode)
|
||||||
|
|
||||||
// A funded FD's maturity date and amount were projected off its principal with no money in it;
|
|
||||||
// the direct credit above does not recompute them, so do it now that the money has landed.
|
|
||||||
for (final Long fdId : fundedFixedDeposits) {
|
|
||||||
depositAccountWritePlatformService.recalculateFixedDepositMaturity(fdId);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Compensate: undo all posted Fineract transactions in reverse order (best-effort).
|
// Compensate: undo all posted Fineract transactions in reverse order (best-effort).
|
||||||
// This restores account balances even when a later entry fails mid-processing.
|
// This restores account balances even when a later entry fails mid-processing.
|
||||||
@@ -1240,70 +1223,6 @@ public class ClerkDisbursementRequestApiResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fixed deposit legs of a clerk request, recognised by id (deposit_type_enum) rather than the entry's label.
|
|
||||||
* <ul>
|
|
||||||
* <li>An FD is never debited here: money leaves an FD only through its maturity or premature closure. The
|
|
||||||
* direct book debit used by Bank Transfer and Collection would otherwise bypass the FD's own refusal.</li>
|
|
||||||
* <li>An FD credit is its first and only funding, for exactly its principal, and an FD appears once.</li>
|
|
||||||
* <li>The FDs funded by one request belong to one customer, and every Savings/Current account debited to fund
|
|
||||||
* them belongs to that customer (or is held jointly) and can pay its amount. A GL source (Disbursement) is
|
|
||||||
* the bank's own money and has no customer to compare.</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @return the ids of the FDs this request funds, for the maturity recalculation after posting
|
|
||||||
*/
|
|
||||||
private Set<Long> validateFixedDepositLegs(final JsonArray entries, final String requestType) {
|
|
||||||
final Set<Long> fdIds = new LinkedHashSet<>();
|
|
||||||
Long firstFdId = null;
|
|
||||||
Long fdClientId = null;
|
|
||||||
for (final JsonElement el : entries) {
|
|
||||||
final JsonObject entry = el.getAsJsonObject();
|
|
||||||
final String type = entry.has("accountType") ? entry.get("accountType").getAsString() : "SAVINGS";
|
|
||||||
if ("GL".equals(type) || "CURRENT".equals(type) || !entry.has("savingsAccountId")
|
|
||||||
|| entry.get("savingsAccountId").isJsonNull()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final Long accountId = entry.get("savingsAccountId").getAsLong();
|
|
||||||
if (!fixedDepositFundingValidator.isFixedDeposit(accountId)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String acNo = entry.has("accountNo") ? entry.get("accountNo").getAsString() : String.valueOf(accountId);
|
|
||||||
if ("Dr".equals(entry.has("dbCr") ? entry.get("dbCr").getAsString() : "Cr")) {
|
|
||||||
throw apiError(422, "Fixed deposit " + acNo + " cannot be debited. Money leaves a fixed deposit only"
|
|
||||||
+ " through its maturity or premature closure.");
|
|
||||||
}
|
|
||||||
if (!fdIds.add(accountId)) {
|
|
||||||
throw apiError(422, "Fixed deposit " + acNo + " appears more than once. A fixed deposit is funded once,"
|
|
||||||
+ " with its full principal in one entry.");
|
|
||||||
}
|
|
||||||
final BigDecimal amount = parseBigDecimal(entry.has("amount") ? entry.get("amount").getAsString() : null);
|
|
||||||
final Long clientId = fixedDepositFundingValidator.validateFirstFunding(accountId, amount);
|
|
||||||
if (firstFdId == null) {
|
|
||||||
firstFdId = accountId;
|
|
||||||
fdClientId = clientId;
|
|
||||||
} else if (!Objects.equals(fdClientId, clientId)) {
|
|
||||||
throw apiError(422, "One " + voucherTypeLabelFor(requestType) + " can fund fixed deposits of one customer"
|
|
||||||
+ " only. Raise a separate request for each customer.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fdIds.isEmpty()) {
|
|
||||||
return fdIds;
|
|
||||||
}
|
|
||||||
for (final JsonElement el : entries) {
|
|
||||||
final JsonObject entry = el.getAsJsonObject();
|
|
||||||
final String type = entry.has("accountType") ? entry.get("accountType").getAsString() : "SAVINGS";
|
|
||||||
if (!"Dr".equals(entry.has("dbCr") ? entry.get("dbCr").getAsString() : "Cr") || "GL".equals(type)
|
|
||||||
|| !entry.has("savingsAccountId") || entry.get("savingsAccountId").isJsonNull()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final BigDecimal amount = parseBigDecimal(entry.has("amount") ? entry.get("amount").getAsString() : null);
|
|
||||||
fixedDepositFundingValidator.validateFundingSource(firstFdId, fdClientId, entry.get("savingsAccountId").getAsLong(),
|
|
||||||
"CURRENT".equals(type), amount);
|
|
||||||
}
|
|
||||||
return fdIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Savings status_enum for an account still open for business (Fineract's SavingsAccountStatusType.ACTIVE). */
|
/** Savings status_enum for an account still open for business (Fineract's SavingsAccountStatusType.ACTIVE). */
|
||||||
private static final int SAVINGS_STATUS_ACTIVE = 300;
|
private static final int SAVINGS_STATUS_ACTIVE = 300;
|
||||||
|
|
||||||
|
|||||||
+13
-163
@@ -48,7 +48,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
|
||||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
@@ -57,7 +56,6 @@ 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.client.domain.KycExpiryStage;
|
import org.apache.fineract.portfolio.client.domain.KycExpiryStage;
|
||||||
import org.apache.fineract.portfolio.client.service.KycRestrictionGuard;
|
import org.apache.fineract.portfolio.client.service.KycRestrictionGuard;
|
||||||
import org.apache.fineract.portfolio.savings.service.FixedDepositFundingValidator;
|
|
||||||
import org.apache.fineract.portfolio.voucher.service.VoucherLine;
|
import org.apache.fineract.portfolio.voucher.service.VoucherLine;
|
||||||
import org.apache.fineract.portfolio.voucher.service.VoucherService;
|
import org.apache.fineract.portfolio.voucher.service.VoucherService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -86,7 +84,6 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
private final VoucherService voucherService;
|
private final VoucherService voucherService;
|
||||||
private final FixedDepositFundingValidator fixedDepositFundingValidator;
|
|
||||||
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||||
|
|
||||||
// ── POST /v1/clerk/withdrawal-requests ── Clerk submits new request ─────────
|
// ── POST /v1/clerk/withdrawal-requests ── Clerk submits new request ─────────
|
||||||
@@ -113,28 +110,13 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
final String tokNo = fromJsonHelper.extractStringNamed("tokNo", root);
|
final String tokNo = fromJsonHelper.extractStringNamed("tokNo", root);
|
||||||
final String txnDate = fromJsonHelper.extractStringNamed("txnDate", root);
|
final String txnDate = fromJsonHelper.extractStringNamed("txnDate", root);
|
||||||
final String denominationData = fromJsonHelper.extractStringNamed("denominationData", root);
|
final String denominationData = fromJsonHelper.extractStringNamed("denominationData", root);
|
||||||
// A GL withdrawal slip (e.g. Sundry Creditors paid out in cash) carries the GL and the cash GL it is
|
|
||||||
// paid from instead of a savings account; it is approved and disbursed exactly like any other slip.
|
|
||||||
final boolean isGl = "GL".equalsIgnoreCase(accountType);
|
|
||||||
final Long glAccountId = fromJsonHelper.extractLongNamed("glAccountId", root);
|
|
||||||
final Long contraGlId = fromJsonHelper.extractLongNamed("contraGlId", root);
|
|
||||||
final Long officeId = fromJsonHelper.extractLongNamed("officeId", root);
|
|
||||||
final String currencyCode = fromJsonHelper.extractStringNamed("currencyCode", root);
|
|
||||||
|
|
||||||
if (accountNo == null || accountNo.isBlank()) {
|
if (accountNo == null || accountNo.isBlank()) {
|
||||||
throw apiError(400, "Account number is required");
|
throw apiError(400, "Account number is required");
|
||||||
}
|
}
|
||||||
if (isGl) {
|
if (savingsAccountId == null) {
|
||||||
validateGlWithdrawalAccounts(glAccountId, contraGlId);
|
|
||||||
} else if (savingsAccountId == null) {
|
|
||||||
throw apiError(400, "Savings account id is required for a withdrawal request");
|
throw apiError(400, "Savings account id is required for a withdrawal request");
|
||||||
}
|
}
|
||||||
// Money leaves a fixed deposit only through its maturity or premature closure, never a withdrawal slip.
|
|
||||||
// Checked by id: the slip's own label is not trusted, and a CURRENT id belongs to m_current_account.
|
|
||||||
if (!isGl && !"CURRENT".equalsIgnoreCase(accountType) && fixedDepositFundingValidator.isFixedDeposit(savingsAccountId)) {
|
|
||||||
throw apiError(422, "Account " + accountNo + " is a fixed deposit and cannot be withdrawn from."
|
|
||||||
+ " Money leaves a fixed deposit only through its maturity or premature closure.");
|
|
||||||
}
|
|
||||||
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
throw apiError(400, "Amount must be greater than zero");
|
throw apiError(400, "Amount must be greater than zero");
|
||||||
}
|
}
|
||||||
@@ -142,9 +124,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
throw apiError(400, "Token number (TOK. NO.) is required");
|
throw apiError(400, "Token number (TOK. NO.) is required");
|
||||||
}
|
}
|
||||||
validateTokenUniqueToday(tokNo, null);
|
validateTokenUniqueToday(tokNo, null);
|
||||||
if (!isGl) {
|
|
||||||
kycRestrictionGuard.check(savingsAccountId, KycRestrictionGuard.Operation.DEBIT);
|
kycRestrictionGuard.check(savingsAccountId, KycRestrictionGuard.Operation.DEBIT);
|
||||||
}
|
|
||||||
|
|
||||||
final String activityLog = buildActivityLog(null, "SUBMITTED", currentUser.getUsername(), null);
|
final String activityLog = buildActivityLog(null, "SUBMITTED", currentUser.getUsername(), null);
|
||||||
|
|
||||||
@@ -152,9 +132,8 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
INSERT INTO m_clerk_withdrawal_request
|
INSERT INTO m_clerk_withdrawal_request
|
||||||
(account_no, account_type, savings_account_id, client_id, client_name, amount,
|
(account_no, account_type, savings_account_id, client_id, client_name, amount,
|
||||||
payment_type_id, payment_type_name, description, cheque_no, bank_name, tok_no, txn_date,
|
payment_type_id, payment_type_name, description, cheque_no, bank_name, tok_no, txn_date,
|
||||||
status, clerk_id, clerk_name, clerk_username, denomination_data, activity_log, submitted_at,
|
status, clerk_id, clerk_name, clerk_username, denomination_data, activity_log, submitted_at)
|
||||||
gl_account_id, contra_gl_id, office_id, currency_code)
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
||||||
RETURNING id
|
RETURNING id
|
||||||
""",
|
""",
|
||||||
Long.class,
|
Long.class,
|
||||||
@@ -163,9 +142,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
txnDate != null ? Date.valueOf(txnDate) : null,
|
txnDate != null ? Date.valueOf(txnDate) : null,
|
||||||
"PENDING",
|
"PENDING",
|
||||||
currentUser.getId(), currentUser.getDisplayName(), currentUser.getUsername(),
|
currentUser.getId(), currentUser.getDisplayName(), currentUser.getUsername(),
|
||||||
denominationData, activityLog, Timestamp.from(Instant.now()),
|
denominationData, activityLog, Timestamp.from(Instant.now())
|
||||||
isGl ? glAccountId : null, isGl ? contraGlId : null, isGl ? (officeId != null ? officeId : 1L) : null,
|
|
||||||
isGl ? (currencyCode != null ? currencyCode : "INR") : null
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Globally unique voucher number from the central m_voucher register
|
// Globally unique voucher number from the central m_voucher register
|
||||||
@@ -221,8 +198,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
+ " txn_date, status, clerk_id, clerk_name, clerk_username,"
|
+ " txn_date, status, clerk_id, clerk_name, clerk_username,"
|
||||||
+ " manager_id, manager_name, manager_username, rejection_reason, txn_id,"
|
+ " manager_id, manager_name, manager_username, rejection_reason, txn_id,"
|
||||||
+ " denomination_data, activity_log, submitted_at, actioned_at,"
|
+ " denomination_data, activity_log, submitted_at, actioned_at,"
|
||||||
+ " cashier_id, disbursed_by, disbursed_at, voucher_no,"
|
+ " cashier_id, disbursed_by, disbursed_at, voucher_no"
|
||||||
+ " gl_account_id, contra_gl_id, office_id, currency_code"
|
|
||||||
+ " FROM m_clerk_withdrawal_request " + where
|
+ " FROM m_clerk_withdrawal_request " + where
|
||||||
+ " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
+ " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
||||||
pageParams.toArray());
|
pageParams.toArray());
|
||||||
@@ -276,8 +252,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
payment_type_name, description, tok_no, txn_date, status,
|
payment_type_name, description, tok_no, txn_date, status,
|
||||||
clerk_name, manager_name, manager_username, rejection_reason, txn_id,
|
clerk_name, manager_name, manager_username, rejection_reason, txn_id,
|
||||||
submitted_at, actioned_at,
|
submitted_at, actioned_at,
|
||||||
cashier_id, disbursed_by, disbursed_at, voucher_no,
|
cashier_id, disbursed_by, disbursed_at, voucher_no
|
||||||
gl_account_id, contra_gl_id, office_id, currency_code
|
|
||||||
FROM m_clerk_withdrawal_request
|
FROM m_clerk_withdrawal_request
|
||||||
""" + where + " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
""" + where + " ORDER BY submitted_at DESC LIMIT ? OFFSET ?",
|
||||||
pageParams.toArray()
|
pageParams.toArray()
|
||||||
@@ -301,8 +276,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
txn_date, status, clerk_id, clerk_name, clerk_username,
|
txn_date, status, clerk_id, clerk_name, clerk_username,
|
||||||
manager_id, manager_name, manager_username, rejection_reason, txn_id,
|
manager_id, manager_name, manager_username, rejection_reason, txn_id,
|
||||||
denomination_data, activity_log, submitted_at, actioned_at,
|
denomination_data, activity_log, submitted_at, actioned_at,
|
||||||
cashier_id, disbursed_by, disbursed_at, voucher_no,
|
cashier_id, disbursed_by, disbursed_at, voucher_no
|
||||||
gl_account_id, contra_gl_id, office_id, currency_code
|
|
||||||
FROM m_clerk_withdrawal_request WHERE id = ?
|
FROM m_clerk_withdrawal_request WHERE id = ?
|
||||||
""", id);
|
""", id);
|
||||||
if (rows.isEmpty()) {
|
if (rows.isEmpty()) {
|
||||||
@@ -321,8 +295,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
final List<Map<String, Object>> rows = jdbcTemplate.queryForList("""
|
final List<Map<String, Object>> rows = jdbcTemplate.queryForList("""
|
||||||
SELECT id, account_no, account_type, savings_account_id, client_id, client_name,
|
SELECT id, account_no, account_type, savings_account_id, client_id, client_name,
|
||||||
amount, payment_type_id, payment_type_name, description, tok_no, status,
|
amount, payment_type_id, payment_type_name, description, tok_no, status,
|
||||||
clerk_id, clerk_name, manager_id, manager_name, submitted_at, actioned_at, voucher_no,
|
clerk_id, clerk_name, manager_id, manager_name, submitted_at, actioned_at, voucher_no
|
||||||
gl_account_id, contra_gl_id, office_id, currency_code
|
|
||||||
FROM m_clerk_withdrawal_request
|
FROM m_clerk_withdrawal_request
|
||||||
WHERE tok_no = ? AND CAST(submitted_at AS DATE) = CURRENT_DATE
|
WHERE tok_no = ? AND CAST(submitted_at AS DATE) = CURRENT_DATE
|
||||||
ORDER BY submitted_at DESC LIMIT 1
|
ORDER BY submitted_at DESC LIMIT 1
|
||||||
@@ -482,16 +455,13 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
"Maker-checker: you cannot approve a withdrawal request you submitted yourself. Ask another manager to review it.");
|
"Maker-checker: you cannot approve a withdrawal request you submitted yourself. Ask another manager to review it.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean isGlRequest = "GL".equalsIgnoreCase((String) record.get("account_type"));
|
|
||||||
final Long savingsAccountId = record.get("savings_account_id") != null
|
final Long savingsAccountId = record.get("savings_account_id") != null
|
||||||
? ((Number) record.get("savings_account_id")).longValue() : null;
|
? ((Number) record.get("savings_account_id")).longValue() : null;
|
||||||
// Checked again on approval, not only on submission: a request can sit in the queue for days,
|
// Checked again on approval, not only on submission: a request can sit in the queue for days,
|
||||||
// and the account can cross the dormancy threshold while it waits. The savings withdrawal
|
// and the account can cross the dormancy threshold while it waits. The savings withdrawal
|
||||||
// below refuses a dormant account anyway, but that failure would surface to the manager as a
|
// below refuses a dormant account anyway, but that failure would surface to the manager as a
|
||||||
// rejected transaction after the approval was recorded; this says why, before anything moves.
|
// rejected transaction after the approval was recorded; this says why, before anything moves.
|
||||||
if (!isGlRequest) {
|
|
||||||
kycRestrictionGuard.check(savingsAccountId, KycRestrictionGuard.Operation.DEBIT);
|
kycRestrictionGuard.check(savingsAccountId, KycRestrictionGuard.Operation.DEBIT);
|
||||||
}
|
|
||||||
final BigDecimal amount = new BigDecimal(record.get("amount").toString());
|
final BigDecimal amount = new BigDecimal(record.get("amount").toString());
|
||||||
final Object ptId = record.get("payment_type_id");
|
final Object ptId = record.get("payment_type_id");
|
||||||
final Object txnDateObj = record.get("txn_date");
|
final Object txnDateObj = record.get("txn_date");
|
||||||
@@ -499,11 +469,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
final String description = (String) record.get("description");
|
final String description = (String) record.get("description");
|
||||||
|
|
||||||
String txnId = null;
|
String txnId = null;
|
||||||
if (isGlRequest) {
|
if (savingsAccountId != null) {
|
||||||
// Dr the GL account, Cr the branch cash GL -- posted here, at approval, like the savings withdrawal.
|
|
||||||
validateGlWithdrawalAccounts(toLong(record.get("gl_account_id")), toLong(record.get("contra_gl_id")));
|
|
||||||
txnId = executeGlWithdrawalJournal(record, amount, ptId, txnDateStr, description);
|
|
||||||
} else if (savingsAccountId != null) {
|
|
||||||
final boolean isCurrentAccount = isCurrentAccountById(savingsAccountId, (String) record.get("account_no"));
|
final boolean isCurrentAccount = isCurrentAccountById(savingsAccountId, (String) record.get("account_no"));
|
||||||
txnId = isCurrentAccount
|
txnId = isCurrentAccount
|
||||||
? executeCurrentAccountWithdrawal(savingsAccountId, amount, ptId, txnDateStr, description)
|
? executeCurrentAccountWithdrawal(savingsAccountId, amount, ptId, txnDateStr, description)
|
||||||
@@ -511,18 +477,14 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Link voucher to the created savings txn and stamp voucher_no on it
|
// Link voucher to the created savings txn and stamp voucher_no on it
|
||||||
if (txnId != null && !isGlRequest) {
|
if (txnId != null) {
|
||||||
voucherService.linkSavingsTransaction(
|
voucherService.linkSavingsTransaction(
|
||||||
VoucherService.REF_CLERK_WITHDRAWAL_REQUEST, id, Long.valueOf(txnId));
|
VoucherService.REF_CLERK_WITHDRAWAL_REQUEST, id, Long.valueOf(txnId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The sides of the slip. A withdrawal is money leaving the member, so their account is
|
// The sides of the slip. A withdrawal is money leaving the member, so their account is
|
||||||
// debited and the till that pays it out is credited.
|
// debited and the till that pays it out is credited.
|
||||||
if (isGlRequest) {
|
|
||||||
addGlWithdrawalVoucherLines(record, amount);
|
|
||||||
} else {
|
|
||||||
addWithdrawalVoucherLines(record, savingsAccountId, amount, txnId);
|
addWithdrawalVoucherLines(record, savingsAccountId, amount, txnId);
|
||||||
}
|
|
||||||
|
|
||||||
final String updatedLog = buildActivityLog(
|
final String updatedLog = buildActivityLog(
|
||||||
(String) record.get("activity_log"), "APPROVED", currentUser.getUsername(), null
|
(String) record.get("activity_log"), "APPROVED", currentUser.getUsername(), null
|
||||||
@@ -727,8 +689,8 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
if (denomMap == null || denomMap.size() == 0) return;
|
if (denomMap == null || denomMap.size() == 0) return;
|
||||||
|
|
||||||
final Object txnIdObj = record.get("txn_id");
|
final Object txnIdObj = record.get("txn_id");
|
||||||
final Long txnId = txnIdObj != null ? parseTxnKey(txnIdObj.toString()) : null;
|
final Long txnId = txnIdObj != null ? Long.valueOf(txnIdObj.toString()) : null;
|
||||||
if (txnId == null) return; // no txn linked — cannot record denomination
|
if (txnId == null) return; // no savings txn linked — cannot record denomination
|
||||||
|
|
||||||
final Object saObj = record.get("savings_account_id");
|
final Object saObj = record.get("savings_account_id");
|
||||||
final Long savingsAccountId = saObj != null ? Long.valueOf(saObj.toString()) : null;
|
final Long savingsAccountId = saObj != null ? Long.valueOf(saObj.toString()) : null;
|
||||||
@@ -831,117 +793,6 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
return rows.isEmpty() ? null : rows.get(0);
|
return rows.isEmpty() ? null : rows.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Long toLong(final Object value) {
|
|
||||||
return value instanceof Number n ? n.longValue() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A savings transaction id is numeric; a GL journal entry's is a hex string. Both are reduced to one
|
|
||||||
* Long the same way CashierGlDenominationApiResource does, so a GL withdrawal's notes group under
|
|
||||||
* the id of its own journal entry in the cashier's position and closing report.
|
|
||||||
*/
|
|
||||||
private Long parseTxnKey(final String raw) {
|
|
||||||
if (raw == null || raw.isBlank()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return Long.valueOf(raw);
|
|
||||||
} catch (final NumberFormatException ignored) {
|
|
||||||
try {
|
|
||||||
final String hex = raw.length() > 15 ? raw.substring(raw.length() - 15) : raw;
|
|
||||||
return Long.parseUnsignedLong(hex, 16);
|
|
||||||
} catch (final NumberFormatException ignored2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A GL withdrawal pays cash out of a GL account: the account must not itself be a cash GL, and the
|
|
||||||
* GL it is paid from must be one of the branch's mapped cash accounts. */
|
|
||||||
private void validateGlWithdrawalAccounts(final Long glAccountId, final Long contraGlId) {
|
|
||||||
if (glAccountId == null || contraGlId == null) {
|
|
||||||
throw apiError(400, "glAccountId and contraGlId are required for a GL withdrawal request");
|
|
||||||
}
|
|
||||||
final List<Long> cashGlIds = jdbcTemplate.queryForList(
|
|
||||||
"SELECT gl_account_id FROM acc_gl_financial_activity_account WHERE financial_activity_type IN (?, ?)",
|
|
||||||
Long.class, FinancialActivity.CASH_AT_MAINVAULT.getValue(), FinancialActivity.CASH_AT_TELLER.getValue());
|
|
||||||
if (!cashGlIds.contains(contraGlId)) {
|
|
||||||
throw apiError(400, "A GL withdrawal must be paid from the branch cash vault GL.");
|
|
||||||
}
|
|
||||||
if (cashGlIds.contains(glAccountId)) {
|
|
||||||
throw apiError(400, "The cash vault itself cannot be the GL account of a withdrawal slip.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Dr the GL account, Cr the cash GL, through the journal-entry command (not the HTTP API, which is
|
|
||||||
* switched off for manual postings). Returns the journal entry's transaction id. */
|
|
||||||
private String executeGlWithdrawalJournal(final Map<?, ?> record, final BigDecimal amount,
|
|
||||||
final Object paymentTypeId, final String txnDateStr, final String note) {
|
|
||||||
final String today = LocalDate.now(DateUtils.getDateTimeZoneOfTenant()).format(DateTimeFormatter.ofPattern("dd MMMM yyyy"));
|
|
||||||
final String date = txnDateStr != null && !txnDateStr.isBlank() ? formatForFineract(txnDateStr) : today;
|
|
||||||
|
|
||||||
final JsonObject debit = new JsonObject();
|
|
||||||
debit.addProperty("glAccountId", toLong(record.get("gl_account_id")));
|
|
||||||
debit.addProperty("amount", amount);
|
|
||||||
final JsonObject credit = new JsonObject();
|
|
||||||
credit.addProperty("glAccountId", toLong(record.get("contra_gl_id")));
|
|
||||||
credit.addProperty("amount", amount);
|
|
||||||
final JsonArray debits = new JsonArray();
|
|
||||||
debits.add(debit);
|
|
||||||
final JsonArray credits = new JsonArray();
|
|
||||||
credits.add(credit);
|
|
||||||
|
|
||||||
final Object office = record.get("office_id");
|
|
||||||
final Object currency = record.get("currency_code");
|
|
||||||
final JsonObject payload = new JsonObject();
|
|
||||||
payload.addProperty("officeId", office instanceof Number n ? n.longValue() : 1L);
|
|
||||||
payload.addProperty("transactionDate", date);
|
|
||||||
payload.addProperty("dateFormat", "dd MMMM yyyy");
|
|
||||||
payload.addProperty("locale", "en");
|
|
||||||
payload.addProperty("currencyCode", currency != null ? currency.toString() : "INR");
|
|
||||||
if (note != null && !note.isBlank()) {
|
|
||||||
payload.addProperty("comments", note);
|
|
||||||
}
|
|
||||||
if (paymentTypeId != null) {
|
|
||||||
payload.addProperty("paymentTypeId", ((Number) paymentTypeId).longValue());
|
|
||||||
}
|
|
||||||
payload.add("credits", credits);
|
|
||||||
payload.add("debits", debits);
|
|
||||||
|
|
||||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createJournalEntry().withJson(payload.toString()).build();
|
|
||||||
// The request raised its own voucher at submission; the journal command must not raise a second.
|
|
||||||
final CommandProcessingResult result = voucherService
|
|
||||||
.withOwnVoucher(() -> commandsSourceWritePlatformService.logCommandSource(commandRequest));
|
|
||||||
return result.getTransactionId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The two sides of an approved GL withdrawal on its voucher: the GL account is debited, the cash GL credited. */
|
|
||||||
private void addGlWithdrawalVoucherLines(final Map<?, ?> record, final BigDecimal amount) {
|
|
||||||
final String voucherNo = (String) record.get("voucher_no");
|
|
||||||
if (voucherNo == null || voucherNo.isBlank()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final Long glId = toLong(record.get("gl_account_id"));
|
|
||||||
final Long contraId = toLong(record.get("contra_gl_id"));
|
|
||||||
final Map<String, Object> gl = glAccountForVoucher(glId);
|
|
||||||
final Map<String, Object> contra = glAccountForVoucher(contraId);
|
|
||||||
voucherService.addLines(voucherNo, List.of(
|
|
||||||
VoucherLine.debitGl(glId, (String) gl.get("name"), (String) gl.get("gl_code"), amount),
|
|
||||||
VoucherLine.creditGl(contraId, (String) contra.getOrDefault("name", "Cash in Hand"), (String) contra.get("gl_code"), amount)));
|
|
||||||
} catch (final RuntimeException e) {
|
|
||||||
LOG.error("Could not record voucher lines for GL withdrawal request voucher {}", voucherNo, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> glAccountForVoucher(final Long glAccountId) {
|
|
||||||
if (glAccountId == null) {
|
|
||||||
return Map.of();
|
|
||||||
}
|
|
||||||
return jdbcTemplate.queryForList("SELECT name, gl_code FROM acc_gl_account WHERE id = ?", glAccountId).stream().findFirst()
|
|
||||||
.orElse(Map.of());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<?, ?> loadRecord(final Long id) {
|
private Map<?, ?> loadRecord(final Long id) {
|
||||||
final List<Map<String, Object>> rows = jdbcTemplate.queryForList("""
|
final List<Map<String, Object>> rows = jdbcTemplate.queryForList("""
|
||||||
SELECT id, account_no, account_type, savings_account_id, client_id, client_name,
|
SELECT id, account_no, account_type, savings_account_id, client_id, client_name,
|
||||||
@@ -949,8 +800,7 @@ public class ClerkWithdrawalRequestApiResource {
|
|||||||
txn_date, status, clerk_id, clerk_name, clerk_username,
|
txn_date, status, clerk_id, clerk_name, clerk_username,
|
||||||
manager_id, manager_name, manager_username, rejection_reason, txn_id,
|
manager_id, manager_name, manager_username, rejection_reason, txn_id,
|
||||||
denomination_data, activity_log, submitted_at, actioned_at,
|
denomination_data, activity_log, submitted_at, actioned_at,
|
||||||
cashier_id, disbursed_by, disbursed_at, voucher_no,
|
cashier_id, disbursed_by, disbursed_at, voucher_no
|
||||||
gl_account_id, contra_gl_id, office_id, currency_code
|
|
||||||
FROM m_clerk_withdrawal_request WHERE id = ?
|
FROM m_clerk_withdrawal_request WHERE id = ?
|
||||||
""", id);
|
""", id);
|
||||||
if (rows.isEmpty()) {
|
if (rows.isEmpty()) {
|
||||||
|
|||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentAmortisationData;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentAmortisationDueData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentAmortisationService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/amortisation")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Premium Amortisation", description = "Premium of HTM holdings amortised straight-line to maturity every 31 March.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentAmortisationApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentAmortisationService amortisationService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{purchaseId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Amortisation schedule of a holding (06 Amortize Entry)")
|
||||||
|
public InvestmentAmortisationData schedule(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||||
|
return this.amortisationService.schedule(purchaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("due")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Holdings due for amortisation on a 31 March (07 Amortize Yearly Process)", description = "fyEnd dd-MM-yyyy")
|
||||||
|
public List<InvestmentAmortisationDueData> due(@QueryParam("fyEnd") @Parameter(description = "dd-MM-yyyy") final String fyEnd) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||||
|
return this.amortisationService.due(LocalDate.parse(fyEnd.trim(), DateTimeFormatter.ofPattern("dd-MM-yyyy")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Post amortisation for a 31 March", description = "With purchaseId: that holding (Amortize Entry); without: "
|
||||||
|
+ "every holding due (Amortize Yearly Process). One JV per holding: Dr 500604 / Cr premium GL.")
|
||||||
|
public CommandProcessingResult amortise(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().amortiseInvestmentPremium().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for the Treasury Investment masters (Bank / Party Master and Script Master), modelled on the bank's existing
|
||||||
|
* CBS Investment screens.
|
||||||
|
*/
|
||||||
|
public final class InvestmentApiConstants {
|
||||||
|
|
||||||
|
private InvestmentApiConstants() {}
|
||||||
|
|
||||||
|
public static final String ENTITY_PARTY = "INVESTMENTPARTY";
|
||||||
|
public static final String ENTITY_SCRIPT = "INVESTMENTSCRIPT";
|
||||||
|
public static final String RESOURCE_PARTY = "investmentparty";
|
||||||
|
public static final String RESOURCE_SCRIPT = "investmentscript";
|
||||||
|
|
||||||
|
/** Instrument ("Bond Type") an investment party / script belongs to. */
|
||||||
|
public static final Set<String> BOND_TYPES = Set.of("GOVT_SECURITY", "NON_SLR", "BOND", "MUTUAL_FUND", "FD", "SHARE");
|
||||||
|
|
||||||
|
/** Bond types that carry coupon terms (ISIN, interest rate, issue date are mandatory for these). */
|
||||||
|
public static final Set<String> DEBT_BOND_TYPES = Set.of("GOVT_SECURITY", "NON_SLR", "BOND");
|
||||||
|
|
||||||
|
/** SLR classification on the Script Master ("GOI" = Central Government). */
|
||||||
|
public static final Set<String> SLR_TYPES = Set.of("GOI", "STATE_GOVT", "TREASURY", "NON_SLR");
|
||||||
|
|
||||||
|
/** Script Code prefix per bond type, e.g. G00001 for the first Govt Security. */
|
||||||
|
public static final Map<String, String> SCRIPT_CODE_PREFIX = Map.of("GOVT_SECURITY", "G", "NON_SLR", "N", "BOND", "B", "MUTUAL_FUND",
|
||||||
|
"M", "FD", "F", "SHARE", "S");
|
||||||
|
|
||||||
|
public static final String PARTY_CODE_PREFIX = "PTY-";
|
||||||
|
|
||||||
|
// Common parameters
|
||||||
|
public static final String BOND_TYPE = "bondType";
|
||||||
|
public static final String GL_ACCOUNT_ID = "glAccountId";
|
||||||
|
public static final String INTEREST_GL_ACCOUNT_ID = "interestGlAccountId";
|
||||||
|
public static final String ADDRESS1 = "address1";
|
||||||
|
public static final String ADDRESS2 = "address2";
|
||||||
|
public static final String ADDRESS3 = "address3";
|
||||||
|
public static final String CITY = "city";
|
||||||
|
public static final String PINCODE = "pincode";
|
||||||
|
public static final String PHONE = "phone";
|
||||||
|
public static final String FAX = "fax";
|
||||||
|
public static final String ACTIVE = "active";
|
||||||
|
public static final String LOCALE = "locale";
|
||||||
|
public static final String DATE_FORMAT = "dateFormat";
|
||||||
|
|
||||||
|
// Party parameters
|
||||||
|
public static final String PARTY_NAME = "partyName";
|
||||||
|
public static final String BANK_TYPE = "bankType";
|
||||||
|
public static final String RBI_CODE = "rbiCode";
|
||||||
|
public static final String EXPOSURE_LIMIT = "limit";
|
||||||
|
public static final String LEI_NO = "leiNo";
|
||||||
|
public static final String CP_BANK_NAME = "cpBankName";
|
||||||
|
public static final String CP_BRANCH_NAME = "cpBranchName";
|
||||||
|
public static final String CP_ACCOUNT_NO = "cpAccountNo";
|
||||||
|
public static final String CP_IFSC = "cpIfsc";
|
||||||
|
|
||||||
|
// Script parameters
|
||||||
|
public static final String SLR_TYPE = "slrType";
|
||||||
|
public static final String SCRIPT_DESC = "scriptDesc";
|
||||||
|
public static final String ISIN = "isin";
|
||||||
|
public static final String FACE_VALUE_PER_UNIT = "faceValuePerUnit";
|
||||||
|
public static final String INT_RATE = "intRate";
|
||||||
|
public static final String ISSUE_DATE = "issueDate";
|
||||||
|
public static final String MATURITY_DATE = "maturityDate";
|
||||||
|
public static final String COMPANY_NAME = "companyName";
|
||||||
|
public static final String CORP_CODE = "corpCode";
|
||||||
|
|
||||||
|
public static final Set<String> PARTY_PARAMS = Set.of(BOND_TYPE, PARTY_NAME, ADDRESS1, ADDRESS2, ADDRESS3, CITY, PINCODE, PHONE, FAX,
|
||||||
|
BANK_TYPE, GL_ACCOUNT_ID, INTEREST_GL_ACCOUNT_ID, RBI_CODE, EXPOSURE_LIMIT, LEI_NO, CP_BANK_NAME, CP_BRANCH_NAME, CP_ACCOUNT_NO, CP_IFSC, ACTIVE,
|
||||||
|
LOCALE);
|
||||||
|
|
||||||
|
public static final Set<String> SCRIPT_PARAMS = Set.of(BOND_TYPE, SLR_TYPE, SCRIPT_DESC, GL_ACCOUNT_ID, ISIN, FACE_VALUE_PER_UNIT,
|
||||||
|
INT_RATE, ISSUE_DATE, MATURITY_DATE, COMPANY_NAME, ADDRESS1, ADDRESS2, ADDRESS3, CITY, PINCODE, PHONE, FAX, CORP_CODE,
|
||||||
|
ACTIVE, LOCALE, DATE_FORMAT);
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.api;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Constants for the Daily Script Master (market rate of a script for a day). */
|
||||||
|
public final class InvestmentDailyRateApiConstants {
|
||||||
|
|
||||||
|
private InvestmentDailyRateApiConstants() {}
|
||||||
|
|
||||||
|
public static final String ENTITY = "INVESTMENTDAILYRATE";
|
||||||
|
public static final String RESOURCE = "investmentdailyrate";
|
||||||
|
|
||||||
|
public static final String SCRIPT_ID = "scriptId";
|
||||||
|
public static final String RATE_DATE = "rateDate";
|
||||||
|
public static final String OPEN_RATE = "openRate";
|
||||||
|
public static final String HIGH_RATE = "highRate";
|
||||||
|
public static final String LOW_RATE = "lowRate";
|
||||||
|
public static final String CLOSE_RATE = "closeRate";
|
||||||
|
public static final String LOCALE = "locale";
|
||||||
|
public static final String DATE_FORMAT = "dateFormat";
|
||||||
|
|
||||||
|
public static final Set<String> PARAMS = Set.of(SCRIPT_ID, RATE_DATE, OPEN_RATE, HIGH_RATE, LOW_RATE, CLOSE_RATE, LOCALE, DATE_FORMAT);
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.DELETE;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentDailyRateData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentDailyRateReadPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/daily-rates")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Daily Script Master", description = "Market rate of each script for a day; Purchase Entry takes its rate from here.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentDailyRateApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentDailyRateReadPlatformService readPlatformService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List daily script rates, newest first")
|
||||||
|
public List<InvestmentDailyRateData> retrieveAll(@QueryParam("scriptId") @Parameter(description = "scriptId") final Long scriptId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentDailyRateApiConstants.ENTITY);
|
||||||
|
return this.readPlatformService.retrieveAll(scriptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("latest")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Newest rate of a script on or before a date", description = "onDate (dd-MM-yyyy) defaults to the business "
|
||||||
|
+ "date. Returns no content when no rate has been entered for the script.")
|
||||||
|
public InvestmentDailyRateData retrieveLatest(@QueryParam("scriptId") @Parameter(description = "scriptId") final Long scriptId,
|
||||||
|
@QueryParam("onDate") @Parameter(description = "dd-MM-yyyy") final String onDate) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentDailyRateApiConstants.ENTITY);
|
||||||
|
final LocalDate on = StringUtils.isBlank(onDate) ? DateUtils.getBusinessLocalDate()
|
||||||
|
: LocalDate.parse(onDate.trim(), DateTimeFormatter.ofPattern("dd-MM-yyyy"));
|
||||||
|
return this.readPlatformService.retrieveLatest(scriptId, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Save a daily script rate")
|
||||||
|
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentDailyRate().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{rateId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Drop a daily script rate")
|
||||||
|
public CommandProcessingResult delete(@PathParam("rateId") @Parameter(description = "rateId") final Long rateId) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentDailyRate(rateId).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Fixed deposits placed with other banks. */
|
||||||
|
public final class InvestmentFdApiConstants {
|
||||||
|
|
||||||
|
private InvestmentFdApiConstants() {}
|
||||||
|
|
||||||
|
public static final String ENTITY = "INVESTMENTFD";
|
||||||
|
public static final String BOND_TYPE_FD = "FD";
|
||||||
|
|
||||||
|
public static final String STATUS_ACTIVE = "ACTIVE";
|
||||||
|
public static final String STATUS_MATURED = "MATURED";
|
||||||
|
public static final String STATUS_PREMATURE = "PREMATURE";
|
||||||
|
public static final String STATUS_RENEWED = "RENEWED";
|
||||||
|
|
||||||
|
/** When the interest is paid: all at maturity (the usual case), or every half year / year. */
|
||||||
|
public static final String PAYOUT_AT_MATURITY = "MATURITY";
|
||||||
|
public static final String PAYOUT_HALF_YEARLY = "HALF_YEARLY";
|
||||||
|
public static final String PAYOUT_YEARLY = "YEARLY";
|
||||||
|
public static final Set<String> PAYOUTS = Set.of(PAYOUT_AT_MATURITY, PAYOUT_HALF_YEARLY, PAYOUT_YEARLY);
|
||||||
|
|
||||||
|
/** How the maturity amount is worked out, to compare with the bank's figure (Jitendra Sir). */
|
||||||
|
public static final String CALC_SIMPLE = "SIMPLE";
|
||||||
|
public static final String CALC_MONTHLY = "MONTHLY";
|
||||||
|
public static final String CALC_QUARTERLY = "QUARTERLY";
|
||||||
|
public static final String CALC_HALF_YEARLY = "HALF_YEARLY";
|
||||||
|
public static final String CALC_YEARLY = "YEARLY";
|
||||||
|
public static final Set<String> CALC_TYPES = Set.of(CALC_SIMPLE, CALC_MONTHLY, CALC_QUARTERLY, CALC_HALF_YEARLY, CALC_YEARLY);
|
||||||
|
|
||||||
|
/** RBI: deposits with other banks within 20% of total deposits as on the previous 31 March, and within 5% with one bank. */
|
||||||
|
public static final BigDecimal GROSS_LIMIT_PERCENT = BigDecimal.valueOf(20);
|
||||||
|
public static final BigDecimal BANK_LIMIT_PERCENT = BigDecimal.valueOf(5);
|
||||||
|
/** Current accounts with banks also count towards the 20% inter-bank exposure. */
|
||||||
|
public static final String BANK_BALANCE_HEADER_GL_CODE = "100200";
|
||||||
|
/** Deposits of the bank, for the suggested total-deposits figure: everything under the deposit header, plus RD. */
|
||||||
|
public static final String DEPOSIT_HEADER_GL_CODE = "200100";
|
||||||
|
public static final String RD_DEPOSIT_GL_CODE = "200201";
|
||||||
|
|
||||||
|
public static final String PARTY_ID = "partyId";
|
||||||
|
public static final String CERT_NO = "certNo";
|
||||||
|
public static final String CERT_DATE = "certDate";
|
||||||
|
public static final String DEAL_DATE = "dealDate";
|
||||||
|
public static final String AMOUNT = "amount";
|
||||||
|
public static final String INT_RATE = "intRate";
|
||||||
|
public static final String MATURITY_DATE = "maturityDate";
|
||||||
|
public static final String PAYOUT = "payout";
|
||||||
|
public static final String CALC_TYPE = "calcType";
|
||||||
|
public static final String BANK_GL_ACCOUNT_ID = "bankGlAccountId";
|
||||||
|
public static final String REMARKS = "remarks";
|
||||||
|
public static final String AS_ON_DATE = "asOnDate";
|
||||||
|
public static final String TOTAL_DEPOSITS = "totalDeposits";
|
||||||
|
public static final String LOCALE = "locale";
|
||||||
|
public static final String DATE_FORMAT = "dateFormat";
|
||||||
|
|
||||||
|
public static final String CLOSURE_MATURITY = "MATURITY";
|
||||||
|
public static final String CLOSURE_PREMATURE = "PREMATURE";
|
||||||
|
public static final String CLOSURE_RENEWAL = "RENEWAL";
|
||||||
|
public static final String LINE_PROVISION = "PROVISION";
|
||||||
|
public static final String LINE_PAYOUT = "PAYOUT";
|
||||||
|
public static final String INTEREST_RECEIVABLE_GL_CODE = "100603";
|
||||||
|
/** Tax the other bank deducts from the interest, recoverable from the tax department. */
|
||||||
|
public static final String TDS_RECEIVABLE_GL_CODE = "100602";
|
||||||
|
|
||||||
|
public static final String CLOSE_DATE = "closeDate";
|
||||||
|
public static final String RECEIVED_AMOUNT = "receivedAmount";
|
||||||
|
public static final String TDS = "tds";
|
||||||
|
public static final String PENAL_AMOUNT = "penalAmount";
|
||||||
|
public static final String PRINCIPAL_BANK_GL_ACCOUNT_ID = "principalBankGlAccountId";
|
||||||
|
public static final String INTEREST_BANK_GL_ACCOUNT_ID = "interestBankGlAccountId";
|
||||||
|
public static final String NEW_CERT_NO = "newCertNo";
|
||||||
|
public static final String NEW_CERT_DATE = "newCertDate";
|
||||||
|
public static final String NEW_INT_RATE = "newIntRate";
|
||||||
|
public static final String NEW_MATURITY_DATE = "newMaturityDate";
|
||||||
|
public static final String NEW_PAYOUT = "newPayout";
|
||||||
|
public static final String NEW_CALC_TYPE = "newCalcType";
|
||||||
|
public static final String MATURITY_AMOUNT = "maturityAmount";
|
||||||
|
public static final String RENEWAL_AMOUNT = "renewalAmount";
|
||||||
|
public static final String DIFF_BANK_GL_ACCOUNT_ID = "diffBankGlAccountId";
|
||||||
|
|
||||||
|
public static final String LINE_MATURITY = "MATURITY";
|
||||||
|
public static final String LINE_TYPE = "lineType";
|
||||||
|
public static final String INT_DATE = "intDate";
|
||||||
|
/** Reminders show lines due within this many days (as for Govt securities). */
|
||||||
|
public static final int DUE_WITHIN_DAYS = 2;
|
||||||
|
public static final Set<String> INTEREST_PARAMS = Set.of(LINE_TYPE, INT_DATE, RECEIVED_AMOUNT, TDS, BANK_GL_ACCOUNT_ID, LOCALE,
|
||||||
|
DATE_FORMAT);
|
||||||
|
|
||||||
|
public static final Set<String> MATURE_PARAMS = Set.of(RECEIVED_AMOUNT, TDS, PRINCIPAL_BANK_GL_ACCOUNT_ID, INTEREST_BANK_GL_ACCOUNT_ID,
|
||||||
|
REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
public static final Set<String> PREMATURE_PARAMS = Set.of(CLOSE_DATE, RECEIVED_AMOUNT, TDS, PENAL_AMOUNT, PRINCIPAL_BANK_GL_ACCOUNT_ID,
|
||||||
|
INTEREST_BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
public static final Set<String> RENEW_PARAMS = Set.of(NEW_CERT_NO, NEW_CERT_DATE, NEW_INT_RATE, NEW_MATURITY_DATE, NEW_PAYOUT,
|
||||||
|
NEW_CALC_TYPE, MATURITY_AMOUNT, RENEWAL_AMOUNT, TDS, DIFF_BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
|
||||||
|
public static final Set<String> FD_PARAMS = Set.of(PARTY_ID, CERT_NO, CERT_DATE, DEAL_DATE, AMOUNT, INT_RATE, MATURITY_DATE, PAYOUT,
|
||||||
|
CALC_TYPE, BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
public static final Set<String> BASE_PARAMS = Set.of(AS_ON_DATE, TOTAL_DEPOSITS, REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
}
|
||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentFdClosureData;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentFdData;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentFdDueData;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentFdScheduleData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentFdClosureService;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentFdInterestService;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentFdService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/fds")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment FD", description = "Fixed deposits the bank places with other banks.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentFdApiResource {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentFdService fdService;
|
||||||
|
private final InvestmentFdClosureService closureService;
|
||||||
|
private final InvestmentFdInterestService interestService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "All FDs placed with other banks")
|
||||||
|
public List<InvestmentFdData> retrieveAll() {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||||
|
return this.fdService.retrieveAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("quote")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Preview an FD: interest, maturity amount and the RBI limits", description = "Dates dd-MM-yyyy")
|
||||||
|
public InvestmentFdData quote(@QueryParam("partyId") final Long partyId, @QueryParam("amount") final BigDecimal amount,
|
||||||
|
@QueryParam("intRate") final BigDecimal intRate, @QueryParam("dealDate") final String dealDate,
|
||||||
|
@QueryParam("maturityDate") final String maturityDate, @QueryParam("payout") final String payout,
|
||||||
|
@QueryParam("calcType") final String calcType) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||||
|
return this.fdService.quote(partyId, amount, intRate, date(dealDate), date(maturityDate), payout, calcType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("due")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "FD interest vouchers and maturities due", description = "Due now or within withinDays (default 2)")
|
||||||
|
public List<InvestmentFdDueData> due(@QueryParam("withinDays") final Integer withinDays) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||||
|
return this.interestService.due(withinDays == null ? InvestmentFdApiConstants.DUE_WITHIN_DAYS : withinDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{fdId}/interest-schedule")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Interest lines of an FD", description = "31 Mar / 30 Sep provisions, payouts and maturity")
|
||||||
|
public InvestmentFdScheduleData schedule(@PathParam("fdId") final Long fdId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||||
|
return this.interestService.schedule(fdId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{fdId}/interest")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Post the next interest line of an FD", description = "lineType PROVISION | PAYOUT, intDate; payout: receivedAmount, tds")
|
||||||
|
public CommandProcessingResult postInterest(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
return this.commandsSourceWritePlatformService
|
||||||
|
.logCommandSource(new CommandWrapperBuilder().postInvestmentFdInterest(fdId).withJson(apiRequestBodyAsJson).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{fdId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "One FD")
|
||||||
|
public InvestmentFdData retrieveOne(@PathParam("fdId") @Parameter(description = "fdId") final Long fdId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||||
|
return this.fdService.retrieveOne(fdId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Place an FD", description = "Dr the bank's FD GL / Cr our bank GL, dated the deal date; refused beyond the RBI limits.")
|
||||||
|
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentFd().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{fdId}/closure-quote")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Preview a maturity, premature closure or renewal", description = "type MATURITY | PREMATURE | RENEWAL")
|
||||||
|
public InvestmentFdClosureData closureQuote(@PathParam("fdId") final Long fdId, @QueryParam("type") final String type,
|
||||||
|
@QueryParam("closeDate") final String closeDate, @QueryParam("receivedAmount") final BigDecimal receivedAmount,
|
||||||
|
@QueryParam("tds") final BigDecimal tds, @QueryParam("renewalAmount") final BigDecimal renewalAmount) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||||
|
return this.closureService.quote(fdId, type, date(closeDate), receivedAmount, tds, renewalAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{fdId}/mature")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Close an FD at maturity", description = "Dated the maturity date; amount as credited by the bank, optional TDS.")
|
||||||
|
public CommandProcessingResult mature(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
return this.commandsSourceWritePlatformService
|
||||||
|
.logCommandSource(new CommandWrapperBuilder().matureInvestmentFd(fdId).withJson(apiRequestBodyAsJson).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{fdId}/premature")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Close an FD before maturity", description = "Not calculated: the amount received (and any penal interest) is entered.")
|
||||||
|
public CommandProcessingResult premature(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
return this.commandsSourceWritePlatformService
|
||||||
|
.logCommandSource(new CommandWrapperBuilder().prematureInvestmentFd(fdId).withJson(apiRequestBodyAsJson).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{fdId}/renew")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Renew an FD at maturity", description = "New certificate, rate and maturity; money added or taken out goes through a bank GL.")
|
||||||
|
public CommandProcessingResult renew(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
return this.commandsSourceWritePlatformService
|
||||||
|
.logCommandSource(new CommandWrapperBuilder().renewInvestmentFd(fdId).withJson(apiRequestBodyAsJson).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("deposit-base")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Record total deposits as on a 31 March", description = "The base of the RBI limits for the following year.")
|
||||||
|
public CommandProcessingResult setDepositBase(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().setInvestmentDepositBase().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LocalDate date(final String value) {
|
||||||
|
return StringUtils.isBlank(value) ? null : LocalDate.parse(value.trim(), DATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Constants for posting an interest schedule line (coupon or closing provision) of a holding to GL. */
|
||||||
|
public final class InvestmentInterestApiConstants {
|
||||||
|
|
||||||
|
private InvestmentInterestApiConstants() {}
|
||||||
|
|
||||||
|
public static final String ENTITY = "INVESTMENTINTEREST";
|
||||||
|
public static final String RESOURCE = "investmentinterest";
|
||||||
|
|
||||||
|
public static final String LINE_TYPE = "lineType";
|
||||||
|
public static final String INT_DATE = "intDate";
|
||||||
|
/** Bank GL the coupon is received into; defaults to the purchase's paying bank. */
|
||||||
|
public static final String BANK_GL_ACCOUNT_ID = "bankGlAccountId";
|
||||||
|
public static final String LOCALE = "locale";
|
||||||
|
public static final String DATE_FORMAT = "dateFormat";
|
||||||
|
|
||||||
|
public static final Set<String> PARAMS = Set.of(LINE_TYPE, INT_DATE, BANK_GL_ACCOUNT_ID, LOCALE, DATE_FORMAT);
|
||||||
|
|
||||||
|
/** A line shows as a task this many days before its date (Jitendra Sir). */
|
||||||
|
public static final int DUE_WITHIN_DAYS = 2;
|
||||||
|
|
||||||
|
public static final String INTEREST_RECEIVABLE_GL_CODE = "100603";
|
||||||
|
public static final String INTEREST_INCOME_GOVT_SECURITIES_GL_CODE = "400401";
|
||||||
|
public static final String INTEREST_INCOME_BONDS_GL_CODE = "400402";
|
||||||
|
|
||||||
|
/** Income GL for a holding's interest: SLR govt securities, or bonds / debentures for the rest. */
|
||||||
|
public static String interestIncomeGlCode(final String bondType) {
|
||||||
|
return "GOVT_SECURITY".equals(bondType) ? INTEREST_INCOME_GOVT_SECURITIES_GL_CODE : INTEREST_INCOME_BONDS_GL_CODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentInterestDueData;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentInterestPostingData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseReadPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/interest-postings")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Interest Posting", description = "Interest lines of holdings that are due to be posted with a voucher.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentInterestApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentPurchaseReadPlatformService readPlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Posted interest lines", description = "Coupons and closing provisions already posted to GL with their "
|
||||||
|
+ "voucher, newest first.")
|
||||||
|
public List<InvestmentInterestPostingData> retrieveAll() {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentInterestApiConstants.ENTITY);
|
||||||
|
return this.readPlatformService.retrieveInterestPostings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("due")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Interest lines due", description = "For every active holding, its next unposted interest line when that "
|
||||||
|
+ "line's date is past or within withinDays days (default 2).")
|
||||||
|
public List<InvestmentInterestDueData> due(@QueryParam("withinDays") @Parameter(description = "withinDays") final Integer withinDays) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentInterestApiConstants.ENTITY);
|
||||||
|
return this.readPlatformService
|
||||||
|
.retrieveDueInterest(withinDays == null ? InvestmentInterestApiConstants.DUE_WITHIN_DAYS : withinDays);
|
||||||
|
}
|
||||||
|
}
|
||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.DELETE;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.PUT;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentPartyData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentMasterReadPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/parties")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Bank / Party Master", description = "Counterparties an investment is bought from or placed with.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentPartyApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentMasterReadPlatformService readPlatformService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List investment parties")
|
||||||
|
public List<InvestmentPartyData> retrieveAll() {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_PARTY);
|
||||||
|
return this.readPlatformService.retrieveAllParties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{partyId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve an investment party")
|
||||||
|
public InvestmentPartyData retrieveOne(@PathParam("partyId") @Parameter(description = "partyId") final Long partyId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_PARTY);
|
||||||
|
return this.readPlatformService.retrieveParty(partyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Create an investment party")
|
||||||
|
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentParty().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("{partyId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Update an investment party")
|
||||||
|
public CommandProcessingResult update(@PathParam("partyId") @Parameter(description = "partyId") final Long partyId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().updateInvestmentParty(partyId).withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{partyId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Drop an investment party")
|
||||||
|
public CommandProcessingResult delete(@PathParam("partyId") @Parameter(description = "partyId") final Long partyId) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentParty(partyId).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.api;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Constants for the Investment Purchase Entry (a deal that creates a holding). */
|
||||||
|
public final class InvestmentPurchaseApiConstants {
|
||||||
|
|
||||||
|
private InvestmentPurchaseApiConstants() {}
|
||||||
|
|
||||||
|
public static final String ENTITY = "INVESTMENTPURCHASE";
|
||||||
|
public static final String RESOURCE = "investmentpurchase";
|
||||||
|
|
||||||
|
public static final Set<String> SETTLEMENT_TYPES = Set.of("T+0", "T+1", "T+2");
|
||||||
|
public static final Set<String> CATEGORIES = Set.of("HTM (Held To Maturity)", "AFS (Available For Sale)", "HFT (Held For Trading)");
|
||||||
|
|
||||||
|
/** Saved deal; nothing posted to GL until it is settled. */
|
||||||
|
public static final String STATUS_PENDING_SETTLEMENT = "PENDING_SETTLEMENT";
|
||||||
|
/** Settled: posted to GL on the settlement date, now a holding. */
|
||||||
|
public static final String STATUS_ACTIVE = "ACTIVE";
|
||||||
|
/** Every unit sold before maturity. */
|
||||||
|
public static final String STATUS_SOLD = "SOLD";
|
||||||
|
/** Repaid at maturity. */
|
||||||
|
public static final String STATUS_REDEEMED = "REDEEMED";
|
||||||
|
|
||||||
|
/** GLs the settlement entry debits besides the script's investment GL. */
|
||||||
|
public static final String BROKEN_PERIOD_INTEREST_GL_CODE = "500606";
|
||||||
|
public static final String STAMP_DUTY_GL_CODE = "500601";
|
||||||
|
public static final String BROKERAGE_GL_CODE = "500602";
|
||||||
|
public static final String OTHER_CHARGES_GL_CODE = "500603";
|
||||||
|
|
||||||
|
public static final String BOND_TYPE = "bondType";
|
||||||
|
public static final String SCRIPT_ID = "scriptId";
|
||||||
|
public static final String PARTY_ID = "partyId";
|
||||||
|
public static final String DEAL_DATE = "dealDate";
|
||||||
|
public static final String DEAL_TIME = "dealTime";
|
||||||
|
public static final String SETTLEMENT_TYPE = "settlementType";
|
||||||
|
public static final String FACE_VALUE = "faceValue";
|
||||||
|
public static final String PURCHASE_RATE = "purchaseRate";
|
||||||
|
public static final String CONSIDER_AMT = "considerAmt";
|
||||||
|
public static final String NAV = "nav";
|
||||||
|
public static final String STAMP_DUTY = "stampDuty";
|
||||||
|
public static final String BROKERAGE = "brokerage";
|
||||||
|
public static final String OTHER_CHARGES = "otherCharges";
|
||||||
|
public static final String CATEGORY = "category";
|
||||||
|
public static final String LAST_INT_DATE = "lastIntDate";
|
||||||
|
public static final String CR_GL_ACCOUNT_ID = "crGlAccountId";
|
||||||
|
public static final String SUB_TYPE = "subType";
|
||||||
|
/** Optional legacy fields (trade no, authorities, lien, delivery mode, …) kept as a JSON object. */
|
||||||
|
public static final String OTHER_DETAILS = "otherDetails";
|
||||||
|
public static final String LOCALE = "locale";
|
||||||
|
public static final String DATE_FORMAT = "dateFormat";
|
||||||
|
|
||||||
|
public static final Set<String> PARAMS = Set.of(BOND_TYPE, SCRIPT_ID, PARTY_ID, DEAL_DATE, DEAL_TIME, SETTLEMENT_TYPE, FACE_VALUE,
|
||||||
|
PURCHASE_RATE, CONSIDER_AMT, NAV, STAMP_DUTY, BROKERAGE, OTHER_CHARGES, CATEGORY, LAST_INT_DATE, CR_GL_ACCOUNT_ID, SUB_TYPE,
|
||||||
|
OTHER_DETAILS, LOCALE, DATE_FORMAT);
|
||||||
|
}
|
||||||
+184
@@ -0,0 +1,184 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.DELETE;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentInterestScheduleData;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentPurchaseData;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentSaleData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseReadPlatformService;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentSaleReadPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/purchases")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Purchase Entry", description = "Investment deals (Govt Security, Bond, Mutual Fund, …) — each becomes a holding.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentPurchaseApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentPurchaseReadPlatformService readPlatformService;
|
||||||
|
private final InvestmentSaleReadPlatformService saleReadPlatformService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List investment purchases (holdings)")
|
||||||
|
public List<InvestmentPurchaseData> retrieveAll() {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentPurchaseApiConstants.ENTITY);
|
||||||
|
return this.readPlatformService.retrieveAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{purchaseId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve an investment purchase")
|
||||||
|
public InvestmentPurchaseData retrieveOne(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentPurchaseApiConstants.ENTITY);
|
||||||
|
return this.readPlatformService.retrieveOne(purchaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{purchaseId}/interest-schedule")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Interest schedule of a holding", description = "Half-yearly coupons and 31 Mar / 30 Sep closing provisions "
|
||||||
|
+ "from purchase to maturity. Calculated only — nothing is posted. asOnDate (dd-MM-yyyy) defaults to the business date.")
|
||||||
|
public InvestmentInterestScheduleData retrieveInterestSchedule(
|
||||||
|
@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||||
|
@QueryParam("asOnDate") @Parameter(description = "dd-MM-yyyy") final String asOnDate) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentPurchaseApiConstants.ENTITY);
|
||||||
|
final LocalDate asOn = StringUtils.isBlank(asOnDate) ? null
|
||||||
|
: LocalDate.parse(asOnDate.trim(), DateTimeFormatter.ofPattern("dd-MM-yyyy"));
|
||||||
|
return this.readPlatformService.retrieveInterestSchedule(purchaseId, asOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Save a purchase entry")
|
||||||
|
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentPurchase().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{purchaseId}/settle")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Settle a purchase", description = "On or after the settlement date: posts the purchase to GL dated the "
|
||||||
|
+ "settlement date and makes the deal an active holding.")
|
||||||
|
public CommandProcessingResult settle(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().settleInvestmentPurchase(purchaseId)
|
||||||
|
.withJson(StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{purchaseId}/interest-postings")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Post an interest line", description = "Posts the next interest schedule line (coupon or closing provision) "
|
||||||
|
+ "of a holding to GL with a JV voucher, on or after its date and in date order.")
|
||||||
|
public CommandProcessingResult postInterest(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().postInvestmentInterest(purchaseId).withJson(apiRequestBodyAsJson)
|
||||||
|
.build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{purchaseId}/redemption-quote")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "What the redemption at maturity will post", description = "Face value back against the book value still "
|
||||||
|
+ "held; refused before maturity or while a sale is pending.")
|
||||||
|
public InvestmentSaleData redemptionQuote(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||||
|
return this.saleReadPlatformService.quoteRedemption(purchaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{purchaseId}/redeem")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Redeem at maturity", description = "On or after maturity, once every coupon is posted: posts the face value "
|
||||||
|
+ "received, dated the maturity date, and closes the holding.")
|
||||||
|
public CommandProcessingResult redeem(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().redeemInvestmentPurchase(purchaseId)
|
||||||
|
.withJson(StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{purchaseId}/write-off")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Write off part of the book value", description = "Dr Loss on Investments / Cr Investment for the amount "
|
||||||
|
+ "given; the units stay held at the reduced book value.")
|
||||||
|
public CommandProcessingResult writeOff(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().writeOffInvestmentPurchase(purchaseId).withJson(apiRequestBodyAsJson)
|
||||||
|
.build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{purchaseId}/transfer")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "GOI Category Transfer", description = "Moves the holding to another security (scriptId) of the same coupon "
|
||||||
|
+ "rate, maturity and unit value; the book value moves between GLs only when the two scripts use different GLs.")
|
||||||
|
public CommandProcessingResult transfer(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().transferInvestmentSecurity(purchaseId).withJson(apiRequestBodyAsJson)
|
||||||
|
.build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{purchaseId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Drop a purchase entry")
|
||||||
|
public CommandProcessingResult delete(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentPurchase(purchaseId).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** Sale (before maturity) and redemption (at maturity) of investment holdings. */
|
||||||
|
public final class InvestmentSaleApiConstants {
|
||||||
|
|
||||||
|
private InvestmentSaleApiConstants() {}
|
||||||
|
|
||||||
|
public static final String ENTITY = "INVESTMENTSALE";
|
||||||
|
public static final String RESOURCE = "investmentsale";
|
||||||
|
|
||||||
|
public static final String TYPE_SELL = "SELL";
|
||||||
|
public static final String TYPE_REDEEM = "REDEEM";
|
||||||
|
/** Part of the book value written off as a loss; the units stay held. */
|
||||||
|
public static final String TYPE_WRITEOFF = "WRITEOFF";
|
||||||
|
/** A year-end amortisation of premium (31 March); recorded like a sale so the book value comes down. */
|
||||||
|
public static final String TYPE_AMORTISE = "AMORTISE";
|
||||||
|
|
||||||
|
public static final String STATUS_PENDING_SETTLEMENT = "PENDING_SETTLEMENT";
|
||||||
|
public static final String STATUS_SETTLED = "SETTLED";
|
||||||
|
|
||||||
|
/** Profit on sale / redemption (income) and loss on sale / redemption (expense). */
|
||||||
|
public static final String PROFIT_ON_SALE_GL_CODE = "400405";
|
||||||
|
public static final String LOSS_ON_SALE_GL_CODE = "500605";
|
||||||
|
/** Premium still on the books at maturity is written off to the amortisation expense. */
|
||||||
|
public static final String PREMIUM_AMORTISATION_GL_CODE = "500604";
|
||||||
|
/** Premium paid over face value, held here until amortised (Jitendra Sir). */
|
||||||
|
public static final String PREMIUM_ON_INVESTMENTS_GL_CODE = "100707";
|
||||||
|
|
||||||
|
public static final String PURCHASE_ID = "purchaseId";
|
||||||
|
public static final String PARTY_ID = "partyId";
|
||||||
|
public static final String DEAL_DATE = "dealDate";
|
||||||
|
public static final String DEAL_TIME = "dealTime";
|
||||||
|
public static final String SETTLEMENT_TYPE = "settlementType";
|
||||||
|
public static final String UNITS = "units";
|
||||||
|
public static final String SALE_RATE = "saleRate";
|
||||||
|
public static final String BROKERAGE = "brokerage";
|
||||||
|
public static final String BANK_GL_ACCOUNT_ID = "bankGlAccountId";
|
||||||
|
public static final String SANCTION_AUTHORITY = "sanctionAuthority";
|
||||||
|
public static final String REMARKS = "remarks";
|
||||||
|
public static final String AMOUNT = "amount";
|
||||||
|
public static final String FY_END = "fyEnd";
|
||||||
|
public static final String LOCALE = "locale";
|
||||||
|
public static final String DATE_FORMAT = "dateFormat";
|
||||||
|
|
||||||
|
public static final Set<String> SELL_PARAMS = Set.of(PURCHASE_ID, PARTY_ID, DEAL_DATE, DEAL_TIME, SETTLEMENT_TYPE, UNITS, SALE_RATE,
|
||||||
|
BROKERAGE, BANK_GL_ACCOUNT_ID, SANCTION_AUTHORITY, REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
public static final Set<String> REDEEM_PARAMS = Set.of(BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
public static final Set<String> WRITEOFF_PARAMS = Set.of(AMOUNT, REMARKS, LOCALE, DATE_FORMAT);
|
||||||
|
public static final Set<String> AMORTISE_PARAMS = Set.of(PURCHASE_ID, FY_END, LOCALE, DATE_FORMAT);
|
||||||
|
}
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.DELETE;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentSaleData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentSaleReadPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/sales")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Sale", description = "Sale of holdings before maturity (all or part of the units), settled on the settlement date.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentSaleApiResource {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentSaleReadPlatformService readPlatformService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List sales and redemptions, newest first")
|
||||||
|
public List<InvestmentSaleData> retrieveAll() {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||||
|
return this.readPlatformService.retrieveAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("quote")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Work out a sale before saving it", description = "Price, interest from the buyer, cost, profit or loss "
|
||||||
|
+ "and the net amount for the units, rate and deal date given (dd-MM-yyyy).")
|
||||||
|
public InvestmentSaleData quote(@QueryParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||||
|
@QueryParam("dealDate") @Parameter(description = "dd-MM-yyyy") final String dealDate,
|
||||||
|
@QueryParam("settlementType") @Parameter(description = "T+0 / T+1 / T+2") final String settlementType,
|
||||||
|
@QueryParam("units") @Parameter(description = "units") final BigDecimal units,
|
||||||
|
@QueryParam("saleRate") @Parameter(description = "saleRate") final BigDecimal saleRate,
|
||||||
|
@QueryParam("brokerage") @Parameter(description = "brokerage") final BigDecimal brokerage,
|
||||||
|
@QueryParam("bankGlAccountId") @Parameter(description = "bankGlAccountId") final Long bankGlAccountId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||||
|
final LocalDate deal = LocalDate.parse(dealDate.trim(), DATE);
|
||||||
|
return this.readPlatformService.quoteSale(purchaseId, deal, StringUtils.defaultIfBlank(settlementType, "T+0"), units, saleRate,
|
||||||
|
brokerage, bankGlAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Save a sale", description = "Saved pending; nothing reaches the GL until it is settled.")
|
||||||
|
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentSale().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("{saleId}/settle")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Settle a sale", description = "On or after the settlement date: posts the sale to GL dated the settlement "
|
||||||
|
+ "date and reduces the units held.")
|
||||||
|
public CommandProcessingResult settle(@PathParam("saleId") @Parameter(description = "saleId") final Long saleId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().settleInvestmentSale(saleId)
|
||||||
|
.withJson(StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("{saleId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Drop a pending sale")
|
||||||
|
public CommandProcessingResult delete(@PathParam("saleId") @Parameter(description = "saleId") final Long saleId) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentSale(saleId).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.PUT;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
|
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||||
|
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentScriptData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentMasterReadPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/scripts")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Script Master", description = "Securities / instruments the bank invests in (Govt Security, Bond, Mutual Fund, …).")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentScriptApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentMasterReadPlatformService readPlatformService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List investment scripts")
|
||||||
|
public List<InvestmentScriptData> retrieveAll() {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_SCRIPT);
|
||||||
|
return this.readPlatformService.retrieveAllScripts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{scriptId}")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve an investment script")
|
||||||
|
public InvestmentScriptData retrieveOne(@PathParam("scriptId") @Parameter(description = "scriptId") final Long scriptId) {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_SCRIPT);
|
||||||
|
return this.readPlatformService.retrieveScript(scriptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Create an investment script")
|
||||||
|
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentScript().withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("{scriptId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Update an investment script")
|
||||||
|
public CommandProcessingResult update(@PathParam("scriptId") @Parameter(description = "scriptId") final Long scriptId,
|
||||||
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
|
final CommandWrapper request = new CommandWrapperBuilder().updateInvestmentScript(scriptId).withJson(apiRequestBodyAsJson).build();
|
||||||
|
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.api;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.investment.data.InvestmentSecurityTransferData;
|
||||||
|
import org.apache.fineract.portfolio.investment.service.InvestmentSecurityTransferService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Path("/v1/investment/transfers")
|
||||||
|
@Component
|
||||||
|
@Tag(name = "Investment Security Transfer", description = "GOI Category Transfer: a holding moved to another security of the same terms.")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InvestmentTransferApiResource {
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final InvestmentSecurityTransferService transferService;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "List security transfers, newest first")
|
||||||
|
public List<InvestmentSecurityTransferData> retrieveAll() {
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission("INVESTMENTTRANSFER");
|
||||||
|
return this.transferService.retrieveAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** Premium amortisation schedule of an HTM holding: one line per 31 March to maturity, then the rest at maturity. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentAmortisationData {
|
||||||
|
|
||||||
|
private final Long purchaseId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final String category;
|
||||||
|
private final String settlementDate;
|
||||||
|
private final String maturityDate;
|
||||||
|
private final BigDecimal faceValue;
|
||||||
|
private final BigDecimal premium;
|
||||||
|
private final BigDecimal unamortisedPremium;
|
||||||
|
private final BigDecimal bookValue;
|
||||||
|
private final String premiumGlCode;
|
||||||
|
private final List<Row> rows;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public static class Row {
|
||||||
|
|
||||||
|
/** YEAR (31 March) or MATURITY (the remainder, written off on redemption). */
|
||||||
|
private final String kind;
|
||||||
|
private final String fyEnd;
|
||||||
|
private final String fromDate;
|
||||||
|
private final String toDate;
|
||||||
|
private final long days;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
private final BigDecimal premiumAfter;
|
||||||
|
private final boolean posted;
|
||||||
|
private final String voucherNo;
|
||||||
|
private final String glTransactionId;
|
||||||
|
/** Next line to post and its 31 March has come. */
|
||||||
|
private final boolean postable;
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** A holding's premium amortisation due for a 31 March (Amortize Yearly Process). */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentAmortisationDueData {
|
||||||
|
|
||||||
|
private final Long purchaseId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final String fyEnd;
|
||||||
|
private final String fromDate;
|
||||||
|
private final long days;
|
||||||
|
private final BigDecimal unamortisedPremium;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.portfolio.investment.domain.InvestmentDailyRate;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentDailyRateData {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final Long scriptId;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final String bondType;
|
||||||
|
private final String rateDate;
|
||||||
|
private final BigDecimal openRate;
|
||||||
|
private final BigDecimal highRate;
|
||||||
|
private final BigDecimal lowRate;
|
||||||
|
private final BigDecimal closeRate;
|
||||||
|
|
||||||
|
public static InvestmentDailyRateData from(final InvestmentDailyRate r) {
|
||||||
|
return InvestmentDailyRateData.builder().id(r.getId()).scriptId(r.getScript().getId()).scriptCode(r.getScript().getScriptCode())
|
||||||
|
.scriptDesc(r.getScript().getScriptDesc()).bondType(r.getScript().getBondType())
|
||||||
|
.rateDate(r.getRateDate().format(DATE_FORMAT)).openRate(r.getOpenRate()).highRate(r.getHighRate()).lowRate(r.getLowRate())
|
||||||
|
.closeRate(r.getCloseRate()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** What closing an FD (maturity, premature or renewal) works out to, and the voucher it posts. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentFdClosureData {
|
||||||
|
|
||||||
|
private final Long fdId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String type;
|
||||||
|
private final String closeDate;
|
||||||
|
private final BigDecimal principal;
|
||||||
|
/** Interest the system works out for the period, to compare with the bank's figure. */
|
||||||
|
private final BigDecimal expectedInterest;
|
||||||
|
/** Interest already booked by provisions and not yet received. */
|
||||||
|
private final BigDecimal provisioned;
|
||||||
|
/** Money credited to our bank by the other bank (net of TDS). */
|
||||||
|
private final BigDecimal receivedAmount;
|
||||||
|
private final BigDecimal tds;
|
||||||
|
private final BigDecimal penalAmount;
|
||||||
|
/** Gross interest as per the bank: received + TDS − principal. */
|
||||||
|
private final BigDecimal interest;
|
||||||
|
/** Interest income of this entry: interest less what was provisioned (negative = reversed from income). */
|
||||||
|
private final BigDecimal income;
|
||||||
|
/** Bank's interest less the system's. */
|
||||||
|
private final BigDecimal difference;
|
||||||
|
private final BigDecimal renewalAmount;
|
||||||
|
/** Renewal: money added (positive) or taken out (negative) against the maturity amount. */
|
||||||
|
private final BigDecimal topUp;
|
||||||
|
private final List<Line> lines;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Line {
|
||||||
|
|
||||||
|
private final String side;
|
||||||
|
private final String glCode;
|
||||||
|
private final String glName;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.portfolio.investment.domain.InvestmentFd;
|
||||||
|
|
||||||
|
/** An FD placed with another bank; also the shape of a purchase preview (with the limit check). */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentFdData {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String dealNo;
|
||||||
|
private final Long partyId;
|
||||||
|
private final String partyCode;
|
||||||
|
private final String partyName;
|
||||||
|
private final String certNo;
|
||||||
|
private final String certDate;
|
||||||
|
private final String dealDate;
|
||||||
|
private final BigDecimal principal;
|
||||||
|
private final BigDecimal intRate;
|
||||||
|
private final String maturityDate;
|
||||||
|
private final long days;
|
||||||
|
private final String payout;
|
||||||
|
private final String calcType;
|
||||||
|
private final BigDecimal expectedInterest;
|
||||||
|
private final BigDecimal expectedMaturityAmount;
|
||||||
|
/** Interest paid each half year / year for a payout FD. */
|
||||||
|
private final BigDecimal periodInterest;
|
||||||
|
private final String fdGlCode;
|
||||||
|
private final String fdGlName;
|
||||||
|
private final String interestGlCode;
|
||||||
|
private final String interestGlName;
|
||||||
|
private final Long bankGlAccountId;
|
||||||
|
private final String bankGlCode;
|
||||||
|
private final String bankGlName;
|
||||||
|
private final String status;
|
||||||
|
private final String remarks;
|
||||||
|
private final String glTransactionId;
|
||||||
|
private final String voucherNo;
|
||||||
|
private final String renewedFromDealNo;
|
||||||
|
private final String closedOnDate;
|
||||||
|
private final String closureType;
|
||||||
|
private final BigDecimal closureAmount;
|
||||||
|
private final BigDecimal closureInterest;
|
||||||
|
private final BigDecimal closureTds;
|
||||||
|
private final BigDecimal closurePenal;
|
||||||
|
private final String closureVoucherNo;
|
||||||
|
private final String closureGlTransactionId;
|
||||||
|
private final InvestmentFdLimitData limit;
|
||||||
|
|
||||||
|
public static InvestmentFdData from(final InvestmentFd f, final BigDecimal periodInterest) {
|
||||||
|
return builder().id(f.getId()).dealNo(f.getDealNo()).partyId(f.getParty().getId()).partyCode(f.getParty().getPartyCode())
|
||||||
|
.partyName(f.getParty().getPartyName()).certNo(f.getCertNo()).certDate(format(f.getCertDate()))
|
||||||
|
.dealDate(format(f.getDealDate())).principal(f.getPrincipal()).intRate(f.getIntRate())
|
||||||
|
.maturityDate(format(f.getMaturityDate())).days(ChronoUnit.DAYS.between(f.getDealDate(), f.getMaturityDate()))
|
||||||
|
.payout(f.getPayout()).calcType(f.getCalcType()).expectedInterest(f.getExpectedInterest())
|
||||||
|
.expectedMaturityAmount(f.getExpectedMaturityAmount()).periodInterest(periodInterest).fdGlCode(code(f.getFdGlAccount()))
|
||||||
|
.fdGlName(name(f.getFdGlAccount())).interestGlCode(code(f.getInterestGlAccount())).interestGlName(name(f.getInterestGlAccount()))
|
||||||
|
.bankGlAccountId(f.getBankGlAccount().getId()).bankGlCode(code(f.getBankGlAccount())).bankGlName(name(f.getBankGlAccount()))
|
||||||
|
.status(f.getStatus()).remarks(f.getRemarks()).glTransactionId(f.getGlTransactionId()).voucherNo(f.getVoucherNo())
|
||||||
|
.renewedFromDealNo(f.getRenewedFrom() == null ? null : f.getRenewedFrom().getDealNo()).closedOnDate(format(f.getClosedOnDate()))
|
||||||
|
.closureType(f.getClosureType()).closureAmount(f.getClosureAmount()).closureInterest(f.getClosureInterest())
|
||||||
|
.closureTds(f.getClosureTds()).closurePenal(f.getClosurePenal()).closureVoucherNo(f.getClosureVoucherNo()).closureGlTransactionId(f.getClosureGlTransactionId()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String code(final GLAccount gl) {
|
||||||
|
return gl == null ? null : gl.getGlCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String name(final GLAccount gl) {
|
||||||
|
return gl == null ? null : gl.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(final LocalDate date) {
|
||||||
|
return date == null ? null : date.format(DATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** The next interest voucher (or the maturity) of an FD that is due, or due within the reminder window. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentFdDueData {
|
||||||
|
|
||||||
|
private final Long fdId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String partyName;
|
||||||
|
private final String certNo;
|
||||||
|
/** PROVISION, PAYOUT or MATURITY. */
|
||||||
|
private final String lineType;
|
||||||
|
private final String intDate;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
private final boolean postable;
|
||||||
|
private final boolean overdue;
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** Where a new FD stands against the RBI limits on deposits with other banks. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentFdLimitData {
|
||||||
|
|
||||||
|
/** The 31 March the limits are based on. */
|
||||||
|
private final String asOnDate;
|
||||||
|
/** Total deposits as on that date, as entered; empty until someone enters it. */
|
||||||
|
private final BigDecimal totalDeposits;
|
||||||
|
/** The same figure worked out from the deposit GLs, as a suggestion. */
|
||||||
|
private final BigDecimal glTotalDeposits;
|
||||||
|
private final BigDecimal grossLimit;
|
||||||
|
private final BigDecimal fdOutstanding;
|
||||||
|
private final BigDecimal bankBalances;
|
||||||
|
private final BigDecimal grossAfter;
|
||||||
|
private final BigDecimal bankLimit;
|
||||||
|
private final String partyName;
|
||||||
|
private final BigDecimal bankAfter;
|
||||||
|
private final BigDecimal newAmount;
|
||||||
|
private final boolean withinLimits;
|
||||||
|
private final String message;
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** Interest lines of an FD: 31 Mar / 30 Sep provisions, payouts of a half-yearly / yearly FD, and maturity. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentFdScheduleData {
|
||||||
|
|
||||||
|
private final InvestmentFdData fd;
|
||||||
|
/** Provisioned interest not yet received (interest receivable of this FD). */
|
||||||
|
private final BigDecimal provisioned;
|
||||||
|
private final List<Row> rows;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public static class Row {
|
||||||
|
|
||||||
|
/** PROVISION, PAYOUT or MATURITY (shown for information; closed on the Maturity screen). */
|
||||||
|
private final String lineType;
|
||||||
|
private final String intDate;
|
||||||
|
private final String fromDate;
|
||||||
|
private final String toDate;
|
||||||
|
private final long days;
|
||||||
|
private final BigDecimal grossInterest;
|
||||||
|
/** Payout: the part already provisioned, cleared from interest receivable. */
|
||||||
|
private final BigDecimal alreadyBooked;
|
||||||
|
private final BigDecimal tds;
|
||||||
|
/** Provision: interest booked. Payout: interest income of the payout (gross less already booked). */
|
||||||
|
private final BigDecimal amount;
|
||||||
|
private final boolean posted;
|
||||||
|
private final String voucherNo;
|
||||||
|
private final String glTransactionId;
|
||||||
|
/** The next line to post, and its date has come. */
|
||||||
|
private final boolean postable;
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** The next unposted interest line of a holding that is due, or due within the reminder window — a task to post. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentInterestDueData {
|
||||||
|
|
||||||
|
private final Long purchaseId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final String lineType;
|
||||||
|
private final String intDate;
|
||||||
|
private final BigDecimal grossInterest;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
/** The date has come: it can be posted now. */
|
||||||
|
private final boolean postable;
|
||||||
|
/** The date has passed without posting. */
|
||||||
|
private final boolean overdue;
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** An interest line (coupon or closing provision) already posted to GL with its voucher — for the transaction log. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentInterestPostingData {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final Long purchaseId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String bondType;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final String lineType;
|
||||||
|
private final String intDate;
|
||||||
|
private final String fromDate;
|
||||||
|
private final String toDate;
|
||||||
|
private final BigDecimal grossInterest;
|
||||||
|
private final BigDecimal alreadyBooked;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
/** Debit side: the bank for a coupon, interest receivable for a provision. */
|
||||||
|
private final String drGlCode;
|
||||||
|
private final String drGlName;
|
||||||
|
private final String incomeGlCode;
|
||||||
|
private final String incomeGlName;
|
||||||
|
private final String glTransactionId;
|
||||||
|
private final String voucherNo;
|
||||||
|
private final String postedOnDate;
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interest schedule of one holding: every half-yearly coupon and every 31 March / 30 September closing provision from
|
||||||
|
* purchase to maturity. Calculated, not yet posted.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentInterestScheduleData {
|
||||||
|
|
||||||
|
private final Long purchaseId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final BigDecimal faceValue;
|
||||||
|
private final BigDecimal unitValue;
|
||||||
|
private final BigDecimal balanceUnits;
|
||||||
|
private final BigDecimal purchaseRate;
|
||||||
|
private final BigDecimal bookValue;
|
||||||
|
private final BigDecimal intRate;
|
||||||
|
private final String dealDate;
|
||||||
|
private final String lastIntDate;
|
||||||
|
private final String maturityDate;
|
||||||
|
/**
|
||||||
|
* Broken-period interest paid to the seller at purchase — booked as an expense (GL 500606), not in this schedule.
|
||||||
|
*/
|
||||||
|
private final BigDecimal purchasedAccruedInterest;
|
||||||
|
private final BigDecimal couponPerHalfYear;
|
||||||
|
private final String asOnDate;
|
||||||
|
private final String interestGlCode;
|
||||||
|
private final String interestGlName;
|
||||||
|
/** Bank the coupon is received into by default: the purchase's paying bank. */
|
||||||
|
private final Long bankGlAccountId;
|
||||||
|
private final String bankGlCode;
|
||||||
|
private final String bankGlName;
|
||||||
|
private final String status;
|
||||||
|
private final List<Row> rows;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public static class Row {
|
||||||
|
|
||||||
|
/** COUPON (cash received) or PROVISION (closing accrual). */
|
||||||
|
private final String type;
|
||||||
|
private final String intDate;
|
||||||
|
private final String fromDate;
|
||||||
|
private final String toDate;
|
||||||
|
private final int days;
|
||||||
|
/** Coupon received, or interest earned since the last coupon up to the closing. */
|
||||||
|
private final BigDecimal grossInterest;
|
||||||
|
/** Interest Receivable balance before this line (purchased accrued interest / earlier provision). */
|
||||||
|
private final BigDecimal alreadyBooked;
|
||||||
|
/** Provision to book now, or the part of the coupon that is new income. */
|
||||||
|
private final BigDecimal amount;
|
||||||
|
/** True when the date is on or before the as-on date. */
|
||||||
|
private final boolean due;
|
||||||
|
/** Posted to GL: its JV voucher and GL transaction. */
|
||||||
|
private final boolean posted;
|
||||||
|
private final String voucherNo;
|
||||||
|
private final String glTransactionId;
|
||||||
|
private final String postedOnDate;
|
||||||
|
/** The next line to post and its date has come. */
|
||||||
|
private final boolean postable;
|
||||||
|
/** Unposted and its date is within the reminder window (or past). */
|
||||||
|
private final boolean dueSoon;
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.portfolio.investment.domain.InvestmentParty;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentPartyData {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String partyCode;
|
||||||
|
private final String bondType;
|
||||||
|
private final String partyName;
|
||||||
|
private final String address1;
|
||||||
|
private final String address2;
|
||||||
|
private final String address3;
|
||||||
|
private final String city;
|
||||||
|
private final String pincode;
|
||||||
|
private final String phone;
|
||||||
|
private final String fax;
|
||||||
|
private final String bankType;
|
||||||
|
private final Long glAccountId;
|
||||||
|
private final String glCode;
|
||||||
|
private final String glName;
|
||||||
|
private final Long interestGlAccountId;
|
||||||
|
private final String interestGlCode;
|
||||||
|
private final String interestGlName;
|
||||||
|
private final String rbiCode;
|
||||||
|
private final BigDecimal limit;
|
||||||
|
private final String leiNo;
|
||||||
|
private final String cpBankName;
|
||||||
|
private final String cpBranchName;
|
||||||
|
private final String cpAccountNo;
|
||||||
|
private final String cpIfsc;
|
||||||
|
private final boolean active;
|
||||||
|
|
||||||
|
public static InvestmentPartyData from(final InvestmentParty p) {
|
||||||
|
return InvestmentPartyData.builder().id(p.getId()).partyCode(p.getPartyCode()).bondType(p.getBondType())
|
||||||
|
.partyName(p.getPartyName()).address1(p.getAddress1()).address2(p.getAddress2()).address3(p.getAddress3())
|
||||||
|
.city(p.getCity()).pincode(p.getPincode()).phone(p.getPhone()).fax(p.getFax()).bankType(p.getBankType())
|
||||||
|
.glAccountId(p.getGlAccount().getId()).glCode(p.getGlAccount().getGlCode()).glName(p.getGlAccount().getName())
|
||||||
|
.interestGlAccountId(p.getInterestGlAccount() == null ? null : p.getInterestGlAccount().getId())
|
||||||
|
.interestGlCode(p.getInterestGlAccount() == null ? null : p.getInterestGlAccount().getGlCode())
|
||||||
|
.interestGlName(p.getInterestGlAccount() == null ? null : p.getInterestGlAccount().getName())
|
||||||
|
.rbiCode(p.getRbiCode()).limit(p.getExposureLimit()).leiNo(p.getLeiNo()).cpBankName(p.getCpBankName())
|
||||||
|
.cpBranchName(p.getCpBranchName()).cpAccountNo(p.getCpAccountNo()).cpIfsc(p.getCpIfsc()).active(p.isActive()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.portfolio.investment.domain.InvestmentPurchase;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentPurchaseData {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String bondType;
|
||||||
|
private final Long scriptId;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final String isin;
|
||||||
|
private final Long partyId;
|
||||||
|
private final String partyCode;
|
||||||
|
private final String partyName;
|
||||||
|
private final String dealDate;
|
||||||
|
private final String dealTime;
|
||||||
|
private final String settlementType;
|
||||||
|
private final String settlementDate;
|
||||||
|
private final BigDecimal faceValue;
|
||||||
|
private final BigDecimal purchaseRate;
|
||||||
|
private final BigDecimal units;
|
||||||
|
private final BigDecimal balanceUnits;
|
||||||
|
private final BigDecimal brokerage;
|
||||||
|
private final BigDecimal stampDuty;
|
||||||
|
private final BigDecimal otherCharges;
|
||||||
|
private final BigDecimal bookValue;
|
||||||
|
private final BigDecimal premiumDiscount;
|
||||||
|
private final BigDecimal intRate;
|
||||||
|
private final String issueDate;
|
||||||
|
private final String maturityDate;
|
||||||
|
private final String lastIntDate;
|
||||||
|
private final Integer accruedDays;
|
||||||
|
private final BigDecimal accruedInterest;
|
||||||
|
private final BigDecimal settlementAmount;
|
||||||
|
private final BigDecimal ytm;
|
||||||
|
private final String category;
|
||||||
|
private final String subType;
|
||||||
|
private final Long investmentGlAccountId;
|
||||||
|
private final String investmentGlCode;
|
||||||
|
private final String investmentGlName;
|
||||||
|
private final Long crGlAccountId;
|
||||||
|
private final String crGlCode;
|
||||||
|
private final String crGlName;
|
||||||
|
private final String otherDetails;
|
||||||
|
private final String status;
|
||||||
|
private final String glTransactionId;
|
||||||
|
private final String settledOnDate;
|
||||||
|
|
||||||
|
public static InvestmentPurchaseData from(final InvestmentPurchase p) {
|
||||||
|
return InvestmentPurchaseData.builder().id(p.getId()).dealNo(p.getDealNo()).certNo(p.getCertNo()).bondType(p.getBondType())
|
||||||
|
.scriptId(p.getScript().getId()).scriptCode(p.getScript().getScriptCode()).scriptDesc(p.getScript().getScriptDesc())
|
||||||
|
.isin(p.getScript().getIsin()).partyId(p.getParty().getId()).partyCode(p.getParty().getPartyCode())
|
||||||
|
.partyName(p.getParty().getPartyName()).dealDate(format(p.getDealDate())).dealTime(p.getDealTime())
|
||||||
|
.settlementType(p.getSettlementType()).settlementDate(format(p.getSettlementDate())).faceValue(p.getFaceValue())
|
||||||
|
.purchaseRate(p.getPurchaseRate()).units(p.getUnits()).balanceUnits(p.getBalanceUnits()).brokerage(p.getBrokerage())
|
||||||
|
.stampDuty(p.getStampDuty()).otherCharges(p.getOtherCharges()).bookValue(p.getBookValue())
|
||||||
|
.premiumDiscount(p.getPremiumDiscount()).intRate(p.getIntRate()).issueDate(format(p.getIssueDate()))
|
||||||
|
.maturityDate(format(p.getMaturityDate())).lastIntDate(format(p.getLastIntDate())).accruedDays(p.getAccruedDays())
|
||||||
|
.accruedInterest(p.getAccruedInterest()).settlementAmount(p.getSettlementAmount()).ytm(p.getYtm()).category(p.getCategory())
|
||||||
|
.subType(p.getSubType()).investmentGlAccountId(p.getInvestmentGlAccount().getId())
|
||||||
|
.investmentGlCode(p.getInvestmentGlAccount().getGlCode()).investmentGlName(p.getInvestmentGlAccount().getName())
|
||||||
|
.crGlAccountId(p.getCrGlAccount().getId()).crGlCode(p.getCrGlAccount().getGlCode()).crGlName(p.getCrGlAccount().getName())
|
||||||
|
.otherDetails(p.getOtherDetails()).status(p.getStatus()).glTransactionId(p.getGlTransactionId())
|
||||||
|
.settledOnDate(format(p.getSettledOnDate())).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(final LocalDate date) {
|
||||||
|
return date == null ? null : date.format(DATE_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.portfolio.investment.domain.InvestmentSale;
|
||||||
|
|
||||||
|
/** A sale or redemption of a holding — also the shape of a quote before it is saved. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentSaleData {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final Long purchaseId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String bondType;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final String saleType;
|
||||||
|
private final Long partyId;
|
||||||
|
private final String partyName;
|
||||||
|
private final String dealDate;
|
||||||
|
private final String dealTime;
|
||||||
|
private final String settlementType;
|
||||||
|
private final String settlementDate;
|
||||||
|
private final BigDecimal units;
|
||||||
|
private final BigDecimal faceValue;
|
||||||
|
private final BigDecimal saleRate;
|
||||||
|
private final BigDecimal consideration;
|
||||||
|
private final Integer accruedDays;
|
||||||
|
private final BigDecimal accruedInterest;
|
||||||
|
private final BigDecimal receivableCleared;
|
||||||
|
/** Interest from the buyer not yet booked: accrued interest less the receivable cleared. */
|
||||||
|
private final BigDecimal interestIncome;
|
||||||
|
private final BigDecimal brokerage;
|
||||||
|
private final BigDecimal bookValueSold;
|
||||||
|
private final BigDecimal premiumCleared;
|
||||||
|
private final BigDecimal profitLoss;
|
||||||
|
private final BigDecimal netAmount;
|
||||||
|
private final Long bankGlAccountId;
|
||||||
|
private final String bankGlCode;
|
||||||
|
private final String bankGlName;
|
||||||
|
private final String investmentGlCode;
|
||||||
|
/** GL the premium share is credited to: 100707, or the investment GL for deals settled before it was used. */
|
||||||
|
private final String premiumGlCode;
|
||||||
|
private final String sanctionAuthority;
|
||||||
|
private final String remarks;
|
||||||
|
private final String status;
|
||||||
|
private final String glTransactionId;
|
||||||
|
private final String voucherNo;
|
||||||
|
private final String settledOnDate;
|
||||||
|
|
||||||
|
public static InvestmentSaleData from(final InvestmentSale s) {
|
||||||
|
return builder().id(s.getId()).purchaseId(s.getPurchase().getId()).dealNo(s.getPurchase().getDealNo())
|
||||||
|
.certNo(s.getPurchase().getCertNo()).bondType(s.getPurchase().getBondType())
|
||||||
|
.scriptCode(s.getPurchase().getScript().getScriptCode()).scriptDesc(s.getPurchase().getScript().getScriptDesc())
|
||||||
|
.saleType(s.getSaleType()).partyId(s.getParty() == null ? null : s.getParty().getId())
|
||||||
|
.partyName(s.getParty() == null ? null : s.getParty().getPartyName()).dealDate(format(s.getDealDate()))
|
||||||
|
.dealTime(s.getDealTime()).settlementType(s.getSettlementType()).settlementDate(format(s.getSettlementDate()))
|
||||||
|
.units(s.getUnits()).faceValue(s.getFaceValue()).saleRate(s.getSaleRate()).consideration(s.getConsideration())
|
||||||
|
.accruedDays(s.getAccruedDays()).accruedInterest(s.getAccruedInterest()).receivableCleared(s.getReceivableCleared())
|
||||||
|
.interestIncome(zero(s.getAccruedInterest()).subtract(zero(s.getReceivableCleared()))).brokerage(s.getBrokerage())
|
||||||
|
.bookValueSold(s.getBookValueSold()).premiumCleared(s.getPremiumCleared()).profitLoss(s.getProfitLoss()).netAmount(s.getNetAmount())
|
||||||
|
.bankGlAccountId(s.getBankGlAccount().getId()).bankGlCode(s.getBankGlAccount().getGlCode())
|
||||||
|
.bankGlName(s.getBankGlAccount().getName()).investmentGlCode(s.getPurchase().getInvestmentGlAccount().getGlCode())
|
||||||
|
.premiumGlCode(s.getPurchase().getPremiumGlAccount() != null ? s.getPurchase().getPremiumGlAccount().getGlCode()
|
||||||
|
: s.getPurchase().getInvestmentGlAccount().getGlCode())
|
||||||
|
.sanctionAuthority(s.getSanctionAuthority()).remarks(s.getRemarks()).status(s.getStatus())
|
||||||
|
.glTransactionId(s.getGlTransactionId()).voucherNo(s.getVoucherNo()).settledOnDate(format(s.getSettledOnDate())).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BigDecimal zero(final BigDecimal v) {
|
||||||
|
return v == null ? BigDecimal.ZERO : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(final LocalDate date) {
|
||||||
|
return date == null ? null : date.format(DATE_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.portfolio.investment.domain.InvestmentScript;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentScriptData {
|
||||||
|
|
||||||
|
/** Dates go out as DD-MM-YYYY, the format the Investment screens use throughout. */
|
||||||
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String scriptCode;
|
||||||
|
private final String bondType;
|
||||||
|
private final String slrType;
|
||||||
|
private final String scriptDesc;
|
||||||
|
private final Long glAccountId;
|
||||||
|
private final String glCode;
|
||||||
|
private final String glName;
|
||||||
|
private final String isin;
|
||||||
|
private final BigDecimal faceValuePerUnit;
|
||||||
|
private final BigDecimal intRate;
|
||||||
|
private final String issueDate;
|
||||||
|
private final String maturityDate;
|
||||||
|
private final String companyName;
|
||||||
|
private final String address1;
|
||||||
|
private final String address2;
|
||||||
|
private final String address3;
|
||||||
|
private final String city;
|
||||||
|
private final String pincode;
|
||||||
|
private final String phone;
|
||||||
|
private final String fax;
|
||||||
|
private final String corpCode;
|
||||||
|
private final boolean active;
|
||||||
|
|
||||||
|
public static InvestmentScriptData from(final InvestmentScript s) {
|
||||||
|
return InvestmentScriptData.builder().id(s.getId()).scriptCode(s.getScriptCode()).bondType(s.getBondType())
|
||||||
|
.slrType(s.getSlrType()).scriptDesc(s.getScriptDesc()).glAccountId(s.getGlAccount().getId())
|
||||||
|
.glCode(s.getGlAccount().getGlCode()).glName(s.getGlAccount().getName()).isin(s.getIsin())
|
||||||
|
.faceValuePerUnit(s.getFaceValuePerUnit()).intRate(s.getIntRate()).issueDate(format(s.getIssueDate()))
|
||||||
|
.maturityDate(format(s.getMaturityDate())).companyName(s.getCompanyName()).address1(s.getAddress1())
|
||||||
|
.address2(s.getAddress2()).address3(s.getAddress3()).city(s.getCity()).pincode(s.getPincode()).phone(s.getPhone())
|
||||||
|
.fax(s.getFax()).corpCode(s.getCorpCode()).active(s.isActive()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(final LocalDate date) {
|
||||||
|
return date == null ? null : date.format(DATE_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.fineract.portfolio.investment.domain.InvestmentSecurityTransfer;
|
||||||
|
|
||||||
|
/** A GOI Category Transfer of a holding, for the transaction log. */
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
public class InvestmentSecurityTransferData {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final Long purchaseId;
|
||||||
|
private final String dealNo;
|
||||||
|
private final String certNo;
|
||||||
|
private final String bondType;
|
||||||
|
private final String fromScriptCode;
|
||||||
|
private final String fromScriptDesc;
|
||||||
|
private final String toScriptCode;
|
||||||
|
private final String toScriptDesc;
|
||||||
|
private final String fromGlCode;
|
||||||
|
private final String toGlCode;
|
||||||
|
private final String toGlName;
|
||||||
|
private final String transferDate;
|
||||||
|
private final BigDecimal units;
|
||||||
|
private final BigDecimal bookValue;
|
||||||
|
private final String remarks;
|
||||||
|
private final String glTransactionId;
|
||||||
|
private final String voucherNo;
|
||||||
|
|
||||||
|
public static InvestmentSecurityTransferData from(final InvestmentSecurityTransfer t) {
|
||||||
|
return builder().id(t.getId()).purchaseId(t.getPurchase().getId()).dealNo(t.getPurchase().getDealNo())
|
||||||
|
.certNo(t.getPurchase().getCertNo()).bondType(t.getPurchase().getBondType()).fromScriptCode(t.getFromScript().getScriptCode())
|
||||||
|
.fromScriptDesc(t.getFromScript().getScriptDesc()).toScriptCode(t.getToScript().getScriptCode())
|
||||||
|
.toScriptDesc(t.getToScript().getScriptDesc()).fromGlCode(t.getFromGlAccount().getGlCode())
|
||||||
|
.toGlCode(t.getToGlAccount().getGlCode()).toGlName(t.getToGlAccount().getName())
|
||||||
|
.transferDate(t.getTransferDate().format(DATE_FORMAT)).units(t.getUnits()).bookValue(t.getBookValue())
|
||||||
|
.remarks(t.getRemarks()).glTransactionId(t.getGlTransactionId()).voucherNo(t.getVoucherNo()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Daily Script Master: the market rate of a script for one day, as the bank's existing CBS keeps it. Purchase Entry
|
||||||
|
* takes its rate from here (Jitendra Sir). Close rate is mandatory; open, high and low are optional.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_daily_rate")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentDailyRate extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "script_id", nullable = false)
|
||||||
|
private InvestmentScript script;
|
||||||
|
|
||||||
|
@Column(name = "rate_date", nullable = false)
|
||||||
|
private LocalDate rateDate;
|
||||||
|
|
||||||
|
@Column(name = "open_rate", scale = 6, precision = 19)
|
||||||
|
private BigDecimal openRate;
|
||||||
|
|
||||||
|
@Column(name = "high_rate", scale = 6, precision = 19)
|
||||||
|
private BigDecimal highRate;
|
||||||
|
|
||||||
|
@Column(name = "low_rate", scale = 6, precision = 19)
|
||||||
|
private BigDecimal lowRate;
|
||||||
|
|
||||||
|
@Column(name = "close_rate", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal closeRate;
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface InvestmentDailyRateRepository extends JpaRepository<InvestmentDailyRate, Long> {
|
||||||
|
|
||||||
|
boolean existsByScriptIdAndRateDate(Long scriptId, LocalDate rateDate);
|
||||||
|
|
||||||
|
/** The newest rate of a script on or before a date. */
|
||||||
|
Optional<InvestmentDailyRate> findFirstByScriptIdAndRateDateLessThanEqualOrderByRateDateDesc(Long scriptId, LocalDate onDate);
|
||||||
|
|
||||||
|
List<InvestmentDailyRate> findByScriptIdOrderByRateDateDesc(Long scriptId);
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/** The bank's total deposits as on a 31 March: the base for the RBI limits on deposits placed with other banks. */
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_deposit_base")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentDepositBase extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@Column(name = "as_on_date", nullable = false, unique = true)
|
||||||
|
private LocalDate asOnDate;
|
||||||
|
|
||||||
|
@Column(name = "total_deposits", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal totalDeposits;
|
||||||
|
|
||||||
|
@Column(name = "remarks", length = 500)
|
||||||
|
private String remarks;
|
||||||
|
}
|
||||||
+28
@@ -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.investment.domain;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface InvestmentDepositBaseRepository extends JpaRepository<InvestmentDepositBase, Long> {
|
||||||
|
|
||||||
|
Optional<InvestmentDepositBase> findByAsOnDate(LocalDate asOnDate);
|
||||||
|
}
|
||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/** A fixed deposit the bank has placed with another bank, from placement to maturity, premature closure or renewal. */
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_fd")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentFd extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@Column(name = "deal_no", nullable = false, length = 20, unique = true)
|
||||||
|
private String dealNo;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "party_id", nullable = false)
|
||||||
|
private InvestmentParty party;
|
||||||
|
|
||||||
|
/** The bank's FD GL (from the party), debited when the money is placed. */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "fd_gl_account_id", nullable = false)
|
||||||
|
private GLAccount fdGlAccount;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "interest_gl_account_id", nullable = false)
|
||||||
|
private GLAccount interestGlAccount;
|
||||||
|
|
||||||
|
/** Certificate (receipt) number issued by the other bank. */
|
||||||
|
@Column(name = "cert_no", nullable = false, length = 50)
|
||||||
|
private String certNo;
|
||||||
|
|
||||||
|
@Column(name = "cert_date")
|
||||||
|
private LocalDate certDate;
|
||||||
|
|
||||||
|
@Column(name = "deal_date", nullable = false)
|
||||||
|
private LocalDate dealDate;
|
||||||
|
|
||||||
|
@Column(name = "principal", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal principal;
|
||||||
|
|
||||||
|
@Column(name = "int_rate", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal intRate;
|
||||||
|
|
||||||
|
@Column(name = "maturity_date", nullable = false)
|
||||||
|
private LocalDate maturityDate;
|
||||||
|
|
||||||
|
@Column(name = "payout", nullable = false, length = 20)
|
||||||
|
private String payout;
|
||||||
|
|
||||||
|
@Column(name = "calc_type", nullable = false, length = 20)
|
||||||
|
private String calcType;
|
||||||
|
|
||||||
|
@Column(name = "expected_interest", scale = 6, precision = 19)
|
||||||
|
private BigDecimal expectedInterest;
|
||||||
|
|
||||||
|
@Column(name = "expected_maturity_amount", scale = 6, precision = 19)
|
||||||
|
private BigDecimal expectedMaturityAmount;
|
||||||
|
|
||||||
|
/** Our bank account the money was paid from. */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "bank_gl_account_id", nullable = false)
|
||||||
|
private GLAccount bankGlAccount;
|
||||||
|
|
||||||
|
/** The FD this one renews, if any. */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "renewed_from_id")
|
||||||
|
private InvestmentFd renewedFrom;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Column(name = "remarks", length = 500)
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@Column(name = "gl_transaction_id", length = 50)
|
||||||
|
private String glTransactionId;
|
||||||
|
|
||||||
|
@Column(name = "voucher_no", length = 30)
|
||||||
|
private String voucherNo;
|
||||||
|
|
||||||
|
@Column(name = "closed_on_date")
|
||||||
|
private LocalDate closedOnDate;
|
||||||
|
|
||||||
|
/** MATURITY, PREMATURE or RENEWAL. */
|
||||||
|
@Column(name = "closure_type", length = 20)
|
||||||
|
private String closureType;
|
||||||
|
|
||||||
|
@Column(name = "closure_amount", scale = 6, precision = 19)
|
||||||
|
private BigDecimal closureAmount;
|
||||||
|
|
||||||
|
@Column(name = "closure_interest", scale = 6, precision = 19)
|
||||||
|
private BigDecimal closureInterest;
|
||||||
|
|
||||||
|
@Column(name = "closure_tds", scale = 6, precision = 19)
|
||||||
|
private BigDecimal closureTds;
|
||||||
|
|
||||||
|
@Column(name = "closure_penal", scale = 6, precision = 19)
|
||||||
|
private BigDecimal closurePenal;
|
||||||
|
|
||||||
|
@Column(name = "closure_gl_transaction_id", length = 50)
|
||||||
|
private String closureGlTransactionId;
|
||||||
|
|
||||||
|
@Column(name = "closure_voucher_no", length = 30)
|
||||||
|
private String closureVoucherNo;
|
||||||
|
}
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/** A posted interest line of an FD: a 31 Mar / 30 Sep provision, or a half-yearly / yearly payout. */
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_fd_interest")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentFdInterest extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "fd_id", nullable = false)
|
||||||
|
private InvestmentFd fd;
|
||||||
|
|
||||||
|
/** PROVISION or PAYOUT. */
|
||||||
|
@Column(name = "line_type", nullable = false, length = 20)
|
||||||
|
private String lineType;
|
||||||
|
|
||||||
|
@Column(name = "int_date", nullable = false)
|
||||||
|
private LocalDate intDate;
|
||||||
|
|
||||||
|
@Column(name = "from_date")
|
||||||
|
private LocalDate fromDate;
|
||||||
|
|
||||||
|
@Column(name = "to_date")
|
||||||
|
private LocalDate toDate;
|
||||||
|
|
||||||
|
@Column(name = "days")
|
||||||
|
private Integer days;
|
||||||
|
|
||||||
|
@Column(name = "gross_interest", scale = 6, precision = 19)
|
||||||
|
private BigDecimal grossInterest;
|
||||||
|
|
||||||
|
/** For a payout: the part already provisioned, cleared from interest receivable. */
|
||||||
|
@Column(name = "already_booked", scale = 6, precision = 19)
|
||||||
|
private BigDecimal alreadyBooked;
|
||||||
|
|
||||||
|
@Column(name = "tds", scale = 6, precision = 19)
|
||||||
|
private BigDecimal tds;
|
||||||
|
|
||||||
|
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "bank_gl_account_id")
|
||||||
|
private GLAccount bankGlAccount;
|
||||||
|
|
||||||
|
@Column(name = "gl_transaction_id", length = 50)
|
||||||
|
private String glTransactionId;
|
||||||
|
|
||||||
|
@Column(name = "voucher_no", length = 30)
|
||||||
|
private String voucherNo;
|
||||||
|
|
||||||
|
@Column(name = "posted_on_date")
|
||||||
|
private LocalDate postedOnDate;
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface InvestmentFdInterestRepository extends JpaRepository<InvestmentFdInterest, Long> {
|
||||||
|
|
||||||
|
List<InvestmentFdInterest> findByFdIdOrderByIntDateAscIdAsc(Long fdId);
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.LockModeType;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Lock;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface InvestmentFdRepository extends JpaRepository<InvestmentFd, Long> {
|
||||||
|
|
||||||
|
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
|
||||||
|
@Query("select f from InvestmentFd f where f.id = :fdId")
|
||||||
|
Optional<InvestmentFd> findByIdWithLock(@Param("fdId") Long fdId);
|
||||||
|
|
||||||
|
List<InvestmentFd> findByStatusOrderByIdAsc(String status);
|
||||||
|
|
||||||
|
boolean existsByPartyIdAndCertNoIgnoreCase(Long partyId, String certNo);
|
||||||
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/** One interest schedule line of a holding (coupon received or closing provision) that has been posted to GL. */
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_interest_posting")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentInterestPosting extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "purchase_id", nullable = false)
|
||||||
|
private InvestmentPurchase purchase;
|
||||||
|
|
||||||
|
/** COUPON or PROVISION. */
|
||||||
|
@Column(name = "line_type", nullable = false, length = 20)
|
||||||
|
private String lineType;
|
||||||
|
|
||||||
|
@Column(name = "int_date", nullable = false)
|
||||||
|
private LocalDate intDate;
|
||||||
|
|
||||||
|
@Column(name = "from_date")
|
||||||
|
private LocalDate fromDate;
|
||||||
|
|
||||||
|
@Column(name = "to_date")
|
||||||
|
private LocalDate toDate;
|
||||||
|
|
||||||
|
@Column(name = "days")
|
||||||
|
private Integer days;
|
||||||
|
|
||||||
|
@Column(name = "gross_interest", scale = 6, precision = 19)
|
||||||
|
private BigDecimal grossInterest;
|
||||||
|
|
||||||
|
@Column(name = "already_booked", scale = 6, precision = 19)
|
||||||
|
private BigDecimal alreadyBooked;
|
||||||
|
|
||||||
|
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
/** Bank the coupon was received into (coupon lines only). */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "bank_gl_account_id")
|
||||||
|
private GLAccount bankGlAccount;
|
||||||
|
|
||||||
|
@Column(name = "gl_transaction_id", length = 50)
|
||||||
|
private String glTransactionId;
|
||||||
|
|
||||||
|
@Column(name = "voucher_no", length = 30)
|
||||||
|
private String voucherNo;
|
||||||
|
|
||||||
|
@Column(name = "posted_on_date")
|
||||||
|
private LocalDate postedOnDate;
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface InvestmentInterestPostingRepository extends JpaRepository<InvestmentInterestPosting, Long> {
|
||||||
|
|
||||||
|
List<InvestmentInterestPosting> findByPurchaseIdOrderByIntDateAsc(Long purchaseId);
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bank / Party Master — the counterparty an investment is bought from or placed with (bank, RBI, issuer, AMC, broker),
|
||||||
|
* following the bank's existing CBS Bank / Party Master.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_party")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentParty extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@Column(name = "party_code", nullable = false, length = 20, unique = true)
|
||||||
|
private String partyCode;
|
||||||
|
|
||||||
|
@Column(name = "bond_type", nullable = false, length = 20)
|
||||||
|
private String bondType;
|
||||||
|
|
||||||
|
@Column(name = "party_name", nullable = false, length = 150)
|
||||||
|
private String partyName;
|
||||||
|
|
||||||
|
@Column(name = "address1", length = 150)
|
||||||
|
private String address1;
|
||||||
|
|
||||||
|
@Column(name = "address2", length = 150)
|
||||||
|
private String address2;
|
||||||
|
|
||||||
|
@Column(name = "address3", length = 150)
|
||||||
|
private String address3;
|
||||||
|
|
||||||
|
@Column(name = "city", length = 100)
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
@Column(name = "pincode", length = 10)
|
||||||
|
private String pincode;
|
||||||
|
|
||||||
|
@Column(name = "phone", length = 20)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Column(name = "fax", length = 20)
|
||||||
|
private String fax;
|
||||||
|
|
||||||
|
@Column(name = "bank_type", length = 60)
|
||||||
|
private String bankType;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "gl_account_id", nullable = false)
|
||||||
|
private GLAccount glAccount;
|
||||||
|
|
||||||
|
/** Interest income GL of the FDs placed with this bank (Bond Type FD only). */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "interest_gl_account_id")
|
||||||
|
private GLAccount interestGlAccount;
|
||||||
|
|
||||||
|
@Column(name = "rbi_code", length = 30)
|
||||||
|
private String rbiCode;
|
||||||
|
|
||||||
|
@Column(name = "exposure_limit", scale = 6, precision = 19)
|
||||||
|
private BigDecimal exposureLimit;
|
||||||
|
|
||||||
|
@Column(name = "lei_no", length = 30)
|
||||||
|
private String leiNo;
|
||||||
|
|
||||||
|
@Column(name = "cp_bank_name", length = 150)
|
||||||
|
private String cpBankName;
|
||||||
|
|
||||||
|
@Column(name = "cp_branch_name", length = 150)
|
||||||
|
private String cpBranchName;
|
||||||
|
|
||||||
|
@Column(name = "cp_account_no", length = 40)
|
||||||
|
private String cpAccountNo;
|
||||||
|
|
||||||
|
@Column(name = "cp_ifsc", length = 11)
|
||||||
|
private String cpIfsc;
|
||||||
|
|
||||||
|
/** An inactive party is kept for history but not offered for new deals. */
|
||||||
|
@Column(name = "is_active", nullable = false)
|
||||||
|
private boolean active = true;
|
||||||
|
}
|
||||||
+28
@@ -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.investment.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface InvestmentPartyRepository extends JpaRepository<InvestmentParty, Long> {
|
||||||
|
|
||||||
|
/** Locks the bank so two FDs with it are checked against the 5% limit one after the other. */
|
||||||
|
@org.springframework.data.jpa.repository.Lock(value = jakarta.persistence.LockModeType.PESSIMISTIC_WRITE)
|
||||||
|
@org.springframework.data.jpa.repository.Query("select p from InvestmentParty p where p.id = :partyId")
|
||||||
|
java.util.Optional<InvestmentParty> findByIdWithLock(@org.springframework.data.repository.query.Param("partyId") Long partyId);}
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Investment Purchase Entry — one deal (as in the bank's existing CBS "Govt.Security/Bond Entry"). Once saved it is the
|
||||||
|
* holding that interest, sale and redemption later work against; {@link #balanceUnits} tracks what is still held. Every
|
||||||
|
* derived figure (units, book value, accrued interest, settlement amount) is computed by the backend.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_purchase")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentPurchase extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
/** Financial-year deal number, e.g. 202627000001 for the first deal of FY 2026-27. */
|
||||||
|
@Column(name = "deal_no", nullable = false, length = 20, unique = true)
|
||||||
|
private String dealNo;
|
||||||
|
|
||||||
|
/** Certificate number — system-generated running number. */
|
||||||
|
@Column(name = "cert_no", nullable = false, length = 20, unique = true)
|
||||||
|
private String certNo;
|
||||||
|
|
||||||
|
@Column(name = "bond_type", nullable = false, length = 20)
|
||||||
|
private String bondType;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "script_id", nullable = false)
|
||||||
|
private InvestmentScript script;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "party_id", nullable = false)
|
||||||
|
private InvestmentParty party;
|
||||||
|
|
||||||
|
@Column(name = "deal_date", nullable = false)
|
||||||
|
private LocalDate dealDate;
|
||||||
|
|
||||||
|
@Column(name = "deal_time", length = 8)
|
||||||
|
private String dealTime;
|
||||||
|
|
||||||
|
@Column(name = "settlement_type", nullable = false, length = 5)
|
||||||
|
private String settlementType;
|
||||||
|
|
||||||
|
@Column(name = "settlement_date", nullable = false)
|
||||||
|
private LocalDate settlementDate;
|
||||||
|
|
||||||
|
@Column(name = "face_value", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
|
||||||
|
/** Price per ₹100 face value (Govt Security / Bond); NAV for a mutual fund. */
|
||||||
|
@Column(name = "purchase_rate", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal purchaseRate;
|
||||||
|
|
||||||
|
@Column(name = "units", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal units;
|
||||||
|
|
||||||
|
@Column(name = "balance_units", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal balanceUnits;
|
||||||
|
|
||||||
|
@Column(name = "brokerage", scale = 6, precision = 19)
|
||||||
|
private BigDecimal brokerage;
|
||||||
|
|
||||||
|
@Column(name = "stamp_duty", scale = 6, precision = 19)
|
||||||
|
private BigDecimal stampDuty;
|
||||||
|
|
||||||
|
@Column(name = "other_charges", scale = 6, precision = 19)
|
||||||
|
private BigDecimal otherCharges;
|
||||||
|
|
||||||
|
@Column(name = "book_value", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal bookValue;
|
||||||
|
|
||||||
|
@Column(name = "premium_discount", scale = 6, precision = 19)
|
||||||
|
private BigDecimal premiumDiscount;
|
||||||
|
|
||||||
|
@Column(name = "int_rate", scale = 4, precision = 9)
|
||||||
|
private BigDecimal intRate;
|
||||||
|
|
||||||
|
@Column(name = "issue_date")
|
||||||
|
private LocalDate issueDate;
|
||||||
|
|
||||||
|
@Column(name = "maturity_date")
|
||||||
|
private LocalDate maturityDate;
|
||||||
|
|
||||||
|
@Column(name = "last_int_date")
|
||||||
|
private LocalDate lastIntDate;
|
||||||
|
|
||||||
|
@Column(name = "accrued_days")
|
||||||
|
private Integer accruedDays;
|
||||||
|
|
||||||
|
/** Interest paid to the seller from the last coupon date up to the deal date (30/360). */
|
||||||
|
@Column(name = "accrued_interest", scale = 6, precision = 19)
|
||||||
|
private BigDecimal accruedInterest;
|
||||||
|
|
||||||
|
@Column(name = "settlement_amount", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal settlementAmount;
|
||||||
|
|
||||||
|
@Column(name = "ytm", scale = 4, precision = 9)
|
||||||
|
private BigDecimal ytm;
|
||||||
|
|
||||||
|
@Column(name = "category", nullable = false, length = 40)
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Column(name = "sub_type", length = 40)
|
||||||
|
private String subType;
|
||||||
|
|
||||||
|
/** Investment GL the holding is booked to (from the script). */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "investment_gl_account_id", nullable = false)
|
||||||
|
private GLAccount investmentGlAccount;
|
||||||
|
|
||||||
|
/** Bank GL the purchase is paid from ("CR. To A/c"). */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "cr_gl_account_id", nullable = false)
|
||||||
|
private GLAccount crGlAccount;
|
||||||
|
|
||||||
|
/** GL holding the premium paid over face value (100707) until it is amortised; empty when it sits in the investment GL. */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "premium_gl_account_id")
|
||||||
|
private GLAccount premiumGlAccount;
|
||||||
|
|
||||||
|
@Column(name = "other_details", length = 4000)
|
||||||
|
private String otherDetails;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** Journal entry transaction posted when the deal was settled. */
|
||||||
|
@Column(name = "gl_transaction_id", length = 50)
|
||||||
|
private String glTransactionId;
|
||||||
|
|
||||||
|
@Column(name = "settled_on_date")
|
||||||
|
private LocalDate settledOnDate;
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.LockModeType;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Lock;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface InvestmentPurchaseRepository extends JpaRepository<InvestmentPurchase, Long> {
|
||||||
|
|
||||||
|
/** Locks the deal row so two postings on one deal (double click, two users) run one after the other. */
|
||||||
|
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
|
||||||
|
@Query("select p from InvestmentPurchase p where p.id = :purchaseId")
|
||||||
|
Optional<InvestmentPurchase> findByIdWithLock(@Param("purchaseId") Long purchaseId);
|
||||||
|
|
||||||
|
boolean existsByScriptId(Long scriptId);
|
||||||
|
}
|
||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/** A sale of some or all units of a holding before maturity (SELL), or its repayment at maturity (REDEEM). */
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_sale")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentSale extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "purchase_id", nullable = false)
|
||||||
|
private InvestmentPurchase purchase;
|
||||||
|
|
||||||
|
/** SELL, REDEEM, WRITEOFF or AMORTISE. */
|
||||||
|
@Column(name = "sale_type", nullable = false, length = 10)
|
||||||
|
private String saleType;
|
||||||
|
|
||||||
|
/** Buyer of a sale; empty for a redemption. */
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "party_id")
|
||||||
|
private InvestmentParty party;
|
||||||
|
|
||||||
|
@Column(name = "deal_date", nullable = false)
|
||||||
|
private LocalDate dealDate;
|
||||||
|
|
||||||
|
@Column(name = "deal_time", length = 8)
|
||||||
|
private String dealTime;
|
||||||
|
|
||||||
|
@Column(name = "settlement_type", length = 5)
|
||||||
|
private String settlementType;
|
||||||
|
|
||||||
|
@Column(name = "settlement_date", nullable = false)
|
||||||
|
private LocalDate settlementDate;
|
||||||
|
|
||||||
|
@Column(name = "units", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal units;
|
||||||
|
|
||||||
|
@Column(name = "face_value", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal faceValue;
|
||||||
|
|
||||||
|
/** Price per ₹100 of face value; 100 for a redemption. */
|
||||||
|
@Column(name = "sale_rate", scale = 6, precision = 19)
|
||||||
|
private BigDecimal saleRate;
|
||||||
|
|
||||||
|
/** Price received for the units, without interest. */
|
||||||
|
@Column(name = "consideration", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal consideration;
|
||||||
|
|
||||||
|
@Column(name = "accrued_days")
|
||||||
|
private Integer accruedDays;
|
||||||
|
|
||||||
|
/** Interest the buyer pays us for the days since the last coupon. */
|
||||||
|
@Column(name = "accrued_interest", scale = 6, precision = 19)
|
||||||
|
private BigDecimal accruedInterest;
|
||||||
|
|
||||||
|
/** Part of that interest already booked by a closing provision, cleared from interest receivable. */
|
||||||
|
@Column(name = "receivable_cleared", scale = 6, precision = 19)
|
||||||
|
private BigDecimal receivableCleared;
|
||||||
|
|
||||||
|
@Column(name = "brokerage", scale = 6, precision = 19)
|
||||||
|
private BigDecimal brokerage;
|
||||||
|
|
||||||
|
/** Cost of the units sold, taken off the investment GL. */
|
||||||
|
@Column(name = "book_value_sold", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal bookValueSold;
|
||||||
|
|
||||||
|
/** Part of the book value taken off that was unamortised premium (credited to the premium GL). */
|
||||||
|
@Column(name = "premium_cleared", scale = 6, precision = 19)
|
||||||
|
private BigDecimal premiumCleared;
|
||||||
|
|
||||||
|
/** Consideration less cost: positive is profit, negative is loss. */
|
||||||
|
@Column(name = "profit_loss", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal profitLoss;
|
||||||
|
|
||||||
|
/** Money into the bank: consideration plus interest, less brokerage. */
|
||||||
|
@Column(name = "net_amount", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal netAmount;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "bank_gl_account_id", nullable = false)
|
||||||
|
private GLAccount bankGlAccount;
|
||||||
|
|
||||||
|
@Column(name = "sanction_authority", length = 100)
|
||||||
|
private String sanctionAuthority;
|
||||||
|
|
||||||
|
@Column(name = "remarks", length = 500)
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@Column(name = "status", nullable = false, length = 20)
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Column(name = "gl_transaction_id", length = 50)
|
||||||
|
private String glTransactionId;
|
||||||
|
|
||||||
|
@Column(name = "voucher_no", length = 30)
|
||||||
|
private String voucherNo;
|
||||||
|
|
||||||
|
@Column(name = "settled_on_date")
|
||||||
|
private LocalDate settledOnDate;
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.LockModeType;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Lock;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface InvestmentSaleRepository extends JpaRepository<InvestmentSale, Long> {
|
||||||
|
|
||||||
|
List<InvestmentSale> findByPurchaseIdOrderByIdAsc(Long purchaseId);
|
||||||
|
|
||||||
|
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
|
||||||
|
@Query("select s from InvestmentSale s where s.id = :saleId")
|
||||||
|
Optional<InvestmentSale> findByIdWithLock(@Param("saleId") Long saleId);
|
||||||
|
|
||||||
|
/** The deal of a sale, without loading either, so the deal can be locked first. */
|
||||||
|
@Query("select s.purchase.id from InvestmentSale s where s.id = :saleId")
|
||||||
|
Optional<Long> findPurchaseIdBySaleId(@Param("saleId") Long saleId);
|
||||||
|
}
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script Master — what is being bought (a Govt Security, bond, mutual fund scheme, share, …), following the bank's
|
||||||
|
* existing CBS Script Master. A purchase picks these terms up instead of the dealer re-typing them.
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_script")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentScript extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
/** System-generated: bond-type letter + running number, e.g. G00001. */
|
||||||
|
@Column(name = "script_code", nullable = false, length = 20, unique = true)
|
||||||
|
private String scriptCode;
|
||||||
|
|
||||||
|
@Column(name = "bond_type", nullable = false, length = 20)
|
||||||
|
private String bondType;
|
||||||
|
|
||||||
|
/** GOI / STATE_GOVT / TREASURY / NON_SLR. */
|
||||||
|
@Column(name = "slr_type", length = 20)
|
||||||
|
private String slrType;
|
||||||
|
|
||||||
|
@Column(name = "script_desc", nullable = false, length = 150)
|
||||||
|
private String scriptDesc;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "gl_account_id", nullable = false)
|
||||||
|
private GLAccount glAccount;
|
||||||
|
|
||||||
|
@Column(name = "isin", length = 20)
|
||||||
|
private String isin;
|
||||||
|
|
||||||
|
@Column(name = "face_value_per_unit", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal faceValuePerUnit;
|
||||||
|
|
||||||
|
@Column(name = "int_rate", scale = 4, precision = 9)
|
||||||
|
private BigDecimal intRate;
|
||||||
|
|
||||||
|
@Column(name = "issue_date")
|
||||||
|
private LocalDate issueDate;
|
||||||
|
|
||||||
|
@Column(name = "maturity_date")
|
||||||
|
private LocalDate maturityDate;
|
||||||
|
|
||||||
|
@Column(name = "company_name", length = 150)
|
||||||
|
private String companyName;
|
||||||
|
|
||||||
|
@Column(name = "address1", length = 150)
|
||||||
|
private String address1;
|
||||||
|
|
||||||
|
@Column(name = "address2", length = 150)
|
||||||
|
private String address2;
|
||||||
|
|
||||||
|
@Column(name = "address3", length = 150)
|
||||||
|
private String address3;
|
||||||
|
|
||||||
|
@Column(name = "city", length = 100)
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
@Column(name = "pincode", length = 10)
|
||||||
|
private String pincode;
|
||||||
|
|
||||||
|
@Column(name = "phone", length = 20)
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Column(name = "fax", length = 20)
|
||||||
|
private String fax;
|
||||||
|
|
||||||
|
@Column(name = "corp_code", length = 30)
|
||||||
|
private String corpCode;
|
||||||
|
|
||||||
|
@Column(name = "is_active", nullable = false)
|
||||||
|
private boolean active = true;
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface InvestmentScriptRepository extends JpaRepository<InvestmentScript, Long> {
|
||||||
|
|
||||||
|
List<InvestmentScript> findByIsinIgnoreCase(String isin);
|
||||||
|
}
|
||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
|
|
||||||
|
/** A holding moved from one government security script to another of the same terms (GOI Category Transfer). */
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_investment_security_transfer")
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class InvestmentSecurityTransfer extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "purchase_id", nullable = false)
|
||||||
|
private InvestmentPurchase purchase;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "from_script_id", nullable = false)
|
||||||
|
private InvestmentScript fromScript;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "to_script_id", nullable = false)
|
||||||
|
private InvestmentScript toScript;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "from_gl_account_id", nullable = false)
|
||||||
|
private GLAccount fromGlAccount;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "to_gl_account_id", nullable = false)
|
||||||
|
private GLAccount toGlAccount;
|
||||||
|
|
||||||
|
@Column(name = "transfer_date", nullable = false)
|
||||||
|
private LocalDate transferDate;
|
||||||
|
|
||||||
|
@Column(name = "units", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal units;
|
||||||
|
|
||||||
|
/** Book value carried over; moved between GLs only when the two scripts map to different GLs. */
|
||||||
|
@Column(name = "book_value", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal bookValue;
|
||||||
|
|
||||||
|
@Column(name = "remarks", length = 500)
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@Column(name = "gl_transaction_id", length = 50)
|
||||||
|
private String glTransactionId;
|
||||||
|
|
||||||
|
@Column(name = "voucher_no", length = 30)
|
||||||
|
private String voucherNo;
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface InvestmentSecurityTransferRepository extends JpaRepository<InvestmentSecurityTransfer, Long> {}
|
||||||
+28
@@ -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.investment.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class InvestmentDailyRateNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public InvestmentDailyRateNotFoundException(final Long id) {
|
||||||
|
super("error.msg.investmentdailyrate.id.invalid", "Daily script rate with identifier " + id + " does not exist", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -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.investment.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class InvestmentPartyNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public InvestmentPartyNotFoundException(final Long id) {
|
||||||
|
super("error.msg.investmentparty.id.invalid", "Investment party with identifier " + id + " does not exist", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -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.investment.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class InvestmentPurchaseNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public InvestmentPurchaseNotFoundException(final Long id) {
|
||||||
|
super("error.msg.investmentpurchase.id.invalid", "Investment purchase with identifier " + id + " does not exist", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -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.investment.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class InvestmentScriptNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public InvestmentScriptNotFoundException(final Long id) {
|
||||||
|
super("error.msg.investmentscript.id.invalid", "Investment script with identifier " + id + " does not exist", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentAmortisationService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSALE", action = "AMORTISE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AmortiseInvestmentPremiumCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentAmortisationService amortisationService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.amortisationService.amortise(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentDailyRateWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTDAILYRATE", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CreateInvestmentDailyRateCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentDailyRateWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.createDailyRate(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentFdService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTFD", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CreateInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentFdService fdService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.fdService.create(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentMasterWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTPARTY", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CreateInvestmentPartyCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.createParty(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentPurchaseWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTPURCHASE", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CreateInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentPurchaseWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.createPurchase(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentSaleWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSALE", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CreateInvestmentSaleCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.createSale(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentMasterWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSCRIPT", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CreateInvestmentScriptCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.createScript(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentDailyRateWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTDAILYRATE", action = "DELETE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DeleteInvestmentDailyRateCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentDailyRateWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.deleteDailyRate(command.entityId());
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentMasterWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTPARTY", action = "DELETE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DeleteInvestmentPartyCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.deleteParty(command.entityId());
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentPurchaseWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTPURCHASE", action = "DELETE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DeleteInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentPurchaseWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.deletePurchase(command.entityId());
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentSaleWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSALE", action = "DELETE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DeleteInvestmentSaleCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.deleteSale(command.entityId());
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentFdClosureService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTFD", action = "MATURE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MatureInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentFdClosureService closureService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.closureService.mature(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentFdInterestService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTFD", action = "POSTINTEREST")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PostInvestmentFdInterestCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentFdInterestService interestService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.interestService.post(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentInterestPostingWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTINTEREST", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PostInvestmentInterestCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentInterestPostingWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.postInterest(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentFdClosureService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTFD", action = "PREMATURE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PrematureInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentFdClosureService closureService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.closureService.premature(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentSaleWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSALE", action = "REDEEM")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RedeemInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.redeem(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentFdClosureService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTFD", action = "RENEW")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RenewInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentFdClosureService closureService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.closureService.renew(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentFdService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTFD", action = "SETBASE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SetInvestmentDepositBaseCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentFdService fdService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.fdService.setDepositBase(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentPurchaseWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTPURCHASE", action = "SETTLE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SettleInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentPurchaseWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.settlePurchase(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentSaleWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSALE", action = "SETTLE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SettleInvestmentSaleCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.settleSale(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentSecurityTransferService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTTRANSFER", action = "CREATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TransferInvestmentSecurityCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentSecurityTransferService transferService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.transferService.transfer(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentMasterWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTPARTY", action = "UPDATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UpdateInvestmentPartyCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.updateParty(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentMasterWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSCRIPT", action = "UPDATE")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UpdateInvestmentScriptCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.updateScript(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.investment.handler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.investment.service.InvestmentSaleWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "INVESTMENTSALE", action = "WRITEOFF")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WriteOffInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.writePlatformService.writeOff(command.entityId(), command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.serialization;
|
||||||
|
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.CLOSE_RATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.HIGH_RATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.LOW_RATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.OPEN_RATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.PARAMS;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.RATE_DATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.RESOURCE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.SCRIPT_ID;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.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.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/** Validates a Daily Script Master entry: close rate is mandatory and the day's rates must sit inside low to high. */
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public final class InvestmentDailyRateCommandFromApiJsonDeserializer {
|
||||||
|
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
public void validateForCreate(final String json) {
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, PARAMS);
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
|
||||||
|
final List<ApiParameterError> errors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||||
|
|
||||||
|
v.reset().parameter(SCRIPT_ID).value(this.fromApiJsonHelper.extractLongNamed(SCRIPT_ID, element)).notNull().longGreaterThanZero();
|
||||||
|
v.reset().parameter(RATE_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(RATE_DATE, element)).notNull();
|
||||||
|
final BigDecimal close = amount(CLOSE_RATE, element);
|
||||||
|
final BigDecimal open = amount(OPEN_RATE, element);
|
||||||
|
final BigDecimal high = amount(HIGH_RATE, element);
|
||||||
|
final BigDecimal low = amount(LOW_RATE, element);
|
||||||
|
v.reset().parameter(CLOSE_RATE).value(close).notNull().positiveAmount();
|
||||||
|
v.reset().parameter(OPEN_RATE).value(open).ignoreIfNull().positiveAmount();
|
||||||
|
v.reset().parameter(HIGH_RATE).value(high).ignoreIfNull().positiveAmount();
|
||||||
|
v.reset().parameter(LOW_RATE).value(low).ignoreIfNull().positiveAmount();
|
||||||
|
|
||||||
|
if (high != null && low != null && high.compareTo(low) < 0) {
|
||||||
|
v.reset().parameter(HIGH_RATE).failWithCodeAndMessage("less.than.low.rate", "High Rate cannot be below Low Rate");
|
||||||
|
}
|
||||||
|
outsideRange(v, CLOSE_RATE, "Close Rate", close, low, high);
|
||||||
|
outsideRange(v, OPEN_RATE, "Open Rate", open, low, high);
|
||||||
|
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void outsideRange(final DataValidatorBuilder v, final String param, final String label, final BigDecimal rate,
|
||||||
|
final BigDecimal low, final BigDecimal high) {
|
||||||
|
if (rate == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (low != null && rate.compareTo(low) < 0) {
|
||||||
|
v.reset().parameter(param).failWithCodeAndMessage("below.low.rate", label + " cannot be below Low Rate");
|
||||||
|
} else if (high != null && rate.compareTo(high) > 0) {
|
||||||
|
v.reset().parameter(param).failWithCodeAndMessage("above.high.rate", label + " cannot be above High Rate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal amount(final String param, final JsonElement element) {
|
||||||
|
return this.fromApiJsonHelper.parameterExists(param, element)
|
||||||
|
? this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(param, element)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.serialization;
|
||||||
|
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.BANK_GL_ACCOUNT_ID;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.INT_DATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.LINE_TYPE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.PARAMS;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.RESOURCE;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.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.investment.service.InvestmentInterestCalculator;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/** Validates a request to post one interest schedule line. */
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public final class InvestmentInterestCommandFromApiJsonDeserializer {
|
||||||
|
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
public void validateForPost(final String json) {
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, PARAMS);
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
|
||||||
|
final List<ApiParameterError> errors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||||
|
final String lineType = this.fromApiJsonHelper.extractStringNamed(LINE_TYPE, element);
|
||||||
|
v.reset().parameter(LINE_TYPE).value(lineType).notBlank();
|
||||||
|
if (StringUtils.isNotBlank(lineType)) {
|
||||||
|
v.reset().parameter(LINE_TYPE).value(lineType).isOneOfTheseValues(InvestmentInterestCalculator.TYPE_COUPON,
|
||||||
|
InvestmentInterestCalculator.TYPE_PROVISION);
|
||||||
|
}
|
||||||
|
v.reset().parameter(INT_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(INT_DATE, element)).notNull();
|
||||||
|
v.reset().parameter(BANK_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(BANK_GL_ACCOUNT_ID, element)).ignoreIfNull()
|
||||||
|
.longGreaterThanZero();
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+264
@@ -0,0 +1,264 @@
|
|||||||
|
/**
|
||||||
|
* 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.investment.serialization;
|
||||||
|
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ADDRESS1;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ADDRESS2;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ADDRESS3;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.BANK_TYPE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.BOND_TYPE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.BOND_TYPES;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CITY;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.COMPANY_NAME;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CORP_CODE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_ACCOUNT_NO;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_BANK_NAME;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_BRANCH_NAME;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_IFSC;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.DEBT_BOND_TYPES;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.EXPOSURE_LIMIT;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.FACE_VALUE_PER_UNIT;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.FAX;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.GL_ACCOUNT_ID;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.INTEREST_GL_ACCOUNT_ID;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.INT_RATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ISIN;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ISSUE_DATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.LEI_NO;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.MATURITY_DATE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PARTY_NAME;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PARTY_PARAMS;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PHONE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PINCODE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.RBI_CODE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.RESOURCE_PARTY;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.RESOURCE_SCRIPT;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SCRIPT_DESC;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SCRIPT_PARAMS;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SLR_TYPE;
|
||||||
|
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SLR_TYPES;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.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.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/** Validates Bank / Party Master and Script Master requests before anything is written. */
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public final class InvestmentMasterCommandFromApiJsonDeserializer {
|
||||||
|
|
||||||
|
private static final String IFSC_PATTERN = "^[A-Z]{4}0[A-Z0-9]{6}$";
|
||||||
|
private static final String PINCODE_PATTERN = "^[0-9]{6}$";
|
||||||
|
|
||||||
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
// -------------------- Bank / Party Master --------------------
|
||||||
|
|
||||||
|
public void validateForCreateParty(final String json) {
|
||||||
|
validateParty(json, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateForUpdateParty(final String json) {
|
||||||
|
validateParty(json, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateParty(final String json, final boolean create) {
|
||||||
|
final JsonElement element = parse(json, PARTY_PARAMS);
|
||||||
|
final List<ApiParameterError> errors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE_PARTY);
|
||||||
|
|
||||||
|
validateBondType(element, v, create);
|
||||||
|
validateRequiredString(element, v, PARTY_NAME, 150, create);
|
||||||
|
validateGlAccountId(element, v, create);
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(INTEREST_GL_ACCOUNT_ID, element)) {
|
||||||
|
v.reset().parameter(INTEREST_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(INTEREST_GL_ACCOUNT_ID, element))
|
||||||
|
.ignoreIfNull().longGreaterThanZero();
|
||||||
|
}
|
||||||
|
validateAddress(element, v);
|
||||||
|
validateOptionalString(element, v, BANK_TYPE, 60);
|
||||||
|
validateOptionalString(element, v, RBI_CODE, 30);
|
||||||
|
validateOptionalString(element, v, LEI_NO, 30);
|
||||||
|
validateOptionalString(element, v, CP_BANK_NAME, 150);
|
||||||
|
validateOptionalString(element, v, CP_BRANCH_NAME, 150);
|
||||||
|
validateOptionalString(element, v, CP_ACCOUNT_NO, 40);
|
||||||
|
|
||||||
|
final String ifsc = this.fromApiJsonHelper.extractStringNamed(CP_IFSC, element);
|
||||||
|
if (StringUtils.isNotBlank(ifsc)) {
|
||||||
|
v.reset().parameter(CP_IFSC).value(ifsc.trim().toUpperCase()).matchesRegularExpression(IFSC_PATTERN,
|
||||||
|
"IFSC must be 11 characters: 4 letters, 0, then 6 letters/digits");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(EXPOSURE_LIMIT, element)) {
|
||||||
|
final BigDecimal limit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(EXPOSURE_LIMIT, element);
|
||||||
|
v.reset().parameter(EXPOSURE_LIMIT).value(limit).ignoreIfNull().zeroOrPositiveAmount();
|
||||||
|
}
|
||||||
|
throwIfErrors(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- Script Master --------------------
|
||||||
|
|
||||||
|
public void validateForCreateScript(final String json) {
|
||||||
|
validateScript(json, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateForUpdateScript(final String json, final String existingBondType) {
|
||||||
|
validateScript(json, false, existingBondType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateScript(final String json, final boolean create) {
|
||||||
|
validateScript(json, create, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateScript(final String json, final boolean create, final String existingBondType) {
|
||||||
|
final JsonElement element = parse(json, SCRIPT_PARAMS);
|
||||||
|
final List<ApiParameterError> errors = new ArrayList<>();
|
||||||
|
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE_SCRIPT);
|
||||||
|
|
||||||
|
validateBondType(element, v, create);
|
||||||
|
validateRequiredString(element, v, SCRIPT_DESC, 150, create);
|
||||||
|
validateGlAccountId(element, v, create);
|
||||||
|
|
||||||
|
final String slrType = this.fromApiJsonHelper.extractStringNamed(SLR_TYPE, element);
|
||||||
|
if (StringUtils.isNotBlank(slrType)) {
|
||||||
|
v.reset().parameter(SLR_TYPE).value(slrType).isOneOfTheseValues(SLR_TYPES.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String bondType = create ? this.fromApiJsonHelper.extractStringNamed(BOND_TYPE, element) : existingBondType;
|
||||||
|
final boolean debt = bondType != null && DEBT_BOND_TYPES.contains(bondType);
|
||||||
|
|
||||||
|
final String isin = this.fromApiJsonHelper.extractStringNamed(ISIN, element);
|
||||||
|
if (create && debt) {
|
||||||
|
v.reset().parameter(ISIN).value(isin).notBlank();
|
||||||
|
}
|
||||||
|
v.reset().parameter(ISIN).value(isin).ignoreIfNull().notExceedingLengthOf(20);
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(INT_RATE, element) || (create && debt)) {
|
||||||
|
final BigDecimal intRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(INT_RATE, element);
|
||||||
|
final DataValidatorBuilder rate = v.reset().parameter(INT_RATE).value(intRate);
|
||||||
|
if (create && debt) {
|
||||||
|
rate.notNull();
|
||||||
|
}
|
||||||
|
rate.ignoreIfNull().zeroOrPositiveAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(FACE_VALUE_PER_UNIT, element)) {
|
||||||
|
final BigDecimal fv = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(FACE_VALUE_PER_UNIT, element);
|
||||||
|
v.reset().parameter(FACE_VALUE_PER_UNIT).value(fv).ignoreIfNull().positiveAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate issueDate = null;
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(ISSUE_DATE, element) || (create && debt)) {
|
||||||
|
issueDate = this.fromApiJsonHelper.extractLocalDateNamed(ISSUE_DATE, element);
|
||||||
|
if (create && debt) {
|
||||||
|
v.reset().parameter(ISSUE_DATE).value(issueDate).notNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(MATURITY_DATE, element) || (create && debt)) {
|
||||||
|
final LocalDate maturityDate = this.fromApiJsonHelper.extractLocalDateNamed(MATURITY_DATE, element);
|
||||||
|
final DataValidatorBuilder maturity = v.reset().parameter(MATURITY_DATE).value(maturityDate);
|
||||||
|
if (debt) {
|
||||||
|
// Interest, sale, redemption and amortisation all run to the maturity date.
|
||||||
|
maturity.notNull();
|
||||||
|
}
|
||||||
|
maturity.ignoreIfNull().validateDateAfter(issueDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateOptionalString(element, v, COMPANY_NAME, 150);
|
||||||
|
validateOptionalString(element, v, CORP_CODE, 30);
|
||||||
|
validateAddress(element, v);
|
||||||
|
throwIfErrors(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------- shared --------------------
|
||||||
|
|
||||||
|
private void validateBondType(final JsonElement element, final DataValidatorBuilder v, final boolean required) {
|
||||||
|
final String bondType = this.fromApiJsonHelper.extractStringNamed(BOND_TYPE, element);
|
||||||
|
if (required) {
|
||||||
|
v.reset().parameter(BOND_TYPE).value(bondType).notBlank();
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(bondType)) {
|
||||||
|
v.reset().parameter(BOND_TYPE).value(bondType).isOneOfTheseValues(BOND_TYPES.toArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateGlAccountId(final JsonElement element, final DataValidatorBuilder v, final boolean required) {
|
||||||
|
if (!required && !this.fromApiJsonHelper.parameterExists(GL_ACCOUNT_ID, element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed(GL_ACCOUNT_ID, element);
|
||||||
|
v.reset().parameter(GL_ACCOUNT_ID).value(glAccountId).notNull().longGreaterThanZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateRequiredString(final JsonElement element, final DataValidatorBuilder v, final String param, final int maxLength,
|
||||||
|
final boolean required) {
|
||||||
|
if (!required && !this.fromApiJsonHelper.parameterExists(param, element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String value = this.fromApiJsonHelper.extractStringNamed(param, element);
|
||||||
|
v.reset().parameter(param).value(value).notBlank().notExceedingLengthOf(maxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateOptionalString(final JsonElement element, final DataValidatorBuilder v, final String param,
|
||||||
|
final int maxLength) {
|
||||||
|
final String value = this.fromApiJsonHelper.extractStringNamed(param, element);
|
||||||
|
v.reset().parameter(param).value(value).ignoreIfNull().notExceedingLengthOf(maxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateAddress(final JsonElement element, final DataValidatorBuilder v) {
|
||||||
|
validateOptionalString(element, v, ADDRESS1, 150);
|
||||||
|
validateOptionalString(element, v, ADDRESS2, 150);
|
||||||
|
validateOptionalString(element, v, ADDRESS3, 150);
|
||||||
|
validateOptionalString(element, v, CITY, 100);
|
||||||
|
validateOptionalString(element, v, PHONE, 20);
|
||||||
|
validateOptionalString(element, v, FAX, 20);
|
||||||
|
final String pincode = this.fromApiJsonHelper.extractStringNamed(PINCODE, element);
|
||||||
|
if (StringUtils.isNotBlank(pincode)) {
|
||||||
|
v.reset().parameter(PINCODE).value(pincode.trim()).matchesRegularExpression(PINCODE_PATTERN, "Pincode must be 6 digits");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonElement parse(final String json, final Set<String> supportedParams) {
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
throw new InvalidJsonException();
|
||||||
|
}
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParams);
|
||||||
|
return this.fromApiJsonHelper.parse(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwIfErrors(final List<ApiParameterError> errors) {
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user