diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java index 03ca3c60a..01a6510ec 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java @@ -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 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"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformService.java index a8ce5d1e5..8c9aa3116 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformService.java @@ -41,5 +41,7 @@ public interface AccountingDropdownReadPlatformService { public Map> retrieveAccountMappingOptionsForCharges(); public Map> retrieveAccountMappingOptions(); + + public Map> retrieveAccountMappingOptionsForShareProducts(); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformServiceImpl.java index 87c6b2892..66ce19274 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingDropdownReadPlatformServiceImpl.java @@ -147,4 +147,15 @@ public class AccountingDropdownReadPlatformServiceImpl implements AccountingDrop return accountOptions; } + @Override + public Map> retrieveAccountMappingOptionsForShareProducts() { + boolean includeAssetAccounts = true; + boolean includeIncomeAccounts = true; + boolean includeExpenseAccounts = false; + boolean includeLiabilityAccounts = true; + boolean includeEquityAccounts = true; + return retrieveAccountMappingOptions(includeAssetAccounts, includeIncomeAccounts, includeExpenseAccounts, includeLiabilityAccounts, + includeEquityAccounts); + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/financialactivityaccount/serialization/FinancialActivityAccountDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/financialactivityaccount/serialization/FinancialActivityAccountDataValidator.java index 104b3c82d..810de31b3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/financialactivityaccount/serialization/FinancialActivityAccountDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/financialactivityaccount/serialization/FinancialActivityAccountDataValidator.java @@ -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)) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/SharesDTO.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/SharesDTO.java new file mode 100644 index 000000000..28cbb7f7f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/SharesDTO.java @@ -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 newTransactions; + + public SharesDTO(final Long shareAccountId, final Long shareProductId, final Long officeId, final String currencyCode, + final boolean cashBasedAccountingEnabled, final boolean accrualBasedAccountingEnabled, + final List 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 getNewTransactions() { + return this.newTransactions; + } + + public void setNewTransactions(List newTransactions) { + this.newTransactions = newTransactions; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/SharesTransactionDTO.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/SharesTransactionDTO.java new file mode 100644 index 000000000..a4871bdf5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/SharesTransactionDTO.java @@ -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 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 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 getFeePayments() { + return this.feePayments; + } + + public ShareAccountTransactionEnumData getTransactionStatus() { + return this.transactionStatus; + } + + public BigDecimal getChargeAmount() { + return this.chargeAmount; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java index 341baf266..3e499d1f1 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java @@ -78,6 +78,9 @@ public class JournalEntry extends AbstractAuditableCustom { @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 { 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 { 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 { this.savingsTransaction = savingsTransaction; this.clientTransaction = clientTransaction; this.paymentDetail = paymentDetail; + this.shareTransactionId = shareTransactionId; } public boolean isDebitEntry() { @@ -206,11 +210,16 @@ public class JournalEntry extends AbstractAuditableCustom { } public Long getEntityId() { - return this.entityId ; + return this.entityId; } - + public Integer getEntityType() { - return this.entityType ; + return this.entityType; } + + public Long getShareTransactionId() { + return this.shareTransactionId; + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForShares.java similarity index 65% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountWritePlatformService.java rename to fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForShares.java index feb754b39..a8f5e83ab 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForShares.java @@ -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) ; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSharesFactory.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSharesFactory.java new file mode 100644 index 000000000..a59fd4097 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSharesFactory.java @@ -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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java index fb9c799ae..ec2c0dd15 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java @@ -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 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 newTransactions = new ArrayList<>(); + + @SuppressWarnings("unchecked") + final List> newTransactionsMap = (List>) accountingBridgeData.get("newTransactions"); + + for (final Map 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 feePayments = new ArrayList<>(); + // extract charge payment details (if exists) + if (map.containsKey("chargesPaid")) { + @SuppressWarnings("unchecked") + final List> chargesPaidData = (List>) map.get("chargesPaid"); + for (final Map 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 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 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 chargePaymentDTOs) { + final Map 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 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) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForSavings.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForSavings.java index 27eb9e8dd..474f32162 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForSavings.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForSavings.java @@ -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()) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForShares.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForShares.java new file mode 100644 index 000000000..f379ed17a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForShares.java @@ -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 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 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 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); + } + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java index 480e722b8..4428daaed 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java @@ -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 accountingBridgeData); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java index 4ac3dc295..aed5f7f42 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java @@ -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 liabilityMap, - Map expenseMap) { + + private void createJournalEnry(Date transactionDate, Long entryId, Office office, String currencyCode, + Map liabilityMap, Map expenseMap) { Set 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 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 dataValidationErrors = new ArrayList<>(); @@ -493,6 +499,23 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa } + @Transactional + @Override + public void createJournalEntriesForShares(final Map 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; diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/PortfolioProductType.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/PortfolioProductType.java index 786699c2d..f43f7e11a 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/PortfolioProductType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/PortfolioProductType.java @@ -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()); + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java index 17558282a..6189087b5 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java @@ -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 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); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformService.java index 2a903d536..0ce623261 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformService.java @@ -41,5 +41,11 @@ public interface ProductToGLAccountMappingReadPlatformService { public List fetchFeeToIncomeAccountMappingsForSavingsProduct(final Long savingsProductId); public List fetchPenaltyToIncomeAccountMappingsForSavingsProduct(final Long savingsProductId); + + public Map fetchAccountMappingDetailsForShareProduct(final Long productId, final Integer accountingType); + + public List fetchPaymentTypeToFundSourceMappingsForShareProduct(final Long productId); + + public List fetchFeeToIncomeAccountMappingsForShareProduct(final Long productId); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java index ef1a2af2e..b5a84f226 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java @@ -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 fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType) { + + final Map 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> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, new Object[] { + PortfolioProductType.SHARES.getValue(), productId }); + + if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) { + for (final Map 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 fetchPaymentTypeToFundSourceMappingsForShareProduct(Long productId) { + return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.SHARES, productId); + } + + @Override + public List fetchFeeToIncomeAccountMappingsForShareProduct(Long productId) { + return fetchChargeToIncomeAccountMappings(PortfolioProductType.SHARES, productId, false); + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java index 359a3d11b..c471a135c 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java @@ -35,4 +35,9 @@ public interface ProductToGLAccountMappingWritePlatformService { Map updateSavingsProductToGLAccountMapping(Long savingsProductId, JsonCommand command, boolean accountingRuleChanged, int accountingRuleTypeId, DepositAccountType accountType); + void createShareProductToGLAccountMapping(Long shareProductId, JsonCommand command); + + Map updateShareProductToGLAccountMapping(Long shareProductId, JsonCommand command, boolean accountingRuleChanged, + int accountingRuleTypeId); + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java index 8e0c87b75..c735997fa 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java @@ -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 updateLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command, @@ -293,4 +342,34 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc } return changes; } + + @Override + public Map updateShareProductToGLAccountMapping(final Long shareProductId, final JsonCommand command, + final boolean accountingRuleChanged, final int accountingRuleTypeId) { + /*** + * Variable tracks all accounting mapping properties that have been + * updated + ***/ + Map 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; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ShareProductToGLAccountMappingHelper.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ShareProductToGLAccountMappingHelper.java new file mode 100644 index 000000000..489532fc0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ShareProductToGLAccountMappingHelper.java @@ -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 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 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 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 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 changes) { + savePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SHARES); + } + + public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId, + final Map changes) { + updatePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SHARES); + } + + public void saveChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId, + final Map 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 changes) { + updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, true); + updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, false); + } + + public Map populateChangesForNewSharesProductToGLAccountMappingCreation(final JsonElement element, + final AccountingRuleType accountingRuleType) { + final Map 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 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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index cc33ecc14..bd1357494 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -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; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/EntityAccountType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/EntityAccountType.java index 2c2a65ade..2ba4784e5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/EntityAccountType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/EntityAccountType.java @@ -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; diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java index 67ed8456f..97c98ba9c 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java @@ -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; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityAccessType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityAccessType.java index a54ac0fa2..b4c11d106 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityAccessType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityAccessType.java @@ -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; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityType.java index 828009527..dd8e384f5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/entityaccess/domain/FineractEntityType.java @@ -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; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java index b244804a4..999840b95 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java @@ -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) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerWritePlatformServiceJpaImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerWritePlatformServiceJpaImpl.java index 8a6d15fd5..bff951cbb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerWritePlatformServiceJpaImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/teller/service/TellerWritePlatformServiceJpaImpl.java @@ -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); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/data/AccountSummaryCollectionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/data/AccountSummaryCollectionData.java index 2e7f1c6f5..f4faacaa3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/data/AccountSummaryCollectionData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/data/AccountSummaryCollectionData.java @@ -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 loanAccounts; - @SuppressWarnings("unused") private final Collection savingsAccounts; - @SuppressWarnings("unused") + private final Collection shareAccounts ; + private final Collection memberLoanAccounts; - @SuppressWarnings("unused") private final Collection memberSavingsAccounts; public AccountSummaryCollectionData(final Collection loanAccounts, - final Collection savingsAccounts) { + final Collection savingsAccounts, final Collection shareAccounts) { this.loanAccounts = defaultLoanAccountsIfEmpty(loanAccounts); this.savingsAccounts = defaultSavingsAccountsIfEmpty(savingsAccounts); + this.shareAccounts = defaultShareAccountsIfEmpty(shareAccounts) ; this.memberLoanAccounts = null; this.memberSavingsAccounts = null; } - + public AccountSummaryCollectionData(final Collection loanAccounts, final Collection savingsAccounts, final Collection memberLoanAccounts, final Collection 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 defaultShareAccountsIfEmpty(final Collection collection) { + Collection returnCollection = null; + if (collection != null && !collection.isEmpty()) { + returnCollection = collection; + } + return returnCollection; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/data/ShareAccountSummaryData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/data/ShareAccountSummaryData.java new file mode 100644 index 000000000..b4c0351ef --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/data/ShareAccountSummaryData.java @@ -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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java index 5622c8d03..339ffd9d2 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -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 loanAccounts = retrieveLoanAccountDetails(loanwhereClause, new Object[] { clientId }); final List savingsAccounts = retrieveAccountDetails(savingswhereClause, new Object[] { clientId }); - return new AccountSummaryCollectionData(loanAccounts, savingsAccounts); + final List 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 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 { + + 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 { final String schemaSql; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java index b20a19b1a..56df39f60 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java @@ -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()); + 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 diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java index 3de0f736f..bbfe50ef6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/constants/ShareAccountApiConstants.java @@ -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 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 approvalParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, approveddate_paramname, note_paramname)); + + Set activateParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, activatedate_paramname)); + + Set closeParameters = new HashSet<>(Arrays.asList(locale_paramname, dateformat_paramname, closeddate_paramname, note_paramname)); + + Set addtionalSharesParameters = new HashSet<>(Arrays.asList(locale_paramname, requesteddate_paramname, requestedshares_paramname, purchasedprice_paramname, dateformat_paramname)); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/PurchasedSharesData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/PurchasedSharesData.java deleted file mode 100644 index 28414aaf3..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/PurchasedSharesData.java +++ /dev/null @@ -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 ; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/ShareAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/ShareAccountData.java deleted file mode 100644 index a7cc0e0ad..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/ShareAccountData.java +++ /dev/null @@ -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 purchasedShares; - - private GLAccountData suspenseAccount; - - private GLAccountData equityAccount; - - private Long savingsAccountId; - - private EnumOptionData lockPeriod; - - private EnumOptionData minimumActivePeriodForDividends; - - private Boolean allowDividendCalculationForInactiveClients; - - private Collection 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 purchasedShares, final GLAccountData suspenseAccount, - final GLAccountData equityAccount, final EnumOptionData lockPeriod, final EnumOptionData minimumActivePeriodForDividends, - final Boolean allowDividendCalculationForInactiveClients, final Collection 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 ; - - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/PurchasedShares.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/PurchasedShares.java deleted file mode 100644 index afa4f4f77..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/PurchasedShares.java +++ /dev/null @@ -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 { - - @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 ; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccount.java deleted file mode 100644 index 55d443503..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccount.java +++ /dev/null @@ -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 { - - @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 ; - - @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 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, - final Boolean allowDividendCalculationForInactiveClients, final PeriodFrequencyType lockPeriod, - final PeriodFrequencyType minimumActivePeriodForDividends, Set 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) { - 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 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 = new ArrayList<>(); - for(PurchasedShares val: purchasedShares) { - purchasedSharesdata.add(new PurchasedSharesData(val.getPurchasedDate(), val.getTotalShares(), val.getPurchasePrice(), val.getStatus())) ; - } - Collection 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 additionalShares) { - this.purchasedShares.addAll(additionalShares) ; - } - - public Set getPurchasedShares() { - return this.purchasedShares ; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountCharge.java deleted file mode 100644 index 73fbfa5fd..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountCharge.java +++ /dev/null @@ -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 { - - @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() ; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountTempRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountTempRepository.java deleted file mode 100644 index 91602ff8d..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountTempRepository.java +++ /dev/null @@ -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 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 findAll() { - return cache.values() ; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/serialization/ShareAccountDataSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/serialization/ShareAccountDataSerializer.java deleted file mode 100644 index 815f36953..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/serialization/ShareAccountDataSerializer.java +++ /dev/null @@ -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 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>() {}.getType(); - //this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), supportedParameters); - - final List 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 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 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 validateAndUpdate(JsonCommand jsonCommand, ShareAccount account) { - Map actualChanges = new HashMap<>(); - if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } - final Type typeOfMap = new TypeToken>() {}.getType(); - //this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), supportedParameters); - - final List 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 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 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 assembleListOfAccountCharges(final JsonElement element, final String currencyCode) { - final Set 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 asemblePurchasedShares(final JsonElement element) { - Set 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 asembleAdditionalShares(final JsonElement element) { - Set 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; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/AccountReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/AccountReadPlatformService.java index 92742849d..42ef5e390 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/AccountReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/AccountReadPlatformService.java @@ -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 retrieveAll() ; - + public AccountData retrieveTemplate(final Long clientId, final Long productId); + + public AccountData retrieveOne(Long id, boolean includeTemplate); + + public Collection retrieveAll(); + public Set getResponseDataParams(); + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountReadPlatformServiceImpl.java deleted file mode 100644 index 971d3fb32..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountReadPlatformServiceImpl.java +++ /dev/null @@ -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 retrieveAll() { - Collection collection = ShareAccountTempRepository.getInstance().findAll() ; - Set set = new HashSet<>() ; - for(ShareAccount data: collection) { - set.add(data.toData()) ; - } - return set; - } - - @Override - public Set getResponseDataParams() { - return null; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java deleted file mode 100644 index d3afa5de6..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java +++ /dev/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 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) { - - } - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java index 28bcd74bb..627eaf954 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java @@ -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, Serializable { private final List savingsChargeTimeTypeOptions; private final List clientChargeCalculationTypeOptions; private final List clientChargeTimeTypeOptions; + private final List shareChargeCalculationTypeOptions; + private final List shareChargeTimeTypeOptions; + private final List feeFrequencyOptions; private final Map> incomeOrLiabilityAccountOptions; @@ -81,14 +85,17 @@ public class ChargeData implements Comparable, Serializable { final List savingsChargeCalculationTypeOptions, final List savingsChargeTimeTypeOptions, final List clientChargeCalculationTypeOptions, final List clientChargeTimeTypeOptions, final List feeFrequencyOptions, final Map> incomeOrLiabilityAccountOptions, - final Collection taxGroupOptions) { + final Collection taxGroupOptions, final List shareChargeCalculationTypeOptions, + final List 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, 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, Serializable { final List clientChargeCalculationTypeOptions = null; final List clientChargeTimeTypeOptions = null; final Map> incomeOrLiabilityAccountOptions = null; + final List shareChargeCalculationTypeOptions = null; + final List shareChargeTimeTypeOptions = null; final Collection 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, Serializable { final List feeFrequencyOptions = null; final GLAccountData account = null; final Map> incomeOrLiabilityAccountOptions = null; + final List shareChargeCalculationTypeOptions = null; + final List shareChargeTimeTypeOptions = null; final TaxGroupData taxGroupData = null; final Collection taxGroupOptions = null; return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode, @@ -166,7 +177,7 @@ public class ChargeData implements Comparable, 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, Serializable { final List clientChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval, final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency, final List feeFrequencyOptions, final GLAccountData account, - final Map> incomeOrLiabilityAccountOptions, final Collection taxGroupOptions) { + final Map> incomeOrLiabilityAccountOptions, final Collection taxGroupOptions, + final List shareChargeCalculationTypeOptions, final List shareChargeTimeTypeOptions) { this.id = id; this.name = name; this.amount = amount; @@ -212,6 +224,8 @@ public class ChargeData implements Comparable, 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, 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 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; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java index 76f574279..bc4cef038 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java @@ -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() }; } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java index cd7fe360d..c3379e342 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java @@ -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()}; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeTimeType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeTimeType.java index 5ec2ad840..5aa6fe561 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeTimeType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeTimeType.java @@ -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()); + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/exception/ShareAccountChargeWithoutMandatoryFieldException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/exception/ShareAccountChargeWithoutMandatoryFieldException.java new file mode 100644 index 000000000..199e776e7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/exception/ShareAccountChargeWithoutMandatoryFieldException.java @@ -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); + } + +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java index bbf18ba66..d58acc3a2 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java @@ -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 validLoanValues = Arrays.asList(ChargeTimeType.validLoanValues()); final Collection validSavingsValues = Arrays.asList(ChargeTimeType.validSavingsValues()); final Collection validClientValues = Arrays.asList(ChargeTimeType.validClientValues()); - + final Collection validShareValues = Arrays.asList(ChargeTimeType.validShareValues()) ; final Collection 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()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformService.java index 18ff74be1..4d36e07b0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformService.java @@ -42,4 +42,8 @@ public interface ChargeDropdownReadPlatformService { List retrieveClientCalculationTypes(); List retrieveClientCollectionTimeTypes(); + + List retrieveSharesCalculationTypes() ; + List retrieveSharesCollectionTimeTypes() ; + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java index 8b5c68a4f..be5753bbe 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java @@ -114,4 +114,16 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead public List retrieveClientCollectionTimeTypes() { return Arrays.asList(chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE)); } + + @Override + public List retrieveSharesCalculationTypes() { + return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), + chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT)); + } + + @Override + public List retrieveSharesCollectionTimeTypes() { + return Arrays.asList(chargeTimeType(ChargeTimeType.SHAREACCOUNT_ACTIVATION), + chargeTimeType(ChargeTimeType.SHARE_PURCHASE), chargeTimeType(ChargeTimeType.SHARE_REDEEM)); + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java index 68cabfd73..0099be5c5 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java @@ -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"); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java index 677e5d471..cd675af3c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java @@ -116,5 +116,11 @@ public interface ChargeReadPlatformService { /** Retrieve savings account charges **/ Collection retrieveSavingsAccountApplicableCharges(Long savingsId); + + /** + * Returns charge definitions which are applicable for shares + */ + Collection retrieveSharesApplicableCharges(); + public Collection retrieveShareProductCharges(final Long shareProductId) ; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java index 542e4714a..dcc4105df 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java @@ -147,11 +147,15 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService final List feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); final Map> incomeOrLiabilityAccountOptions = this.accountingDropdownReadPlatformService .retrieveAccountMappingOptionsForCharges(); + final List shareChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService + .retrieveSharesCalculationTypes(); + final List shareChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveSharesCollectionTimeTypes(); final Collection 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 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 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 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() }); + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java index 21aba2e2f..c46447012 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java @@ -299,7 +299,7 @@ public class ClientsApiResource { final AccountSummaryCollectionData clientAccount = this.accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId); - final Set CLIENT_ACCOUNTS_DATA_PARAMETERS = new HashSet<>(Arrays.asList("loanAccounts", "savingsAccounts")); + final Set 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); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java index c251d1ab6..e97711462 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java @@ -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 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 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 propertyMap, AccountNumberFormat accountNumberFormat) { String accountNumber = StringUtils.leftPad(propertyMap.get(ID), AccountNumberGenerator.maxLength, '0'); if (accountNumberFormat != null && accountNumberFormat.getPrefixEnum() != null) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java index 0c80ce3c5..c4ce47016 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java @@ -157,12 +157,13 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform final List feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); // this field is applicable only for client charges final Map> incomeOrLiabilityAccountOptions = null; + final List shareChargeCalculationTypeOptions = null ; + final List shareChargeTimeTypeOptions = null ; final Collection taxGroupOptions = null; - return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions, - incomeOrLiabilityAccountOptions, taxGroupOptions); + incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions); } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/api/ProductsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/api/ProductsApiResource.java index 5f58b3683..9badddae2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/api/ProductsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/api/ProductsApiResource.java @@ -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 toApiJsonSerializer; - private final DefaultToApiJsonSerializer toApiObjectJsonSerializer ; + private final DefaultToApiJsonSerializer toApiObjectJsonSerializer; private final PlatformSecurityContext platformSecurityContext; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - + @Autowired - public ProductsApiResource(final ApplicationContext applicationContext, - final ApiRequestParameterHelper apiRequestParameterHelper, - final DefaultToApiJsonSerializer toApiJsonSerializer, - final PlatformSecurityContext platformSecurityContext, + public ProductsApiResource(final ApplicationContext applicationContext, final ApiRequestParameterHelper apiRequestParameterHelper, + final DefaultToApiJsonSerializer toApiJsonSerializer, final PlatformSecurityContext platformSecurityContext, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final DefaultToApiJsonSerializer 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 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 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()); + 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); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/service/ProductReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/service/ProductReadPlatformService.java index 5c93948f1..e0ae46a18 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/service/ProductReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/products/service/ProductReadPlatformService.java @@ -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 retrieveAllProducts(); + public Page retrieveAllProducts(Integer offSet, Integer limit); - public ProductData retrieveOne(final Long productId); + public ProductData retrieveOne(final Long productId, boolean includeTemplate); public ProductData retrieveTemplate(); public Set getResponseDataParams(); + + public Collection retrieveAllForLookup() ; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsAccountTransactionType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsAccountTransactionType.java index f326b0817..057f88399 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsAccountTransactionType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsAccountTransactionType.java @@ -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(); } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java index d4b9cf1ba..e7da01185 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java @@ -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; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index 053f438f5..5cc2065e9 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -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 { } 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 { 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); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java index c050e7c52..56eadcf70 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java @@ -35,4 +35,6 @@ public interface SavingsAccountDomainService { BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isAccountTransfer, boolean isRegularTransaction); void postJournalEntries(SavingsAccount savingsAccount, Set existingTransactionIds, Set existingReversedTransactionIds); + + SavingsAccountTransaction handleDividendPayout(SavingsAccount account, LocalDate transactionDate, BigDecimal transactionAmount); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java index 3ce2f197e..53141c266 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -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 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(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java index a9cfc5d3e..2e6abccfa 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java @@ -135,6 +135,14 @@ public final class SavingsAccountTransaction extends AbstractPersistable { 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 { 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 { 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 { 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 { 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 { } public boolean isCredit() { - return isDeposit() || isInterestPostingAndNotReversed(); + return isDeposit() || isInterestPostingAndNotReversed() || isDividendPayoutAndNotReversed(); } public boolean isDebit() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java index 744180d64..6f7ffe62f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java @@ -35,7 +35,7 @@ public final class SavingsAccountTransactionSummaryWrapper { public BigDecimal calculateTotalDeposits(final MonetaryCurrency currency, final List 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 transactions) { Money total = Money.zero(currency); for (final SavingsAccountTransaction transaction : transactions) { @@ -141,5 +140,4 @@ public final class SavingsAccountTransactionSummaryWrapper { } return total.getAmountDefaultedToNullIfZero(); } - } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java index e2fa675af..59f7f1932 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java @@ -74,12 +74,18 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou private static final class SavingsAccountChargeMapper implements RowMapper { 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 feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); // this field is applicable only for client charges final Map> incomeOrLiabilityAccountOptions = null; - + final List shareChargeCalculationTypeOptions = null; + final List shareChargeTimeTypeOptions = null; final Collection 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 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 "; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java index c29556136..270562e30 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsEnumerations.java @@ -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; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/PurchasedSharesData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/PurchasedSharesData.java new file mode 100644 index 000000000..95048c63b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/PurchasedSharesData.java @@ -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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountApplicationTimelineData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountApplicationTimelineData.java new file mode 100644 index 000000000..73ef7513b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountApplicationTimelineData.java @@ -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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountChargeData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountChargeData.java new file mode 100644 index 000000000..497db0eb1 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountChargeData.java @@ -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 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 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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountData.java new file mode 100644 index 000000000..36d3e9b1b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountData.java @@ -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 purchasedShares; + + private Long savingsAccountId; + + private BigDecimal currentMarketPrice; + + private Integer lockinPeriod; + + private EnumOptionData lockPeriodTypeEnum; + + private Integer minimumActivePeriod; + + private EnumOptionData minimumActivePeriodTypeEnum; + + private Boolean allowDividendCalculationForInactiveClients; + + private Collection charges; + + private Collection dividends ; + + // Data for template + private Collection productOptions; + private Collection chargeOptions; + private Collection lockinPeriodFrequencyTypeOptions; + private Collection minimumActivePeriodFrequencyTypeOptions; + private Collection 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 charges, + final Collection 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 productOptions, + final Collection 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 charges, final BigDecimal currentMarketPrice, + final Collection minimumActivePeriodFrequencyTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, final Collection 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 charges, + final Collection purchasedSharesData, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum, + final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum, + final Boolean allowDividendCalculationForInactiveClients, final Collection productOptions, + final Collection chargeOptions, final Collection clientSavingsAccounts, + final Collection lockinPeriodFrequencyTypeOptions, + final Collection 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 productOptions, + Collection chargeOptions, Collection clientSavingsAccounts, + Collection lockinPeriodFrequencyTypeOptions, Collection 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 = null; + GLAccountData suspenseAccount = null; + Integer lockinPeriod = null; + EnumOptionData lockPeriodTypeEnum = null; + Integer minimumActivePeriod = null; + EnumOptionData minimumActivePeriodTypeEnum = null; + Collection 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 chargeOptions = null; + final Collection clientSavingsAccounts = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection minimumActivePeriodFrequencyTypeOption = null; + final BigDecimal currenMarketPrice = null; + final Long productId = null; + final Collection 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 getPurchasedShares() { + return this.purchasedShares; + } + + public void setCurrentMarketPrice(final BigDecimal currentMarketPrice) { + this.currentMarketPrice = currentMarketPrice; + } + + public void setDividends(Collection dividends) { + this.dividends = dividends ; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountDividendData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountDividendData.java new file mode 100644 index 000000000..ad195f470 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountDividendData.java @@ -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 ; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountStatusEnumData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountStatusEnumData.java new file mode 100644 index 000000000..c1dffc907 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountStatusEnumData.java @@ -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; + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountTransactionEnumData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountTransactionEnumData.java new file mode 100644 index 000000000..cfc774803 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountTransactionEnumData.java @@ -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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/PurchasedSharesStatusType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/PurchasedSharesStatusType.java new file mode 100644 index 000000000..949218a24 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/PurchasedSharesStatusType.java @@ -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()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccount.java new file mode 100644 index 000000000..36facf649 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccount.java @@ -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 { + + @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 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 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 purchasedShares, final Boolean allowDividendCalculationForInactiveClients, + final Integer lockinPeriodFrequency, final PeriodFrequencyType lockPeriodType, final Integer minimumActivePeriodFrequency, + final PeriodFrequencyType minimumActivePeriodType, Set 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 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 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 additionalShares) { + this.shareAccountTransactions.addAll(additionalShares); + } + + public Set getShareAccountTransactions() { + return this.shareAccountTransactions; + } + + public Set getChargeTransactions() { + Set 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 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 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 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 getCharges() { + return this.charges; + } + + public void addChargeTransaction(ShareAccountTransaction chargeTransaction) { + chargeTransaction.setShareAccount(this); + this.shareAccountTransactions.add(chargeTransaction); + } + + public Set getPendingForApprovalSharePurchaseTransactions() { + Set 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 ; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountCharge.java new file mode 100644 index 000000000..a415c3620 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountCharge.java @@ -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 { + + @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(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountChargePaidBy.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountChargePaidBy.java new file mode 100644 index 000000000..97f1ce315 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountChargePaidBy.java @@ -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{ + + @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() ; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendDetails.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendDetails.java new file mode 100644 index 000000000..40821cb58 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendDetails.java @@ -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 { + + @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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendRepository.java new file mode 100644 index 000000000..04ae892ae --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendRepository.java @@ -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, + JpaSpecificationExecutor { + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendStatusType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendStatusType.java new file mode 100644 index 000000000..99b03c89b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountDividendStatusType.java @@ -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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountRepository.java similarity index 94% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountRepository.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountRepository.java index 4867bf4d5..285203b05 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/domain/ShareAccountRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountRepository.java @@ -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; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountRepositoryWrapper.java new file mode 100644 index 000000000..33d556b1b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountRepositoryWrapper.java @@ -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) ; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountStatusType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountStatusType.java new file mode 100644 index 000000000..c276c8f93 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountStatusType.java @@ -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(); + } + +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountTransaction.java new file mode 100644 index 000000000..384e94ff5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/domain/ShareAccountTransaction.java @@ -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 { + + @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 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 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) ; + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/exceptions/IssueableSharesExceededException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/exceptions/IssueableSharesExceededException.java new file mode 100644 index 000000000..c924df061 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/exceptions/IssueableSharesExceededException.java @@ -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"); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ActivateShareAccountCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ActivateShareAccountCommandHandler.java new file mode 100644 index 000000000..f40f2dc72 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ActivateShareAccountCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApplyAddtionalSharesCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApplyAddtionalSharesCommandHandler.java new file mode 100644 index 000000000..ce9c7ddb4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApplyAddtionalSharesCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApproveAddtionalSharesCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApproveAddtionalSharesCommandHandler.java new file mode 100644 index 000000000..4d81eeb86 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApproveAddtionalSharesCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApproveShareAccountCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApproveShareAccountCommandHandler.java new file mode 100644 index 000000000..194627a2f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/ApproveShareAccountCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/CloseShareAccountCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/CloseShareAccountCommandHandler.java new file mode 100644 index 000000000..62fefa469 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/CloseShareAccountCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/handler/CreateShareAccountCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/CreateShareAccountCommandHandler.java similarity index 92% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/handler/CreateShareAccountCommandHandler.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/CreateShareAccountCommandHandler.java index ce4ca75da..5226d3237 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/handler/CreateShareAccountCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/CreateShareAccountCommandHandler.java @@ -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; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RedeemSharesCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RedeemSharesCommandHandler.java new file mode 100644 index 000000000..09bf0b416 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RedeemSharesCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RejectAddtionalSharesCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RejectAddtionalSharesCommandHandler.java new file mode 100644 index 000000000..dce0f842e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RejectAddtionalSharesCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RejectShareAccountCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RejectShareAccountCommandHandler.java new file mode 100644 index 000000000..962eacb84 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/RejectShareAccountCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/UndoApproveShareAccountCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/UndoApproveShareAccountCommandHandler.java new file mode 100644 index 000000000..bbfe92fc4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/UndoApproveShareAccountCommandHandler.java @@ -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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/handler/UpdateShareAccountCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/UpdateShareAccountCommandHandler.java similarity index 92% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/handler/UpdateShareAccountCommandHandler.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/UpdateShareAccountCommandHandler.java index 5945d0b61..d71ab1279 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/handler/UpdateShareAccountCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/handler/UpdateShareAccountCommandHandler.java @@ -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; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java new file mode 100644 index 000000000..13c7b4dac --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java @@ -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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.supportedParameters); + + final List 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 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 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 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 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 validateAndUpdate(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.supportedParameters); + + final List 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 updatedCharges = assembleListOfAccountChargesforUpdate(account, element, shareProduct.getCurrency() + .getCode()); + if (!updatedCharges.isEmpty()) { + actualChanges.put(ShareAccountApiConstants.charges_paramname, new HashSet()); + } + + } + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + return actualChanges; + } + + @SuppressWarnings("null") + public Map validateAndApprove(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.approvalParameters); + final List 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 charges = shareAccount.getCharges() ; + Set transactions = shareAccount.getShareAccountTransactions(); + for (ShareAccountTransaction transaction : transactions) { + Set 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 validateAndUndoApprove(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + //final Type typeOfMap = new TypeToken>() {}.getType(); + //this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.activateParameters); + //final List 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 validateAndReject(JsonCommand jsonCommand, ShareAccount account) { + Map 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 validateAndActivate(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.activateParameters); + final List 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 charges = account.getCharges(); + for (ShareAccountCharge charge : charges) { + if (charge.isShareAccountActivation()) { + charge.markAsFullyPaid(); + } + } + } + + private Set assembleListOfAccountCharges(final JsonElement element, final String currencyCode) { + final Set 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 assembleListOfAccountChargesforUpdate(final ShareAccount account, final JsonElement element, + final String currencyCode) { + Set 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 validateAndApplyAddtionalShares(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareAccountApiConstants.addtionalSharesParameters); + final List 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 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 validateAndApproveAddtionalShares(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareAccountApiConstants.addtionalSharesParameters); + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount"); + JsonElement element = jsonCommand.parsedJson(); + final ArrayList 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 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 validateAndRejectAddtionalShares(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareAccountApiConstants.addtionalSharesParameters); + JsonElement element = jsonCommand.parsedJson(); + final ArrayList 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 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 validateAndRedeemShares(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareAccountApiConstants.addtionalSharesParameters); + final List 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 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 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 validateAndClose(JsonCommand jsonCommand, ShareAccount account) { + Map actualChanges = new HashMap<>(); + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.closeParameters); + final List 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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/PurchasedSharesReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/PurchasedSharesReadPlatformService.java new file mode 100644 index 000000000..4f3b4010f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/PurchasedSharesReadPlatformService.java @@ -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 retrievePurchasedShares(final Long accountId) ; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/PurchasedSharesReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/PurchasedSharesReadPlatformServiceImpl.java new file mode 100644 index 000000000..5aa7f73f4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/PurchasedSharesReadPlatformServiceImpl.java @@ -0,0 +1,86 @@ +/** + * 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.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData; +import org.joda.time.LocalDate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class PurchasedSharesReadPlatformServiceImpl implements + PurchasedSharesReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public PurchasedSharesReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource) ; + } + @Override + public Collection retrievePurchasedShares( + Long accountId) { + PurchasedSharesDataRowMapper mapper = new PurchasedSharesDataRowMapper() ; + final String sql = "select " + mapper.schema() + " where saps.account_id=?"; + return this.jdbcTemplate.query(sql, mapper, new Object[] { accountId}); + } + + private final static class PurchasedSharesDataRowMapper implements RowMapper { + + private final String schema ; + + public PurchasedSharesDataRowMapper() { + StringBuffer buff = new StringBuffer() + .append("saps.id, saps.account_id, saps.transaction_date, saps.total_shares, saps.unit_price, ") + .append("saps.status_enum, saps.type_enum, saps.amount, saps.charge_amount as chargeamount ") + .append(" from m_share_account_transactions saps "); + schema = buff.toString() ; + } + @Override + public PurchasedSharesData mapRow(ResultSet rs, int rowNum) + throws SQLException { + final Long id = rs.getLong("id") ; + final Long accountId = rs.getLong("account_id") ; + final LocalDate purchasedDate = new LocalDate(rs.getDate("transaction_date")) ; + final Long numberOfShares = JdbcSupport.getLong(rs, "total_shares") ; + final BigDecimal purchasedPrice = rs.getBigDecimal("unit_price") ; + final Integer status = rs.getInt("status_enum") ; + final EnumOptionData statusEnum = SharesEnumerations.purchasedSharesEnum(status) ; + final Integer type = rs.getInt("type_enum") ; + final EnumOptionData typeEnum = SharesEnumerations.purchasedSharesEnum(type) ; + final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amount") ; + final BigDecimal chargeAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "chargeamount") ; + return new PurchasedSharesData(id,accountId, purchasedDate, numberOfShares, purchasedPrice, statusEnum, typeEnum, amount, chargeAmount); + } + + public String schema() { + return this.schema ; + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountChargeReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountChargeReadPlatformService.java new file mode 100644 index 000000000..af23abea2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountChargeReadPlatformService.java @@ -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.ShareAccountChargeData; + +public interface ShareAccountChargeReadPlatformService { + + Collection retrieveAccountCharges(final Long accountId, final String status) ; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountChargeReadPlatformServiceImpl.java new file mode 100644 index 000000000..b4ddd962a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountChargeReadPlatformServiceImpl.java @@ -0,0 +1,148 @@ +/** + * 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.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.portfolio.charge.data.ChargeData; +import org.apache.fineract.portfolio.charge.service.ChargeEnumerations; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountChargeData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class ShareAccountChargeReadPlatformServiceImpl implements + ShareAccountChargeReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public ShareAccountChargeReadPlatformServiceImpl( + final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Collection retrieveAccountCharges( + Long accountId, String status) { + final ShareAccountChargeMapper rm = new ShareAccountChargeMapper(); + final StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("select ").append(rm.schema()) + .append(" where sc.account_id=? "); + if (status.equalsIgnoreCase("active")) { + sqlBuilder.append(" and sc.is_active = 1 "); + } else if (status.equalsIgnoreCase("inactive")) { + sqlBuilder.append(" and sc.is_active = 0 "); + } + sqlBuilder.append(" order by sc.charge_time_enum ASC"); + + return this.jdbcTemplate.query(sqlBuilder.toString(), rm, + new Object[] { accountId }); + } + + private final static class ShareAccountChargeMapper implements + RowMapper { + + private final String schema; + + public ShareAccountChargeMapper() { + StringBuffer buff = new StringBuffer() + .append("sc.id as id, c.id as chargeId, sc.account_id as accountId, c.name as name, ") + .append("sc.amount as amountDue, sc.amount_paid_derived as amountPaid, ") + .append("sc.amount_waived_derived as amountWaived, sc.amount_writtenoff_derived as amountWrittenOff, ") + .append("sc.amount_outstanding_derived as amountOutstanding, sc.calculation_percentage as percentageOf, ") + .append("sc.calculation_on_amount as amountPercentageAppliedTo, sc.charge_time_enum as chargeTime, ") + .append("sc.charge_calculation_enum as chargeCalculation, c.is_active as isActive, ") + .append("c.currency_code as currencyCode, oc.name as currencyName, ") + .append("oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, ") + .append("oc.internationalized_name_code as currencyNameCode from m_charge c ") + .append("join m_organisation_currency oc on c.currency_code = oc.code ") + .append("join m_share_account_charge sc on sc.charge_id = c.id "); + + schema = buff.toString(); + } + + @Override + public ShareAccountChargeData mapRow(ResultSet rs, int rowNum) + throws SQLException { + final Long id = rs.getLong("id"); + final Long chargeId = rs.getLong("chargeId"); + final Long accountId = rs.getLong("accountId"); + final String name = rs.getString("name"); + final BigDecimal amount = rs.getBigDecimal("amountDue"); + final BigDecimal amountPaid = JdbcSupport + .getBigDecimalDefaultToZeroIfNull(rs, "amountPaid"); + final BigDecimal amountWaived = JdbcSupport + .getBigDecimalDefaultToZeroIfNull(rs, "amountWaived"); + final BigDecimal amountWrittenOff = JdbcSupport + .getBigDecimalDefaultToZeroIfNull(rs, "amountWrittenOff"); + final BigDecimal amountOutstanding = rs + .getBigDecimal("amountOutstanding"); + + final BigDecimal percentageOf = JdbcSupport + .getBigDecimalDefaultToZeroIfNull(rs, "percentageOf"); + final BigDecimal amountPercentageAppliedTo = JdbcSupport + .getBigDecimalDefaultToZeroIfNull(rs, + "amountPercentageAppliedTo"); + + 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 currencyDecimalPlaces = JdbcSupport.getInteger(rs, + "currencyDecimalPlaces"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, + "inMultiplesOf"); + + final CurrencyData currency = new CurrencyData(currencyCode, + currencyName, currencyDecimalPlaces, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + final int chargeTime = rs.getInt("chargeTime"); + final EnumOptionData chargeTimeType = ChargeEnumerations + .chargeTimeType(chargeTime); + + final int chargeCalculation = rs.getInt("chargeCalculation"); + final EnumOptionData chargeCalculationType = ChargeEnumerations + .chargeCalculationType(chargeCalculation); + final Boolean isActive = rs.getBoolean("isActive"); + + final Collection chargeOptions = null; + return new ShareAccountChargeData(id, chargeId, accountId, name, + currency, amount, amountPaid, amountWaived, + amountWrittenOff, amountOutstanding, chargeTimeType, + chargeCalculationType, percentageOf, + amountPercentageAppliedTo, chargeOptions, isActive); + } + + public String schema() { + return this.schema; + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountCommandsServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountCommandsServiceImpl.java similarity index 51% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountCommandsServiceImpl.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountCommandsServiceImpl.java index 14a1b37cd..6f56afa12 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/service/ShareAccountCommandsServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountCommandsServiceImpl.java @@ -16,19 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.accounts.service; - -import java.util.Set; +package org.apache.fineract.portfolio.shareaccounts.service; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; 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.ShareAccountTempRepository; -import org.apache.fineract.portfolio.accounts.serialization.ShareAccountDataSerializer; +import org.apache.fineract.portfolio.accounts.service.AccountsCommandsService; +import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -69,68 +64,22 @@ public class ShareAccountCommandsServiceImpl implements AccountsCommandsService } public Object approveShareAccount(Long accountId, JsonCommand jsonCommand) { - //We need to add approval date also - ShareAccount account = ShareAccountTempRepository.getInstance().findOne(accountId); - account.setStatus("Approved"); - Set purchasedShares = account.getPurchasedShares() ; - for(PurchasedShares pur: purchasedShares) { - pur.setStatus("Approved") ; - } - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(account.getId()) // - .build(); + return null ; } public Object rejectShareAccount(Long accountId, JsonCommand jsonCommand) { - ShareAccount account = ShareAccountTempRepository.getInstance().findOne(accountId); - account.setStatus("Rejected"); - //rejection date we need to capture - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(account.getId()) // - .build(); + return null ; } public Object applyAdditionalShares(Long accountId, JsonCommand jsonCommand) { - ShareAccount account = ShareAccountTempRepository.getInstance().findOne(accountId); - Set additionalShares = this.shareAccountDataSerializer.asembleAdditionalShares(jsonCommand.parsedJson()) ; - account.addAddtionalShares(additionalShares) ; - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(account.getId()) // - .build(); + return null ; } public Object approveAdditionalShares(Long accountId, JsonCommand jsonCommand) { - //user might have requested for different dates. - //we need to capture either purchase date or ids [] - ShareAccount account = ShareAccountTempRepository.getInstance().findOne(accountId); - Set purchasedShares = account.getPurchasedShares() ; - for(PurchasedShares pur: purchasedShares) { - if(pur.getStatus().equals("Submitted") && !pur.getStatus().equals("Rejected")) { - pur.setStatus("Approved") ; - } - } - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(account.getId()) // - .build(); + return null ; } public Object rejectAdditionalShares(Long accountId, JsonCommand jsonCommand) { - //user might have requested for different dates. - //we need to capture either purchase date or ids [] - ShareAccount account = ShareAccountTempRepository.getInstance().findOne(accountId); - Set purchasedShares = account.getPurchasedShares() ; - for(PurchasedShares pur: purchasedShares) { - if(pur.getStatus().equals("Submitted")) { - pur.setStatus("Rejected") ; - } - } - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(account.getId()) // - .build(); + return null ; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformService.java new file mode 100644 index 000000000..bacf6db4f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformService.java @@ -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.shareaccounts.service; + +import java.util.List; +import java.util.Map; + +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; + +public interface ShareAccountDividendReadPlatformService { + + List> retriveDividendDetailsForPostDividents(); + + Page retriveAll(Long payoutDetailId, SearchParameters searchParameters); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformServiceImpl.java new file mode 100644 index 000000000..1be1eacfb --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountDividendReadPlatformServiceImpl.java @@ -0,0 +1,140 @@ +/** + * 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.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendStatusType; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendStatusType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class ShareAccountDividendReadPlatformServiceImpl implements ShareAccountDividendReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PaginationHelper paginationHelper = new PaginationHelper<>(); + + @Autowired + public ShareAccountDividendReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public List> retriveDividendDetailsForPostDividents() { + StringBuilder sb = new StringBuilder(); + sb.append("select "); + sb.append(" sadd.id as id, "); + sb.append(" sa.savings_account_id as savingsAccountId "); + sb.append(" from m_share_account_dividend_details sadd"); + sb.append(" inner join m_share_product_dividend_pay_out spdpo on spdpo.id = sadd.dividend_pay_out_id "); + sb.append(" inner join m_share_account sa on sa.id = sadd.account_id "); + sb.append(" where spdpo.status = ? and sadd.status = ?"); + return this.jdbcTemplate.queryForList(sb.toString(), ShareProductDividendStatusType.APPROVED.getValue(), + ShareAccountDividendStatusType.INITIATED.getValue()); + } + + @Override + public Page retriveAll(final Long payoutDetailId, final SearchParameters searchParameters) { + ShareAccountDividendMapper shareAccountDividendMapper = new ShareAccountDividendMapper(); + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(shareAccountDividendMapper.schema()); + sqlBuilder.append(" where sadd.dividend_pay_out_id = ? "); + List params = new ArrayList<>(2); + params.add(payoutDetailId); + if (searchParameters.getAccountNo() != null) { + sqlBuilder.append(" and sa.account_no = ? "); + params.add(searchParameters.getAccountNo()); + } + if (searchParameters.isOrderByRequested()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + if (searchParameters.isOffset()) { + sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + final String sqlCountRows = "SELECT FOUND_ROWS()"; + Object[] paramsObj = params.toArray(); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), paramsObj, + shareAccountDividendMapper); + } + + private static final class ShareAccountDividendMapper implements RowMapper { + + private final String sql; + + public ShareAccountDividendMapper() { + StringBuilder sb = new StringBuilder(); + sb.append(" sadd.id as id, sadd.amount as amount,"); + sb.append(" sadd.status as status, sadd.savings_transaction_id as savingsTransactionId,"); + sb.append(" sa.id as accountId,sa.account_no as accountNumber, "); + sb.append(" mc.id as clientId,mc.display_name as clientName "); + sb.append(" from m_share_account_dividend_details sadd"); + sb.append(" inner join m_share_account sa on sa.id = sadd.account_id "); + sb.append(" inner join m_client mc on mc.id=sa.client_id "); + sql = sb.toString(); + } + + public String schema() { + return this.sql; + } + + @Override + public ShareAccountDividendData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final BigDecimal amount = rs.getBigDecimal("amount"); + final Integer status = JdbcSupport.getInteger(rs, "status"); + final EnumOptionData statusEnum = SharesEnumerations.ShareAccountDividendStatusEnum(status); + final Long savingsTransactionId = JdbcSupport.getLong(rs, "savingsTransactionId"); + + final Long accounId = rs.getLong("accountId"); + final String accountNumber = rs.getString("accountNumber"); + final String clientName = rs.getString("clientName"); + final Long clientId = rs.getLong("clientId"); + final ShareAccountData accountData = ShareAccountData.lookup(accounId, accountNumber, clientId, clientName); + return new ShareAccountDividendData(id, accountData, amount, statusEnum, savingsTransactionId); + } + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformService.java new file mode 100644 index 000000000..06cdf62f5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformService.java @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareaccounts.service; + +import java.util.Collection; +import java.util.Set; + +import org.apache.fineract.portfolio.accounts.service.AccountReadPlatformService; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; +import org.joda.time.LocalDate; + +public interface ShareAccountReadPlatformService extends AccountReadPlatformService { + + @Override + public ShareAccountData retrieveTemplate(final Long clientId, final Long productId); + + @Override + public ShareAccountData retrieveOne(Long id, boolean includeTemplate); + + @Override + public Set getResponseDataParams(); + + Collection retrieveAllShareAccountDataForDividends(Long productId, boolean fetchInActiveAccounts, LocalDate startDate); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java new file mode 100644 index 000000000..3635aaae6 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountReadPlatformServiceImpl.java @@ -0,0 +1,482 @@ +/** + * 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.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.portfolio.accountdetails.data.ShareAccountSummaryData; +import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants; +import org.apache.fineract.portfolio.accounts.data.AccountData; +import org.apache.fineract.portfolio.charge.data.ChargeData; +import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; +import org.apache.fineract.portfolio.client.data.ClientData; +import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; +import org.apache.fineract.portfolio.products.constants.ProductsApiConstants; +import org.apache.fineract.portfolio.products.data.ProductData; +import org.apache.fineract.portfolio.products.service.ProductReadPlatformService; +import org.apache.fineract.portfolio.savings.data.SavingsAccountData; +import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService; +import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountApplicationTimelineData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountStatusEnumData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountChargeData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; +import org.apache.fineract.portfolio.shareaccounts.domain.PurchasedSharesStatusType; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountStatusType; +import org.apache.fineract.portfolio.shareproducts.SharePeriodFrequencyType; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductData; +import org.apache.fineract.portfolio.shareproducts.service.ShareProductDropdownReadPlatformService; +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.context.ApplicationContext; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service(value = "share" + AccountsApiConstants.READPLATFORM_NAME) +public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlatformService { + + private final ApplicationContext applicationContext; + private final ChargeReadPlatformService chargeReadPlatformService; + private final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService; + private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final ClientReadPlatformService clientReadPlatformService; + private final ShareAccountChargeReadPlatformService shareAccountChargeReadPlatformService; + private final PurchasedSharesReadPlatformService purchasedSharesReadPlatformService; + private final JdbcTemplate jdbcTemplate; + private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); + + @Autowired + public ShareAccountReadPlatformServiceImpl(final RoutingDataSource dataSource, final ApplicationContext applicationContext, + final ChargeReadPlatformService chargeReadPlatformService, + final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService, + final SavingsAccountReadPlatformService savingsAccountReadPlatformService, + final ClientReadPlatformService clientReadPlatformService, + final ShareAccountChargeReadPlatformService shareAccountChargeReadPlatformService, + final PurchasedSharesReadPlatformService purchasedSharesReadPlatformService) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.applicationContext = applicationContext; + this.chargeReadPlatformService = chargeReadPlatformService; + this.shareProductDropdownReadPlatformService = shareProductDropdownReadPlatformService; + this.savingsAccountReadPlatformService = savingsAccountReadPlatformService; + this.clientReadPlatformService = clientReadPlatformService; + this.shareAccountChargeReadPlatformService = shareAccountChargeReadPlatformService; + this.purchasedSharesReadPlatformService = purchasedSharesReadPlatformService; + + } + + @Override + public ShareAccountData retrieveTemplate(Long clientId, Long productId) { + ShareAccountData toReturn = null; + String serviceName = "share" + ProductsApiConstants.READPLATFORM_NAME; + ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName); + ClientData client = this.clientReadPlatformService.retrieveOne(clientId); + + if (productId != null) { + final ShareProductData productData = (ShareProductData) service.retrieveOne(productId, false); + final BigDecimal marketPrice = deriveMarketPrice(productData); + final Collection productCharges = this.chargeReadPlatformService.retrieveShareProductCharges(productId); + final Collection charges = convertChargesToShareAccountCharges(productCharges); + final Collection lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + final Collection minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveMinimumActivePeriodFrequencyTypeOptions(); + final Collection clientSavingsAccounts = this.savingsAccountReadPlatformService + .retrieveAllForLookup(clientId); + toReturn = new ShareAccountData(client.id(), client.displayName(), productData.getCurrency(), charges, marketPrice, + minimumActivePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions, clientSavingsAccounts, productData.getNominaltShares()); + } else { + Collection productOptions = service.retrieveAllForLookup(); + final Collection chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); + toReturn = new ShareAccountData(client.id(), client.displayName(), productOptions, chargeOptions); + } + return toReturn; + } + + private BigDecimal deriveMarketPrice(final ShareProductData shareProductData) { + BigDecimal marketValue = shareProductData.getUnitPrice(); + Collection marketDataSet = shareProductData.getMarketPrice(); + if (marketDataSet != null && !marketDataSet.isEmpty()) { + Date currentDate = DateUtils.getDateOfTenant(); + for (ShareProductMarketPriceData data : marketDataSet) { + Date futureDate = data.getStartDate(); + if (currentDate.after(futureDate)) { + marketValue = data.getShareValue(); + } + } + } + return marketValue; + } + + @Override + public ShareAccountData retrieveOne(final Long id, final boolean includeTemplate) { + Collection charges = this.shareAccountChargeReadPlatformService.retrieveAccountCharges(id, "active"); + Collection purchasedShares = this.purchasedSharesReadPlatformService.retrievePurchasedShares(id); + + ShareAccountMapper mapper = new ShareAccountMapper(charges, purchasedShares); + String query = "select " + mapper.schema() + "where sa.id=?"; + ShareAccountData data = this.jdbcTemplate.queryForObject(query, mapper, new Object[] { id }); + String serviceName = "share" + ProductsApiConstants.READPLATFORM_NAME; + ProductReadPlatformService service = (ProductReadPlatformService) this.applicationContext.getBean(serviceName); + final ShareProductData productData = (ShareProductData) service.retrieveOne(data.getProductId(), false); + final BigDecimal currentMarketPrice = deriveMarketPrice(productData); + data.setCurrentMarketPrice(currentMarketPrice); + if(!includeTemplate) { + Collection dividends = this.retrieveAssociatedDividends(id) ; + data.setDividends(dividends); + } + if (includeTemplate) { + final Collection lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + final Collection minimumActivePeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; + final Collection clientSavingsAccounts = this.savingsAccountReadPlatformService.retrieveAllForLookup(data + .getClientId()); + Collection productOptions = service.retrieveAllForLookup(); + final Collection chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); + data = ShareAccountData.template(data, productOptions, chargeOptions, clientSavingsAccounts, lockinPeriodFrequencyTypeOptions, + minimumActivePeriodFrequencyTypeOptions); + } + return data; + } + + private Collection retrieveAssociatedDividends(final Long shareAccountId) { + ShareAccountDividendRowMapper mapper = new ShareAccountDividendRowMapper(); + String query = "select " + mapper.schema() + "where sadd.account_id=?"; + return this.jdbcTemplate.query(query, mapper, new Object[] { shareAccountId }); + } + + @Override + public Collection retrieveAll() { + return null; + } + + @Override + public Set getResponseDataParams() { + return null; + } + + @Override + public Collection retrieveAllShareAccountDataForDividends(final Long id, final boolean fetchInActiveAccounts, + final LocalDate startDate) { + ShareAccountMapperForDividents mapper = new ShareAccountMapperForDividents(); + StringBuilder sb = new StringBuilder("select "); + sb.append(mapper.schema); + sb.append(" where sa.product_id = ? "); + + List params = new ArrayList<>(3); + params.add(id); + params.add(ShareAccountStatusType.ACTIVE.getValue()); + if (fetchInActiveAccounts) { + sb.append(" and (sa.status_enum = ? or (sa.status_enum = ? "); + sb.append(" and sa.closed_date > ?)) "); + params.add(ShareAccountStatusType.CLOSED.getValue()); + params.add(formatter.print(startDate)); + } else { + sb.append(" and sa.status_enum = ? "); + } + sb.append(" and saps.status_enum = ?"); + params.add(PurchasedSharesStatusType.APPROVED.getValue()); + Object[] whereClauseItems = params.toArray(); + return this.jdbcTemplate.query(sb.toString(), whereClauseItems, mapper); + } + + public Collection convertChargesToShareAccountCharges(Collection productCharges) { + final Collection savingsCharges = new ArrayList<>(); + for (final ChargeData chargeData : productCharges) { + final ShareAccountChargeData savingsCharge = chargeData.toShareAccountChargeData(); + savingsCharges.add(savingsCharge); + } + return savingsCharges; + } + + private final static class ShareAccountMapper implements RowMapper { + + private final Collection charges; + private final Collection purchasedShares; + + private final String schema; + + public ShareAccountMapper(final Collection charges, final Collection purchasedShares) { + this.charges = charges; + this.purchasedShares = purchasedShares; + StringBuffer buff = new StringBuffer() + .append("sa.id as id, sa.external_id as externalId, sa.status_enum as statusEnum, ") + .append("sa.savings_account_id, msa.account_no as savingsAccNo, ") + .append("c.id as clientId, c.display_name as clientName, ") + .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.allow_dividends_inactive_clients as allowdividendsforinactiveclients, ") + .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_client c ON c.id = sa.client_id ") + .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 ") + .append("left join m_savings_account msa on sa.savings_account_id = msa.id "); + this.schema = buff.toString(); + } + + @Override + public ShareAccountData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final String externalId = rs.getString("externalId"); + final Long savingsAccountId = JdbcSupport.getLong(rs, "savings_account_id"); + final String savingsAccountNumber = rs.getString("savingsAccNo"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + final Long totalApprovedShares = JdbcSupport.getLong(rs, "approvedShares"); + final Long totalPendingShares = JdbcSupport.getLong(rs, "pendingShares"); + final Boolean allowdividendsforinactiveclients = rs.getBoolean("allowdividendsforinactiveclients"); + + final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum"); + final ShareAccountStatusEnumData status = SharesEnumerations.status(statusEnum); + + 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); + + 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 Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriod"); + EnumOptionData lockinPeriodFrequencyType = null; + + final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodEnum"); + if (lockinPeriodFrequencyTypeValue != null) { + final SharePeriodFrequencyType lockinPeriodType = SharePeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); + lockinPeriodFrequencyType = SharesEnumerations.lockinPeriodFrequencyType(lockinPeriodType); + } + + final Integer minimumActivePeriod = JdbcSupport.getInteger(rs, "minimumactivePeriod"); + EnumOptionData minimumActivePeriodType = null; + final Integer minimumActivePeriodTypeValue = JdbcSupport.getInteger(rs, "minimumactivePeriodEnum"); + if (minimumActivePeriodTypeValue != null) { + final SharePeriodFrequencyType minmumPeriodType = SharePeriodFrequencyType.fromInt(minimumActivePeriodTypeValue); + minimumActivePeriodType = SharesEnumerations.lockinPeriodFrequencyType(minmumPeriodType); + } + + final String shortProductName = null; + final ShareAccountSummaryData summary = new ShareAccountSummaryData(id, accountNo, externalId, productId, productName, + shortProductName, status, currency, totalApprovedShares, totalPendingShares, timeline); + return new ShareAccountData(id, accountNo, externalId, savingsAccountId, savingsAccountNumber, clientId, clientName, + productId, productName, status, timeline, currency, summary, charges, purchasedShares, lockinPeriodFrequency, + lockinPeriodFrequencyType, minimumActivePeriod, minimumActivePeriodType, allowdividendsforinactiveclients); + + } + + public String schema() { + return this.schema; + } + } + + private final static class ShareAccountMapperForDividents implements RowMapper { + + private final String schema; + final PurchasedSharesDataRowMapper purchasedSharesDataRowMapper = new PurchasedSharesDataRowMapper(); + + public ShareAccountMapperForDividents() { + StringBuilder sb = new StringBuilder(); + + sb.append("sa.id as id, sa.status_enum as statusEnum, "); + sb.append("c.id as clientId, c.display_name as clientName, "); + sb.append("sa.account_no as accountNo, "); + sb.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, "); + sb.append(purchasedSharesDataRowMapper.schema()); + sb.append(" from m_share_account sa "); + sb.append(" join m_client c ON c.id = sa.client_id "); + sb.append(" join m_share_account_transactions saps ON saps.account_id = sa.id "); + schema = sb.toString(); + } + + @Override + public ShareAccountData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); + final Long clientId = JdbcSupport.getLong(rs, "clientId"); + final String clientName = rs.getString("clientName"); + final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum"); + final ShareAccountStatusEnumData status = SharesEnumerations.status(statusEnum); + + final CurrencyData currency = null; + final Long totalApprovedShares = null; + final Long totalPendingShares = null; + final String externalId = null; + final Long productId = null; + final String productName = null; + final String shortProductName = null; + final ShareAccountApplicationTimelineData timeline = null; + final Boolean allowdividendsforinactiveclients = null; + + final Collection charges = null; + final Collection purchasedSharesData = new ArrayList<>(); + final Integer lockinPeriod = null; + final EnumOptionData lockPeriodTypeEnum = null; + final Integer minimumActivePeriod = null; + final EnumOptionData minimumActivePeriodTypeEnum = null; + purchasedSharesData.add(this.purchasedSharesDataRowMapper.mapRow(rs, rowNum)); + + while (rs.next()) { + if (id.equals(rs.getLong("id"))) { + purchasedSharesData.add(this.purchasedSharesDataRowMapper.mapRow(rs, rowNum)); + } else { + rs.previous(); + break; + } + } + + final ShareAccountSummaryData summary = new ShareAccountSummaryData(id, accountNo, externalId, productId, productName, + shortProductName, status, currency, totalApprovedShares, totalPendingShares, timeline); + + return new ShareAccountData(id, accountNo, externalId, clientId, clientName, productId, shortProductName, productId, + shortProductName, status, timeline, currency, summary, charges, purchasedSharesData, lockinPeriod, lockPeriodTypeEnum, + minimumActivePeriod, minimumActivePeriodTypeEnum, allowdividendsforinactiveclients); + + } + } + + private final static class PurchasedSharesDataRowMapper implements RowMapper { + + private final String schema; + + public PurchasedSharesDataRowMapper() { + StringBuffer buff = new StringBuffer() + .append("saps.id as purchasedId, saps.account_id as accountId, saps.transaction_date as transactionDate, saps.total_shares as purchasedShares, saps.unit_price as unitPrice, ") + .append("saps.status_enum as purchaseStatus, saps.type_enum as purchaseType, saps.amount as amount, saps.charge_amount as chargeamount "); + schema = buff.toString(); + } + + @Override + public PurchasedSharesData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = rs.getLong("purchasedId"); + final Long accountId = rs.getLong("accountId"); + final LocalDate transactionDate = new LocalDate(rs.getDate("transactionDate")); + final Long numberOfShares = JdbcSupport.getLong(rs, "purchasedShares"); + final BigDecimal purchasedPrice = rs.getBigDecimal("unitPrice"); + final Integer status = rs.getInt("purchaseStatus"); + final EnumOptionData statusEnum = SharesEnumerations.purchasedSharesEnum(status); + final Integer type = rs.getInt("purchaseType"); + final EnumOptionData typeEnum = SharesEnumerations.purchasedSharesEnum(type); + final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount"); + final BigDecimal chargeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "chargeamount"); + return new PurchasedSharesData(id, accountId, transactionDate, numberOfShares, purchasedPrice, statusEnum, typeEnum, amount, + chargeAmount); + } + + public String schema() { + return this.schema; + } + } + + private final static class ShareAccountDividendRowMapper implements RowMapper { + + private final String schema; + + ShareAccountDividendRowMapper() { + StringBuffer buff = new StringBuffer() + .append("spdp.created_date, sadd.id, sadd.amount, sadd.savings_transaction_id, sadd.status ") + .append(" from m_share_account_dividend_details sadd ") + .append("JOIN m_share_product_dividend_pay_out spdp ON spdp.id = sadd.dividend_pay_out_id "); + schema = buff.toString(); + } + + @Override + public ShareAccountDividendData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final Date postedDate = JdbcSupport.getLocalDate(rs, "created_date").toDate(); + final BigDecimal postedAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount"); + final Long savingTransactionId = rs.getLong("savings_transaction_id"); + final Integer status = rs.getInt("status") ; + final EnumOptionData statusEnum = SharesEnumerations.ShareAccountDividendStatusEnum(status); + final ShareAccountData shareAccountData = null; + return new ShareAccountDividendData(id, postedDate, shareAccountData, postedAmount, statusEnum, savingTransactionId); + } + + public String schema() { + return this.schema; + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/ShareChargeData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountSchedularService.java similarity index 73% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/ShareChargeData.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountSchedularService.java index ef302bdfd..31e93099e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/ShareChargeData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountSchedularService.java @@ -16,17 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.accounts.data; +package org.apache.fineract.portfolio.shareaccounts.service; -public class ShareChargeData { - private Long accountId ; +public interface ShareAccountSchedularService { + + void postDividend(final Long dividendDetailId, final Long savingsId); - private Long chargeId ; - - public ShareChargeData(final Long accountId, final Long chargeId) { - this.accountId = accountId ; - this.chargeId = chargeId ; - } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountSchedularServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountSchedularServiceImpl.java new file mode 100644 index 000000000..bb42540a3 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountSchedularServiceImpl.java @@ -0,0 +1,61 @@ +/** + * 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 javax.transaction.Transactional; + +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountDomainService; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendDetails; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendStatusType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ShareAccountSchedularServiceImpl implements ShareAccountSchedularService { + + private final ShareAccountDividendRepository shareAccountDividendRepository; + private final SavingsAccountDomainService savingsAccountDomainService; + private final SavingsAccountAssembler savingsAccountAssembler; + + @Autowired + public ShareAccountSchedularServiceImpl(final ShareAccountDividendRepository shareAccountDividendRepository, + final SavingsAccountDomainService savingsAccountDomainService, final SavingsAccountAssembler savingsAccountAssembler) { + this.shareAccountDividendRepository = shareAccountDividendRepository; + this.savingsAccountDomainService = savingsAccountDomainService; + this.savingsAccountAssembler = savingsAccountAssembler; + } + + @Override + @Transactional + public void postDividend(final Long dividendDetailId, final Long savingsId) { + + ShareAccountDividendDetails shareAccountDividendDetails = this.shareAccountDividendRepository.findOne(dividendDetailId); + final SavingsAccount savingsAccount = this.savingsAccountAssembler.assembleFrom(savingsId); + SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountDomainService.handleDividendPayout(savingsAccount, + DateUtils.getLocalDateOfTenant(), shareAccountDividendDetails.getAmount()); + shareAccountDividendDetails.update(ShareAccountDividendStatusType.POSTED.getValue(), savingsAccountTransaction.getId()); + this.shareAccountDividendRepository.save(shareAccountDividendDetails); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformService.java new file mode 100644 index 000000000..76c53b2b4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformService.java @@ -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.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; + + +public interface ShareAccountWritePlatformService { + + public CommandProcessingResult createShareAccount(JsonCommand jsonCommand) ; + + public CommandProcessingResult updateShareAccount(Long accountId, JsonCommand jsonCommand) ; + + public CommandProcessingResult approveShareAccount(Long accountId, JsonCommand jsonCommand) ; + + public CommandProcessingResult activateShareAccount(Long accountId, JsonCommand jsonCommand) ; + + public CommandProcessingResult rejectShareAccount(Long entityId, JsonCommand jsonCommand); + + public CommandProcessingResult undoApproveShareAccount(Long entityId, JsonCommand jsonCommand); + + public CommandProcessingResult closeShareAccount(Long accountId, JsonCommand jsonCommand) ; + + public CommandProcessingResult applyAddtionalShares(final Long accountId, JsonCommand jsonCommand) ; + + public CommandProcessingResult approveAdditionalShares(Long accountId, JsonCommand jsonCommand) ; + + public CommandProcessingResult rejectAdditionalShares(Long accountId, JsonCommand jsonCommand) ; + + public CommandProcessingResult redeemShares(Long accountId, JsonCommand jsonCommand); +} + diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..daad3be95 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,404 @@ +/** + * 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.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; +import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat; +import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper; +import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType; +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.organisation.monetary.data.CurrencyData; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants; +import org.apache.fineract.portfolio.client.domain.AccountNumberGenerator; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionEnumData; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountChargePaidBy; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountTransaction; +import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer; +import org.joda.time.LocalDate; +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; + + private final ShareAccountRepositoryWrapper shareAccountRepository; + + private final AccountNumberGenerator accountNumberGenerator; + + private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; + + private final JournalEntryWritePlatformService journalEntryWritePlatformService; + + @Autowired + public ShareAccountWritePlatformServiceJpaRepositoryImpl(final ShareAccountDataSerializer accountDataSerializer, + final ShareAccountRepositoryWrapper shareAccountRepository, + final AccountNumberGenerator accountNumberGenerator, + final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, + final JournalEntryWritePlatformService journalEntryWritePlatformService) { + this.accountDataSerializer = accountDataSerializer; + this.shareAccountRepository = shareAccountRepository; + this.accountNumberGenerator = accountNumberGenerator; + this.accountNumberFormatRepository = accountNumberFormatRepository; + this.journalEntryWritePlatformService = journalEntryWritePlatformService; + } + + @Override + public CommandProcessingResult createShareAccount(JsonCommand jsonCommand) { + try { + ShareAccount account = this.accountDataSerializer.validateAndCreate(jsonCommand); + this.shareAccountRepository.save(account); + generateAccountNumber(account); + //this.shareProductRepository.save(account.getShareProduct()); //subscribed shares is increased + journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, + account.getPendingForApprovalSharePurchaseTransactions())); + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(account.getId()) // + .build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + private void generateAccountNumber(final ShareAccount account) { + if (account.isAccountNumberRequiresAutoGeneration()) { + final AccountNumberFormat accountNumberFormat = this.accountNumberFormatRepository.findByAccountType(EntityAccountType.SHARES); + account.updateAccountNumber(this.accountNumberGenerator.generate(account, accountNumberFormat)); + this.shareAccountRepository.save(account); + } + } + + private Map populateJournalEntries(final ShareAccount account, final Set transactions) { + final Map accountingBridgeData = new HashMap<>(); + Boolean cashBasedAccounting = account.getShareProduct().getAccountingType().intValue() == 2 ? Boolean.TRUE : Boolean.FALSE; + accountingBridgeData.put("cashBasedAccountingEnabled", cashBasedAccounting); + accountingBridgeData.put("accrualBasedAccountingEnabled", Boolean.FALSE); + accountingBridgeData.put("shareAccountId", account.getId()); + accountingBridgeData.put("shareProductId", account.getShareProduct().getId()); + accountingBridgeData.put("officeId", account.getOfficeId()); + MonetaryCurrency currency = account.getCurrency(); + final CurrencyData currencyData = new CurrencyData(currency.getCode(), "", currency.getDigitsAfterDecimal(), + currency.getCurrencyInMultiplesOf(), "", ""); + accountingBridgeData.put("currency", currencyData); + final List> newTransactionsMap = new ArrayList<>(); + accountingBridgeData.put("newTransactions", newTransactionsMap); + + for (ShareAccountTransaction transaction : transactions) { + final Map transactionDto = new HashMap<>(); + transactionDto.put("officeId", account.getOfficeId()); + transactionDto.put("id", transaction.getId()); + transactionDto.put("date", new LocalDate(transaction.getPurchasedDate())); + final Integer status = transaction.getTransactionStatus(); + final ShareAccountTransactionEnumData statusEnum = new ShareAccountTransactionEnumData(status.longValue(), null, null); + final Integer type = transaction.getTransactionType(); + final ShareAccountTransactionEnumData typeEnum = new ShareAccountTransactionEnumData(type.longValue(), null, null); + transactionDto.put("status", statusEnum); + transactionDto.put("type", typeEnum); + transactionDto.put("amount", transaction.amount()); + transactionDto.put("chargeAmount", transaction.chargeAmount()); + transactionDto.put("paymentTypeId", null); // FIXME::make it cash + // payment + if (transaction.getChargesPaidBy() != null && !transaction.getChargesPaidBy().isEmpty()) { + final List> chargesPaidData = new ArrayList<>(); + transactionDto.put("chargesPaid", chargesPaidData); + Set chargesPaidBySet = transaction.getChargesPaidBy(); + for (ShareAccountChargePaidBy chargesPaidBy : chargesPaidBySet) { + Map chargesPaidDto = new HashMap<>(); + chargesPaidDto.put("chargeId", chargesPaidBy.getChargeId()); + chargesPaidDto.put("sharesChargeId", chargesPaidBy.getShareChargeId()); + chargesPaidDto.put("amount", chargesPaidBy.getAmount()); + chargesPaidData.add(chargesPaidDto); + } + } + newTransactionsMap.add(transactionDto); + } + return accountingBridgeData; + } + + @Override + public CommandProcessingResult updateShareAccount(Long accountId, JsonCommand jsonCommand) { + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndUpdate(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + } + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult applyAddtionalShares(final Long accountId, JsonCommand jsonCommand) { + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndApplyAddtionalShares(jsonCommand, account); + ShareAccountTransaction transaction = null; + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + transaction = (ShareAccountTransaction) changes.get(ShareAccountApiConstants.additionalshares_paramname); + transaction = account.getShareAccountTransaction(transaction); + if (transaction != null) { + changes.clear(); + changes.put(ShareAccountApiConstants.additionalshares_paramname, transaction.getId()); + Set transactions = new HashSet<>(); + transactions.add(transaction); + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions)); + } + } + + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult approveShareAccount(Long accountId, JsonCommand jsonCommand) { + + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndApprove(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + } + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, + account.getShareAccountTransactions())); + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult rejectShareAccount(Long accountId, JsonCommand jsonCommand) { + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndReject(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + } + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, + account.getShareAccountTransactions())); + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult undoApproveShareAccount(Long accountId, JsonCommand jsonCommand) { + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndUndoApprove(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + } + /* + * this.journalEntryWritePlatformService.createJournalEntriesForShares + * (populateJournalEntries(account, + * account.getShareAccountTransactions())); + */ + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult activateShareAccount(Long accountId, JsonCommand jsonCommand) { + + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndActivate(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + } + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, + account.getChargeTransactions())); + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult approveAdditionalShares(Long accountId, JsonCommand jsonCommand) { + + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndApproveAddtionalShares(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + ArrayList transactionIds = (ArrayList) changes.get(ShareAccountApiConstants.requestedshares_paramname); + if (transactionIds != null) { + Set transactions = new HashSet<>(); + for (Long id : transactionIds) { + ShareAccountTransaction transaction = account.retrievePurchasedShares(id); + transactions.add(transaction); + } + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions)); + } + } + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult rejectAdditionalShares(Long accountId, JsonCommand jsonCommand) { + + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndRejectAddtionalShares(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + ArrayList transactionIds = (ArrayList) changes.get(ShareAccountApiConstants.requestedshares_paramname); + if (transactionIds != null) { + Set transactions = new HashSet<>(); + for (Long id : transactionIds) { + ShareAccountTransaction transaction = account.retrievePurchasedShares(id); + transactions.add(transaction); + } + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions)); + } + } + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult redeemShares(Long accountId, JsonCommand jsonCommand) { + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndRedeemShares(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + ShareAccountTransaction transaction = (ShareAccountTransaction) changes + .get(ShareAccountApiConstants.requestedshares_paramname); + transaction = account.getShareAccountTransaction(transaction); + Set transactions = new HashSet<>(); + transactions.add(transaction); + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions)); + changes.clear(); + changes.put(ShareAccountApiConstants.requestedshares_paramname, transaction.getId()); + + } + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(accountId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult closeShareAccount(Long accountId, JsonCommand jsonCommand) { + try { + ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); + Map changes = this.accountDataSerializer.validateAndClose(jsonCommand, account); + if (!changes.isEmpty()) { + this.shareAccountRepository.save(account); + ShareAccountTransaction transaction = (ShareAccountTransaction) changes + .get(ShareAccountApiConstants.requestedshares_paramname); + transaction = account.getShareAccountTransaction(transaction); + Set transactions = new HashSet<>(); + transactions.add(transaction); + this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions)); + changes.clear(); + changes.put(ShareAccountApiConstants.requestedshares_paramname, transaction.getId()); + + } + 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) { + + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/SharesEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/SharesEnumerations.java new file mode 100644 index 000000000..9be43d9f7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/SharesEnumerations.java @@ -0,0 +1,196 @@ +/** + * 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 org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountStatusEnumData; +import org.apache.fineract.portfolio.shareaccounts.domain.PurchasedSharesStatusType; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendStatusType; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountStatusType; +import org.apache.fineract.portfolio.shareproducts.SharePeriodFrequencyType; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendStatusType; + +public class SharesEnumerations { + + public static ShareAccountStatusEnumData status(final Integer statusEnum) { + return status(ShareAccountStatusType.fromInt(statusEnum)); + } + + public static ShareAccountStatusEnumData status(final ShareAccountStatusType type) { + final boolean submittedAndPendingApproval = type.isSubmittedAndPendingApproval(); + final boolean isApproved = type.isApproved(); + final boolean isRejected = type.isRejected(); + final boolean isActive = type.isActive(); + final boolean isClosed = type.isClosed(); + + ShareAccountStatusEnumData optionData = null; + switch (type) { + case INVALID: + optionData = new ShareAccountStatusEnumData(ShareAccountStatusType.INVALID.getValue().longValue(), + ShareAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected, isActive, + isClosed); + break; + + case SUBMITTED_AND_PENDING_APPROVAL: + optionData = new ShareAccountStatusEnumData(ShareAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue().longValue(), + ShareAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval", + submittedAndPendingApproval, isApproved, isRejected, isActive, isClosed); + break; + case APPROVED: + optionData = new ShareAccountStatusEnumData(ShareAccountStatusType.APPROVED.getValue().longValue(), + ShareAccountStatusType.APPROVED.getCode(), "Approved", submittedAndPendingApproval, isApproved, isRejected, + isActive, isClosed); + break; + + case ACTIVE: + optionData = new ShareAccountStatusEnumData(ShareAccountStatusType.ACTIVE.getValue().longValue(), + ShareAccountStatusType.ACTIVE.getCode(), "Active", submittedAndPendingApproval, isApproved, isRejected, isActive, + isClosed); + break; + case REJECTED: + optionData = new ShareAccountStatusEnumData(ShareAccountStatusType.REJECTED.getValue().longValue(), + ShareAccountStatusType.REJECTED.getCode(), "Rejected", submittedAndPendingApproval, isApproved, isRejected, + isActive, isClosed); + break; + case CLOSED: + optionData = new ShareAccountStatusEnumData(ShareAccountStatusType.CLOSED.getValue().longValue(), + ShareAccountStatusType.CLOSED.getCode(), "Closed", submittedAndPendingApproval, isApproved, isRejected, isActive, + isClosed); + break; + } + + return optionData; + } + + public static EnumOptionData purchasedSharesEnum(PurchasedSharesStatusType type) { + EnumOptionData data = new EnumOptionData(PurchasedSharesStatusType.INVALID.getValue().longValue(), + PurchasedSharesStatusType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case APPLIED: + data = new EnumOptionData(PurchasedSharesStatusType.APPLIED.getValue().longValue(), + PurchasedSharesStatusType.APPLIED.getCode(), "Pending Approval"); + break; + case APPROVED: + data = new EnumOptionData(PurchasedSharesStatusType.APPROVED.getValue().longValue(), + PurchasedSharesStatusType.APPROVED.getCode(), "Approved"); + break; + case REJECTED: + data = new EnumOptionData(PurchasedSharesStatusType.REJECTED.getValue().longValue(), + PurchasedSharesStatusType.REJECTED.getCode(), "Rejected"); + break; + case PURCHASED: + data = new EnumOptionData(PurchasedSharesStatusType.PURCHASED.getValue().longValue(), + PurchasedSharesStatusType.PURCHASED.getCode(), "Purchase"); + break; + case REDEEMED: + data = new EnumOptionData(PurchasedSharesStatusType.REDEEMED.getValue().longValue(), + PurchasedSharesStatusType.REDEEMED.getCode(), "Redeem"); + break; + case CHARGE_PAYMENT: + data = new EnumOptionData(PurchasedSharesStatusType.CHARGE_PAYMENT.getValue().longValue(), + PurchasedSharesStatusType.CHARGE_PAYMENT.getCode(), "Charge Payment"); + break; + + } + return data; + } + + public static EnumOptionData purchasedSharesEnum(final Integer enumValue) { + return purchasedSharesEnum(PurchasedSharesStatusType.fromInt(enumValue)); + } + + public static EnumOptionData lockinPeriodFrequencyType(final int id) { + return lockinPeriodFrequencyType(SharePeriodFrequencyType.fromInt(id)); + } + + public static EnumOptionData lockinPeriodFrequencyType(final SharePeriodFrequencyType type) { + final String codePrefix = "savings.lockin."; + EnumOptionData optionData = new EnumOptionData(SharePeriodFrequencyType.INVALID.getValue().longValue(), + SharePeriodFrequencyType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case DAYS: + optionData = new EnumOptionData(SharePeriodFrequencyType.DAYS.getValue().longValue(), codePrefix + + SharePeriodFrequencyType.DAYS.getCode(), "Days"); + break; + case WEEKS: + optionData = new EnumOptionData(SharePeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix + + SharePeriodFrequencyType.WEEKS.getCode(), "Weeks"); + break; + case MONTHS: + optionData = new EnumOptionData(SharePeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix + + SharePeriodFrequencyType.MONTHS.getCode(), "Months"); + break; + case YEARS: + optionData = new EnumOptionData(SharePeriodFrequencyType.YEARS.getValue().longValue(), codePrefix + + SharePeriodFrequencyType.YEARS.getCode(), "Years"); + break; + } + return optionData; + } + + public static EnumOptionData ShareAccountDividendStatusEnum(ShareAccountDividendStatusType type) { + EnumOptionData data = new EnumOptionData(ShareAccountDividendStatusType.INVALID.getValue().longValue(), + ShareAccountDividendStatusType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case POSTED: + data = new EnumOptionData(ShareAccountDividendStatusType.POSTED.getValue().longValue(), + ShareAccountDividendStatusType.POSTED.getCode(), "Dividend Posted"); + break; + case INITIATED: + data = new EnumOptionData(ShareAccountDividendStatusType.INITIATED.getValue().longValue(), + ShareAccountDividendStatusType.INITIATED.getCode(), "Dividend Initiated"); + break; + + } + return data; + } + + public static EnumOptionData ShareAccountDividendStatusEnum(final Integer enumValue) { + return ShareAccountDividendStatusEnum(ShareAccountDividendStatusType.fromInt(enumValue)); + } + + public static EnumOptionData ShareProductDividendStatusEnum(ShareProductDividendStatusType type) { + EnumOptionData data = new EnumOptionData(ShareAccountDividendStatusType.INVALID.getValue().longValue(), + ShareAccountDividendStatusType.INVALID.getCode(), "Invalid"); + switch (type) { + case INVALID: + break; + case APPROVED: + data = new EnumOptionData(ShareProductDividendStatusType.APPROVED.getValue().longValue(), + ShareProductDividendStatusType.APPROVED.getCode(), "Dividend Approved"); + break; + case INITIATED: + data = new EnumOptionData(ShareProductDividendStatusType.INITIATED.getValue().longValue(), + ShareProductDividendStatusType.INITIATED.getCode(), "Dividend Initiated"); + break; + + } + return data; + } + + public static EnumOptionData ShareProductDividendStatusEnum(final Integer enumValue) { + return ShareProductDividendStatusEnum(ShareProductDividendStatusType.fromInt(enumValue)); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/SharePeriodFrequencyType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/SharePeriodFrequencyType.java new file mode 100644 index 000000000..d04b5f060 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/SharePeriodFrequencyType.java @@ -0,0 +1,85 @@ +/** + * 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.shareproducts; + +import java.util.ArrayList; +import java.util.List; + +/** + * An enumeration of supported calendar periods used in savings. + */ +public enum SharePeriodFrequencyType { + DAYS(0, "sharePeriodFrequencyType.days"), // + WEEKS(1, "sharePeriodFrequencyType.weeks"), // + MONTHS(2, "sharePeriodFrequencyType.months"), // + YEARS(3, "sharePeriodFrequencyType.years"), // + INVALID(4, "sharePeriodFrequencyType.invalid"); + + private final Integer value; + private final String code; + + private SharePeriodFrequencyType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public static SharePeriodFrequencyType fromInt(final Integer type) { + SharePeriodFrequencyType repaymentFrequencyType = SharePeriodFrequencyType.INVALID; + if (type != null) { + switch (type) { + case 0: + repaymentFrequencyType = SharePeriodFrequencyType.DAYS; + break; + case 1: + repaymentFrequencyType = SharePeriodFrequencyType.WEEKS; + break; + case 2: + repaymentFrequencyType = SharePeriodFrequencyType.MONTHS; + break; + case 3: + repaymentFrequencyType = SharePeriodFrequencyType.YEARS; + break; + } + } + return repaymentFrequencyType; + } + + public boolean isInvalid() { + return this.value.equals(SharePeriodFrequencyType.INVALID.value); + } + + public static Object[] integerValues() { + final List values = new ArrayList<>(); + for (final SharePeriodFrequencyType enumType : values()) { + if (!enumType.isInvalid()) { + values.add(enumType.getValue()); + } + } + + return values.toArray(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/api/ShareDividendApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/api/ShareDividendApiResource.java new file mode 100644 index 000000000..41eba0e3a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/api/ShareDividendApiResource.java @@ -0,0 +1,153 @@ +/** + * 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.shareproducts.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import org.apache.commons.lang.StringUtils; +import org.apache.fineract.commands.domain.CommandWrapper; +import org.apache.fineract.commands.service.CommandWrapperBuilder; +import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; +import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; +import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformService; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductDividendPayOutData; +import org.apache.fineract.portfolio.shareproducts.service.ShareProductDividendReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/shareproduct/{productId}/dividend") +@Component +@Scope("singleton") +public class ShareDividendApiResource { + + private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final DefaultToApiJsonSerializer toApiAccountDetailJsonSerializer; + private final PlatformSecurityContext platformSecurityContext; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService; + private final ShareProductDividendReadPlatformService shareProductDividendReadPlatformService; + private final String resourceNameForPermissions = "DIVIDEND_SHAREPRODUCT"; + + @Autowired + public ShareDividendApiResource(final DefaultToApiJsonSerializer toApiJsonSerializer, + final PlatformSecurityContext platformSecurityContext, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final DefaultToApiJsonSerializer toApiDividendsJsonSerializer, + final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService, + final ShareProductDividendReadPlatformService shareProductDividendReadPlatformService) { + this.toApiJsonSerializer = toApiJsonSerializer; + this.platformSecurityContext = platformSecurityContext; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.toApiAccountDetailJsonSerializer = toApiDividendsJsonSerializer; + this.shareAccountDividendReadPlatformService = shareAccountDividendReadPlatformService; + this.shareProductDividendReadPlatformService = shareProductDividendReadPlatformService; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAll(@PathParam("productId") final Long productId, @QueryParam("offset") final Integer offset, + @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, + @QueryParam("sortOrder") final String sortOrder, @QueryParam("status") final Integer status) { + + this.platformSecurityContext.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); + final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder); + Page dividendPayoutDetails = this.shareProductDividendReadPlatformService.retriveAll(productId, + status, searchParameters); + return this.toApiJsonSerializer.serialize(dividendPayoutDetails); + } + + @GET + @Path("{dividendId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveDividendDetails(@PathParam("dividendId") final Long dividendId, @QueryParam("offset") final Integer offset, + @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, + @QueryParam("sortOrder") final String sortOrder, @QueryParam("accountNo") final String accountNo) { + + this.platformSecurityContext.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); + final SearchParameters searchParameters = SearchParameters.forPaginationAndAccountNumberSearch(offset, limit, orderBy, sortOrder, + accountNo); + Page dividendDetails = this.shareAccountDividendReadPlatformService.retriveAll(dividendId, + searchParameters); + return this.toApiAccountDetailJsonSerializer.serialize(dividendDetails); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String createDividendDetail(@PathParam("productId") final Long productId, final String apiRequestBodyAsJson) { + this.platformSecurityContext.authenticatedUser(); + CommandWrapper commandWrapper = new CommandWrapperBuilder().createShareProductDividendPayoutCommand(productId) + .withJson(apiRequestBodyAsJson).build(); + final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); + return this.toApiJsonSerializer.serialize(commandProcessingResult); + } + + @PUT + @Path("{dividendId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String updateDividendDetail(@PathParam("productId") final Long productId, @PathParam("dividendId") final Long dividendId, + @QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) { + CommandWrapper commandWrapper = null; + this.platformSecurityContext.authenticatedUser(); + if (is(commandParam, "approve")) { + commandWrapper = new CommandWrapperBuilder().approveShareProductDividendPayoutCommand(productId, dividendId) + .withJson(apiRequestBodyAsJson).build(); + } else { + throw new UnrecognizedQueryParamException("command", commandParam); + } + final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); + return this.toApiJsonSerializer.serialize(commandProcessingResult); + } + + @DELETE + @Path("{dividendId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteDividendDetail(@PathParam("productId") final Long productId, @PathParam("dividendId") final Long dividendId) { + this.platformSecurityContext.authenticatedUser(); + final CommandWrapper commandWrapper = new CommandWrapperBuilder().deleteShareProductDividendPayoutCommand(productId, dividendId) + .build(); + final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); + return this.toApiJsonSerializer.serialize(commandProcessingResult); + } + + private boolean is(final String commandParam, final String commandValue) { + return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/constants/ShareProductApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/constants/ShareProductApiConstants.java new file mode 100644 index 000000000..4a0725e6a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/constants/ShareProductApiConstants.java @@ -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.shareproducts.constants; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.fineract.accounting.common.AccountingConstants; + +public interface ShareProductApiConstants { + + // Command Strings + public final String PREIEW_DIVIDENDS_COMMAND_STRING = "previewdividends"; + public final String POST_DIVIDENdS_COMMAND_STRING = "postdividends"; + public final String SHARE_PRODUCT_RESOURCE_NAME = "shareproduct"; + + String locale_paramname = "locale"; + String dateFormatParamName = "dateFormat"; + String id_paramname = "id"; + String name_paramname = "name"; + String shortname_paramname = "shortName"; + String description_paramname = "description"; + String externalid_paramname = "externalId"; + String totalshares_paramname = "totalShares"; + String currency_paramname = "currencyCode"; + String digitsafterdecimal_paramname = "digitsAfterDecimal"; + String inmultiplesof_paramname = "inMultiplesOf"; + String totalsharesissued_paramname = "sharesIssued"; + String unitprice_paramname = "unitPrice"; + String sharecapital_paramname = "shareCapital"; + String minimumshares_paramname = "minimumShares"; + String nominaltshares_paramname = "nominalShares"; + String maximumshares_paramname = "maximumShares"; + String marketprice_paramname = "marketPricePeriods"; + String charges_paramname = "chargesSelected"; + String allowdividendcalculationforinactiveclients_paramname = "allowDividendCalculationForInactiveClients"; + String lockperiod_paramname = "lockinPeriodFrequency"; + String lockinperiodfrequencytype_paramname = "lockinPeriodFrequencyType"; + + String minimumactiveperiodfordividends_paramname = "minimumActivePeriodForDividends"; + String minimumactiveperiodfrequencytype_paramname = "minimumactiveperiodFrequencyType"; + + String startdate_paramname = "fromDate"; + String sharevalue_paramname = "shareValue"; + String dividendPeriodStartDateParamName = "dividendPeriodStartDate"; + String dividendPeriodEndDateParamName = "dividendPeriodEndDate"; + String dividendAmountParamName = "dividendAmount"; + + String accountingRuleParamName = "accountingRule"; + + Set supportedParametersForCreate = new HashSet<>(Arrays.asList(locale_paramname, name_paramname, shortname_paramname, + shortname_paramname, description_paramname, externalid_paramname, totalshares_paramname, currency_paramname, + digitsafterdecimal_paramname, digitsafterdecimal_paramname, inmultiplesof_paramname, totalsharesissued_paramname, + unitprice_paramname, minimumshares_paramname, nominaltshares_paramname, maximumshares_paramname, marketprice_paramname, + charges_paramname, allowdividendcalculationforinactiveclients_paramname, lockperiod_paramname, + lockinperiodfrequencytype_paramname, minimumactiveperiodfordividends_paramname, minimumactiveperiodfrequencytype_paramname, + sharecapital_paramname, accountingRuleParamName, + AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), + AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_EQUITY.getValue(), + AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_REFERENCE.getValue(), + AccountingConstants.SHARES_PRODUCT_ACCOUNTING_PARAMS.SHARES_SUSPENSE.getValue())); + + Set supportedParametersForDivident = new HashSet<>(Arrays.asList(locale_paramname, dateFormatParamName, + dividendPeriodStartDateParamName, dividendPeriodEndDateParamName, dividendAmountParamName)); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductData.java new file mode 100644 index 000000000..0deb6e289 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductData.java @@ -0,0 +1,339 @@ +/** + * 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.shareproducts.data; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.fineract.accounting.common.AccountingRuleType; +import org.apache.fineract.accounting.glaccount.data.GLAccountData; +import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; +import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +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.products.data.ProductData; + +public class ShareProductData implements ProductData { + + private final Long id; + private final String name; + private final String shortName; + private final String description; + private final String externalId; + private final CurrencyData currency; + private final Long totalShares; + private final Long totalSharesIssued; + private final BigDecimal unitPrice; + private final BigDecimal shareCapital; + private final Long minimumShares; + private final Long nominalShares; + private final Long maximumShares; + private final Collection marketPrice; + private final Collection charges; + private final Boolean allowDividendCalculationForInactiveClients; + + private final Integer lockinPeriod; + private final EnumOptionData lockPeriodTypeEnum; + + private final Integer minimumActivePeriod; + private final EnumOptionData minimumActivePeriodForDividendsTypeEnum; + + // accounting + private final EnumOptionData accountingRule; + private final Map accountingMappings; + private final Collection paymentChannelToFundSourceMappings; + private final Collection feeToGLAccountMappings; + + private final Collection currencyOptions; + private final Collection chargeOptions; + final Collection minimumActivePeriodFrequencyTypeOptions; + final Collection lockinPeriodFrequencyTypeOptions; + private final Map> accountingMappingOptions; + + private ShareProductData(final Long id, final String name, final String shortName, final String description, final String externalId, + final CurrencyData currency, final Long totalShares, final Long totalSharesIssued, final BigDecimal unitPrice, + final BigDecimal shareCapital, final Long minimumShares, final Long nominaltShares, final Long maximumShares, + Collection marketPrice, final Collection charges, + final Boolean allowDividendCalculationForInactiveClients, final Integer lockinPeriod, final EnumOptionData lockPeriodEnum, + final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodForDividendsTypeEnum, EnumOptionData accountingRule, + Map accountingMappings, Collection paymentChannelToFundSourceMappings, + Collection feeToGLAccountMappings, final Collection currencyOptions, + final Collection chargeOptions, final Collection minimumActivePeriodFrequencyTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, Map> accountingMappingOptions) { + this.id = id; + this.name = name; + this.shortName = shortName; + this.description = description; + this.externalId = externalId; + this.currency = currency; + this.totalShares = totalShares; + this.totalSharesIssued = totalSharesIssued; + this.unitPrice = unitPrice; + this.shareCapital = shareCapital; + this.minimumShares = minimumShares; + this.nominalShares = nominaltShares; + this.maximumShares = maximumShares; + this.marketPrice = marketPrice; + this.charges = charges; + this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients; + this.lockinPeriod = lockinPeriod; + this.lockPeriodTypeEnum = lockPeriodEnum; + this.minimumActivePeriod = minimumActivePeriod; + this.minimumActivePeriodForDividendsTypeEnum = minimumActivePeriodForDividendsTypeEnum; + this.accountingMappings = accountingMappings; + this.accountingRule = accountingRule; + this.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings; + this.feeToGLAccountMappings = feeToGLAccountMappings; + this.currencyOptions = currencyOptions; + this.chargeOptions = chargeOptions; + this.minimumActivePeriodFrequencyTypeOptions = minimumActivePeriodFrequencyTypeOptions; + this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; + this.accountingMappingOptions = accountingMappingOptions; + } + + public static ShareProductData data(final Long id, final String name, final String shortName, final String description, + final String externalId, final CurrencyData currency, final Long totalShares, final Long totalSharesIssued, + final BigDecimal unitPrice, final BigDecimal shareCapital, final Long minimumShares, final Long nominaltShares, + final Long maximumShares, Collection marketPrice, final Collection charges, + + final Boolean allowDividendCalculationForInactiveClients, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum, + final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodForDividendsEnum, EnumOptionData accountingRule) { + + final Map accountingMappings = null; + final Collection paymentChannelToFundSourceMappings = null; + final Map> accountingMappingOptions = null; + final Collection feeToGLAccountMappings = null; + + final Collection currencyOptions = null; + final Collection chargeOptions = null; + final Collection minimumActivePeriodFrequencyTypeOptions = null; + final Collection lockinPeriodFrequencyTypeOptions = null; + return new ShareProductData(id, name, shortName, description, externalId, currency, totalShares, totalSharesIssued, unitPrice, + shareCapital, minimumShares, nominaltShares, maximumShares, marketPrice, charges, + allowDividendCalculationForInactiveClients, lockinPeriod, lockPeriodTypeEnum, minimumActivePeriod, + minimumActivePeriodForDividendsEnum, accountingRule, accountingMappings, paymentChannelToFundSourceMappings, + feeToGLAccountMappings, currencyOptions, chargeOptions, minimumActivePeriodFrequencyTypeOptions, + lockinPeriodFrequencyTypeOptions, accountingMappingOptions); + + } + + private ShareProductData(final Collection currencyOptions, final Collection chargeOptions, + final Collection minimumActivePeriodFrequencyTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, + final Map> accountingMappingOptions) { + this.id = null; + this.name = null; + this.shortName = null; + this.description = null; + this.externalId = null; + this.currency = null; + this.totalShares = null; + this.totalSharesIssued = null; + this.unitPrice = null; + this.shareCapital = null; + this.minimumShares = null; + this.nominalShares = null; + this.maximumShares = null; + this.marketPrice = null; + this.charges = null; + this.allowDividendCalculationForInactiveClients = null; + this.lockPeriodTypeEnum = null; + this.minimumActivePeriodForDividendsTypeEnum = null; + this.accountingRule = null; + this.accountingMappings = null; + this.paymentChannelToFundSourceMappings = null; + this.feeToGLAccountMappings = null; + this.currencyOptions = currencyOptions; + this.chargeOptions = chargeOptions; + this.minimumActivePeriodFrequencyTypeOptions = minimumActivePeriodFrequencyTypeOptions; + this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; + this.lockinPeriod = null; + this.minimumActivePeriod = null; + this.accountingMappingOptions = accountingMappingOptions; + } + + public static ShareProductData template(final Collection currencyOptions, final Collection chargeOptions, + final Collection minimumActivePeriodFrequencyTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, Map> accountingMappingOptions) { + return new ShareProductData(currencyOptions, chargeOptions, minimumActivePeriodFrequencyTypeOptions, + lockinPeriodFrequencyTypeOptions, accountingMappingOptions); + } + + public static ShareProductData template(final ShareProductData data, final Collection currencyOptions, + final Collection chargeOptions, final Collection minimumActivePeriodFrequencyTypeOptions, + final Collection lockinPeriodFrequencyTypeOptions, + final Map> accountingMappingOptions) { + + return new ShareProductData(data.id, data.name, data.shortName, data.description, data.externalId, data.currency, + data.totalShares, data.totalSharesIssued, data.unitPrice, data.shareCapital, data.minimumShares, data.nominalShares, + data.maximumShares, data.marketPrice, data.charges, data.allowDividendCalculationForInactiveClients, data.lockinPeriod, + data.lockPeriodTypeEnum, data.minimumActivePeriod, data.minimumActivePeriodForDividendsTypeEnum, data.accountingRule, + data.accountingMappings, data.paymentChannelToFundSourceMappings, data.feeToGLAccountMappings, currencyOptions, + chargeOptions, minimumActivePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions, accountingMappingOptions); + } + + public static ShareProductData withAccountingDetails(final ShareProductData data, final Map accountingMappings, + final Collection paymentChannelToFundSourceMappings, + final Collection feeToGLAccountMappings) { + return new ShareProductData(data.id, data.name, data.shortName, data.description, data.externalId, data.currency, + data.totalShares, data.totalSharesIssued, data.unitPrice, data.shareCapital, data.minimumShares, data.nominalShares, + data.maximumShares, data.marketPrice, data.charges, data.allowDividendCalculationForInactiveClients, data.lockinPeriod, + data.lockPeriodTypeEnum, data.minimumActivePeriod, data.minimumActivePeriodForDividendsTypeEnum, data.accountingRule, + accountingMappings, paymentChannelToFundSourceMappings, feeToGLAccountMappings, data.currencyOptions, data.chargeOptions, + data.minimumActivePeriodFrequencyTypeOptions, data.lockinPeriodFrequencyTypeOptions, data.accountingMappingOptions); + } + + private ShareProductData(final Long id, final String name, final String shortName, final Long totalShares) { + this.id = id; + this.name = name; + this.shortName = shortName; + this.description = null; + this.externalId = null; + this.currency = null; + this.totalShares = totalShares; + this.totalSharesIssued = null; + this.unitPrice = null; + this.shareCapital = null; + this.minimumShares = null; + this.nominalShares = null; + this.maximumShares = null; + this.marketPrice = null; + this.charges = null; + this.allowDividendCalculationForInactiveClients = null; + this.lockPeriodTypeEnum = null; + this.minimumActivePeriodForDividendsTypeEnum = null; + this.currencyOptions = null; + this.chargeOptions = null; + this.minimumActivePeriodFrequencyTypeOptions = null; + this.lockinPeriodFrequencyTypeOptions = null; + this.lockinPeriod = null; + this.minimumActivePeriod = null; + this.accountingRule = null; + this.accountingMappings = null; + this.paymentChannelToFundSourceMappings = null; + this.feeToGLAccountMappings = null; + this.accountingMappingOptions = null; + } + + public static ShareProductData generic(final Long id, final String name, final String shortName, final Long totalShares) { + return new ShareProductData(id, name, shortName, totalShares); + } + + public static ShareProductData lookup(final Long id, final String name) { + final String shortName = null; + final Long totalShares = null; + return new ShareProductData(id, name, shortName, totalShares); + + } + + public Long getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public String getShortName() { + return this.shortName; + } + + public String getDescription() { + return this.description; + } + + public String getExternalId() { + return this.externalId; + } + + public CurrencyData getCurrency() { + return this.currency; + } + + public Long getTotalShares() { + return this.totalShares; + } + + public Long getTotalSharesIssued() { + return this.totalSharesIssued; + } + + public BigDecimal getUnitPrice() { + return this.unitPrice; + } + + public BigDecimal getShareCapital() { + return this.shareCapital; + } + + public Long getMinimumShares() { + return this.minimumShares; + } + + public Long getNominaltShares() { + return this.nominalShares; + } + + public Long getMaximumShares() { + return this.maximumShares; + } + + public Collection getMarketPrice() { + return this.marketPrice; + } + + public Boolean getAllowDividendCalculationForInactiveClients() { + return this.allowDividendCalculationForInactiveClients; + } + + public EnumOptionData getLockPeriod() { + return this.lockPeriodTypeEnum; + } + + public EnumOptionData getMinimumActivePeriodForDividends() { + return this.minimumActivePeriodForDividendsTypeEnum; + } + + public Collection getCurrencyOptions() { + return currencyOptions; + } + + public Collection getChargeOptions() { + return chargeOptions; + } + + public Integer getMinimumActivePeriod() { + return this.minimumActivePeriod; + } + + public boolean hasAccountingEnabled() { + return this.accountingRule.getId() > AccountingRuleType.NONE.getValue(); + } + + public int accountingRuleTypeId() { + return this.accountingRule.getId().intValue(); + } + + public EnumOptionData getAccountingRule() { + return this.accountingRule; + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductDividendPayOutData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductDividendPayOutData.java new file mode 100644 index 000000000..bf2bdc552 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductDividendPayOutData.java @@ -0,0 +1,56 @@ +/** + * 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.shareproducts.data; + +import java.math.BigDecimal; +import java.util.Collection; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; +import org.joda.time.LocalDate; + +public class ShareProductDividendPayOutData { + + @SuppressWarnings("unused") + private final Long id; + @SuppressWarnings("unused") + private final ShareProductData productData; + @SuppressWarnings("unused") + private final BigDecimal amount; + @SuppressWarnings("unused") + private final EnumOptionData status; + @SuppressWarnings("unused") + private final LocalDate dividendPeriodStartDate; + @SuppressWarnings("unused") + private final LocalDate dividendPeriodEndDate; + @SuppressWarnings("unused") + private final Collection accountDividendsData; + + public ShareProductDividendPayOutData(final Long id, final ShareProductData productData, final BigDecimal amount, + LocalDate dividendStartDate, final LocalDate dividendEndDate, final Collection accountDividendsData, + final EnumOptionData status) { + this.id = id; + this.productData = productData; + this.amount = amount; + this.dividendPeriodEndDate = dividendEndDate; + this.accountDividendsData = accountDividendsData; + this.dividendPeriodStartDate = dividendStartDate; + this.status = status; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ShareMarketPriceData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductMarketPriceData.java similarity index 72% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ShareMarketPriceData.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductMarketPriceData.java index 90fb3fdda..97b3e1af4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ShareMarketPriceData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/data/ShareProductMarketPriceData.java @@ -16,24 +16,31 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.data; +package org.apache.fineract.portfolio.shareproducts.data; import java.math.BigDecimal; import java.util.Date; -public class ShareMarketPriceData { +public class ShareProductMarketPriceData { - private final Date startDate; + private final Long id ; + + private final Date fromDate; private final BigDecimal shareValue; - public ShareMarketPriceData(final Date startDate, final BigDecimal shareValue) { - this.startDate = startDate ; + public ShareProductMarketPriceData(final Long id, final Date fromDate, final BigDecimal shareValue) { + this.id = id ; + this.fromDate = fromDate ; this.shareValue = shareValue ; } + public Long getId() { + return this.id ; + } + public Date getStartDate() { - return this.startDate; + return this.fromDate; } public BigDecimal getShareValue() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProduct.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProduct.java similarity index 56% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProduct.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProduct.java index 2c50648eb..4fee7efa0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProduct.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProduct.java @@ -16,13 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.domain; +package org.apache.fineract.portfolio.shareproducts.domain; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.Date; -import java.util.HashSet; -import java.util.List; import java.util.Set; import javax.persistence.CascadeType; @@ -35,30 +32,25 @@ import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; -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.accounting.common.AccountingRuleType; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom; -import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; -import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; -import org.apache.fineract.portfolio.shares.data.ShareMarketPriceData; -import org.apache.fineract.portfolio.shares.data.ShareProductData; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData; 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_shareproducts") +@Table(name = "m_share_product") public class ShareProduct extends AbstractAuditableCustom { @Column(name = "name", nullable = false, unique = true) @@ -87,27 +79,22 @@ public class ShareProduct extends AbstractAuditableCustom { @Column(name = "total_shares", nullable = false) private Long totalShares; - @Column(name = "total_shares_issued", nullable = false) + @Column(name = "issued_shares", nullable = false) private Long totalSharesIssued; + @Column(name = "totalsubscribed_shares", nullable = false) + private Long totalSubscribedShares; + @Column(name = "unit_price", nullable = false) private BigDecimal unitPrice; - @Column(name = "share_capital", nullable = false) + @Column(name = "capital_amount", nullable = false) private BigDecimal shareCapital; - @ManyToOne - @JoinColumn(name = "suspence_account", nullable = false) - private GLAccount suspenseAccount; - - @ManyToOne - @JoinColumn(name = "equity_account", nullable = false) - private GLAccount equityAccount; - @Column(name = "minimum_client_shares") private Long minimumShares; - @Column(name = "default_client_shares", nullable = false) + @Column(name = "nominal_client_shares", nullable = false) private Long nominalShares; @Column(name = "maximum_client_shares") @@ -115,38 +102,44 @@ public class ShareProduct extends AbstractAuditableCustom { @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "product", orphanRemoval = true) - Set marketPrice = new HashSet<>(); + Set marketPrice; @ManyToMany(fetch = FetchType.EAGER) - @JoinTable(name = "m_product_charges", joinColumns = @JoinColumn(name = "product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) + @JoinTable(name = "m_share_product_charge", joinColumns = @JoinColumn(name = "product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) private Set charges; @Column(name = "allow_dividends_inactive_clients") private Boolean allowDividendCalculationForInactiveClients; - @Enumerated(EnumType.ORDINAL) - @Column(name = "lock_period", nullable = true) - private PeriodFrequencyType lockPeriod; + @Column(name = "lockin_period_frequency") + private Integer lockinPeriod; @Enumerated(EnumType.ORDINAL) - @Column(name = "dividend_active_period", nullable = true) - private PeriodFrequencyType minimumActivePeriodForDividends; + @Column(name = "lockin_period_frequency_enum", nullable = true) + private PeriodFrequencyType lockPeriodType; + + @Column(name = "minimum_active_period_frequency") + private Integer minimumActivePeriod; + + @Enumerated(EnumType.ORDINAL) + @Column(name = "minimum_active_period_frequency_enum", nullable = true) + private PeriodFrequencyType minimumActivePeriodType; + + @Column(name = "accounting_type", nullable = false) + protected Integer accountingRule; protected ShareProduct() { - - } - // FIXME Remove this method - public void setTempId(Long id) { - super.setId(id); + } public ShareProduct(final String name, final String shortName, final String description, final String externalId, final MonetaryCurrency currency, final Long totalShares, final Long totalSharesIssued, final BigDecimal unitPrice, - final BigDecimal shareCapital, final GLAccount suspenseAccount, final GLAccount equityAccount, final Long minimumShares, - final Long nominalShares, final Long maximumShares, Set marketPrice, Set charges, - final Boolean allowDividendCalculationForInactiveClients, final PeriodFrequencyType lockPeriod, - final PeriodFrequencyType minimumActivePeriodForDividends, - AppUser createdBy, DateTime createdDate, AppUser lastModifiedBy, DateTime lastModifiedDate) { + final BigDecimal shareCapital, final Long minimumShares, final Long nominalShares, final Long maximumShares, + Set marketPrice, Set charges, final Boolean allowDividendCalculationForInactiveClients, + final Integer lockinPeriod, final PeriodFrequencyType lockPeriodType, final Integer minimumActivePeriod, + final PeriodFrequencyType minimumActivePeriodForDividendsType, AppUser createdBy, DateTime createdDate, AppUser lastModifiedBy, + DateTime lastModifiedDate, final AccountingRuleType accountingRuleType) { + this.name = name; this.shortName = shortName; this.description = description; @@ -156,42 +149,25 @@ public class ShareProduct extends AbstractAuditableCustom { this.totalSharesIssued = totalSharesIssued; this.unitPrice = unitPrice; this.shareCapital = shareCapital; - this.suspenseAccount = suspenseAccount; - this.equityAccount = equityAccount; this.minimumShares = minimumShares; this.nominalShares = nominalShares; this.maximumShares = maximumShares; this.marketPrice = marketPrice; this.charges = charges; this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients; - this.lockPeriod = lockPeriod; - this.minimumActivePeriodForDividends = minimumActivePeriodForDividends; - setCreatedBy(createdBy) ; - setCreatedDate(createdDate) ; - setLastModifiedBy(lastModifiedBy) ; - setLastModifiedDate(lastModifiedDate) ; - } - - public ShareProductData toData() { - GLAccountData suspenseAccount1 = new GLAccountData(suspenseAccount.getId(), suspenseAccount.getName(), suspenseAccount.getGlCode()); - GLAccountData equityAccount1 = new GLAccountData(equityAccount.getId(), equityAccount.getName(), equityAccount.getGlCode()); - List chargeData = new ArrayList<>(); - for(Charge charge: this.charges) { - chargeData.add(ChargeData.lookup(charge.getId(), charge.getName(), charge.isPenalty())) ; + this.lockinPeriod = lockinPeriod; + this.lockPeriodType = lockPeriodType; + this.minimumActivePeriod = minimumActivePeriod; + this.minimumActivePeriodType = minimumActivePeriodForDividendsType; + setCreatedBy(createdBy); + setCreatedDate(createdDate); + setLastModifiedBy(lastModifiedBy); + setLastModifiedDate(lastModifiedDate); + startDate = DateUtils.getDateOfTenant(); + endDate = DateUtils.getDateOfTenant(); + if (accountingRuleType != null) { + this.accountingRule = accountingRuleType.getValue(); } - List marketData = new ArrayList<>() ; - for(ShareMarketPrice pri: marketPrice) { - marketData.add(new ShareMarketPriceData(pri.getStartDate(), pri.getPrice())) ; - } - 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());; - CurrencyData curr = new CurrencyData(currency.getCode(), "", currency.getDigitsAfterDecimal(), currency.getCurrencyInMultiplesOf(), - "", ""); - return new ShareProductData(getId(), name, shortName, description, externalId, curr, totalShares, totalSharesIssued, unitPrice, - shareCapital, suspenseAccount1, equityAccount1, minimumShares, nominalShares, maximumShares, marketData, chargeData, - allowDividendCalculationForInactiveClients, lock, mini); } public boolean setProductName(String productName) { @@ -204,9 +180,9 @@ public class ShareProduct extends AbstractAuditableCustom { } public String getProductName() { - return this.name ; + return this.name; } - + public boolean setShortName(String shortName) { boolean returnValue = false; if (!this.shortName.equals(shortName)) { @@ -245,9 +221,9 @@ public class ShareProduct extends AbstractAuditableCustom { } public Long getTotalShares() { - return this.totalShares ; + return this.totalShares; } - + public boolean setTotalIssuedShares(Long totalSharesIssued) { boolean returnValue = false; if (!this.totalSharesIssued.equals(totalSharesIssued)) { @@ -267,9 +243,9 @@ public class ShareProduct extends AbstractAuditableCustom { } public MonetaryCurrency getCurrency() { - return this.currency ; + return this.currency; } - + public boolean setUnitPrice(BigDecimal unitPrice) { boolean returnValue = false; if (!this.unitPrice.equals(unitPrice)) { @@ -279,24 +255,6 @@ public class ShareProduct extends AbstractAuditableCustom { return returnValue; } - 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 setMinimumShares(final Long minimumShares) { boolean returnValue = false; if (!this.minimumShares.equals(minimumShares)) { @@ -324,13 +282,30 @@ public class ShareProduct extends AbstractAuditableCustom { return returnValue; } - public boolean setMarketPrice(Set marketPrice) { - this.marketPrice = marketPrice; - return true; + public boolean setMarketPrice(Set marketPrice) { + boolean update = false; + for (ShareProductMarketPriceData data : marketPrice) { + if (data.getId() == null) { + ShareProductMarketPrice entity = new ShareProductMarketPrice(data.getStartDate(), data.getShareValue()); + entity.setShareProduct(this); + this.marketPrice.add(entity); + update = true; + } else { + for (ShareProductMarketPrice priceData : this.marketPrice) { + if (priceData.getId() == data.getId()) { + priceData.setStartDate(data.getStartDate()); + priceData.setShareValue(data.getShareValue()); + update = true; + } + } + } + } + return update; } public boolean setCharges(Set charges) { - this.charges = charges; + this.charges.clear(); + this.charges.addAll(charges); return true; } @@ -343,21 +318,117 @@ public class ShareProduct extends AbstractAuditableCustom { return returnValue; } - public boolean setLockPeriod(final PeriodFrequencyType lockPeriod) { + public boolean setLockinPeriod(final Integer lockinPeriod) { boolean returnValue = false; - if (!this.lockPeriod.equals(lockPeriod)) { - this.lockPeriod = lockPeriod; + if (this.lockinPeriod == null) { + this.lockinPeriod = lockinPeriod; + returnValue = true; + } else if (!this.lockinPeriod.equals(lockinPeriod)) { + this.lockinPeriod = lockinPeriod; returnValue = true; } return returnValue; } - public boolean setminimumActivePeriodForDividends(final PeriodFrequencyType minimumActivePeriodForDividends) { + public boolean setLockPeriodFrequencyType(final PeriodFrequencyType lockPeriod) { boolean returnValue = false; - if (!this.minimumActivePeriodForDividends.equals(minimumActivePeriodForDividends)) { - this.minimumActivePeriodForDividends = minimumActivePeriodForDividends; + if (!this.lockPeriodType.equals(lockPeriod)) { + this.lockPeriodType = lockPeriod; returnValue = true; } return returnValue; } + + public boolean setminimumActivePeriod(final Integer minimumActivePeriod) { + boolean returnValue = false; + if (this.minimumActivePeriod == null) { + this.minimumActivePeriod = minimumActivePeriod; + returnValue = true; + } else if (!this.minimumActivePeriod.equals(minimumActivePeriod)) { + this.minimumActivePeriod = minimumActivePeriod; + returnValue = true; + } + return returnValue; + } + + public boolean setminimumActivePeriodFrequencyType(final PeriodFrequencyType minimumActivePeriodForDividends) { + boolean returnValue = false; + if (!this.minimumActivePeriodType.equals(minimumActivePeriodForDividends)) { + this.minimumActivePeriodType = minimumActivePeriodForDividends; + returnValue = true; + } + return returnValue; + } + + public String getShortName() { + return this.shortName; + } + + public boolean setshareCapitalValue(BigDecimal shareCapitalValue) { + boolean updated = false; + if (this.shareCapital == null) { + this.shareCapital = shareCapitalValue; + updated = true; + } else if (!this.shareCapital.equals(shareCapitalValue)) { + this.shareCapital = shareCapitalValue; + updated = true; + } + return updated; + } + + public boolean setAccountingRule(final Integer accountingRule) { + boolean returnValue = false; + if (!this.accountingRule.equals(accountingRule)) { + this.accountingRule = accountingRule; + returnValue = true; + } + return returnValue; + } + + public Long getSharesIssued() { + return this.totalSharesIssued; + } + + public BigDecimal getUnitPrice() { + return this.unitPrice; + } + + public Integer getAccountingType() { + return this.accountingRule; + } + + public boolean isSharesAllowed(Long requestedShares) { + boolean allowed = true; + if (minimumShares != null && maximumShares != null) { + if (requestedShares < minimumShares || requestedShares > maximumShares) { + allowed = false; + } + } + return allowed; + } + + public BigDecimal deriveMarketPrice(final Date currentDate) { + BigDecimal marketValue = this.unitPrice; + if (this.marketPrice != null && !this.marketPrice.isEmpty()) { + for (ShareProductMarketPrice data : this.marketPrice) { + Date futureDate = data.getStartDate(); + if (currentDate.after(futureDate)) { + marketValue = data.getPrice(); + } + } + } + return marketValue; + } + + public void addSubscribedShares(final Long subscribedShares) { + if(this.totalSubscribedShares == null) { + this.totalSubscribedShares = new Long(0) ; + } + this.totalSubscribedShares += subscribedShares ; + } + + public Long getSubscribedShares() { + return this.totalSubscribedShares ; + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividendPayOutDetails.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividendPayOutDetails.java new file mode 100644 index 000000000..d282899ed --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividendPayOutDetails.java @@ -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.portfolio.shareproducts.domain; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendDetails; +import org.apache.fineract.useradministration.domain.AppUser; +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; +import org.joda.time.LocalDate; + +@Entity +@Table(name = "m_share_product_dividend_pay_out") +public class ShareProductDividendPayOutDetails extends AbstractAuditableCustom { + + @Column(name = "product_id", nullable = true) + private Long shareProductId; + + @Column(name = "amount", scale = 6, precision = 19) + private BigDecimal amount; + + @Column(name = "dividend_period_start_date") + @Temporal(TemporalType.DATE) + private Date dividendPeriodStartDate; + + @Column(name = "dividend_period_end_date") + @Temporal(TemporalType.DATE) + private Date dividendPeriodEndDate; + + @Column(name = "status", nullable = false) + private Integer status; + + @LazyCollection(LazyCollectionOption.TRUE) + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "dividend_pay_out_id", referencedColumnName = "id", nullable = false) + private List accountDividendDetails = new ArrayList<>(); + + protected ShareProductDividendPayOutDetails() { + + } + + public ShareProductDividendPayOutDetails(final Long shareProductId, final BigDecimal amount, final Date dividendPeriodStartDate, + final Date dividendPeriodEndDate) { + this.shareProductId = shareProductId; + this.amount = amount; + this.dividendPeriodStartDate = dividendPeriodStartDate; + this.dividendPeriodEndDate = dividendPeriodEndDate; + this.status = ShareProductDividendStatusType.INITIATED.getValue(); + } + + public LocalDate getDividendPeriodEndDateAsLocalDate() { + LocalDate dividendPeriodEndDate = null; + if (this.dividendPeriodEndDate != null) { + dividendPeriodEndDate = new LocalDate(this.dividendPeriodEndDate); + } + return dividendPeriodEndDate; + } + + public List getAccountDividendDetails() { + return this.accountDividendDetails; + } + + public void approveDividendPayout() { + this.status = ShareProductDividendStatusType.APPROVED.getValue(); + } + + public ShareProductDividendStatusType getStatus() { + return ShareProductDividendStatusType.fromInt(this.status); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividendStatusType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividendStatusType.java new file mode 100644 index 000000000..da65e5ca0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividendStatusType.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareproducts.domain; + + +public enum ShareProductDividendStatusType { + + INVALID(0, "shareAccountDividendStatusType.invalid"), INITIATED(100, "shareAccountDividendStatusType.initiated"), APPROVED(300, + "shareAccountDividendStatusType.approved"); + + private final Integer value; + private final String code; + + private ShareProductDividendStatusType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public static ShareProductDividendStatusType fromInt(final Integer type) { + + ShareProductDividendStatusType enumeration = ShareProductDividendStatusType.INVALID; + switch (type) { + case 100: + enumeration = ShareProductDividendStatusType.INITIATED; + break; + case 300: + enumeration = ShareProductDividendStatusType.APPROVED; + break; + + } + return enumeration; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return this.code; + } + + public boolean isApproved() { + return this.value.equals(ShareProductDividendStatusType.APPROVED.getValue()); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividentPayOutDetailsRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividentPayOutDetailsRepository.java new file mode 100644 index 000000000..be74493ed --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividentPayOutDetailsRepository.java @@ -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.shareproducts.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface ShareProductDividentPayOutDetailsRepository extends JpaRepository, + JpaSpecificationExecutor { + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividentPayOutDetailsRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividentPayOutDetailsRepositoryWrapper.java new file mode 100644 index 000000000..8a14a2181 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductDividentPayOutDetailsRepositoryWrapper.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareproducts.domain; + +import org.apache.fineract.portfolio.shareproducts.exception.DividendNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ShareProductDividentPayOutDetailsRepositoryWrapper { + + private final ShareProductDividentPayOutDetailsRepository shareProductDividentPayOutDetailsRepository; + + @Autowired + public ShareProductDividentPayOutDetailsRepositoryWrapper( + final ShareProductDividentPayOutDetailsRepository shareProductDividentPayOutDetailsRepository) { + this.shareProductDividentPayOutDetailsRepository = shareProductDividentPayOutDetailsRepository; + } + + public ShareProductDividendPayOutDetails findOneWithNotFoundDetection(final Long dividendId) { + ShareProductDividendPayOutDetails shareProductDividendPayOutDetails = this.shareProductDividentPayOutDetailsRepository + .findOne(dividendId); + if (shareProductDividendPayOutDetails == null) { throw new DividendNotFoundException(dividendId, "share"); } + return shareProductDividendPayOutDetails; + } + + public void save(final ShareProductDividendPayOutDetails shareProductDividendPayOutDetails) { + this.shareProductDividentPayOutDetailsRepository.save(shareProductDividendPayOutDetails); + } + + public void delete(final ShareProductDividendPayOutDetails shareProductDividendPayOutDetails) { + this.shareProductDividentPayOutDetailsRepository.delete(shareProductDividendPayOutDetails); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareMarketPrice.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductMarketPrice.java similarity index 73% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareMarketPrice.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductMarketPrice.java index 8b84c55f0..604214994 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareMarketPrice.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductMarketPrice.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.domain; +package org.apache.fineract.portfolio.shareproducts.domain; import java.math.BigDecimal; import java.util.Date; @@ -32,22 +32,26 @@ import javax.persistence.TemporalType; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity -@Table(name = "m_share_marketprice") -public class ShareMarketPrice extends AbstractPersistable { +@Table(name = "m_share_product_market_price") +public class ShareProductMarketPrice extends AbstractPersistable { @ManyToOne(optional = false) @JoinColumn(name = "product_id", referencedColumnName = "id", nullable = false) private ShareProduct product; - @Column(name = "start_date") + @Column(name = "from_date") @Temporal(TemporalType.DATE) - private Date startDate; + private Date fromDate; @Column(name = "share_value", nullable = false) private BigDecimal shareValue ; - public ShareMarketPrice(final Date startDate, final BigDecimal shareValue) { - this.startDate = startDate ; + public ShareProductMarketPrice() { + + } + + public ShareProductMarketPrice(final Date fromDate, final BigDecimal shareValue) { + this.fromDate = fromDate ; this.shareValue = shareValue ; } @@ -56,10 +60,18 @@ public class ShareMarketPrice extends AbstractPersistable { } public Date getStartDate() { - return this.startDate ; + return this.fromDate ; } public BigDecimal getPrice() { return this.shareValue ; } + + public void setStartDate(Date date) { + this.fromDate = date ; + } + + public void setShareValue(BigDecimal value) { + this.shareValue = value ; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProductRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductRepository.java similarity index 94% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProductRepository.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductRepository.java index e6a36931b..522215135 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProductRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductRepository.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.domain; +package org.apache.fineract.portfolio.shareproducts.domain; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductRepositoryWrapper.java new file mode 100644 index 000000000..1a448a871 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/domain/ShareProductRepositoryWrapper.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareproducts.domain; + +import org.apache.fineract.portfolio.products.exception.ProductNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ShareProductRepositoryWrapper { + + private final ShareProductRepository shareProductRepository ; + + @Autowired + public ShareProductRepositoryWrapper(final ShareProductRepository shareProductRepository) { + this.shareProductRepository = shareProductRepository ; + } + + public ShareProduct findOneWithNotFoundDetection(final Long productId) { + ShareProduct product = this.shareProductRepository.findOne(productId) ; + if(product == null) { + throw new ProductNotFoundException(productId, "share") ; + } + return product ; + } + + public void save(ShareProduct product) { + this.shareProductRepository.save(product) ; + } + + public void saveAndFlush(ShareProduct product) { + this.shareProductRepository.saveAndFlush(product) ; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/exception/DividendNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/exception/DividendNotFoundException.java new file mode 100644 index 000000000..ca3780696 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/exception/DividendNotFoundException.java @@ -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.shareproducts.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class DividendNotFoundException extends AbstractPlatformResourceNotFoundException { + + public DividendNotFoundException(final Long id, String type) { + super("error.msg.dividend.id.invalid", type + " dividend with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/exception/DividentProcessingException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/exception/DividentProcessingException.java new file mode 100644 index 000000000..8ff11ac52 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/exception/DividentProcessingException.java @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareproducts.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +public class DividentProcessingException extends AbstractPlatformDomainRuleException { + + public DividentProcessingException(final String msgcode, final String defaultUserMessage) { + super("error.msg.divident.processing." + msgcode, defaultUserMessage); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/ApproveShareProductDividendCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/ApproveShareProductDividendCommandHandler.java new file mode 100644 index 000000000..48676702a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/ApproveShareProductDividendCommandHandler.java @@ -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.shareproducts.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.shareproducts.service.ShareProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = "SHAREPRODUCT", action = "APPROVE_DIVIDEND") +public class ApproveShareProductDividendCommandHandler implements NewCommandSourceHandler { + + private final ShareProductWritePlatformService shareProductWritePlatformService; + + @Autowired + public ApproveShareProductDividendCommandHandler(final ShareProductWritePlatformService shareProductWritePlatformService) { + this.shareProductWritePlatformService = shareProductWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(JsonCommand jsonCommand) { + return this.shareProductWritePlatformService.approveShareProductDividend(jsonCommand.entityId()); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/handler/CreateShareProductCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/CreateShareProductCommandHandler.java similarity index 92% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/handler/CreateShareProductCommandHandler.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/CreateShareProductCommandHandler.java index 4ba6023c0..7b495c1f9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/handler/CreateShareProductCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/CreateShareProductCommandHandler.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.handler; +package org.apache.fineract.portfolio.shareproducts.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.shares.service.ShareProductWritePlatformService; +import org.apache.fineract.portfolio.shareproducts.service.ShareProductWritePlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/CreateShareProductDividendCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/CreateShareProductDividendCommandHandler.java new file mode 100644 index 000000000..7f2aa1bfc --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/CreateShareProductDividendCommandHandler.java @@ -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.shareproducts.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.shareproducts.service.ShareProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = "SHAREPRODUCT", action = "CREATE_DIVIDEND") +public class CreateShareProductDividendCommandHandler implements NewCommandSourceHandler { + + private final ShareProductWritePlatformService shareProductWritePlatformService; + + @Autowired + public CreateShareProductDividendCommandHandler(final ShareProductWritePlatformService shareProductWritePlatformService) { + this.shareProductWritePlatformService = shareProductWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(JsonCommand jsonCommand) { + return this.shareProductWritePlatformService.createShareProductDividend(jsonCommand.entityId(), jsonCommand); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/DeleteShareProductDividendCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/DeleteShareProductDividendCommandHandler.java new file mode 100644 index 000000000..e9f5b2415 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/DeleteShareProductDividendCommandHandler.java @@ -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.shareproducts.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.shareproducts.service.ShareProductWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@CommandType(entity = "SHAREPRODUCT", action = "DELETE_DIVIDEND") +public class DeleteShareProductDividendCommandHandler implements NewCommandSourceHandler { + + private final ShareProductWritePlatformService shareProductWritePlatformService; + + @Autowired + public DeleteShareProductDividendCommandHandler(final ShareProductWritePlatformService shareProductWritePlatformService) { + this.shareProductWritePlatformService = shareProductWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(JsonCommand jsonCommand) { + return this.shareProductWritePlatformService.deleteShareProductDividend(jsonCommand.entityId()); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/handler/UpdateShareProductCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/UpdateShareProductCommandHandler.java similarity index 92% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/handler/UpdateShareProductCommandHandler.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/UpdateShareProductCommandHandler.java index c2003fa9e..f4b57109b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/handler/UpdateShareProductCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/handler/UpdateShareProductCommandHandler.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.handler; +package org.apache.fineract.portfolio.shareproducts.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.shares.service.ShareProductWritePlatformService; +import org.apache.fineract.portfolio.shareproducts.service.ShareProductWritePlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/serialization/ShareProductDataSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/serialization/ShareProductDataSerializer.java similarity index 57% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/serialization/ShareProductDataSerializer.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/serialization/ShareProductDataSerializer.java index 59c65d8a3..345294b2c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/serialization/ShareProductDataSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/serialization/ShareProductDataSerializer.java @@ -16,12 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.serialization; +package org.apache.fineract.portfolio.shareproducts.serialization; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -30,12 +29,12 @@ 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.accounting.common.AccountingRuleType; 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; @@ -44,9 +43,10 @@ import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException; -import org.apache.fineract.portfolio.shares.constants.ShareProductApiConstants; -import org.apache.fineract.portfolio.shares.domain.ShareMarketPrice; -import org.apache.fineract.portfolio.shares.domain.ShareProduct; +import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProductMarketPrice; import org.apache.fineract.useradministration.domain.AppUser; import org.joda.time.DateTime; import org.joda.time.LocalDate; @@ -58,34 +58,28 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; -//This class responsibility is to validate data and serialize to entity and return object to the caller @Service public class ShareProductDataSerializer { - private final Set supportedParameters = new HashSet<>(Arrays.asList("")); - private final FromJsonHelper fromApiJsonHelper; private final ChargeRepositoryWrapper chargeRepository; - private final GLAccountRepositoryWrapper glAccountRepository; + private final PlatformSecurityContext platformSecurityContext; - private final PlatformSecurityContext platformSecurityContext ; - @Autowired public ShareProductDataSerializer(final FromJsonHelper fromApiJsonHelper, final ChargeRepositoryWrapper chargeRepository, - final GLAccountRepositoryWrapper glAccountRepository, final PlatformSecurityContext platformSecurityContext) { this.fromApiJsonHelper = fromApiJsonHelper; this.chargeRepository = chargeRepository; - this.glAccountRepository = glAccountRepository; - this.platformSecurityContext = platformSecurityContext ; + this.platformSecurityContext = platformSecurityContext; } public ShareProduct validateAndCreate(JsonCommand jsonCommand) { if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken>() {}.getType(); - //this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), supportedParameters); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareProductApiConstants.supportedParametersForCreate); final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesproduct"); @@ -96,14 +90,19 @@ public class ShareProductDataSerializer { .notExceedingLengthOf(200); final String shortName = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.shortname_paramname, element); baseDataValidator.reset().parameter(ShareProductApiConstants.shortname_paramname).value(shortName).notBlank() - .notExceedingLengthOf(10); + .notExceedingLengthOf(4); String description = null; if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.description_paramname, element)) { description = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.description_paramname, element); } String externalId = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.externalid_paramname, element); + // baseDataValidator.reset().parameter(ShareProductApiConstants.externalid_paramname).value(externalId).notBlank(); + Long totalNumberOfShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalshares_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.totalshares_paramname).value(totalNumberOfShares).notNull() + .longGreaterThanZero(); + final String currencyCode = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.currency_paramname, element); final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerWithLocaleNamed( ShareProductApiConstants.digitsafterdecimal_paramname, element); @@ -113,29 +112,65 @@ public class ShareProductDataSerializer { final Long sharesIssued = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalsharesissued_paramname, element); final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareProductApiConstants.unitprice_paramname, element, locale); - final BigDecimal shareCapitalValue = null; - Long suspenseAccountId = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.suspenseaccount_paramname, element); - Long equityAccountId = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.equityaccount_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.unitprice_paramname).value(unitPrice).notNull().positiveAmount(); + + BigDecimal shareCapitalValue = BigDecimal.ONE; + if (sharesIssued != null && unitPrice != null) { + shareCapitalValue = BigDecimal.valueOf(sharesIssued).multiply(unitPrice); + } + + Integer accountingRule = this.fromApiJsonHelper.extractIntegerNamed(ShareProductApiConstants.accountingRuleParamName, element, + locale); + baseDataValidator.reset().parameter(ShareProductApiConstants.accountingRuleParamName).value(accountingRule).notNull() + .integerGreaterThanZero(); + AccountingRuleType accountingRuleType = null; + if (accountingRule != null) { + accountingRuleType = AccountingRuleType.fromInt(accountingRule); + } + Long minimumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.minimumshares_paramname, element); Long nominalClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.nominaltshares_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares).notNull() + .longGreaterThanZero(); + if (minimumClientShares != null && nominalClientShares != null) { + baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares) + .longGreaterThanNumber(minimumClientShares); + } Long maximumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.maximumshares_paramname, element); - Set marketPriceSet = asembleShareMarketPrice(element); + if (maximumClientShares != null && nominalClientShares != null) { + baseDataValidator.reset().parameter(ShareProductApiConstants.maximumshares_paramname).value(maximumClientShares) + .longGreaterThanNumber(nominalClientShares); + } + + Set marketPriceSet = asembleShareMarketPrice(element); Set charges = assembleListOfProductCharges(element, currencyCode); Boolean allowdividendsForInactiveClients = this.fromApiJsonHelper.extractBooleanNamed( ShareProductApiConstants.allowdividendcalculationforinactiveclients_paramname, element); - PeriodFrequencyType lockPeriod = extractPeriodType(ShareProductApiConstants.lockperiod_paramname, element); - PeriodFrequencyType minimumActivePeriod = extractPeriodType(ShareProductApiConstants.minimumactiveperiodfordividends_paramname, - element); - GLAccount suspenseAccount = glAccountRepository.findOneWithNotFoundDetection(suspenseAccountId); - GLAccount equityAccount = glAccountRepository.findOneWithNotFoundDetection(equityAccountId); - AppUser modifiedBy = null; - DateTime modifiedOn = null; - AppUser createdBy = platformSecurityContext.authenticatedUser() ; - DateTime createdDate = DateUtils.getLocalDateTimeOfTenant().toDateTime() ; + + Integer minimumActivePeriod = this.fromApiJsonHelper.extractIntegerNamed( + ShareProductApiConstants.minimumactiveperiodfordividends_paramname, element, locale); + PeriodFrequencyType minimumActivePeriodType = extractPeriodType( + ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, element); + if (minimumActivePeriod != null) { + baseDataValidator.reset().parameter(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname) + .value(minimumActivePeriodType.getValue()).integerSameAsNumber(PeriodFrequencyType.DAYS.getValue()); + } + + Integer lockinPeriod = this.fromApiJsonHelper.extractIntegerNamed(ShareProductApiConstants.lockperiod_paramname, element, locale); + PeriodFrequencyType lockPeriodType = extractPeriodType(ShareProductApiConstants.lockinperiodfrequencytype_paramname, element); + + AppUser createdBy = platformSecurityContext.authenticatedUser(); + AppUser modifiedBy = createdBy; + DateTime createdDate = DateUtils.getLocalDateTimeOfTenant().toDateTime(); + DateTime modifiedOn = createdDate; ShareProduct product = new ShareProduct(productName, shortName, description, externalId, currency, totalNumberOfShares, - sharesIssued, unitPrice, shareCapitalValue, suspenseAccount, equityAccount, minimumClientShares, nominalClientShares, - maximumClientShares, marketPriceSet, charges, allowdividendsForInactiveClients, lockPeriod, minimumActivePeriod, - createdBy, createdDate, modifiedBy, modifiedOn); + sharesIssued, unitPrice, shareCapitalValue, minimumClientShares, nominalClientShares, maximumClientShares, marketPriceSet, + charges, allowdividendsForInactiveClients, lockinPeriod, lockPeriodType, minimumActivePeriod, minimumActivePeriodType, + createdBy, createdDate, modifiedBy, modifiedOn, accountingRuleType); + for (ShareProductMarketPrice data : marketPriceSet) { + data.setShareProduct(product); + } + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } return product; } @@ -145,8 +180,26 @@ public class ShareProductDataSerializer { return frequencyType; } - private Set asembleShareMarketPrice(final JsonElement element) { - Set set = null; + private Set asembleShareMarketPriceForUpdate(final JsonElement element) { + Set set = null; + if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.marketprice_paramname, element)) { + set = new HashSet<>(); + JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareProductApiConstants.marketprice_paramname, element); + for (JsonElement arrayElement : array) { + Long id = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.id_paramname, arrayElement); + LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareProductApiConstants.startdate_paramname, + arrayElement); + final BigDecimal shareValue = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( + ShareProductApiConstants.sharevalue_paramname, arrayElement); + ShareProductMarketPriceData obj = new ShareProductMarketPriceData(id, localDate.toDate(), shareValue); + set.add(obj); + } + } + return set; + } + + private Set asembleShareMarketPrice(final JsonElement element) { + Set set = null; if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.marketprice_paramname, element)) { set = new HashSet<>(); JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareProductApiConstants.marketprice_paramname, element); @@ -155,7 +208,7 @@ public class ShareProductDataSerializer { arrayElement); final BigDecimal shareValue = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( ShareProductApiConstants.sharevalue_paramname, arrayElement); - ShareMarketPrice obj = new ShareMarketPrice(localDate.toDate(), shareValue); + ShareProductMarketPrice obj = new ShareProductMarketPrice(localDate.toDate(), shareValue); set.add(obj); } } @@ -173,8 +226,8 @@ public class ShareProductDataSerializer { final Long id = jsonObject.get("id").getAsLong(); final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id); if (!currencyCode.equals(charge.getCurrencyCode())) { - final String errorMessage = "Charge and Loan Product must have the same currency."; - throw new InvalidCurrencyException("charge", "attach.to.loan.product", errorMessage); + final String errorMessage = "Charge and Share Product must have the same currency."; + throw new InvalidCurrencyException("charge", "attach.to.share.product", errorMessage); } charges.add(charge); } @@ -189,7 +242,8 @@ public class ShareProductDataSerializer { if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken>() {}.getType(); - //this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), supportedParameters); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareProductApiConstants.supportedParametersForCreate); final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesproduct"); @@ -198,6 +252,7 @@ public class ShareProductDataSerializer { final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject()); if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.name_paramname, element)) { final String productName = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.name_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.name_paramname).value(productName).notBlank(); if (product.setProductName(productName)) { actualChanges.put(ShareProductApiConstants.name_paramname, productName); } @@ -205,6 +260,7 @@ public class ShareProductDataSerializer { if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.shortname_paramname, element)) { final String shortName = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.shortname_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.shortname_paramname).value(shortName).notBlank(); if (product.setShortName(shortName)) { actualChanges.put(ShareProductApiConstants.shortname_paramname, shortName); } @@ -218,6 +274,7 @@ public class ShareProductDataSerializer { if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.externalid_paramname, element)) { String externalId = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.externalid_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.externalid_paramname).value(externalId).notBlank(); if (product.setExternalId(externalId)) { actualChanges.put(ShareProductApiConstants.externalid_paramname, externalId); } @@ -225,14 +282,15 @@ public class ShareProductDataSerializer { if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.totalshares_paramname, element)) { Long totalShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalshares_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.totalshares_paramname).value(totalShares).notNull() + .longGreaterThanZero(); if (product.setTotalShares(totalShares)) { actualChanges.put(ShareProductApiConstants.totalshares_paramname, totalShares); } } - + Long sharesIssued = null; if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.totalsharesissued_paramname, element)) { - final Long sharesIssued = this.fromApiJsonHelper - .extractLongNamed(ShareProductApiConstants.totalsharesissued_paramname, element); + sharesIssued = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalsharesissued_paramname, element); if (product.setTotalIssuedShares(sharesIssued)) { actualChanges.put(ShareProductApiConstants.totalsharesissued_paramname, sharesIssued); } @@ -252,32 +310,29 @@ public class ShareProductDataSerializer { } } + BigDecimal unitPrice = null; if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.unitprice_paramname, element)) { - final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareProductApiConstants.unitprice_paramname, - element, locale); + unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareProductApiConstants.unitprice_paramname, element, locale); + baseDataValidator.reset().parameter(ShareProductApiConstants.unitprice_paramname).value(unitPrice).notNull().positiveAmount(); if (product.setUnitPrice(unitPrice)) { actualChanges.put(ShareProductApiConstants.unitprice_paramname, unitPrice); } } - if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.suspenseaccount_paramname, element)) { - Long suspenseAccountId = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.suspenseaccount_paramname, element); - GLAccount suspenseAccount = glAccountRepository.findOneWithNotFoundDetection(suspenseAccountId); - if (product.setSuspenseAccount(suspenseAccount)) { - actualChanges.put(ShareProductApiConstants.suspenseaccount_paramname, suspenseAccount); - } - } - - if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.equityaccount_paramname, element)) { - Long equityAccountId = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.equityaccount_paramname, element); - GLAccount equityAccount = glAccountRepository.findOneWithNotFoundDetection(equityAccountId); - if (product.setEquityAccount(equityAccount)) { - actualChanges.put(ShareProductApiConstants.equityaccount_paramname, equityAccount); + if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.accountingRuleParamName, element)) { + Integer accountingRule = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(ShareProductApiConstants.accountingRuleParamName, + element); + baseDataValidator.reset().parameter(ShareProductApiConstants.accountingRuleParamName).value(accountingRule).notNull() + .integerGreaterThanZero(); + if (product.setAccountingRule(accountingRule)) { + actualChanges.put(ShareProductApiConstants.accountingRuleParamName, accountingRule); } } if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.minimumshares_paramname, element)) { Long minimumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.minimumshares_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.minimumshares_paramname).value(minimumClientShares).notNull() + .longGreaterThanZero(); if (product.setMinimumShares(minimumClientShares)) { actualChanges.put(ShareProductApiConstants.minimumshares_paramname, minimumClientShares); } @@ -285,6 +340,8 @@ public class ShareProductDataSerializer { if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.nominaltshares_paramname, element)) { Long nominalClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.nominaltshares_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares).notNull() + .longGreaterThanZero(); if (product.setNominalShares(nominalClientShares)) { actualChanges.put(ShareProductApiConstants.nominaltshares_paramname, nominalClientShares); } @@ -292,13 +349,15 @@ public class ShareProductDataSerializer { if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.maximumshares_paramname, element)) { Long maximumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.maximumshares_paramname, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.maximumshares_paramname).value(maximumClientShares).notNull() + .longGreaterThanZero(); if (product.setMaximumShares(maximumClientShares)) { actualChanges.put(ShareProductApiConstants.maximumshares_paramname, maximumClientShares); } } if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.marketprice_paramname, element)) { - Set marketPrice = asembleShareMarketPrice(element); + Set marketPrice = asembleShareMarketPriceForUpdate(element); if (product.setMarketPrice(marketPrice)) { actualChanges.put(ShareProductApiConstants.marketprice_paramname, marketPrice); } @@ -321,21 +380,81 @@ public class ShareProductDataSerializer { } } - if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.lockperiod_paramname, element)) { - PeriodFrequencyType lockPeriod = extractPeriodType(ShareProductApiConstants.lockperiod_paramname, element); - if (product.setLockPeriod(lockPeriod)) { - actualChanges.put(ShareProductApiConstants.lockperiod_paramname, lockPeriod); - } - } + Integer minimumActivePeriod = null; if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.minimumactiveperiodfordividends_paramname, element)) { - PeriodFrequencyType minimumActivePeriod = extractPeriodType(ShareProductApiConstants.minimumactiveperiodfordividends_paramname, - element); - if (product.setminimumActivePeriodForDividends(minimumActivePeriod)) { + minimumActivePeriod = this.fromApiJsonHelper.extractIntegerNamed( + ShareProductApiConstants.minimumactiveperiodfordividends_paramname, element, locale); + if (product.setminimumActivePeriod(minimumActivePeriod)) { actualChanges.put(ShareProductApiConstants.minimumactiveperiodfordividends_paramname, minimumActivePeriod); } } + + if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, element)) { + PeriodFrequencyType minimumActivePeriodType = extractPeriodType( + ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, element); + if (minimumActivePeriod != null) { + baseDataValidator.reset().parameter(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname) + .value(minimumActivePeriodType.getValue()).integerSameAsNumber(PeriodFrequencyType.DAYS.getValue()); + } + if (product.setminimumActivePeriodFrequencyType(minimumActivePeriodType)) { + actualChanges.put(ShareProductApiConstants.minimumactiveperiodfrequencytype_paramname, minimumActivePeriodType); + } + } + final Integer lockinPeriod; + if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.lockperiod_paramname, element)) { + lockinPeriod = this.fromApiJsonHelper.extractIntegerNamed(ShareProductApiConstants.lockperiod_paramname, element, locale); + if (product.setLockinPeriod(lockinPeriod)) { + actualChanges.put(ShareProductApiConstants.lockperiod_paramname, lockinPeriod); + } + } + + if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.lockinperiodfrequencytype_paramname, element)) { + PeriodFrequencyType lockPeriod = extractPeriodType(ShareProductApiConstants.lockinperiodfrequencytype_paramname, element); + if (product.setLockPeriodFrequencyType(lockPeriod)) { + actualChanges.put(ShareProductApiConstants.lockinperiodfrequencytype_paramname, lockPeriod); + } + } + + BigDecimal shareCapitalValue; + if (sharesIssued != null || unitPrice != null) { + if (sharesIssued == null) sharesIssued = product.getSharesIssued(); + if (unitPrice == null) unitPrice = product.getUnitPrice(); + shareCapitalValue = BigDecimal.valueOf(sharesIssued).multiply(unitPrice); + if (product.setshareCapitalValue(shareCapitalValue)) { + actualChanges.put(ShareProductApiConstants.sharecapital_paramname, shareCapitalValue); + } + } + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } return actualChanges; } + public void validateDividendDetails(JsonCommand jsonCommand) { + if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), + ShareProductApiConstants.supportedParametersForDivident); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource("sharesproduct.dividend.processing"); + + JsonElement element = jsonCommand.parsedJson(); + + final LocalDate dividendPeriodStartDate = this.fromApiJsonHelper.extractLocalDateNamed( + ShareProductApiConstants.dividendPeriodStartDateParamName, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.dividendPeriodStartDateParamName).value(dividendPeriodStartDate) + .notBlank(); + + final LocalDate dividendPeriodEndDate = this.fromApiJsonHelper.extractLocalDateNamed( + ShareProductApiConstants.dividendPeriodEndDateParamName, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.dividendPeriodStartDateParamName).value(dividendPeriodEndDate) + .notBlank().validateDateAfter(dividendPeriodStartDate); + final BigDecimal dividendAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( + ShareProductApiConstants.dividendAmountParamName, element); + baseDataValidator.reset().parameter(ShareProductApiConstants.dividendAmountParamName).value(dividendAmount).notBlank() + .positiveAmount(); + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java new file mode 100644 index 000000000..ee3686383 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java @@ -0,0 +1,59 @@ +/** + * 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.shareproducts.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.products.service.ProductCommandsService; +import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonElement; + +@Service(value = "SHAREPRODUCT_COMMANDSERVICE") +public class ShareProductCommandsServiceImpl implements ProductCommandsService { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public ShareProductCommandsServiceImpl(final FromJsonHelper fromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonHelper; + } + + + public CommandProcessingResult postDividends(Long productId, JsonCommand jsonCommand) { + return null ; + } + + @Override + public Object handleCommand(Long productId, String command, String jsonBody) { + final JsonElement parsedCommand = this.fromApiJsonHelper.parse(jsonBody); + final JsonCommand jsonCommand = JsonCommand.from(jsonBody, parsedCommand, this.fromApiJsonHelper, null, null, null, null, null, + null, null, null, null, null); + if (ShareProductApiConstants.PREIEW_DIVIDENDS_COMMAND_STRING.equals(command)) { + return null ; + } else if (ShareProductApiConstants.POST_DIVIDENdS_COMMAND_STRING.equals(command)) { return postDividends(productId, + jsonCommand); } + // throw unknow commandexception + return CommandProcessingResult.empty(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendAssembler.java new file mode 100644 index 000000000..1ff71c7de --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendAssembler.java @@ -0,0 +1,131 @@ +/** + * 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.shareproducts.service; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.portfolio.products.service.ProductReadPlatformService; +import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; +import org.apache.fineract.portfolio.shareaccounts.domain.PurchasedSharesStatusType; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendDetails; +import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductData; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails; +import org.joda.time.Days; +import org.joda.time.LocalDate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ShareProductDividendAssembler { + + private final ProductReadPlatformService shareProductReadPlatformService; + private final ShareAccountReadPlatformService ShareAccountReadPlatformService; + + @Autowired + public ShareProductDividendAssembler(final ShareProductReadPlatformServiceImpl shareProductReadPlatformService, + final ShareAccountReadPlatformService ShareAccountReadPlatformService) { + this.shareProductReadPlatformService = shareProductReadPlatformService; + this.ShareAccountReadPlatformService = ShareAccountReadPlatformService; + } + + public ShareProductDividendPayOutDetails calculateDividends(final Long productId, final BigDecimal amount, + final LocalDate dividendPeriodStartDate, final LocalDate dividendPeriodEndDate) { + + ShareProductData product = (ShareProductData) this.shareProductReadPlatformService.retrieveOne(productId, false); + MonetaryCurrency currency = new MonetaryCurrency(product.getCurrency().code(), product.getCurrency().decimalPlaces(), product + .getCurrency().currencyInMultiplesOf()); + Collection shareAccountDatas = this.ShareAccountReadPlatformService.retrieveAllShareAccountDataForDividends( + productId, product.getAllowDividendCalculationForInactiveClients(), dividendPeriodStartDate); + ShareProductDividendPayOutDetails productDividendPayOutDetails = null; + final int minimumActivePeriod = product.getMinimumActivePeriod(); + final Map numberOfSharesdaysPerAccount = new HashMap<>(); + long numberOfShareDays = calculateNumberOfShareDays(dividendPeriodEndDate, dividendPeriodStartDate, minimumActivePeriod, + shareAccountDatas, numberOfSharesdaysPerAccount); + + if (numberOfShareDays > 0) { + double amountPerShareDay = amount.doubleValue() / numberOfShareDays; + productDividendPayOutDetails = new ShareProductDividendPayOutDetails(productId, Money.of(currency, amount).getAmount(), + dividendPeriodStartDate.toDate(), dividendPeriodEndDate.toDate()); + for (ShareAccountData accountData : shareAccountDatas) { + long numberOfShareDaysPerAccount = numberOfSharesdaysPerAccount.get(accountData.getId()); + double amountForAccount = numberOfShareDaysPerAccount * amountPerShareDay; + final Money accountAmount = Money.of(currency, BigDecimal.valueOf(amountForAccount)); + ShareAccountDividendDetails dividendDetails = new ShareAccountDividendDetails(accountData.getId(), + accountAmount.getAmount()); + productDividendPayOutDetails.getAccountDividendDetails().add(dividendDetails); + } + } + + return productDividendPayOutDetails; + } + + private long calculateNumberOfShareDays(final LocalDate postingDate, final LocalDate lastDividendPostDate, + final int minimumActivePeriod, final Collection shareAccountDatas, + final Map numberOfSharesdaysPerAccount) { + long numberOfShareDays = 0; + for (ShareAccountData accountData : shareAccountDatas) { + long numberOfShareDaysPerAccount = 0; + Collection purchasedShares = accountData.getPurchasedShares(); + long numberOfShares = 0; + LocalDate lastDividendAppliedDate = null; + for (PurchasedSharesData purchasedSharesData : purchasedShares) { + final PurchasedSharesStatusType status = PurchasedSharesStatusType.fromInt(purchasedSharesData.getStatus().getId() + .intValue()); + final PurchasedSharesStatusType type = PurchasedSharesStatusType.fromInt(purchasedSharesData.getType().getId() + .intValue()); + if (status.isApproved() && !type.isChargePayment()) { + + LocalDate shareStartDate = purchasedSharesData.getPurchasedDate(); + if (shareStartDate.isBefore(lastDividendPostDate)) { + shareStartDate = lastDividendPostDate; + } + int numberOfPurchseDays = Days.daysBetween(shareStartDate, postingDate).getDays(); + if (type.isPurchased() && numberOfPurchseDays < minimumActivePeriod) { + continue; + } + + if (lastDividendAppliedDate != null) { + numberOfShareDaysPerAccount += (Days.daysBetween(lastDividendAppliedDate, shareStartDate).getDays() * numberOfShares); + } + lastDividendAppliedDate = shareStartDate; + if (type.isPurchased()) { + numberOfShares += purchasedSharesData.getNumberOfShares(); + } else { + numberOfShares -= purchasedSharesData.getNumberOfShares(); + } + + } + } + if (lastDividendAppliedDate != null) { + numberOfShareDaysPerAccount += (Days.daysBetween(lastDividendAppliedDate, postingDate).getDays() * numberOfShares); + } + numberOfShareDays += numberOfShareDaysPerAccount; + numberOfSharesdaysPerAccount.put(accountData.getId(), numberOfShareDaysPerAccount); + } + return numberOfShareDays; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformService.java new file mode 100644 index 000000000..18eba3a3d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformService.java @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareproducts.service; + +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductDividendPayOutData; + +public interface ShareProductDividendReadPlatformService { + + Page retriveAll(Long productId, Integer status, SearchParameters searchParameters); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java new file mode 100644 index 000000000..6760ef955 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDividendReadPlatformServiceImpl.java @@ -0,0 +1,128 @@ +/** + * 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.shareproducts.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; +import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductDividendPayOutData; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductData; +import org.joda.time.LocalDate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class ShareProductDividendReadPlatformServiceImpl implements ShareProductDividendReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PaginationHelper paginationHelper = new PaginationHelper<>(); + + @Autowired + public ShareProductDividendReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Page retriveAll(final Long productId, final Integer status, + final SearchParameters searchParameters) { + ShareProductDividendMapper shareProductDividendMapper = new ShareProductDividendMapper(); + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(shareProductDividendMapper.schema()); + sqlBuilder.append(" where sp.id = ? "); + List params = new ArrayList<>(2); + params.add(productId); + if (status != null) { + sqlBuilder.append(" and pod.status = ?"); + params.add(status); + } + if (searchParameters.isOrderByRequested()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + if (searchParameters.isOffset()) { + sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + final String sqlCountRows = "SELECT FOUND_ROWS()"; + Object[] paramsObj = params.toArray(); + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), paramsObj, + shareProductDividendMapper); + } + + private static final class ShareProductDividendMapper implements RowMapper { + + private final String sql; + + public ShareProductDividendMapper() { + StringBuilder sb = new StringBuilder(); + sb.append(" pod.id as id, pod.amount as amount,"); + sb.append(" pod.status as status, pod.dividend_period_start_date as startDate,"); + sb.append(" pod.dividend_period_end_date as endDate,"); + sb.append(" sp.id as productId,sp.name as productName "); + sb.append(" from m_share_product_dividend_pay_out pod"); + sb.append(" inner join m_share_product sp on sp.id = pod.product_id "); + sql = sb.toString(); + } + + public String schema() { + return this.sql; + } + + @Override + public ShareProductDividendPayOutData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final BigDecimal amount = rs.getBigDecimal("amount"); + final Integer status = JdbcSupport.getInteger(rs, "status"); + final EnumOptionData statusEnum = SharesEnumerations.ShareProductDividendStatusEnum(status); + final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate"); + final LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate"); + + final Long productId = rs.getLong("productId"); + final String productName = rs.getString("productName"); + + final ShareProductData productData = ShareProductData.lookup(productId, productName); + final Collection accountDividendsData = null; + return new ShareProductDividendPayOutData(id, productData, amount, startDate, endDate, accountDividendsData, statusEnum); + } + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformService.java new file mode 100644 index 000000000..ae3f98f23 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformService.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareproducts.service; + +import java.util.Collection; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; + +public interface ShareProductDropdownReadPlatformService { + + Collection retrieveLockinPeriodFrequencyTypeOptions(); + + Collection retrieveMinimumActivePeriodFrequencyTypeOptions() ; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformServiceImpl.java new file mode 100644 index 000000000..c7c7b801a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductDropdownReadPlatformServiceImpl.java @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.shareproducts.service; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations; +import org.apache.fineract.portfolio.shareproducts.SharePeriodFrequencyType; +import org.springframework.stereotype.Service; + +@Service +public class ShareProductDropdownReadPlatformServiceImpl implements ShareProductDropdownReadPlatformService { + + @Override + public Collection retrieveLockinPeriodFrequencyTypeOptions() { + final List allowedLockinPeriodFrequencyTypeOptions = Arrays.asList( // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.DAYS), // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.WEEKS), // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.MONTHS), // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.YEARS) // + ); + + return allowedLockinPeriodFrequencyTypeOptions; + } + + @Override + public Collection retrieveMinimumActivePeriodFrequencyTypeOptions() { + final List minimumActivePeriodFrequencyTypeOptions = Arrays.asList( // + SharesEnumerations.lockinPeriodFrequencyType(SharePeriodFrequencyType.DAYS) // + ); + + return minimumActivePeriodFrequencyTypeOptions; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java new file mode 100644 index 000000000..cd3b42e55 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java @@ -0,0 +1,283 @@ +/** + * 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.shareproducts.service; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService; +import org.apache.fineract.accounting.common.AccountingEnumerations; +import org.apache.fineract.accounting.glaccount.data.GLAccountData; +import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; +import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingReadPlatformService; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService; +import org.apache.fineract.portfolio.charge.data.ChargeData; +import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; +import org.apache.fineract.portfolio.products.data.ProductData; +import org.apache.fineract.portfolio.products.exception.ProductNotFoundException; +import org.apache.fineract.portfolio.products.service.ProductReadPlatformService; +import org.apache.fineract.portfolio.savings.service.SavingsEnumerations; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData; +import org.apache.fineract.portfolio.shareproducts.data.ShareProductData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service(value = "shareReadPlatformService") +public class ShareProductReadPlatformServiceImpl implements ProductReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final CurrencyReadPlatformService currencyReadPlatformService; + private final ChargeReadPlatformService chargeReadPlatformService; + private final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService; + private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService; + private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService; + private final PaginationHelper provisioningEntryDataPaginationHelper = new PaginationHelper<>(); + + @Autowired + public ShareProductReadPlatformServiceImpl(final RoutingDataSource dataSource, + final CurrencyReadPlatformService currencyReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService, + final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService, + final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService, + final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.currencyReadPlatformService = currencyReadPlatformService; + this.chargeReadPlatformService = chargeReadPlatformService; + this.shareProductDropdownReadPlatformService = shareProductDropdownReadPlatformService; + this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService; + this.accountMappingReadPlatformService = accountMappingReadPlatformService; + } + + @Override + public Page retrieveAllProducts(Integer offSet, Integer limit) { + AllShareProductRowMapper mapper = new AllShareProductRowMapper(); + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(mapper.schema()); + if (limit != null) { + sqlBuilder.append(" limit ").append(limit); + } + if (offSet != null) { + sqlBuilder.append(" offset ").append(offSet); + } + + final String sqlCountRows = "SELECT FOUND_ROWS()"; + Object[] whereClauseItemsitems = new Object[] {}; + return this.provisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), + whereClauseItemsitems, mapper); + } + + @Override + public ProductData retrieveOne(Long productId, boolean includeTemplate) { + MarketPriceRowMapper marketRowMapper = new MarketPriceRowMapper(); + + try { + final String sql1 = "select " + marketRowMapper.schema() + " where marketData.product_id = ?"; + final Collection shareMarketCollection = this.jdbcTemplate.query(sql1, marketRowMapper, + new Object[] { productId }); + final Collection charges = this.chargeReadPlatformService.retrieveShareProductCharges(productId); + ShareProductRowMapper mapper = new ShareProductRowMapper(shareMarketCollection, charges); + final String sql = "select " + mapper.schema() + " where shareproduct.id = ?"; + ShareProductData data = (ShareProductData) this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { productId }); + + if (data.hasAccountingEnabled()) { + final Map accountingMappings = this.accountMappingReadPlatformService + .fetchAccountMappingDetailsForShareProduct(productId, data.accountingRuleTypeId()); + final Collection paymentChannelToFundSourceMappings = this.accountMappingReadPlatformService + .fetchPaymentTypeToFundSourceMappingsForShareProduct(productId); + Collection feeToGLAccountMappings = this.accountMappingReadPlatformService + .fetchFeeToIncomeAccountMappingsForShareProduct(productId); + data = ShareProductData.withAccountingDetails(data, accountingMappings, paymentChannelToFundSourceMappings, + feeToGLAccountMappings); + } + + if (includeTemplate) { + Collection chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); + final Collection currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); + final Collection lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + final Collection minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveMinimumActivePeriodFrequencyTypeOptions(); + final Map> accountingMappingOptions = this.accountingDropdownReadPlatformService + .retrieveAccountMappingOptionsForShareProducts(); + data = ShareProductData.template(data, currencyOptions, chargeOptions, minimumActivePeriodFrequencyTypeOptions, + lockinPeriodFrequencyTypeOptions, accountingMappingOptions); + } + return data; + } catch (final EmptyResultDataAccessException e) { + throw new ProductNotFoundException(productId, "share"); + } + } + + @Override + public ProductData retrieveTemplate() { + Collection chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); + final Collection currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies(); + final Collection lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveLockinPeriodFrequencyTypeOptions(); + final Collection minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService + .retrieveMinimumActivePeriodFrequencyTypeOptions(); + final Map> accountingMappingOptions = this.accountingDropdownReadPlatformService + .retrieveAccountMappingOptionsForShareProducts(); + return ShareProductData.template(currencyOptions, chargeOptions, minimumActivePeriodFrequencyTypeOptions, + lockinPeriodFrequencyTypeOptions, accountingMappingOptions); + } + + @Override + public Collection retrieveAllForLookup() { + AllShareProductRowMapper mapper = new AllShareProductRowMapper(); + String sql = "select " + mapper.schema(); + return this.jdbcTemplate.query(sql, mapper, new Object[] {}); + } + + @Override + public Set getResponseDataParams() { + return null; + } + + private static final class AllShareProductRowMapper implements RowMapper { + + @Override + public ShareProductData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String name = rs.getString("name"); + final String shortName = rs.getString("short_name"); + final Long totalShares = rs.getLong("total_shares"); + return ShareProductData.generic(id, name, shortName, totalShares); + } + + public String schema() { + return "shareproduct.id, shareproduct.name, shareproduct.short_name, shareproduct.total_shares from m_share_product shareproduct"; + } + } + + private static final class MarketPriceRowMapper implements RowMapper { + + @Override + public ShareProductMarketPriceData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final Date fromDate = rs.getDate("from_date"); + final BigDecimal shareValue = rs.getBigDecimal("share_value"); + return new ShareProductMarketPriceData(id, fromDate, shareValue); + } + + public String schema() { + return "marketData.id, marketData.from_date, marketData.share_value from m_share_product_market_price marketData"; + } + } + + private final static class ShareProductRowMapper implements RowMapper { + + Collection shareMarketCollection; + Collection charges; + private StringBuffer buff = new StringBuffer(); + + ShareProductRowMapper(Collection shareMarketCollection, Collection charges) { + this.shareMarketCollection = shareMarketCollection; + this.charges = charges; + buff.append("shareproduct.id, shareproduct.name, shareproduct.short_name, ") + .append("shareproduct.external_id, shareproduct.description, shareproduct.start_date,") + .append("shareproduct.end_date, shareproduct.currency_code, shareproduct.currency_digits, ") + .append("shareproduct.currency_multiplesof, shareproduct.total_shares, shareproduct.issued_shares, ") + .append("shareproduct.unit_price, shareproduct.capital_amount, ") + .append("shareproduct.accounting_type as accountingType, ") + .append("shareproduct.minimum_client_shares, shareproduct.nominal_client_shares, ") + .append("shareproduct.maximum_client_shares, shareproduct.minimum_active_period_frequency, ") + .append("shareproduct.minimum_active_period_frequency_enum, shareproduct.lockin_period_frequency, ") + .append("shareproduct.lockin_period_frequency_enum, shareproduct.allow_dividends_inactive_clients, ") + .append("shareproduct.createdby_id, created.username as createdName, modified.username as modifiedName, ") + .append("shareproduct.created_date, shareproduct.lastmodifiedby_id, shareproduct.lastmodified_date, ") + .append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ") + .append("curr.display_symbol as currencyDisplaySymbol ").append("from m_share_product shareproduct ") + .append("LEFT JOIN m_currency curr on curr.code = shareproduct.currency_code ") + .append("LEFT JOIN m_appuser created ON created.id = shareproduct.createdby_id ") + .append("LEFT JOIN m_appuser modified ON modified.id = shareproduct.lastmodifiedby_id "); + + } + + @Override + public ShareProductData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String name = rs.getString("name"); + final String shortName = rs.getString("short_name"); + final String externalId = rs.getString("external_id"); + final String description = rs.getString("description"); + final String currencyCode = rs.getString("currency_code"); + final Integer currencyDigits = rs.getInt("currency_digits"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "currency_multiplesof"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + final Long totalShares = rs.getLong("total_shares"); + final Long issuedShares = rs.getLong("issued_shares"); + final BigDecimal unitPrice = rs.getBigDecimal("unit_price"); + final BigDecimal capitalAmount = rs.getBigDecimal("capital_amount"); + final Long minimumClientShares = JdbcSupport.getLong(rs, "minimum_client_shares"); + final Long nominalClientShares = JdbcSupport.getLong(rs, "nominal_client_shares"); + final Long maximumClientShares = JdbcSupport.getLong(rs, "maximum_client_shares"); + final Boolean allowDividendsForInactiveClients = rs.getBoolean("allow_dividends_inactive_clients"); + // final Long createdById = rs.getLong("createdby_id") ; + // final Date createdDate = rs.getDate("created_date") ; + // final Long modifiedById = rs.getLong("lastmodifiedby_id") ; + // final Date modifiedDate = rs.getDate("lastmodified_date") ; + final Integer minimumActivePeriod = JdbcSupport.getInteger(rs, "minimum_active_period_frequency"); + final Integer minimumActviePeriodEnumValue = JdbcSupport.getInteger(rs, "minimum_active_period_frequency_enum"); + EnumOptionData minimumActivePeriodType = null; + if (minimumActviePeriodEnumValue != null) { + minimumActivePeriodType = SavingsEnumerations.lockinPeriodFrequencyType(minimumActviePeriodEnumValue); + } + + final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockin_period_frequency"); + EnumOptionData lockinPeriodFrequencyType = null; + final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockin_period_frequency_enum"); + if (lockinPeriodFrequencyTypeValue != null) { + lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue); + } + final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType"); + final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId); + + return ShareProductData.data(id, name, shortName, description, externalId, currency, totalShares, issuedShares, unitPrice, + capitalAmount, minimumClientShares, nominalClientShares, maximumClientShares, shareMarketCollection, charges, + allowDividendsForInactiveClients, lockinPeriodFrequency, lockinPeriodFrequencyType, minimumActivePeriod, + minimumActivePeriodType, accountingRuleType); + } + + public String schema() { + return this.buff.toString(); + } + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformService.java similarity index 77% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductWritePlatformService.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformService.java index 23a2051f9..7bb811165 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformService.java @@ -16,15 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.portfolio.shares.service; +package org.apache.fineract.portfolio.shareproducts.service; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; - public interface ShareProductWritePlatformService { - CommandProcessingResult createShareProduct(JsonCommand jsonCommand) ; - + CommandProcessingResult createShareProduct(JsonCommand jsonCommand); + CommandProcessingResult updateProduct(Long productId, JsonCommand command); + + CommandProcessingResult createShareProductDividend(Long productId, JsonCommand jsonCommand); + + CommandProcessingResult approveShareProductDividend(Long PayOutDetailId); + + CommandProcessingResult deleteShareProductDividend(Long PayOutDetailId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..e9aaac737 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,190 @@ +/** + * 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.shareproducts.service; + +import java.math.BigDecimal; +import java.util.Map; + +import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepositoryWrapper; +import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper; +import org.apache.fineract.portfolio.shareproducts.exception.DividentProcessingException; +import org.apache.fineract.portfolio.shareproducts.serialization.ShareProductDataSerializer; +import org.joda.time.LocalDate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; + +import com.google.gson.JsonElement; + +@Service +public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareProductWritePlatformService { + + private final ShareProductRepositoryWrapper repository; + private final ShareProductDataSerializer serializer; + private final FromJsonHelper fromApiJsonHelper; + private final ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepository; + private final ShareProductDividendAssembler shareProductDividendAssembler; + private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; + + @Autowired + public ShareProductWritePlatformServiceJpaRepositoryImpl(final ShareProductRepositoryWrapper repository, + final ShareProductDataSerializer serializer, final FromJsonHelper fromApiJsonHelper, + final ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepositor, + final ShareProductDividendAssembler shareProductDividendAssembler, + final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) { + this.repository = repository; + this.serializer = serializer; + this.fromApiJsonHelper = fromApiJsonHelper; + this.shareProductDividentPayOutDetailsRepository = shareProductDividentPayOutDetailsRepositor; + this.shareProductDividendAssembler = shareProductDividendAssembler; + this.accountMappingWritePlatformService = accountMappingWritePlatformService; + } + + @Override + public CommandProcessingResult createShareProduct(JsonCommand jsonCommand) { + try { + ShareProduct product = this.serializer.validateAndCreate(jsonCommand); + this.repository.save(product); + + // save accounting mappings + this.accountMappingWritePlatformService.createShareProductToGLAccountMapping(product.getId(), jsonCommand); + + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(product.getId()) // + .build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + + } + + @Override + public CommandProcessingResult updateProduct(Long productId, JsonCommand jsonCommand) { + try { + ShareProduct product = this.repository.findOneWithNotFoundDetection(productId); + final Map changes = this.serializer.validateAndUpdate(jsonCommand, product); + + // accounting related changes + final boolean accountingTypeChanged = changes.containsKey(ShareProductApiConstants.accountingRuleParamName); + final Map accountingMappingChanges = this.accountMappingWritePlatformService + .updateShareProductToGLAccountMapping(product.getId(), jsonCommand, accountingTypeChanged, product.getAccountingType()); + changes.putAll(accountingMappingChanges); + + if (!changes.isEmpty()) { + this.repository.saveAndFlush(product); + } + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(productId) // + .with(changes) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult createShareProductDividend(final Long productId, final JsonCommand jsonCommand) { + try { + this.serializer.validateDividendDetails(jsonCommand); + JsonElement element = jsonCommand.parsedJson(); + final LocalDate dividendPeriodStartDate = this.fromApiJsonHelper.extractLocalDateNamed( + ShareProductApiConstants.dividendPeriodStartDateParamName, element); + final LocalDate dividendPeriodEndDate = this.fromApiJsonHelper.extractLocalDateNamed( + ShareProductApiConstants.dividendPeriodEndDateParamName, element); + final BigDecimal dividendAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( + ShareProductApiConstants.dividendAmountParamName, element); + + final ShareProductDividendPayOutDetails dividendPayOutDetails = this.shareProductDividendAssembler.calculateDividends( + productId, dividendAmount, dividendPeriodStartDate, dividendPeriodEndDate); + if (dividendPayOutDetails == null) { throw new DividentProcessingException("eligible.shares.not.found", + "No eligible shares for creating dividends"); } + this.shareProductDividentPayOutDetailsRepository.save(dividendPayOutDetails); + + return new CommandProcessingResultBuilder() // + .withCommandId(jsonCommand.commandId()) // + .withEntityId(productId) // + .withSubEntityId(dividendPayOutDetails.getId())// + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult approveShareProductDividend(final Long PayOutDetailId) { + try { + ShareProductDividendPayOutDetails dividendPayOutDetails = this.shareProductDividentPayOutDetailsRepository + .findOneWithNotFoundDetection(PayOutDetailId); + if (dividendPayOutDetails.getStatus().isApproved()) { throw new DividentProcessingException("alreay.approved", + "Can't approve already appoved dividends "); } + dividendPayOutDetails.approveDividendPayout(); + this.shareProductDividentPayOutDetailsRepository.save(dividendPayOutDetails); + return new CommandProcessingResultBuilder() // + .withEntityId(PayOutDetailId) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(dve); + return CommandProcessingResult.empty(); + } + } + + @Override + public CommandProcessingResult deleteShareProductDividend(final Long PayOutDetailId) { + try { + ShareProductDividendPayOutDetails dividendPayOutDetails = this.shareProductDividentPayOutDetailsRepository + .findOneWithNotFoundDetection(PayOutDetailId); + if (dividendPayOutDetails.getStatus().isApproved()) { throw new DividentProcessingException("alreay.approved", + "Can't delete already appoved dividends "); } + this.shareProductDividentPayOutDetailsRepository.delete(dividendPayOutDetails); + return new CommandProcessingResultBuilder() // + .withEntityId(PayOutDetailId) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(dve); + return CommandProcessingResult.empty(); + } + } + + private void handleDataIntegrityIssues(final DataIntegrityViolationException dve) { + final Throwable realCause = dve.getMostSpecificCause(); + throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue", + "Unknown data integrity issue with resource GL Closure: " + realCause.getMessage()); + } + + private void handleDataIntegrityIssues(@SuppressWarnings("unused") final JsonCommand command, final DataIntegrityViolationException dve) { + final Throwable realCause = dve.getMostSpecificCause(); + throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue", + "Unknown data integrity issue with resource GL Closure: " + realCause.getMessage()); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/constants/ShareProductApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/constants/ShareProductApiConstants.java deleted file mode 100644 index ae384f8bc..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/constants/ShareProductApiConstants.java +++ /dev/null @@ -1,52 +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.shares.constants; - -public interface ShareProductApiConstants { - - //Command Strings - public final String PREIEW_DIVIDENDS_COMMAND_STRING = "previewdividends" ; - public final String POST_DIVIDENdS_COMMAND_STRING = "postdividends" ; - - String id_paramname = "id"; - String name_paramname = "name"; - String shortname_paramname = "shortName"; - String description_paramname = "description"; - String externalid_paramname = "externalId"; - String totalshares_paramname = "totalShares"; - String currency_paramname = "currencyCode"; - String digitsafterdecimal_paramname = "digitsAfterDecimal"; - String inmultiplesof_paramname = "inMultiplesOf"; - String totalsharesissued_paramname = "totalSharesIssued"; - String unitprice_paramname = "unitPrice"; - String sharecapital_paramname = "shareCapital"; - String suspenseaccount_paramname = "suspenseAccount"; - String equityaccount_paramname = "equityAccount"; - String minimumshares_paramname = "minimumShares"; - String nominaltshares_paramname = "nominaltShares"; - String maximumshares_paramname = "maximumShares"; - String marketprice_paramname = "marketPrice"; - String charges_paramname = "charges"; - String allowdividendcalculationforinactiveclients_paramname = "allowDividendCalculationForInactiveClients"; - String lockperiod_paramname = "lockPeriod"; - String minimumactiveperiodfordividends_paramname = "minimumActivePeriodForDividends"; - - String startdate_paramname = "startDate"; - String sharevalue_paramname = "shareValue"; -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/DividendsData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/DividendsData.java deleted file mode 100644 index 9bba515cd..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/DividendsData.java +++ /dev/null @@ -1,49 +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.shares.data; - -import java.math.BigDecimal; -import java.util.Date; - -public class DividendsData { - - private Long clientId; - - private String clientName; - - private String shareAccountNo; - - private String savingsAccountNo; - - private Long numberOfShares; - - private BigDecimal dividendAmount; - - private Date dividendIssuedDate; - - public DividendsData(final Long clientId, final String clientName, final String savingsAccountNo, final Long numberOfShares, - final BigDecimal dividendAmount, final Date dividendIssuedDate) { - this.clientId = clientId; - this.clientName = clientName; - this.savingsAccountNo = savingsAccountNo; - this.numberOfShares = numberOfShares; - this.dividendAmount = dividendAmount; - this.dividendIssuedDate = dividendIssuedDate; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ProductDividendsData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ProductDividendsData.java deleted file mode 100644 index f48dc8f74..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ProductDividendsData.java +++ /dev/null @@ -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.shares.data; - -import java.math.BigDecimal; -import java.util.Collection; -import java.util.Date; - -import org.apache.fineract.organisation.monetary.data.CurrencyData; - - -public class ProductDividendsData { - - private Long id ; - - private Long productId ; - - private String productName ; - - private Date dividendsIssuedDate ; - - private BigDecimal dividendAmount ; - - private CurrencyData currency ; - - Collection dividendsData ; - - public ProductDividendsData(final Long productId, final String productName, final Date dividendsIssuedDate, - final BigDecimal dividendAmount, final CurrencyData currency, final Collection dividendsData) { - this.productId = productId ; - this.productName = productName ; - this.dividendsIssuedDate = dividendsIssuedDate ; - this.dividendAmount = dividendAmount ; - this.dividendsData = dividendsData ; - this.currency = currency ; - } - - public void setId(Long id) { - this.id = id ; - } - - public Long getId() { - return this.id ; - } - - public Long getProductId() { - return productId ; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ShareProductData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ShareProductData.java deleted file mode 100644 index 60b744f63..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/data/ShareProductData.java +++ /dev/null @@ -1,160 +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.shares.data; - -import java.math.BigDecimal; -import java.util.List; - -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.charge.data.ChargeData; -import org.apache.fineract.portfolio.products.data.ProductData; - -public class ShareProductData implements ProductData{ - - private final Long id; - private final String name; - private final String shortName; - private final String description; - private final String externalId; - private final CurrencyData currency; - private final Long totalShares; - private final Long totalSharesIssued; - private final BigDecimal unitPrice; - private final BigDecimal shareCapital; - private final GLAccountData suspenseAccount; - private final GLAccountData equityAccount; - private final Long minimumShares; - private final Long nominaltShares; - private final Long maximumShares; - private final List marketPrice; - private final List charges; - private Boolean allowDividendCalculationForInactiveClients; - private final EnumOptionData lockPeriod; - private final EnumOptionData minimumActivePeriodForDividends; - - public ShareProductData(final Long id, final String name, final String shortName, final String description, final String externalId, - final CurrencyData currency, final Long totalShares, final Long totalSharesIssued, final BigDecimal unitPrice, - final BigDecimal shareCapital, final GLAccountData suspenseAccount, final GLAccountData equityAccount, - final Long minimumShares, final Long nominaltShares, final Long maximumShares, List marketPrice, - final List charges, final Boolean allowDividendCalculationForInactiveClients, final EnumOptionData lockPeriod, - final EnumOptionData minimumActivePeriodForDividends) { - this.id = id; - this.name = name; - this.shortName = shortName; - this.description = description; - this.externalId = externalId; - this.currency = currency; - this.totalShares = totalShares; - this.totalSharesIssued = totalSharesIssued; - this.unitPrice = unitPrice; - this.shareCapital = shareCapital; - this.suspenseAccount = suspenseAccount; - this.equityAccount = equityAccount; - this.minimumShares = minimumShares; - this.nominaltShares = nominaltShares; - this.maximumShares = maximumShares; - this.marketPrice = marketPrice; - this.charges = charges; - this.allowDividendCalculationForInactiveClients = allowDividendCalculationForInactiveClients; - this.lockPeriod = lockPeriod; - this.minimumActivePeriodForDividends = minimumActivePeriodForDividends; - } - - public Long getId() { - return this.id; - } - - public String getName() { - return this.name; - } - - public String getShortName() { - return this.shortName; - } - - public String getDescription() { - return this.description; - } - - public String getExternalId() { - return this.externalId; - } - - public CurrencyData getCurrency() { - return this.currency; - } - - public Long getTotalShares() { - return this.totalShares; - } - - public Long getTotalSharesIssued() { - return this.totalSharesIssued; - } - - public BigDecimal getUnitPrice() { - return this.unitPrice; - } - - public BigDecimal getShareCapital() { - return this.shareCapital; - } - - public GLAccountData getSuspenseAccount() { - return this.suspenseAccount; - } - - public GLAccountData getEquityAccount() { - return this.equityAccount; - } - - public Long getMinimumShares() { - return this.minimumShares; - } - - public Long getNominaltShares() { - return this.nominaltShares; - } - - public Long getMaximumShares() { - return this.maximumShares; - } - - public List getMarketPrice() { - return this.marketPrice; - } - - public List getCharges() { - return this.charges; - } - - public Boolean getAllowDividendCalculationForInactiveClients() { - return this.allowDividendCalculationForInactiveClients; - } - - public EnumOptionData getLockPeriod() { - return this.lockPeriod; - } - - public EnumOptionData getMinimumActivePeriodForDividends() { - return this.minimumActivePeriodForDividends; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProductTempRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProductTempRepository.java deleted file mode 100644 index f6c0facd7..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/domain/ShareProductTempRepository.java +++ /dev/null @@ -1,79 +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.shares.domain; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import org.apache.fineract.portfolio.accounts.domain.ShareAccount; -import org.apache.fineract.portfolio.shares.data.ProductDividendsData; - - -public class ShareProductTempRepository { - - Map cache = new HashMap<>() ; - Map> accountsCache = new HashMap<>() ; - Map dividendsCahe = new HashMap<>() ; - - private final static ShareProductTempRepository instance = new ShareProductTempRepository() ; - - private ShareProductTempRepository() { - - } - - public final static ShareProductTempRepository getInstance() { - return instance ; - } - - public ShareProduct fineOne(Long productId) { - return this.cache.get(productId) ; - } - - public Collection findAll() { - return this.cache.values() ; - } - public void save(ShareProduct product) { - Long id = new Long(cache.size() + 1) ; - product.setTempId(id) ; - this.cache.put(id, product) ; - } - - public void addAccount(Long productId, ShareAccount account) { - if(accountsCache.containsKey(productId)) { - ArrayList list = accountsCache.get(productId) ; - list.add(account) ; - }else { - ArrayList list = new ArrayList<>() ; - list.add(account) ; - accountsCache.put(productId, list) ; - } - } - - public ArrayList getAllAccounts(Long productId) { - return accountsCache.get(productId) ; - } - - public void saveDividends(ProductDividendsData data) { - Long id = new Long(dividendsCahe.size()+1) ; - data.setId(id) ; - dividendsCahe.put(data.getProductId(), data) ; - } -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductCommandsServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductCommandsServiceImpl.java deleted file mode 100644 index 3257adf62..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductCommandsServiceImpl.java +++ /dev/null @@ -1,105 +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.shares.service; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; - -import org.apache.fineract.infrastructure.core.api.JsonCommand; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; -import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; -import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; -import org.apache.fineract.organisation.monetary.data.CurrencyData; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; -import org.apache.fineract.portfolio.accounts.domain.ShareAccount; -import org.apache.fineract.portfolio.products.service.ProductCommandsService; -import org.apache.fineract.portfolio.shares.constants.ShareProductApiConstants; -import org.apache.fineract.portfolio.shares.data.DividendsData; -import org.apache.fineract.portfolio.shares.data.ProductDividendsData; -import org.apache.fineract.portfolio.shares.domain.ShareProduct; -import org.apache.fineract.portfolio.shares.domain.ShareProductTempRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.stereotype.Service; - -import com.google.gson.JsonElement; - -@Service(value = "SHAREPRODUCT_COMMANDSERVICE") -public class ShareProductCommandsServiceImpl implements ProductCommandsService { - - private final FromJsonHelper fromApiJsonHelper; - - @Autowired - public ShareProductCommandsServiceImpl(final FromJsonHelper fromApiJsonHelper) { - this.fromApiJsonHelper = fromApiJsonHelper; - } - - public ProductDividendsData previewDividends(Long productId, JsonCommand jsonCommand) { - ArrayList accounts = ShareProductTempRepository.getInstance().getAllAccounts(productId); - ShareProduct product = ShareProductTempRepository.getInstance().fineOne(productId); - Long total = product.getTotalShares(); - JsonElement element = jsonCommand.parsedJson(); - final BigDecimal totalDividendAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("dividendAmount", element); - BigDecimal perShareValue = totalDividendAmount.divide(new BigDecimal(total)); - Date date = new Date(); - ArrayList dividends = new ArrayList<>(); - for (ShareAccount account : accounts) { - if(account.getStatus().equals("Approved")) { - BigDecimal val = perShareValue.multiply(new BigDecimal(account.getTotalShares())); - DividendsData data = new DividendsData(account.getClientId(), account.getClientName(), account.getSavingsAccountNo(), - account.getTotalShares(), val, date); - dividends.add(data); - } - } - - MonetaryCurrency currency = product.getCurrency() ; - CurrencyData cur = new CurrencyData(currency.getCode(), "", currency.getDigitsAfterDecimal(), currency.getCurrencyInMultiplesOf(), - "", ""); - ProductDividendsData toReturn = new ProductDividendsData(productId, product.getProductName(), date, totalDividendAmount, cur, dividends); - return toReturn; - } - - public CommandProcessingResult postDividends(Long productId, JsonCommand jsonCommand) { - try { - ProductDividendsData data = previewDividends(productId, jsonCommand); - ShareProductTempRepository.getInstance().saveDividends(data); - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(data.getId()) // - .build(); - } catch (final DataIntegrityViolationException dve) { - return CommandProcessingResult.empty(); - } - } - - @Override - public Object handleCommand(Long productId, String command, String jsonBody) { - final JsonElement parsedCommand = this.fromApiJsonHelper.parse(jsonBody); - final JsonCommand jsonCommand = JsonCommand.from(jsonBody, parsedCommand, this.fromApiJsonHelper, null, null, null, null, null, - null, null, null, null, null); - if (ShareProductApiConstants.PREIEW_DIVIDENDS_COMMAND_STRING.equals(command)) { - return previewDividends(productId, jsonCommand); - } else if (ShareProductApiConstants.POST_DIVIDENdS_COMMAND_STRING.equals(command)) { return postDividends(productId, - jsonCommand); } - // throw unknow commandexception - return CommandProcessingResult.empty(); - } - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductReadPlatformServiceImpl.java deleted file mode 100644 index 0158d069d..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductReadPlatformServiceImpl.java +++ /dev/null @@ -1,64 +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.shares.service; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import org.apache.fineract.portfolio.products.data.ProductData; -import org.apache.fineract.portfolio.products.service.ProductReadPlatformService; -import org.apache.fineract.portfolio.shares.domain.ShareProduct; -import org.apache.fineract.portfolio.shares.domain.ShareProductTempRepository; -import org.springframework.stereotype.Service; - - -@Service(value = "shareReadPlatformService") -public class ShareProductReadPlatformServiceImpl implements ProductReadPlatformService{ - - ShareProductTempRepository repo = ShareProductTempRepository.getInstance() ; - - @Override - public Collection retrieveAllProducts() { - Collection entities = repo.findAll() ; - List toReturn = new ArrayList<>() ; - for(ShareProduct entity: entities) { - toReturn.add(entity.toData()) ; - } - return toReturn; - } - - @Override - public ProductData retrieveOne(Long productId) { - ShareProduct product = repo.fineOne(productId) ; - return product.toData() ; - } - - @Override - public ProductData retrieveTemplate() { - return null; - } - - @Override - public Set getResponseDataParams() { - return null; - } - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java deleted file mode 100644 index f245c2bdd..000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shares/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java +++ /dev/null @@ -1,90 +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.shares.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.products.exception.ProductNotFoundException; -import org.apache.fineract.portfolio.shares.domain.ShareProduct; -import org.apache.fineract.portfolio.shares.domain.ShareProductRepository; -import org.apache.fineract.portfolio.shares.domain.ShareProductTempRepository; -import org.apache.fineract.portfolio.shares.serialization.ShareProductDataSerializer; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.stereotype.Service; - -@Service -public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareProductWritePlatformService { - - private final ShareProductRepository repository; - private final ShareProductDataSerializer serializer; - - @Autowired - public ShareProductWritePlatformServiceJpaRepositoryImpl(final ShareProductRepository repository, - final ShareProductDataSerializer serializer) { - this.repository = repository; - this.serializer = serializer; - } - - @Override - public CommandProcessingResult createShareProduct(JsonCommand jsonCommand) { - try { - ShareProduct product = this.serializer.validateAndCreate(jsonCommand); - //this.repository.save(product); - ShareProductTempRepository.getInstance().save(product) ; - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(product.getId()) // - .build(); - } catch (final DataIntegrityViolationException dve) { - handleDataIntegrityIssues(jsonCommand, dve); - return CommandProcessingResult.empty(); - } - - } - - @Override - public CommandProcessingResult updateProduct(Long productId, JsonCommand jsonCommand) { - try { - //ShareProduct product = this.repository.findOne(productId); - ShareProduct product = ShareProductTempRepository.getInstance().fineOne(productId) ; - if (product == null) { throw new ProductNotFoundException(productId, "share"); } - final Map changes = this.serializer.validateAndUpdate(jsonCommand, product); - if(!changes.isEmpty()) { - //this.repository.saveAndFlush(product) ; - } - return new CommandProcessingResultBuilder() // - .withCommandId(jsonCommand.commandId()) // - .withEntityId(productId) // - .with(changes) // - .build(); - } catch (DataIntegrityViolationException dve) { - handleDataIntegrityIssues(jsonCommand, dve); - return CommandProcessingResult.empty(); - } - } - - private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { - - } - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerService.java b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerService.java index 6d3a8f9cc..6be117ed5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerService.java @@ -35,4 +35,6 @@ public interface ScheduledJobRunnerService { void updateMaturityDetailsOfDepositAccounts(); void generateRDSchedule(); + + void postDividends() throws JobExecutionException; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java index ea28f533e..d68efb115 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java @@ -19,6 +19,7 @@ package org.apache.fineract.scheduledjobs.service; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Collection; import java.util.Date; import java.util.List; @@ -40,6 +41,8 @@ import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformS import org.apache.fineract.portfolio.savings.service.DepositAccountWritePlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService; +import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformService; +import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedularService; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; @@ -62,18 +65,24 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService private final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService; private final DepositAccountReadPlatformService depositAccountReadPlatformService; private final DepositAccountWritePlatformService depositAccountWritePlatformService; + private final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService; + private final ShareAccountSchedularService shareAccountSchedularService; @Autowired public ScheduledJobRunnerServiceImpl(final RoutingDataSourceServiceFactory dataSourceServiceFactory, final SavingsAccountWritePlatformService savingsAccountWritePlatformService, final SavingsAccountChargeReadPlatformService savingsAccountChargeReadPlatformService, final DepositAccountReadPlatformService depositAccountReadPlatformService, - final DepositAccountWritePlatformService depositAccountWritePlatformService) { + final DepositAccountWritePlatformService depositAccountWritePlatformService, + final ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService, + final ShareAccountSchedularService shareAccountSchedularService) { this.dataSourceServiceFactory = dataSourceServiceFactory; this.savingsAccountWritePlatformService = savingsAccountWritePlatformService; this.savingsAccountChargeReadPlatformService = savingsAccountChargeReadPlatformService; this.depositAccountReadPlatformService = depositAccountReadPlatformService; this.depositAccountWritePlatformService = depositAccountWritePlatformService; + this.shareAccountDividendReadPlatformService = shareAccountDividendReadPlatformService; + this.shareAccountSchedularService = shareAccountSchedularService; } @Transactional @@ -364,4 +373,33 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService } + @Override + @CronTarget(jobName = JobName.POST_DIVIDENTS_FOR_SHARES) + public void postDividends() throws JobExecutionException { + List> dividendDetails = this.shareAccountDividendReadPlatformService.retriveDividendDetailsForPostDividents(); + StringBuilder errorMsg = new StringBuilder(); + for (Map dividendMap : dividendDetails) { + final Long id = ((BigInteger) dividendMap.get("id")).longValue(); + final Long savingsId = ((BigInteger) dividendMap.get("savingsAccountId")).longValue(); + try { + this.shareAccountSchedularService.postDividend(id, savingsId); + } catch (final PlatformApiDataValidationException e) { + final List errors = e.getErrors(); + for (final ApiParameterError error : errors) { + logger.error("Post Dividends to savings failed for Divident detail Id:" + id + " and savings Id: " + savingsId + + " with message " + error.getDeveloperMessage()); + errorMsg.append("Post Dividends to savings failed for Divident detail Id:").append(id).append(" and savings Id:") + .append(savingsId).append(" with message ").append(error.getDeveloperMessage()); + } + } catch (final Exception e) { + logger.error("Post Dividends to savings failed for Divident detail Id:" + id + " and savings Id: " + savingsId + + " with message " + e.getLocalizedMessage()); + errorMsg.append("Post Dividends to savings failed for Divident detail Id:").append(id).append(" and savings Id:") + .append(savingsId).append(" with message ").append(e.getLocalizedMessage()); + } + } + + if (errorMsg.length() > 0) { throw new JobExecutionException(errorMsg.toString()); } + } + } diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V299__share_products.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V299__share_products.sql new file mode 100644 index 000000000..ec3a27c30 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V299__share_products.sql @@ -0,0 +1,194 @@ +CREATE TABLE `m_share_product` ( +`id` BIGINT(20) NOT NULL AUTO_INCREMENT, +`name` VARCHAR(200) NOT NULL, +`short_name` VARCHAR(4) NOT NULL, +`external_id` VARCHAR(100) NULL DEFAULT NULL, +`description` VARCHAR(500) NOT NULL, +`start_date` DATETIME NULL DEFAULT NULL, +`end_date` DATETIME NULL DEFAULT NULL, +`currency_code` VARCHAR(3) NOT NULL, +`currency_digits` SMALLINT(5) NOT NULL, +`currency_multiplesof` SMALLINT(5) NULL DEFAULT NULL, +`total_shares` BIGINT(20) NOT NULL, +`issued_shares` BIGINT(20) NULL DEFAULT NULL, +`totalsubscribed_shares` BIGINT(20) NULL DEFAULT NULL, +`unit_price` DECIMAL(10,2) NOT NULL, +`capital_amount` DECIMAL(20,2) NOT NULL, +`minimum_client_shares` BIGINT(20) NULL DEFAULT NULL, +`nominal_client_shares` BIGINT(20) NOT NULL, +`maximum_client_shares` BIGINT(20) NULL DEFAULT NULL, +`minimum_active_period_frequency` DECIMAL(19,6) NULL DEFAULT NULL, +`minimum_active_period_frequency_enum` SMALLINT(5) NULL DEFAULT NULL, +`lockin_period_frequency` DECIMAL(19,6) NULL DEFAULT NULL, +`lockin_period_frequency_enum` SMALLINT(5) NULL DEFAULT NULL, +`allow_dividends_inactive_clients` SMALLINT(1) NULL DEFAULT 0, +`createdby_id` BIGINT(20) NULL DEFAULT NULL, +`created_date` DATETIME NULL DEFAULT NULL, +`lastmodifiedby_id` BIGINT(20) NULL DEFAULT NULL, +`lastmodified_date` DATETIME NULL DEFAULT NULL, +`accounting_type` SMALLINT(2) NOT NULL, +PRIMARY KEY (`id`), +UNIQUE INDEX `name` (`name`), +CONSTRAINT `m_share_product_ibfk_1` FOREIGN KEY (`createdby_id`) REFERENCES `m_appuser` (`id`), +CONSTRAINT `m_share_product_ibfk_2` FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `m_appuser` (`id`) +) ; + +CREATE TABLE `m_share_product_market_price` ( +`id` BIGINT(20) NOT NULL AUTO_INCREMENT, +`product_id` BIGINT(20) NOT NULL, +`from_date` DATE NULL DEFAULT NULL, +`share_value` DECIMAL(10,2) NOT NULL, +PRIMARY KEY (`id`), +CONSTRAINT `m_share_product_market_price_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `m_share_product` (`id`) +) ; + +CREATE TABLE `m_share_product_charge` ( +`product_id` BIGINT(20) NOT NULL, +`charge_id` BIGINT(20) NOT NULL, +PRIMARY KEY (`product_id`, `charge_id`), +CONSTRAINT `m_share_product_charge_ibfk_1` FOREIGN KEY (`charge_id`) REFERENCES `m_charge` (`id`), +CONSTRAINT `m_share_product_charge_ibfk_2` FOREIGN KEY (`product_id`) REFERENCES `m_share_product` (`id`) +) ; + +CREATE TABLE `m_share_account` ( +`id` BIGINT(20) NOT NULL AUTO_INCREMENT, +`account_no` VARCHAR(50) NOT NULL, +`product_id` BIGINT(20) NOT NULL, +`client_id` BIGINT(20) NOT NULL, +`external_id` VARCHAR(100) NULL DEFAULT NULL, +`status_enum` SMALLINT(5) NOT NULL DEFAULT '300', +`total_approved_shares` BIGINT(20) NULL DEFAULT NULL, +`total_pending_shares` BIGINT(20) NULL DEFAULT NULL, +`submitted_date` DATE NOT NULL, +`submitted_userid` BIGINT(20) NULL DEFAULT NULL, +`approved_date` DATE NULL DEFAULT NULL, +`approved_userid` BIGINT(20) NULL DEFAULT NULL, +`rejected_date` DATE NULL DEFAULT NULL, +`rejected_userid` BIGINT(20) NULL DEFAULT NULL, +`activated_date` DATE NULL DEFAULT NULL, +`activated_userid` BIGINT(20) NULL DEFAULT NULL, +`closed_date` DATE NULL DEFAULT NULL, +`closed_userid` BIGINT(20) NULL DEFAULT NULL, +`currency_code` VARCHAR(3) NOT NULL, +`currency_digits` SMALLINT(5) NOT NULL, +`currency_multiplesof` SMALLINT(5) NULL DEFAULT NULL, +`savings_account_id` BIGINT(20) NOT NULL, +`minimum_active_period_frequency` DECIMAL(19,6) NULL DEFAULT NULL, +`minimum_active_period_frequency_enum` SMALLINT(5) NULL DEFAULT NULL, +`lockin_period_frequency` DECIMAL(19,6) NULL DEFAULT NULL, +`lockin_period_frequency_enum` SMALLINT(5) NULL DEFAULT NULL, +`allow_dividends_inactive_clients` SMALLINT(1) NULL DEFAULT 0, +`created_date` DATETIME NULL DEFAULT NULL, +`lastmodifiedby_id` BIGINT(20) NULL DEFAULT NULL, +`lastmodified_date` DATETIME NULL DEFAULT NULL, +PRIMARY KEY (`id`), +CONSTRAINT `m_share_account_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `m_share_product` (`id`), +CONSTRAINT `m_share_account_ibfk_2` FOREIGN KEY (`savings_account_id`) REFERENCES `m_savings_account` (`id`), +CONSTRAINT `m_share_account_ibfk_3` FOREIGN KEY (`submitted_userid`) REFERENCES `m_appuser` (`id`), +CONSTRAINT `m_share_account_ibfk_4` FOREIGN KEY (`approved_userid`) REFERENCES `m_appuser` (`id`), +CONSTRAINT `m_share_account_ibfk_5` FOREIGN KEY (`rejected_userid`) REFERENCES `m_appuser` (`id`), +CONSTRAINT `m_share_account_ibfk_6` FOREIGN KEY (`activated_userid`) REFERENCES `m_appuser` (`id`), +CONSTRAINT `m_share_account_ibfk_7` FOREIGN KEY (`closed_userid`) REFERENCES `m_appuser` (`id`), +CONSTRAINT `m_share_account_ibfk_8` FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `m_appuser` (`id`), +CONSTRAINT `m_share_account_ibfk_9` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`) +) ; + +CREATE TABLE `m_share_account_charge` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `account_id` BIGINT(20) NOT NULL, + `charge_id` BIGINT(20) NOT NULL, + `charge_time_enum` SMALLINT(5) NOT NULL, + `charge_calculation_enum` SMALLINT(5) NOT NULL, + `charge_payment_mode_enum` SMALLINT(5) NOT NULL DEFAULT '0', + `calculation_percentage` DECIMAL(19,6) NULL DEFAULT NULL, + `calculation_on_amount` DECIMAL(19,6) NULL DEFAULT NULL, + `charge_amount_or_percentage` DECIMAL(19,6) NULL DEFAULT NULL, + `amount` DECIMAL(19,6) NOT NULL, + `amount_paid_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `amount_waived_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `amount_writtenoff_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `amount_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT '0.000000', + `is_paid_derived` TINYINT(1) NOT NULL DEFAULT '0', + `waived` TINYINT(1) NOT NULL DEFAULT '0', + `min_cap` DECIMAL(19,6) NULL DEFAULT NULL, + `max_cap` DECIMAL(19,6) NULL DEFAULT NULL, + `is_active` TINYINT(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + INDEX `charge_id` (`charge_id`), + INDEX `m_share_account_charge_ibfk_2` (`account_id`), + CONSTRAINT `m_share_account_charge_ibfk_1` FOREIGN KEY (`charge_id`) REFERENCES `m_charge` (`id`), + CONSTRAINT `m_share_account_charge_ibfk_2` FOREIGN KEY (`account_id`) REFERENCES `m_share_account` (`id`) +); + + +CREATE TABLE `m_share_account_transactions` ( +`id` BIGINT(20) NOT NULL AUTO_INCREMENT, +`account_id` BIGINT(20) NOT NULL, +`transaction_date` DATE NULL DEFAULT NULL, +`total_shares` BIGINT(20) NULL DEFAULT NULL, +`unit_price` DECIMAL(10,2) NULL DEFAULT NULL, +`amount` DECIMAL(20,2) NULL DEFAULT NULL, +`charge_amount` DECIMAL(20,2) NULL DEFAULT NULL, +`status_enum` SMALLINT(5) NOT NULL DEFAULT '300', +`type_enum` SMALLINT(5) NULL DEFAULT NULL, +PRIMARY KEY (`id`), +CONSTRAINT `m_share_account_purchased_shares_ibfk_1` FOREIGN KEY (`account_id`) REFERENCES `m_share_account` (`id`) +) ; + +CREATE TABLE `m_share_account_charge_paid_by` ( +`id` BIGINT(20) NOT NULL AUTO_INCREMENT, +`share_transaction_id` BIGINT(20) NOT NULL, +`charge_transaction_id` BIGINT(20) NOT NULL, +`amount` DECIMAL(20,2) NOT NULL, +PRIMARY KEY (`id`), +CONSTRAINT `m_share_account_transactions_charge_mapping_ibfk1` FOREIGN KEY (`share_transaction_id`) REFERENCES `m_share_account_transactions` (`id`), +CONSTRAINT `m_share_account_transactions_charge_mapping_ibfk2` FOREIGN KEY (`charge_transaction_id`) REFERENCES `m_share_account_charge` (`id`) +) ; + +CREATE TABLE `m_share_product_dividend_pay_out` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `product_id` BIGINT(20) NOT NULL, + `amount` DECIMAL(19,6) NOT NULL, + `dividend_period_start_date` DATE NOT NULL, + `dividend_period_end_date` DATE NOT NULL, + `status` SMALLINT(3) NOT NULL, + `createdby_id` BIGINT(20) NULL DEFAULT NULL, + `created_date` DATETIME NULL DEFAULT NULL, + `lastmodifiedby_id` BIGINT(20) NULL DEFAULT NULL, + `lastmodified_date` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_m_share_product_dividend_pay_out_product_id` FOREIGN KEY (`product_id`) REFERENCES `m_share_product` (`id`), + CONSTRAINT `FK_m_share_product_dividend_pay_out_createdby_id` FOREIGN KEY (`createdby_id`) REFERENCES `m_appuser` (`id`), + CONSTRAINT `FK_m_share_product_dividend_pay_out_lastmodifiedby_id` FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `m_appuser` (`id`) +); + +CREATE TABLE `m_share_account_dividend_details` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `dividend_pay_out_id` BIGINT(20) NOT NULL, + `account_id` BIGINT(20) NOT NULL, + `amount` DECIMAL(19,6) NOT NULL, + `status` SMALLINT(3) NOT NULL, + `savings_transaction_id` BIGINT(20) NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_m_share_account_dividend_details_dividend_pay_out_id` FOREIGN KEY (`dividend_pay_out_id`) REFERENCES `m_share_product_dividend_pay_out` (`id`), + CONSTRAINT `FK_m_share_account_dividend_details_account_id` FOREIGN KEY (`account_id`) REFERENCES `m_share_account` (`id`) +); + +ALTER TABLE `acc_gl_journal_entry` +ADD COLUMN `share_transaction_id` BIGINT(20) NULL AFTER `payment_details_id`, +ADD CONSTRAINT `FK_acc_gl_journal_entry_m_share_account_transaction` FOREIGN KEY (`share_transaction_id`) REFERENCES `m_share_account_transactions` (`id`); + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('SHAREPRODUCT', 'CREATE_DIVIDEND_SHAREPRODUCT', 'SHAREPRODUCT', 'CREATE_DIVIDEND', 0), ('SHAREPRODUCT', 'CREATE_DIVIDEND_SHAREPRODUCT_CHECKER', 'SHAREPRODUCT', 'CREATE_DIVIDEND_CHECKER', 0),('SHAREPRODUCT', 'APPROVE_DIVIDEND_SHAREPRODUCT', 'SHAREPRODUCT', 'APPROVE_DIVIDEND', 0), ('SHAREPRODUCT', 'APPROVE_DIVIDEND_SHAREPRODUCT_CHECKER', 'SHAREPRODUCT', 'APPROVE_DIVIDEND_CHECKER', 0),('SHAREPRODUCT', 'DELETE_DIVIDEND_SHAREPRODUCT', 'SHAREPRODUCT', 'DELETE_DIVIDEND', 0), ('SHAREPRODUCT', 'DELETE_DIVIDEND_SHAREPRODUCT_CHECKER', 'SHAREPRODUCT', 'DELETE_DIVIDEND_CHECKER', 0),('SHAREPRODUCT', 'READ_DIVIDEND_SHAREPRODUCT', 'SHAREPRODUCT', 'READ_DIVIDEND', 0),('SHAREACCOUNT', 'APPROVE_SHAREACCOUNT', 'SHAREACCOUNT', 'APPROVE', 0), +('SHAREACCOUNT', 'ACTIVATE_SHAREACCOUNT', 'SHAREACCOUNT', 'ACTIVATE', 0), +('SHAREACCOUNT', 'UNDOAPPROVAL_SHAREACCOUNT', 'SHAREACCOUNT', 'UNDOAPPROVAL', 0), +('SHAREACCOUNT', 'REJECT_SHAREACCOUNT', 'SHAREACCOUNT', 'REJECT', 0), +('SHAREACCOUNT', 'APPLYADDITIONALSHARES_SHAREACCOUNT', 'SHAREACCOUNT', 'APPLYADDITIONALSHARES', 0), +('SHAREACCOUNT', 'APPROVEADDITIONALSHARES_SHAREACCOUNT', 'SHAREACCOUNT', 'APPROVEADDITIONALSHARES', 0), +('SHAREACCOUNT', 'REJECTADDITIONALSHARES_SHAREACCOUNT', 'SHAREACCOUNT', 'REJECTADDITIONALSHARES', 0), +('SHAREACCOUNT', 'REDEEMSHARES_SHAREACCOUNT', 'SHAREACCOUNT', 'REDEEMSHARES', 0), +('SHAREACCOUNT', 'CLOSE_SHAREACCOUNT', 'SHAREACCOUNT', 'CLOSE', 0) +; + +INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`) VALUES ('Post Dividends For Shares', 'Post Dividends For Shares', '0 0 0 1/1 * ? *', now()); + +