Issue fixes on shares and dividends reported by QA

This commit is contained in:
Nazeer Hussain Shaik
2016-04-19 17:55:23 +05:30
parent fe6e1e88fe
commit 60588a78e0
27 changed files with 763 additions and 256 deletions
@@ -39,4 +39,8 @@ public interface JournalEntryRepository extends JpaRepository<JournalEntry, Long
@Query("from JournalEntry journalEntry where journalEntry.entityId= :entityId and journalEntry.entityType = :entityType") @Query("from JournalEntry journalEntry where journalEntry.entityId= :entityId and journalEntry.entityType = :entityType")
List<JournalEntry> findProvisioningJournalEntriesByEntityId(@Param("entityId") Long entityId, @Param("entityType") Integer entityType) ; List<JournalEntry> findProvisioningJournalEntriesByEntityId(@Param("entityId") Long entityId, @Param("entityType") Integer entityType) ;
@Query("from JournalEntry journalEntry where journalEntry.transactionId= :transactionId and journalEntry.reversed is false and journalEntry.entityType = :entityType")
List<JournalEntry> findJournalEntries(@Param("transactionId") String transactionId, @Param("entityType") Integer entityType) ;
} }
@@ -1001,6 +1001,37 @@ public class AccountingProcessorHelper {
"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"); }
} }
public void revertCashBasedJournalEntryForSharesCharges(final Office office, final String currencyCode,
final CASH_ACCOUNTS_FOR_SHARES accountTypeToBeCredited, final Long shareProductId, final Long shareAccountId,
final String transactionId, final Date transactionDate, final BigDecimal totalAmount,
final List<ChargePaymentDTO> chargePaymentDTOs) {
final Map<GLAccount, BigDecimal> creditDetailsMap = new LinkedHashMap<>();
for (final ChargePaymentDTO chargePaymentDTO : chargePaymentDTOs) {
final GLAccount chargeSpecificAccount = getLinkedGLAccountForShareCharges(shareProductId, accountTypeToBeCredited.getValue(),
chargePaymentDTO.getChargeId());
BigDecimal chargeSpecificAmount = chargePaymentDTO.getAmount();
// adjust net credit amount if the account is already present in the
// map
if (creditDetailsMap.containsKey(chargeSpecificAccount)) {
final BigDecimal existingAmount = creditDetailsMap.get(chargeSpecificAccount);
chargeSpecificAmount = chargeSpecificAmount.add(existingAmount);
}
creditDetailsMap.put(chargeSpecificAccount, chargeSpecificAmount);
}
BigDecimal totalCreditedAmount = BigDecimal.ZERO;
for (final Map.Entry<GLAccount, BigDecimal> entry : creditDetailsMap.entrySet()) {
final GLAccount account = entry.getKey();
final BigDecimal amount = entry.getValue();
totalCreditedAmount = totalCreditedAmount.add(amount);
createDebitJournalEntryForShares(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, 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 Long shareAccountId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false; final boolean manualEntry = false;
@@ -119,6 +119,8 @@ public class CashBasedAccountingProcessorForShares implements AccountingProcesso
BigDecimal amountForJE = amount; BigDecimal amountForJE = amount;
if (chargeAmount != null && chargeAmount.compareTo(BigDecimal.ZERO) == 1) { if (chargeAmount != null && chargeAmount.compareTo(BigDecimal.ZERO) == 1) {
amountForJE = amount.subtract(chargeAmount); amountForJE = amount.subtract(chargeAmount);
/*this.helper.revertCashBasedJournalEntryForSharesCharges(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.INCOME_FROM_FEES,
shareProductId, shareAccountId, transactionId, transactionDate, chargeAmount, feePayments);*/
} }
this.helper.createJournalEntriesForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue(), this.helper.createJournalEntriesForShares(office, currencyCode, CASH_ACCOUNTS_FOR_SHARES.SHARES_SUSPENSE.getValue(),
CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(), shareProductId, paymentTypeId, shareAccountId, transactionId, CASH_ACCOUNTS_FOR_SHARES.SHARES_REFERENCE.getValue(), shareProductId, paymentTypeId, shareAccountId, transactionId,
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.accounting.journalentry.service; package org.apache.fineract.accounting.journalentry.service;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@@ -44,4 +45,6 @@ public interface JournalEntryWritePlatformService {
public String createProvisioningJournalEntries(ProvisioningEntry entry) ; public String createProvisioningJournalEntries(ProvisioningEntry entry) ;
void createJournalEntriesForShares(Map<String, Object> accountingBridgeData); void createJournalEntriesForShares(Map<String, Object> accountingBridgeData);
void revertShareAccountJournalEntries(final ArrayList<Long> transactionId, final Date transactionDate);
} }
@@ -53,6 +53,7 @@ import org.apache.fineract.accounting.journalentry.exception.JournalEntriesNotFo
import org.apache.fineract.accounting.journalentry.exception.JournalEntryInvalidException; import org.apache.fineract.accounting.journalentry.exception.JournalEntryInvalidException;
import org.apache.fineract.accounting.journalentry.exception.JournalEntryInvalidException.GL_JOURNAL_ENTRY_INVALID_REASON; import org.apache.fineract.accounting.journalentry.exception.JournalEntryInvalidException.GL_JOURNAL_ENTRY_INVALID_REASON;
import org.apache.fineract.accounting.journalentry.serialization.JournalEntryCommandFromApiJsonDeserializer; import org.apache.fineract.accounting.journalentry.serialization.JournalEntryCommandFromApiJsonDeserializer;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.accounting.provisioning.domain.LoanProductProvisioningEntry; import org.apache.fineract.accounting.provisioning.domain.LoanProductProvisioningEntry;
import org.apache.fineract.accounting.provisioning.domain.ProvisioningEntry; import org.apache.fineract.accounting.provisioning.domain.ProvisioningEntry;
import org.apache.fineract.accounting.rule.domain.AccountingRule; import org.apache.fineract.accounting.rule.domain.AccountingRule;
@@ -383,6 +384,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
// save the reversal entry // save the reversal entry
this.glJournalEntryRepository.save(reversalJournalEntry); this.glJournalEntryRepository.save(reversalJournalEntry);
journalEntry.setReversalJournalEntry(reversalJournalEntry); journalEntry.setReversalJournalEntry(reversalJournalEntry);
journalEntry.setReversed(true);
// save the updated journal entry // save the updated journal entry
this.glJournalEntryRepository.save(journalEntry); this.glJournalEntryRepository.save(journalEntry);
} }
@@ -516,6 +518,44 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
} }
@Override
public void revertShareAccountJournalEntries(final ArrayList<Long> transactionIds, final Date transactionDate) {
for (Long shareTransactionId : transactionIds) {
String transactionId = AccountingProcessorHelper.SHARE_TRANSACTION_IDENTIFIER + shareTransactionId.longValue();
List<JournalEntry> journalEntries = this.glJournalEntryRepository.findJournalEntries(transactionId,
PortfolioProductType.SHARES.getValue());
if (journalEntries == null || journalEntries.isEmpty()) continue;
final Long officeId = journalEntries.get(0).getOffice().getId();
final String reversalTransactionId = generateTransactionId(officeId);
for (final JournalEntry journalEntry : journalEntries) {
JournalEntry reversalJournalEntry;
String reversalComment = "Reversal entry for Journal Entry with id :" + journalEntry.getId() + " and transaction Id "
+ journalEntry.getTransactionId();
if (journalEntry.isDebitEntry()) {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, Boolean.FALSE,
transactionDate, JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment,
journalEntry.getEntityType(), journalEntry.getEntityId(), journalEntry.getReferenceNumber(),
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction(),
journalEntry.getShareTransactionId());
} else {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, Boolean.FALSE,
transactionDate, JournalEntryType.DEBIT, journalEntry.getAmount(), reversalComment,
journalEntry.getEntityType(), journalEntry.getEntityId(), journalEntry.getReferenceNumber(),
journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(), journalEntry.getClientTransaction(),
journalEntry.getShareTransactionId());
}
// save the reversal entry
this.glJournalEntryRepository.save(reversalJournalEntry);
journalEntry.setReversalJournalEntry(reversalJournalEntry);
journalEntry.setReversed(true);
// save the updated journal entry
this.glJournalEntryRepository.save(journalEntry);
}
}
}
private void validateBusinessRulesForJournalEntries(final JournalEntryCommand command) { private void validateBusinessRulesForJournalEntries(final JournalEntryCommand command) {
/** check if date of Journal entry is valid ***/ /** check if date of Journal entry is valid ***/
final LocalDate entryLocalDate = command.getTransactionDate(); final LocalDate entryLocalDate = command.getTransactionDate();
@@ -290,9 +290,11 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO; final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
final Collection<ChargeData> chargeOptions = null; final Collection<ChargeData> chargeOptions = null;
final Boolean isActive = null; final Boolean isActive = null;
final BigDecimal chargeAmountOrPercentage = BigDecimal.ZERO ;
return new ShareAccountChargeData(shareChargeId, this.id, shareAccountId, this.name, this.currency, this.amount, amountPaid, return new ShareAccountChargeData(shareChargeId, this.id, shareAccountId, this.name, this.currency, this.amount, amountPaid,
amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, this.chargeCalculationType, percentage, amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, this.chargeCalculationType, percentage,
amountPercentageAppliedTo, chargeOptions, isActive); amountPercentageAppliedTo, chargeOptions, isActive, chargeAmountOrPercentage);
} }
public boolean isPenalty() { public boolean isPenalty() {
@@ -34,6 +34,8 @@ public interface SavingsAccountReadPlatformService {
Collection<SavingsAccountData> retrieveActiveForLookup(Long clientId, DepositAccountType depositAccountType); Collection<SavingsAccountData> retrieveActiveForLookup(Long clientId, DepositAccountType depositAccountType);
public Collection<SavingsAccountData> retrieveActiveForLookup(final Long clientId, DepositAccountType depositAccountType, String currencyCode) ;
SavingsAccountData retrieveOne(Long savingsId); SavingsAccountData retrieveOne(Long savingsId);
SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly); SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly);
@@ -132,6 +132,15 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters); return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters);
} }
@Override
public Collection<SavingsAccountData> retrieveActiveForLookup(final Long clientId, DepositAccountType depositAccountType, String currencyCode) {
final StringBuilder sqlBuilder = new StringBuilder("select " + this.savingAccountMapper.schema());
sqlBuilder.append(" where sa.client_id = ? and sa.status_enum = 300 and sa.deposit_type_enum = ? and sa.currency_code = ? ");
final Object[] queryParameters = new Object[] { clientId, depositAccountType.getValue(), currencyCode };
return this.jdbcTemplate.query(sqlBuilder.toString(), this.savingAccountMapper, queryParameters);
}
@Override @Override
public Page<SavingsAccountData> retrieveAll(final SearchParameters searchParameters) { public Page<SavingsAccountData> retrieveAll(final SearchParameters searchParameters) {
@@ -69,7 +69,7 @@ public class ShareAccountChargeData {
final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived,
final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo,
final Collection<ChargeData> chargeOptions, final Boolean isActive) { final Collection<ChargeData> chargeOptions, final Boolean isActive, final BigDecimal chargeamountorpercentage) {
this.id = id; this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.accountId = accountId; this.accountId = accountId;
@@ -84,11 +84,12 @@ public class ShareAccountChargeData {
this.amountWaived = amountWaived; this.amountWaived = amountWaived;
this.amountWrittenOff = amountWrittenOff; this.amountWrittenOff = amountWrittenOff;
this.amountOutstanding = amountOutstanding; this.amountOutstanding = amountOutstanding;
this.amountOrPercentage = getAmountOrPercentage(); this.amountOrPercentage = chargeamountorpercentage;
this.chargeOptions = chargeOptions; this.chargeOptions = chargeOptions;
this.isActive = isActive; this.isActive = isActive;
} }
private BigDecimal getAmountOrPercentage() { private BigDecimal getAmountOrPercentage() {
return (this.chargeCalculationType != null) && (this.chargeCalculationType.getId().intValue() > 1) ? this.percentage : this.amount; return (this.chargeCalculationType != null) && (this.chargeCalculationType.getId().intValue() > 1) ? this.percentage : this.amount;
} }
@@ -61,7 +61,7 @@ public class ShareAccountData implements AccountData {
private ShareAccountSummaryData summary; private ShareAccountSummaryData summary;
private Collection<PurchasedSharesData> purchasedShares; private Collection<ShareAccountTransactionData> purchasedShares;
private Long savingsAccountId; private Long savingsAccountId;
@@ -92,7 +92,7 @@ public class ShareAccountData implements AccountData {
final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId, final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId,
final String productName, final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline, final String productName, final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline,
final CurrencyData currency, final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges, final CurrencyData currency, final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
final Collection<PurchasedSharesData> purchasedSharesData, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum, final Collection<ShareAccountTransactionData> purchasedSharesData, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum,
final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum, Boolean allowdividendsforinactiveclients) { final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum, Boolean allowdividendsforinactiveclients) {
this.id = id; this.id = id;
this.accountNo = accountNo; this.accountNo = accountNo;
@@ -145,7 +145,7 @@ public class ShareAccountData implements AccountData {
final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId, final String savingsAccountNumber, final Long clientId, final String clientName, final Long productId,
final String productName, final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline, final String productName, final ShareAccountStatusEnumData status, final ShareAccountApplicationTimelineData timeline,
final CurrencyData currency, final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges, final CurrencyData currency, final ShareAccountSummaryData summaryData, final Collection<ShareAccountChargeData> charges,
final Collection<PurchasedSharesData> purchasedSharesData, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum, final Collection<ShareAccountTransactionData> purchasedSharesData, final Integer lockinPeriod, final EnumOptionData lockPeriodTypeEnum,
final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum, final Integer minimumActivePeriod, final EnumOptionData minimumActivePeriodTypeEnum,
final Boolean allowDividendCalculationForInactiveClients, final Collection<ProductData> productOptions, final Boolean allowDividendCalculationForInactiveClients, final Collection<ProductData> productOptions,
final Collection<ChargeData> chargeOptions, final Collection<SavingsAccountData> clientSavingsAccounts, final Collection<ChargeData> chargeOptions, final Collection<SavingsAccountData> clientSavingsAccounts,
@@ -200,7 +200,7 @@ public class ShareAccountData implements AccountData {
public static ShareAccountData lookup(final Long id, final String accountNo, final Long clientId, final String clientName) { public static ShareAccountData lookup(final Long id, final String accountNo, final Long clientId, final String clientName) {
String externalId = null; String externalId = null;
ShareAccountSummaryData summaryData = null; ShareAccountSummaryData summaryData = null;
Collection<PurchasedSharesData> purchasedSharesData = null; Collection<ShareAccountTransactionData> purchasedSharesData = null;
GLAccountData suspenseAccount = null; GLAccountData suspenseAccount = null;
Integer lockinPeriod = null; Integer lockinPeriod = null;
EnumOptionData lockPeriodTypeEnum = null; EnumOptionData lockPeriodTypeEnum = null;
@@ -233,7 +233,7 @@ public class ShareAccountData implements AccountData {
return this.id; return this.id;
} }
public Collection<PurchasedSharesData> getPurchasedShares() { public Collection<ShareAccountTransactionData> getPurchasedShares() {
return this.purchasedShares; return this.purchasedShares;
} }
@@ -23,7 +23,8 @@ import java.math.BigDecimal;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
public class PurchasedSharesData { @SuppressWarnings("unused")
public class ShareAccountTransactionData {
private final Long id; private final Long id;
@@ -43,8 +44,11 @@ public class PurchasedSharesData {
private final BigDecimal chargeAmount ; private final BigDecimal chargeAmount ;
public PurchasedSharesData(final Long id, final Long accountId, final LocalDate purchasedDate, final Long numberOfShares, private final BigDecimal amountPaid ;
final BigDecimal purchasedPrice, final EnumOptionData status, final EnumOptionData type, final BigDecimal amount, final BigDecimal chargeAmount) {
public ShareAccountTransactionData(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,
final BigDecimal amountPaid) {
this.id = id; this.id = id;
this.accountId = accountId; this.accountId = accountId;
this.purchasedDate = purchasedDate; this.purchasedDate = purchasedDate;
@@ -54,6 +58,7 @@ public class PurchasedSharesData {
this.type = type; this.type = type;
this.amount = amount ; this.amount = amount ;
this.chargeAmount = chargeAmount ; this.chargeAmount = chargeAmount ;
this.amountPaid = amountPaid ;
} }
public LocalDate getPurchasedDate() { public LocalDate getPurchasedDate() {
@@ -18,10 +18,8 @@
*/ */
package org.apache.fineract.portfolio.shareaccounts.domain; package org.apache.fineract.portfolio.shareaccounts.domain;
import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.Set; import java.util.Set;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
@@ -261,6 +259,17 @@ public class ShareAccount extends AbstractPersistable<Long> {
return true; return true;
} }
public void addTransaction(final ShareAccountTransaction transaction) {
transaction.setShareAccount(this);
if(transaction.isPendingForApprovalTransaction()) {
this.totalSharesPending += transaction.getTotalShares() ;
}else if(transaction.isPurchasTransaction()) {
this.totalSharesApproved += transaction.getTotalShares() ;
}
this.shareAccountTransactions.add(transaction);
}
public boolean setAllowDividendCalculationForInactiveClients(Boolean allowDividendCalculationForInactiveClients) { public boolean setAllowDividendCalculationForInactiveClients(Boolean allowDividendCalculationForInactiveClients) {
boolean returnValue = false; boolean returnValue = false;
if (this.allowDividendCalculationForInactiveClients == null) { if (this.allowDividendCalculationForInactiveClients == null) {
@@ -346,7 +355,7 @@ public class ShareAccount extends AbstractPersistable<Long> {
public Set<ShareAccountTransaction> getChargeTransactions() { public Set<ShareAccountTransaction> getChargeTransactions() {
Set<ShareAccountTransaction> chargeTransactions = new HashSet<>(); Set<ShareAccountTransaction> chargeTransactions = new HashSet<>();
for (ShareAccountTransaction transaction : this.shareAccountTransactions) { for (ShareAccountTransaction transaction : this.shareAccountTransactions) {
if (transaction.isChargeTransaction()) { if (transaction.isActive() && transaction.isChargeTransaction()) {
chargeTransactions.add(transaction); chargeTransactions.add(transaction);
} }
} }
@@ -380,18 +389,6 @@ public class ShareAccount extends AbstractPersistable<Long> {
this.charges.add(charge); 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) { public void approve(final Date approvedDate, final AppUser approvedUser) {
this.approvedDate = approvedDate; this.approvedDate = approvedDate;
this.approvedBy = approvedUser; this.approvedBy = approvedUser;
@@ -418,10 +415,14 @@ public class ShareAccount extends AbstractPersistable<Long> {
this.closedDate = null; this.closedDate = null;
this.closedBy = null; this.closedBy = null;
this.totalSharesApproved = null; this.totalSharesApproved = null;
Iterator<ShareAccountTransaction> iter = this.shareAccountTransactions.iterator(); Long tempTotalShares = new Long(0);
ShareAccountTransaction share = iter.next(); for (ShareAccountTransaction transaction : this.shareAccountTransactions) {
share.undoApprove(); if(transaction.isPurchasTransaction()) {
this.totalSharesPending = share.getTotalShares(); transaction.undoApprove();
tempTotalShares += transaction.getTotalShares() ;
}
}
this.totalSharesPending = tempTotalShares;
} }
public void reject(final Date rejectedDate, final AppUser rejectedUser) { public void reject(final Date rejectedDate, final AppUser rejectedUser) {
@@ -430,9 +431,11 @@ public class ShareAccount extends AbstractPersistable<Long> {
this.status = ShareAccountStatusType.REJECTED.getValue(); this.status = ShareAccountStatusType.REJECTED.getValue();
this.totalSharesPending = null; this.totalSharesPending = null;
this.totalSharesApproved = null; this.totalSharesApproved = null;
Iterator<ShareAccountTransaction> iter = this.shareAccountTransactions.iterator(); for (ShareAccountTransaction transaction : this.shareAccountTransactions) {
ShareAccountTransaction share = iter.next(); if(transaction.isPurchasTransaction()) {
share.reject(); transaction.reject();
}
}
} }
public void close(final Date closedDate, final AppUser closedBy) { public void close(final Date closedDate, final AppUser closedBy) {
@@ -469,7 +472,7 @@ public class ShareAccount extends AbstractPersistable<Long> {
public Set<ShareAccountTransaction> getPendingForApprovalSharePurchaseTransactions() { public Set<ShareAccountTransaction> getPendingForApprovalSharePurchaseTransactions() {
Set<ShareAccountTransaction> purchaseTransactions = new HashSet<>(); Set<ShareAccountTransaction> purchaseTransactions = new HashSet<>();
for (ShareAccountTransaction transaction : this.shareAccountTransactions) { for (ShareAccountTransaction transaction : this.shareAccountTransactions) {
if (transaction.isPendingForApprovalTransaction()) { if (transaction.isActive() && transaction.isPendingForApprovalTransaction()) {
purchaseTransactions.add(transaction); purchaseTransactions.add(transaction);
} }
} }
@@ -523,4 +526,30 @@ public class ShareAccount extends AbstractPersistable<Long> {
public Date getApprovedDate() { public Date getApprovedDate() {
return this.approvedDate ; return this.approvedDate ;
} }
public void removeTransactions() {
for(ShareAccountTransaction transaction: this.shareAccountTransactions) {
transaction.setActive(false);
}
this.totalSharesApproved = new Long(0) ;
this.totalSharesPending = new Long(0) ;
}
public void removeCharges() {
for(ShareAccountCharge charge: this.charges) {
charge.setActive(false);
}
}
public void addCharges(Set<ShareAccountCharge> charges) {
this.charges.addAll(charges) ;
}
public Integer getLockinPeriodFrequency() {
return this.lockinPeriodFrequency ;
}
public PeriodFrequencyType getLockinPeriodFrequencyType() {
return this.lockinPeriodFrequencyType ;
}
} }
@@ -83,7 +83,7 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
private boolean waived = false; private boolean waived = false;
@Column(name = "is_active", nullable = false) @Column(name = "is_active", nullable = false)
private boolean status = true; private boolean active = true;
@Column(name = "charge_amount_or_percentage") @Column(name = "charge_amount_or_percentage")
private BigDecimal amountOrPercentage; private BigDecimal amountOrPercentage;
@@ -118,13 +118,13 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
populateDerivedFields(transactionAmount, chargeAmount); populateDerivedFields(transactionAmount, chargeAmount);
this.paid = determineIfFullyPaid(); this.paid = determineIfFullyPaid();
this.status = status; this.active = status;
} }
public void populateDerivedFields(final BigDecimal transactionAmount, final BigDecimal chargeAmount) { private void populateDerivedFields(final BigDecimal transactionAmount, final BigDecimal chargeAmount) {
this.amountOrPercentage = chargeAmount ;
if (this.chargeCalculation.equals(ChargeCalculationType.FLAT.getValue())) { if (this.chargeCalculation.equals(ChargeCalculationType.FLAT.getValue())) {
this.percentage = null; this.percentage = null;
this.amountOrPercentage = chargeAmount ;
this.amount = BigDecimal.ZERO; this.amount = BigDecimal.ZERO;
this.amountPercentageAppliedTo = null; this.amountPercentageAppliedTo = null;
this.amountPaid = null; this.amountPaid = null;
@@ -149,6 +149,7 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
} }
public void resetToOriginal(final MonetaryCurrency currency) { public void resetToOriginal(final MonetaryCurrency currency) {
this.amount = BigDecimal.ZERO ;
this.amountPaid = BigDecimal.ZERO; this.amountPaid = BigDecimal.ZERO;
this.amountWaived = BigDecimal.ZERO; this.amountWaived = BigDecimal.ZERO;
this.amountWrittenOff = BigDecimal.ZERO; this.amountWrittenOff = BigDecimal.ZERO;
@@ -163,7 +164,7 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
this.amountPaid = amountPaid.getAmount(); this.amountPaid = amountPaid.getAmount();
this.amountOutstanding = calculateAmountOutstanding(currency); this.amountOutstanding = calculateAmountOutstanding(currency);
this.paid = false; this.paid = false;
this.status = true; this.active = true;
} }
public Money waive(final MonetaryCurrency currency) { public Money waive(final MonetaryCurrency currency) {
@@ -181,7 +182,7 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
this.amountWaived = amountWaived.getAmount(); this.amountWaived = amountWaived.getAmount();
this.amountOutstanding = calculateAmountOutstanding(currency); this.amountOutstanding = calculateAmountOutstanding(currency);
this.waived = false; this.waived = false;
this.status = true; this.active = true;
} }
public Money pay(final MonetaryCurrency currency, final Money amountPaid) { public Money pay(final MonetaryCurrency currency, final Money amountPaid) {
@@ -203,17 +204,8 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
this.shareAccount = shareAccount; this.shareAccount = shareAccount;
} }
public void update(final BigDecimal amount) { public void update(final BigDecimal transactionAmount, final BigDecimal amount) {
final BigDecimal transactionAmount = BigDecimal.ZERO; populateDerivedFields(transactionAmount, amount);
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) { private boolean isGreaterThanZero(final BigDecimal value) {
@@ -360,7 +352,7 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
} }
public boolean isActive() { public boolean isActive() {
return this.status; return this.active;
} }
public boolean isNotActive() { public boolean isNotActive() {
@@ -420,6 +412,10 @@ public class ShareAccountCharge extends AbstractPersistable<Long> {
return toReturnAmount; return toReturnAmount;
} }
public void setActive(boolean active) {
this.active = active ;
}
@Override @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
if (obj == null) { return false; } if (obj == null) { return false; }
@@ -74,4 +74,9 @@ public class ShareAccountChargePaidBy extends AbstractPersistable<Long>{
public Long getShareChargeId() { public Long getShareChargeId() {
return this.shareAccountCharge.getId() ; return this.shareAccountCharge.getId() ;
} }
public void reset() {
this.shareAccountTransaction = null ;
this.shareAccountCharge = null ;
}
} }
@@ -58,8 +58,11 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
@Column(name = "amount") @Column(name = "amount")
private BigDecimal amount ; private BigDecimal amount ;
@Column(name = "amount_paid")
private BigDecimal amountPaid ;
@Column(name = "charge_amount") @Column(name = "charge_amount")
private BigDecimal totalChargeAmount ; private BigDecimal chargeAmount ;
@Column(name = "status_enum", nullable = true) @Column(name = "status_enum", nullable = true)
private Integer status; private Integer status;
@@ -67,6 +70,9 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
@Column(name = "type_enum", nullable = true) @Column(name = "type_enum", nullable = true)
private Integer type ; private Integer type ;
@Column(name = "is_active", nullable = false)
private boolean active = true ;
@LazyCollection(LazyCollectionOption.FALSE) @LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "shareAccountTransaction", orphanRemoval = true) @OneToMany(cascade = CascadeType.ALL, mappedBy = "shareAccountTransaction", orphanRemoval = true)
private final Set<ShareAccountChargePaidBy> shareAccountChargesPaid = new HashSet<>(); private final Set<ShareAccountChargePaidBy> shareAccountChargesPaid = new HashSet<>();
@@ -86,24 +92,27 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
this.status = PurchasedSharesStatusType.APPLIED.getValue(); this.status = PurchasedSharesStatusType.APPLIED.getValue();
this.type = PurchasedSharesStatusType.PURCHASED.getValue() ; this.type = PurchasedSharesStatusType.PURCHASED.getValue() ;
this.amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ; this.amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ;
this.amountPaid = new BigDecimal(this.amount.doubleValue()) ;
} }
private ShareAccountTransaction(final Date transactionDate, final Long totalShares, final BigDecimal shareValue, private ShareAccountTransaction(final Date transactionDate, final Long totalShares, final BigDecimal shareValue,
final Integer status, final Integer type, final BigDecimal amount, final BigDecimal chargeAmount) { final Integer status, final Integer type, final BigDecimal amount, final BigDecimal chargeAmount, final BigDecimal amountPaid) {
this.transactionDate = transactionDate; this.transactionDate = transactionDate;
this.totalShares = totalShares; this.totalShares = totalShares;
this.shareValue = shareValue; this.shareValue = shareValue;
this.status = status ; this.status = status ;
this.type = type ; this.type = type ;
this.amount = amount ; this.amount = amount ;
this.totalChargeAmount = chargeAmount ; this.chargeAmount = chargeAmount ;
this.amountPaid = amountPaid ;
} }
public static ShareAccountTransaction createRedeemTransaction(final Date transactionDate, final Long totalShares, final BigDecimal shareValue) { public static ShareAccountTransaction createRedeemTransaction(final Date transactionDate, final Long totalShares, final BigDecimal shareValue) {
final Integer status = PurchasedSharesStatusType.APPROVED.getValue() ; final Integer status = PurchasedSharesStatusType.APPROVED.getValue() ;
final Integer type = PurchasedSharesStatusType.REDEEMED.getValue() ; final Integer type = PurchasedSharesStatusType.REDEEMED.getValue() ;
final BigDecimal amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ; final BigDecimal amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ;
return new ShareAccountTransaction(transactionDate, totalShares, shareValue, status, type, amount, null) ; BigDecimal amountPaid = new BigDecimal(amount.doubleValue()) ;
return new ShareAccountTransaction(transactionDate, totalShares, shareValue, status, type, amount, null, amountPaid) ;
} }
public static ShareAccountTransaction createChargeTransaction(final Date transactionDate, final ShareAccountCharge charge) { public static ShareAccountTransaction createChargeTransaction(final Date transactionDate, final ShareAccountCharge charge) {
@@ -113,7 +122,8 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
final Integer type = PurchasedSharesStatusType.CHARGE_PAYMENT.getValue() ; final Integer type = PurchasedSharesStatusType.CHARGE_PAYMENT.getValue() ;
BigDecimal amount = charge.percentageOrAmount() ; BigDecimal amount = charge.percentageOrAmount() ;
BigDecimal chargeAmount = null ; BigDecimal chargeAmount = null ;
return new ShareAccountTransaction(transactionDate, totalShares, unitPrice, status, type, amount, chargeAmount) ; BigDecimal amountPaid = null ;
return new ShareAccountTransaction(transactionDate, totalShares, unitPrice, status, type, amount, chargeAmount, amountPaid) ;
} }
public Date getPurchasedDate() { public Date getPurchasedDate() {
@@ -133,6 +143,7 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
this.totalShares = totalShares; this.totalShares = totalShares;
this.shareValue = shareValue; this.shareValue = shareValue;
this.amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ; this.amount = shareValue.multiply(BigDecimal.valueOf(totalShares)) ;
this.chargeAmount = BigDecimal.ZERO ;
this.status = PurchasedSharesStatusType.APPLIED.getValue(); this.status = PurchasedSharesStatusType.APPLIED.getValue();
} }
@@ -146,8 +157,8 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
public void reject() { public void reject() {
this.status = PurchasedSharesStatusType.REJECTED.getValue() ; this.status = PurchasedSharesStatusType.REJECTED.getValue() ;
if(this.totalChargeAmount != null) { if(this.chargeAmount != null) {
this.amount = this.amount.subtract(totalChargeAmount) ; this.amount = this.amount.subtract(chargeAmount) ;
} }
} }
@@ -167,6 +178,10 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
return this.status.equals(PurchasedSharesStatusType.APPROVED.getValue()) && this.type.equals(PurchasedSharesStatusType.CHARGE_PAYMENT.getValue()) ; return this.status.equals(PurchasedSharesStatusType.APPROVED.getValue()) && this.type.equals(PurchasedSharesStatusType.CHARGE_PAYMENT.getValue()) ;
} }
public boolean isPurchaseRejectedTransaction() {
return this.status.equals(PurchasedSharesStatusType.REJECTED.getValue()) && this.type.equals(PurchasedSharesStatusType.PURCHASED.getValue()) ;
}
public void addShareAccountChargePaidBy(final ShareAccountChargePaidBy chargePaidBy) { public void addShareAccountChargePaidBy(final ShareAccountChargePaidBy chargePaidBy) {
this.shareAccountChargesPaid.add(chargePaidBy) ; this.shareAccountChargesPaid.add(chargePaidBy) ;
} }
@@ -176,17 +191,17 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
} }
public BigDecimal chargeAmount() { public BigDecimal chargeAmount() {
return this.totalChargeAmount ; return this.chargeAmount ;
} }
public void updateChargeAmount(BigDecimal totalChargeAmount) { public void updateChargeAmount(BigDecimal totalChargeAmount) {
this.amount = this.amount.add(totalChargeAmount); this.amount = this.amount.add(totalChargeAmount);
this.totalChargeAmount = totalChargeAmount ; this.chargeAmount = totalChargeAmount ;
} }
public void deductChargesFromTotalAmount(BigDecimal totalChargeAmount) { public void deductChargesFromTotalAmount(BigDecimal totalChargeAmount) {
this.amount = this.amount.subtract(totalChargeAmount); this.amount = this.amount.subtract(totalChargeAmount);
this.totalChargeAmount = totalChargeAmount ; this.chargeAmount = totalChargeAmount ;
} }
public Set<ShareAccountChargePaidBy> getChargesPaidBy() { public Set<ShareAccountChargePaidBy> getChargesPaidBy() {
@@ -201,9 +216,32 @@ public class ShareAccountTransaction extends AbstractPersistable<Long> {
return this.type ; return this.type ;
} }
public void adjustRedeemAmount() { public void updateAmountPaid(final BigDecimal amountPaid) {
if(this.totalChargeAmount != null) { this.amountPaid = amountPaid ;
this.amount = this.amount.subtract(totalChargeAmount) ; }
public void addAmountPaid(final BigDecimal amountPaid) {
if(isRedeemTransaction()) {
this.amountPaid = this.amountPaid.subtract(amountPaid) ;
}else if(isPurchasTransaction() /*|| isPurchaseRejectedTransaction()*/) {
this.amountPaid = this.amountPaid.add(amountPaid) ;
} }
} }
public void resetAmountPaid() {
this.amountPaid = BigDecimal.ZERO ;
}
public void setActive(boolean active) {
this.active = active ;
if(!this.active) {
//this.shareAccountChargesPaid.clear();
}
}
public boolean isActive() {
return this.active ;
}
} }
@@ -49,7 +49,6 @@ import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException; 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.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper; import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount; import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
@@ -87,9 +86,8 @@ public class ShareAccountDataSerializer {
@Autowired @Autowired
public ShareAccountDataSerializer(final PlatformSecurityContext platformSecurityContext, final FromJsonHelper fromApiJsonHelper, public ShareAccountDataSerializer(final PlatformSecurityContext platformSecurityContext, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepository, final ChargeRepositoryWrapper chargeRepository, final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper,
final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper, final ClientRepositoryWrapper clientRepositoryWrapper, final ClientRepositoryWrapper clientRepositoryWrapper, final ShareProductRepositoryWrapper shareProductRepository) {
final ShareProductRepositoryWrapper shareProductRepository) {
this.platformSecurityContext = platformSecurityContext; this.platformSecurityContext = platformSecurityContext;
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
this.chargeRepository = chargeRepository; this.chargeRepository = chargeRepository;
@@ -117,7 +115,7 @@ public class ShareAccountDataSerializer {
baseDataValidator.reset().parameter(ShareAccountApiConstants.submitteddate_paramname).value(submittedDate).notNull(); baseDataValidator.reset().parameter(ShareAccountApiConstants.submitteddate_paramname).value(submittedDate).notNull();
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element); final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull(); // baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull();
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element); Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull() baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
@@ -126,24 +124,15 @@ public class ShareAccountDataSerializer {
final Long requestedShares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element); final Long requestedShares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).notNull() baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).notNull()
.longGreaterThanZero(); .longGreaterThanZero();
boolean allowed = shareProduct.isSharesAllowed(requestedShares) ; if(requestedShares < shareProduct.getMinimumClientShares()) {
if(!allowed) { baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares)
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).failWithCode("differ.from.productdefinition", "Out of range");; .failWithCode("client.can.not.purchase.shares.lessthan.product.definition", "Client can not purchase shares less than product definition");
} }
/*Long subscribedShares = shareProduct.getSubscribedShares() ; if(requestedShares > shareProduct.getMaximumClientShares()) {
if(subscribedShares == null) subscribedShares = new Long(0) ; baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares)
Long totalShares = shareProduct.getTotalShares() ; .failWithCode("client.can.not.purchase.shares.morethan.product.definition", "Client can not purchase shares more than product definition");
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); LocalDate applicationDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.applicationdate_param, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.applicationdate_param).value(applicationDate).notNull(); baseDataValidator.reset().parameter(ShareAccountApiConstants.applicationdate_param).value(applicationDate).notNull();
@@ -185,7 +174,7 @@ public class ShareAccountDataSerializer {
String accountNo = null; String accountNo = null;
Long approvedShares = null; Long approvedShares = null;
Long pendingShares = requestedShares; Long pendingShares = requestedShares;
BigDecimal unitPrice = shareProduct.deriveMarketPrice(applicationDate.toDate()) ; BigDecimal unitPrice = shareProduct.deriveMarketPrice(applicationDate.toDate());
ShareAccountTransaction transaction = new ShareAccountTransaction(applicationDate.toDate(), requestedShares, unitPrice); ShareAccountTransaction transaction = new ShareAccountTransaction(applicationDate.toDate(), requestedShares, unitPrice);
Set<ShareAccountTransaction> sharesPurchased = new HashSet<>(); Set<ShareAccountTransaction> sharesPurchased = new HashSet<>();
sharesPurchased.add(transaction); sharesPurchased.add(transaction);
@@ -204,17 +193,17 @@ public class ShareAccountDataSerializer {
charge.update(account); charge.update(account);
} }
} }
createChargeTransaction(account, transaction); createChargeTransaction(account);
return account; return account;
} }
private void createChargeTransaction(ShareAccount account, final ShareAccountTransaction transaction) { private void createChargeTransaction(ShareAccount account) {
BigDecimal totalChargeAmount = BigDecimal.ZERO; BigDecimal totalChargeAmount = BigDecimal.ZERO;
Set<ShareAccountCharge> charges = account.getCharges(); Set<ShareAccountCharge> charges = account.getCharges();
Date currentDate = DateUtils.getLocalDateOfTenant().toDate(); Date currentDate = DateUtils.getLocalDateOfTenant().toDate();
for (ShareAccountCharge charge : charges) { for (ShareAccountCharge charge : charges) {
if (charge.isShareAccountActivation()) { if (charge.isActive() && charge.isShareAccountActivation()) {
charge.deriveChargeAmount(totalChargeAmount) ; charge.deriveChargeAmount(totalChargeAmount);
ShareAccountTransaction chargeTransaction = ShareAccountTransaction.createChargeTransaction(currentDate, charge); ShareAccountTransaction chargeTransaction = ShareAccountTransaction.createChargeTransaction(currentDate, charge);
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(chargeTransaction, charge, charge.percentageOrAmount()); ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(chargeTransaction, charge, charge.percentageOrAmount());
chargeTransaction.addShareAccountChargePaidBy(paidBy); chargeTransaction.addShareAccountChargePaidBy(paidBy);
@@ -225,7 +214,7 @@ public class ShareAccountDataSerializer {
Set<ShareAccountTransaction> pendingApprovalTransaction = account.getPendingForApprovalSharePurchaseTransactions(); Set<ShareAccountTransaction> pendingApprovalTransaction = account.getPendingForApprovalSharePurchaseTransactions();
for (ShareAccountTransaction pending : pendingApprovalTransaction) { for (ShareAccountTransaction pending : pendingApprovalTransaction) {
for (ShareAccountCharge charge : charges) { for (ShareAccountCharge charge : charges) {
if (charge.isSharesPurchaseCharge()) { if (charge.isActive() && charge.isSharesPurchaseCharge()) {
BigDecimal amount = charge.deriveChargeAmount(pending.amount()); BigDecimal amount = charge.deriveChargeAmount(pending.amount());
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(pending, charge, amount); ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(pending, charge, amount);
pending.addShareAccountChargePaidBy(paidBy); pending.addShareAccountChargePaidBy(paidBy);
@@ -245,7 +234,7 @@ public class ShareAccountDataSerializer {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount"); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount");
JsonElement element = jsonCommand.parsedJson(); JsonElement element = jsonCommand.parsedJson();
ShareProduct shareProduct = account.getShareProduct() ; ShareProduct shareProduct = account.getShareProduct();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject()); final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.productid_paramname, element)) { if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.productid_paramname, element)) {
final Long productId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.productid_paramname, element); final Long productId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.productid_paramname, element);
@@ -266,7 +255,7 @@ public class ShareAccountDataSerializer {
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.externalid_paramname, element)) { if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.externalid_paramname, element)) {
final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element); final String externalId = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.externalid_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull(); // baseDataValidator.reset().parameter(ShareAccountApiConstants.externalid_paramname).value(externalId).notNull();
if (account.setExternalId(externalId)) { if (account.setExternalId(externalId)) {
actualChanges.put(ShareAccountApiConstants.externalid_paramname, externalId); actualChanges.put(ShareAccountApiConstants.externalid_paramname, externalId);
} }
@@ -274,25 +263,51 @@ public class ShareAccountDataSerializer {
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.savingsaccountid_paramname, element)) { if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.savingsaccountid_paramname, element)) {
Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element); Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.savingsaccountid_paramname, element);
SavingsAccount savingsAccount = this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsAccountId); baseDataValidator.reset().parameter(ShareAccountApiConstants.savingsaccountid_paramname).value(savingsAccountId).notNull()
if (account.setSavingsAccount(savingsAccount)) { .longGreaterThanZero();
actualChanges.put(ShareAccountApiConstants.savingsaccountid_paramname, savingsAccount); if(savingsAccountId != null) {
SavingsAccount savingsAccount = this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(savingsAccountId);
if (account.setSavingsAccount(savingsAccount)) {
actualChanges.put(ShareAccountApiConstants.savingsaccountid_paramname, savingsAccount.getId());
}
} }
} }
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)
|| this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions();
List<Long> reveralIds = new ArrayList<>();
for (ShareAccountTransaction transaction : transactions) {
reveralIds.add(transaction.getId());
}
actualChanges.put("reversalIds", reveralIds);
account.removeTransactions();
account.removeCharges();
}
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) { if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) {
Long requestedShares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element); Long requestedShares = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element);
/*BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareAccountApiConstants.purchasedprice_paramname, /*
element, locale);*/ * BigDecimal unitPrice =
* this.fromApiJsonHelper.extractBigDecimalNamed
* (ShareAccountApiConstants.purchasedprice_paramname, element,
* locale);
*/
Date applicationDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.applicationdate_param, element) Date applicationDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.applicationdate_param, element)
.toDate(); .toDate();
BigDecimal unitPrice = shareProduct.deriveMarketPrice(applicationDate) ; BigDecimal unitPrice = shareProduct.deriveMarketPrice(applicationDate);
ShareAccountTransaction purchased = new ShareAccountTransaction(applicationDate, requestedShares, unitPrice); ShareAccountTransaction transaction = new ShareAccountTransaction(applicationDate, requestedShares, unitPrice);
account.updateRequestedShares(purchased); account.addTransaction(transaction);
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, purchased); actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, "Transaction");
boolean allowed = shareProduct.isSharesAllowed(requestedShares) ;
if(!allowed) { if(requestedShares < shareProduct.getMinimumClientShares()) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).failWithCode("differ.from.productdefinition", "Out of range");; baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares)
.failWithCode("client.can.not.purchase.shares.lessthan.product.definition", "Client can not purchase shares less than product definition");
}
if(requestedShares > shareProduct.getMaximumClientShares()) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares)
.failWithCode("client.can.not.purchase.shares.morethan.product.definition", "Client can not purchase shares more than product definition");
} }
} }
@@ -340,13 +355,19 @@ public class ShareAccountDataSerializer {
} }
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) { if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
shareProduct = account.getShareProduct(); shareProduct = account.getShareProduct();
Set<ShareAccountCharge> updatedCharges = assembleListOfAccountChargesforUpdate(account, element, shareProduct.getCurrency() final MonetaryCurrency currency = shareProduct.getCurrency();
.getCode()); Set<ShareAccountCharge> charges = assembleListOfAccountCharges(element, currency.getCode());
if (!updatedCharges.isEmpty()) { if (charges != null) {
actualChanges.put(ShareAccountApiConstants.charges_paramname, new HashSet<ShareAccountCharge>()); for (ShareAccountCharge charge : charges) {
charge.update(account);
}
account.addCharges(charges);
if (!charges.isEmpty()) {
actualChanges.put(ShareAccountApiConstants.charges_paramname, new HashSet<ShareAccountCharge>());
}
} }
} }
createChargeTransaction(account);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
return actualChanges; return actualChanges;
} }
@@ -366,8 +387,16 @@ public class ShareAccountDataSerializer {
final DateTimeFormatter formatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(jsonCommand.extractLocale()); final DateTimeFormatter formatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(jsonCommand.extractLocale());
final String submittalDateAsString = formatter.print(submittalDate); final String submittalDateAsString = formatter.print(submittalDate);
baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).value(submittalDateAsString) baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).value(submittalDateAsString)
.failWithCodeNoParameterAddedToErrorCode("cannot.be.before.submittal.date"); .failWithCodeNoParameterAddedToErrorCode("approved.date.cannot.be.before.submitted.date");
} }
Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions() ;
for(ShareAccountTransaction transaction: transactions) {
if(transaction.isActive() && transaction.isPendingForApprovalTransaction()) {
validateTotalSubsribedShares(account, transaction, baseDataValidator) ;
}
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
AppUser approvedUser = this.platformSecurityContext.authenticatedUser(); AppUser approvedUser = this.platformSecurityContext.authenticatedUser();
@@ -377,19 +406,38 @@ public class ShareAccountDataSerializer {
return actualChanges; return actualChanges;
} }
private void validateTotalSubsribedShares(final ShareAccount account, final ShareAccountTransaction transaction, final DataValidatorBuilder baseDataValidator) {
Long totalSubsribedShares = account.getShareProduct().getSubscribedShares() ;
Long requested = new Long(0) ;
if(transaction.isActive() && transaction.isPendingForApprovalTransaction()) {
requested +=transaction.getTotalShares() ;
}
Long totalSharesIssuable = account.getShareProduct().getSharesIssued() ;
if(totalSharesIssuable == null) totalSharesIssuable = account.getShareProduct().getTotalShares() ;
if(totalSubsribedShares == null) totalSubsribedShares = new Long(0) ;
if((totalSubsribedShares+requested) > totalSharesIssuable) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requested)
.failWithCodeNoParameterAddedToErrorCode("shares.requested.can.not.be.approved.exceeding.totalshares.issuable");
}
}
private void updateTotalChargeDerived(final ShareAccount shareAccount) { private void updateTotalChargeDerived(final ShareAccount shareAccount) {
// Set<ShareAccountCharge> charges = shareAccount.getCharges() ; // Set<ShareAccountCharge> charges = shareAccount.getCharges() ;
Set<ShareAccountTransaction> transactions = shareAccount.getShareAccountTransactions(); Set<ShareAccountTransaction> transactions = shareAccount.getShareAccountTransactions();
for (ShareAccountTransaction transaction : transactions) { for (ShareAccountTransaction transaction : transactions) {
Set<ShareAccountChargePaidBy> paidBySet = transaction.getChargesPaidBy(); if (transaction.isActive()) {
if (paidBySet != null && !paidBySet.isEmpty()) { Set<ShareAccountChargePaidBy> paidBySet = transaction.getChargesPaidBy();
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) { if (paidBySet != null && !paidBySet.isEmpty()) {
ShareAccountCharge charge = chargePaidBy.getCharge(); for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
if (charge.isSharesPurchaseCharge()) { ShareAccountCharge charge = chargePaidBy.getCharge();
Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount()); if (charge.isActive() && charge.isSharesPurchaseCharge()) {
charge.updatePaidAmountBy(money); Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money);
}
} }
} }
if (!transaction.isChargeTransaction()) {
transaction.addAmountPaid(transaction.chargeAmount());
}
} }
} }
} }
@@ -397,19 +445,54 @@ public class ShareAccountDataSerializer {
public Map<String, Object> validateAndUndoApprove(JsonCommand jsonCommand, ShareAccount account) { public Map<String, Object> validateAndUndoApprove(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>(); Map<String, Object> actualChanges = new HashMap<>();
if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); } if (StringUtils.isBlank(jsonCommand.json())) { throw new InvalidJsonException(); }
//final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType(); // final Type typeOfMap = new TypeToken<Map<String, Object>>()
//this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, jsonCommand.json(), ShareAccountApiConstants.activateParameters); // {}.getType();
//final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); // this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap,
//final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("sharesaccount"); // jsonCommand.json(), ShareAccountApiConstants.activateParameters);
//JsonElement element = jsonCommand.parsedJson(); // final List<ApiParameterError> dataValidationErrors = new
//String notes = this.fromApiJsonHelper.extractStringNamed(ShareAccountApiConstants.note_paramname, element); // 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) // baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).validateDateAfter(account.get)
//AppUser approvedUser = this.platformSecurityContext.authenticatedUser(); // AppUser approvedUser =
// this.platformSecurityContext.authenticatedUser();
account.undoApprove(); account.undoApprove();
updateTotalChargeDerivedForUndoApproval(account) ;
actualChanges.put(ShareAccountApiConstants.charges_paramname, Boolean.TRUE); actualChanges.put(ShareAccountApiConstants.charges_paramname, Boolean.TRUE);
return actualChanges; return actualChanges;
} }
private void updateTotalChargeDerivedForUndoApproval(final ShareAccount shareAccount) {
Set<ShareAccountTransaction> purchaseTransactions = shareAccount.getPendingForApprovalSharePurchaseTransactions() ;
//we will have only 1 purchase transaction. Take that
ShareAccountTransaction purchaseTransaction = null ;
for (ShareAccountTransaction transaction : purchaseTransactions) {
purchaseTransaction = transaction ;
}
BigDecimal transactionAmount = BigDecimal.ZERO ;
if(purchaseTransaction != null) {
transactionAmount = purchaseTransaction.amount().subtract(purchaseTransaction.chargeAmount()) ;
}
Set<ShareAccountCharge> charges = shareAccount.getCharges() ;
for(ShareAccountCharge charge: charges) {
if(charge.isActive() && charge.isSharesPurchaseCharge()) {
charge.update(transactionAmount, charge.percentageOrAmount()) ;
charge.deriveChargeAmount(transactionAmount);
}
}
Set<ShareAccountTransaction> transactions = shareAccount.getShareAccountTransactions();
for (ShareAccountTransaction transaction : transactions) {
if (transaction.isActive()) {
if (!transaction.isChargeTransaction()) {
transaction.resetAmountPaid();
}
}
}
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
public Map<String, Object> validateAndReject(JsonCommand jsonCommand, ShareAccount account) { public Map<String, Object> validateAndReject(JsonCommand jsonCommand, ShareAccount account) {
Map<String, Object> actualChanges = new HashMap<>(); Map<String, Object> actualChanges = new HashMap<>();
@@ -449,10 +532,17 @@ public class ShareAccountDataSerializer {
private void handlechargesOnActivation(final ShareAccount account) { private void handlechargesOnActivation(final ShareAccount account) {
Set<ShareAccountCharge> charges = account.getCharges(); Set<ShareAccountCharge> charges = account.getCharges();
for (ShareAccountCharge charge : charges) { for (ShareAccountCharge charge : charges) {
if (charge.isShareAccountActivation()) { if (charge.isActive() && charge.isShareAccountActivation()) {
charge.markAsFullyPaid(); charge.markAsFullyPaid();
} }
} }
Set<ShareAccountTransaction> transactions = account.getChargeTransactions();
for (ShareAccountTransaction transaction : transactions) {
if (transaction.isChargeTransaction()) {
transaction.updateAmountPaid(transaction.amount());
}
}
} }
private Set<ShareAccountCharge> assembleListOfAccountCharges(final JsonElement element, final String currencyCode) { private Set<ShareAccountCharge> assembleListOfAccountCharges(final JsonElement element, final String currencyCode) {
@@ -484,49 +574,6 @@ public class ShareAccountDataSerializer {
return charges; return charges;
} }
private Set<ShareAccountCharge> assembleListOfAccountChargesforUpdate(final ShareAccount account, final JsonElement element,
final String currencyCode) {
Set<ShareAccountCharge> updated = new HashSet<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.charges_paramname, element)) {
JsonArray chargesArray = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.charges_paramname, element);
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has("id")) {
final Long id = jsonObject.get("id").getAsLong();
final Long chargeId = jsonObject.get("chargeId").getAsLong();
BigDecimal amount = jsonObject.get("amount").getAsBigDecimal();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
if (!currencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Share Account must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.share.account", errorMessage);
}
ShareAccountCharge updatedCharge = account.updateShareCharge(id, chargeId, amount);
updated.add(updatedCharge);
} else {
if (jsonObject.has("chargeId")) {
final Long id = jsonObject.get("chargeId").getAsLong();
BigDecimal amount = jsonObject.get("amount").getAsBigDecimal();
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id);
if (!currencyCode.equals(charge.getCurrencyCode())) {
final String errorMessage = "Charge and Share Account must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.share.account", errorMessage);
}
ChargeTimeType chargeTime = ChargeTimeType.fromInt(charge.getChargeTimeType());
ChargeCalculationType chargeCalculation = ChargeCalculationType.fromInt(charge.getChargeCalculation());
Boolean status = Boolean.TRUE;
ShareAccountCharge accountCharge = ShareAccountCharge.createNewWithoutShareAccount(charge, amount, chargeTime,
chargeCalculation, status);
account.addShareAccountCharge(accountCharge);
updated.add(accountCharge);
}
}
}
}
}
return updated;
}
private PeriodFrequencyType extractPeriodType(String paramName, final JsonElement element) { private PeriodFrequencyType extractPeriodType(String paramName, final JsonElement element) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.INVALID; PeriodFrequencyType frequencyType = PeriodFrequencyType.INVALID;
frequencyType = PeriodFrequencyType.fromInt(this.fromApiJsonHelper.extractIntegerWithLocaleNamed(paramName, element)); frequencyType = PeriodFrequencyType.fromInt(this.fromApiJsonHelper.extractIntegerWithLocaleNamed(paramName, element));
@@ -546,15 +593,33 @@ public class ShareAccountDataSerializer {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname).value(requestedDate).notNull(); baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname).value(requestedDate).notNull();
final Long sharesRequested = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element); final Long sharesRequested = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.requestedshares_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested).notNull(); baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested).notNull();
ShareProduct shareProduct = account.getShareProduct() ; ShareProduct shareProduct = account.getShareProduct();
if(sharesRequested != null) { if (sharesRequested != null) {
boolean allowed = shareProduct.isSharesAllowed(sharesRequested) ; Long totalSharesAfterapproval = account.getTotalApprovedShares() + sharesRequested;
if(!allowed) { if(totalSharesAfterapproval > shareProduct.getMaximumClientShares()) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested).failWithCode("differ.from.productdefinition", "Out of range");; baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(sharesRequested)
.failWithCode("exceeding.maximum.limit.defined.in.the.shareproduct", "Existing and requested shares count is more than product definition");
} }
} }
boolean isTransactionBeforeExistingTransactions = false ;
Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions() ;
for(ShareAccountTransaction transaction: transactions) {
if(!transaction.isChargeTransaction()) {
LocalDate transactionDate = new LocalDate(transaction.getPurchasedDate()) ;
if(requestedDate.isBefore(transactionDate)) {
isTransactionBeforeExistingTransactions = true ;
break ;
}
}
}
if(isTransactionBeforeExistingTransactions) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname)
.value(requestedDate)
.failWithCodeNoParameterAddedToErrorCode("purchase.transaction.date.cannot.be.before.existing.transactions");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
final BigDecimal unitPrice = shareProduct.deriveMarketPrice(requestedDate.toDate()) ; final BigDecimal unitPrice = shareProduct.deriveMarketPrice(requestedDate.toDate());
ShareAccountTransaction purchaseTransaction = new ShareAccountTransaction(requestedDate.toDate(), sharesRequested, unitPrice); ShareAccountTransaction purchaseTransaction = new ShareAccountTransaction(requestedDate.toDate(), sharesRequested, unitPrice);
account.addAdditionalPurchasedShares(purchaseTransaction); account.addAdditionalPurchasedShares(purchaseTransaction);
handleAdditionalSharesChargeTransactions(account, purchaseTransaction); handleAdditionalSharesChargeTransactions(account, purchaseTransaction);
@@ -566,7 +631,7 @@ public class ShareAccountDataSerializer {
Set<ShareAccountCharge> charges = account.getCharges(); Set<ShareAccountCharge> charges = account.getCharges();
BigDecimal totalChargeAmount = BigDecimal.ZERO; BigDecimal totalChargeAmount = BigDecimal.ZERO;
for (ShareAccountCharge charge : charges) { for (ShareAccountCharge charge : charges) {
if (charge.isSharesPurchaseCharge()) { if (charge.isActive() && charge.isSharesPurchaseCharge()) {
BigDecimal amount = charge.updateChargeDetailsForAdditionalSharesRequest(purchaseTransaction.amount()); BigDecimal amount = charge.updateChargeDetailsForAdditionalSharesRequest(purchaseTransaction.amount());
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(purchaseTransaction, charge, amount); ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(purchaseTransaction, charge, amount);
purchaseTransaction.addShareAccountChargePaidBy(paidBy); purchaseTransaction.addShareAccountChargePaidBy(paidBy);
@@ -588,20 +653,21 @@ public class ShareAccountDataSerializer {
final ArrayList<Long> purchasedShares = new ArrayList<>(); final ArrayList<Long> purchasedShares = new ArrayList<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) { if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) {
JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.requestedshares_paramname, element); JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.requestedshares_paramname, element);
long totalShares = 0 ; long totalShares = 0;
for (JsonElement arrayElement : array) { for (JsonElement arrayElement : array) {
final Long purchasedSharesId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.id_paramname, arrayElement); final Long purchasedSharesId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.id_paramname, arrayElement);
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(purchasedSharesId).notBlank() ; baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(purchasedSharesId).notBlank();
ShareAccountTransaction transaction = account.retrievePurchasedShares(purchasedSharesId); ShareAccountTransaction transaction = account.retrievePurchasedShares(purchasedSharesId);
if (transaction != null) { if (transaction != null) {
totalShares+=transaction.getTotalShares().longValue() ; validateTotalSubsribedShares(account, transaction, baseDataValidator) ;
totalShares += transaction.getTotalShares().longValue();
transaction.approve(); transaction.approve();
updateTotalChargeDerivedForAdditonalShares(account, transaction); updateTotalChargeDerivedForAdditonalShares(account, transaction);
} }
purchasedShares.add(purchasedSharesId); purchasedShares.add(purchasedSharesId);
} }
if(totalShares > 0) { if (totalShares > 0) {
account.updateApprovedShares(new Long(totalShares)) ; account.updateApprovedShares(new Long(totalShares));
} }
} }
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -614,11 +680,14 @@ public class ShareAccountDataSerializer {
if (paidBySet != null && !paidBySet.isEmpty()) { if (paidBySet != null && !paidBySet.isEmpty()) {
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) { for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
ShareAccountCharge charge = chargePaidBy.getCharge(); ShareAccountCharge charge = chargePaidBy.getCharge();
if (charge.isSharesPurchaseCharge()) { if (charge.isActive() && charge.isSharesPurchaseCharge()) {
Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount()); Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money); charge.updatePaidAmountBy(money);
} }
} }
if (!transaction.isChargeTransaction()) {
transaction.addAmountPaid(transaction.chargeAmount());
}
} }
} }
@@ -631,20 +700,20 @@ public class ShareAccountDataSerializer {
JsonElement element = jsonCommand.parsedJson(); JsonElement element = jsonCommand.parsedJson();
final ArrayList<Long> purchasedShares = new ArrayList<>(); final ArrayList<Long> purchasedShares = new ArrayList<>();
if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) { if (this.fromApiJsonHelper.parameterExists(ShareAccountApiConstants.requestedshares_paramname, element)) {
long totalShares = 0 ; long totalShares = 0;
JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.requestedshares_paramname, element); JsonArray array = this.fromApiJsonHelper.extractJsonArrayNamed(ShareAccountApiConstants.requestedshares_paramname, element);
for (JsonElement arrayElement : array) { for (JsonElement arrayElement : array) {
final Long purchasedSharesId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.id_paramname, arrayElement); final Long purchasedSharesId = this.fromApiJsonHelper.extractLongNamed(ShareAccountApiConstants.id_paramname, arrayElement);
ShareAccountTransaction shares = account.retrievePurchasedShares(purchasedSharesId); ShareAccountTransaction shares = account.retrievePurchasedShares(purchasedSharesId);
if (shares != null) { if (shares != null) {
shares.reject(); shares.reject();
updateTotalChargeDerivedForAdditonalSharesReject(account, shares) ; updateTotalChargeDerivedForAdditonalSharesReject(account, shares);
totalShares +=shares.getTotalShares().longValue() ; totalShares += shares.getTotalShares().longValue();
} }
purchasedShares.add(purchasedSharesId); purchasedShares.add(purchasedSharesId);
} }
if(totalShares > 0) { if (totalShares > 0) {
account.removePendingShares(new Long(totalShares)) ; account.removePendingShares(new Long(totalShares));
} }
} }
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, purchasedShares); actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, purchasedShares);
@@ -656,11 +725,14 @@ public class ShareAccountDataSerializer {
if (paidBySet != null && !paidBySet.isEmpty()) { if (paidBySet != null && !paidBySet.isEmpty()) {
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) { for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
ShareAccountCharge charge = chargePaidBy.getCharge(); ShareAccountCharge charge = chargePaidBy.getCharge();
if (charge.isSharesPurchaseCharge()) { if (charge.isActive() && charge.isSharesPurchaseCharge()) {
Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount()); Money money = Money.of(shareAccount.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money); charge.updatePaidAmountBy(money);
} }
} }
if (!transaction.isChargeTransaction()) {
transaction.addAmountPaid(transaction.chargeAmount());
}
} }
} }
@@ -683,19 +755,116 @@ public class ShareAccountDataSerializer {
final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareAccountApiConstants.purchasedprice_paramname, final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareAccountApiConstants.purchasedprice_paramname,
element, locale); element, locale);
baseDataValidator.reset().parameter(ShareAccountApiConstants.purchasedprice_paramname).value(unitPrice).notNull().positiveAmount(); baseDataValidator.reset().parameter(ShareAccountApiConstants.purchasedprice_paramname).value(unitPrice).notNull().positiveAmount();
boolean isTransactionBeforeExistingTransactions = false ;
Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions() ;
for(ShareAccountTransaction transaction: transactions) {
if(!transaction.isChargeTransaction()) {
LocalDate transactionDate = new LocalDate(transaction.getPurchasedDate()) ;
if(requestedDate.isBefore(transactionDate)) {
isTransactionBeforeExistingTransactions = true ;
break ;
}
}
}
if(isTransactionBeforeExistingTransactions) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname)
.value(requestedDate)
.failWithCodeNoParameterAddedToErrorCode("redeem.transaction.date.cannot.be.before.existing.transactions");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
ShareAccountTransaction transaction = ShareAccountTransaction.createRedeemTransaction(requestedDate.toDate(), sharesRequested, ShareAccountTransaction transaction = ShareAccountTransaction.createRedeemTransaction(requestedDate.toDate(), sharesRequested,
unitPrice); unitPrice);
account.addAdditionalPurchasedShares(transaction); account.addAdditionalPurchasedShares(transaction);
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, transaction); actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, transaction);
validateRedeemRequest(account, transaction, baseDataValidator, dataValidationErrors) ;
handleRedeemSharesChargeTransactions(account, transaction); handleRedeemSharesChargeTransactions(account, transaction);
return actualChanges; return actualChanges;
} }
private void validateRedeemRequest(final ShareAccount account, ShareAccountTransaction redeemTransaction,
final DataValidatorBuilder baseDataValidator, final List<ApiParameterError> dataValidationErrors ) {
if (account.getTotalApprovedShares() < redeemTransaction.getTotalShares()) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname)
.value(redeemTransaction.getTotalShares())
.failWithCodeNoParameterAddedToErrorCode("cannot.be.redeemed.due.to.insufficient.shares");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
LocalDate redeemDate = new LocalDate(redeemTransaction.getPurchasedDate());
final Integer lockinPeriod = account.getLockinPeriodFrequency();
final PeriodFrequencyType periodType = account.getLockinPeriodFrequencyType();
if (lockinPeriod == null && periodType == null) { return; }
Long totalSharesCanBeRedeemed = new Long(0);
Long totalSharesPurchasedBeforeRedeem = new Long(0) ;
boolean isPurchaseTransactionExist = false ;
Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions();
for (ShareAccountTransaction transaction : transactions) {
if (transaction.isActive() && !transaction.isChargeTransaction()) {
LocalDate purchaseDate = new LocalDate(transaction.getPurchasedDate());
LocalDate lockinDate = deriveLockinPeriodDuration(lockinPeriod, periodType, purchaseDate);
if (!lockinDate.isAfter(redeemDate)) {
if (transaction.isPurchasTransaction()) {
totalSharesCanBeRedeemed += transaction.getTotalShares();
} else if (transaction.isRedeemTransaction()) {
totalSharesCanBeRedeemed -= transaction.getTotalShares();
}
}
if(!purchaseDate.isAfter(redeemDate)) {
isPurchaseTransactionExist = true ;
if (transaction.isPurchasTransaction()) {
totalSharesPurchasedBeforeRedeem += transaction.getTotalShares();
} else if (transaction.isRedeemTransaction()) {
totalSharesPurchasedBeforeRedeem -= transaction.getTotalShares();
}
}
}
}
if(!isPurchaseTransactionExist) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requesteddate_paramname)
.value(redeemDate)
.failWithCodeNoParameterAddedToErrorCode("no.purchase.transaction.found.before.redeem.date");
}else if (totalSharesPurchasedBeforeRedeem < redeemTransaction.getTotalShares()) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname)
.value(redeemTransaction.getTotalShares())
.failWithCodeNoParameterAddedToErrorCode("cannot.be.redeemed.due.to.insufficient.shares.for.this.redeem.date");
}else if (totalSharesCanBeRedeemed < redeemTransaction.getTotalShares()) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname)
.value(redeemTransaction.getTotalShares())
.failWithCodeNoParameterAddedToErrorCode("cannot.be.redeemed.due.to.lockinperiod");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private LocalDate deriveLockinPeriodDuration(final Integer lockinPeriod, final PeriodFrequencyType periodType, LocalDate purchaseDate) {
LocalDate lockinDate = null ;
switch(periodType) {
case INVALID: //It never comes in to this state.
break ;
case DAYS:
lockinDate = purchaseDate.plusDays(lockinPeriod) ;
break ;
case WEEKS:
lockinDate = purchaseDate.plusWeeks(lockinPeriod) ;
break ;
case MONTHS:
lockinDate = purchaseDate.plusMonths(lockinPeriod) ;
break ;
case YEARS:
lockinDate = purchaseDate.plusYears(lockinPeriod) ;
break ;
}
return lockinDate ;
}
private void handleRedeemSharesChargeTransactions(final ShareAccount account, final ShareAccountTransaction transaction) { private void handleRedeemSharesChargeTransactions(final ShareAccount account, final ShareAccountTransaction transaction) {
Set<ShareAccountCharge> charges = account.getCharges(); Set<ShareAccountCharge> charges = account.getCharges();
BigDecimal totalChargeAmount = BigDecimal.ZERO; BigDecimal totalChargeAmount = BigDecimal.ZERO;
for (ShareAccountCharge charge : charges) { for (ShareAccountCharge charge : charges) {
if (charge.isSharesRedeemCharge()) { if (charge.isActive() && charge.isSharesRedeemCharge()) {
BigDecimal amount = charge.updateChargeDetailsForAdditionalSharesRequest(transaction.amount()); BigDecimal amount = charge.updateChargeDetailsForAdditionalSharesRequest(transaction.amount());
ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(transaction, charge, amount); ShareAccountChargePaidBy paidBy = new ShareAccountChargePaidBy(transaction, charge, amount);
transaction.addShareAccountChargePaidBy(paidBy); transaction.addShareAccountChargePaidBy(paidBy);
@@ -707,14 +876,13 @@ public class ShareAccountDataSerializer {
if (paidBySet != null && !paidBySet.isEmpty()) { if (paidBySet != null && !paidBySet.isEmpty()) {
for (ShareAccountChargePaidBy chargePaidBy : paidBySet) { for (ShareAccountChargePaidBy chargePaidBy : paidBySet) {
ShareAccountCharge charge = chargePaidBy.getCharge(); ShareAccountCharge charge = chargePaidBy.getCharge();
if (charge.isSharesRedeemCharge()) { if (charge.isActive() && charge.isSharesRedeemCharge()) {
Money money = Money.of(account.getCurrency(), chargePaidBy.getAmount()); Money money = Money.of(account.getCurrency(), chargePaidBy.getAmount());
charge.updatePaidAmountBy(money); charge.updatePaidAmountBy(money);
} }
} }
} }
transaction.addAmountPaid(transaction.chargeAmount());
// transaction.adjustRedeemAmount();
} }
public Map<String, Object> validateAndClose(JsonCommand jsonCommand, ShareAccount account) { public Map<String, Object> validateAndClose(JsonCommand jsonCommand, ShareAccount account) {
@@ -727,15 +895,35 @@ public class ShareAccountDataSerializer {
JsonElement element = jsonCommand.parsedJson(); JsonElement element = jsonCommand.parsedJson();
LocalDate closedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.closeddate_paramname, element); LocalDate closedDate = this.fromApiJsonHelper.extractLocalDateNamed(ShareAccountApiConstants.closeddate_paramname, element);
baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).value(closedDate).notNull(); baseDataValidator.reset().parameter(ShareAccountApiConstants.approveddate_paramname).value(closedDate).notNull();
boolean isTransactionBeforeExistingTransactions = false ;
Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions() ;
for(ShareAccountTransaction transaction: transactions) {
if(!transaction.isChargeTransaction()) {
LocalDate transactionDate = new LocalDate(transaction.getPurchasedDate()) ;
if(closedDate.isBefore(transactionDate)) {
isTransactionBeforeExistingTransactions = true ;
break ;
}
}
}
if(isTransactionBeforeExistingTransactions) {
baseDataValidator.reset().parameter(ShareAccountApiConstants.closeddate_paramname)
.value(closedDate)
.failWithCodeNoParameterAddedToErrorCode("share.account.cannot.be.closed.before.existing.transactions");
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
AppUser approvedUser = this.platformSecurityContext.authenticatedUser(); AppUser approvedUser = this.platformSecurityContext.authenticatedUser();
final BigDecimal unitPrice = account.getShareProduct().deriveMarketPrice(DateUtils.getDateOfTenant()) ; final BigDecimal unitPrice = account.getShareProduct().deriveMarketPrice(DateUtils.getDateOfTenant());
ShareAccountTransaction transaction = ShareAccountTransaction.createRedeemTransaction(closedDate.toDate(), account.getTotalApprovedShares(), unitPrice) ; ShareAccountTransaction transaction = ShareAccountTransaction.createRedeemTransaction(closedDate.toDate(),
account.getTotalApprovedShares(), unitPrice);
account.addAdditionalPurchasedShares(transaction); account.addAdditionalPurchasedShares(transaction);
account.close(closedDate.toDate(), approvedUser); account.close(closedDate.toDate(), approvedUser);
handleRedeemSharesChargeTransactions(account, transaction) ; handleRedeemSharesChargeTransactions(account, transaction);
actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, transaction); actualChanges.put(ShareAccountApiConstants.requestedshares_paramname, transaction);
updateTotalChargeDerived(account); updateTotalChargeDerived(account);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
return actualChanges; return actualChanges;
} }
} }
@@ -20,9 +20,9 @@ package org.apache.fineract.portfolio.shareaccounts.service;
import java.util.Collection; import java.util.Collection;
import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionData;
public interface PurchasedSharesReadPlatformService { public interface PurchasedSharesReadPlatformService {
public Collection<PurchasedSharesData> retrievePurchasedShares(final Long accountId) ; public Collection<ShareAccountTransactionData> retrievePurchasedShares(final Long accountId) ;
} }
@@ -26,7 +26,7 @@ import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource; import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionData;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@@ -44,26 +44,27 @@ public class PurchasedSharesReadPlatformServiceImpl implements
this.jdbcTemplate = new JdbcTemplate(dataSource) ; this.jdbcTemplate = new JdbcTemplate(dataSource) ;
} }
@Override @Override
public Collection<PurchasedSharesData> retrievePurchasedShares( public Collection<ShareAccountTransactionData> retrievePurchasedShares(
Long accountId) { Long accountId) {
PurchasedSharesDataRowMapper mapper = new PurchasedSharesDataRowMapper() ; PurchasedSharesDataRowMapper mapper = new PurchasedSharesDataRowMapper() ;
final String sql = "select " + mapper.schema() + " where saps.account_id=?"; final String sql = "select " + mapper.schema() + " where saps.account_id=? and saps.is_active = 1";
return this.jdbcTemplate.query(sql, mapper, new Object[] { accountId}); return this.jdbcTemplate.query(sql, mapper, new Object[] { accountId});
} }
private final static class PurchasedSharesDataRowMapper implements RowMapper<PurchasedSharesData> { private final static class PurchasedSharesDataRowMapper implements RowMapper<ShareAccountTransactionData> {
private final String schema ; private final String schema ;
public PurchasedSharesDataRowMapper() { public PurchasedSharesDataRowMapper() {
StringBuffer buff = new StringBuffer() StringBuffer buff = new StringBuffer()
.append("saps.id, saps.account_id, saps.transaction_date, saps.total_shares, saps.unit_price, ") .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("saps.status_enum, saps.type_enum, saps.amount, saps.charge_amount as chargeamount, ")
.append("saps.amount_paid as amountPaid")
.append(" from m_share_account_transactions saps "); .append(" from m_share_account_transactions saps ");
schema = buff.toString() ; schema = buff.toString() ;
} }
@Override @Override
public PurchasedSharesData mapRow(ResultSet rs, int rowNum) public ShareAccountTransactionData mapRow(ResultSet rs, int rowNum)
throws SQLException { throws SQLException {
final Long id = rs.getLong("id") ; final Long id = rs.getLong("id") ;
final Long accountId = rs.getLong("account_id") ; final Long accountId = rs.getLong("account_id") ;
@@ -76,7 +77,9 @@ public class PurchasedSharesReadPlatformServiceImpl implements
final EnumOptionData typeEnum = SharesEnumerations.purchasedSharesEnum(type) ; final EnumOptionData typeEnum = SharesEnumerations.purchasedSharesEnum(type) ;
final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amount") ; final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amount") ;
final BigDecimal chargeAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "chargeamount") ; final BigDecimal chargeAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "chargeamount") ;
return new PurchasedSharesData(id,accountId, purchasedDate, numberOfShares, purchasedPrice, statusEnum, typeEnum, amount, chargeAmount); final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid") ;
return new ShareAccountTransactionData(id,accountId, purchasedDate, numberOfShares, purchasedPrice, statusEnum, typeEnum, amount, chargeAmount, amountPaid);
} }
public String schema() { public String schema() {
@@ -80,6 +80,7 @@ public class ShareAccountChargeReadPlatformServiceImpl implements
.append("sc.charge_calculation_enum as chargeCalculation, c.is_active as isActive, ") .append("sc.charge_calculation_enum as chargeCalculation, c.is_active as isActive, ")
.append("c.currency_code as currencyCode, oc.name as currencyName, ") .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.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, ")
.append("sc.charge_amount_or_percentage, ")
.append("oc.internationalized_name_code as currencyNameCode from m_charge c ") .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_organisation_currency oc on c.currency_code = oc.code ")
.append("join m_share_account_charge sc on sc.charge_id = c.id "); .append("join m_share_account_charge sc on sc.charge_id = c.id ");
@@ -132,13 +133,14 @@ public class ShareAccountChargeReadPlatformServiceImpl implements
final EnumOptionData chargeCalculationType = ChargeEnumerations final EnumOptionData chargeCalculationType = ChargeEnumerations
.chargeCalculationType(chargeCalculation); .chargeCalculationType(chargeCalculation);
final Boolean isActive = rs.getBoolean("isActive"); final Boolean isActive = rs.getBoolean("isActive");
final BigDecimal chargeamountorpercentage = rs.getBigDecimal("charge_amount_or_percentage") ;
final Collection<ChargeData> chargeOptions = null; final Collection<ChargeData> chargeOptions = null;
return new ShareAccountChargeData(id, chargeId, accountId, name, return new ShareAccountChargeData(id, chargeId, accountId, name,
currency, amount, amountPaid, amountWaived, currency, amount, amountPaid, amountWaived,
amountWrittenOff, amountOutstanding, chargeTimeType, amountWrittenOff, amountOutstanding, chargeTimeType,
chargeCalculationType, percentageOf, chargeCalculationType, percentageOf,
amountPercentageAppliedTo, chargeOptions, isActive); amountPercentageAppliedTo, chargeOptions, isActive, chargeamountorpercentage);
} }
public String schema() { public String schema() {
@@ -42,9 +42,10 @@ import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.products.constants.ProductsApiConstants; import org.apache.fineract.portfolio.products.constants.ProductsApiConstants;
import org.apache.fineract.portfolio.products.data.ProductData; import org.apache.fineract.portfolio.products.data.ProductData;
import org.apache.fineract.portfolio.products.service.ProductReadPlatformService; import org.apache.fineract.portfolio.products.service.ProductReadPlatformService;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData; import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountApplicationTimelineData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountApplicationTimelineData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountDividendData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountStatusEnumData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountStatusEnumData;
@@ -114,7 +115,7 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService
.retrieveMinimumActivePeriodFrequencyTypeOptions(); .retrieveMinimumActivePeriodFrequencyTypeOptions();
final Collection<SavingsAccountData> clientSavingsAccounts = this.savingsAccountReadPlatformService final Collection<SavingsAccountData> clientSavingsAccounts = this.savingsAccountReadPlatformService
.retrieveAllForLookup(clientId); .retrieveActiveForLookup(clientId, DepositAccountType.SAVINGS_DEPOSIT, productData.getCurrency().code());
toReturn = new ShareAccountData(client.id(), client.displayName(), productData.getCurrency(), charges, marketPrice, toReturn = new ShareAccountData(client.id(), client.displayName(), productData.getCurrency(), charges, marketPrice,
minimumActivePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions, clientSavingsAccounts, productData.getNominaltShares()); minimumActivePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions, clientSavingsAccounts, productData.getNominaltShares());
} else { } else {
@@ -143,7 +144,7 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
@Override @Override
public ShareAccountData retrieveOne(final Long id, final boolean includeTemplate) { public ShareAccountData retrieveOne(final Long id, final boolean includeTemplate) {
Collection<ShareAccountChargeData> charges = this.shareAccountChargeReadPlatformService.retrieveAccountCharges(id, "active"); Collection<ShareAccountChargeData> charges = this.shareAccountChargeReadPlatformService.retrieveAccountCharges(id, "active");
Collection<PurchasedSharesData> purchasedShares = this.purchasedSharesReadPlatformService.retrievePurchasedShares(id); Collection<ShareAccountTransactionData> purchasedShares = this.purchasedSharesReadPlatformService.retrievePurchasedShares(id);
ShareAccountMapper mapper = new ShareAccountMapper(charges, purchasedShares); ShareAccountMapper mapper = new ShareAccountMapper(charges, purchasedShares);
String query = "select " + mapper.schema() + "where sa.id=?"; String query = "select " + mapper.schema() + "where sa.id=?";
@@ -161,8 +162,8 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.shareProductDropdownReadPlatformService
.retrieveLockinPeriodFrequencyTypeOptions(); .retrieveLockinPeriodFrequencyTypeOptions();
final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; final Collection<EnumOptionData> minimumActivePeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
final Collection<SavingsAccountData> clientSavingsAccounts = this.savingsAccountReadPlatformService.retrieveAllForLookup(data final Collection<SavingsAccountData> clientSavingsAccounts = this.savingsAccountReadPlatformService
.getClientId()); .retrieveActiveForLookup(data.getClientId(), DepositAccountType.SAVINGS_DEPOSIT, productData.getCurrency().code());
Collection<ProductData> productOptions = service.retrieveAllForLookup(); Collection<ProductData> productOptions = service.retrieveAllForLookup();
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges(); final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSharesApplicableCharges();
data = ShareAccountData.template(data, productOptions, chargeOptions, clientSavingsAccounts, lockinPeriodFrequencyTypeOptions, data = ShareAccountData.template(data, productOptions, chargeOptions, clientSavingsAccounts, lockinPeriodFrequencyTypeOptions,
@@ -224,11 +225,11 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
private final static class ShareAccountMapper implements RowMapper<ShareAccountData> { private final static class ShareAccountMapper implements RowMapper<ShareAccountData> {
private final Collection<ShareAccountChargeData> charges; private final Collection<ShareAccountChargeData> charges;
private final Collection<PurchasedSharesData> purchasedShares; private final Collection<ShareAccountTransactionData> purchasedShares;
private final String schema; private final String schema;
public ShareAccountMapper(final Collection<ShareAccountChargeData> charges, final Collection<PurchasedSharesData> purchasedShares) { public ShareAccountMapper(final Collection<ShareAccountChargeData> charges, final Collection<ShareAccountTransactionData> purchasedShares) {
this.charges = charges; this.charges = charges;
this.purchasedShares = purchasedShares; this.purchasedShares = purchasedShares;
StringBuffer buff = new StringBuffer() StringBuffer buff = new StringBuffer()
@@ -392,7 +393,7 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
final Boolean allowdividendsforinactiveclients = null; final Boolean allowdividendsforinactiveclients = null;
final Collection<ShareAccountChargeData> charges = null; final Collection<ShareAccountChargeData> charges = null;
final Collection<PurchasedSharesData> purchasedSharesData = new ArrayList<>(); final Collection<ShareAccountTransactionData> purchasedSharesData = new ArrayList<>();
final Integer lockinPeriod = null; final Integer lockinPeriod = null;
final EnumOptionData lockPeriodTypeEnum = null; final EnumOptionData lockPeriodTypeEnum = null;
final Integer minimumActivePeriod = null; final Integer minimumActivePeriod = null;
@@ -418,19 +419,21 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
} }
} }
private final static class PurchasedSharesDataRowMapper implements RowMapper<PurchasedSharesData> { private final static class PurchasedSharesDataRowMapper implements RowMapper<ShareAccountTransactionData> {
private final String schema; private final String schema;
public PurchasedSharesDataRowMapper() { public PurchasedSharesDataRowMapper() {
StringBuffer buff = new StringBuffer() 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.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 "); .append("saps.status_enum as purchaseStatus, saps.type_enum as purchaseType, saps.amount as amount, saps.charge_amount as chargeamount, ")
.append("saps.amount_paid as amountPaid ");
schema = buff.toString(); schema = buff.toString();
} }
@Override @Override
public PurchasedSharesData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { public ShareAccountTransactionData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final Long id = rs.getLong("purchasedId"); final Long id = rs.getLong("purchasedId");
final Long accountId = rs.getLong("accountId"); final Long accountId = rs.getLong("accountId");
final LocalDate transactionDate = new LocalDate(rs.getDate("transactionDate")); final LocalDate transactionDate = new LocalDate(rs.getDate("transactionDate"));
@@ -442,8 +445,9 @@ public class ShareAccountReadPlatformServiceImpl implements ShareAccountReadPlat
final EnumOptionData typeEnum = SharesEnumerations.purchasedSharesEnum(type); final EnumOptionData typeEnum = SharesEnumerations.purchasedSharesEnum(type);
final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount"); final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amount");
final BigDecimal chargeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "chargeamount"); final BigDecimal chargeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "chargeamount");
return new PurchasedSharesData(id, accountId, transactionDate, numberOfShares, purchasedPrice, statusEnum, typeEnum, amount, final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "amountPaid");
chargeAmount); return new ShareAccountTransactionData(id, accountId, transactionDate, numberOfShares, purchasedPrice, statusEnum, typeEnum, amount,
chargeAmount, amountPaid);
} }
public String schema() { public String schema() {
@@ -18,7 +18,9 @@
*/ */
package org.apache.fineract.portfolio.shareaccounts.service; package org.apache.fineract.portfolio.shareaccounts.service;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -32,6 +34,7 @@ import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccou
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants; import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants;
@@ -42,6 +45,8 @@ import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountChargePaid
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper; import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountTransaction; import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountTransaction;
import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer; import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
@@ -54,6 +59,8 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
private final ShareAccountRepositoryWrapper shareAccountRepository; private final ShareAccountRepositoryWrapper shareAccountRepository;
private final ShareProductRepositoryWrapper shareProductRepository ;
private final AccountNumberGenerator accountNumberGenerator; private final AccountNumberGenerator accountNumberGenerator;
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
@@ -63,11 +70,13 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
@Autowired @Autowired
public ShareAccountWritePlatformServiceJpaRepositoryImpl(final ShareAccountDataSerializer accountDataSerializer, public ShareAccountWritePlatformServiceJpaRepositoryImpl(final ShareAccountDataSerializer accountDataSerializer,
final ShareAccountRepositoryWrapper shareAccountRepository, final ShareAccountRepositoryWrapper shareAccountRepository,
final ShareProductRepositoryWrapper shareProductRepository,
final AccountNumberGenerator accountNumberGenerator, final AccountNumberGenerator accountNumberGenerator,
final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService) { final JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.accountDataSerializer = accountDataSerializer; this.accountDataSerializer = accountDataSerializer;
this.shareAccountRepository = shareAccountRepository; this.shareAccountRepository = shareAccountRepository;
this.shareProductRepository = shareProductRepository ;
this.accountNumberGenerator = accountNumberGenerator; this.accountNumberGenerator = accountNumberGenerator;
this.accountNumberFormatRepository = accountNumberFormatRepository; this.accountNumberFormatRepository = accountNumberFormatRepository;
this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.journalEntryWritePlatformService = journalEntryWritePlatformService;
@@ -79,7 +88,6 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
ShareAccount account = this.accountDataSerializer.validateAndCreate(jsonCommand); ShareAccount account = this.accountDataSerializer.validateAndCreate(jsonCommand);
this.shareAccountRepository.save(account); this.shareAccountRepository.save(account);
generateAccountNumber(account); generateAccountNumber(account);
//this.shareProductRepository.save(account.getShareProduct()); //subscribed shares is increased
journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account,
account.getPendingForApprovalSharePurchaseTransactions())); account.getPendingForApprovalSharePurchaseTransactions()));
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
@@ -126,7 +134,16 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
final ShareAccountTransactionEnumData typeEnum = new ShareAccountTransactionEnumData(type.longValue(), null, null); final ShareAccountTransactionEnumData typeEnum = new ShareAccountTransactionEnumData(type.longValue(), null, null);
transactionDto.put("status", statusEnum); transactionDto.put("status", statusEnum);
transactionDto.put("type", typeEnum); transactionDto.put("type", typeEnum);
transactionDto.put("amount", transaction.amount()); if(transaction.isPurchaseRejectedTransaction() || transaction.isRedeemTransaction()) {
BigDecimal amount = transaction.amount() ;
if(transaction.chargeAmount() != null) {
amount = amount.add(transaction.chargeAmount()) ;
}
transactionDto.put("amount", amount);
}else {
transactionDto.put("amount", transaction.amount());
}
transactionDto.put("chargeAmount", transaction.chargeAmount()); transactionDto.put("chargeAmount", transaction.chargeAmount());
transactionDto.put("paymentTypeId", null); // FIXME::make it cash transactionDto.put("paymentTypeId", null); // FIXME::make it cash
// payment // payment
@@ -147,14 +164,24 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
return accountingBridgeData; return accountingBridgeData;
} }
@SuppressWarnings("unchecked")
@Override @Override
public CommandProcessingResult updateShareAccount(Long accountId, JsonCommand jsonCommand) { public CommandProcessingResult updateShareAccount(Long accountId, JsonCommand jsonCommand) {
try { try {
Date transactionDate = DateUtils.getDateOfTenant();
ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId);
Map<String, Object> changes = this.accountDataSerializer.validateAndUpdate(jsonCommand, account); Map<String, Object> changes = this.accountDataSerializer.validateAndUpdate(jsonCommand, account);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.shareAccountRepository.save(account); this.shareAccountRepository.save(account);
} }
// since we are reverting all journal entries we need to add journal
// entries for application request
if (changes.containsKey("reversalIds")) {
ArrayList<Long> reversalIds = (ArrayList<Long>) changes.get("reversalIds");
this.journalEntryWritePlatformService.revertShareAccountJournalEntries(reversalIds, transactionDate);
journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account,
account.getPendingForApprovalSharePurchaseTransactions()));
}
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(jsonCommand.commandId()) // .withCommandId(jsonCommand.commandId()) //
.withEntityId(accountId) // .withEntityId(accountId) //
@@ -205,8 +232,21 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.shareAccountRepository.save(account); this.shareAccountRepository.save(account);
} }
this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions();
account.getShareAccountTransactions())); Set<ShareAccountTransaction> journalTransactions = new HashSet<>();
Long totalSubsribedShares = new Long(0) ;
for (ShareAccountTransaction transaction : transactions) {
if (transaction.isActive() && transaction.isPurchasTransaction()) {
journalTransactions.add(transaction);
totalSubsribedShares += transaction.getTotalShares() ;
}
}
ShareProduct shareProduct = account.getShareProduct() ;
shareProduct.addSubscribedShares(totalSubsribedShares);
this.shareProductRepository.save(shareProduct);
this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, journalTransactions));
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(jsonCommand.commandId()) // .withCommandId(jsonCommand.commandId()) //
.withEntityId(accountId) // .withEntityId(accountId) //
@@ -226,8 +266,14 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.shareAccountRepository.save(account); this.shareAccountRepository.save(account);
} }
this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions();
account.getShareAccountTransactions())); Set<ShareAccountTransaction> journalTransactions = new HashSet<>();
for (ShareAccountTransaction transaction : transactions) {
if (transaction.isActive() && !transaction.isChargeTransaction()) {
journalTransactions.add(transaction);
}
}
this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, journalTransactions));
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(jsonCommand.commandId()) // .withCommandId(jsonCommand.commandId()) //
.withEntityId(accountId) // .withEntityId(accountId) //
@@ -247,11 +293,18 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.shareAccountRepository.save(account); this.shareAccountRepository.save(account);
} }
/*
* this.journalEntryWritePlatformService.createJournalEntriesForShares Set<ShareAccountTransaction> transactions = account.getShareAccountTransactions() ;
* (populateJournalEntries(account, ArrayList<Long> journalEntryTransactions = new ArrayList<>() ;
* account.getShareAccountTransactions())); for(ShareAccountTransaction transaction: transactions) {
*/ if(transaction.isActive() && !transaction.isChargeTransaction()){
journalEntryTransactions.add(transaction.getId()) ;
}
}
Date transactionDate = DateUtils.getDateOfTenant();
this.journalEntryWritePlatformService.revertShareAccountJournalEntries(journalEntryTransactions, transactionDate);
journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account,
account.getPendingForApprovalSharePurchaseTransactions()));
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(jsonCommand.commandId()) // .withCommandId(jsonCommand.commandId()) //
.withEntityId(accountId) // .withEntityId(accountId) //
@@ -285,6 +338,7 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
} }
} }
@SuppressWarnings("unchecked")
@Override @Override
public CommandProcessingResult approveAdditionalShares(Long accountId, JsonCommand jsonCommand) { public CommandProcessingResult approveAdditionalShares(Long accountId, JsonCommand jsonCommand) {
@@ -294,14 +348,21 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.shareAccountRepository.save(account); this.shareAccountRepository.save(account);
ArrayList<Long> transactionIds = (ArrayList<Long>) changes.get(ShareAccountApiConstants.requestedshares_paramname); ArrayList<Long> transactionIds = (ArrayList<Long>) changes.get(ShareAccountApiConstants.requestedshares_paramname);
Long totalSubscribedShares = new Long(0) ;
if (transactionIds != null) { if (transactionIds != null) {
Set<ShareAccountTransaction> transactions = new HashSet<>(); Set<ShareAccountTransaction> transactions = new HashSet<>();
for (Long id : transactionIds) { for (Long id : transactionIds) {
ShareAccountTransaction transaction = account.retrievePurchasedShares(id); ShareAccountTransaction transaction = account.retrievePurchasedShares(id);
transactions.add(transaction); transactions.add(transaction);
totalSubscribedShares += transaction.getTotalShares() ;
} }
this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions)); this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions));
} }
if(!totalSubscribedShares.equals(new Long(0))) {
ShareProduct shareProduct = account.getShareProduct() ;
shareProduct.addSubscribedShares(totalSubscribedShares);
this.shareProductRepository.save(shareProduct);
}
} }
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(jsonCommand.commandId()) // .withCommandId(jsonCommand.commandId()) //
@@ -314,9 +375,9 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
} }
} }
@SuppressWarnings("unchecked")
@Override @Override
public CommandProcessingResult rejectAdditionalShares(Long accountId, JsonCommand jsonCommand) { public CommandProcessingResult rejectAdditionalShares(Long accountId, JsonCommand jsonCommand) {
try { try {
ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId); ShareAccount account = this.shareAccountRepository.findOneWithNotFoundDetection(accountId);
Map<String, Object> changes = this.accountDataSerializer.validateAndRejectAddtionalShares(jsonCommand, account); Map<String, Object> changes = this.accountDataSerializer.validateAndRejectAddtionalShares(jsonCommand, account);
@@ -352,7 +413,21 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
this.shareAccountRepository.save(account); this.shareAccountRepository.save(account);
ShareAccountTransaction transaction = (ShareAccountTransaction) changes ShareAccountTransaction transaction = (ShareAccountTransaction) changes
.get(ShareAccountApiConstants.requestedshares_paramname); .get(ShareAccountApiConstants.requestedshares_paramname);
transaction = account.getShareAccountTransaction(transaction); transaction = account.getShareAccountTransaction(transaction); // after
// saving,
// entity
// will
// have
// different
// object.
// So
// need
// to
// retrieve
// to
// get
// the
// id
Set<ShareAccountTransaction> transactions = new HashSet<>(); Set<ShareAccountTransaction> transactions = new HashSet<>();
transactions.add(transaction); transactions.add(transaction);
this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions)); this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, transactions));
@@ -398,6 +473,7 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA
return CommandProcessingResult.empty(); return CommandProcessingResult.empty();
} }
} }
private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
} }
@@ -226,7 +226,10 @@ public class ShareProduct extends AbstractAuditableCustom<AppUser, Long> {
public boolean setTotalIssuedShares(Long totalSharesIssued) { public boolean setTotalIssuedShares(Long totalSharesIssued) {
boolean returnValue = false; boolean returnValue = false;
if (!this.totalSharesIssued.equals(totalSharesIssued)) { if(this.totalSharesIssued == null) {
this.totalSharesIssued = totalSharesIssued ;
returnValue = true ;
}else if (!this.totalSharesIssued.equals(totalSharesIssued)) {
this.totalSharesIssued = totalSharesIssued; this.totalSharesIssued = totalSharesIssued;
returnValue = true; returnValue = true;
} }
@@ -431,4 +434,15 @@ public class ShareProduct extends AbstractAuditableCustom<AppUser, Long> {
return this.totalSubscribedShares ; return this.totalSubscribedShares ;
} }
public Long getMinimumClientShares() {
return this.minimumShares ;
}
public Long getMaximumClientShares() {
return this.maximumShares ;
}
public Long getDefaultClientShares() {
return this.nominalShares ;
}
} }
@@ -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.AbstractPlatformResourceNotFoundException;
public class ShareAccountsNotFoundException extends AbstractPlatformResourceNotFoundException{
public ShareAccountsNotFoundException(final Long productId) {
super("error.msg.shareproduct.shareaccounts.not.defined", " No Share Accounts found for this prodct " + productId, productId);
}
}
@@ -39,6 +39,7 @@ import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants;
import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
@@ -102,14 +103,18 @@ public class ShareProductDataSerializer {
Long totalNumberOfShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalshares_paramname, element); Long totalNumberOfShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalshares_paramname, element);
baseDataValidator.reset().parameter(ShareProductApiConstants.totalshares_paramname).value(totalNumberOfShares).notNull() baseDataValidator.reset().parameter(ShareProductApiConstants.totalshares_paramname).value(totalNumberOfShares).notNull()
.longGreaterThanZero(); .longGreaterThanZero();
final Long sharesIssued = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalsharesissued_paramname, element);
if(sharesIssued != null && totalNumberOfShares != null && sharesIssued > totalNumberOfShares) {
baseDataValidator.reset().parameter(ShareProductApiConstants.totalsharesissued_paramname).value(sharesIssued)
.failWithCodeNoParameterAddedToErrorCode("sharesIssued.cannot.be.greater.than.totalNumberOfShares");
}
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.currency_paramname, element); final String currencyCode = this.fromApiJsonHelper.extractStringNamed(ShareProductApiConstants.currency_paramname, element);
final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerWithLocaleNamed( final Integer digitsAfterDecimal = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
ShareProductApiConstants.digitsafterdecimal_paramname, element); ShareProductApiConstants.digitsafterdecimal_paramname, element);
final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerWithLocaleNamed( final Integer inMultiplesOf = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
ShareProductApiConstants.inmultiplesof_paramname, element); ShareProductApiConstants.inmultiplesof_paramname, element);
final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf); final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal, inMultiplesOf);
final Long sharesIssued = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.totalsharesissued_paramname, element);
final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareProductApiConstants.unitprice_paramname, element, final BigDecimal unitPrice = this.fromApiJsonHelper.extractBigDecimalNamed(ShareProductApiConstants.unitprice_paramname, element,
locale); locale);
baseDataValidator.reset().parameter(ShareProductApiConstants.unitprice_paramname).value(unitPrice).notNull().positiveAmount(); baseDataValidator.reset().parameter(ShareProductApiConstants.unitprice_paramname).value(unitPrice).notNull().positiveAmount();
@@ -132,12 +137,12 @@ public class ShareProductDataSerializer {
Long nominalClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.nominaltshares_paramname, element); Long nominalClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.nominaltshares_paramname, element);
baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares).notNull() baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares).notNull()
.longGreaterThanZero(); .longGreaterThanZero();
if (minimumClientShares != null && nominalClientShares != null) { if (minimumClientShares != null && nominalClientShares != null && !minimumClientShares.equals(nominalClientShares)) {
baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares) baseDataValidator.reset().parameter(ShareProductApiConstants.nominaltshares_paramname).value(nominalClientShares)
.longGreaterThanNumber(minimumClientShares); .longGreaterThanNumber(minimumClientShares);
} }
Long maximumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.maximumshares_paramname, element); Long maximumClientShares = this.fromApiJsonHelper.extractLongNamed(ShareProductApiConstants.maximumshares_paramname, element);
if (maximumClientShares != null && nominalClientShares != null) { if (maximumClientShares != null && nominalClientShares != null && !maximumClientShares.equals(nominalClientShares)) {
baseDataValidator.reset().parameter(ShareProductApiConstants.maximumshares_paramname).value(maximumClientShares) baseDataValidator.reset().parameter(ShareProductApiConstants.maximumshares_paramname).value(maximumClientShares)
.longGreaterThanNumber(nominalClientShares); .longGreaterThanNumber(nominalClientShares);
} }
@@ -294,8 +299,16 @@ public class ShareProductDataSerializer {
if (product.setTotalIssuedShares(sharesIssued)) { if (product.setTotalIssuedShares(sharesIssued)) {
actualChanges.put(ShareProductApiConstants.totalsharesissued_paramname, sharesIssued); actualChanges.put(ShareProductApiConstants.totalsharesissued_paramname, sharesIssued);
} }
}else {
product.setTotalIssuedShares(sharesIssued) ;
} }
if(sharesIssued != null && product.getSubscribedShares() != null && sharesIssued < product.getSubscribedShares()) {
baseDataValidator.reset().parameter(ShareProductApiConstants.totalsharesissued_paramname).value(sharesIssued)
.failWithCodeNoParameterAddedToErrorCode("sharesissued.can.not.be.lessthan.accounts.subscribed.shares");
}
if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.currency_paramname, element) if (this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.currency_paramname, element)
&& this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.digitsafterdecimal_paramname, element) && this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.digitsafterdecimal_paramname, element)
&& this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.inmultiplesof_paramname, element)) { && this.fromApiJsonHelper.parameterExists(ShareProductApiConstants.inmultiplesof_paramname, element)) {
@@ -416,17 +429,17 @@ public class ShareProductDataSerializer {
} }
} }
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
BigDecimal shareCapitalValue; BigDecimal shareCapitalValue;
if (sharesIssued != null || unitPrice != null) { if (sharesIssued != null || unitPrice != null) {
if (sharesIssued == null) sharesIssued = product.getSharesIssued(); if (sharesIssued == null) sharesIssued = product.getTotalShares();
if (unitPrice == null) unitPrice = product.getUnitPrice(); if (unitPrice == null) unitPrice = product.getUnitPrice();
shareCapitalValue = BigDecimal.valueOf(sharesIssued).multiply(unitPrice); shareCapitalValue = BigDecimal.valueOf(sharesIssued).multiply(unitPrice);
if (product.setshareCapitalValue(shareCapitalValue)) { if (product.setshareCapitalValue(shareCapitalValue)) {
actualChanges.put(ShareProductApiConstants.sharecapital_paramname, shareCapitalValue); actualChanges.put(ShareProductApiConstants.sharecapital_paramname, shareCapitalValue);
} }
} }
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
return actualChanges; return actualChanges;
} }
@@ -455,6 +468,7 @@ public class ShareProductDataSerializer {
ShareProductApiConstants.dividendAmountParamName, element); ShareProductApiConstants.dividendAmountParamName, element);
baseDataValidator.reset().parameter(ShareProductApiConstants.dividendAmountParamName).value(dividendAmount).notBlank() baseDataValidator.reset().parameter(ShareProductApiConstants.dividendAmountParamName).value(dividendAmount).notBlank()
.positiveAmount(); .positiveAmount();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
} }
} }
@@ -26,13 +26,14 @@ import java.util.Map;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.products.service.ProductReadPlatformService; import org.apache.fineract.portfolio.products.service.ProductReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.data.PurchasedSharesData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionData;
import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData;
import org.apache.fineract.portfolio.shareaccounts.domain.PurchasedSharesStatusType; import org.apache.fineract.portfolio.shareaccounts.domain.PurchasedSharesStatusType;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendDetails; import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendDetails;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService; import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
import org.apache.fineract.portfolio.shareproducts.data.ShareProductData; import org.apache.fineract.portfolio.shareproducts.data.ShareProductData;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails; import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails;
import org.apache.fineract.portfolio.shareproducts.exception.ShareAccountsNotFoundException;
import org.joda.time.Days; import org.joda.time.Days;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -59,8 +60,15 @@ public class ShareProductDividendAssembler {
.getCurrency().currencyInMultiplesOf()); .getCurrency().currencyInMultiplesOf());
Collection<ShareAccountData> shareAccountDatas = this.ShareAccountReadPlatformService.retrieveAllShareAccountDataForDividends( Collection<ShareAccountData> shareAccountDatas = this.ShareAccountReadPlatformService.retrieveAllShareAccountDataForDividends(
productId, product.getAllowDividendCalculationForInactiveClients(), dividendPeriodStartDate); productId, product.getAllowDividendCalculationForInactiveClients(), dividendPeriodStartDate);
if(shareAccountDatas == null || shareAccountDatas.isEmpty()) {
throw new ShareAccountsNotFoundException(product.getId()) ;
}
ShareProductDividendPayOutDetails productDividendPayOutDetails = null; ShareProductDividendPayOutDetails productDividendPayOutDetails = null;
final int minimumActivePeriod = product.getMinimumActivePeriod(); int minimumActivePeriod = 0 ;
if(product.getMinimumActivePeriod() != null) { //minimum active period may be null
minimumActivePeriod = product.getMinimumActivePeriod();
}
final Map<Long, Long> numberOfSharesdaysPerAccount = new HashMap<>(); final Map<Long, Long> numberOfSharesdaysPerAccount = new HashMap<>();
long numberOfShareDays = calculateNumberOfShareDays(dividendPeriodEndDate, dividendPeriodStartDate, minimumActivePeriod, long numberOfShareDays = calculateNumberOfShareDays(dividendPeriodEndDate, dividendPeriodStartDate, minimumActivePeriod,
shareAccountDatas, numberOfSharesdaysPerAccount); shareAccountDatas, numberOfSharesdaysPerAccount);
@@ -88,10 +96,10 @@ public class ShareProductDividendAssembler {
long numberOfShareDays = 0; long numberOfShareDays = 0;
for (ShareAccountData accountData : shareAccountDatas) { for (ShareAccountData accountData : shareAccountDatas) {
long numberOfShareDaysPerAccount = 0; long numberOfShareDaysPerAccount = 0;
Collection<PurchasedSharesData> purchasedShares = accountData.getPurchasedShares(); Collection<ShareAccountTransactionData> purchasedShares = accountData.getPurchasedShares();
long numberOfShares = 0; long numberOfShares = 0;
LocalDate lastDividendAppliedDate = null; LocalDate lastDividendAppliedDate = null;
for (PurchasedSharesData purchasedSharesData : purchasedShares) { for (ShareAccountTransactionData purchasedSharesData : purchasedShares) {
final PurchasedSharesStatusType status = PurchasedSharesStatusType.fromInt(purchasedSharesData.getStatus().getId() final PurchasedSharesStatusType status = PurchasedSharesStatusType.fromInt(purchasedSharesData.getStatus().getId()
.intValue()); .intValue());
final PurchasedSharesStatusType type = PurchasedSharesStatusType.fromInt(purchasedSharesData.getType().getId() final PurchasedSharesStatusType type = PurchasedSharesStatusType.fromInt(purchasedSharesData.getType().getId()
@@ -243,7 +243,7 @@ public class ShareProductReadPlatformServiceImpl implements ProductReadPlatformS
currencyDisplaySymbol, currencyNameCode); currencyDisplaySymbol, currencyNameCode);
final Long totalShares = rs.getLong("total_shares"); final Long totalShares = rs.getLong("total_shares");
final Long issuedShares = rs.getLong("issued_shares"); final Long issuedShares = JdbcSupport.getLongDefaultToNullIfZero(rs, "issued_shares");
final BigDecimal unitPrice = rs.getBigDecimal("unit_price"); final BigDecimal unitPrice = rs.getBigDecimal("unit_price");
final BigDecimal capitalAmount = rs.getBigDecimal("capital_amount"); final BigDecimal capitalAmount = rs.getBigDecimal("capital_amount");
final Long minimumClientShares = JdbcSupport.getLong(rs, "minimum_client_shares"); final Long minimumClientShares = JdbcSupport.getLong(rs, "minimum_client_shares");
@@ -129,16 +129,18 @@ CREATE TABLE `m_share_account_transactions` (
`unit_price` DECIMAL(10,2) NULL DEFAULT NULL, `unit_price` DECIMAL(10,2) NULL DEFAULT NULL,
`amount` DECIMAL(20,2) NULL DEFAULT NULL, `amount` DECIMAL(20,2) NULL DEFAULT NULL,
`charge_amount` DECIMAL(20,2) NULL DEFAULT NULL, `charge_amount` DECIMAL(20,2) NULL DEFAULT NULL,
`amount_paid` DECIMAL(20,2) NULL DEFAULT NULL,
`status_enum` SMALLINT(5) NOT NULL DEFAULT '300', `status_enum` SMALLINT(5) NOT NULL DEFAULT '300',
`type_enum` SMALLINT(5) NULL DEFAULT NULL, `type_enum` SMALLINT(5) NULL DEFAULT NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `m_share_account_purchased_shares_ibfk_1` FOREIGN KEY (`account_id`) REFERENCES `m_share_account` (`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` ( CREATE TABLE `m_share_account_charge_paid_by` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT, `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`share_transaction_id` BIGINT(20) NOT NULL, `share_transaction_id` BIGINT(20) NULL DEFAULT NULL,
`charge_transaction_id` BIGINT(20) NOT NULL, `charge_transaction_id` BIGINT(20) NULL DEFAULT NULL,
`amount` DECIMAL(20,2) NOT NULL, `amount` DECIMAL(20,2) NOT NULL,
PRIMARY KEY (`id`), 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_ibfk1` FOREIGN KEY (`share_transaction_id`) REFERENCES `m_share_account_transactions` (`id`),