Shares And Dividends Implementation

This commit is contained in:
Nazeer Hussain Shaik
2016-04-13 16:34:38 +05:30
parent 9fedac2ba1
commit 08c553f9fc
155 changed files with 9974 additions and 2398 deletions
@@ -234,10 +234,10 @@ public class AccountingConstants {
}
public static enum FINANCIAL_ACTIVITY {
ASSET_TRANSFER(100, "assetTransfer", GLAccountType.ASSET), LIABILITY_TRANSFER(200, "liabilityTransfer", GLAccountType.LIABILITY),
CASH_AT_MAINVAULT (101, "cashAtMainVault", GLAccountType.ASSET),
CASH_AT_TELLER (102, "cashAtTeller", GLAccountType.ASSET),OPENING_BALANCES_TRANSFER_CONTRA (300,"openingBalancesTransferContra",GLAccountType.EQUITY),
ASSET_FUND_SOURCE(103, "fundSource", GLAccountType.ASSET);
ASSET_TRANSFER(100, "assetTransfer", GLAccountType.ASSET), LIABILITY_TRANSFER(200, "liabilityTransfer", GLAccountType.LIABILITY), CASH_AT_MAINVAULT(
101, "cashAtMainVault", GLAccountType.ASSET), CASH_AT_TELLER(102, "cashAtTeller", GLAccountType.ASSET), OPENING_BALANCES_TRANSFER_CONTRA(
300, "openingBalancesTransferContra", GLAccountType.EQUITY), ASSET_FUND_SOURCE(103, "fundSource", GLAccountType.ASSET), PAYABLE_DIVIDENDS(
201, "payableDividends", GLAccountType.LIABILITY);
private final Integer value;
private final String code;
@@ -305,6 +305,62 @@ public class AccountingConstants {
}
}
/*** Accounting placeholders for cash based accounting for Share products ***/
public static enum CASH_ACCOUNTS_FOR_SHARES {
SHARES_REFERENCE(1), SHARES_SUSPENSE(2), INCOME_FROM_FEES(3), SHARES_EQUITY(4);
private final Integer value;
private CASH_ACCOUNTS_FOR_SHARES(final Integer value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
private static final Map<Integer, CASH_ACCOUNTS_FOR_SHARES> intToEnumMap = new HashMap<>();
static {
for (final CASH_ACCOUNTS_FOR_SHARES type : CASH_ACCOUNTS_FOR_SHARES.values()) {
intToEnumMap.put(type.value, type);
}
}
public static CASH_ACCOUNTS_FOR_SHARES fromInt(final int i) {
final CASH_ACCOUNTS_FOR_SHARES type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
}
/***
* Enum of all accounting related input parameter names used while
* creating/updating a savings product
***/
public static enum SHARES_PRODUCT_ACCOUNTING_PARAMS {
SHARES_REFERENCE("shareReferenceId"), SHARES_SUSPENSE("shareSuspenseId"), INCOME_FROM_FEES("incomeFromFeeAccountId"), SHARES_EQUITY(
"shareEquityId");
private final String value;
private SHARES_PRODUCT_ACCOUNTING_PARAMS(final String value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
public static final String ASSESTS_TAG_OPTION_CODE_NAME = "AssetAccountTags";
public static final String LIABILITIES_TAG_OPTION_CODE_NAME = "LiabilityAccountTags";
public static final String EQUITY_TAG_OPTION_CODE_NAME = "EquityAccountTags";
@@ -41,5 +41,7 @@ public interface AccountingDropdownReadPlatformService {
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForCharges();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptions();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForShareProducts();
}
@@ -147,4 +147,15 @@ public class AccountingDropdownReadPlatformServiceImpl implements AccountingDrop
return accountOptions;
}
@Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForShareProducts() {
boolean includeAssetAccounts = true;
boolean includeIncomeAccounts = true;
boolean includeExpenseAccounts = false;
boolean includeLiabilityAccounts = true;
boolean includeEquityAccounts = true;
return retrieveAccountMappingOptions(includeAssetAccounts, includeIncomeAccounts, includeExpenseAccounts, includeLiabilityAccounts,
includeEquityAccounts);
}
}
@@ -68,7 +68,8 @@ public final class FinancialActivityAccountDataValidator {
baseDataValidator.reset().parameter(paramNameForFinancialActivity).value(financialActivityId).notNull().isOneOfTheseValues(
FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(),
FINANCIAL_ACTIVITY.CASH_AT_MAINVAULT.getValue(), FINANCIAL_ACTIVITY.CASH_AT_TELLER.getValue(),
FINANCIAL_ACTIVITY.OPENING_BALANCES_TRANSFER_CONTRA.getValue(), FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue());
FINANCIAL_ACTIVITY.OPENING_BALANCES_TRANSFER_CONTRA.getValue(), FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue(),
FINANCIAL_ACTIVITY.PAYABLE_DIVIDENDS.getValue());
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed(paramNameForGLAccount, element);
baseDataValidator.reset().parameter(paramNameForGLAccount).value(glAccountId).notNull().integerGreaterThanZero();
@@ -94,7 +95,8 @@ public final class FinancialActivityAccountDataValidator {
element);
baseDataValidator.reset().parameter(paramNameForFinancialActivity).value(financialActivityId).ignoreIfNull().isOneOfTheseValues(
FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(),
FINANCIAL_ACTIVITY.OPENING_BALANCES_TRANSFER_CONTRA.getValue(), FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue());
FINANCIAL_ACTIVITY.OPENING_BALANCES_TRANSFER_CONTRA.getValue(), FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue(),
FINANCIAL_ACTIVITY.PAYABLE_DIVIDENDS.getValue());
}
if (this.fromApiJsonHelper.parameterExists(paramNameForGLAccount, element)) {
@@ -0,0 +1,101 @@
/**
* 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.accounting.journalentry.data;
import java.util.List;
public class SharesDTO {
private Long shareAccountId;
private Long shareProductId;
private Long officeId;
private String currencyCode;
private boolean cashBasedAccountingEnabled;
private boolean accrualBasedAccountingEnabled;
private List<SharesTransactionDTO> newTransactions;
public SharesDTO(final Long shareAccountId, final Long shareProductId, final Long officeId, final String currencyCode,
final boolean cashBasedAccountingEnabled, final boolean accrualBasedAccountingEnabled,
final List<SharesTransactionDTO> newTransactions) {
this.shareAccountId = shareAccountId;
this.shareProductId = shareProductId;
this.officeId = officeId;
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
this.accrualBasedAccountingEnabled = accrualBasedAccountingEnabled;
this.newTransactions = newTransactions;
this.currencyCode = currencyCode;
}
public Long getOfficeId() {
return this.officeId;
}
public void setOfficeId(final Long officeId) {
this.officeId = officeId;
}
public boolean isCashBasedAccountingEnabled() {
return this.cashBasedAccountingEnabled;
}
public void setCashBasedAccountingEnabled(final boolean cashBasedAccountingEnabled) {
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
}
public boolean isAccrualBasedAccountingEnabled() {
return this.accrualBasedAccountingEnabled;
}
public void setAccrualBasedAccountingEnabled(final boolean accrualBasedAccountingEnabled) {
this.accrualBasedAccountingEnabled = accrualBasedAccountingEnabled;
}
public String getCurrencyCode() {
return this.currencyCode;
}
public void setCurrencyCode(final String currencyCode) {
this.currencyCode = currencyCode;
}
public Long getShareAccountId() {
return this.shareAccountId;
}
public void setShareAccountId(Long shareAccountId) {
this.shareAccountId = shareAccountId;
}
public Long getShareProductId() {
return this.shareProductId;
}
public void setShareProductId(Long shareProductId) {
this.shareProductId = shareProductId;
}
public List<SharesTransactionDTO> getNewTransactions() {
return this.newTransactions;
}
public void setNewTransactions(List<SharesTransactionDTO> newTransactions) {
this.newTransactions = newTransactions;
}
}
@@ -0,0 +1,94 @@
/**
* 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.accounting.journalentry.data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionEnumData;
public class SharesTransactionDTO {
private final Long officeId;
private final String transactionId;
private final Date transactionDate;
private final Long paymentTypeId;
private final ShareAccountTransactionEnumData transactionType;
private final ShareAccountTransactionEnumData transactionStatus;
private final BigDecimal amount;
/*** Breakup of amounts **/
private final BigDecimal chargeAmount;
/** Breakdowns of fees and penalties this Transaction pays **/
private final List<ChargePaymentDTO> feePayments;
public SharesTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate,
final ShareAccountTransactionEnumData transactionType, ShareAccountTransactionEnumData transactionStatus,
final BigDecimal amount, final BigDecimal chargeAmount, final List<ChargePaymentDTO> feePayments) {
this.paymentTypeId = paymentTypeId;
this.transactionId = transactionId;
this.transactionDate = transactionDate;
this.amount = amount;
this.chargeAmount = chargeAmount;
this.transactionType = transactionType;
this.transactionStatus = transactionStatus;
this.feePayments = feePayments;
this.officeId = officeId;
}
public Long getOfficeId() {
return this.officeId;
}
public String getTransactionId() {
return this.transactionId;
}
public Date getTransactionDate() {
return this.transactionDate;
}
public Long getPaymentTypeId() {
return this.paymentTypeId;
}
public ShareAccountTransactionEnumData getTransactionType() {
return this.transactionType;
}
public BigDecimal getAmount() {
return this.amount;
}
public List<ChargePaymentDTO> getFeePayments() {
return this.feePayments;
}
public ShareAccountTransactionEnumData getTransactionStatus() {
return this.transactionStatus;
}
public BigDecimal getChargeAmount() {
return this.chargeAmount;
}
}
@@ -78,6 +78,9 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@JoinColumn(name = "client_transaction_id", nullable = false)
private ClientTransaction clientTransaction;
@Column(name = "share_transaction_id", nullable = true)
private Long shareTransactionId;
@Column(name = "reversed", nullable = false)
private boolean reversed = false;
@@ -110,10 +113,10 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
final String currencyCode, final String transactionId, final boolean manualEntry, final Date transactionDate,
final JournalEntryType journalEntryType, final BigDecimal amount, final String description, final Integer entityType,
final Long entityId, final String referenceNumber, final LoanTransaction loanTransaction,
final SavingsAccountTransaction savingsTransaction, final ClientTransaction clientTransaction) {
final SavingsAccountTransaction savingsTransaction, final ClientTransaction clientTransaction, Long shareTransactionId) {
return new JournalEntry(office, paymentDetail, glAccount, currencyCode, transactionId, manualEntry, transactionDate,
journalEntryType.getValue(), amount, description, entityType, entityId, referenceNumber, loanTransaction,
savingsTransaction, clientTransaction);
savingsTransaction, clientTransaction, shareTransactionId);
}
protected JournalEntry() {
@@ -124,7 +127,7 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
final String transactionId, final boolean manualEntry, final Date transactionDate, final Integer type, final BigDecimal amount,
final String description, final Integer entityType, final Long entityId, final String referenceNumber,
final LoanTransaction loanTransaction, final SavingsAccountTransaction savingsTransaction,
final ClientTransaction clientTransaction) {
final ClientTransaction clientTransaction, final Long shareTransactionId) {
this.office = office;
this.glAccount = glAccount;
this.reversalJournalEntry = null;
@@ -143,6 +146,7 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
this.savingsTransaction = savingsTransaction;
this.clientTransaction = clientTransaction;
this.paymentDetail = paymentDetail;
this.shareTransactionId = shareTransactionId;
}
public boolean isDebitEntry() {
@@ -206,11 +210,16 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
}
public Long getEntityId() {
return this.entityId ;
return this.entityId;
}
public Integer getEntityType() {
return this.entityType ;
return this.entityType;
}
public Long getShareTransactionId() {
return this.shareTransactionId;
}
}
@@ -16,15 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.service;
package org.apache.fineract.accounting.journalentry.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.accounting.journalentry.data.SharesDTO;
public interface AccountingProcessorForShares {
public interface ShareAccountWritePlatformService {
void createJournalEntriesForShares(SharesDTO sharesDTO);
public CommandProcessingResult createShareAccount(JsonCommand jsonCommand) ;
public CommandProcessingResult updateShareAccount(Long accountId, JsonCommand jsonCommand) ;
}
@@ -0,0 +1,48 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.service;
import org.apache.fineract.accounting.journalentry.data.SharesDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class AccountingProcessorForSharesFactory {
private final ApplicationContext applicationContext;
@Autowired
public AccountingProcessorForSharesFactory(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public AccountingProcessorForShares determineProcessor(final SharesDTO sharesDTO) {
AccountingProcessorForShares accountingProcessorForShares = null;
if (sharesDTO.isCashBasedAccountingEnabled()) {
accountingProcessorForShares = this.applicationContext.getBean("cashBasedAccountingProcessorForShares",
AccountingProcessorForShares.class);
}
return accountingProcessorForShares;
}
}
@@ -31,6 +31,7 @@ import org.apache.fineract.accounting.closure.domain.GLClosureRepository;
import org.apache.fineract.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SHARES;
import org.apache.fineract.accounting.common.AccountingConstants.FINANCIAL_ACTIVITY;
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccount;
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
@@ -44,6 +45,8 @@ import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
import org.apache.fineract.accounting.journalentry.data.TaxPaymentDTO;
import org.apache.fineract.accounting.journalentry.data.SharesDTO;
import org.apache.fineract.accounting.journalentry.data.SharesTransactionDTO;
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
@@ -69,6 +72,7 @@ import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionEnumData;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -80,6 +84,7 @@ public class AccountingProcessorHelper {
public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
public static final String CLIENT_TRANSACTION_IDENTIFIER = "C";
public static final String PROVISIONING_TRANSACTION_IDENTIFIER = "P";
public static final String SHARE_TRANSACTION_IDENTIFIER = "SH";
private final JournalEntryRepository glJournalEntryRepository;
private final ProductToGLAccountMappingRepository accountMappingRepository;
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
@@ -244,6 +249,51 @@ public class AccountingProcessorHelper {
accrualBasedAccountingEnabled, newSavingsTransactions);
}
public SharesDTO populateSharesDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
final boolean accrualBasedAccountingEnabled) {
final Long shareAccountId = (Long) accountingBridgeData.get("shareAccountId");
final Long shareProductId = (Long) accountingBridgeData.get("shareProductId");
final Long officeId = (Long) accountingBridgeData.get("officeId");
final CurrencyData currencyData = (CurrencyData) accountingBridgeData.get("currency");
final List<SharesTransactionDTO> newTransactions = new ArrayList<>();
@SuppressWarnings("unchecked")
final List<Map<String, Object>> newTransactionsMap = (List<Map<String, Object>>) accountingBridgeData.get("newTransactions");
for (final Map<String, Object> map : newTransactionsMap) {
final Long transactionOfficeId = (Long) map.get("officeId");
final String transactionId = ((Long) map.get("id")).toString();
final Date transactionDate = ((LocalDate) map.get("date")).toDate();
final ShareAccountTransactionEnumData transactionType = (ShareAccountTransactionEnumData) map.get("type");
final ShareAccountTransactionEnumData transactionStatus = (ShareAccountTransactionEnumData) map.get("status");
final BigDecimal amount = (BigDecimal) map.get("amount");
final BigDecimal chargeAmount = (BigDecimal) map.get("chargeAmount");
final Long paymentTypeId = (Long) map.get("paymentTypeId");
final List<ChargePaymentDTO> feePayments = new ArrayList<>();
// extract charge payment details (if exists)
if (map.containsKey("chargesPaid")) {
@SuppressWarnings("unchecked")
final List<Map<String, Object>> chargesPaidData = (List<Map<String, Object>>) map.get("chargesPaid");
for (final Map<String, Object> chargePaid : chargesPaidData) {
final Long chargeId = (Long) chargePaid.get("chargeId");
final Long loanChargeId = (Long) chargePaid.get("sharesChargeId");
final BigDecimal chargeAmountPaid = (BigDecimal) chargePaid.get("amount");
final ChargePaymentDTO chargePaymentDTO = new ChargePaymentDTO(chargeId, loanChargeId, chargeAmountPaid);
feePayments.add(chargePaymentDTO);
}
}
final SharesTransactionDTO transaction = new SharesTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
transactionDate, transactionType, transactionStatus, amount, chargeAmount, feePayments);
newTransactions.add(transaction);
}
return new SharesDTO(shareAccountId, shareProductId, officeId, currencyData.code(), cashBasedAccountingEnabled,
accrualBasedAccountingEnabled, newTransactions);
}
public ClientTransactionDTO populateClientTransactionDtoFromMap(final Map<String, Object> accountingBridgeData) {
final Long transactionOfficeId = (Long) accountingBridgeData.get("officeId");
@@ -745,6 +795,7 @@ public class AccountingProcessorHelper {
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
final Long shareTransactionId = null;
clientTransaction = this.clientTransactionRepository.findOneWithNotFoundDetection(clientId, transactionId);
@@ -752,7 +803,7 @@ public class AccountingProcessorHelper {
modifiedTransactionId = CLIENT_TRANSACTION_IDENTIFIER + transactionId;
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.CLIENT.getValue(), clientId,
null, loanTransaction, savingsAccountTransaction, clientTransaction);
null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -762,6 +813,7 @@ public class AccountingProcessorHelper {
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final Long shareTransactionId = null;
final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
@@ -771,7 +823,7 @@ public class AccountingProcessorHelper {
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId,
null, loanTransaction, savingsAccountTransaction, clientTransaction);
null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -782,6 +834,7 @@ public class AccountingProcessorHelper {
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
final Long shareTransactionId = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
long id = Long.parseLong(transactionId);
@@ -790,7 +843,7 @@ public class AccountingProcessorHelper {
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -800,12 +853,12 @@ public class AccountingProcessorHelper {
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
PaymentDetail paymentDetail = null;
final Long shareTransactionId = null;
final boolean manualEntry = false;
String modifiedTransactionId = PROVISIONING_TRANSACTION_IDENTIFIER + provisioningentryId;
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.PROVISIONING.getValue(),
provisioningentryId, null, loanTransaction, savingsAccountTransaction, clientTransaction);
provisioningentryId, null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -815,11 +868,12 @@ public class AccountingProcessorHelper {
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
PaymentDetail paymentDetail = null;
final Long shareTransactionId = null;
final boolean manualEntry = false;
String modifiedTransactionId = PROVISIONING_TRANSACTION_IDENTIFIER + provisioningentryId;
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.PROVISIONING.getValue(),
provisioningentryId, null, loanTransaction, savingsAccountTransaction, clientTransaction);
provisioningentryId, null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -830,6 +884,7 @@ public class AccountingProcessorHelper {
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
final Long shareTransactionId = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
long id = Long.parseLong(transactionId);
@@ -838,7 +893,7 @@ public class AccountingProcessorHelper {
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -849,6 +904,7 @@ public class AccountingProcessorHelper {
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
final Long shareTransactionId = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
long id = Long.parseLong(transactionId);
@@ -857,7 +913,7 @@ public class AccountingProcessorHelper {
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId,
null, loanTransaction, savingsAccountTransaction, clientTransaction);
null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -868,6 +924,7 @@ public class AccountingProcessorHelper {
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
final PaymentDetail paymentDetail = null;
final Long shareTransactionId = null;
clientTransaction = this.clientTransactionRepository.findOneWithNotFoundDetection(clientId, transactionId);
String modifiedTransactionId = transactionId.toString();
@@ -875,10 +932,113 @@ public class AccountingProcessorHelper {
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.CLIENT.getValue(), clientId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
public void createJournalEntriesForShares(final Office office, final String currencyCode, final int accountTypeToDebitId,
final int accountTypeToCreditId, final Long shareProductId, final Long paymentTypeId, final Long shareAccountId,
final String transactionId, final Date transactionDate, final BigDecimal amount) {
createDebitJournalEntryForShares(office, currencyCode, accountTypeToDebitId, shareProductId, paymentTypeId, shareAccountId,
transactionId, transactionDate, amount);
createCreditJournalEntryForShares(office, currencyCode, accountTypeToCreditId, shareProductId, paymentTypeId, shareAccountId,
transactionId, transactionDate, amount);
}
public void createDebitJournalEntryForShares(final Office office, final String currencyCode, final int accountTypeToDebitId,
final Long shareProductId, final Long paymentTypeId, final Long shareAccountId, final String transactionId,
final Date transactionDate, final BigDecimal amount) {
final GLAccount debitAccount = getLinkedGLAccountForShareProduct(shareProductId, accountTypeToDebitId, paymentTypeId);
createDebitJournalEntryForShares(office, currencyCode, debitAccount, shareAccountId, transactionId, transactionDate, amount);
}
public void createCreditJournalEntryForShares(final Office office, final String currencyCode, final int accountTypeToCreditId,
final Long shareProductId, final Long paymentTypeId, final Long shareAccountId, final String transactionId,
final Date transactionDate, final BigDecimal amount) {
final GLAccount creditAccount = getLinkedGLAccountForShareProduct(shareProductId, accountTypeToCreditId, paymentTypeId);
createCreditJournalEntryForShares(office, currencyCode, creditAccount, shareAccountId, transactionId, transactionDate, amount);
}
public void createCashBasedJournalEntriesForSharesCharges(final Office office, final String currencyCode,
final CASH_ACCOUNTS_FOR_SHARES accountTypeToBeDebited, final CASH_ACCOUNTS_FOR_SHARES accountTypeToBeCredited,
final Long shareProductId, final Long paymentTypeId, final Long shareAccountId, final String transactionId,
final Date transactionDate, final BigDecimal totalAmount, final List<ChargePaymentDTO> chargePaymentDTOs) {
createDebitJournalEntryForShares(office, currencyCode, accountTypeToBeDebited.getValue(), shareProductId, paymentTypeId,
shareAccountId, transactionId, transactionDate, totalAmount);
createCashBasedJournalEntryForSharesCharges(office, currencyCode, accountTypeToBeCredited, shareProductId, shareAccountId,
transactionId, transactionDate, totalAmount, chargePaymentDTOs);
}
public void createCashBasedJournalEntryForSharesCharges(final Office office, final String currencyCode,
final CASH_ACCOUNTS_FOR_SHARES accountTypeToBeCredited, final Long shareProductId, final Long shareAccountId,
final String transactionId, final Date transactionDate, final BigDecimal totalAmount,
final List<ChargePaymentDTO> chargePaymentDTOs) {
final Map<GLAccount, BigDecimal> creditDetailsMap = new LinkedHashMap<>();
for (final ChargePaymentDTO chargePaymentDTO : chargePaymentDTOs) {
final GLAccount chargeSpecificAccount = getLinkedGLAccountForShareCharges(shareProductId, accountTypeToBeCredited.getValue(),
chargePaymentDTO.getChargeId());
BigDecimal chargeSpecificAmount = chargePaymentDTO.getAmount();
// adjust net credit amount if the account is already present in the
// map
if (creditDetailsMap.containsKey(chargeSpecificAccount)) {
final BigDecimal existingAmount = creditDetailsMap.get(chargeSpecificAccount);
chargeSpecificAmount = chargeSpecificAmount.add(existingAmount);
}
creditDetailsMap.put(chargeSpecificAccount, chargeSpecificAmount);
}
BigDecimal totalCreditedAmount = BigDecimal.ZERO;
for (final Map.Entry<GLAccount, BigDecimal> entry : creditDetailsMap.entrySet()) {
final GLAccount account = entry.getKey();
final BigDecimal amount = entry.getValue();
totalCreditedAmount = totalCreditedAmount.add(amount);
createCreditJournalEntryForShares(office, currencyCode, account, shareAccountId, transactionId, transactionDate, amount);
}
if (totalAmount.compareTo(totalCreditedAmount) != 0) { throw new PlatformDataIntegrityException(
"Recent Portfolio changes w.r.t Charges for shares have Broken the accounting code",
"Recent Portfolio changes w.r.t Charges for shares have Broken the accounting code"); }
}
private void createDebitJournalEntryForShares(final Office office, final String currencyCode, final GLAccount account,
final Long shareAccountId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
Long shareTransactionId = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
shareTransactionId = Long.parseLong(transactionId);
modifiedTransactionId = SHARE_TRANSACTION_IDENTIFIER + transactionId;
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SHARES.getValue(), shareAccountId,
null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.save(journalEntry);
}
private void createCreditJournalEntryForShares(final Office office, final String currencyCode, final GLAccount account,
final Long shareAccountId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
Long shareTransactionId = null;
final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
shareTransactionId = Long.parseLong(transactionId);
modifiedTransactionId = SHARE_TRANSACTION_IDENTIFIER + transactionId;
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SHARES.getValue(),
shareAccountId, null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.save(journalEntry);
}
private GLAccount getLinkedGLAccountForLoanProduct(final Long loanProductId, final int accountMappingTypeId, final Long paymentTypeId) {
GLAccount glAccount = null;
if (isOrganizationAccount(accountMappingTypeId)) {
@@ -984,6 +1144,48 @@ public class AccountingProcessorHelper {
return glAccount;
}
private GLAccount getLinkedGLAccountForShareProduct(final Long shareProductId, final int accountMappingTypeId, final Long paymentTypeId) {
GLAccount glAccount = null;
if (isOrganizationAccount(accountMappingTypeId)) {
FinancialActivityAccount financialActivityAccount = this.financialActivityAccountRepository
.findByFinancialActivityTypeWithNotFoundDetection(accountMappingTypeId);
glAccount = financialActivityAccount.getGlAccount();
} else {
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(shareProductId,
PortfolioProductType.SHARES.getValue(), accountMappingTypeId);
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue()) {
final ProductToGLAccountMapping paymentChannelSpecificAccountMapping = this.accountMappingRepository
.findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentTypeId(shareProductId,
PortfolioProductType.SHARES.getValue(), accountMappingTypeId, paymentTypeId);
if (paymentChannelSpecificAccountMapping != null) {
accountMapping = paymentChannelSpecificAccountMapping;
}
}
glAccount = accountMapping.getGlAccount();
}
return glAccount;
}
private GLAccount getLinkedGLAccountForShareCharges(final Long shareProductId, final int accountMappingTypeId, final Long chargeId) {
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(shareProductId,
PortfolioProductType.SHARES.getValue(), accountMappingTypeId);
/*****
* Get more specific mappings for Charges and penalties (based on the
* actual charge /penalty coupled with the loan product). Note the
* income from fees and income from penalties placeholder ID would be
* the same for both cash and accrual based accounts
*****/
final ProductToGLAccountMapping chargeSpecificIncomeAccountMapping = this.accountMappingRepository
.findByProductIdAndProductTypeAndFinancialAccountTypeAndChargeId(shareProductId, PortfolioProductType.SHARES.getValue(),
accountMappingTypeId, chargeId);
if (chargeSpecificIncomeAccountMapping != null) {
accountMapping = chargeSpecificIncomeAccountMapping;
}
return accountMapping.getGlAccount();
}
private boolean isOrganizationAccount(final int accountMappingTypeId) {
boolean isOrganizationAccount = false;
if (FINANCIAL_ACTIVITY.fromInt(accountMappingTypeId) != null) {
@@ -124,6 +124,12 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
}
}
/** Handle Deposits and reversals of Dividend pay outs **/
else if (savingsTransactionDTO.getTransactionType().isDividendPayout()) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
FINANCIAL_ACTIVITY.PAYABLE_DIVIDENDS.getValue(), CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL.getValue(),
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
}
/** Handle withdrawals and reversals of withdrawals **/
else if (savingsTransactionDTO.getTransactionType().isWithdrawal()) {
if (savingsTransactionDTO.isAccountTransfer()) {
@@ -0,0 +1,129 @@
/**
* 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.accounting.journalentry.service;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.apache.fineract.accounting.closure.domain.GLClosure;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SHARES;
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
import org.apache.fineract.accounting.journalentry.data.SharesDTO;
import org.apache.fineract.accounting.journalentry.data.SharesTransactionDTO;
import org.apache.fineract.organisation.office.domain.Office;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CashBasedAccountingProcessorForShares implements AccountingProcessorForShares {
private final AccountingProcessorHelper helper;
@Autowired
public CashBasedAccountingProcessorForShares(final AccountingProcessorHelper helper) {
this.helper = helper;
}
@Override
public void createJournalEntriesForShares(SharesDTO sharesDTO) {
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(sharesDTO.getOfficeId());
final Long shareAccountId = sharesDTO.getShareAccountId();
final Long shareProductId = sharesDTO.getShareProductId();
final String currencyCode = sharesDTO.getCurrencyCode();
for (SharesTransactionDTO transactionDTO : sharesDTO.getNewTransactions()) {
final Date transactionDate = transactionDTO.getTransactionDate();
final String transactionId = transactionDTO.getTransactionId();
final Office office = this.helper.getOfficeById(transactionDTO.getOfficeId());
final Long paymentTypeId = transactionDTO.getPaymentTypeId();
final BigDecimal amount = transactionDTO.getAmount();
final BigDecimal chargeAmount = transactionDTO.getChargeAmount();
final List<ChargePaymentDTO> feePayments = transactionDTO.getFeePayments();
this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
if (transactionDTO.getTransactionType().isPurchased()) {
createJournalEntriesForPurchase(shareAccountId, shareProductId, currencyCode, transactionDTO, transactionDate,
transactionId, office, paymentTypeId, amount, chargeAmount, feePayments);
} else if (transactionDTO.getTransactionType().isRedeemed() && transactionDTO.getTransactionStatus().isApproved()) {
createJournalEntriesForRedeem(shareAccountId, shareProductId, currencyCode, transactionDate, transactionId, office,
paymentTypeId, amount, chargeAmount, feePayments);
} else if (transactionDTO.getTransactionType().isChargePayment()) {
this.helper.createCashBasedJournalEntriesForSharesCharges(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE,
CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES, shareProductId, paymentTypeId, shareAccountId, transactionId,
transactionDate, amount, feePayments);
}
}
}
public void createJournalEntriesForRedeem(final Long shareAccountId, final Long shareProductId, final String currencyCode,
final Date transactionDate, final String transactionId, final Office office, final Long paymentTypeId, final BigDecimal amount,
final BigDecimal chargeAmount, final List<ChargePaymentDTO> feePayments) {
if (chargeAmount == null || chargeAmount.compareTo(BigDecimal.ZERO) != 1) {
this.helper.createJournalEntriesForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_EQUITY.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(), shareProductId, paymentTypeId, shareAccountId, transactionId,
transactionDate, amount);
} else {
this.helper.createDebitJournalEntryForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_EQUITY.getValue(),
shareProductId, paymentTypeId, shareAccountId, transactionId, transactionDate, amount.add(chargeAmount));
this.helper.createCreditJournalEntryForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(),
shareProductId, paymentTypeId, shareAccountId, transactionId, transactionDate, amount);
this.helper.createCashBasedJournalEntryForSharesCharges(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES,
shareProductId, shareAccountId, transactionId, transactionDate, chargeAmount, feePayments);
}
}
public void createJournalEntriesForPurchase(final Long shareAccountId, final Long shareProductId, final String currencyCode,
SharesTransactionDTO transactionDTO, final Date transactionDate, final String transactionId, final Office office,
final Long paymentTypeId, final BigDecimal amount, final BigDecimal chargeAmount, final List<ChargePaymentDTO> feePayments) {
if (transactionDTO.getTransactionStatus().isApplied()) {
if (chargeAmount == null || chargeAmount.compareTo(BigDecimal.ZERO) != 1) {
this.helper.createJournalEntriesForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue(), shareProductId, paymentTypeId, shareAccountId, transactionId,
transactionDate, amount);
} else {
this.helper.createDebitJournalEntryForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(),
shareProductId, paymentTypeId, shareAccountId, transactionId, transactionDate, amount);
this.helper.createCreditJournalEntryForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue(),
shareProductId, paymentTypeId, shareAccountId, transactionId, transactionDate, amount.subtract(chargeAmount));
this.helper.createCashBasedJournalEntryForSharesCharges(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES,
shareProductId, shareAccountId, transactionId, transactionDate, chargeAmount, feePayments);
}
} else if (transactionDTO.getTransactionStatus().isApproved()) {
BigDecimal amountForJE = amount;
if (chargeAmount != null && chargeAmount.compareTo(BigDecimal.ZERO) == 1) {
amountForJE = amount.subtract(chargeAmount);
}
this.helper.createJournalEntriesForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_EQUITY.getValue(), shareProductId, paymentTypeId, shareAccountId, transactionId,
transactionDate, amountForJE);
} else if (transactionDTO.getTransactionStatus().isRejected()) {
BigDecimal amountForJE = amount;
if (chargeAmount != null && chargeAmount.compareTo(BigDecimal.ZERO) == 1) {
amountForJE = amount.subtract(chargeAmount);
}
this.helper.createJournalEntriesForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(), shareProductId, paymentTypeId, shareAccountId, transactionId,
transactionDate, amountForJE);
}
}
}
@@ -42,4 +42,6 @@ public interface JournalEntryWritePlatformService {
public String revertProvisioningJournalEntries(final Date reversalTransactionDate, final Long entityId, final Integer entityType) ;
public String createProvisioningJournalEntries(ProvisioningEntry entry) ;
void createJournalEntriesForShares(Map<String, Object> accountingBridgeData);
}
@@ -45,6 +45,7 @@ import org.apache.fineract.accounting.journalentry.command.SingleDebitOrCreditEn
import org.apache.fineract.accounting.journalentry.data.ClientTransactionDTO;
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
import org.apache.fineract.accounting.journalentry.data.SharesDTO;
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
@@ -94,6 +95,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
private final OfficeRepository officeRepository;
private final AccountingProcessorForLoanFactory accountingProcessorForLoanFactory;
private final AccountingProcessorForSavingsFactory accountingProcessorForSavingsFactory;
private final AccountingProcessorForSharesFactory accountingProcessorForSharesFactory;
private final AccountingProcessorHelper helper;
private final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final AccountingRuleRepository accountingRuleRepository;
@@ -111,6 +113,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final AccountingProcessorHelper accountingProcessorHelper, final AccountingRuleRepository accountingRuleRepository,
final AccountingProcessorForLoanFactory accountingProcessorForLoanFactory,
final AccountingProcessorForSavingsFactory accountingProcessorForSavingsFactory,
final AccountingProcessorForSharesFactory accountingProcessorForSharesFactory,
final GLAccountReadPlatformService glAccountReadPlatformService,
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository, final PlatformSecurityContext context,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
@@ -123,6 +126,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
this.glAccountRepository = glAccountRepository;
this.accountingProcessorForLoanFactory = accountingProcessorForLoanFactory;
this.accountingProcessorForSavingsFactory = accountingProcessorForSavingsFactory;
this.accountingProcessorForSharesFactory = accountingProcessorForSharesFactory;
this.helper = accountingProcessorHelper;
this.accountingRuleRepository = accountingRuleRepository;
this.glAccountReadPlatformService = glAccountReadPlatformService;
@@ -335,13 +339,13 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry,
journalEntry.getTransactionDate(), JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment, null, null,
journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(),
journalEntry.getClientTransaction());
journalEntry.getClientTransaction(), journalEntry.getShareTransactionId());
} else {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry,
journalEntry.getTransactionDate(), JournalEntryType.DEBIT, journalEntry.getAmount(), reversalComment, null, null,
journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(),
journalEntry.getClientTransaction());
journalEntry.getClientTransaction(), journalEntry.getShareTransactionId());
}
// save the reversal entry
this.glJournalEntryRepository.saveAndFlush(reversalJournalEntry);
@@ -366,13 +370,15 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), journalEntry.getTransactionId(), Boolean.FALSE,
reversalTransactionDate, JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment,
journalEntry.getEntityType(), journalEntry.getEntityId(), journalEntry.getReferenceNumber(),
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction());
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction(),
journalEntry.getShareTransactionId());
} else {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), journalEntry.getTransactionId(), Boolean.FALSE,
reversalTransactionDate, JournalEntryType.DEBIT, journalEntry.getAmount(), reversalComment,
journalEntry.getEntityType(), journalEntry.getEntityId(), journalEntry.getReferenceNumber(),
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction());
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction(),
journalEntry.getShareTransactionId());
}
// save the reversal entry
this.glJournalEntryRepository.save(reversalJournalEntry);
@@ -428,26 +434,26 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
expenseMap.put(entry.getExpenseAccount(), amount);
}
}
createJournalEnry(provisioningEntry.getCreatedDate(), provisioningEntry.getId(), key.office, key.currency, liabilityMap, expenseMap);
createJournalEnry(provisioningEntry.getCreatedDate(), provisioningEntry.getId(), key.office, key.currency, liabilityMap,
expenseMap);
}
return "P"+provisioningEntry.getId() ;
return "P" + provisioningEntry.getId();
}
private void createJournalEnry(Date transactionDate, Long entryId, Office office, String currencyCode, Map<GLAccount, BigDecimal> liabilityMap,
Map<GLAccount, BigDecimal> expenseMap) {
private void createJournalEnry(Date transactionDate, Long entryId, Office office, String currencyCode,
Map<GLAccount, BigDecimal> liabilityMap, Map<GLAccount, BigDecimal> expenseMap) {
Set<GLAccount> liabilityAccounts = liabilityMap.keySet();
for (GLAccount account : liabilityAccounts) {
this.helper.createProvisioningCreditJournalEntry(transactionDate,entryId, office, currencyCode, account,
this.helper.createProvisioningCreditJournalEntry(transactionDate, entryId, office, currencyCode, account,
liabilityMap.get(account));
}
Set<GLAccount> expenseAccounts = expenseMap.keySet();
for (GLAccount account : expenseAccounts) {
this.helper.createProvisioningDebitJournalEntry(transactionDate,entryId, office, currencyCode, account,
this.helper.createProvisioningDebitJournalEntry(transactionDate, entryId, office, currencyCode, account,
expenseMap.get(account));
}
}
private void validateCommentForReversal(final String reversalComment) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
@@ -493,6 +499,23 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
@Transactional
@Override
public void createJournalEntriesForShares(final Map<String, Object> accountingBridgeData) {
final boolean cashBasedAccountingEnabled = (Boolean) accountingBridgeData.get("cashBasedAccountingEnabled");
final boolean accrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("accrualBasedAccountingEnabled");
if (cashBasedAccountingEnabled) {
final SharesDTO sharesDTO = this.helper.populateSharesDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled,
accrualBasedAccountingEnabled);
final AccountingProcessorForShares accountingProcessorForShares = this.accountingProcessorForSharesFactory
.determineProcessor(sharesDTO);
accountingProcessorForShares.createJournalEntriesForShares(sharesDTO);
}
}
private void validateBusinessRulesForJournalEntries(final JournalEntryCommand command) {
/** check if date of Journal entry is valid ***/
final LocalDate entryLocalDate = command.getTransactionDate();
@@ -539,9 +562,10 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
this.organisationCurrencyRepository.findOneWithNotFoundDetection(currencyCode);
final ClientTransaction clientTransaction = null;
final Long shareTransactionId = null;
final JournalEntry glJournalEntry = JournalEntry.createNew(office, paymentDetail, glAccount, currencyCode, transactionId,
manualEntry, transactionDate, type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, referenceNumber,
null, null, clientTransaction);
null, null, clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(glJournalEntry);
}
}
@@ -648,14 +672,15 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
final ClientTransaction clientTransaction = null;
final Long shareTransactionId = null;
final JournalEntry glJournalEntry = JournalEntry.createNew(office, null, glAccount, currencyCode, transactionId, manualEntry,
transactionDate, type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, null, null, null,
clientTransaction);
clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(glJournalEntry);
final JournalEntry contraEntry = JournalEntry.createNew(office, null, contraAccount, currencyCode, transactionId, manualEntry,
transactionDate, contraType, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, null, null, null,
clientTransaction);
clientTransaction, shareTransactionId);
this.glJournalEntryRepository.saveAndFlush(contraEntry);
}
}
@@ -682,7 +707,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final ClientTransactionDTO clientTransactionDTO = this.helper.populateClientTransactionDtoFromMap(accountingBridgeData);
accountingProcessorForClientTransactions.createJournalEntriesForClientTransaction(clientTransactionDTO);
}
private class OfficeCurrencyKey {
Office office;
@@ -22,7 +22,8 @@ import java.util.HashMap;
import java.util.Map;
public enum PortfolioProductType {
LOAN(1, "productType.loan"), SAVING(2, "productType.saving"), CLIENT(2, "productType.client"), PROVISIONING(3, "productType.provisioning");
LOAN(1, "productType.loan"), SAVING(2, "productType.saving"), CLIENT(2, "productType.client"), PROVISIONING(3,
"productType.provisioning"), SHARES(4, "productType.shares");
private final Integer value;
private final String code;
@@ -70,4 +71,8 @@ public enum PortfolioProductType {
return this.value.equals(PortfolioProductType.CLIENT.getValue());
}
public boolean isShareProduct() {
return this.value.equals(PortfolioProductType.SHARES.getValue());
}
}
@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.accounting.common.AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
@@ -38,6 +39,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -118,8 +120,8 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue()).value(incomeFromRecoveryAccountId)
.notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue())
.value(incomeFromRecoveryAccountId).notNull().integerGreaterThanZero();
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
@@ -227,6 +229,47 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForShareProductCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ShareProductApiConstants.SHARE_PRODUCT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
// accounting related data validation
final Integer accountingRuleType = this.fromApiJsonHelper
.extractIntegerNamed(accountingRuleParamName, element, Locale.getDefault());
baseDataValidator.reset().parameter(accountingRuleParamName).value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBasedAccounting(accountingRuleType)) {
final Long shareReferenceId = this.fromApiJsonHelper.extractLongNamed(
SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue(), element);
baseDataValidator.reset().parameter(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue())
.value(shareReferenceId).notNull().integerGreaterThanZero();
final Long shareSuspenseId = this.fromApiJsonHelper.extractLongNamed(
SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue())
.value(shareSuspenseId).notNull().integerGreaterThanZero();
final Long incomeFromFeeAccountId = this.fromApiJsonHelper.extractLongNamed(
SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), element);
baseDataValidator.reset().parameter(SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue())
.value(incomeFromFeeAccountId).notNull().integerGreaterThanZero();
final Long shareEquityId = this.fromApiJsonHelper.extractLongNamed(
SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue(), element);
baseDataValidator.reset().parameter(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue())
.value(shareEquityId).notNull().integerGreaterThanZero();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private boolean isCashBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}
@@ -41,5 +41,11 @@ public interface ProductToGLAccountMappingReadPlatformService {
public List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForSavingsProduct(final Long savingsProductId);
public List<ChargeToGLAccountMapper> fetchPenaltyToIncomeAccountMappingsForSavingsProduct(final Long savingsProductId);
public Map<String, Object> fetchAccountMappingDetailsForShareProduct(final Long productId, final Integer accountingType);
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForShareProduct(final Long productId);
public List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(final Long productId);
}
@@ -29,8 +29,10 @@ import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SHARES;
import org.apache.fineract.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS;
import org.apache.fineract.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_DATA_PARAMS;
import org.apache.fineract.accounting.common.AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
@@ -326,4 +328,50 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
return chargeToGLAccountMappers;
}
@Override
public Map<String, Object> fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null ";
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, new Object[] {
PortfolioProductType.SHARES.getValue(), productId });
if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType");
final CASH_ACCOUNTS_FOR_SHARES glAccountForShares = CASH_ACCOUNTS_FOR_SHARES.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final String glAccountName = (String) productToGLAccountMap.get("glAccountName");
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData(glAccountId, glAccountName, glCode);
if (glAccountForShares.equals(CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE)) {
accountMappingDetails.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue(), gLAccountData);
} else if (glAccountForShares.equals(CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE)) {
accountMappingDetails.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue(), gLAccountData);
} else if (glAccountForShares.equals(CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES)) {
accountMappingDetails.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), gLAccountData);
} else if (glAccountForShares.equals(CASH_ACCOUNTS_FOR_SHARES.SHARES_EQUITY)) {
accountMappingDetails.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue(), gLAccountData);
}
}
}
return accountMappingDetails;
}
@Override
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForShareProduct(Long productId) {
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.SHARES, productId);
}
@Override
public List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(Long productId) {
return fetchChargeToIncomeAccountMappings(PortfolioProductType.SHARES, productId, false);
}
}
@@ -35,4 +35,9 @@ public interface ProductToGLAccountMappingWritePlatformService {
Map<String, Object> updateSavingsProductToGLAccountMapping(Long savingsProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId, DepositAccountType accountType);
void createShareProductToGLAccountMapping(Long shareProductId, JsonCommand command);
Map<String, Object> updateShareProductToGLAccountMapping(Long shareProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId);
}
@@ -24,10 +24,12 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SHARES;
import org.apache.fineract.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.producttoaccountmapping.serialization.ProductToGLAccountMappingFromApiJsonDeserializer;
@@ -47,16 +49,19 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
private final ProductToGLAccountMappingFromApiJsonDeserializer deserializer;
private final LoanProductToGLAccountMappingHelper loanProductToGLAccountMappingHelper;
private final SavingsProductToGLAccountMappingHelper savingsProductToGLAccountMappingHelper;
private final ShareProductToGLAccountMappingHelper shareProductToGLAccountMappingHelper;
@Autowired
public ProductToGLAccountMappingWritePlatformServiceImpl(final FromJsonHelper fromApiJsonHelper,
final ProductToGLAccountMappingFromApiJsonDeserializer deserializer,
final LoanProductToGLAccountMappingHelper loanProductToGLAccountMappingHelper,
final SavingsProductToGLAccountMappingHelper savingsProductToGLAccountMappingHelper) {
final SavingsProductToGLAccountMappingHelper savingsProductToGLAccountMappingHelper,
final ShareProductToGLAccountMappingHelper shareProductToGLAccountMappingHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.deserializer = deserializer;
this.loanProductToGLAccountMappingHelper = loanProductToGLAccountMappingHelper;
this.savingsProductToGLAccountMappingHelper = savingsProductToGLAccountMappingHelper;
this.shareProductToGLAccountMappingHelper = shareProductToGLAccountMappingHelper;
}
@Override
@@ -230,6 +235,50 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
}
@Override
@Transactional
public void createShareProductToGLAccountMapping(final Long shareProductId, final JsonCommand command) {
this.deserializer.validateForShareProductCreate(command.json());
final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final Integer accountingRuleTypeId = this.fromApiJsonHelper.extractIntegerNamed(accountingRuleParamName, element,
Locale.getDefault());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
this.shareProductToGLAccountMappingHelper.saveSharesToAssetAccountMapping(element,
SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue(), shareProductId,
CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue());
// income
this.shareProductToGLAccountMappingHelper.saveSharesToIncomeAccountMapping(element,
SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), shareProductId,
CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES.getValue());
// expenses
this.shareProductToGLAccountMappingHelper.saveSharesToEquityAccountMapping(element,
SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue(), shareProductId,
CASH_ACCOUNTS_FOR_SHARES.SHARES_EQUITY.getValue());
// liability
this.shareProductToGLAccountMappingHelper.saveSharesToLiabilityAccountMapping(element,
SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue(), shareProductId,
CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue());
// advanced accounting mappings
this.savingsProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, shareProductId, null);
this.savingsProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, shareProductId, null);
break;
default:
break;
}
}
@Override
@Transactional
public Map<String, Object> updateLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command,
@@ -293,4 +342,34 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
}
return changes;
}
@Override
public Map<String, Object> updateShareProductToGLAccountMapping(final Long shareProductId, final JsonCommand command,
final boolean accountingRuleChanged, final int accountingRuleTypeId) {
/***
* Variable tracks all accounting mapping properties that have been
* updated
***/
Map<String, Object> changes = new HashMap<>();
final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
/***
* If the accounting rule has been changed, delete all existing mapping
* for the product and recreate a new set of mappings
***/
if (accountingRuleChanged) {
this.shareProductToGLAccountMappingHelper.deleteSharesProductToGLAccountMapping(shareProductId);
createShareProductToGLAccountMapping(shareProductId, command);
changes = this.shareProductToGLAccountMappingHelper.populateChangesForNewSharesProductToGLAccountMappingCreation(element,
accountingRuleType);
}/*** else examine and update individual changes ***/
else {
this.shareProductToGLAccountMappingHelper.handleChangesToSharesProductToGLAccountMappings(shareProductId, changes, element,
accountingRuleType);
this.shareProductToGLAccountMappingHelper.updatePaymentChannelToFundSourceMappings(command, element, shareProductId, changes);
this.shareProductToGLAccountMappingHelper.updateChargesToIncomeAccountMappings(command, element, shareProductId, changes);
}
return changes;
}
}
@@ -0,0 +1,204 @@
/**
* 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.accounting.producttoaccountmapping.service;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SHARES;
import org.apache.fineract.accounting.common.AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
@Component
public class ShareProductToGLAccountMappingHelper extends ProductToGLAccountMappingHelper {
@Autowired
public ShareProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper,
final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper) {
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper,
paymentTypeRepositoryWrapper);
}
/*** Set of abstractions for saving Share Products to GL Account Mappings ***/
public void saveSharesToAssetAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.ASSET, PortfolioProductType.SHARES);
}
public void saveSharesToIncomeAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.INCOME, PortfolioProductType.SHARES);
}
public void saveSharesToEquityAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.EQUITY, PortfolioProductType.SHARES);
}
public void saveSharesToLiabilityAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.LIABILITY, PortfolioProductType.SHARES);
}
/*** Set of abstractions for merging Shares Products to GL Account Mappings ***/
public void mergeSharesToAssetAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.ASSET,
PortfolioProductType.SHARES);
}
public void mergeSharesToIncomeAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.INCOME,
PortfolioProductType.SHARES);
}
public void mergeSharesToEquityAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.EQUITY,
PortfolioProductType.SHARES);
}
public void mergeSharesToLiabilityAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes,
GLAccountType.LIABILITY, PortfolioProductType.SHARES);
}
/*** Abstractions for payments channel related to Shares products ***/
public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
savePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SHARES);
}
public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
updatePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SHARES);
}
public void saveChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
saveChargesToIncomeOrLiabilityAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, true);
saveChargesToIncomeOrLiabilityAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, false);
}
public void updateChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, true);
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, false);
}
public Map<String, Object> populateChangesForNewSharesProductToGLAccountMappingCreation(final JsonElement element,
final AccountingRuleType accountingRuleType) {
final Map<String, Object> changes = new HashMap<>();
final Long shareReferenceId = this.fromApiJsonHelper.extractLongNamed(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue(),
element);
final Long incomeFromFeeAccountId = this.fromApiJsonHelper.extractLongNamed(
SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), element);
final Long shareSuspenseId = this.fromApiJsonHelper.extractLongNamed(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue(),
element);
final Long shareEquityId = this.fromApiJsonHelper.extractLongNamed(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue(),
element);
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
changes.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue(), shareReferenceId);
changes.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeeAccountId);
changes.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue(), shareSuspenseId);
changes.put(SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue(), shareEquityId);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
default:
break;
}
return changes;
}
/**
* Examines and updates each account mapping for given loan product with
* changes passed in from the Json element
*
* @param sharesProductId
* @param changes
* @param element
* @param accountingRuleType
*/
public void handleChangesToSharesProductToGLAccountMappings(final Long sharesProductId, final Map<String, Object> changes,
final JsonElement element, final AccountingRuleType accountingRuleType) {
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
mergeSharesToAssetAccountMappingChanges(element, SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue(),
sharesProductId, CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.toString(), changes);
// income
mergeSharesToIncomeAccountMappingChanges(element, SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
sharesProductId, CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES.getValue(),
CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES.toString(), changes);
// liability
mergeSharesToLiabilityAccountMappingChanges(element, SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue(),
sharesProductId, CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.toString(), changes);
// equity
mergeSharesToEquityAccountMappingChanges(element, SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue(),
sharesProductId, CASH_ACCOUNTS_FOR_SHARES.SHARES_EQUITY.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_EQUITY.toString(), changes);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
default:
break;
}
}
public void deleteSharesProductToGLAccountMapping(final Long sharesProductId) {
deleteProductToGLAccountMapping(sharesProductId, PortfolioProductType.SHARES);
}
}
@@ -830,7 +830,7 @@ public class CommandWrapperBuilder {
this.href = "/loans/" + loanId;
return this;
}
public CommandWrapperBuilder undoLastDisbursalLoanApplication(final Long loanId) {
this.actionName = "DISBURSALLASTUNDO";
this.entityName = "LOAN";
@@ -2584,36 +2584,82 @@ public class CommandWrapperBuilder {
this.href = "/loans/" + loanId + "/schedule";
return this;
}
public CommandWrapperBuilder createProduct(String productType) {
this.entityName = productType.toUpperCase()+"PRODUCT" ; //To Support different type of products
this.entityName = productType.toUpperCase() + "PRODUCT"; // To Support
// different
// type of
// products
this.actionName = "CREATE";
this.entityId = null;
this.href = "/products/"+productType;
this.href = "/products/" + productType;
return this;
}
public CommandWrapperBuilder updateProduct(String productType, final Long productId) {
this.entityName = productType.toUpperCase()+"PRODUCT" ;
this.entityName = productType.toUpperCase() + "PRODUCT";
this.actionName = "UPDATE";
this.entityId = productId;
this.href = "/products/" + productType+"/"+productId;
this.href = "/products/" + productType + "/" + productId;
return this;
}
public CommandWrapperBuilder createAccount(String accountType) {
this.entityName = accountType.toUpperCase()+"ACCOUNT" ; //To Support different type of Accounts
this.entityName = accountType.toUpperCase() + "ACCOUNT"; // To Support
// different
// type of
// Accounts
this.actionName = "CREATE";
this.entityId = null;
this.href = "/accounts/"+accountType;
this.href = "/accounts/" + accountType;
return this;
}
public CommandWrapperBuilder updateAccount(String accountType, final Long accountId) {
this.entityName = accountType.toUpperCase() + "ACCOUNT";
this.actionName = "UPDATE";
this.entityId = accountId;
this.href = "/accounts/" + accountType + "/" + accountId;
return this;
}
public CommandWrapperBuilder createProductCommand(String productType, String command, final Long productId) {
this.entityName = productType.toUpperCase() + "PRODUCT";
this.actionName = "CREATE" + "_" + command.toUpperCase();
this.entityId = productId;
this.href = "/products/" + productType + "/" + productId + "?command=" + command;
return this;
}
public CommandWrapperBuilder createShareProductDividendPayoutCommand(final Long productId) {
this.entityName = "SHAREPRODUCT";
this.actionName = "CREATE_DIVIDEND";
this.entityId = productId;
this.href = "/shareproduct/" + productId + "/dividend";
return this;
}
public CommandWrapperBuilder approveShareProductDividendPayoutCommand(final Long productId, final Long dividendId) {
this.entityName = "SHAREPRODUCT";
this.actionName = "APPROVE_DIVIDEND";
this.entityId = dividendId;
this.href = "/shareproduct/" + productId + "/dividend/" + dividendId;
return this;
}
public CommandWrapperBuilder updateAccount(String accountType, final Long accountId) {
public CommandWrapperBuilder deleteShareProductDividendPayoutCommand(final Long productId, final Long dividendId) {
this.entityName = "SHAREPRODUCT";
this.actionName = "DELETE_DIVIDEND";
this.entityId = dividendId;
this.href = "/shareproduct/" + productId + "/dividend/" + dividendId;
return this;
}
public CommandWrapperBuilder createAccountCommand(String accountType, final Long accountId, String command) {
this.entityName = accountType.toUpperCase()+"ACCOUNT" ;
this.actionName = "UPDATE";
this.actionName = command.toUpperCase();
this.entityId = accountId;
this.href = "/accounts/" + accountType+"/"+accountId;
this.href = "/accounts/" + accountType+"/"+accountId+"?command="+command;
return this;
}
@@ -23,7 +23,7 @@ import java.util.Map;
public enum EntityAccountType {
CLIENT(1, "accountType.client"), LOAN(2, "accountType.loan"), SAVINGS(3, "accountType.savings"), CENTER(4, "accountType.center"),
GROUP(5, "accountType.group");
GROUP(5, "accountType.group"), SHARES(6, "accountType.shares");
private final Integer value;
private final String code;
@@ -43,13 +43,13 @@ public final class SearchParameters {
private final Long savingsId;
private final Boolean orphansOnly;
//Provisning Entries Search Params
private final Long provisioningEntryId ;
private final Long productId ;
private final Long categoryId ;
private final boolean isSelfUser;
public static SearchParameters from(final String sqlSearch, final Long officeId, final String externalId, final String name,
// Provisning Entries Search Params
private final Long provisioningEntryId;
private final Long productId;
private final Long categoryId;
private final boolean isSelfUser;
public static SearchParameters from(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy) {
final Long staffId = null;
final String accountNo = null;
@@ -145,6 +145,20 @@ public final class SearchParameters {
loanId, savingsId, orphansOnly, isSelfUser);
}
public static SearchParameters forPaginationAndAccountNumberSearch(final Integer offset, final Integer limit, final String orderBy,
final String sortOrder, final String accountNumber) {
final Integer maxLimitAllowed = getCheckedLimit(limit);
final Long staffId = null;
final Long loanId = null;
final Long savingsId = null;
final Boolean orphansOnly = false;
final boolean isSelfUser = false;
return new SearchParameters(null, null, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder, staffId,
accountNumber, loanId, savingsId, orphansOnly, isSelfUser);
}
public static SearchParameters forPagination(final Integer offset, final Integer limit) {
final Integer maxLimitAllowed = getCheckedLimit(limit);
@@ -160,11 +174,11 @@ public final class SearchParameters {
loanId, savingsId, orphansOnly, isSelfUser);
}
public final static SearchParameters forProvisioningEntries(final Long provisioningEntryId, final Long officeId, final Long productId,
public final static SearchParameters forProvisioningEntries(final Long provisioningEntryId, final Long officeId, final Long productId,
final Long categoryId, final Integer offset, final Integer limit) {
return new SearchParameters(provisioningEntryId, officeId, productId, categoryId, offset, limit) ;
return new SearchParameters(provisioningEntryId, officeId, productId, categoryId, offset, limit);
}
public static SearchParameters forSavings(final String sqlSearch, final String externalId, final Integer offset, final Integer limit,
final String orderBy, final String sortOrder) {
@@ -216,11 +230,11 @@ public final class SearchParameters {
this.savingsId = savingsId;
this.orphansOnly = orphansOnly;
this.currencyCode = null;
this.provisioningEntryId = null ;
this.productId = null ;
this.categoryId = null ;
this.provisioningEntryId = null;
this.productId = null;
this.categoryId = null;
this.isSelfUser = isSelfUser;
}
private SearchParameters(final Long provisioningEntryId, final Long officeId, final Long productId, final Long categoryId,
@@ -242,17 +256,17 @@ public final class SearchParameters {
this.officeId = officeId;
this.offset = offset;
this.limit = limit;
this.provisioningEntryId = provisioningEntryId ;
this.productId = productId ;
this.categoryId = categoryId ;
this.provisioningEntryId = provisioningEntryId;
this.productId = productId;
this.categoryId = categoryId;
this.isSelfUser = false;
}
public SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name, final String hierarchy,
final String firstname, final String lastname, final Integer offset, final Integer limit, final String orderBy,
final String sortOrder, final Long staffId, final String accountNo, final Long loanId, final Long savingsId,
final Boolean orphansOnly, final String currencyCode) {
public SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy, final String firstname, final String lastname, final Integer offset, final Integer limit,
final String orderBy, final String sortOrder, final Long staffId, final String accountNo, final Long loanId,
final Long savingsId, final Boolean orphansOnly, final String currencyCode) {
this.sqlSearch = sqlSearch;
this.officeId = officeId;
this.externalId = externalId;
@@ -270,9 +284,9 @@ public final class SearchParameters {
this.savingsId = savingsId;
this.orphansOnly = orphansOnly;
this.currencyCode = currencyCode;
this.provisioningEntryId = null ;
this.productId = null ;
this.categoryId = null ;
this.provisioningEntryId = null;
this.productId = null;
this.categoryId = null;
this.isSelfUser = false;
}
@@ -400,32 +414,33 @@ public final class SearchParameters {
if (this.orphansOnly != null) { return this.orphansOnly; }
return false;
}
public Long getProvisioningEntryId() {
return this.provisioningEntryId;
}
public boolean isProvisioningEntryIdPassed() {
return this.provisioningEntryId != null && this.provisioningEntryId != 0 ;
return this.provisioningEntryId != null && this.provisioningEntryId != 0;
}
public Long getProductId() {
return this.productId;
}
public boolean isProductIdPassed() {
return this.productId != null && this.productId != 0 ;
return this.productId != null && this.productId != 0;
}
public Long getCategoryId() {
return this.categoryId;
}
public boolean isCategoryIdPassed() {
return this.categoryId != null && this.categoryId != 0 ;
return this.categoryId != null && this.categoryId != 0;
}
public boolean isSelfUser() {
return this.isSelfUser;
}
return this.isSelfUser;
}
}
@@ -25,7 +25,7 @@ public class FineractEntityAccessType {
public static final FineractEntityAccessType OFFICE_ACCESS_TO_LOAN_PRODUCTS = new FineractEntityAccessType("Office Access to Loan Products");
public static final FineractEntityAccessType OFFICE_ACCESS_TO_SAVINGS_PRODUCTS = new FineractEntityAccessType("Office Access to Savings Products");
public static final FineractEntityAccessType OFFICE_ACCESS_TO_CHARGES = new FineractEntityAccessType("Office Access to Fees/Charges");
private FineractEntityAccessType (String str) {
this.str = str;
}
@@ -27,7 +27,8 @@ public class FineractEntityType {
public static FineractEntityType LOAN_PRODUCT = new FineractEntityType ("loan_product", "Loan Products", "m_product_loan");
public static FineractEntityType SAVINGS_PRODUCT = new FineractEntityType ("savings_product", "Savings Products", "m_savings_product");
public static FineractEntityType CHARGE = new FineractEntityType ("charge", "Fees/Charges", "m_charge");
public static FineractEntityType SHARE_PRODUCT = new FineractEntityType("shares_product", "Shares Products", "m_share_product") ;
private FineractEntityType (String type, String description, String table_name) {
this.type = type;
this.description = description;
@@ -56,9 +57,11 @@ public class FineractEntityType {
retType = LOAN_PRODUCT;
} else if (type.equals(SAVINGS_PRODUCT)) {
retType = SAVINGS_PRODUCT;
} else if (type.equals(CHARGE))
retType = CHARGE;
} else if (type.equals(CHARGE)) {
retType = CHARGE;
}else if(type.equals(SHARE_PRODUCT)) {
retType = SHARE_PRODUCT ;
}
return retType;
}
@@ -29,17 +29,18 @@ public enum JobName {
TRANSFER_FEE_CHARGE_FOR_LOANS("Transfer Fee For Loans From Savings"), //
ACCOUNTING_RUNNING_BALANCE_UPDATE("Update Accounting Running Balances"), //
PAY_DUE_SAVINGS_CHARGES("Pay Due Savings Charges"), //
APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"),
EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"),
ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"),
UPDATE_NPA("Update Non Performing Assets"),
UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"),
TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"),
ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions"),
RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"),
GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule"),
GENERATE_LOANLOSS_PROVISIONING("Generate Loan Loss Provisioning");
APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"), //
EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"), //
ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"), //
UPDATE_NPA("Update Non Performing Assets"), //
UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"), //
TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"), //
ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions"), //
RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"), //
GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule"), //
GENERATE_LOANLOSS_PROVISIONING("Generate Loan Loss Provisioning"), //
POST_DIVIDENTS_FOR_SHARES("Post Dividends For Shares");
private final String name;
private JobName(final String name) {
@@ -422,6 +422,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
final String uniqueVal = String.valueOf(time) + currentUser.getId() + cashierOffice.getId();
final String transactionId = Long.toHexString(Long.parseLong(uniqueVal));
ClientTransaction clientTransaction = null;
final Long shareTransactionId = null;
final JournalEntry debitJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
// detail
@@ -430,7 +431,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
transactionId, false, // manual entry
cashierTxn.getTxnDate(), JournalEntryType.DEBIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
null, null, null, // entity Type, entityId, reference number
null, null, clientTransaction); // Loan and Savings Txn
null, null, clientTransaction, shareTransactionId); // Loan and Savings Txn
final JournalEntry creditJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
// detail
@@ -439,7 +440,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
transactionId, false, // manual entry
cashierTxn.getTxnDate(), JournalEntryType.CREDIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
null, null, null, // entity Type, entityId, reference number
null, null, clientTransaction); // Loan and Savings Txn
null, null, clientTransaction, shareTransactionId); // Loan and Savings Txn
this.glJournalEntryRepository.saveAndFlush(debitJournalEntry);
this.glJournalEntryRepository.saveAndFlush(creditJournalEntry);
@@ -23,30 +23,31 @@ import java.util.Collection;
/**
* Immutable data object representing a summary of various accounts.
*/
@SuppressWarnings("unused")
public class AccountSummaryCollectionData {
@SuppressWarnings("unused")
private final Collection<LoanAccountSummaryData> loanAccounts;
@SuppressWarnings("unused")
private final Collection<SavingsAccountSummaryData> savingsAccounts;
@SuppressWarnings("unused")
private final Collection<ShareAccountSummaryData> shareAccounts ;
private final Collection<LoanAccountSummaryData> memberLoanAccounts;
@SuppressWarnings("unused")
private final Collection<SavingsAccountSummaryData> memberSavingsAccounts;
public AccountSummaryCollectionData(final Collection<LoanAccountSummaryData> loanAccounts,
final Collection<SavingsAccountSummaryData> savingsAccounts) {
final Collection<SavingsAccountSummaryData> savingsAccounts, final Collection<ShareAccountSummaryData> shareAccounts) {
this.loanAccounts = defaultLoanAccountsIfEmpty(loanAccounts);
this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts);
this.shareAccounts = defaultShareAccountsIfEmpty(shareAccounts) ;
this.memberLoanAccounts = null;
this.memberSavingsAccounts = null;
}
public AccountSummaryCollectionData(final Collection<LoanAccountSummaryData> loanAccounts,
final Collection<SavingsAccountSummaryData> savingsAccounts, final Collection<LoanAccountSummaryData> memberLoanAccounts,
final Collection<SavingsAccountSummaryData> memberSavingsAccounts) {
this.loanAccounts = defaultLoanAccountsIfEmpty(loanAccounts);
this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts);
this.shareAccounts = null ;
this.memberLoanAccounts = defaultLoanAccountsIfEmpty(memberLoanAccounts);
this.memberSavingsAccounts = defaultSavingsAccountsIfEmpty(memberSavingsAccounts);
}
@@ -66,5 +67,13 @@ public class AccountSummaryCollectionData {
}
return returnCollection;
}
private Collection<ShareAccountSummaryData> defaultShareAccountsIfEmpty(final Collection<ShareAccountSummaryData> collection) {
Collection<ShareAccountSummaryData> returnCollection = null;
if (collection != null && !collection.isEmpty()) {
returnCollection = collection;
}
return returnCollection;
}
}
@@ -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.accountdetails.data;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountApplicationTimelineData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountStatusEnumData;
@SuppressWarnings("unused")
public class ShareAccountSummaryData {
private final Long id;
private final String accountNo;
private final Long totalApprovedShares;
private final Long totalPendingForApprovalShares;
private final String externalId;
private final Long productId;
private final String productName;
private final String shortProductName;
private final ShareAccountStatusEnumData status;
private final CurrencyData currency;
private final ShareAccountApplicationTimelineData timeline;
public ShareAccountSummaryData(final Long id, final String accountNo,
final String externalId, final Long productId,
final String productName, final String shortProductName,
final ShareAccountStatusEnumData status,
final CurrencyData currency, final Long approvedShares,
final Long pendingForApprovalShares,
final ShareAccountApplicationTimelineData timeline) {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
if(approvedShares == null) {
this.totalApprovedShares = new Long(0) ;
}else {
this.totalApprovedShares = approvedShares;
}
if(pendingForApprovalShares == null) {
this.totalPendingForApprovalShares = new Long(0) ;
}else {
this.totalPendingForApprovalShares = pendingForApprovalShares;
}
this.productId = productId;
this.productName = productName;
this.shortProductName = shortProductName;
this.status = status;
this.currency = currency;
this.timeline = timeline;
}
public String getAccountNo() {
return this.accountNo;
}
}
@@ -31,6 +31,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.accountdetails.data.AccountSummaryCollectionData;
import org.apache.fineract.portfolio.accountdetails.data.LoanAccountSummaryData;
import org.apache.fineract.portfolio.accountdetails.data.SavingsAccountSummaryData;
import org.apache.fineract.portfolio.accountdetails.data.ShareAccountSummaryData;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData;
@@ -39,6 +40,9 @@ import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountApplicationTimelineData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountStatusEnumData;
import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -68,7 +72,8 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
final String savingswhereClause = " where sa.client_id = ? order by sa.status_enum ASC, sa.account_no ASC";
final List<LoanAccountSummaryData> loanAccounts = retrieveLoanAccountDetails(loanwhereClause, new Object[] { clientId });
final List<SavingsAccountSummaryData> savingsAccounts = retrieveAccountDetails(savingswhereClause, new Object[] { clientId });
return new AccountSummaryCollectionData(loanAccounts, savingsAccounts);
final List<ShareAccountSummaryData> shareAccounts = retrieveShareAccountDetails(clientId) ;
return new AccountSummaryCollectionData(loanAccounts, savingsAccounts, shareAccounts);
}
@Override
@@ -121,6 +126,108 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
return this.jdbcTemplate.query(savingsSql, savingsAccountSummaryDataMapper, inputs);
}
private List<ShareAccountSummaryData> retrieveShareAccountDetails(final Long clientId) {
final ShareAccountSummaryDataMapper mapper = new ShareAccountSummaryDataMapper() ;
final String query = "select " + mapper.schema() + " where sa.client_id = ?" ;
return this.jdbcTemplate.query(query, mapper, new Object [] {clientId});
}
private final static class ShareAccountSummaryDataMapper implements RowMapper<ShareAccountSummaryData> {
private final String schema ;
ShareAccountSummaryDataMapper() {
final StringBuffer buff = new StringBuffer()
.append("sa.id as id, sa.external_id as externalId, sa.status_enum as statusEnum, ")
.append("sa.account_no as accountNo, sa.total_approved_shares as approvedShares, sa.total_pending_shares as pendingShares, ")
.append("sa.savings_account_id as savingsAccountNo, sa.minimum_active_period_frequency as minimumactivePeriod,")
.append("sa.minimum_active_period_frequency_enum as minimumactivePeriodEnum,")
.append("sa.lockin_period_frequency as lockinPeriod, sa.lockin_period_frequency_enum as lockinPeriodEnum, ")
.append("sa.submitted_date as submittedDate, sbu.username as submittedByUsername, ")
.append("sbu.firstname as submittedByFirstname, sbu.lastname as submittedByLastname, ")
.append("sa.rejected_date as rejectedDate, rbu.username as rejectedByUsername, ")
.append("rbu.firstname as rejectedByFirstname, rbu.lastname as rejectedByLastname, ")
.append("sa.approved_date as approvedDate, abu.username as approvedByUsername, ")
.append("abu.firstname as approvedByFirstname, abu.lastname as approvedByLastname, ")
.append("sa.activated_date as activatedDate, avbu.username as activatedByUsername, ")
.append("avbu.firstname as activatedByFirstname, avbu.lastname as activatedByLastname, ")
.append("sa.closed_date as closedDate, cbu.username as closedByUsername, ")
.append("cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname, ")
.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, ")
.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ")
.append("curr.display_symbol as currencyDisplaySymbol, sa.product_id as productId, p.name as productName, p.short_name as shortProductName ")
.append("from m_share_account sa ")
.append("join m_share_product as p on p.id = sa.product_id ")
.append("join m_currency curr on curr.code = sa.currency_code ")
.append("left join m_appuser sbu on sbu.id = sa.submitted_userid ")
.append("left join m_appuser rbu on rbu.id = sa.rejected_userid ")
.append("left join m_appuser abu on abu.id = sa.approved_userid ")
.append("left join m_appuser avbu on rbu.id = sa.activated_userid ")
.append("left join m_appuser cbu on cbu.id = sa.closed_userid ") ;
schema = buff.toString() ;
}
@Override
public ShareAccountSummaryData mapRow(ResultSet rs, int rowNum)
throws SQLException {
final Long id = JdbcSupport.getLong(rs, "id");
final String accountNo = rs.getString("accountNo");
final Long approvedShares = JdbcSupport.getLong(rs, "approvedShares");
final Long pendingShares = JdbcSupport.getLong(rs, "pendingShares");
final String externalId = rs.getString("externalId");
final Long productId = JdbcSupport.getLong(rs, "productId");
final String productName = rs.getString("productName");
final String shortProductName = rs.getString("shortProductName");
final Integer statusId = JdbcSupport.getInteger(rs, "statusEnum");
final ShareAccountStatusEnumData status = SharesEnumerations.status(statusId) ;
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf,
currencyDisplaySymbol, currencyNameCode);
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedDate");
final String submittedByUsername = rs.getString("submittedByUsername");
final String submittedByFirstname = rs.getString("submittedByFirstname");
final String submittedByLastname = rs.getString("submittedByLastname");
final LocalDate rejectedOnDate = JdbcSupport.getLocalDate(rs, "rejectedDate");
final String rejectedByUsername = rs.getString("rejectedByUsername");
final String rejectedByFirstname = rs.getString("rejectedByFirstname");
final String rejectedByLastname = rs.getString("rejectedByLastname");
final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedDate");
final String approvedByUsername = rs.getString("approvedByUsername");
final String approvedByFirstname = rs.getString("approvedByFirstname");
final String approvedByLastname = rs.getString("approvedByLastname");
final LocalDate activatedOnDate = JdbcSupport.getLocalDate(rs, "activatedDate");
final String activatedByUsername = rs.getString("activatedByUsername");
final String activatedByFirstname = rs.getString("activatedByFirstname");
final String activatedByLastname = rs.getString("activatedByLastname");
final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedDate");
final String closedByUsername = rs.getString("closedByUsername");
final String closedByFirstname = rs.getString("closedByFirstname");
final String closedByLastname = rs.getString("closedByLastname");
final ShareAccountApplicationTimelineData timeline = new ShareAccountApplicationTimelineData(submittedOnDate,
submittedByUsername, submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername,
rejectedByFirstname, rejectedByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname, activatedOnDate,
activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername, closedByFirstname,
closedByLastname);
return new ShareAccountSummaryData(id, accountNo, externalId, productId, productName, shortProductName, status, currency,
approvedShares, pendingShares, timeline);
}
public String schema() {
return this.schema ;
}
}
private static final class SavingsAccountSummaryDataMapper implements RowMapper<SavingsAccountSummaryData> {
final String schemaSql;
@@ -19,7 +19,6 @@
package org.apache.fineract.portfolio.accounts.api;
import java.util.Collection;
import java.util.HashSet;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -44,7 +43,6 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants;
import org.apache.fineract.portfolio.accounts.data.AccountData;
import org.apache.fineract.portfolio.accounts.service.AccountReadPlatformService;
import org.apache.fineract.portfolio.accounts.service.AccountsCommandsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
@@ -78,6 +76,21 @@ public class AccountsApiResource {
this.toApiObjectJsonSerializer = toApiObjectJsonSerializer ;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String template(@PathParam("type") final String accountType, @QueryParam("clientId") final Long clientId,
@QueryParam("productId") final Long productId,
@Context final UriInfo uriInfo) {
this.platformSecurityContext.authenticatedUser() ;
String serviceName = accountType+AccountsApiConstants.READPLATFORM_NAME ;
AccountReadPlatformService service = (AccountReadPlatformService) this.applicationContext.getBean(serviceName) ;
final AccountData accountData = service.retrieveTemplate(clientId, productId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, accountData, service.getResponseDataParams());
}
@GET
@Path("{accountId}")
@Consumes({ MediaType.APPLICATION_JSON })
@@ -85,9 +98,9 @@ public class AccountsApiResource {
public String retrieveAccount(@PathParam("accountId") final Long accountId, @PathParam("type") final String accountType,
@Context final UriInfo uriInfo) {
String serviceName = accountType+AccountsApiConstants.READPLATFORM_NAME ;
AccountReadPlatformService service = (AccountReadPlatformService) this.applicationContext.getBean(serviceName) ;
AccountData data = service.retrieveOne(accountId) ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
AccountReadPlatformService service = (AccountReadPlatformService) this.applicationContext.getBean(serviceName) ;
AccountData data = service.retrieveOne(accountId, settings.isTemplate()) ;
return this.toApiJsonSerializer.serialize(settings, data, service.getResponseDataParams());
}
@@ -118,12 +131,12 @@ public class AccountsApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String handleCommands(@PathParam("type") final String accountType, @PathParam("accountId") final Long accountId, @QueryParam("command") final String commandParam,
@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
String serviceName = accountType.toUpperCase()+AccountsApiConstants.ACCOUNT_COMMANDSERVICE ;
AccountsCommandsService service = (AccountsCommandsService) this.applicationContext.getBean(serviceName) ;
final Object obj = service.handleCommand(accountId, commandParam, apiRequestBodyAsJson) ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiObjectJsonSerializer.serialize(settings, obj, new HashSet<String>());
final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null;
this.platformSecurityContext.authenticatedUser();
commandWrapper = new CommandWrapperBuilder().createAccountCommand(accountType, accountId, commandParam).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
@PUT
@@ -18,60 +18,97 @@
*/
package org.apache.fineract.portfolio.accounts.constants;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface ShareAccountApiConstants {
//Command Strings
String APPROVE_COMMAND = "approve" ;
String REJECT_COMMAND = "reject" ;
String APPLY_ADDITIONALSHARES_COMMAND = "applyadditionalshares" ;
String APPROVE_ADDITIONSHARES_COMMAND = "approveadditionalshares" ;
String REJECT_ADDITIONSHARES_COMMAND = "rejectadditionalshares" ;
//
String id_paramname = "id" ;
String clientid_paramname = "clientId" ;
String productid_paramname = "productId" ;
String submitteddate_paramname = "submittedDate" ;
String approveddate_paramname = "approvedDate" ;
String fieldofferid_paramname = "fieldOfficerId" ;
String externalid_paramname = "externalId" ;
String currency_paramname = "currencyCode" ;
String digitsafterdecimal_paramname = "digitsAfterDecimal" ;
String inmultiplesof_paramname = "inMultiplesOf" ;
String purchasedshares_paramname = "purchasedShares" ;
String additionalshares_paramname = "additionalShares" ;
String suspenseaccount_paramname = "suspenseAccount" ;
String equityaccount_paramname = "equityAccount" ;
String savingsaccountid_paramname = "savingsAccountId" ;
String lockperiod_paramname = "lockPeriod" ;
String minimumactiveperiodfordividends_paramname = "minimumActivePeriodForDividends" ;
String allowdividendcalculationforinactiveclients_paramname = "allowDividendCalculationForInactiveClients" ;
String charges_paramname = "charges" ;
String purchaseddate_paramname = "purchasedDate" ;
String numberofshares_paramname = "numberOfShares" ;
String purchasedprice_paramname = "purchasedPrice" ;
public static final String amountParamName = "amount";
public static final String dateFormatParamName = "dateFormat";
public static final String dueAsOfDateParamName = "dueDate";
public static final String feeIntervalParamName = "feeInterval";
public static final String feeOnMonthDayParamName = "feeOnMonthDay";
public static final String localeParamName = "locale";
// Command Strings
String APPROVE_COMMAND = "approve";
String REJECT_COMMAND = "reject";
String APPLY_ADDITIONALSHARES_COMMAND = "applyadditionalshares";
String APPROVE_ADDITIONSHARES_COMMAND = "approveadditionalshares";
String REJECT_ADDITIONSHARES_COMMAND = "rejectadditionalshares";
//
String locale_paramname = "locale" ;
String dateformat_paramname = "dateFormat" ;
String id_paramname = "id";
String clientid_paramname = "clientId";
String productid_paramname = "productId";
String submitteddate_paramname = "submittedDate";
String approveddate_paramname = "approvedDate";
String activatedate_paramname = "activatedDate" ;
String fieldofferid_paramname = "fieldOfficerId";
String externalid_paramname = "externalId";
String currency_paramname = "currencyCode";
String digitsafterdecimal_paramname = "digitsAfterDecimal";
String inmultiplesof_paramname = "inMultiplesOf";
String requestedshares_paramname = "requestedShares";
String savingsaccountid_paramname = "savingsAccountId";
String lockinperiod_paramname = "lockinPeriodFrequency";
String lockperiodfrequencytype_paramname = "lockinPeriodFrequencyType";
String minimumactiveperiod_paramname = "minimumActivePeriod";
String minimumactiveperiodfrequencytype_paramname = "minimumActivePeriodFrequencyType";
String allowdividendcalculationforinactiveclients_paramname = "allowDividendCalculationForInactiveClients";
String charges_paramname = "charges";
String applicationdate_param = "applicationDate";
String purchaseddate_paramname = "purchasedDate";
String numberofshares_paramname = "numberOfShares";
String purchasedprice_paramname = "unitPrice";
String note_paramname = "note" ;
public String requesteddate_paramname = "requestedDate" ;
public String additionalshares_paramname = "additionalshares" ;
public String closeddate_paramname = "closedDate" ;
Set<String> supportedParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, id_paramname,clientid_paramname, productid_paramname,
submitteddate_paramname,approveddate_paramname, externalid_paramname, currency_paramname, digitsafterdecimal_paramname,
inmultiplesof_paramname, requestedshares_paramname,savingsaccountid_paramname,lockinperiod_paramname,
lockperiodfrequencytype_paramname,minimumactiveperiod_paramname, minimumactiveperiodfrequencytype_paramname,
allowdividendcalculationforinactiveclients_paramname, charges_paramname, applicationdate_param,
purchaseddate_paramname,numberofshares_paramname,purchasedprice_paramname));
Set<String> approvalParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, approveddate_paramname, note_paramname));
Set<String> activateParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, activatedate_paramname));
Set<String> closeParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, closeddate_paramname, note_paramname));
Set<String> addtionalSharesParameters = new HashSet<>(Arrays.asList(locale_paramname, requesteddate_paramname, requestedshares_paramname, purchasedprice_paramname, dateformat_paramname));
}
@@ -1,60 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.data;
import java.math.BigDecimal;
import java.util.Date;
public class PurchasedSharesData {
private final Date purchasedDate ;
private final Long numberOfShares ;
private final BigDecimal purchasedPrice ;
private final String status ;
public PurchasedSharesData(final Date purchasedDate, final Long numberOfShares, final BigDecimal purchasedPrice, final String status) {
this.purchasedDate = purchasedDate ;
this.numberOfShares = numberOfShares ;
this.purchasedPrice = purchasedPrice ;
this.status = status ;
}
public Date getPurchasedDate() {
return this.purchasedDate;
}
public Long getNumberOfShares() {
return this.numberOfShares;
}
public BigDecimal getPurchasedPrice() {
return this.purchasedPrice;
}
public String getStatus() {
return this.status ;
}
}
@@ -1,99 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.data;
import java.util.Collection;
import java.util.Date;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
public class ShareAccountData implements AccountData{
private Long id;
private String accountNo;
private String externalId;
private Long productId;
private String productName;
private String loanProductDescription;
private Long fieldOfficerId;
private String fieldOfficerName;
private Long clientId;
private String clientName;
private Long clientOfficeId;
private CurrencyData currency;
private Date submittedDate;
private Date approvedDate;
private Collection<PurchasedSharesData> purchasedShares;
private GLAccountData suspenseAccount;
private GLAccountData equityAccount;
private Long savingsAccountId;
private EnumOptionData lockPeriod;
private EnumOptionData minimumActivePeriodForDividends;
private Boolean allowDividendCalculationForInactiveClients;
private Collection<ShareChargeData> charges ;
private String status ;
public ShareAccountData(final Long id, final String accountNo, final Long clientId, final String clientName, final Long productId, final String productName,
final Long fieldOfficerId, final String externalId, final Date submittedDate, final Collection<PurchasedSharesData> purchasedShares, final GLAccountData suspenseAccount,
final GLAccountData equityAccount, final EnumOptionData lockPeriod, final EnumOptionData minimumActivePeriodForDividends,
final Boolean allowDividendCalculationForInactiveClients, final Collection<ShareChargeData> charges, final String status) {
this.id = id ;
this.accountNo = accountNo ;
this.clientId = clientId ;
this.clientName = clientName ;
this.productId = productId ;
this.productName = productName ;
this.fieldOfficerId = fieldOfficerId ;
this.externalId = externalId ;
this.submittedDate = submittedDate ;
this.purchasedShares = purchasedShares ;
this.suspenseAccount = suspenseAccount ;
this.equityAccount = equityAccount ;
this.lockPeriod = lockPeriod ;
this.minimumActivePeriodForDividends = minimumActivePeriodForDividends ;
this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients ;
this.charges = charges ;
this.status = status ;
}
}
@@ -1,93 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.domain;
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.fineract.portfolio.accounts.data.PurchasedSharesData;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_purchasedshares")
public class PurchasedShares extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "account_id", referencedColumnName = "id", nullable = false)
private ShareAccount shareAccount ;
@Column(name = "purchased_date")
@Temporal(TemporalType.DATE)
private Date purchasedDate ;
@Column(name = "share_count")
private Long totalShares ;
@Column(name = "share_price")
private BigDecimal shareValue ;
@Column(name ="status")
private String status = "Submitted";
protected PurchasedShares() {
}
public void setShareAccount(final ShareAccount shareAccount) {
this.shareAccount = shareAccount ;
}
public PurchasedShares(final Date purchasedDate, final Long totalShares, final BigDecimal shareValue) {
this.purchasedDate = purchasedDate ;
this.totalShares = totalShares ;
this.shareValue = shareValue ;
}
public PurchasedSharesData toData() {
return new PurchasedSharesData(this.purchasedDate, this.totalShares, this.shareValue, this.status) ;
}
public Date getPurchasedDate() {
return this.purchasedDate ;
}
public Long getTotalShares() {
return this.totalShares ;
}
public BigDecimal getPurchasePrice() {
return this.shareValue ;
}
public String getStatus() {
return this.status ;
}
public void setStatus(String status) {
this.status = status ;
}
}
@@ -1,333 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.accounts.data.PurchasedSharesData;
import org.apache.fineract.portfolio.accounts.data.ShareAccountData;
import org.apache.fineract.portfolio.accounts.data.ShareChargeData;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.shares.domain.ShareProduct;
import org.apache.fineract.useradministration.domain.AppUser;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.DateTime;
@Entity
@Table(name = "m_shareaccounts")
public class ShareAccount extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne
@JoinColumn(name = "client_id", nullable = true)
private Client client ;
@ManyToOne
@JoinColumn(name = "product_id", nullable = true)
private ShareProduct shareProduct ;
@Column(name = "submitted_date")
@Temporal(TemporalType.TIMESTAMP)
private Date submittedDate ;
@Column(name = "approved_date")
@Temporal(TemporalType.TIMESTAMP)
private Date approvedDate ;
@Column(name = "field_officer")
private Long fieldOfficerId ;
@Column(name = "external_id")
private String externalId ;
@Embedded
private MonetaryCurrency currency;
@ManyToOne
@JoinColumn(name = "suspense_account", nullable = true)
private GLAccount suspenseAccount ;
@ManyToOne
@JoinColumn(name = "equity_account", nullable = true)
private GLAccount equityAccount ;
@ManyToOne
@JoinColumn(name = "savings_id", nullable = true)
private SavingsAccount savingsAccount ;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "shareAccount", orphanRemoval = true)
private Set<PurchasedShares> purchasedShares ;
@Column(name = "allow_dividends_inactive_clients")
private Boolean allowDividendCalculationForInactiveClients;
@Enumerated(EnumType.ORDINAL)
@Column(name = "lock_period", nullable = true)
private PeriodFrequencyType lockPeriod;
@Enumerated(EnumType.ORDINAL)
@Column(name = "dividend_active_period", nullable = true)
private PeriodFrequencyType minimumActivePeriodForDividends;
@Column(name = "status")
private String status = "Submitted"; //change it to enum
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "shareAccount", orphanRemoval = true)
private Set<ShareAccountCharge> charges ;
protected ShareAccount() {
}
public ShareAccount(final Client client, final ShareProduct shareProduct, final Date submittedDate, final Date approvedDate,
final Long fieldOfficerId , final String externalId, final MonetaryCurrency currency, GLAccount suspenseAccount,
final GLAccount equityAccount, final SavingsAccount savingsAccount, final Set<PurchasedShares> purchasedShares,
final Boolean allowDividendCalculationForInactiveClients, final PeriodFrequencyType lockPeriod,
final PeriodFrequencyType minimumActivePeriodForDividends, Set<ShareAccountCharge> charges,
AppUser createdBy, DateTime createdDate, AppUser lastModifiedBy, DateTime lastModifiedDate) {
this.client = client ;
this.shareProduct = shareProduct ;
this.submittedDate = submittedDate ;
this.approvedDate = approvedDate ;
this.fieldOfficerId = fieldOfficerId ;
this.externalId = externalId ;
this.currency = currency ;
this.suspenseAccount = suspenseAccount ;
this.equityAccount = equityAccount ;
this.savingsAccount = savingsAccount ;
this.purchasedShares = purchasedShares ;
this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients ;
this.lockPeriod = lockPeriod ;
this.minimumActivePeriodForDividends = minimumActivePeriodForDividends ;
this.charges = charges ;
setCreatedBy(createdBy) ;
setCreatedDate(createdDate) ;
setLastModifiedBy(lastModifiedBy) ;
setLastModifiedDate(lastModifiedDate) ;
}
public boolean setShareProduct(final ShareProduct shareProduct) {
boolean toReturn = false ;
if(!this.shareProduct.getId().equals(shareProduct.getId())) {
this.shareProduct = shareProduct ;
toReturn = true ;
}
return toReturn ;
}
public ShareProduct getShareProduct() {
return this.shareProduct ;
}
public boolean setSubmittedDate(final Date submittedDate) {
boolean toReturn = false ;
if(!this.submittedDate.equals(submittedDate)) {
this.submittedDate = submittedDate ;
toReturn = true ;
}
return toReturn ;
}
public boolean setApprovedDate(final Date approvedDate) {
boolean toReturn = false ;
if(!this.approvedDate.equals(approvedDate)) {
this.approvedDate = approvedDate ;
toReturn = true ;
}
return toReturn ;
}
public boolean setFieldOfficer(final Long fieldOfficerId) {
boolean toReturn = false ;
if(!this.fieldOfficerId.equals(fieldOfficerId)) {
this.fieldOfficerId = fieldOfficerId ;
toReturn = true ;
}
return toReturn ;
}
public boolean setExternalId(final String externalId) {
boolean toReturn = false ;
if(!this.externalId.equals(externalId)) {
this.externalId = externalId ;
toReturn = true ;
}
return toReturn ;
}
public boolean setSuspenseAccount(GLAccount suspenseAccount) {
boolean returnValue = false;
if (!this.suspenseAccount.getId().equals(suspenseAccount.getId())) {
this.suspenseAccount = suspenseAccount;
returnValue = true;
}
return returnValue;
}
public boolean setEquityAccount(GLAccount equityAccount) {
boolean returnValue = false;
if (!this.equityAccount.getId().equals(equityAccount.getId())) {
this.equityAccount = equityAccount;
returnValue = true;
}
return returnValue;
}
public boolean setSavingsAccount(final SavingsAccount savingsAccount) {
boolean returnValue = false;
if (!this.savingsAccount.getId().equals(savingsAccount.getId())) {
this.savingsAccount = savingsAccount;
returnValue = true;
}
return returnValue;
}
public boolean setPurchasedShares(Set<PurchasedShares> purchasedShares) {
this.purchasedShares = purchasedShares ;
return true ;
}
public boolean setAllowDividendCalculationForInactiveClients(Boolean allowDividendCalculationForInactiveClients) {
boolean returnValue = false;
if (!this.allowDividendCalculationForInactiveClients.equals(allowDividendCalculationForInactiveClients)) {
this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients;
returnValue = true;
}
return returnValue;
}
public boolean setLockPeriod(final PeriodFrequencyType lockPeriod) {
boolean returnValue = false;
if (!this.lockPeriod.equals(lockPeriod)) {
this.lockPeriod = lockPeriod;
returnValue = true;
}
return returnValue;
}
public boolean setminimumActivePeriodForDividends(final PeriodFrequencyType minimumActivePeriodForDividends) {
boolean returnValue = false;
if (!this.minimumActivePeriodForDividends.equals(minimumActivePeriodForDividends)) {
this.minimumActivePeriodForDividends = minimumActivePeriodForDividends;
returnValue = true;
}
return returnValue;
}
public boolean setCharges(final Set<ShareAccountCharge> charges) {
this.charges = charges ;
return true ;
}
//FIXME: Remove this
public void setTempId(Long id) {
setId(id) ;
}
public ShareAccountData toData() {
String accountNo = "000025900"+getId() ;
Collection<PurchasedSharesData> purchasedSharesdata = new ArrayList<>();
for(PurchasedShares val: purchasedShares) {
purchasedSharesdata.add(new PurchasedSharesData(val.getPurchasedDate(), val.getTotalShares(), val.getPurchasePrice(), val.getStatus())) ;
}
Collection<ShareChargeData> chargesdata = new ArrayList<>();
for(ShareAccountCharge charge: charges) {
chargesdata.add(new ShareChargeData(charge.getAccountId(), charge.getChargeId())) ;
}
GLAccountData suspenseAccount1 = new GLAccountData(suspenseAccount.getId(), suspenseAccount.getName(), suspenseAccount.getGlCode());
GLAccountData equityAccount1 = new GLAccountData(equityAccount.getId(), equityAccount.getName(), equityAccount.getGlCode());
EnumOptionData lock = new EnumOptionData(this.lockPeriod.getValue().longValue(), this.lockPeriod.getCode(),
this.lockPeriod.toString());
EnumOptionData mini = new EnumOptionData(this.minimumActivePeriodForDividends.getValue().longValue(), this.minimumActivePeriodForDividends.getCode(),
this.minimumActivePeriodForDividends.toString());;
ShareAccountData data = new ShareAccountData(getId(), accountNo, this.client.getId(), this.client.getDisplayName(), this.shareProduct.getId(),
this.shareProduct.getProductName(), fieldOfficerId, externalId,
submittedDate, purchasedSharesdata, suspenseAccount1, equityAccount1, lock, mini,
allowDividendCalculationForInactiveClients, chargesdata, status) ;
return data ;
}
public Long getClientId() {
return this.client.getId() ;
}
public String getClientName() {
return this.client.getDisplayName() ;
}
public Long getTotalShares() {
long value = 0 ;
for(PurchasedShares val: purchasedShares) {
if(val.getStatus().equals("Approved")) {
value += val.getTotalShares().longValue() ;
}
}
return new Long(value) ;
}
public String getShareAccountNo() {
return "000025900"+getId() ;
}
public String getSavingsAccountNo() {
return this.savingsAccount.getAccountNumber() ;
}
public void setStatus(String status) {
this.status = status ;
}
public String getStatus() {
return this.status ;
}
public void addAddtionalShares(Set<PurchasedShares> additionalShares) {
this.purchasedShares.addAll(additionalShares) ;
}
public Set<PurchasedShares> getPurchasedShares() {
return this.purchasedShares ;
}
}
@@ -1,65 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.domain;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_shareaccounts_charges")
public class ShareAccountCharge extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "account_id", referencedColumnName = "id", nullable = false)
private ShareAccount shareAccount;
@ManyToOne(optional = false)
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable = false)
private Charge charge;
protected ShareAccountCharge() {
}
public ShareAccountCharge(final ShareAccount shareAccount, final Charge charge) {
this.shareAccount = shareAccount;
this.charge = charge;
}
public ShareAccountCharge(final Charge charge) {
this.charge = charge;
}
public void setShareAccount(ShareAccount shareAccount) {
this.shareAccount = shareAccount;
}
public Long getAccountId() {
return this.shareAccount.getId() ;
}
public Long getChargeId() {
return this.charge.getId() ;
}
}
@@ -1,56 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.domain;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.portfolio.shares.domain.ShareProductTempRepository;
public class ShareAccountTempRepository {
private final static ShareAccountTempRepository instance = new ShareAccountTempRepository() ;
private Map<Long, ShareAccount> cache = new HashMap<>() ;
private ShareAccountTempRepository() {
}
public static ShareAccountTempRepository getInstance() {
return instance ;
}
public void save(ShareAccount account) {
Long id = new Long(cache.size()+1) ;
account.setTempId(id) ;
cache.put(id, account) ;
ShareProductTempRepository.getInstance().addAccount(account.getShareProduct().getId(), account) ;
}
public ShareAccount findOne(Long id) {
return cache.get(id) ;
}
public Collection<ShareAccount> findAll() {
return cache.values() ;
}
}
@@ -1,333 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.serialization;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
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.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants;
import org.apache.fineract.portfolio.accounts.domain.PurchasedShares;
import org.apache.fineract.portfolio.accounts.domain.ShareAccount;
import org.apache.fineract.portfolio.accounts.domain.ShareAccountCharge;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.apache.fineract.portfolio.products.exception.ProductNotFoundException;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.apache.fineract.portfolio.shares.domain.ShareProduct;
import org.apache.fineract.portfolio.shares.domain.ShareProductTempRepository;
import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
@Service
public class ShareAccountDataSerializer {
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList(""));
private final PlatformSecurityContext platformSecurityContext;
private final FromJsonHelper fromApiJsonHelper;
private final ChargeRepositoryWrapper chargeRepository;
private final GLAccountRepositoryWrapper glAccountRepository;
private final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper;
private final ClientRepositoryWrapper clientRepositoryWrapper;
@Autowired
public ShareAccountDataSerializer(final PlatformSecurityContext platformSecurityContext, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepository, GLAccountRepositoryWrapper glAccountRepository,
final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper, final ClientRepositoryWrapper clientRepositoryWrapper) {
this.platformSecurityContext = platformSecurityContext;
this.fromApiJsonHelper = fromApiJsonHelper;
this.chargeRepository = chargeRepository;
this.glAccountRepository = glAccountRepository;
this.savingsAccountRepositoryWrapper = savingsAccountRepositoryWrapper;
this.clientRepositoryWrapper = clientRepositoryWrapper;
}
public ShareAccount validateAndCreate(JsonCommand jsonCommand) {
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
//this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
final Long clientId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.clientid_paramname, element);
final Long productId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.productid_paramname, element);
final Date submittedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.submitteddate_paramname, element)
.toDate();
// On creation submitted date will not be there.
final Long fieldOfficerId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.fieldofferid_paramname, element);
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
Long suspenseAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.suspenseaccount_paramname, element);
Long equityAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.equityaccount_paramname, element);
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
Set<PurchasedShares> sharesPurchased = asemblePurchasedShares(element);
Boolean allowdividendsForInactiveClients = this.fromApiJsonHelper.extractBooleanNamed(
ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname, element);
PeriodFrequencyType lockPeriod = extractPeriodType(ShareAccountApiConstants.lockperiod_paramname, element);
PeriodFrequencyType minimumActivePeriod = extractPeriodType(ShareAccountApiConstants.minimumactiveperiodfordividends_paramname,
element);
Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
GLAccount suspenseAccount = glAccountRepository.findOneWithNotFoundDetection(suspenseAccountId);
GLAccount equityAccount = glAccountRepository.findOneWithNotFoundDetection(equityAccountId);
SavingsAccount savingsAccount = this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsAccountId);
ShareProduct shareProduct = ShareProductTempRepository.getInstance().fineOne(productId);
if(shareProduct == null) {
throw new ProductNotFoundException(productId, "Share") ;
}
final MonetaryCurrency currency = shareProduct.getCurrency() ;
Set<ShareAccountCharge> charges = assembleListOfAccountCharges(element, currency.getCode());
AppUser modifiedBy = null;
DateTime modifiedOn = null;
Date approvedDate = null;
AppUser createdBy = platformSecurityContext.authenticatedUser();
DateTime createdDate = DateUtils.getLocalDateTimeOfTenant().toDateTime();
ShareAccount account = new ShareAccount(client, shareProduct, submittedDate, approvedDate, fieldOfficerId, externalId, currency,
suspenseAccount, equityAccount, savingsAccount, sharesPurchased, allowdividendsForInactiveClients, lockPeriod,
minimumActivePeriod, charges, createdBy, createdDate, modifiedBy, modifiedOn);
for(PurchasedShares pur: sharesPurchased) {
pur.setShareAccount(account) ;
}
for(ShareAccountCharge charge: charges) {
charge.setShareAccount(account) ;
}
return account;
}
public Map<String, Object> validateAndUpdate(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
//this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.productid_paramname, element)) {
final Long productId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.productid_paramname, element);
ShareProduct shareProduct = ShareProductTempRepository.getInstance().fineOne(productId);
if(account.setShareProduct(shareProduct)) {
actualChanges.put(ShareAccountApiConstants.productid_paramname, shareProduct) ;
}
}
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.submitteddate_paramname, element)) {
final Date submittedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.submitteddate_paramname, element)
.toDate();
if(account.setSubmittedDate(submittedDate)) {
actualChanges.put(ShareAccountApiConstants.submitteddate_paramname, submittedDate) ;
}
}
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.fieldofferid_paramname, element)) {
final Long fieldOfficerId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.fieldofferid_paramname, element);
if(account.setFieldOfficer(fieldOfficerId)){
actualChanges.put(ShareAccountApiConstants.fieldofferid_paramname, fieldOfficerId) ;
}
}
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.externalid_paramname, element)) {
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
if(account.setExternalId(externalId)) {
actualChanges.put(ShareAccountApiConstants.externalid_paramname, externalId) ;
}
}
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.suspenseaccount_paramname, element)) {
Long suspenseAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.suspenseaccount_paramname, element);
GLAccount suspenseAccount = glAccountRepository.findOneWithNotFoundDetection(suspenseAccountId);
if(account.setSuspenseAccount(suspenseAccount)) {
actualChanges.put(ShareAccountApiConstants.suspenseaccount_paramname, suspenseAccount) ;
}
}
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.equityaccount_paramname, element)) {
Long equityAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.equityaccount_paramname, element);
GLAccount equityAccount = glAccountRepository.findOneWithNotFoundDetection(equityAccountId);
if(account.setEquityAccount(equityAccount)) {
actualChanges.put(ShareAccountApiConstants.equityaccount_paramname, equityAccount) ;
}
}
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.savingsaccountid_paramname, element)) {
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
SavingsAccount savingsAccount = this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsAccountId);
if(account.setSavingsAccount(savingsAccount)) {
actualChanges.put(ShareAccountApiConstants.savingsaccountid_paramname, savingsAccount) ;
}
}
if(this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.purchasedshares_paramname, element)) {
Set<PurchasedShares> sharesPurchased = asemblePurchasedShares(element);
account.setPurchasedShares(sharesPurchased) ;
actualChanges.put(ShareAccountApiConstants.purchasedshares_paramname, sharesPurchased) ;
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname, element)) {
Boolean allowdividendsForInactiveClients = this.fromApiJsonHelper.extractBooleanNamed(
ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname, element);
if (account.setAllowDividendCalculationForInactiveClients(allowdividendsForInactiveClients)) {
actualChanges.put(ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname,
allowdividendsForInactiveClients);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.lockperiod_paramname, element)) {
PeriodFrequencyType lockPeriod = extractPeriodType(ShareAccountApiConstants.lockperiod_paramname, element);
if (account.setLockPeriod(lockPeriod)) {
actualChanges.put(ShareAccountApiConstants.lockperiod_paramname, lockPeriod);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.minimumactiveperiodfordividends_paramname, element)) {
PeriodFrequencyType minimumActivePeriod = extractPeriodType(ShareAccountApiConstants.minimumactiveperiodfordividends_paramname,
element);
if (account.setminimumActivePeriodForDividends(minimumActivePeriod)) {
actualChanges.put(ShareAccountApiConstants.minimumactiveperiodfordividends_paramname, minimumActivePeriod);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
ShareProduct shareProduct = account.getShareProduct() ;
Set<ShareAccountCharge> charges = assembleListOfAccountCharges(element, shareProduct.getCurrency().getCode());
if(account.setCharges(charges)) {
actualChanges.put(ShareAccountApiConstants.charges_paramname, charges);
}
}
AppUser modifiedBy = platformSecurityContext.authenticatedUser();
DateTime modifiedOn = DateUtils.getLocalDateTimeOfTenant().toDateTime();
return actualChanges;
}
private Set<ShareAccountCharge> assembleListOfAccountCharges(final JsonElement element, final String currencyCode) {
final Set<ShareAccountCharge> charges = new HashSet<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
JsonArray chargesArray = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.charges_paramname, element);
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has("id")) {
final Long id = jsonObject.get("id").getAsLong();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
if (!currencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Share Account must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.share.account", errorMessage);
}
ShareAccountCharge accountCharge = new ShareAccountCharge(charge);
charges.add(accountCharge);
}
}
}
}
return charges;
}
private PeriodFrequencyType extractPeriodType(String paramName, final JsonElement element) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.INVALID;
frequencyType = PeriodFrequencyType.fromInt(this.fromApiJsonHelper.extractIntegerWithLocaleNamed(paramName, element));
return frequencyType;
}
private Set<PurchasedShares> asemblePurchasedShares(final JsonElement element) {
Set<PurchasedShares> set = null;
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.purchasedshares_paramname, element)) {
set = new HashSet<>();
JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.purchasedshares_paramname, element);
for (JsonElement arrayElement : array) {
LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.purchaseddate_paramname,
arrayElement);
final Long shares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.numberofshares_paramname, arrayElement);
final BigDecimal shareValue = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
ShareAccountApiConstants.purchasedprice_paramname, arrayElement);
PurchasedShares obj = new PurchasedShares(localDate.toDate(), shares, shareValue);
set.add(obj);
}
}
return set;
}
public Set<PurchasedShares> asembleAdditionalShares(final JsonElement element) {
Set<PurchasedShares> set = null;
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.additionalshares_paramname, element)) {
set = new HashSet<>();
JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.additionalshares_paramname, element);
for (JsonElement arrayElement : array) {
LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.purchaseddate_paramname,
arrayElement);
final Long shares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.numberofshares_paramname, arrayElement);
final BigDecimal shareValue = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
ShareAccountApiConstants.purchasedprice_paramname, arrayElement);
PurchasedShares obj = new PurchasedShares(localDate.toDate(), shares, shareValue);
set.add(obj);
}
}
return set;
}
}
@@ -23,12 +23,14 @@ import java.util.Set;
import org.apache.fineract.portfolio.accounts.data.AccountData;
public interface AccountReadPlatformService {
public AccountData retrieveOne(Long id) ;
public Collection<AccountData> retrieveAll() ;
public AccountData retrieveTemplate(final Long clientId, final Long productId);
public AccountData retrieveOne(Long id, boolean includeTemplate);
public Collection<AccountData> retrieveAll();
public Set<String> getResponseDataParams();
}
@@ -1,53 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.service;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants;
import org.apache.fineract.portfolio.accounts.data.AccountData;
import org.apache.fineract.portfolio.accounts.domain.ShareAccount;
import org.apache.fineract.portfolio.accounts.domain.ShareAccountTempRepository;
import org.springframework.stereotype.Service;
@Service(value = "share"+AccountsApiConstants.READPLATFORM_NAME)
public class ShareAccountReadPlatformServiceImpl implements AccountReadPlatformService{
@Override
public AccountData retrieveOne(Long id) {
return ShareAccountTempRepository.getInstance().findOne(id).toData();
}
@Override
public Collection<AccountData> retrieveAll() {
Collection<ShareAccount> collection = ShareAccountTempRepository.getInstance().findAll() ;
Set<AccountData> set = new HashSet<>() ;
for(ShareAccount data: collection) {
set.add(data.toData()) ;
}
return set;
}
@Override
public Set<String> getResponseDataParams() {
return null;
}
}
@@ -1,85 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.service;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.portfolio.accounts.domain.ShareAccount;
import org.apache.fineract.portfolio.accounts.domain.ShareAccountTempRepository;
import org.apache.fineract.portfolio.accounts.exceptions.ShareAccountNotFoundException;
import org.apache.fineract.portfolio.accounts.serialization.ShareAccountDataSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
@Service
public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareAccountWritePlatformService {
private final ShareAccountDataSerializer accountDataSerializer;
@Autowired
public ShareAccountWritePlatformServiceJpaRepositoryImpl(final ShareAccountDataSerializer accountDataSerializer) {
this.accountDataSerializer = accountDataSerializer;
}
@Override
public CommandProcessingResult createShareAccount(JsonCommand jsonCommand) {
try {
ShareAccount account = this.accountDataSerializer.validateAndCreate(jsonCommand);
ShareAccountTempRepository.getInstance().save(account);
return new CommandProcessingResultBuilder() //
.withCommandId(jsonCommand.commandId()) //
.withEntityId(account.getId()) //
.build();
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(jsonCommand, dve);
return CommandProcessingResult.empty();
}
}
@Override
public CommandProcessingResult updateShareAccount(Long accountId, JsonCommand jsonCommand) {
try {
ShareAccount account = ShareAccountTempRepository.getInstance().findOne(accountId);
if (account == null) {
throw new ShareAccountNotFoundException(accountId) ;
}
Map<String, Object> changes = this.accountDataSerializer.validateAndUpdate(jsonCommand, account);
if (!changes.isEmpty()) {
// Save the data here
}
return new CommandProcessingResultBuilder() //
.withCommandId(jsonCommand.commandId()) //
.withEntityId(accountId) //
.with(changes) //
.build();
} catch (DataIntegrityViolationException dve) {
handleDataIntegrityIssues(jsonCommand, dve);
return CommandProcessingResult.empty();
}
}
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
}
}
@@ -31,6 +31,7 @@ import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountChargeData;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
@@ -69,6 +70,9 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
private final List<EnumOptionData> savingsChargeTimeTypeOptions;
private final List<EnumOptionData> clientChargeCalculationTypeOptions;
private final List<EnumOptionData> clientChargeTimeTypeOptions;
private final List<EnumOptionData> shareChargeCalculationTypeOptions;
private final List<EnumOptionData> shareChargeTimeTypeOptions;
private final List<EnumOptionData> feeFrequencyOptions;
private final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions;
@@ -81,14 +85,17 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions,
final List<EnumOptionData> clientChargeCalculationTypeOptions, final List<EnumOptionData> clientChargeTimeTypeOptions,
final List<EnumOptionData> feeFrequencyOptions, final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions,
final Collection<TaxGroupData> taxGroupOptions) {
final Collection<TaxGroupData> taxGroupOptions, final List<EnumOptionData> shareChargeCalculationTypeOptions,
final List<EnumOptionData> shareChargeTimeTypeOptions) {
final GLAccountData account = null;
final TaxGroupData taxGroupData = null;
return new ChargeData(null, null, null, null, null, null, null, null, false, false, taxGroupData, currencyOptions,
chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, null, null, null, null,
null, feeFrequencyOptions, account, incomeOrLiabilityAccountOptions, taxGroupOptions);
null, feeFrequencyOptions, account, incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions,
shareChargeTimeTypeOptions);
}
public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) {
@@ -99,7 +106,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
template.loanChargeTimeTypeOptions, template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions,
template.clientChargeCalculationTypeOptions, template.clientChargeTimeTypeOptions, charge.feeOnMonthDay,
charge.feeInterval, charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions,
charge.incomeOrLiabilityAccount, template.incomeOrLiabilityAccountOptions, template.taxGroupOptions);
charge.incomeOrLiabilityAccount, template.incomeOrLiabilityAccountOptions, template.taxGroupOptions,
template.shareChargeCalculationTypeOptions, template.shareChargeTimeTypeOptions);
}
public static ChargeData instance(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
@@ -121,14 +129,15 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> clientChargeCalculationTypeOptions = null;
final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
final List<EnumOptionData> shareChargeCalculationTypeOptions = null;
final List<EnumOptionData> shareChargeTimeTypeOptions = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
penalty, active, taxGroupData, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions,
chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, accountData,
incomeOrLiabilityAccountOptions, taxGroupOptions);
incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions);
}
public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) {
@@ -159,6 +168,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> feeFrequencyOptions = null;
final GLAccountData account = null;
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
final List<EnumOptionData> shareChargeCalculationTypeOptions = null;
final List<EnumOptionData> shareChargeTimeTypeOptions = null;
final TaxGroupData taxGroupData = null;
final Collection<TaxGroupData> taxGroupOptions = null;
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
@@ -166,7 +177,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, account,
incomeOrLiabilityAccountOptions, taxGroupOptions);
incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions);
}
private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
@@ -180,7 +191,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> clientChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval,
final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency,
final List<EnumOptionData> feeFrequencyOptions, final GLAccountData account,
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions, final Collection<TaxGroupData> taxGroupOptions) {
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions, final Collection<TaxGroupData> taxGroupOptions,
final List<EnumOptionData> shareChargeCalculationTypeOptions, final List<EnumOptionData> shareChargeTimeTypeOptions) {
this.id = id;
this.name = name;
this.amount = amount;
@@ -212,6 +224,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
this.incomeOrLiabilityAccountOptions = incomeOrLiabilityAccountOptions;
this.taxGroup = taxGroupData;
this.taxGroupOptions = taxGroupOptions;
this.shareChargeCalculationTypeOptions = shareChargeCalculationTypeOptions;
this.shareChargeTimeTypeOptions = shareChargeTimeTypeOptions;
}
@Override
@@ -264,6 +278,23 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
this.feeInterval, isActive, inactivationDate);
}
public ShareAccountChargeData toShareAccountChargeData() {
final Long shareChargeId = null;
final Long shareAccountId = null;
final BigDecimal amountPaid = BigDecimal.ZERO;
final BigDecimal amountWaived = BigDecimal.ZERO;
final BigDecimal amountWrittenOff = BigDecimal.ZERO;
final BigDecimal amountOutstanding = BigDecimal.ZERO;
final BigDecimal percentage = BigDecimal.ZERO;
final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
final Collection<ChargeData> chargeOptions = null;
final Boolean isActive = null;
return new ShareAccountChargeData(shareChargeId, this.id, shareAccountId, this.name, this.currency, this.amount, amountPaid,
amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, this.chargeCalculationType, percentage,
amountPercentageAppliedTo, chargeOptions, isActive);
}
public boolean isPenalty() {
return this.penalty;
}
@@ -23,8 +23,9 @@ public enum ChargeAppliesTo {
INVALID(0, "chargeAppliesTo.invalid"), //
LOAN(1, "chargeAppliesTo.loan"), //
SAVINGS(2, "chargeAppliesTo.savings"), //
CLIENT(3, "chargeAppliesTo.client");
CLIENT(3, "chargeAppliesTo.client"),
SHARES(4, "chargeAppliesTo.shares");
private final Integer value;
private final String code;
@@ -55,6 +56,9 @@ public enum ChargeAppliesTo {
case 3:
chargeAppliesToType = CLIENT;
break;
case 4:
chargeAppliesToType = SHARES ;
break ;
default:
chargeAppliesToType = INVALID;
break;
@@ -76,7 +80,10 @@ public enum ChargeAppliesTo {
return this.value.equals(ChargeAppliesTo.CLIENT.getValue());
}
public boolean isSharesCharge() {
return this.value.equals(SHARES.getValue()) ;
}
public static Object[] validValues() {
return new Object[] { ChargeAppliesTo.LOAN.getValue(), ChargeAppliesTo.SAVINGS.getValue(), ChargeAppliesTo.CLIENT.getValue() };
return new Object[] { ChargeAppliesTo.LOAN.getValue(), ChargeAppliesTo.SAVINGS.getValue(), ChargeAppliesTo.CLIENT.getValue(), ChargeAppliesTo.SHARES.getValue() };
}
}
@@ -53,10 +53,18 @@ public enum ChargeCalculationType {
return new Integer[] { ChargeCalculationType.FLAT.getValue(), ChargeCalculationType.PERCENT_OF_AMOUNT.getValue() };
}
public static Object[] validValuesForShares() {
return new Integer[] { ChargeCalculationType.FLAT.getValue(), ChargeCalculationType.PERCENT_OF_AMOUNT.getValue() };
}
public static Object[] validValuesForClients() {
return new Integer[] { ChargeCalculationType.FLAT.getValue() };
}
public static Object[] validValuesForShareAccountActivation() {
return new Integer[] { ChargeCalculationType.FLAT.getValue() };
}
public static Object[] validValuesForTrancheDisbursement(){
return new Integer[] { ChargeCalculationType.FLAT.getValue(), ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT.getValue()};
}
@@ -19,7 +19,6 @@
package org.apache.fineract.portfolio.charge.domain;
public enum ChargeTimeType {
INVALID(0, "chargeTimeType.invalid"), //
DISBURSEMENT(1, "chargeTimeType.disbursement"), // only for loan charges
SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate"), // for loan and
@@ -30,9 +29,13 @@ public enum ChargeTimeType {
MONTHLY_FEE(7, "chargeTimeType.monthlyFee"), // only for savings
INSTALMENT_FEE(8, "chargeTimeType.instalmentFee"), // only for loan charges
OVERDUE_INSTALLMENT(9, "chargeTimeType.overdueInstallment"), // only for
OVERDRAFT_FEE(10, "chargeTimeType.overdraftFee"),// only for savings
OVERDRAFT_FEE(10, "chargeTimeType.overdraftFee"), // only for savings
WEEKLY_FEE(11, "chargeTimeType.weeklyFee"), // only for savings
TRANCHE_DISBURSEMENT(12,"chargeTimeType.tranchedisbursement"); // only for loan
TRANCHE_DISBURSEMENT(12, "chargeTimeType.tranchedisbursement"), // only for
// loan
SHAREACCOUNT_ACTIVATION(13, "chargeTimeType.activation"), // only for loan
SHARE_PURCHASE(14, "chargeTimeType.sharespurchase"),
SHARE_REDEEM(15, "chargeTimeType.sharesredeem");
private final Integer value;
private final String code;
@@ -52,7 +55,8 @@ public enum ChargeTimeType {
public static Object[] validLoanValues() {
return new Integer[] { ChargeTimeType.DISBURSEMENT.getValue(), ChargeTimeType.SPECIFIED_DUE_DATE.getValue(),
ChargeTimeType.INSTALMENT_FEE.getValue(), ChargeTimeType.OVERDUE_INSTALLMENT.getValue(), ChargeTimeType.TRANCHE_DISBURSEMENT.getValue() };
ChargeTimeType.INSTALMENT_FEE.getValue(), ChargeTimeType.OVERDUE_INSTALLMENT.getValue(),
ChargeTimeType.TRANCHE_DISBURSEMENT.getValue() };
}
public static Object[] validLoanChargeValues() {
@@ -70,6 +74,10 @@ public enum ChargeTimeType {
return new Integer[] { ChargeTimeType.SPECIFIED_DUE_DATE.getValue() };
}
public static Object[] validShareValues() {
return new Integer[] { ChargeTimeType.SHAREACCOUNT_ACTIVATION.getValue(), ChargeTimeType.SHARE_PURCHASE.getValue(), ChargeTimeType.SHARE_REDEEM.getValue() };
}
public static ChargeTimeType fromInt(final Integer chargeTime) {
ChargeTimeType chargeTimeType = ChargeTimeType.INVALID;
if (chargeTime != null) {
@@ -110,6 +118,15 @@ public enum ChargeTimeType {
case 12:
chargeTimeType = TRANCHE_DISBURSEMENT;
break;
case 13:
chargeTimeType = SHAREACCOUNT_ACTIVATION;
break;
case 14:
chargeTimeType = SHARE_PURCHASE;
break;
case 15:
chargeTimeType = SHARE_REDEEM;
break;
default:
chargeTimeType = INVALID;
break;
@@ -167,16 +184,27 @@ public enum ChargeTimeType {
}
public boolean isAllowedSavingsChargeTime() {
return isOnSpecifiedDueDate() || isSavingsActivation() || isSavingsClosure() || isWithdrawalFee() || isAnnualFee() || isMonthlyFee()
|| isWeeklyFee() || isOverdraftFee();
return isOnSpecifiedDueDate() || isSavingsActivation() || isSavingsClosure() || isWithdrawalFee() || isAnnualFee()
|| isMonthlyFee() || isWeeklyFee() || isOverdraftFee();
}
public boolean isOverdraftFee() {
return this.value.equals(ChargeTimeType.OVERDRAFT_FEE.getValue());
}
public boolean isTrancheDisbursement(){
return this.value.equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue());
public boolean isTrancheDisbursement() {
return this.value.equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue());
}
public boolean isShareAccountActivation() {
return this.value.equals(ChargeTimeType.SHAREACCOUNT_ACTIVATION.getValue());
}
public boolean isSharesPurchase() {
return this.value.equals(ChargeTimeType.SHARE_PURCHASE.getValue());
}
public boolean isSharesRedeem() {
return this.value.equals(ChargeTimeType.SHARE_REDEEM.getValue());
}
}
@@ -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.charge.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when currency mismatch
* occurs
*/
public class ShareAccountChargeWithoutMandatoryFieldException extends AbstractPlatformDomainRuleException {
public ShareAccountChargeWithoutMandatoryFieldException(final String entity, final String postFix, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super("error.msg." + entity + "." + postFix + ".cannot.be.blank", defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -176,6 +176,25 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
.longGreaterThanZero();
}
}else if(appliesTo.isSharesCharge()) {
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeTimeType", element);
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull();
if (chargeTimeType != null) {
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType)
.isOneOfTheseValues(ChargeTimeType.validShareValues());
}
if (chargeCalculationType != null) {
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
.isOneOfTheseValues(ChargeCalculationType.validValuesForShares());
}
if(chargeTimeType != null && chargeTimeType.equals(ChargeTimeType.SHAREACCOUNT_ACTIVATION.getValue())) {
if(chargeCalculationType != null) {
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
.isOneOfTheseValues(ChargeCalculationType.validValuesForShareAccountActivation());
}
}
}
final String name = this.fromApiJsonHelper.extractStringNamed("name", element);
@@ -263,11 +282,11 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
final Collection<Object> validLoanValues = Arrays.asList(ChargeTimeType.validLoanValues());
final Collection<Object> validSavingsValues = Arrays.asList(ChargeTimeType.validSavingsValues());
final Collection<Object> validClientValues = Arrays.asList(ChargeTimeType.validClientValues());
final Collection<Object> validShareValues = Arrays.asList(ChargeTimeType.validShareValues()) ;
final Collection<Object> allValidValues = new ArrayList<>(validLoanValues);
allValidValues.addAll(validSavingsValues);
allValidValues.addAll(validClientValues);
allValidValues.addAll(validShareValues) ;
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull()
.isOneOfTheseValues(allValidValues.toArray(new Object[allValidValues.size()]));
}
@@ -281,13 +300,13 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
final Integer feeInterval = this.fromApiJsonHelper.extractIntegerNamed("feeInterval", element, Locale.getDefault());
baseDataValidator.reset().parameter("feeInterval").value(feeInterval).integerGreaterThanZero();
}
if (this.fromApiJsonHelper.parameterExists("chargeCalculationType", element)) {
final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 5);
}
if (this.fromApiJsonHelper.parameterExists("chargePaymentMode", element)) {
final Integer chargePaymentMode = this.fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargePaymentMode").value(chargePaymentMode).notNull().inMinMaxRange(0, 1);
@@ -338,6 +357,11 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
private void performChargeTimeNCalculationTypeValidation(DataValidatorBuilder baseDataValidator, final Integer chargeTimeType,
final Integer chargeCalculationType) {
if(chargeTimeType.equals(ChargeTimeType.SHAREACCOUNT_ACTIVATION.getValue())){
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
.isOneOfTheseValues(ChargeCalculationType.validValuesForShareAccountActivation());
}
if (chargeTimeType.equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue())) {
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
.isOneOfTheseValues(ChargeCalculationType.validValuesForTrancheDisbursement());
@@ -42,4 +42,8 @@ public interface ChargeDropdownReadPlatformService {
List<EnumOptionData> retrieveClientCalculationTypes();
List<EnumOptionData> retrieveClientCollectionTimeTypes();
List<EnumOptionData> retrieveSharesCalculationTypes() ;
List<EnumOptionData> retrieveSharesCollectionTimeTypes() ;
}
@@ -114,4 +114,16 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
public List<EnumOptionData> retrieveClientCollectionTimeTypes() {
return Arrays.asList(chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE));
}
@Override
public List<EnumOptionData> retrieveSharesCalculationTypes() {
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT),
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT));
}
@Override
public List<EnumOptionData> retrieveSharesCollectionTimeTypes() {
return Arrays.asList(chargeTimeType(ChargeTimeType.SHAREACCOUNT_ACTIVATION),
chargeTimeType(ChargeTimeType.SHARE_PURCHASE), chargeTimeType(ChargeTimeType.SHARE_REDEEM));
}
}
@@ -81,6 +81,16 @@ public class ChargeEnumerations {
optionData = new EnumOptionData(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue().longValue(),
ChargeTimeType.TRANCHE_DISBURSEMENT.getCode(), "Tranche Disbursement");
break;
case SHAREACCOUNT_ACTIVATION:
optionData = new EnumOptionData(ChargeTimeType.SHAREACCOUNT_ACTIVATION.getValue().longValue(), ChargeTimeType.SHAREACCOUNT_ACTIVATION.getCode(), "Share Account Activate") ;
break ;
case SHARE_PURCHASE:
optionData = new EnumOptionData(ChargeTimeType.SHARE_PURCHASE.getValue().longValue(), ChargeTimeType.SHARE_PURCHASE.getCode(), "Share Purchase") ;
break ;
case SHARE_REDEEM:
optionData = new EnumOptionData(ChargeTimeType.SHARE_REDEEM.getValue().longValue(), ChargeTimeType.SHARE_REDEEM.getCode(), "Share Redeem") ;
break ;
default:
optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid");
break;
@@ -105,6 +115,9 @@ public class ChargeEnumerations {
case CLIENT:
optionData = new EnumOptionData(ChargeAppliesTo.CLIENT.getValue().longValue(), ChargeAppliesTo.CLIENT.getCode(), "Client");
break;
case SHARES:
optionData = new EnumOptionData(ChargeAppliesTo.SHARES.getValue().longValue(), ChargeAppliesTo.SHARES.getCode(), "Shares") ;
break ;
default:
optionData = new EnumOptionData(ChargeAppliesTo.INVALID.getValue().longValue(), ChargeAppliesTo.INVALID.getCode(),
"Invalid");
@@ -116,5 +116,11 @@ public interface ChargeReadPlatformService {
/** Retrieve savings account charges **/
Collection<ChargeData> retrieveSavingsAccountApplicableCharges(Long savingsId);
/**
* Returns charge definitions which are applicable for shares
*/
Collection<ChargeData> retrieveSharesApplicableCharges();
public Collection<ChargeData> retrieveShareProductCharges(final Long shareProductId) ;
}
@@ -147,11 +147,15 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = this.accountingDropdownReadPlatformService
.retrieveAccountMappingOptionsForCharges();
final List<EnumOptionData> shareChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSharesCalculationTypes();
final List<EnumOptionData> shareChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveSharesCollectionTimeTypes();
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions,
allowedChargeTimeOptions, chargePaymentOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
clientChargeTimeTypeOptions, feeFrequencyOptions, incomeOrLiabilityAccountOptions, taxGroupOptions);
clientChargeTimeTypeOptions, feeFrequencyOptions, incomeOrLiabilityAccountOptions, taxGroupOptions,
shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions);
}
@Override
@@ -292,6 +296,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
return chargeSchema() + " join m_savings_product_charge spc on spc.charge_id = c.id";
}
public String shareProductChargeSchema() {
return chargeSchema() + " join m_share_product_charge mspc on mspc.charge_id = c.id";
}
@Override
public ChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
@@ -396,6 +404,16 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
return this.jdbcTemplate.query(sql, rm, new Object[] { savingsProductId });
}
@Override
public Collection<ChargeData> retrieveShareProductCharges(final Long shareProductId) {
final ChargeMapper rm = new ChargeMapper();
String sql = "select " + rm.shareProductChargeSchema() + " where c.is_deleted=0 and c.is_active=1 and mspc.product_id=? ";
sql += addInClauseToSQL_toLimitChargesMappedToOffice_ifOfficeSpecificProductsEnabled();
return this.jdbcTemplate.query(sql, rm, new Object[] { shareProductId });
}
@Override
public Collection<ChargeData> retrieveSavingsAccountApplicableCharges(Long savingsAccountId) {
@@ -419,4 +437,13 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
return this.jdbcTemplate.query(sql, rm, new Object[] { ChargeAppliesTo.CLIENT.getValue() });
}
@Override
public Collection<ChargeData> retrieveSharesApplicableCharges() {
final ChargeMapper rm = new ChargeMapper();
String sql = "select " + rm.chargeSchema() + " where c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=? ";
sql += addInClauseToSQL_toLimitChargesMappedToOffice_ifOfficeSpecificProductsEnabled();
sql += " order by c.name ";
return this.jdbcTemplate.query(sql, rm, new Object[] { ChargeAppliesTo.SHARES.getValue() });
}
}
@@ -299,7 +299,7 @@ public class ClientsApiResource {
final AccountSummaryCollectionData clientAccount = this.accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
final Set<String> CLIENT_ACCOUNTS_DATA_PARAMETERS = new HashSet<>(Arrays.asList("loanAccounts", "savingsAccounts"));
final Set<String> CLIENT_ACCOUNTS_DATA_PARAMETERS = new HashSet<>(Arrays.asList("loanAccounts", "savingsAccounts", "shareAccounts"));
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount, CLIENT_ACCOUNTS_DATA_PARAMETERS);
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
import org.springframework.stereotype.Component;
/**
@@ -45,7 +46,8 @@ public class AccountNumberGenerator {
private final static String OFFICE_NAME = "officeName";
private final static String LOAN_PRODUCT_SHORT_NAME = "loanProductShortName";
private final static String SAVINGS_PRODUCT_SHORT_NAME = "savingsProductShortName";
private final static String SHARE_PRODUCT_SHORT_NAME = "sharesProductShortName" ;
public String generate(Client client, AccountNumberFormat accountNumberFormat) {
Map<String, String> propertyMap = new HashMap<>();
propertyMap.put(ID, client.getId().toString());
@@ -73,6 +75,13 @@ public class AccountNumberGenerator {
return generateAccountNumber(propertyMap, accountNumberFormat);
}
public String generate(ShareAccount shareaccount, AccountNumberFormat accountNumberFormat) {
Map<String, String> propertyMap = new HashMap<>();
propertyMap.put(ID, shareaccount.getId().toString());
propertyMap.put(SHARE_PRODUCT_SHORT_NAME, shareaccount.getShareProduct().getShortName());
return generateAccountNumber(propertyMap, accountNumberFormat) ;
}
private String generateAccountNumber(Map<String, String> propertyMap, AccountNumberFormat accountNumberFormat) {
String accountNumber = StringUtils.leftPad(propertyMap.get(ID), AccountNumberGenerator.maxLength, '0');
if (accountNumberFormat != null && accountNumberFormat.getPrefixEnum() != null) {
@@ -157,12 +157,13 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
// this field is applicable only for client charges
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
final List<EnumOptionData> shareChargeCalculationTypeOptions = null ;
final List<EnumOptionData> shareChargeTimeTypeOptions = null ;
final Collection<TaxGroupData> taxGroupOptions = null;
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
incomeOrLiabilityAccountOptions, taxGroupOptions);
incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions);
}
@Override
@@ -18,9 +18,6 @@
*/
package org.apache.fineract.portfolio.products.api;
import java.util.Collection;
import java.util.HashSet;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -41,80 +38,85 @@ import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.products.constants.ProductsApiConstants;
import org.apache.fineract.portfolio.products.data.ProductData;
import org.apache.fineract.portfolio.products.service.ProductCommandsService;
import org.apache.fineract.portfolio.products.service.ProductReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/products/{type}")
@Component
@Scope("singleton")
public class ProductsApiResource {
private final ApplicationContext applicationContext ;
private final ApplicationContext applicationContext;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final DefaultToApiJsonSerializer<ProductData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<Object> toApiObjectJsonSerializer ;
private final DefaultToApiJsonSerializer<Object> toApiObjectJsonSerializer;
private final PlatformSecurityContext platformSecurityContext;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public ProductsApiResource(final ApplicationContext applicationContext,
final ApiRequestParameterHelper apiRequestParameterHelper,
final DefaultToApiJsonSerializer<ProductData> toApiJsonSerializer,
final PlatformSecurityContext platformSecurityContext,
public ProductsApiResource(final ApplicationContext applicationContext, final ApiRequestParameterHelper apiRequestParameterHelper,
final DefaultToApiJsonSerializer<ProductData> toApiJsonSerializer, final PlatformSecurityContext platformSecurityContext,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final DefaultToApiJsonSerializer<Object> toApiDividendsJsonSerializer) {
this.applicationContext = applicationContext ;
this.apiRequestParameterHelper = apiRequestParameterHelper ;
this.toApiJsonSerializer = toApiJsonSerializer ;
this.platformSecurityContext = platformSecurityContext ;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService ;
this.toApiObjectJsonSerializer = toApiDividendsJsonSerializer ;
this.applicationContext = applicationContext;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.toApiJsonSerializer = toApiJsonSerializer;
this.platformSecurityContext = platformSecurityContext;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.toApiObjectJsonSerializer = toApiDividendsJsonSerializer;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@PathParam("type") final String productType, @Context final UriInfo uriInfo) {
String serviceName = productType+ProductsApiConstants.READPLATFORM_NAME ;
ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName) ;
ProductData data = service.retrieveTemplate() ;
String serviceName = productType + ProductsApiConstants.READPLATFORM_NAME;
ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName);
ProductData data = service.retrieveTemplate();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, data, service.getResponseDataParams());
}
@GET
@Path("{productId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveProduct(@PathParam("productId") final Long productId, @PathParam("type") final String productType,
@Context final UriInfo uriInfo) {
String serviceName = productType+ProductsApiConstants.READPLATFORM_NAME ;
ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName) ;
ProductData data = service.retrieveOne(productId) ;
String serviceName = productType + ProductsApiConstants.READPLATFORM_NAME;
ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
ProductData data = service.retrieveOne(productId, settings.isTemplate());
return this.toApiJsonSerializer.serialize(settings, data, service.getResponseDataParams());
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllProducts(@PathParam("type") final String productType, @Context final UriInfo uriInfo) {
String serviceName = productType+ProductsApiConstants.READPLATFORM_NAME ;
ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName) ;
Collection<ProductData> data = service.retrieveAllProducts() ;
public String retrieveAllProducts(/*
* @PathParam("type") final String
* productType, @Context final UriInfo
* uriInfo
*/
@PathParam("type") final String productType, @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@Context final UriInfo uriInfo) {
String serviceName = productType + ProductsApiConstants.READPLATFORM_NAME;
ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName);
Page<ProductData> data = service.retrieveAllProducts(offset, limit);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, data, service.getResponseDataParams());
return this.toApiJsonSerializer.serialize(settings, data, service.getResponseDataParams());
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@@ -125,32 +127,32 @@ public class ProductsApiResource {
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
@POST
@Path("{productId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String handleCommands(@PathParam("type") final String productType, @PathParam("productId") final Long productId, @QueryParam("command") final String commandParam,
@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
String serviceName = productType.toUpperCase()+ProductsApiConstants.PRODUCT_COMMANDSERVICE ;
ProductCommandsService service = (ProductCommandsService) this.applicationContext.getBean(serviceName) ;
final Object obj = service.handleCommand(productId, commandParam, apiRequestBodyAsJson) ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiObjectJsonSerializer.serialize(settings, obj, new HashSet<String>());
public String handleCommands(@PathParam("type") final String productType, @PathParam("productId") final Long productId,
@QueryParam("command") final String commandParam, @SuppressWarnings("unused") @Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = new CommandWrapperBuilder().createProductCommand(productType, commandParam, productId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
@PUT
@Path("{productId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateProduct(@PathParam("type") final String productType, @PathParam("productId") final Long productId, final String apiRequestBodyAsJson) {
public String updateProduct(@PathParam("type") final String productType, @PathParam("productId") final Long productId,
final String apiRequestBodyAsJson) {
this.platformSecurityContext.authenticatedUser();
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateProduct(productType, productId)
.withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
@@ -21,15 +21,18 @@ package org.apache.fineract.portfolio.products.service;
import java.util.Collection;
import java.util.Set;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.portfolio.products.data.ProductData;
public interface ProductReadPlatformService {
public Collection<ProductData> retrieveAllProducts();
public Page<ProductData> retrieveAllProducts(Integer offSet, Integer limit);
public ProductData retrieveOne(final Long productId);
public ProductData retrieveOne(final Long productId, boolean includeTemplate);
public ProductData retrieveTemplate();
public Set<String> getResponseDataParams();
public Collection<ProductData> retrieveAllForLookup() ;
}
@@ -34,6 +34,7 @@ public enum SavingsAccountTransactionType {
ANNUAL_FEE(5, "savingsAccountTransactionType.annualFee"), //
WAIVE_CHARGES(6, "savingsAccountTransactionType.waiveCharge"), //
PAY_CHARGE(7, "savingsAccountTransactionType.payCharge"), //
DIVIDEND_PAYOUT(8, "savingsAccountTransactionType.dividendPayout"), //
INITIATE_TRANSFER(12, "savingsAccountTransactionType.initiateTransfer"), //
APPROVE_TRANSFER(13, "savingsAccountTransactionType.approveTransfer"), //
WITHDRAW_TRANSFER(14, "savingsAccountTransactionType.withdrawTransfer"), //
@@ -84,6 +85,9 @@ public enum SavingsAccountTransactionType {
case 7:
savingsAccountTransactionType = SavingsAccountTransactionType.PAY_CHARGE;
break;
case 8:
savingsAccountTransactionType = SavingsAccountTransactionType.DIVIDEND_PAYOUT;
break;
case 12:
savingsAccountTransactionType = SavingsAccountTransactionType.INITIATE_TRANSFER;
break;
@@ -165,6 +169,10 @@ public enum SavingsAccountTransactionType {
return this.value.equals(SavingsAccountTransactionType.WRITTEN_OFF.getValue());
}
public boolean isDividendPayout() {
return this.value.equals(SavingsAccountTransactionType.DIVIDEND_PAYOUT.getValue());
}
public boolean isIncomeFromInterest() {
return this.value.equals(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue());
}
@@ -174,6 +182,6 @@ public enum SavingsAccountTransactionType {
}
public boolean isCredit() {
return isDeposit() || isInterestPosting();
return isDeposit() || isInterestPosting() || isDividendPayout();
}
}
@@ -31,6 +31,7 @@ public class SavingsAccountTransactionEnumData {
private final String value;
private final boolean deposit;
private final boolean dividendPayout;
private final boolean withdrawal;
private final boolean interestPosting;
private final boolean feeDeduction;
@@ -48,6 +49,7 @@ public class SavingsAccountTransactionEnumData {
this.code = code;
this.value = value;
this.deposit = Long.valueOf(SavingsAccountTransactionType.DEPOSIT.getValue()).equals(this.id);
this.dividendPayout = Long.valueOf(SavingsAccountTransactionType.DIVIDEND_PAYOUT.getValue()).equals(this.id);
this.withdrawal = Long.valueOf(SavingsAccountTransactionType.WITHDRAWAL.getValue()).equals(this.id);
this.interestPosting = Long.valueOf(SavingsAccountTransactionType.INTEREST_POSTING.getValue()).equals(this.id);
this.feeDeduction = Long.valueOf(SavingsAccountTransactionType.ANNUAL_FEE.getValue()).equals(this.id)
@@ -127,5 +129,9 @@ public class SavingsAccountTransactionEnumData {
public boolean isWithholdTax() {
return this.withholdTax;
}
public boolean isDividendPayout() {
return this.dividendPayout;
}
}
@@ -88,6 +88,7 @@ import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
@@ -844,6 +845,15 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
public SavingsAccountTransaction deposit(final SavingsAccountTransactionDTO transactionDTO) {
return deposit(transactionDTO, SavingsAccountTransactionType.DEPOSIT);
}
public SavingsAccountTransaction dividendPayout(final SavingsAccountTransactionDTO transactionDTO) {
return deposit(transactionDTO, SavingsAccountTransactionType.DIVIDEND_PAYOUT);
}
public SavingsAccountTransaction deposit(final SavingsAccountTransactionDTO transactionDTO,
final SavingsAccountTransactionType savingsAccountTransactionType) {
final String resourceTypeName = depositAccountType().resourceName();
if (isNotActive()) {
final String defaultUserMessage = "Transaction is not allowed. Account is not active.";
@@ -886,7 +896,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final Money amount = Money.of(this.currency, transactionDTO.getTransactionAmount());
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(this, office(), transactionDTO.getPaymentDetail(),
transactionDTO.getTransactionDate(), amount, transactionDTO.getCreatedDate(), transactionDTO.getAppUser());
transactionDTO.getTransactionDate(), amount, transactionDTO.getCreatedDate(), transactionDTO.getAppUser(),
savingsAccountTransactionType);
this.transactions.add(transaction);
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
@@ -35,4 +35,6 @@ public interface SavingsAccountDomainService {
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isAccountTransfer, boolean isRegularTransaction);
void postJournalEntries(SavingsAccount savingsAccount, Set<Long> existingTransactionIds, Set<Long> existingReversedTransactionIds);
SavingsAccountTransaction handleDividendPayout(SavingsAccount account, LocalDate transactionDate, BigDecimal transactionAmount);
}
@@ -26,6 +26,7 @@ import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO;
import org.apache.fineract.portfolio.savings.exception.DepositAccountTransactionNotAllowedException;
@@ -102,10 +103,12 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
}
account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck(), depositAccountOnHoldTransactions);
account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck(),
depositAccountOnHoldTransactions);
saveTransactionToGenerateTransactionId(withdrawal);
this.savingsAccountRepository.save(account);
@@ -127,7 +130,15 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final boolean isAccountTransfer, final boolean isRegularTransaction) {
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.DEPOSIT;
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
savingsAccountTransactionType);
}
private SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final boolean isAccountTransfer, final boolean isRegularTransaction,
final SavingsAccountTransactionType savingsAccountTransactionType) {
AppUser user = getAppUserIfPresent();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -142,7 +153,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, new Date(), user);
final SavingsAccountTransaction deposit = account.deposit(transactionDTO);
final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType);
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate)) {
@@ -163,6 +174,18 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
return deposit;
}
@Override
public SavingsAccountTransaction handleDividendPayout(final SavingsAccount account, final LocalDate transactionDate,
final BigDecimal transactionAmount) {
final DateTimeFormatter fmt = null;
final PaymentDetail paymentDetail = null;
final boolean isAccountTransfer = false;
final boolean isRegularTransaction = true;
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.DIVIDEND_PAYOUT;
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
savingsAccountTransactionType);
}
private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) {
this.savingsAccountTransactionRepository.save(transaction);
return transaction.getId();
@@ -135,6 +135,14 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
createdDate, amount, isReversed, appUser);
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser,
final SavingsAccountTransactionType savingsAccountTransactionType) {
final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date,
createdDate, amount, isReversed, appUser);
}
public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final boolean isReversed = false;
@@ -283,12 +291,20 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
return SavingsAccountTransactionType.fromInt(this.typeOf).isDeposit() && isNotReversed();
}
public boolean isDividendPayout() {
return SavingsAccountTransactionType.fromInt(this.typeOf).isDividendPayout();
}
public boolean isDividendPayoutAndNotReversed() {
return SavingsAccountTransactionType.fromInt(this.typeOf).isDividendPayout() && isNotReversed();
}
public boolean isWithdrawal() {
return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawal();
}
public boolean isPostInterestCalculationRequired() {
return this.isDeposit() || this.isChargeTransaction();
return this.isDeposit() || this.isChargeTransaction() || this.isDividendPayout();
}
public boolean isInterestPostingAndNotReversed() {
@@ -470,7 +486,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit()) {
if (isDeposit() || isDividendPayoutAndNotReversed()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
@@ -486,7 +502,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit()) {
if (isDeposit() || isDividendPayoutAndNotReversed()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
@@ -515,7 +531,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
} else {
if (isDeposit()) {
if (isDeposit() || isDividendPayoutAndNotReversed()) {
// endOfDayBalance = openingBalance.plus(getAmount(currency));
// if (endOfDayBalance.isLessThanZero()) {
endOfDayBalance = endOfDayBalance.plus(getAmount(currency));
@@ -558,7 +574,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
}
public boolean isCredit() {
return isDeposit() || isInterestPostingAndNotReversed();
return isDeposit() || isInterestPostingAndNotReversed() || isDividendPayoutAndNotReversed();
}
public boolean isDebit() {
@@ -35,7 +35,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalDeposits(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
if (transaction.isDeposit() && transaction.isNotReversed()) {
if (transaction.isDepositAndNotReversed() || transaction.isDividendPayoutAndNotReversed()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -131,7 +131,6 @@ public final class SavingsAccountTransactionSummaryWrapper {
}
return total.getAmountDefaultedToNullIfZero();
}
public BigDecimal calculateTotalWithholdTaxWithdrawal(MonetaryCurrency currency, List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
@@ -141,5 +140,4 @@ public final class SavingsAccountTransactionSummaryWrapper {
}
return total.getAmountDefaultedToNullIfZero();
}
}
@@ -74,12 +74,18 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
private static final class SavingsAccountChargeMapper implements RowMapper<SavingsAccountChargeData> {
public String schema() {
return "sc.id as id, c.id as chargeId, sc.savings_account_id as accountId, c.name as name, " + "sc.amount as amountDue, "
+ "sc.amount_paid_derived as amountPaid, " + "sc.amount_waived_derived as amountWaived, "
+ "sc.amount_writtenoff_derived as amountWrittenOff, " + "sc.amount_outstanding_derived as amountOutstanding, "
return "sc.id as id, c.id as chargeId, sc.savings_account_id as accountId, c.name as name, "
+ "sc.amount as amountDue, "
+ "sc.amount_paid_derived as amountPaid, "
+ "sc.amount_waived_derived as amountWaived, "
+ "sc.amount_writtenoff_derived as amountWrittenOff, "
+ "sc.amount_outstanding_derived as amountOutstanding, "
+ "sc.calculation_percentage as percentageOf, sc.calculation_on_amount as amountPercentageAppliedTo, "
+ "sc.charge_time_enum as chargeTime, " + "sc.is_penalty as penalty, " + "sc.charge_due_date as dueAsOfDate, "
+ "sc.fee_on_month as feeOnMonth, " + "sc.fee_on_day as feeOnDay, sc.fee_interval as feeInterval, "
+ "sc.charge_time_enum as chargeTime, "
+ "sc.is_penalty as penalty, "
+ "sc.charge_due_date as dueAsOfDate, "
+ "sc.fee_on_month as feeOnMonth, "
+ "sc.fee_on_day as feeOnDay, sc.fee_interval as feeInterval, "
+ "sc.charge_calculation_enum as chargeCalculation, "
+ "sc.is_active as isActive, sc.inactivated_on_date as inactivationDate, "
+ "c.currency_code as currencyCode, oc.name as currencyName, "
@@ -160,14 +166,14 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
// this field is applicable only for client charges
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
final List<EnumOptionData> shareChargeCalculationTypeOptions = null;
final List<EnumOptionData> shareChargeTimeTypeOptions = null;
final Collection<TaxGroupData> taxGroupOptions = null;
// TODO AA : revisit for merge conflict - Not sure method signature
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
incomeOrLiabilityAccountOptions, taxGroupOptions);
incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions);
}
@Override
@@ -244,7 +250,8 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
@Override
public Collection<SavingsAccountAnnualFeeData> retrieveChargesWithDue() {
final String sql = "select " + this.chargeDueMapper.schema()
final String sql = "select "
+ this.chargeDueMapper.schema()
+ " where sac.charge_due_date is not null and sac.charge_due_date <= NOW() and sac.waived = 0 and sac.is_paid_derived=0 and sac.is_active=1 and sa.status_enum = "
+ SavingsAccountStatusType.ACTIVE.getValue() + " order by sac.charge_due_date ";
@@ -183,6 +183,10 @@ public class SavingsEnumerations {
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WITHHOLD_TAX.getValue().longValue(),
SavingsAccountTransactionType.WITHHOLD_TAX.getCode(), "Withhold Tax");
break;
case DIVIDEND_PAYOUT:
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.DIVIDEND_PAYOUT.getValue().longValue(),
SavingsAccountTransactionType.DIVIDEND_PAYOUT.getCode(), "Dividend Payout");
break;
}
return optionData;
}
@@ -0,0 +1,78 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.shareaccounts.data;
import java.math.BigDecimal;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.joda.time.LocalDate;
public class PurchasedSharesData {
private final Long id;
private final Long accountId;
private final LocalDate purchasedDate;
private final Long numberOfShares;
private final BigDecimal purchasedPrice;
private final EnumOptionData status;
private final EnumOptionData type;
private final BigDecimal amount ;
private final BigDecimal chargeAmount ;
public PurchasedSharesData(final Long id, final Long accountId, final LocalDate purchasedDate, final Long numberOfShares,
final BigDecimal purchasedPrice, final EnumOptionData status, final EnumOptionData type, final BigDecimal amount, final BigDecimal chargeAmount) {
this.id = id;
this.accountId = accountId;
this.purchasedDate = purchasedDate;
this.numberOfShares = numberOfShares;
this.purchasedPrice = purchasedPrice;
this.status = status;
this.type = type;
this.amount = amount ;
this.chargeAmount = chargeAmount ;
}
public LocalDate getPurchasedDate() {
return this.purchasedDate;
}
public Long getNumberOfShares() {
return this.numberOfShares;
}
public BigDecimal getPurchasedPrice() {
return this.purchasedPrice;
}
public EnumOptionData getStatus() {
return this.status;
}
public EnumOptionData getType() {
return this.type;
}
}
@@ -0,0 +1,108 @@
/**
* 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.shareaccounts.data;
import org.joda.time.LocalDate;
@SuppressWarnings("unused")
public class ShareAccountApplicationTimelineData {
private final LocalDate submittedOnDate;
private final String submittedByUsername;
private final String submittedByFirstname;
private final String submittedByLastname;
private final LocalDate rejectedDate;
private final String rejectedByUsername;
private final String rejectedByFirstname;
private final String rejectedByLastname;
private final LocalDate approvedDate;
private final String approvedByUsername;
private final String approvedByFirstname;
private final String approvedByLastname;
private final LocalDate activatedDate;
private final String activatedByUsername;
private final String activatedByFirstname;
private final String activatedByLastname;
private final LocalDate closedDate;
private final String closedByUsername;
private final String closedByFirstname;
private final String closedByLastname;
public static ShareAccountApplicationTimelineData templateDefault() {
final LocalDate submittedOnDate = null;
final String submittedByUsername = null;
final String submittedByFirstname = null;
final String submittedByLastname = null;
final LocalDate rejectedOnDate = null;
final String rejectedByUsername = null;
final String rejectedByFirstname = null;
final String rejectedByLastname = null;
final LocalDate approvedOnDate = null;
final String approvedByUsername = null;
final String approvedByFirstname = null;
final String approvedByLastname = null;
final LocalDate activatedOnDate = null;
final String activatedByUsername = null;
final String activatedByFirstname = null;
final String activatedByLastname = null;
final LocalDate closedOnDate = null;
final String closedByUsername = null;
final String closedByFirstname = null;
final String closedByLastname = null;
return new ShareAccountApplicationTimelineData(submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname,
rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname, approvedOnDate, approvedByUsername,
approvedByFirstname, approvedByLastname, activatedOnDate, activatedByUsername, activatedByFirstname, activatedByLastname,
closedOnDate, closedByUsername, closedByFirstname, closedByLastname);
}
public ShareAccountApplicationTimelineData(final LocalDate submittedOnDate, final String submittedByUsername,
final String submittedByFirstname, final String submittedByLastname, final LocalDate rejectedOnDate,
final String rejectedByUsername, final String rejectedByFirstname, final String rejectedByLastname,
final LocalDate approvedOnDate, final String approvedByUsername,
final String approvedByFirstname, final String approvedByLastname, final LocalDate activatedOnDate,
final String activatedByUsername, final String activatedByFirstname, final String activatedByLastname,
final LocalDate closedOnDate, final String closedByUsername, final String closedByFirstname, final String closedByLastname) {
this.submittedOnDate = submittedOnDate;
this.submittedByUsername = submittedByUsername;
this.submittedByFirstname = submittedByFirstname;
this.submittedByLastname = submittedByLastname;
this.rejectedDate = rejectedOnDate;
this.rejectedByUsername = rejectedByUsername;
this.rejectedByFirstname = rejectedByFirstname;
this.rejectedByLastname = rejectedByLastname;
this.approvedDate = approvedOnDate;
this.approvedByUsername = approvedByUsername;
this.approvedByFirstname = approvedByFirstname;
this.approvedByLastname = approvedByLastname;
this.activatedDate = activatedOnDate;
this.activatedByUsername = activatedByUsername;
this.activatedByFirstname = activatedByFirstname;
this.activatedByLastname = activatedByLastname;
this.closedDate = closedOnDate;
this.closedByUsername = closedByUsername;
this.closedByFirstname = closedByFirstname;
this.closedByLastname = closedByLastname;
}
}
@@ -0,0 +1,95 @@
/**
* 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.shareaccounts.data;
import java.math.BigDecimal;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
@SuppressWarnings("unused")
public class ShareAccountChargeData {
private final Long id;
private final Long chargeId;
private final Long accountId;
private final String name;
private final EnumOptionData chargeTimeType;
private final EnumOptionData chargeCalculationType;
private final BigDecimal percentage;
private final BigDecimal amountPercentageAppliedTo;
private final CurrencyData currency;
private final BigDecimal amount;
private final BigDecimal amountPaid;
private final BigDecimal amountWaived;
private final BigDecimal amountWrittenOff;
private final BigDecimal amountOutstanding;
private final BigDecimal amountOrPercentage;
private final Boolean isActive;
private final Collection<ChargeData> chargeOptions;
public ShareAccountChargeData(final Long id, final Long chargeId, final Long accountId, final String name,
final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived,
final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo,
final Collection<ChargeData> chargeOptions, final Boolean isActive) {
this.id = id;
this.chargeId = chargeId;
this.accountId = accountId;
this.name = name;
this.chargeTimeType = chargeTimeType;
this.chargeCalculationType = chargeCalculationType;
this.percentage = percentage;
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
this.currency = currency;
this.amount = amount;
this.amountPaid = amountPaid;
this.amountWaived = amountWaived;
this.amountWrittenOff = amountWrittenOff;
this.amountOutstanding = amountOutstanding;
this.amountOrPercentage = getAmountOrPercentage();
this.chargeOptions = chargeOptions;
this.isActive = isActive;
}
private BigDecimal getAmountOrPercentage() {
return (this.chargeCalculationType != null) && (this.chargeCalculationType.getId().intValue() > 1) ? this.percentage : this.amount;
}
}
@@ -0,0 +1,247 @@
/**
* 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.shareaccounts.data;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.accountdetails.data.ShareAccountSummaryData;
import org.apache.fineract.portfolio.accounts.data.AccountData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.products.data.ProductData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.shareproducts.data.ShareProductData;
@SuppressWarnings("unused")
public class ShareAccountData implements AccountData {
private Long id;
private String accountNo;
private String externalId;
private String savingsAccountNumber;
private Long clientId;
private String clientName;
private Long defaultShares ;
private Long productId;
private String productName;
private ShareAccountStatusEnumData status;
ShareAccountApplicationTimelineData timeline;
private CurrencyData currency;
private ShareAccountSummaryData summary;
private Collection<PurchasedSharesData> purchasedShares;
private Long savingsAccountId;
private BigDecimal currentMarketPrice;
private Integer lockinPeriod;
private EnumOptionData lockPeriodTypeEnum;
private Integer minimumActivePeriod;
private EnumOptionData minimumActivePeriodTypeEnum;
private Boolean allowDividendCalculationForInactiveClients;
private Collection<ShareAccountChargeData> charges;
private Collection<ShareAccountDividendData> dividends ;
// Data for template
private Collection<ProductData> productOptions;
private Collection<ChargeData> chargeOptions;
private Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions;
private Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions;
private Collection<SavingsAccountData> clientSavingsAccounts;
public ShareAccountData(final Long id, final String accountNo, final String externalId, final Long savingsAccountId,
final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId,
final String productName, final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline,
final CurrencyData currency, final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
final Collection<PurchasedSharesData> purchasedSharesData, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum,
final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum, Boolean allowdividendsforinactiveclients) {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
this.savingsAccountId = savingsAccountId;
this.savingsAccountNumber = savingsAccountNumber;
this.clientId = clientId;
this.clientName = clientName;
this.productId = productId;
this.productName = productName;
this.status = status;
this.timeline = timeline;
this.currency = currency;
this.summary = summaryData;
this.charges = charges;
this.purchasedShares = purchasedSharesData;
this.lockinPeriod = lockinPeriod;
this.lockPeriodTypeEnum = lockPeriodTypeEnum;
this.minimumActivePeriod = minimumActivePeriod;
this.minimumActivePeriodTypeEnum = minimumActivePeriodTypeEnum;
this.allowDividendCalculationForInactiveClients = allowdividendsforinactiveclients;
}
public ShareAccountData(final Long clientId, final String clientName, final Collection<ProductData> productOptions,
final Collection<ChargeData> chargeOptions) {
this.clientId = clientId;
this.clientName = clientName;
this.productOptions = productOptions;
this.chargeOptions = chargeOptions;
}
public ShareAccountData(final Long clientId, final String clientName, final CurrencyData currency,
final Collection<ShareAccountChargeData> charges, final BigDecimal currentMarketPrice,
final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<SavingsAccountData> clientSavingsAccounts,
final Long defaultShares) {
this.clientId = clientId;
this.clientName = clientName;
this.charges = charges;
this.currency = currency;
this.currentMarketPrice = currentMarketPrice;
// template
this.minimumActivePeriodFrequencyTypeOptions = minimumActivePeriodFrequencyTypeOptions;
this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
this.clientSavingsAccounts = clientSavingsAccounts;
this.defaultShares = defaultShares ;
}
private ShareAccountData(final Long id, final String accountNo, final String externalId, final Long savingsAccountId,
final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId,
final String productName, final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline,
final CurrencyData currency, final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
final Collection<PurchasedSharesData> purchasedSharesData, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum,
final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum,
final Boolean allowDividendCalculationForInactiveClients, final Collection<ProductData> productOptions,
final Collection<ChargeData> chargeOptions, final Collection<SavingsAccountData> clientSavingsAccounts,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions,
final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOption, final BigDecimal currenMarketPrice) {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
this.savingsAccountId = savingsAccountId;
this.savingsAccountNumber = savingsAccountNumber;
this.clientId = clientId;
this.clientName = clientName;
this.productId = productId;
this.productName = productName;
this.status = status;
this.timeline = timeline;
this.currency = currency;
this.summary = summaryData;
this.charges = charges;
this.purchasedShares = purchasedSharesData;
this.lockinPeriod = lockinPeriod;
this.lockPeriodTypeEnum = lockPeriodTypeEnum;
this.minimumActivePeriod = minimumActivePeriod;
this.minimumActivePeriodTypeEnum = minimumActivePeriodTypeEnum;
this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients;
this.productOptions = productOptions;
this.chargeOptions = chargeOptions;
this.clientSavingsAccounts = clientSavingsAccounts;
this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
this.minimumActivePeriodFrequencyTypeOptions = minimumActivePeriodFrequencyTypeOption;
this.currentMarketPrice = currenMarketPrice;
}
public Long getProductId() {
return this.productId;
}
public Long getClientId() {
return this.clientId;
}
public static ShareAccountData template(ShareAccountData data, Collection<ProductData> productOptions,
Collection<ChargeData> chargeOptions, Collection<SavingsAccountData> clientSavingsAccounts,
Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions) {
return new ShareAccountData(data.id, data.accountNo, data.externalId, data.savingsAccountId, data.savingsAccountNumber,
data.clientId, data.clientName, data.productId, data.productName, data.status, data.timeline, data.currency, data.summary,
data.charges, data.purchasedShares, data.lockinPeriod, data.lockPeriodTypeEnum, data.minimumActivePeriod,
data.minimumActivePeriodTypeEnum, data.allowDividendCalculationForInactiveClients, productOptions, chargeOptions,
clientSavingsAccounts, lockinPeriodFrequencyTypeOptions, minimumActivePeriodFrequencyTypeOptions, data.currentMarketPrice);
}
public static ShareAccountData lookup(final Long id, final String accountNo, final Long clientId, final String clientName) {
String externalId = null;
ShareAccountSummaryData summaryData = null;
Collection<PurchasedSharesData> purchasedSharesData = null;
GLAccountData suspenseAccount = null;
Integer lockinPeriod = null;
EnumOptionData lockPeriodTypeEnum = null;
Integer minimumActivePeriod = null;
EnumOptionData minimumActivePeriodTypeEnum = null;
Collection<ShareAccountChargeData> charges = null;
ShareAccountStatusEnumData status = null;
ShareAccountApplicationTimelineData timeline = null;
CurrencyData currency = null;
Boolean allowDividendCalculationForInactiveClients = null;
final Long savingsAccountId = null;
final String savingsAccountNumber = null;
final String productName = null;
final Collection<ChargeData> chargeOptions = null;
final Collection<SavingsAccountData> clientSavingsAccounts = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOption = null;
final BigDecimal currenMarketPrice = null;
final Long productId = null;
final Collection<ProductData> productOptions = null;
return new ShareAccountData(id, accountNo, externalId, savingsAccountId, savingsAccountNumber, clientId, clientName, productId,
productName, status, timeline, currency, summaryData, charges, purchasedSharesData, lockinPeriod, lockPeriodTypeEnum,
minimumActivePeriod, minimumActivePeriodTypeEnum, allowDividendCalculationForInactiveClients, productOptions,
chargeOptions, clientSavingsAccounts, lockinPeriodFrequencyTypeOptions, minimumActivePeriodFrequencyTypeOption,
currenMarketPrice);
}
public Long getId() {
return this.id;
}
public Collection<PurchasedSharesData> getPurchasedShares() {
return this.purchasedShares;
}
public void setCurrentMarketPrice(final BigDecimal currentMarketPrice) {
this.currentMarketPrice = currentMarketPrice;
}
public void setDividends(Collection<ShareAccountDividendData> dividends) {
this.dividends = dividends ;
}
}
@@ -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.shareaccounts.data;
import java.math.BigDecimal;
import java.util.Date;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
@SuppressWarnings("unused")
public class ShareAccountDividendData {
private final Long id;
private final Date postedDate ;
private final ShareAccountData accountData;
private final BigDecimal amount;
private final EnumOptionData status;
private final Long savingsTransactionId;
public ShareAccountDividendData(final Long id, final ShareAccountData accountData, final BigDecimal amount) {
this.id = id;
this.accountData = accountData;
this.amount = amount;
this.status = null;
this.savingsTransactionId = null;
this.postedDate = null ;
}
public ShareAccountDividendData(final Long id, final ShareAccountData accountData, final BigDecimal amount,
final EnumOptionData status, final Long savingsTransactionId) {
this.id = id;
this.accountData = accountData;
this.amount = amount;
this.status = status;
this.savingsTransactionId = savingsTransactionId;
this.postedDate = null ;
}
public ShareAccountDividendData(final Long id, final Date postedDate, final ShareAccountData accountData, final BigDecimal amount,
final EnumOptionData status, final Long savingsTransactionId) {
this.id = id;
this.accountData = accountData;
this.amount = amount;
this.status = status;
this.savingsTransactionId = savingsTransactionId;
this.postedDate = postedDate ;
}
}
@@ -0,0 +1,52 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.shareaccounts.data;
/**
* Immutable data object represent savings account status enumerations.
*/
@SuppressWarnings("unused")
public class ShareAccountStatusEnumData {
private final Long id;
private final String code;
private final String value;
private final boolean submittedAndPendingApproval;
private final boolean approved;
private final boolean rejected;
private final boolean active;
private final boolean closed;
public ShareAccountStatusEnumData(final Long id, final String code, final String value, final boolean submittedAndPendingApproval,
final boolean approved, final boolean rejected, final boolean active, final boolean closed) {
this.id = id;
this.code = code;
this.value = value;
this.submittedAndPendingApproval = submittedAndPendingApproval;
this.approved = approved;
this.rejected = rejected;
this.active = active;
this.closed = closed;
}
public Long id() {
return this.id;
}
}
@@ -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.shareaccounts.data;
public class ShareAccountTransactionEnumData {
private final Long id;
private final String code;
private final String value;
private final boolean isApplied;
private final boolean isApproved;
private final boolean isRejected;
private final boolean isPurchased;
private final boolean isRedeemed;
private final boolean isChargePayment;
public ShareAccountTransactionEnumData(final Long id, final String code, final String value) {
this.id = id;
this.code = code;
this.value = value;
this.isApplied = Long.valueOf(100).equals(this.id);
this.isApproved = Long.valueOf(300).equals(this.id);
this.isRejected = Long.valueOf(400).equals(this.id);
this.isPurchased = Long.valueOf(500).equals(this.id);
this.isRedeemed = Long.valueOf(600).equals(this.id);
this.isChargePayment = Long.valueOf(700).equals(this.id);
}
public Long getId() {
return this.id;
}
public String getCode() {
return this.code;
}
public String getValue() {
return this.value;
}
public boolean isApplied() {
return this.isApplied;
}
public boolean isApproved() {
return this.isApproved;
}
public boolean isRejected() {
return this.isRejected;
}
public boolean isPurchased() {
return this.isPurchased;
}
public boolean isRedeemed() {
return this.isRedeemed;
}
public boolean isChargePayment() {
return this.isChargePayment;
}
}
@@ -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.shareaccounts.domain;
public enum PurchasedSharesStatusType {
INVALID(0, "purchasedSharesStatusType.invalid"), APPLIED(100, "purchasedSharesStatusType.applied"), APPROVED(300,
"purchasedSharesStatusType.approved"), REJECTED(400, "purchasedSharesStatusType.rejected"), PURCHASED(500,
"purchasedSharesType.purchased"), REDEEMED(600, "purchasedSharesType.redeemed"),
CHARGE_PAYMENT(700, "charge.payment");
private final Integer value;
private final String code;
private PurchasedSharesStatusType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public static PurchasedSharesStatusType fromInt(final Integer type) {
PurchasedSharesStatusType enumeration = PurchasedSharesStatusType.INVALID;
switch (type) {
case 100:
enumeration = PurchasedSharesStatusType.APPLIED;
break;
case 300:
enumeration = PurchasedSharesStatusType.APPROVED;
break;
case 400:
enumeration = PurchasedSharesStatusType.REJECTED;
break;
case 500:
enumeration = PurchasedSharesStatusType.PURCHASED;
break;
case 600:
enumeration = PurchasedSharesStatusType.REDEEMED;
break;
case 700:
enumeration = PurchasedSharesStatusType.CHARGE_PAYMENT;
break ;
}
return enumeration;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public boolean isApproved() {
return this.value.equals(PurchasedSharesStatusType.APPROVED.getValue());
}
public boolean isPurchased() {
return this.value.equals(PurchasedSharesStatusType.PURCHASED.getValue());
}
public boolean isChargePayment() {
return this.value.equals(PurchasedSharesStatusType.CHARGE_PAYMENT.getValue());
}
}
@@ -0,0 +1,526 @@
/**
* 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.shareaccounts.domain;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
import org.apache.fineract.useradministration.domain.AppUser;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_share_account")
public class ShareAccount extends AbstractPersistable<Long> {
@ManyToOne
@JoinColumn(name = "client_id", nullable = true)
private Client client;
@ManyToOne
@JoinColumn(name = "product_id", nullable = true)
private ShareProduct shareProduct;
@Column(name = "status_enum", nullable = false)
protected Integer status;
@Column(name = "submitted_date")
@Temporal(TemporalType.DATE)
private Date submittedDate;
@ManyToOne(optional = true)
@JoinColumn(name = "submitted_userid", nullable = true)
protected AppUser submittedBy;
@Temporal(TemporalType.DATE)
@Column(name = "approved_date")
protected Date approvedDate;
@ManyToOne(optional = true)
@JoinColumn(name = "approved_userid", nullable = true)
protected AppUser approvedBy;
@Temporal(TemporalType.DATE)
@Column(name = "rejected_date")
protected Date rejectedDate;
@ManyToOne(optional = true)
@JoinColumn(name = "rejected_userid", nullable = true)
protected AppUser rejectedBy;
@Temporal(TemporalType.DATE)
@Column(name = "activated_date", nullable = true)
protected Date activatedDate;
@ManyToOne(optional = true)
@JoinColumn(name = "activated_userid", nullable = true)
protected AppUser activatedBy;
@Temporal(TemporalType.DATE)
@Column(name = "closed_date")
protected Date closedDate;
@ManyToOne(optional = true)
@JoinColumn(name = "closed_userid", nullable = true)
protected AppUser closedBy;
@Temporal(TemporalType.DATE)
@Column(name = "lastmodified_date")
protected Date modifiedDate;
@ManyToOne(optional = true)
@JoinColumn(name = "lastmodifiedby_id", nullable = true)
protected AppUser modifiedBy;
@Column(name = "external_id")
private String externalId;
@Column(name = "account_no", length = 20, unique = true, nullable = false)
private String accountNumber;
@Column(name = "total_approved_shares")
private Long totalSharesApproved;
@Column(name = "total_pending_shares")
private Long totalSharesPending;
@Embedded
private MonetaryCurrency currency;
@Column(name = "allow_dividends_inactive_clients")
private Boolean allowDividendCalculationForInactiveClients;
@ManyToOne
@JoinColumn(name = "savings_account_id", nullable = true)
private SavingsAccount savingsAccount;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "shareAccount", orphanRemoval = true)
private Set<ShareAccountTransaction> shareAccountTransactions;
@Column(name = "lockin_period_frequency")
private Integer lockinPeriodFrequency;
@Enumerated(EnumType.ORDINAL)
@Column(name = "lockin_period_frequency_enum", nullable = true)
private PeriodFrequencyType lockinPeriodFrequencyType;
@Column(name = "minimum_active_period_frequency")
private Integer minimumActivePeriodFrequency;
@Enumerated(EnumType.ORDINAL)
@Column(name = "minimum_active_period_frequency_enum", nullable = true)
private PeriodFrequencyType minimumActivePeriodFrequencyType;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "shareAccount", orphanRemoval = true)
private Set<ShareAccountCharge> charges;
@Transient
protected boolean accountNumberRequiresAutoGeneration = false;
protected ShareAccount() {
}
public ShareAccount(final Client client, final ShareProduct shareProduct, final String externalId, final MonetaryCurrency currency,
final SavingsAccount savingsAccount, final String accountNo, final Long totalSharesApproved, final Long totalSharesPending,
final Set<ShareAccountTransaction> purchasedShares, final Boolean allowDividendCalculationForInactiveClients,
final Integer lockinPeriodFrequency, final PeriodFrequencyType lockPeriodType, final Integer minimumActivePeriodFrequency,
final PeriodFrequencyType minimumActivePeriodType, Set<ShareAccountCharge> charges, AppUser submittedBy,
final Date submittedDate, AppUser approvedBy, Date approvedDate, AppUser rejectedBy, Date rejectedDate, AppUser activatedBy,
Date activatedDate, AppUser closedBy, Date closedDate, AppUser modifiedBy, Date modifiedDate) {
this.client = client;
this.shareProduct = shareProduct;
this.externalId = externalId;
this.currency = currency;
this.savingsAccount = savingsAccount;
if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true;
} else {
this.accountNumber = accountNo;
}
this.totalSharesApproved = totalSharesApproved;
this.totalSharesPending = totalSharesPending;
this.shareAccountTransactions = purchasedShares;
this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients;
this.lockinPeriodFrequency = lockinPeriodFrequency;
this.lockinPeriodFrequencyType = lockPeriodType;
this.minimumActivePeriodFrequency = minimumActivePeriodFrequency;
this.minimumActivePeriodFrequencyType = minimumActivePeriodType;
this.charges = charges;
this.submittedDate = submittedDate;
this.submittedBy = submittedBy;
this.approvedDate = approvedDate;
this.approvedBy = approvedBy;
this.rejectedDate = rejectedDate;
this.rejectedBy = rejectedBy;
this.activatedDate = activatedDate;
this.activatedBy = activatedBy;
this.closedDate = closedDate;
this.closedBy = closedBy;
this.modifiedBy = modifiedBy;
this.modifiedDate = modifiedDate;
this.status = ShareAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue();
}
public boolean setShareProduct(final ShareProduct shareProduct) {
boolean toReturn = false;
if (!this.shareProduct.getId().equals(shareProduct.getId())) {
this.shareProduct = shareProduct;
toReturn = true;
}
return toReturn;
}
public ShareProduct getShareProduct() {
return this.shareProduct;
}
public boolean setSubmittedDate(final Date submittedDate) {
boolean toReturn = false;
if (!this.submittedDate.equals(submittedDate)) {
this.submittedDate = submittedDate;
toReturn = true;
}
return toReturn;
}
public boolean setApprovedDate(final Date approvedDate) {
boolean toReturn = false;
if (!this.approvedDate.equals(approvedDate)) {
this.approvedDate = approvedDate;
toReturn = true;
}
return toReturn;
}
public boolean setExternalId(final String externalId) {
boolean toReturn = false;
if (!this.externalId.equals(externalId)) {
this.externalId = externalId;
toReturn = true;
}
return toReturn;
}
public boolean setSavingsAccount(final SavingsAccount savingsAccount) {
boolean returnValue = false;
if (!this.savingsAccount.getId().equals(savingsAccount.getId())) {
this.savingsAccount = savingsAccount;
returnValue = true;
}
return returnValue;
}
public boolean setPurchasedShares(Set<ShareAccountTransaction> purchasedShares) {
this.shareAccountTransactions = purchasedShares;
return true;
}
public boolean setAllowDividendCalculationForInactiveClients(Boolean allowDividendCalculationForInactiveClients) {
boolean returnValue = false;
if (this.allowDividendCalculationForInactiveClients == null) {
this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients;
returnValue = true;
} else if (!this.allowDividendCalculationForInactiveClients.equals(allowDividendCalculationForInactiveClients)) {
this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients;
returnValue = true;
}
return returnValue;
}
public boolean setLockPeriod(final Integer lockinPeriod) {
boolean returnValue = false;
if (this.lockinPeriodFrequency == null || !this.lockinPeriodFrequency.equals(lockinPeriod)) {
this.lockinPeriodFrequency = lockinPeriod;
returnValue = true;
}
return returnValue;
}
public boolean setLockPeriodFrequencyEnum(final PeriodFrequencyType lockinPeriodFrequencyType) {
boolean returnValue = false;
if (!this.lockinPeriodFrequencyType.getValue().equals(lockinPeriodFrequencyType.getValue())) {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
returnValue = true;
}
return returnValue;
}
public boolean setminimumActivePeriod(final Integer minimumActivePeriodFrequency) {
boolean returnValue = false;
if (this.minimumActivePeriodFrequency == null || !this.minimumActivePeriodFrequency.equals(minimumActivePeriodFrequency)) {
this.minimumActivePeriodFrequency = minimumActivePeriodFrequency;
returnValue = true;
}
return returnValue;
}
public boolean setminimumActivePeriodTypeEnum(final PeriodFrequencyType minimumActivePeriodForDividends) {
boolean returnValue = false;
if (!this.minimumActivePeriodFrequencyType.getValue().equals(minimumActivePeriodForDividends.getValue())) {
this.minimumActivePeriodFrequencyType = minimumActivePeriodForDividends;
returnValue = true;
}
return returnValue;
}
public boolean setCharges(final Set<ShareAccountCharge> charges) {
this.charges = charges;
return true;
}
public void updateAccountNumber(String accountNo) {
this.accountNumber = accountNo;
this.accountNumberRequiresAutoGeneration = false;
}
public boolean isAccountNumberRequiresAutoGeneration() {
return this.accountNumberRequiresAutoGeneration;
}
public Long getClientId() {
return this.client.getId();
}
public String getClientName() {
return this.client.getDisplayName();
}
public String getSavingsAccountNo() {
return this.savingsAccount.getAccountNumber();
}
public void addAddtionalShares(Set<ShareAccountTransaction> additionalShares) {
this.shareAccountTransactions.addAll(additionalShares);
}
public Set<ShareAccountTransaction> getShareAccountTransactions() {
return this.shareAccountTransactions;
}
public Set<ShareAccountTransaction> getChargeTransactions() {
Set<ShareAccountTransaction> chargeTransactions = new HashSet<>();
for (ShareAccountTransaction transaction : this.shareAccountTransactions) {
if (transaction.isChargeTransaction()) {
chargeTransactions.add(transaction);
}
}
return chargeTransactions;
}
public void updateRequestedShares(ShareAccountTransaction purchased) {
if (!this.shareAccountTransactions.isEmpty()) {
Iterator<ShareAccountTransaction> iter = this.shareAccountTransactions.iterator();
ShareAccountTransaction existing = iter.next();
existing.update(purchased.getPurchasedDate(), purchased.getTotalShares(), purchased.getPurchasePrice());
}
}
public void addAdditionalPurchasedShares(ShareAccountTransaction purchased) {
purchased.setShareAccount(this);
if (purchased.isRedeemTransaction()) {
this.totalSharesApproved -= purchased.getTotalShares();
} else {
if (this.totalSharesPending == null) {
this.totalSharesPending = purchased.getTotalShares();
} else {
this.totalSharesPending += purchased.getTotalShares();
}
}
this.shareAccountTransactions.add(purchased);
}
public void addShareAccountCharge(ShareAccountCharge charge) {
charge.update(this);
this.charges.add(charge);
}
public ShareAccountCharge updateShareCharge(final Long id, final Long chargeId, final BigDecimal amount) {
ShareAccountCharge updatedCharge = null;
for (ShareAccountCharge charge : this.charges) {
if (charge.getId() == id && charge.getChargeId() == chargeId) {
updatedCharge = charge;
charge.update(amount);
break;
}
}
return updatedCharge;
}
public void approve(final Date approvedDate, final AppUser approvedUser) {
this.approvedDate = approvedDate;
this.approvedBy = approvedUser;
for (ShareAccountTransaction transaction : this.shareAccountTransactions) {
transaction.approve();
}
this.status = ShareAccountStatusType.APPROVED.getValue();
this.totalSharesApproved = this.totalSharesPending;
this.totalSharesPending = null;
}
public void activate(final Date approvedDate, final AppUser approvedUser) {
this.activatedDate = approvedDate;
this.activatedBy = approvedUser;
this.status = ShareAccountStatusType.ACTIVE.getValue();
}
public void undoApprove() {
this.status = ShareAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue();
this.approvedDate = null;
this.approvedBy = null;
this.rejectedDate = null;
this.rejectedBy = null;
this.closedDate = null;
this.closedBy = null;
this.totalSharesApproved = null;
Iterator<ShareAccountTransaction> iter = this.shareAccountTransactions.iterator();
ShareAccountTransaction share = iter.next();
share.undoApprove();
this.totalSharesPending = share.getTotalShares();
}
public void reject(final Date rejectedDate, final AppUser rejectedUser) {
this.rejectedDate = rejectedDate;
this.rejectedBy = rejectedUser;
this.status = ShareAccountStatusType.REJECTED.getValue();
this.totalSharesPending = null;
this.totalSharesApproved = null;
Iterator<ShareAccountTransaction> iter = this.shareAccountTransactions.iterator();
ShareAccountTransaction share = iter.next();
share.reject();
}
public void close(final Date closedDate, final AppUser closedBy) {
this.closedDate = closedDate ;
this.closedBy = closedBy ;
this.status = ShareAccountStatusType.CLOSED.getValue() ;
this.totalSharesPending = null;
this.totalSharesApproved = null;
}
public String getAccountNumber() {
return this.accountNumber;
}
public ShareAccountTransaction retrievePurchasedShares(final Long id) {
ShareAccountTransaction toReturn = null;
for (ShareAccountTransaction share : this.shareAccountTransactions) {
if (share.getId().equals(id)) {
toReturn = share;
break;
}
}
return toReturn;
}
public Set<ShareAccountCharge> getCharges() {
return this.charges;
}
public void addChargeTransaction(ShareAccountTransaction chargeTransaction) {
chargeTransaction.setShareAccount(this);
this.shareAccountTransactions.add(chargeTransaction);
}
public Set<ShareAccountTransaction> getPendingForApprovalSharePurchaseTransactions() {
Set<ShareAccountTransaction> purchaseTransactions = new HashSet<>();
for (ShareAccountTransaction transaction : this.shareAccountTransactions) {
if (transaction.isPendingForApprovalTransaction()) {
purchaseTransactions.add(transaction);
}
}
return purchaseTransactions;
}
public MonetaryCurrency getCurrency() {
return this.currency;
}
public void updateApprovedShares(Long shares) {
if (this.totalSharesApproved == null) {
this.totalSharesApproved = shares;
} else {
this.totalSharesApproved += shares;
this.totalSharesPending -= shares;
}
}
public Long getTotalApprovedShares() {
return this.totalSharesApproved ;
}
public void removePendingShares(Long totalShares) {
this.totalSharesPending -= totalShares;
}
public Long getOfficeId() {
return this.client.getOffice().getId();
}
public ShareAccountTransaction getShareAccountTransaction(final ShareAccountTransaction transaction) {
ShareAccountTransaction returnTrans = null;
for (ShareAccountTransaction tran : this.shareAccountTransactions) {
if (tran.getPurchasedDate().equals(transaction.getPurchasedDate())
&& tran.getTotalShares().equals(transaction.getTotalShares())
&& tran.getPurchasePrice().equals(transaction.getPurchasePrice())
&& tran.getTransactionStatus().equals(transaction.getTransactionStatus())
&& tran.getTransactionType().equals(transaction.getTransactionType())) {
returnTrans = tran;
break;
}
}
return returnTrans;
}
public Date getSubmittedDate(){
return this.submittedDate ;
}
public Date getApprovedDate() {
return this.approvedDate ;
}
}
@@ -0,0 +1,444 @@
/**
* 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.shareaccounts.domain;
import java.math.BigDecimal;
import java.math.MathContext;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_share_account_charge")
public class ShareAccountCharge extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "account_id", referencedColumnName = "id", nullable = false)
private ShareAccount shareAccount;
@ManyToOne(optional = false)
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable = false)
private Charge charge;
@Column(name = "charge_time_enum", nullable = false)
private Integer chargeTime;
@Column(name = "charge_calculation_enum")
private Integer chargeCalculation;
@Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true)
private BigDecimal percentage;
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal amountPercentageAppliedTo;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountPaid;
@Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountWaived;
@Column(name = "amount_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal amountWrittenOff;
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
private BigDecimal amountOutstanding;
@Column(name = "is_paid_derived", nullable = false)
private boolean paid = false;
@Column(name = "waived", nullable = false)
private boolean waived = false;
@Column(name = "is_active", nullable = false)
private boolean status = true;
@Column(name = "charge_amount_or_percentage")
private BigDecimal amountOrPercentage;
public static ShareAccountCharge createNewWithoutShareAccount(final Charge chargeDefinition, final BigDecimal amountPayable,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final boolean status) {
return new ShareAccountCharge(null, chargeDefinition, amountPayable, chargeTime, chargeCalculation, status);
}
protected ShareAccountCharge() {
//
}
private ShareAccountCharge(final ShareAccount shareAccount, final Charge chargeDefinition, final BigDecimal amount,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final boolean status) {
this.shareAccount = shareAccount;
this.charge = chargeDefinition;
this.chargeTime = (chargeTime == null) ? chargeDefinition.getChargeTimeType() : chargeTime.getValue();
this.chargeCalculation = chargeDefinition.getChargeCalculation();
if (chargeCalculation != null) {
this.chargeCalculation = chargeCalculation.getValue();
}
BigDecimal chargeAmount = chargeDefinition.getAmount();
if (amount != null) {
chargeAmount = amount;
}
final BigDecimal transactionAmount = new BigDecimal(0);
populateDerivedFields(transactionAmount, chargeAmount);
this.paid = determineIfFullyPaid();
this.status = status;
}
public void populateDerivedFields(final BigDecimal transactionAmount, final BigDecimal chargeAmount) {
if (this.chargeCalculation.equals(ChargeCalculationType.FLAT.getValue())) {
this.percentage = null;
this.amountOrPercentage = chargeAmount ;
this.amount = BigDecimal.ZERO;
this.amountPercentageAppliedTo = null;
this.amountPaid = null;
this.amountOutstanding = BigDecimal.ZERO;
this.amountWaived = null;
this.amountWrittenOff = null;
} else if (this.chargeCalculation.equals(ChargeCalculationType.PERCENT_OF_AMOUNT.getValue())) {
this.percentage = chargeAmount;
this.amountPercentageAppliedTo = transactionAmount;
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
this.amountPaid = null;
this.amountOutstanding = calculateOutstanding();
this.amountWaived = null;
this.amountWrittenOff = null;
}
}
public void markAsFullyPaid() {
this.amountPaid = this.amount;
this.amountOutstanding = BigDecimal.ZERO;
this.paid = true;
}
public void resetToOriginal(final MonetaryCurrency currency) {
this.amountPaid = BigDecimal.ZERO;
this.amountWaived = BigDecimal.ZERO;
this.amountWrittenOff = BigDecimal.ZERO;
this.amountOutstanding = calculateAmountOutstanding(currency);
this.paid = false;
this.waived = false;
}
public void undoPayment(final MonetaryCurrency currency, final Money transactionAmount) {
Money amountPaid = getAmountPaid(currency);
amountPaid = amountPaid.minus(transactionAmount);
this.amountPaid = amountPaid.getAmount();
this.amountOutstanding = calculateAmountOutstanding(currency);
this.paid = false;
this.status = true;
}
public Money waive(final MonetaryCurrency currency) {
Money amountWaivedToDate = Money.of(currency, this.amountWaived);
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
this.amountWaived = amountWaivedToDate.plus(amountOutstanding).getAmount();
this.amountOutstanding = BigDecimal.ZERO;
this.waived = true;
return amountOutstanding;
}
public void undoWaiver(final MonetaryCurrency currency, final Money transactionAmount) {
Money amountWaived = getAmountWaived(currency);
amountWaived = amountWaived.minus(transactionAmount);
this.amountWaived = amountWaived.getAmount();
this.amountOutstanding = calculateAmountOutstanding(currency);
this.waived = false;
this.status = true;
}
public Money pay(final MonetaryCurrency currency, final Money amountPaid) {
Money amountPaidToDate = Money.of(currency, this.amountPaid);
Money amountOutstanding = Money.of(currency, this.amountOutstanding);
amountPaidToDate = amountPaidToDate.plus(amountPaid);
amountOutstanding = amountOutstanding.minus(amountPaid);
this.amountPaid = amountPaidToDate.getAmount();
this.amountOutstanding = amountOutstanding.getAmount();
this.paid = determineIfFullyPaid();
return Money.of(currency, this.amountOutstanding);
}
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
return getAmount(currency).minus(getAmountWaived(currency)).minus(getAmountPaid(currency)).getAmount();
}
public void update(final ShareAccount shareAccount) {
this.shareAccount = shareAccount;
}
public void update(final BigDecimal amount) {
final BigDecimal transactionAmount = BigDecimal.ZERO;
if (this.chargeCalculation.equals(ChargeCalculationType.FLAT.getValue())) {
this.amountOrPercentage = amount;
this.amountOutstanding = calculateOutstanding();
} else if (this.chargeCalculation.equals(ChargeCalculationType.PERCENT_OF_AMOUNT.getValue())) {
this.percentage = amount;
this.amountPercentageAppliedTo = transactionAmount;
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
this.amountOutstanding = calculateOutstanding();
}
}
private boolean isGreaterThanZero(final BigDecimal value) {
return value.compareTo(BigDecimal.ZERO) == 1;
}
private boolean determineIfFullyPaid() {
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
}
private BigDecimal calculateOutstanding() {
BigDecimal amountPaidLocal = BigDecimal.ZERO;
if (this.amountPaid != null) {
amountPaidLocal = this.amountPaid;
}
BigDecimal amountWaivedLocal = BigDecimal.ZERO;
if (this.amountWaived != null) {
amountWaivedLocal = this.amountWaived;
}
BigDecimal amountWrittenOffLocal = BigDecimal.ZERO;
if (this.amountWrittenOff != null) {
amountWrittenOffLocal = this.amountWrittenOff;
}
final BigDecimal totalAccountedFor = amountPaidLocal.add(amountWaivedLocal).add(amountWrittenOffLocal);
return this.amount.subtract(totalAccountedFor);
}
private BigDecimal percentageOf(final BigDecimal value, final BigDecimal percentage) {
BigDecimal percentageOf = BigDecimal.ZERO;
if (isGreaterThanZero(value)) {
final MathContext mc = new MathContext(8, MoneyHelper.getRoundingMode());
final BigDecimal multiplicand = percentage.divide(BigDecimal.valueOf(100l), mc);
percentageOf = value.multiply(multiplicand, mc);
}
return percentageOf;
}
public BigDecimal percentageOrAmount() {
return this.amountOrPercentage;
}
public BigDecimal amoutOutstanding() {
return this.amountOutstanding;
}
public boolean isNotFullyPaid() {
return !isPaid();
}
public boolean isPaid() {
return this.paid;
}
public boolean isWaived() {
return this.waived;
}
public boolean isPaidOrPartiallyPaid(final MonetaryCurrency currency) {
final Money amountWaivedOrWrittenOff = getAmountWaived(currency).plus(getAmountWrittenOff(currency));
return Money.of(currency, this.amountPaid).plus(amountWaivedOrWrittenOff).isGreaterThanZero();
}
public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount);
}
private Money getAmountPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.amountPaid);
}
public Money getAmountWaived(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWaived);
}
public Money getAmountWrittenOff(final MonetaryCurrency currency) {
return Money.of(currency, this.amountWrittenOff);
}
public Money getAmountOutstanding(final MonetaryCurrency currency) {
return Money.of(currency, this.amountOutstanding);
}
/**
* @param incrementBy
* Amount used to pay off this charge
* @return Actual amount paid on this charge
*/
public Money updatePaidAmountBy(final Money incrementBy) {
Money amountPaidToDate = Money.of(incrementBy.getCurrency(), this.amountPaid);
Money amountPaidOnThisCharge = Money.zero(incrementBy.getCurrency());
amountPaidOnThisCharge = incrementBy;
amountPaidToDate = amountPaidToDate.plus(incrementBy);
this.amountPaid = amountPaidToDate.getAmount();
final Money amountExpected = Money.of(incrementBy.getCurrency(), this.amountPaid);
this.amountOutstanding = amountExpected.minus(amountPaidToDate).getAmount();
this.paid = determineIfFullyPaid();
return amountPaidOnThisCharge;
}
public String name() {
return this.charge.getName();
}
public String currencyCode() {
return this.charge.getCurrencyCode();
}
public Charge getCharge() {
return this.charge;
}
public ShareAccount shareAccount() {
return this.shareAccount;
}
public boolean isShareAccountActivation() {
return ChargeTimeType.fromInt(this.chargeTime).isShareAccountActivation();
}
public boolean isShareAccountClosure() {
return ChargeTimeType.fromInt(this.chargeTime).isSavingsClosure();
}
public boolean hasCurrencyCodeOf(final String matchingCurrencyCode) {
if (this.currencyCode() == null || matchingCurrencyCode == null) { return false; }
return this.currencyCode().equalsIgnoreCase(matchingCurrencyCode);
}
public BigDecimal updateWithdralFeeAmount(final BigDecimal transactionAmount) {
BigDecimal amountPaybale = BigDecimal.ZERO;
if (ChargeCalculationType.fromInt(this.chargeCalculation).isFlat()) {
amountPaybale = this.amount;
} else if (ChargeCalculationType.fromInt(this.chargeCalculation).isPercentageOfAmount()) {
amountPaybale = transactionAmount.multiply(this.percentage).divide(BigDecimal.valueOf(100l));
}
this.amountOutstanding = amountPaybale;
return amountPaybale;
}
public boolean isActive() {
return this.status;
}
public boolean isNotActive() {
return !isActive();
}
public Long getChargeId() {
return this.charge.getId();
}
public boolean isSharesPurchaseCharge() {
return ChargeTimeType.fromInt(this.chargeTime).isSharesPurchase();
}
public boolean isSharesRedeemCharge() {
return ChargeTimeType.fromInt(this.chargeTime).isSharesRedeem();
}
public Integer getChargeTimeType() {
return this.chargeTime;
}
public BigDecimal deriveChargeAmount(BigDecimal transactionAmount) {
BigDecimal toReturnAmount = amountOrPercentage;
if (ChargeCalculationType.fromInt(this.chargeCalculation) == ChargeCalculationType.PERCENT_OF_AMOUNT) {
toReturnAmount = percentageOf(transactionAmount, this.percentage);
this.amountPercentageAppliedTo = transactionAmount;
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
this.amountPaid = null;
this.amountOutstanding = calculateOutstanding();
this.amountWaived = null;
this.amountWrittenOff = null;
}else {
this.amount = this.amountOrPercentage;
this.amountOutstanding = calculateOutstanding();
this.amountWaived = null;
this.amountWrittenOff = null;
}
return toReturnAmount;
}
public BigDecimal updateChargeDetailsForAdditionalSharesRequest(final BigDecimal transactionAmount) {
BigDecimal toReturnAmount = amountOrPercentage;
if (ChargeCalculationType.fromInt(this.chargeCalculation) == ChargeCalculationType.PERCENT_OF_AMOUNT) {
toReturnAmount = percentageOf(transactionAmount, this.percentage);
this.amountPercentageAppliedTo = this.amountPercentageAppliedTo.add(transactionAmount);
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
this.amountOutstanding = calculateOutstanding();
this.amountWaived = null;
this.amountWrittenOff = null;
} else {
this.amount = this.amount.add(this.amountOrPercentage);
this.amountOutstanding = calculateOutstanding();
this.amountWaived = null;
this.amountWrittenOff = null;
}
return toReturnAmount;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) { return false; }
final ShareAccountCharge rhs = (ShareAccountCharge) obj;
return new EqualsBuilder().appendSuper(super.equals(obj)) //
.append(getId(), rhs.getId()) //
.append(this.charge.getId(), rhs.charge.getId()) //
.append(this.amount, rhs.amount) //
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(3, 5) //
.append(getId()) //
.append(this.charge.getId()) //
.append(this.amount)//
.toHashCode();
}
}
@@ -0,0 +1,77 @@
/**
* 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.shareaccounts.domain;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_share_account_charge_paid_by")
public class ShareAccountChargePaidBy extends AbstractPersistable<Long>{
@ManyToOne(optional = false)
@JoinColumn(name = "share_transaction_id", referencedColumnName = "id", nullable = false)
private ShareAccountTransaction shareAccountTransaction;
@ManyToOne(optional = false)
@JoinColumn(name = "charge_transaction_id", referencedColumnName = "id", nullable = false)
private ShareAccountCharge shareAccountCharge;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount ;
protected ShareAccountChargePaidBy() {
}
public ShareAccountChargePaidBy(final ShareAccountTransaction shareAccountTransaction,
final ShareAccountCharge shareAccountCharge, final BigDecimal amount) {
this.shareAccountTransaction = shareAccountTransaction ;
this.shareAccountCharge = shareAccountCharge ;
this.amount = amount ;
}
public ShareAccountCharge getCharge() {
return this.shareAccountCharge ;
}
public BigDecimal getAmount() {
return this.amount ;
}
public ShareAccountTransaction getShareAccountTransaction() {
return this.shareAccountTransaction ;
}
public Long getChargeId() {
return this.shareAccountCharge.getChargeId() ;
}
public Long getShareChargeId() {
return this.shareAccountCharge.getId() ;
}
}
@@ -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.shareaccounts.domain;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_share_account_dividend_details")
public class ShareAccountDividendDetails extends AbstractPersistable<Long> {
@Column(name = "account_id", nullable = false)
private Long shareAccountId;
@Column(name = "amount", scale = 6, precision = 19)
private BigDecimal amount;
@Column(name = "status")
private Integer status;
@Column(name = "savings_transaction_id")
private Long savingsTransactionId;
protected ShareAccountDividendDetails() {
}
public ShareAccountDividendDetails(final Long shareAccountId, final BigDecimal amount) {
this.shareAccountId = shareAccountId;
this.amount = amount;
this.status = ShareAccountDividendStatusType.INITIATED.getValue();
}
public void update(final Integer status, final Long savingsTransactionId) {
this.status = status;
this.savingsTransactionId = savingsTransactionId;
}
public BigDecimal getAmount() {
return this.amount;
}
}
@@ -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.shareaccounts.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ShareAccountDividendRepository extends JpaRepository<ShareAccountDividendDetails, Long>,
JpaSpecificationExecutor<ShareAccountDividendDetails> {
}
@@ -0,0 +1,57 @@
/**
* 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.shareaccounts.domain;
public enum ShareAccountDividendStatusType {
INVALID(0, "shareAccountDividendStatusType.invalid"), INITIATED(100, "shareAccountDividendStatusType.initiated"), POSTED(300,
"shareAccountDividendStatusType.posted");
private final Integer value;
private final String code;
private ShareAccountDividendStatusType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public static ShareAccountDividendStatusType fromInt(final Integer type) {
ShareAccountDividendStatusType enumeration = ShareAccountDividendStatusType.INVALID;
switch (type) {
case 100:
enumeration = ShareAccountDividendStatusType.INITIATED;
break;
case 300:
enumeration = ShareAccountDividendStatusType.POSTED;
break;
}
return enumeration;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
}
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.domain;
package org.apache.fineract.portfolio.shareaccounts.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -0,0 +1,49 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.shareaccounts.domain;
import org.apache.fineract.portfolio.accounts.exceptions.ShareAccountNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ShareAccountRepositoryWrapper {
private final ShareAccountRepository shareAccountRepository ;
@Autowired
public ShareAccountRepositoryWrapper(final ShareAccountRepository shareAccountRepository) {
this.shareAccountRepository = shareAccountRepository ;
}
public ShareAccount findOneWithNotFoundDetection(final Long accountId) {
ShareAccount account = this.shareAccountRepository.findOne(accountId) ;
if(account == null) {
throw new ShareAccountNotFoundException(accountId) ;
}
return account ;
}
public void save(final ShareAccount shareAccount) {
this.shareAccountRepository.save(shareAccount) ;
}
public void saveAndFlush(final ShareAccount shareAccount) {
this.shareAccountRepository.saveAndFlush(shareAccount) ;
}
}
@@ -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.shareaccounts.domain;
/**
* Enum representation of {@link ShareAccount} status states.
*/
public enum ShareAccountStatusType {
INVALID(0, "shareAccountStatusType.invalid"), //
SUBMITTED_AND_PENDING_APPROVAL(100, "shareAccountStatusType.submitted.and.pending.approval"), //
APPROVED(200, "shareAccountStatusType.approved"), //
ACTIVE(300, "shareAccountStatusType.active"), //
REJECTED(500, "shareAccountStatusType.rejected"), //
CLOSED(600, "shareAccountStatusType.closed");
private final Integer value;
private final String code;
public static ShareAccountStatusType fromInt(final Integer type) {
ShareAccountStatusType enumeration = ShareAccountStatusType.INVALID;
switch (type) {
case 100:
enumeration = ShareAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
break;
case 200:
enumeration = ShareAccountStatusType.APPROVED;
break;
case 300:
enumeration = ShareAccountStatusType.ACTIVE;
break;
case 500:
enumeration = ShareAccountStatusType.REJECTED;
break;
case 600:
enumeration = ShareAccountStatusType.CLOSED;
break;
}
return enumeration;
}
private ShareAccountStatusType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public boolean hasStateOf(final ShareAccountStatusType state) {
return this.value.equals(state.getValue());
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public boolean isSubmittedAndPendingApproval() {
return this.value.equals(ShareAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue());
}
public boolean isApproved() {
return this.value.equals(ShareAccountStatusType.APPROVED.getValue());
}
public boolean isRejected() {
return this.value.equals(ShareAccountStatusType.REJECTED.getValue());
}
public boolean isActive() {
return this.value.equals(ShareAccountStatusType.ACTIVE.getValue());
}
public boolean isActiveOrAwaitingApprovalOrDisbursal() {
return isApproved() || isSubmittedAndPendingApproval() || isActive();
}
public boolean isClosed() {
return this.value.equals(ShareAccountStatusType.CLOSED.getValue()) || isRejected();
}
}
@@ -0,0 +1,209 @@
/**
* 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.shareaccounts.domain;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_share_account_transactions")
public class ShareAccountTransaction extends AbstractPersistable<Long> {
@ManyToOne(optional = false)
@JoinColumn(name = "account_id", referencedColumnName = "id", nullable = false)
private ShareAccount shareAccount;
@Column(name = "transaction_date")
@Temporal(TemporalType.DATE)
private Date transactionDate;
@Column(name = "total_shares")
private Long totalShares;
@Column(name = "unit_price")
private BigDecimal shareValue;
@Column(name = "amount")
private BigDecimal amount ;
@Column(name = "charge_amount")
private BigDecimal totalChargeAmount ;
@Column(name = "status_enum", nullable = true)
private Integer status;
@Column(name = "type_enum", nullable = true)
private Integer type ;
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "shareAccountTransaction", orphanRemoval = true)
private final Set<ShareAccountChargePaidBy> shareAccountChargesPaid = new HashSet<>();
protected ShareAccountTransaction() {
}
public void setShareAccount(final ShareAccount shareAccount) {
this.shareAccount = shareAccount;
}
public ShareAccountTransaction(final Date transactionDate, final Long totalShares, final BigDecimal shareValue) {
this.transactionDate = transactionDate;
this.totalShares = totalShares;
this.shareValue = shareValue;
this.status = PurchasedSharesStatusType.APPLIED.getValue();
this.type = PurchasedSharesStatusType.PURCHASED.getValue() ;
this.amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ;
}
private ShareAccountTransaction(final Date transactionDate, final Long totalShares, final BigDecimal shareValue,
final Integer status, final Integer type, final BigDecimal amount, final BigDecimal chargeAmount) {
this.transactionDate = transactionDate;
this.totalShares = totalShares;
this.shareValue = shareValue;
this.status = status ;
this.type = type ;
this.amount = amount ;
this.totalChargeAmount = chargeAmount ;
}
public static ShareAccountTransaction createRedeemTransaction(final Date transactionDate, final Long totalShares, final BigDecimal shareValue) {
final Integer status = PurchasedSharesStatusType.APPROVED.getValue() ;
final Integer type = PurchasedSharesStatusType.REDEEMED.getValue() ;
final BigDecimal amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ;
return new ShareAccountTransaction(transactionDate, totalShares, shareValue, status, type, amount, null) ;
}
public static ShareAccountTransaction createChargeTransaction(final Date transactionDate, final ShareAccountCharge charge) {
final Long totalShares = null ;
final BigDecimal unitPrice = null ;
final Integer status = PurchasedSharesStatusType.APPROVED.getValue() ;
final Integer type = PurchasedSharesStatusType.CHARGE_PAYMENT.getValue() ;
BigDecimal amount = charge.percentageOrAmount() ;
BigDecimal chargeAmount = amount ;
return new ShareAccountTransaction(transactionDate, totalShares, unitPrice, status, type, amount, chargeAmount) ;
}
public Date getPurchasedDate() {
return this.transactionDate;
}
public Long getTotalShares() {
return this.totalShares;
}
public BigDecimal getPurchasePrice() {
return this.shareValue;
}
public void update(final Date purchasedDate, final Long totalShares, final BigDecimal shareValue) {
this.transactionDate = purchasedDate;
this.totalShares = totalShares;
this.shareValue = shareValue;
this.amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ;
this.status = PurchasedSharesStatusType.APPLIED.getValue();
}
public void approve() {
this.status = PurchasedSharesStatusType.APPROVED.getValue() ;
}
public void undoApprove() {
this.status = PurchasedSharesStatusType.APPLIED.getValue() ;
}
public void reject() {
this.status = PurchasedSharesStatusType.REJECTED.getValue() ;
if(this.totalChargeAmount != null) {
this.amount = this.amount.subtract(totalChargeAmount) ;
}
}
public boolean isPendingForApprovalTransaction() {
return this.status.equals(PurchasedSharesStatusType.APPLIED.getValue()) && this.type.equals(PurchasedSharesStatusType.PURCHASED.getValue()) ;
}
public boolean isPurchasTransaction() {
return this.status.equals(PurchasedSharesStatusType.APPROVED.getValue()) && this.type.equals(PurchasedSharesStatusType.PURCHASED.getValue()) ;
}
public boolean isRedeemTransaction() {
return this.status.equals(PurchasedSharesStatusType.APPROVED.getValue()) && this.type.equals(PurchasedSharesStatusType.REDEEMED.getValue()) ;
}
public boolean isChargeTransaction() {
return this.status.equals(PurchasedSharesStatusType.APPROVED.getValue()) && this.type.equals(PurchasedSharesStatusType.CHARGE_PAYMENT.getValue()) ;
}
public void addShareAccountChargePaidBy(final ShareAccountChargePaidBy chargePaidBy) {
this.shareAccountChargesPaid.add(chargePaidBy) ;
}
public BigDecimal amount() {
return this.amount ;
}
public BigDecimal chargeAmount() {
return this.totalChargeAmount ;
}
public void updateChargeAmount(BigDecimal totalChargeAmount) {
this.amount = this.amount.add(totalChargeAmount);
this.totalChargeAmount = totalChargeAmount ;
}
public void deductChargesFromTotalAmount(BigDecimal totalChargeAmount) {
this.amount = this.amount.subtract(totalChargeAmount);
this.totalChargeAmount = totalChargeAmount ;
}
public Set<ShareAccountChargePaidBy> getChargesPaidBy() {
return this.shareAccountChargesPaid ;
}
public Integer getTransactionStatus() {
return this.status ;
}
public Integer getTransactionType() {
return this.type ;
}
public void adjustRedeemAmount() {
if(this.totalChargeAmount != null) {
this.amount = this.amount.subtract(totalChargeAmount) ;
}
}
}
@@ -0,0 +1,31 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.shareaccounts.exceptions;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException}
*/
public class IssueableSharesExceededException extends AbstractPlatformResourceNotFoundException {
public IssueableSharesExceededException() {
super("error.msg.shares.issuable.shares.exceeded", "Issueable Shares exceeded than product definition");
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "ACTIVATE")
public class ActivateShareAccountCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public ActivateShareAccountCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.activateShareAccount(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "APPLYADDITIONALSHARES")
public class ApplyAddtionalSharesCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public ApplyAddtionalSharesCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.applyAddtionalShares(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "APPROVEADDITIONALSHARES")
public class ApproveAddtionalSharesCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public ApproveAddtionalSharesCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.approveAdditionalShares(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "APPROVE")
public class ApproveShareAccountCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public ApproveShareAccountCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.approveShareAccount(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "CLOSE")
public class CloseShareAccountCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public CloseShareAccountCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.closeShareAccount(jsonCommand.entityId(), jsonCommand);
}
}
@@ -16,13 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.handler;
package org.apache.fineract.portfolio.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.accounts.service.ShareAccountWritePlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "REDEEMSHARES")
public class RedeemSharesCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public RedeemSharesCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.redeemShares(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "REJECTADDITIONALSHARES")
public class RejectAddtionalSharesCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public RejectAddtionalSharesCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.rejectAdditionalShares(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "REJECT")
public class RejectShareAccountCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public RejectShareAccountCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.rejectShareAccount(jsonCommand.entityId(), jsonCommand);
}
}
@@ -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.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "SHAREACCOUNT", action = "UNDOAPPROVAL")
public class UndoApproveShareAccountCommandHandler implements NewCommandSourceHandler{
private final ShareAccountWritePlatformService shareAccountWritePlatformService ;
@Autowired
public UndoApproveShareAccountCommandHandler(final ShareAccountWritePlatformService shareAccountWritePlatformService) {
this.shareAccountWritePlatformService = shareAccountWritePlatformService ;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
return this.shareAccountWritePlatformService.undoApproveShareAccount(jsonCommand.entityId(), jsonCommand);
}
}
@@ -16,13 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.accounts.handler;
package org.apache.fineract.portfolio.shareaccounts.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.accounts.service.ShareAccountWritePlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -0,0 +1,741 @@
/**
* 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.shareaccounts.serialization;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
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.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountCharge;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountChargePaidBy;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountTransaction;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
import org.apache.fineract.useradministration.domain.AppUser;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
@Service
public class ShareAccountDataSerializer {
private final PlatformSecurityContext platformSecurityContext;
private final FromJsonHelper fromApiJsonHelper;
private final ChargeRepositoryWrapper chargeRepository;
private final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper;
private final ClientRepositoryWrapper clientRepositoryWrapper;
private final ShareProductRepositoryWrapper shareProductRepository;
@Autowired
public ShareAccountDataSerializer(final PlatformSecurityContext platformSecurityContext, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepository,
final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper, final ClientRepositoryWrapper clientRepositoryWrapper,
final ShareProductRepositoryWrapper shareProductRepository) {
this.platformSecurityContext = platformSecurityContext;
this.fromApiJsonHelper = fromApiJsonHelper;
this.chargeRepository = chargeRepository;
this.savingsAccountRepositoryWrapper = savingsAccountRepositoryWrapper;
this.clientRepositoryWrapper = clientRepositoryWrapper;
this.shareProductRepository = shareProductRepository;
}
public ShareAccount validateAndCreate(JsonCommand jsonCommand) {
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
final Long clientId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.clientid_paramname, element);
final Long productId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.productid_paramname, element);
ShareProduct shareProduct = this.shareProductRepository.findOneWithNotFoundDetection(productId);
final LocalDate submittedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.submitteddate_paramname,
element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.submitteddate_paramname).value(submittedDate).notNull();
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull();
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
.longGreaterThanZero();
final Long requestedShares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).notNull()
.longGreaterThanZero();
boolean allowed = shareProduct.isSharesAllowed(requestedShares) ;
if(!allowed) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).failWithCode("differ.from.productdefinition", "Out of range");;
}
/*Long subscribedShares = shareProduct.getSubscribedShares() ;
if(subscribedShares == null) subscribedShares = new Long(0) ;
Long totalShares = shareProduct.getTotalShares() ;
Long issuedShares = shareProduct.getSharesIssued() ;
if(issuedShares == null) issuedShares = totalShares ;
if((subscribedShares+requestedShares) > issuedShares) {
throw new IssueableSharesExceededException() ;
}
shareProduct.addSubscribedShares(requestedShares);*/
/*BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareAccountApiConstants.purchasedprice_paramname, element,
locale);
baseDataValidator.reset().parameter(ShareAccountApiConstants.purchasedprice_paramname).value(unitPrice).notNull().positiveAmount();*/
LocalDate applicationDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.applicationdate_param, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.applicationdate_param).value(applicationDate).notNull();
Boolean allowdividendsForInactiveClients = this.fromApiJsonHelper.extractBooleanNamed(
ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname, element);
Integer minimumActivePeriod = this.fromApiJsonHelper.extractIntegerNamed(ShareAccountApiConstants.minimumactiveperiod_paramname,
element, locale);
PeriodFrequencyType minimumActivePeriodEnum = extractPeriodType(
ShareAccountApiConstants.minimumactiveperiodfrequencytype_paramname, element);
if (minimumActivePeriod != null) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.minimumactiveperiodfrequencytype_paramname)
.value(minimumActivePeriodEnum.getValue()).integerSameAsNumber(PeriodFrequencyType.DAYS.getValue());
}
Integer lockinPeriod = this.fromApiJsonHelper.extractIntegerNamed(ShareAccountApiConstants.lockinperiod_paramname, element, locale);
PeriodFrequencyType lockPeriodEnum = extractPeriodType(ShareAccountApiConstants.lockperiodfrequencytype_paramname, element);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
SavingsAccount savingsAccount = this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsAccountId);
final MonetaryCurrency currency = shareProduct.getCurrency();
Set<ShareAccountCharge> charges = assembleListOfAccountCharges(element, currency.getCode());
AppUser submittedBy = platformSecurityContext.authenticatedUser();
AppUser approvedBy = null;
Date approvedDate = null;
AppUser rejectedBy = null;
Date rejectedDate = null;
AppUser activatedBy = null;
Date activatedDate = null;
AppUser closedBy = null;
Date closedDate = null;
AppUser modifiedBy = null;
Date modifiedDate = null;
String accountNo = null;
Long approvedShares = null;
Long pendingShares = requestedShares;
BigDecimal unitPrice = shareProduct.deriveMarketPrice(applicationDate.toDate()) ;
ShareAccountTransaction transaction = new ShareAccountTransaction(applicationDate.toDate(), requestedShares, unitPrice);
Set<ShareAccountTransaction> sharesPurchased = new HashSet<>();
sharesPurchased.add(transaction);
ShareAccount account = new ShareAccount(client, shareProduct, externalId, currency, savingsAccount, accountNo, approvedShares,
pendingShares, sharesPurchased, allowdividendsForInactiveClients, lockinPeriod, lockPeriodEnum, minimumActivePeriod,
minimumActivePeriodEnum, charges, submittedBy, submittedDate.toDate(), approvedBy, approvedDate, rejectedBy, rejectedDate,
activatedBy, activatedDate, closedBy, closedDate, modifiedBy, modifiedDate);
for (ShareAccountTransaction pur : sharesPurchased) {
pur.setShareAccount(account);
}
if (charges != null) {
for (ShareAccountCharge charge : charges) {
charge.update(account);
}
}
createChargeTransaction(account, transaction);
return account;
}
private void createChargeTransaction(ShareAccount account, final ShareAccountTransaction transaction) {
BigDecimal totalChargeAmount = BigDecimal.ZERO;
Set<ShareAccountCharge> charges = account.getCharges();
Date currentDate = DateUtils.getLocalDateOfTenant().toDate();
for (ShareAccountCharge charge : charges) {
if (charge.isShareAccountActivation()) {
charge.deriveChargeAmount(totalChargeAmount) ;
ShareAccountTransaction chargeTransaction = ShareAccountTransaction.createChargeTransaction(currentDate, charge);
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(chargeTransaction, charge, charge.percentageOrAmount());
chargeTransaction.addShareAccountChargePaidBy(paidBy);
account.addChargeTransaction(chargeTransaction);
}
}
Set<ShareAccountTransaction> pendingApprovalTransaction = account.getPendingForApprovalSharePurchaseTransactions();
for (ShareAccountTransaction pending : pendingApprovalTransaction) {
for (ShareAccountCharge charge : charges) {
if (charge.isSharesPurchaseCharge()) {
BigDecimal amount = charge.deriveChargeAmount(pending.amount());
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(pending, charge, amount);
pending.addShareAccountChargePaidBy(paidBy);
totalChargeAmount = totalChargeAmount.add(amount);
}
}
pending.updateChargeAmount(totalChargeAmount);
}
}
public Map<String, Object> validateAndUpdate(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
ShareProduct shareProduct = account.getShareProduct() ;
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.productid_paramname, element)) {
final Long productId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.productid_paramname, element);
shareProduct = this.shareProductRepository.findOneWithNotFoundDetection(productId);
if (account.setShareProduct(shareProduct)) {
actualChanges.put(ShareAccountApiConstants.productid_paramname, productId);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.submitteddate_paramname, element)) {
final Date submittedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.submitteddate_paramname,
element).toDate();
baseDataValidator.reset().parameter(ShareAccountApiConstants.submitteddate_paramname).value(submittedDate).notNull();
if (account.setSubmittedDate(submittedDate)) {
actualChanges.put(ShareAccountApiConstants.submitteddate_paramname, submittedDate);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.externalid_paramname, element)) {
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull();
if (account.setExternalId(externalId)) {
actualChanges.put(ShareAccountApiConstants.externalid_paramname, externalId);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.savingsaccountid_paramname, element)) {
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
SavingsAccount savingsAccount = this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsAccountId);
if (account.setSavingsAccount(savingsAccount)) {
actualChanges.put(ShareAccountApiConstants.savingsaccountid_paramname, savingsAccount);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) {
Long requestedShares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element);
/*BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareAccountApiConstants.purchasedprice_paramname,
element, locale);*/
Date applicationDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.applicationdate_param, element)
.toDate();
BigDecimal unitPrice = shareProduct.deriveMarketPrice(applicationDate) ;
ShareAccountTransaction purchased = new ShareAccountTransaction(applicationDate, requestedShares, unitPrice);
account.updateRequestedShares(purchased);
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, purchased);
boolean allowed = shareProduct.isSharesAllowed(requestedShares) ;
if(!allowed) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).failWithCode("differ.from.productdefinition", "Out of range");;
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname, element)) {
Boolean allowdividendsForInactiveClients = this.fromApiJsonHelper.extractBooleanNamed(
ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname, element);
if (account.setAllowDividendCalculationForInactiveClients(allowdividendsForInactiveClients)) {
actualChanges.put(ShareAccountApiConstants.allowdividendcalculationforinactiveclients_paramname,
allowdividendsForInactiveClients);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.lockinperiod_paramname, element)) {
final Integer lockinperiod = this.fromApiJsonHelper.extractIntegerNamed(ShareAccountApiConstants.lockinperiod_paramname,
element, locale);
baseDataValidator.reset().parameter(ShareAccountApiConstants.lockinperiod_paramname).value(lockinperiod).notNull();
if (account.setLockPeriod(lockinperiod)) {
actualChanges.put(ShareAccountApiConstants.lockinperiod_paramname, lockinperiod);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.lockperiodfrequencytype_paramname, element)) {
PeriodFrequencyType lockPeriod = extractPeriodType(ShareAccountApiConstants.lockperiodfrequencytype_paramname, element);
if (account.setLockPeriodFrequencyEnum(lockPeriod)) {
actualChanges.put(ShareAccountApiConstants.lockperiodfrequencytype_paramname, lockPeriod);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.minimumactiveperiod_paramname, element)) {
final Integer minimumActivePeriod = this.fromApiJsonHelper.extractIntegerNamed(
ShareAccountApiConstants.minimumactiveperiod_paramname, element, locale);
baseDataValidator.reset().parameter(ShareAccountApiConstants.minimumactiveperiod_paramname).value(minimumActivePeriod)
.notNull();
if (account.setminimumActivePeriod(minimumActivePeriod)) {
actualChanges.put(ShareAccountApiConstants.minimumactiveperiod_paramname, minimumActivePeriod);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.minimumactiveperiodfrequencytype_paramname, element)) {
PeriodFrequencyType minimumActivePeriod = extractPeriodType(
ShareAccountApiConstants.minimumactiveperiodfrequencytype_paramname, element);
if (account.setminimumActivePeriodTypeEnum(minimumActivePeriod)) {
actualChanges.put(ShareAccountApiConstants.minimumactiveperiodfrequencytype_paramname, minimumActivePeriod);
}
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
shareProduct = account.getShareProduct();
Set<ShareAccountCharge> updatedCharges = assembleListOfAccountChargesforUpdate(account, element, shareProduct.getCurrency()
.getCode());
if (!updatedCharges.isEmpty()) {
actualChanges.put(ShareAccountApiConstants.charges_paramname, new HashSet<ShareAccountCharge>());
}
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
return actualChanges;
}
@SuppressWarnings("null")
public Map<String, Object> validateAndApprove(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.approvalParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
LocalDate approvedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.approveddate_paramname, element);
final LocalDate submittalDate = new LocalDate(account.getSubmittedDate());
if (approvedDate != null && approvedDate.isBefore(submittalDate)) {
final DateTimeFormatter formatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(jsonCommand.extractLocale());
final String submittalDateAsString = formatter.print(submittalDate);
baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).value(submittalDateAsString)
.failWithCodeNoParameterAddedToErrorCode("cannot.be.before.submittal.date");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
AppUser approvedUser = this.platformSecurityContext.authenticatedUser();
account.approve(approvedDate.toDate(), approvedUser);
actualChanges.put(ShareAccountApiConstants.id_paramname, account.getId());
updateTotalChargeDerived(account);
return actualChanges;
}
private void updateTotalChargeDerived(final ShareAccount shareAccount) {
// Set<ShareAccountCharge> charges = shareAccount.getCharges() ;
Set<ShareAccountTransaction> transactions = shareAccount.getShareAccountTransactions();
for (ShareAccountTransaction transaction : transactions) {
Set<ShareAccountChargePaidBy> paidBySet = transaction.getChargesPaidBy();
if (paidBySet != null && !paidBySet.isEmpty()) {
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
ShareAccountCharge charge = chargePaidBy.getCharge();
if (charge.isSharesPurchaseCharge()) {
Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money);
}
}
}
}
}
public Map<String, Object> validateAndUndoApprove(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
//final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
//this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.activateParameters);
//final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
//final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
//JsonElement element = jsonCommand.parsedJson();
//String notes = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.note_paramname, element);
// baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).validateDateAfter(account.get)
//AppUser approvedUser = this.platformSecurityContext.authenticatedUser();
account.undoApprove();
actualChanges.put(ShareAccountApiConstants.charges_paramname, Boolean.TRUE);
return actualChanges;
}
@SuppressWarnings("unused")
public Map<String, Object> validateAndReject(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
AppUser rejectedUser = this.platformSecurityContext.authenticatedUser();
Date rejectedDate = DateUtils.getDateOfTenant();
account.reject(rejectedDate, rejectedUser);
actualChanges.put(ShareAccountApiConstants.charges_paramname, Boolean.TRUE);
return actualChanges;
}
@SuppressWarnings("null")
public Map<String, Object> validateAndActivate(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.activateParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
LocalDate activatedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.activatedate_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.activatedate_paramname).value(activatedDate).notNull();
final LocalDate approvedDate = new LocalDate(account.getApprovedDate());
if (activatedDate != null && activatedDate.isBefore(approvedDate)) {
final DateTimeFormatter formatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(jsonCommand.extractLocale());
final String submittalDateAsString = formatter.print(approvedDate);
baseDataValidator.reset().parameter(ShareAccountApiConstants.activatedate_paramname).value(submittalDateAsString)
.failWithCodeNoParameterAddedToErrorCode("cannot.be.before.approved.date");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
AppUser approvedUser = this.platformSecurityContext.authenticatedUser();
account.activate(activatedDate.toDate(), approvedUser);
handlechargesOnActivation(account);
actualChanges.put(ShareAccountApiConstants.charges_paramname, activatedDate.toDate());
return actualChanges;
}
private void handlechargesOnActivation(final ShareAccount account) {
Set<ShareAccountCharge> charges = account.getCharges();
for (ShareAccountCharge charge : charges) {
if (charge.isShareAccountActivation()) {
charge.markAsFullyPaid();
}
}
}
private Set<ShareAccountCharge> assembleListOfAccountCharges(final JsonElement element, final String currencyCode) {
final Set<ShareAccountCharge> charges = new HashSet<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
JsonArray chargesArray = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.charges_paramname, element);
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has("chargeId")) {
final Long id = jsonObject.get("chargeId").getAsLong();
BigDecimal amount = jsonObject.get("amount").getAsBigDecimal();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
if (!currencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Share Account must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.share.account", errorMessage);
}
ChargeTimeType chargeTime = null;
ChargeCalculationType chargeCalculation = null;
Boolean status = Boolean.TRUE;
ShareAccountCharge accountCharge = ShareAccountCharge.createNewWithoutShareAccount(charge, amount, chargeTime,
chargeCalculation, status);
charges.add(accountCharge);
}
}
}
}
return charges;
}
private Set<ShareAccountCharge> assembleListOfAccountChargesforUpdate(final ShareAccount account, final JsonElement element,
final String currencyCode) {
Set<ShareAccountCharge> updated = new HashSet<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
JsonArray chargesArray = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.charges_paramname, element);
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has("id")) {
final Long id = jsonObject.get("id").getAsLong();
final Long chargeId = jsonObject.get("chargeId").getAsLong();
BigDecimal amount = jsonObject.get("amount").getAsBigDecimal();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
if (!currencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Share Account must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.share.account", errorMessage);
}
ShareAccountCharge updatedCharge = account.updateShareCharge(id, chargeId, amount);
updated.add(updatedCharge);
} else {
if (jsonObject.has("chargeId")) {
final Long id = jsonObject.get("chargeId").getAsLong();
BigDecimal amount = jsonObject.get("amount").getAsBigDecimal();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
if (!currencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Share Account must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.share.account", errorMessage);
}
ChargeTimeType chargeTime = ChargeTimeType.fromInt(charge.getChargeTimeType());
ChargeCalculationType chargeCalculation = ChargeCalculationType.fromInt(charge.getChargeCalculation());
Boolean status = Boolean.TRUE;
ShareAccountCharge accountCharge = ShareAccountCharge.createNewWithoutShareAccount(charge, amount, chargeTime,
chargeCalculation, status);
account.addShareAccountCharge(accountCharge);
updated.add(accountCharge);
}
}
}
}
}
return updated;
}
private PeriodFrequencyType extractPeriodType(String paramName, final JsonElement element) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.INVALID;
frequencyType = PeriodFrequencyType.fromInt(this.fromApiJsonHelper.extractIntegerWithLocaleNamed(paramName, element));
return frequencyType;
}
public Map<String, Object> validateAndApplyAddtionalShares(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(),
ShareAccountApiConstants.addtionalSharesParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
LocalDate requestedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.requesteddate_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname).value(requestedDate).notNull();
final Long sharesRequested = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested).notNull();
ShareProduct shareProduct = account.getShareProduct() ;
if(sharesRequested != null) {
boolean allowed = shareProduct.isSharesAllowed(sharesRequested) ;
if(!allowed) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested).failWithCode("differ.from.productdefinition", "Out of range");;
}
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
final BigDecimal unitPrice = shareProduct.deriveMarketPrice(requestedDate.toDate()) ;
ShareAccountTransaction purchaseTransaction = new ShareAccountTransaction(requestedDate.toDate(), sharesRequested, unitPrice);
account.addAdditionalPurchasedShares(purchaseTransaction);
handleAdditionalSharesChargeTransactions(account, purchaseTransaction);
actualChanges.put(ShareAccountApiConstants.additionalshares_paramname, purchaseTransaction);
return actualChanges;
}
private void handleAdditionalSharesChargeTransactions(final ShareAccount account, final ShareAccountTransaction purchaseTransaction) {
Set<ShareAccountCharge> charges = account.getCharges();
BigDecimal totalChargeAmount = BigDecimal.ZERO;
for (ShareAccountCharge charge : charges) {
if (charge.isSharesPurchaseCharge()) {
BigDecimal amount = charge.updateChargeDetailsForAdditionalSharesRequest(purchaseTransaction.amount());
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(purchaseTransaction, charge, amount);
purchaseTransaction.addShareAccountChargePaidBy(paidBy);
totalChargeAmount = totalChargeAmount.add(amount);
}
}
purchaseTransaction.updateChargeAmount(totalChargeAmount);
}
public Map<String, Object> validateAndApproveAddtionalShares(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(),
ShareAccountApiConstants.addtionalSharesParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
final ArrayList<Long> purchasedShares = new ArrayList<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) {
JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.requestedshares_paramname, element);
long totalShares = 0 ;
for (JsonElement arrayElement : array) {
final Long purchasedSharesId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.id_paramname, arrayElement);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(purchasedSharesId).notBlank() ;
ShareAccountTransaction transaction = account.retrievePurchasedShares(purchasedSharesId);
if (transaction != null) {
totalShares+=transaction.getTotalShares().longValue() ;
transaction.approve();
updateTotalChargeDerivedForAdditonalShares(account, transaction);
}
purchasedShares.add(purchasedSharesId);
}
if(totalShares > 0) {
account.updateApprovedShares(new Long(totalShares)) ;
}
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, purchasedShares);
return actualChanges;
}
private void updateTotalChargeDerivedForAdditonalShares(final ShareAccount shareAccount, final ShareAccountTransaction transaction) {
Set<ShareAccountChargePaidBy> paidBySet = transaction.getChargesPaidBy();
if (paidBySet != null && !paidBySet.isEmpty()) {
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
ShareAccountCharge charge = chargePaidBy.getCharge();
if (charge.isSharesPurchaseCharge()) {
Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money);
}
}
}
}
public Map<String, Object> validateAndRejectAddtionalShares(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(),
ShareAccountApiConstants.addtionalSharesParameters);
JsonElement element = jsonCommand.parsedJson();
final ArrayList<Long> purchasedShares = new ArrayList<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) {
long totalShares = 0 ;
JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.requestedshares_paramname, element);
for (JsonElement arrayElement : array) {
final Long purchasedSharesId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.id_paramname, arrayElement);
ShareAccountTransaction shares = account.retrievePurchasedShares(purchasedSharesId);
if (shares != null) {
shares.reject();
updateTotalChargeDerivedForAdditonalSharesReject(account, shares) ;
totalShares +=shares.getTotalShares().longValue() ;
}
purchasedShares.add(purchasedSharesId);
}
if(totalShares > 0) {
account.removePendingShares(new Long(totalShares)) ;
}
}
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, purchasedShares);
return actualChanges;
}
private void updateTotalChargeDerivedForAdditonalSharesReject(final ShareAccount shareAccount, final ShareAccountTransaction transaction) {
Set<ShareAccountChargePaidBy> paidBySet = transaction.getChargesPaidBy();
if (paidBySet != null && !paidBySet.isEmpty()) {
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
ShareAccountCharge charge = chargePaidBy.getCharge();
if (charge.isSharesPurchaseCharge()) {
Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money);
}
}
}
}
public Map<String, Object> validateAndRedeemShares(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(),
ShareAccountApiConstants.addtionalSharesParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
LocalDate requestedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.requesteddate_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname).value(requestedDate).notNull();
final Long sharesRequested = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested).notNull()
.longGreaterThanZero();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareAccountApiConstants.purchasedprice_paramname,
element, locale);
baseDataValidator.reset().parameter(ShareAccountApiConstants.purchasedprice_paramname).value(unitPrice).notNull().positiveAmount();
ShareAccountTransaction transaction = ShareAccountTransaction.createRedeemTransaction(requestedDate.toDate(), sharesRequested,
unitPrice);
account.addAdditionalPurchasedShares(transaction);
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, transaction);
handleRedeemSharesChargeTransactions(account, transaction);
return actualChanges;
}
private void handleRedeemSharesChargeTransactions(final ShareAccount account, final ShareAccountTransaction transaction) {
Set<ShareAccountCharge> charges = account.getCharges();
BigDecimal totalChargeAmount = BigDecimal.ZERO;
for (ShareAccountCharge charge : charges) {
if (charge.isSharesRedeemCharge()) {
BigDecimal amount = charge.updateChargeDetailsForAdditionalSharesRequest(transaction.amount());
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(transaction, charge, amount);
transaction.addShareAccountChargePaidBy(paidBy);
totalChargeAmount = totalChargeAmount.add(amount);
}
}
transaction.deductChargesFromTotalAmount(totalChargeAmount);
Set<ShareAccountChargePaidBy> paidBySet = transaction.getChargesPaidBy();
if (paidBySet != null && !paidBySet.isEmpty()) {
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
ShareAccountCharge charge = chargePaidBy.getCharge();
if (charge.isSharesRedeemCharge()) {
Money money = Money.of(account.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money);
}
}
}
// transaction.adjustRedeemAmount();
}
public Map<String, Object> validateAndClose(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.closeParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson();
LocalDate closedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.closeddate_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).value(closedDate).notNull();
AppUser approvedUser = this.platformSecurityContext.authenticatedUser();
final BigDecimal unitPrice = account.getShareProduct().deriveMarketPrice(DateUtils.getDateOfTenant()) ;
ShareAccountTransaction transaction = ShareAccountTransaction.createRedeemTransaction(closedDate.toDate(), account.getTotalApprovedShares(), unitPrice) ;
account.addAdditionalPurchasedShares(transaction);
account.close(closedDate.toDate(), approvedUser);
handleRedeemSharesChargeTransactions(account, transaction) ;
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, transaction);
updateTotalChargeDerived(account);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
return actualChanges;
}
}
@@ -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.shareaccounts.service;
import java.util.Collection;
import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData;
public interface PurchasedSharesReadPlatformService {
public Collection<PurchasedSharesData> retrievePurchasedShares(final Long accountId) ;
}

Some files were not shown because too many files have changed in this diff Show More