Merge branch 'client-fees' into develop

This commit is contained in:
Vishwas Babu A J
2015-09-01 04:22:35 +05:30
27 changed files with 820 additions and 151 deletions
@@ -223,7 +223,8 @@ public class AccountingConstants {
public static enum FINANCIAL_ACTIVITY {
ASSET_TRANSFER(100, "assetTransfer", GLAccountType.ASSET), LIABILITY_TRANSFER(200, "liabilityTransfer", GLAccountType.LIABILITY),
CASH_AT_MAINVAULT (101, "cashAtMainVault", GLAccountType.ASSET),
CASH_AT_TELLER (102, "cashAtTeller", GLAccountType.ASSET),OPENING_BALANCES_TRANSFER_CONTRA (300,"openingBalancesTransferContra",GLAccountType.EQUITY);;
CASH_AT_TELLER (102, "cashAtTeller", GLAccountType.ASSET),OPENING_BALANCES_TRANSFER_CONTRA (300,"openingBalancesTransferContra",GLAccountType.EQUITY),
ASSET_FUND_SOURCE(103, "fundSource", GLAccountType.ASSET);
private final Integer value;
private final String code;
@@ -25,6 +25,8 @@ public interface AccountingDropdownReadPlatformService {
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForSavingsProducts();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForCharges();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptions();
}
@@ -58,42 +58,79 @@ public class AccountingDropdownReadPlatformServiceImpl implements AccountingDrop
return retrieveAccountMappingOptions();
}
@Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForCharges() {
// only get income and liability accounts
boolean includeIncomeAccounts = true;
boolean includeLiabilityAccounts = true;
boolean includeAssetAccounts = false;
boolean includeExpenseAccounts = false;
boolean includeEquityAccounts = false;
return retrieveAccountMappingOptions(includeAssetAccounts, includeIncomeAccounts, includeExpenseAccounts, includeLiabilityAccounts,
includeEquityAccounts);
}
@Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptions() {
boolean includeAssetAccounts = true;
boolean includeIncomeAccounts = true;
boolean includeExpenseAccounts = true;
boolean includeLiabilityAccounts = true;
boolean includeEquityAccounts = true;
return retrieveAccountMappingOptions(includeAssetAccounts, includeIncomeAccounts, includeExpenseAccounts, includeLiabilityAccounts,
includeEquityAccounts);
}
private Map<String, List<GLAccountData>> retrieveAccountMappingOptions(boolean includeAssetAccounts, boolean includeIncomeAccounts,
boolean includeExpenseAccounts, boolean includeLiabilityAccounts, boolean includeEquityAccounts) {
final Map<String, List<GLAccountData>> accountOptions = new HashMap<>();
List<GLAccountData> assetAccountOptions = this.accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null;
}
accountOptions.put("assetAccountOptions", assetAccountOptions);
List<GLAccountData> incomeAccountOptions = this.accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null;
if (includeAssetAccounts) {
List<GLAccountData> assetAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null;
}
accountOptions.put("assetAccountOptions", assetAccountOptions);
}
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
List<GLAccountData> expenseAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null;
if (includeIncomeAccounts) {
List<GLAccountData> incomeAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null;
}
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
}
accountOptions.put("expenseAccountOptions", expenseAccountOptions);
List<GLAccountData> liabilityAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.LIABILITY);
if (liabilityAccountOptions.isEmpty()) {
liabilityAccountOptions = null;
if (includeExpenseAccounts) {
List<GLAccountData> expenseAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null;
}
accountOptions.put("expenseAccountOptions", expenseAccountOptions);
}
if (includeLiabilityAccounts) {
List<GLAccountData> liabilityAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.LIABILITY);
if (liabilityAccountOptions.isEmpty()) {
liabilityAccountOptions = null;
}
accountOptions.put("liabilityAccountOptions", liabilityAccountOptions);
}
if (includeEquityAccounts) {
List<GLAccountData> equityAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.EQUITY);
if (equityAccountOptions.isEmpty()) {
equityAccountOptions = null;
}
accountOptions.put("equityAccountOptions", equityAccountOptions);
}
accountOptions.put("liabilityAccountOptions", liabilityAccountOptions);
List<GLAccountData> equityAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.EQUITY);
if (equityAccountOptions.isEmpty()) {
equityAccountOptions = null;
}
accountOptions.put("equityAccountOptions",equityAccountOptions);
return accountOptions;
}
@@ -0,0 +1,42 @@
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
public class ClientChargePaymentDTO {
private final Long chargeId;
private final BigDecimal amount;
private final Long clientChargeId;
private final boolean isPenalty;
private final Long incomeAccountId;
public ClientChargePaymentDTO(Long chargeId, BigDecimal amount, Long clientChargeId, boolean isPenalty, Long incomeAccountId) {
super();
this.chargeId = chargeId;
this.amount = amount;
this.clientChargeId = clientChargeId;
this.isPenalty = isPenalty;
this.incomeAccountId = incomeAccountId;
}
public BigDecimal getAmount() {
return this.amount;
}
public Long getClientChargeId() {
return this.clientChargeId;
}
public boolean isPenalty() {
return this.isPenalty;
}
public Long getChargeId() {
return chargeId;
}
public Long getIncomeAccountId() {
return this.incomeAccountId;
}
}
@@ -0,0 +1,100 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.client.domain.ClientTransactionType;
public class ClientTransactionDTO {
private final Long officeId;
private final Long clientId;
private String currencyCode;
private final Long transactionId;
private final Date transactionDate;
private final Long paymentTypeId;
private final EnumOptionData transactionType;
private final BigDecimal amount;
private final Boolean accountingEnabled;
/*** Boolean values determines if the transaction is reversed ***/
private final boolean reversed;
/** Breakdowns of fees this Transaction pays **/
private final List<ClientChargePaymentDTO> chargePayments;
public ClientTransactionDTO(final Long clientId, final Long officeId, final Long paymentTypeId, final Long transactionId,
final Date transactionDate, final EnumOptionData transactionType, final String currencyCode, final BigDecimal amount,
final boolean reversed, final boolean accountingEnabled, final List<ClientChargePaymentDTO> clientChargePayments) {
this.clientId = clientId;
this.paymentTypeId = paymentTypeId;
this.transactionId = transactionId;
this.transactionDate = transactionDate;
this.amount = amount;
this.reversed = reversed;
this.transactionType = transactionType;
this.chargePayments = clientChargePayments;
this.officeId = officeId;
this.accountingEnabled = accountingEnabled;
this.currencyCode = currencyCode;
}
public Long getOfficeId() {
return this.officeId;
}
public Date getTransactionDate() {
return this.transactionDate;
}
public BigDecimal getAmount() {
return this.amount;
}
public boolean isReversed() {
return this.reversed;
}
public Long getPaymentTypeId() {
return this.paymentTypeId;
}
public EnumOptionData getTransactionType() {
return transactionType;
}
public boolean isChargePayment() {
return ClientTransactionType.PAY_CHARGE.getValue().equals(new Integer(this.transactionType.getId().intValue()));
}
public String getCurrencyCode() {
return this.currencyCode;
}
public List<ClientChargePaymentDTO> getChargePayments() {
return this.chargePayments;
}
public Long getTransactionId() {
return this.transactionId;
}
public Boolean getAccountingEnabled() {
return this.accountingEnabled;
}
public Long getClientId() {
return this.clientId;
}
}
@@ -21,6 +21,7 @@ import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
@@ -60,6 +61,10 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@JoinColumn(name = "savings_transaction_id", nullable = false)
private SavingsAccountTransaction savingsTransaction;
@ManyToOne
@JoinColumn(name = "client_transaction_id", nullable = false)
private ClientTransaction clientTransaction;
@Column(name = "reversed", nullable = false)
private boolean reversed = false;
@@ -92,10 +97,10 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
final String currencyCode, final String transactionId, final boolean manualEntry, final Date transactionDate,
final JournalEntryType journalEntryType, final BigDecimal amount, final String description, final Integer entityType,
final Long entityId, final String referenceNumber, final LoanTransaction loanTransaction,
final SavingsAccountTransaction savingsTransaction) {
final SavingsAccountTransaction savingsTransaction, final ClientTransaction clientTransaction) {
return new JournalEntry(office, paymentDetail, glAccount, currencyCode, transactionId, manualEntry, transactionDate,
journalEntryType.getValue(), amount, description, entityType, entityId, referenceNumber, loanTransaction,
savingsTransaction);
savingsTransaction, clientTransaction);
}
protected JournalEntry() {
@@ -105,7 +110,8 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
public JournalEntry(final Office office, final PaymentDetail paymentDetail, final GLAccount glAccount, final String currencyCode,
final String transactionId, final boolean manualEntry, final Date transactionDate, final Integer type, final BigDecimal amount,
final String description, final Integer entityType, final Long entityId, final String referenceNumber,
final LoanTransaction loanTransaction, final SavingsAccountTransaction savingsTransaction) {
final LoanTransaction loanTransaction, final SavingsAccountTransaction savingsTransaction,
final ClientTransaction clientTransaction) {
this.office = office;
this.glAccount = glAccount;
this.reversalJournalEntry = null;
@@ -122,6 +128,7 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
this.currencyCode = currencyCode;
this.loanTransaction = loanTransaction;
this.savingsTransaction = savingsTransaction;
this.clientTransaction = clientTransaction;
this.paymentDetail = paymentDetail;
}
@@ -181,4 +188,8 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
return transactionId;
}
public ClientTransaction getClientTransaction() {
return this.clientTransaction;
}
}
@@ -0,0 +1,8 @@
package org.mifosplatform.accounting.journalentry.service;
import org.mifosplatform.accounting.journalentry.data.ClientTransactionDTO;
public interface AccountingProcessorForClientTransactions {
void createJournalEntriesForClientTransaction(ClientTransactionDTO clientTransactionDTO);
}
@@ -23,7 +23,10 @@ import org.mifosplatform.accounting.common.AccountingConstants.FINANCIAL_ACTIVIT
import org.mifosplatform.accounting.financialactivityaccount.domain.FinancialActivityAccount;
import org.mifosplatform.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.mifosplatform.accounting.journalentry.data.ChargePaymentDTO;
import org.mifosplatform.accounting.journalentry.data.ClientChargePaymentDTO;
import org.mifosplatform.accounting.journalentry.data.ClientTransactionDTO;
import org.mifosplatform.accounting.journalentry.data.LoanDTO;
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
import org.mifosplatform.accounting.journalentry.data.SavingsDTO;
@@ -37,12 +40,15 @@ import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProd
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
import org.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.portfolio.client.domain.ClientTransactionRepositoryWrapper;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
@@ -58,12 +64,15 @@ public class AccountingProcessorHelper {
public static final String LOAN_TRANSACTION_IDENTIFIER = "L";
public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
public static final String CLIENT_TRANSACTION_IDENTIFIER = "C";
private final JournalEntryRepository glJournalEntryRepository;
private final ProductToGLAccountMappingRepository accountMappingRepository;
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
private final GLClosureRepository closureRepository;
private final GLAccountRepositoryWrapper accountRepositoryWrapper;
private final OfficeRepository officeRepository;
private final LoanTransactionRepository loanTransactionRepository;
private final ClientTransactionRepositoryWrapper clientTransactionRepository;
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
@@ -73,7 +82,9 @@ public class AccountingProcessorHelper {
final OfficeRepository officeRepository, final LoanTransactionRepository loanTransactionRepository,
final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
final GLAccountRepositoryWrapper accountRepositoryWrapper,
final ClientTransactionRepositoryWrapper clientTransactionRepositoryWrapper) {
this.glJournalEntryRepository = glJournalEntryRepository;
this.accountMappingRepository = accountMappingRepository;
this.closureRepository = closureRepository;
@@ -82,6 +93,8 @@ public class AccountingProcessorHelper {
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
this.financialActivityAccountRepository = financialActivityAccountRepository;
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
this.accountRepositoryWrapper = accountRepositoryWrapper;
this.clientTransactionRepository = clientTransactionRepositoryWrapper;
}
public LoanDTO populateLoanDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
@@ -202,6 +215,45 @@ public class AccountingProcessorHelper {
accrualBasedAccountingEnabled, newSavingsTransactions);
}
public ClientTransactionDTO populateClientTransactionDtoFromMap(final Map<String, Object> accountingBridgeData) {
final Long transactionOfficeId = (Long) accountingBridgeData.get("officeId");
final Long clientId = (Long) accountingBridgeData.get("clientId");
final Long transactionId = (Long) accountingBridgeData.get("id");
final Date transactionDate = ((LocalDate) accountingBridgeData.get("date")).toDate();
final EnumOptionData transactionType = (EnumOptionData) accountingBridgeData.get("type");
final BigDecimal amount = (BigDecimal) accountingBridgeData.get("amount");
final boolean reversed = (Boolean) accountingBridgeData.get("reversed");
final Long paymentTypeId = (Long) accountingBridgeData.get("paymentTypeId");
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
final Boolean accountingEnabled = (Boolean) accountingBridgeData.get("accountingEnabled");
final List<ClientChargePaymentDTO> clientChargePaymentDTOs = new ArrayList<>();
// extract client charge payment details (if exists)
if (accountingBridgeData.containsKey("clientChargesPaid")) {
@SuppressWarnings("unchecked")
final List<Map<String, Object>> clientChargesPaidData = (List<Map<String, Object>>) accountingBridgeData
.get("clientChargesPaid");
for (final Map<String, Object> clientChargePaid : clientChargesPaidData) {
final Long chargeId = (Long) clientChargePaid.get("chargeId");
final Long clientChargeId = (Long) clientChargePaid.get("clientChargeId");
final boolean isPenalty = (Boolean) clientChargePaid.get("isPenalty");
final BigDecimal chargeAmountPaid = (BigDecimal) clientChargePaid.get("amount");
final Long incomeAccountId = (Long) clientChargePaid.get("incomeAccountId");
final ClientChargePaymentDTO clientChargePaymentDTO = new ClientChargePaymentDTO(chargeId, chargeAmountPaid, clientChargeId,
isPenalty, incomeAccountId);
clientChargePaymentDTOs.add(clientChargePaymentDTO);
}
}
final ClientTransactionDTO clientTransactionDTO = new ClientTransactionDTO(clientId, transactionOfficeId, paymentTypeId,
transactionId, transactionDate, transactionType, currencyCode, amount, reversed, accountingEnabled,
clientChargePaymentDTOs);
return clientTransactionDTO;
}
/**
* Convenience method that creates a pair of related Debits and Credits for
* Accrual Based accounting.
@@ -223,9 +275,8 @@ public class AccountingProcessorHelper {
* @param isReversal
*/
public void createAccrualBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId,
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
int accountTypeToDebitId = accountTypeToBeDebited;
int accountTypeToCreditId = accountTypeToBeCredited;
// reverse debits and credits for reversals
@@ -233,8 +284,8 @@ public class AccountingProcessorHelper {
accountTypeToDebitId = accountTypeToBeCredited;
accountTypeToCreditId = accountTypeToBeDebited;
}
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId,
loanId, transactionId, transactionDate, amount);
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, amount);
}
/**
@@ -354,9 +405,8 @@ public class AccountingProcessorHelper {
* @param isReversal
*/
public void createCashBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId,
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
int accountTypeToDebitId = accountTypeToBeDebited;
int accountTypeToCreditId = accountTypeToBeCredited;
// reverse debits and credits for reversals
@@ -364,8 +414,8 @@ public class AccountingProcessorHelper {
accountTypeToDebitId = accountTypeToBeCredited;
accountTypeToCreditId = accountTypeToBeDebited;
}
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId,
loanId, transactionId, transactionDate, amount);
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, amount);
}
public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode,
@@ -394,8 +444,9 @@ public class AccountingProcessorHelper {
* transaction Date
**/
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(transactionDate) || latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
if (latestGLClosure.getClosingDate().after(transactionDate)
|| latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
}
}
@@ -553,21 +604,21 @@ public class AccountingProcessorHelper {
}
}
private void createCreditJournalEntryForLoan(final Office office, final String currencyCode, final GLAccount account,
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
private void createCreditJournalEntryForClientPayments(final Office office, final String currencyCode, final GLAccount account,
final Long clientId, final Long transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
long id = Long.parseLong(transactionId);
loanTransaction = this.loanTransactionRepository.findOne(id);
modifiedTransactionId = LOAN_TRANSACTION_IDENTIFIER + transactionId;
}
clientTransaction = this.clientTransactionRepository.findOneWithNotFoundDetection(clientId, transactionId);
String modifiedTransactionId = transactionId.toString();
modifiedTransactionId = CLIENT_TRANSACTION_IDENTIFIER + transactionId;
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null,
loanTransaction, savingsAccountTransaction);
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.CLIENT.getValue(), clientId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -576,6 +627,7 @@ public class AccountingProcessorHelper {
final boolean manualEntry = false;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
@@ -585,7 +637,26 @@ public class AccountingProcessorHelper {
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId,
null, loanTransaction, savingsAccountTransaction);
null, loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
private void createCreditJournalEntryForLoan(final Office office, final String currencyCode, final GLAccount account, final Long loanId,
final String transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
long id = Long.parseLong(transactionId);
loanTransaction = this.loanTransactionRepository.findOne(id);
modifiedTransactionId = LOAN_TRANSACTION_IDENTIFIER + transactionId;
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -594,6 +665,7 @@ public class AccountingProcessorHelper {
final boolean manualEntry = false;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
@@ -603,7 +675,7 @@ public class AccountingProcessorHelper {
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null,
loanTransaction, savingsAccountTransaction);
loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -612,6 +684,7 @@ public class AccountingProcessorHelper {
final boolean manualEntry = false;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) {
@@ -620,8 +693,26 @@ public class AccountingProcessorHelper {
modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId;
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId,
null, loanTransaction, savingsAccountTransaction);
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
private void createDebitJournalEntryForClientPayments(final Office office, final String currencyCode, final GLAccount account,
final Long clientId, final Long transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false;
ClientTransaction clientTransaction = null;
LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null;
final PaymentDetail paymentDetail = null;
clientTransaction = this.clientTransactionRepository.findOneWithNotFoundDetection(clientId, transactionId);
String modifiedTransactionId = transactionId.toString();
modifiedTransactionId = CLIENT_TRANSACTION_IDENTIFIER + transactionId;
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.CLIENT.getValue(), clientId, null,
loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
@@ -659,12 +750,13 @@ public class AccountingProcessorHelper {
private GLAccount getLinkedGLAccountForLoanCharges(final Long loanProductId, final int accountMappingTypeId, final Long chargeId) {
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(loanProductId,
PortfolioProductType.LOAN.getValue(), accountMappingTypeId);
/*****
* Get more specific mappings for Charges and penalties (based on the
* actual charge /penalty coupled with the loan product). Note the
* income from fees and income from penalties placeholder ID would be
* the same for both cash and accrual based accounts
*****/
/*****
* Get more specific mappings for Charges and penalties (based
* on the actual charge /penalty coupled with the loan product).
* Note the income from fees and income from penalties
* placeholder ID would be the same for both cash and accrual
* based accounts
*****/
// Vishwas TODO: remove this condition as it should always be true
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue()
@@ -679,15 +771,17 @@ public class AccountingProcessorHelper {
return accountMapping.getGlAccount();
}
private GLAccount getLinkedGLAccountForSavingsCharges(final Long savingsProductId, final int accountMappingTypeId, final Long chargeId) {
private GLAccount getLinkedGLAccountForSavingsCharges(final Long savingsProductId, final int accountMappingTypeId,
final Long chargeId) {
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(savingsProductId,
PortfolioProductType.SAVING.getValue(), accountMappingTypeId);
/*****
* Get more specific mappings for Charges and penalties (based on the
* actual charge /penalty coupled with the loan product). Note the
* income from fees and income from penalties placeholder ID would be
* the same for both cash and accrual based accounts
*****/
/*****
* Get more specific mappings for Charges and penalties (based
* on the actual charge /penalty coupled with the loan product).
* Note the income from fees and income from penalties
* placeholder ID would be the same for both cash and accrual
* based accounts
*****/
// Vishwas TODO: remove this condition as it should always be true
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES.getValue()
@@ -737,4 +831,56 @@ public class AccountingProcessorHelper {
}
return isOrganizationAccount;
}
public BigDecimal createCreditJournalEntryOrReversalForClientPayments(final Office office, final String currencyCode,
final Long clientId, final Long transactionId, final Date transactionDate, final Boolean isReversal,
final List<ClientChargePaymentDTO> clientChargePaymentDTOs) {
/***
* Map to track each account affected and the net credit to be made for
* a particular account
***/
final Map<GLAccount, BigDecimal> creditDetailsMap = new LinkedHashMap<>();
for (final ClientChargePaymentDTO clientChargePaymentDTO : clientChargePaymentDTOs) {
if (clientChargePaymentDTO.getIncomeAccountId() != null) {
final GLAccount chargeSpecificAccount = getGLAccountById(clientChargePaymentDTO.getIncomeAccountId());
BigDecimal chargeSpecificAmount = clientChargePaymentDTO.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);
if (isReversal) {
createDebitJournalEntryForClientPayments(office, currencyCode, account, clientId, transactionId, transactionDate, amount);
} else {
createCreditJournalEntryForClientPayments(office, currencyCode, account, clientId, transactionId, transactionDate, amount);
}
}
return totalCreditedAmount;
}
public void createDebitJournalEntryOrReversalForClientChargePayments(final Office office, final String currencyCode,
final Long clientId, final Long transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
final GLAccount account = financialActivityAccountRepository
.findByFinancialActivityTypeWithNotFoundDetection(FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue()).getGlAccount();
if (isReversal) {
createCreditJournalEntryForClientPayments(office, currencyCode, account, clientId, transactionId, transactionDate, amount);
} else {
createDebitJournalEntryForClientPayments(office, currencyCode, account, clientId, transactionId, transactionDate, amount);
}
}
private GLAccount getGLAccountById(final Long accountId) {
return this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
}
}
@@ -0,0 +1,70 @@
package org.mifosplatform.accounting.journalentry.service;
import java.math.BigDecimal;
import java.util.Date;
import org.mifosplatform.accounting.closure.domain.GLClosure;
import org.mifosplatform.accounting.journalentry.data.ClientTransactionDTO;
import org.mifosplatform.organisation.office.domain.Office;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CashBasedAccountingProcessorForClientTransactions implements AccountingProcessorForClientTransactions {
AccountingProcessorHelper helper;
@Autowired
public CashBasedAccountingProcessorForClientTransactions(final AccountingProcessorHelper accountingProcessorHelper) {
this.helper = accountingProcessorHelper;
}
@Override
public void createJournalEntriesForClientTransaction(ClientTransactionDTO clientTransactionDTO) {
if (clientTransactionDTO.getAccountingEnabled()) {
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(clientTransactionDTO.getOfficeId());
final Date transactionDate = clientTransactionDTO.getTransactionDate();
final Office office = this.helper.getOfficeById(clientTransactionDTO.getOfficeId());
this.helper.checkForBranchClosures(latestGLClosure, transactionDate);
/** Handle client payments **/
if (clientTransactionDTO.isChargePayment()) {
createJournalEntriesForChargePayments(clientTransactionDTO, office);
}
}
}
/**
* Create a single debit to fund source and multiple credits for the income
* account mapped with each charge this payment pays off
*
* In case the loan transaction is a reversal, all debits are turned into
* credits and vice versa
*/
private void createJournalEntriesForChargePayments(final ClientTransactionDTO clientTransactionDTO, final Office office) {
// client properties
final Long clientId = clientTransactionDTO.getClientId();
// transaction properties
final String currencyCode = clientTransactionDTO.getCurrencyCode();
final Long transactionId = clientTransactionDTO.getTransactionId();
final Date transactionDate = clientTransactionDTO.getTransactionDate();
final BigDecimal amount = clientTransactionDTO.getAmount();
final boolean isReversal = clientTransactionDTO.isReversed();
if (amount != null && !(amount.compareTo(BigDecimal.ZERO) == 0)) {
BigDecimal totalCreditedAmount = this.helper.createCreditJournalEntryOrReversalForClientPayments(office, currencyCode, clientId,
transactionId, transactionDate, isReversal, clientTransactionDTO.getChargePayments());
/***
* create a single Debit entry (or reversal) for the entire amount
* that was credited (accounting is turned on at the level of for
* each charge that has been paid by this transaction)
**/
this.helper.createDebitJournalEntryOrReversalForClientChargePayments(office, currencyCode, clientId, transactionId,
transactionDate, totalCreditedAmount, isReversal);
}
}
}
@@ -20,6 +20,8 @@ public interface JournalEntryWritePlatformService {
void createJournalEntriesForSavings(Map<String, Object> accountingBridgeData);
void createJournalEntriesForClientTransactions(Map<String, Object> accountingBridgeData);
CommandProcessingResult defineOpeningBalance(JsonCommand command);
}
@@ -27,6 +27,7 @@ import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformServi
import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand;
import org.mifosplatform.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
import org.mifosplatform.accounting.journalentry.data.ClientTransactionDTO;
import org.mifosplatform.accounting.journalentry.data.LoanDTO;
import org.mifosplatform.accounting.journalentry.data.SavingsDTO;
import org.mifosplatform.accounting.journalentry.domain.JournalEntry;
@@ -52,6 +53,7 @@ import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -82,6 +84,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
private final PlatformSecurityContext context;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper;
private final CashBasedAccountingProcessorForClientTransactions accountingProcessorForClientTransactions;
@Autowired
public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
@@ -93,7 +96,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final GLAccountReadPlatformService glAccountReadPlatformService,
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository, final PlatformSecurityContext context,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper) {
final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper,
final CashBasedAccountingProcessorForClientTransactions accountingProcessorForClientTransactions) {
this.glClosureRepository = glClosureRepository;
this.officeRepository = officeRepository;
this.glJournalEntryRepository = glJournalEntryRepository;
@@ -108,6 +112,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
this.context = context;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
this.financialActivityAccountRepositoryWrapper = financialActivityAccountRepositoryWrapper;
this.accountingProcessorForClientTransactions = accountingProcessorForClientTransactions;
}
@Transactional
@@ -248,18 +253,21 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
}
private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits) {
private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] credits,
final SingleDebitOrCreditEntryCommand[] debits) {
// sum of all debits must be = sum of all credits
BigDecimal creditsSum = BigDecimal.ZERO;
BigDecimal debitsSum = BigDecimal.ZERO;
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
if (creditEntryCommand.getAmount() == null
|| creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
creditsSum = creditsSum.add(creditEntryCommand.getAmount());
}
for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
if (debitEntryCommand.getAmount() == null
|| debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
debitsSum = debitsSum.add(debitEntryCommand.getAmount());
}
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
@@ -283,8 +291,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
@Override
public CommandProcessingResult revertJournalEntry(final JsonCommand command) {
// is the transaction Id valid
final List<JournalEntry> journalEntries = this.glJournalEntryRepository.findUnReversedManualJournalEntriesByTransactionId(command
.getTransactionId());
final List<JournalEntry> journalEntries = this.glJournalEntryRepository
.findUnReversedManualJournalEntriesByTransactionId(command.getTransactionId());
String reversalComment = command.stringValueOfParameterNamed("comments");
if (journalEntries.size() <= 1) { throw new JournalEntriesNotFoundException(command.getTransactionId()); }
@@ -311,12 +319,14 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry,
journalEntry.getTransactionDate(), JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment, null, null,
journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction());
journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(),
journalEntry.getClientTransaction());
} else {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry,
journalEntry.getTransactionDate(), JournalEntryType.DEBIT, journalEntry.getAmount(), reversalComment, null, null,
journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction());
journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(),
journalEntry.getClientTransaction());
}
// save the reversal entry
this.glJournalEntryRepository.saveAndFlush(reversalJournalEntry);
@@ -384,8 +394,9 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
// shouldn't be before an accounting closure
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId());
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(transactionDate) || latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
if (latestGLClosure.getClosingDate().after(transactionDate)
|| latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
}
/*** check if credits and debits are valid **/
@@ -393,16 +404,16 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final SingleDebitOrCreditEntryCommand[] debits = command.getDebits();
// atleast one debit or credit must be present
if (credits == null || credits.length <= 0 || debits == null || debits.length <= 0) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); }
if (credits == null || credits.length <= 0 || debits == null
|| debits.length <= 0) { throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null,
null, null); }
checkDebitAndCreditAmounts(credits, debits);
}
private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, final Office office, final PaymentDetail paymentDetail,
final String currencyCode, final Date transactionDate,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId,
final JournalEntryType type, final String referenceNumber) {
final String currencyCode, final Date transactionDate, final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands,
final String transactionId, final JournalEntryType type, final String referenceNumber) {
final boolean manualEntry = true;
for (final SingleDebitOrCreditEntryCommand singleDebitOrCreditEntryCommand : singleDebitOrCreditEntryCommands) {
final GLAccount glAccount = this.glAccountRepository.findOne(singleDebitOrCreditEntryCommand.getGlAccountId());
@@ -418,9 +429,10 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
/** Validate current code is appropriate **/
this.organisationCurrencyRepository.findOneWithNotFoundDetection(currencyCode);
final ClientTransaction clientTransaction = null;
final JournalEntry glJournalEntry = JournalEntry.createNew(office, paymentDetail, glAccount, currencyCode, transactionId,
manualEntry, transactionDate, type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, referenceNumber,
null, null);
null, null, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(glJournalEntry);
}
}
@@ -499,9 +511,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
private void saveAllDebitOrCreditOpeningBalanceEntries(final JournalEntryCommand command, final Office office,
final String currencyCode, final Date transactionDate,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId,
final JournalEntryType type, final Long contraAccountId) {
final String currencyCode, final Date transactionDate, final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands,
final String transactionId, final JournalEntryType type, final Long contraAccountId) {
final boolean manualEntry = true;
final GLAccount contraAccount = this.glAccountRepository.findOne(contraAccountId);
@@ -526,12 +537,15 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
comments = singleDebitOrCreditEntryCommand.getComments();
}
final ClientTransaction clientTransaction = null;
final JournalEntry glJournalEntry = JournalEntry.createNew(office, null, glAccount, currencyCode, transactionId, manualEntry,
transactionDate, type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, null, null, null);
transactionDate, type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, null, null, null,
clientTransaction);
this.glJournalEntryRepository.saveAndFlush(glJournalEntry);
final JournalEntry contraEntry = JournalEntry.createNew(office, null, contraAccount, currencyCode, transactionId, manualEntry,
transactionDate, contraType, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, null, null, null);
transactionDate, contraType, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, null, null, null,
clientTransaction);
this.glJournalEntryRepository.saveAndFlush(contraEntry);
}
}
@@ -553,4 +567,10 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
"Defining Opening balances not allowed after journal entries posted", transactionIds); }
}
@Override
public void createJournalEntriesForClientTransactions(Map<String, Object> accountingBridgeData) {
final ClientTransactionDTO clientTransactionDTO = this.helper.populateClientTransactionDtoFromMap(accountingBridgeData);
accountingProcessorForClientTransactions.createJournalEntriesForClientTransaction(clientTransactionDTO);
}
}
@@ -9,7 +9,7 @@ import java.util.HashMap;
import java.util.Map;
public enum PortfolioProductType {
LOAN(1, "productType.loan"), SAVING(2, "productType.saving");
LOAN(1, "productType.loan"), SAVING(2, "productType.saving"), CLIENT(2, "productType.client");
private final Integer value;
private final String code;
@@ -33,6 +33,7 @@ public enum PortfolioProductType {
}
private static final Map<Integer, PortfolioProductType> intToEnumMap = new HashMap<>();
static {
for (final PortfolioProductType type : PortfolioProductType.values()) {
intToEnumMap.put(type.value, type);
@@ -52,4 +53,8 @@ public enum PortfolioProductType {
return this.value.equals(PortfolioProductType.LOAN.getValue());
}
public boolean isClient() {
return this.value.equals(PortfolioProductType.CLIENT.getValue());
}
}
@@ -39,6 +39,7 @@ import org.mifosplatform.organisation.teller.exception.CashierExistForTellerExce
import org.mifosplatform.organisation.teller.exception.CashierNotFoundException;
import org.mifosplatform.organisation.teller.exception.TellerNotFoundException;
import org.mifosplatform.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -177,8 +178,8 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
Set<Cashier> isTellerIdPresentInCashier = teller.getCashiers();
for (final Cashier tellerIdInCashier : isTellerIdPresentInCashier) {
if (tellerIdInCashier.getTeller().getId().toString().equalsIgnoreCase(tellerId.toString())) { throw new CashierExistForTellerException(
tellerId); }
if (tellerIdInCashier.getTeller().getId().toString()
.equalsIgnoreCase(tellerId.toString())) { throw new CashierExistForTellerException(tellerId); }
}
tellerRepository.delete(teller);
@@ -407,6 +408,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
final Long time = System.currentTimeMillis();
final String uniqueVal = String.valueOf(time) + currentUser.getId() + cashierOffice.getId();
final String transactionId = Long.toHexString(Long.parseLong(uniqueVal));
ClientTransaction clientTransaction = null;
final JournalEntry debitJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
// detail
@@ -415,7 +417,8 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
transactionId, false, // manual entry
cashierTxn.getTxnDate(), JournalEntryType.DEBIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
null, null, null, // entity Type, entityId, reference number
null, null); // Loan and Savings Txn
null, null, clientTransaction); // Loan and Savings Txn
final JournalEntry creditJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
// detail
creditAccount, "USD", // FIXME: Take currency code from the
@@ -423,7 +426,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
transactionId, false, // manual entry
cashierTxn.getTxnDate(), JournalEntryType.CREDIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
null, null, null, // entity Type, entityId, reference number
null, null); // Loan and Savings Txn
null, null, clientTransaction); // Loan and Savings Txn
this.glJournalEntryRepository.saveAndFlush(debitJournalEntry);
this.glJournalEntryRepository.saveAndFlush(creditJournalEntry);
@@ -0,0 +1,7 @@
package org.mifosplatform.portfolio.charge.api;
public class ChargesApiConstants {
public static final String glAccountIdParamName = "incomeAccountId";
}
@@ -41,10 +41,11 @@ import org.springframework.stereotype.Component;
@Scope("singleton")
public class ChargesApiResource {
private final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "amount", "currency", "penalty",
"active", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions",
"chargeAppliesToOptions", "chargeTimeTypeOptions", "currencyOptions", "loanChargeCalculationTypeOptions",
"loanChargeTimeTypeOptions", "savingsChargeCalculationTypeOptions", "savingsChargeTimeTypeOptions"));
private final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "amount", "currency", "penalty", "active",
"chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "chargeAppliesToOptions",
"chargeTimeTypeOptions", "currencyOptions", "loanChargeCalculationTypeOptions", "loanChargeTimeTypeOptions",
"savingsChargeCalculationTypeOptions", "savingsChargeTimeTypeOptions", "incomeAccount", "clientChargeCalculationTypeOptions",
"clientChargeTimeTypeOptions"));
private final String resourceNameForPermissions = "CHARGE";
@@ -9,9 +9,11 @@ import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
@@ -38,6 +40,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
private final BigDecimal minCap;
private final BigDecimal maxCap;
private final EnumOptionData feeFrequency;
private final GLAccountData incomeOrLiabilityAccount;
private final Collection<CurrencyData> currencyOptions;
private final List<EnumOptionData> chargeCalculationTypeOptions;//
@@ -53,18 +56,21 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
private final List<EnumOptionData> clientChargeTimeTypeOptions;
private final List<EnumOptionData> feeFrequencyOptions;
private final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions;
public static ChargeData template(final Collection<CurrencyData> currencyOptions,
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions,
final List<EnumOptionData> chargeTimeTypeOptions, final List<EnumOptionData> chargePaymentModeOptions,
final List<EnumOptionData> loansChargeCalculationTypeOptions, final List<EnumOptionData> loansChargeTimeTypeOptions,
final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions,
final List<EnumOptionData> clientChargeCalculationTypeOptions, final List<EnumOptionData> clientChargeTimeTypeOptions,
final List<EnumOptionData> feeFrequencyOptions) {
final List<EnumOptionData> feeFrequencyOptions, final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions) {
final GLAccountData account = null;
return new ChargeData(null, null, null, null, null, null, null, null, false, false, currencyOptions, chargeCalculationTypeOptions,
chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions,
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions,
clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, null, null, null, null, null, feeFrequencyOptions);
clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, null, null, null, null, null, feeFrequencyOptions, account,
incomeOrLiabilityAccountOptions);
}
public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) {
@@ -74,13 +80,15 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
template.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions,
template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions,
template.clientChargeCalculationTypeOptions, template.clientChargeTimeTypeOptions, charge.feeOnMonthDay, charge.feeInterval,
charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions);
charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions, charge.incomeOrLiabilityAccount,
template.incomeOrLiabilityAccountOptions);
}
public static ChargeData instance(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
final EnumOptionData chargePaymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final boolean penalty,
final boolean active, final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency) {
final boolean active, final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency,
final GLAccountData accountData) {
final Collection<CurrencyData> currencyOptions = null;
final List<EnumOptionData> chargeCalculationTypeOptions = null;
@@ -94,12 +102,14 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> feeFrequencyOptions = null;
final List<EnumOptionData> clientChargeCalculationTypeOptions = null;
final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions);
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, accountData,
incomeOrLiabilityAccountOptions);
}
public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) {
@@ -128,12 +138,15 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final EnumOptionData feeFrequency = null;
final List<EnumOptionData> feeFrequencyOptions = null;
final GLAccountData account = null;
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions);
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, account,
incomeOrLiabilityAccountOptions);
}
private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
@@ -146,7 +159,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
final List<EnumOptionData> savingsChargeTimeTypeOptions, final List<EnumOptionData> clientChargeCalculationTypeOptions,
final List<EnumOptionData> clientChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval,
final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency,
final List<EnumOptionData> feeFrequencyOptions) {
final List<EnumOptionData> feeFrequencyOptions, final GLAccountData account,
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions) {
this.id = id;
this.name = name;
this.amount = amount;
@@ -174,6 +188,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
this.loanChargeTimeTypeOptions = loansChargeTimeTypeOptions;
this.feeFrequency = feeFrequency;
this.feeFrequencyOptions = feeFrequencyOptions;
this.incomeOrLiabilityAccount = account;
this.incomeOrLiabilityAccountOptions = incomeOrLiabilityAccountOptions;
}
@Override
@@ -13,18 +13,24 @@ import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.joda.time.MonthDay;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.charge.api.ChargesApiConstants;
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.exception.ChargeDueAtDisbursementCannotBePenaltyException;
import org.mifosplatform.portfolio.charge.exception.ChargeMustBePenaltyException;
@@ -85,7 +91,11 @@ public class Charge extends AbstractPersistable<Long> {
@Column(name = "fee_frequency", nullable = true)
private Integer feeFrequency;
public static Charge fromJson(final JsonCommand command) {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "income_or_liability_account_id")
private GLAccount account;
public static Charge fromJson(final JsonCommand command, final GLAccount account) {
final String name = command.stringValueOfParameterNamed("name");
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount");
@@ -108,7 +118,7 @@ public class Charge extends AbstractPersistable<Long> {
final Integer feeFrequency = command.integerValueOfParameterNamed("feeFrequency");
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode,
feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency);
feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, account);
}
protected Charge() {
@@ -118,7 +128,7 @@ public class Charge extends AbstractPersistable<Long> {
private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active,
final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final BigDecimal minCap,
final BigDecimal maxCap, final Integer feeFrequency) {
final BigDecimal maxCap, final Integer feeFrequency, final GLAccount account) {
this.name = name;
this.amount = amount;
this.currencyCode = currencyCode;
@@ -127,6 +137,7 @@ public class Charge extends AbstractPersistable<Long> {
this.chargeCalculation = chargeCalculationType.getValue();
this.penalty = penalty;
this.active = active;
this.account = account;
this.chargePaymentMode = paymentMode == null ? null : paymentMode.getValue();
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
@@ -461,6 +472,11 @@ public class Charge extends AbstractPersistable<Long> {
.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(this.name); }
if (!penalty && ChargeTimeType.fromInt(this.chargeTime).isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
if (command.isChangeInLongParameterNamed(ChargesApiConstants.glAccountIdParamName, getIncomeAccountId())) {
final Long newValue = command.longValueOfParameterNamed(ChargesApiConstants.glAccountIdParamName);
actualChanges.put(ChargesApiConstants.glAccountIdParamName, newValue);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
return actualChanges;
@@ -484,10 +500,14 @@ public class Charge extends AbstractPersistable<Long> {
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
final EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
final EnumOptionData feeFrequencyType = ChargeEnumerations.chargePaymentMode(this.feeFrequency);
GLAccountData accountData = null;
if (account != null) {
accountData = new GLAccountData(account.getId(), account.getName(), account.getGlCode());
}
final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.minCap, this.maxCap,
feeFrequencyType);
feeFrequencyType, accountData);
}
public Integer getChargePaymentMode() {
@@ -525,4 +545,20 @@ public class Charge extends AbstractPersistable<Long> {
public Integer feeFrequency() {
return this.feeFrequency;
}
public GLAccount getAccount() {
return this.account;
}
public void setAccount(GLAccount account) {
this.account = account;
}
private Long getIncomeAccountId() {
Long incomeAccountId = null;
if (this.account != null) {
incomeAccountId = this.account.getId();
}
return incomeAccountId;
}
}
@@ -23,6 +23,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.charge.api.ChargesApiConstants;
import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo;
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
@@ -39,9 +40,10 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("name", "amount", "locale", "currencyCode",
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty",
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency"));
private final Set<String> supportedParameters = new HashSet<>(
Arrays.asList("name", "amount", "locale", "currencyCode", "currencyOptions", "chargeAppliesTo", "chargeTimeType",
"chargeCalculationType", "chargeCalculationTypeOptions", "penalty", "active", "chargePaymentMode", "feeOnMonthDay",
"feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency", ChargesApiConstants.glAccountIdParamName));
private final FromJsonHelper fromApiJsonHelper;
@@ -145,13 +147,18 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
.isOneOfTheseValues(ChargeTimeType.validClientValues());
}
// final ChargeTimeType ctt =
// ChargeTimeType.fromInt(chargeTimeType);
if (chargeCalculationType != null) {
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
.isOneOfTheseValues(ChargeCalculationType.validValuesForSavings());
.isOneOfTheseValues(ChargeCalculationType.validValuesForClients());
}
// GL Account can be linked to clients
if (this.fromApiJsonHelper.parameterExists(ChargesApiConstants.glAccountIdParamName, element)) {
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed(ChargesApiConstants.glAccountIdParamName, element);
baseDataValidator.reset().parameter(ChargesApiConstants.glAccountIdParamName).value(glAccountId).notNull()
.longGreaterThanZero();
}
}
final String name = this.fromApiJsonHelper.extractStringNamed("name", element);
@@ -286,6 +293,12 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("feeFrequency").value(feeFrequency).inMinMaxRange(0, 3);
}
if (this.fromApiJsonHelper.parameterExists(ChargesApiConstants.glAccountIdParamName, element)) {
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed(ChargesApiConstants.glAccountIdParamName, element);
baseDataValidator.reset().parameter(ChargesApiConstants.glAccountIdParamName).value(glAccountId).notNull()
.longGreaterThanZero();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -11,8 +11,11 @@ import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.joda.time.MonthDay;
import org.mifosplatform.accounting.common.AccountingDropdownReadPlatformService;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
@@ -45,16 +48,19 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService;
private final MifosEntityAccessUtil mifosEntityAccessUtil;
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
@Autowired
public ChargeReadPlatformServiceImpl(final CurrencyReadPlatformService currencyReadPlatformService,
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource,
final DropdownReadPlatformService dropdownReadPlatformService, final MifosEntityAccessUtil mifosEntityAccessUtil) {
final DropdownReadPlatformService dropdownReadPlatformService, final MifosEntityAccessUtil mifosEntityAccessUtil,
final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService) {
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.currencyReadPlatformService = currencyReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
this.mifosEntityAccessUtil = mifosEntityAccessUtil;
this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
}
@Override
@@ -118,11 +124,13 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
.retrieveClientCalculationTypes();
final List<EnumOptionData> clientChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveClientCollectionTimeTypes();
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = this.accountingDropdownReadPlatformService
.retrieveAccountMappingOptionsForCharges();
return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions,
allowedChargeTimeOptions, chargePaymentOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
clientChargeTimeTypeOptions, feeFrequencyOptions);
clientChargeTimeTypeOptions, feeFrequencyOptions, incomeOrLiabilityAccountOptions);
}
@Override
@@ -245,8 +253,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
+ "c.is_active as active, oc.name as currencyName, oc.decimal_places as currencyDecimalPlaces, "
+ "oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
+ "oc.internationalized_name_code as currencyNameCode, c.fee_on_day as feeOnDay, c.fee_on_month as feeOnMonth, "
+ "c.fee_interval as feeInterval, c.fee_frequency as feeFrequency,c.min_cap as minCap,c.max_cap as maxCap "
+ "from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code";
+ "c.fee_interval as feeInterval, c.fee_frequency as feeFrequency,c.min_cap as minCap,c.max_cap as maxCap, "
+ "c.income_or_liability_account_id as glAccountId , acc.name as glAccountName, acc.gl_code as glCode "
+ "from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code "
+ " LEFT JOIN acc_gl_account acc on acc.id = c.income_or_liability_account_id ";
}
public String loanProductChargeSchema() {
@@ -303,8 +313,17 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final BigDecimal minCap = rs.getBigDecimal("minCap");
final BigDecimal maxCap = rs.getBigDecimal("maxCap");
// extract GL Account
final Long glAccountId = JdbcSupport.getLong(rs, "glAccountId");
final String glAccountName = rs.getString("glAccountName");
final String glCode = rs.getString("glCode");
GLAccountData glAccountData = null;
if (glAccountId != null) {
glAccountData = new GLAccountData(glAccountId, glAccountName, glCode);
}
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType,
chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, minCap, maxCap, feeFrequencyType);
chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, minCap, maxCap, feeFrequencyType, glAccountData);
}
}
@@ -10,6 +10,8 @@ import java.util.Map;
import javax.sql.DataSource;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -19,6 +21,7 @@ import org.mifosplatform.infrastructure.entityaccess.domain.MifosEntityAccessTyp
import org.mifosplatform.infrastructure.entityaccess.domain.MifosEntityType;
import org.mifosplatform.infrastructure.entityaccess.service.MifosEntityAccessUtil;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.charge.api.ChargesApiConstants;
import org.mifosplatform.portfolio.charge.domain.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
import org.mifosplatform.portfolio.charge.exception.ChargeCannotBeDeletedException;
@@ -47,20 +50,21 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
private final ChargeRepository chargeRepository;
private final LoanProductRepository loanProductRepository;
private final MifosEntityAccessUtil mifosEntityAccessUtil;
private final GLAccountRepositoryWrapper gLAccountRepository;
@Autowired
public ChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final ChargeDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final ChargeRepository chargeRepository, final LoanProductRepository loanProductRepository, final RoutingDataSource dataSource,
final MifosEntityAccessUtil mifosEntityAccessUtil
) {
this.context = context;
final ChargeDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer, final ChargeRepository chargeRepository,
final LoanProductRepository loanProductRepository, final RoutingDataSource dataSource,
final MifosEntityAccessUtil mifosEntityAccessUtil, final GLAccountRepositoryWrapper glAccountRepository) {
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
this.chargeRepository = chargeRepository;
this.loanProductRepository = loanProductRepository;
this.mifosEntityAccessUtil = mifosEntityAccessUtil;
this.gLAccountRepository = glAccountRepository;
}
@Transactional
@@ -68,18 +72,25 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
@CacheEvict(value = "charges", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('ch')")
public CommandProcessingResult createCharge(final JsonCommand command) {
try {
this.context.authenticatedUser();
this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json());
final Charge charge = Charge.fromJson(command);
// Retrieve linked GLAccount for Client charges (if present)
final Long glAccountId = command.longValueOfParameterNamed(ChargesApiConstants.glAccountIdParamName);
GLAccount glAccount = null;
if (glAccountId != null) {
glAccount = this.gLAccountRepository.findOneWithNotFoundDetection(glAccountId);
}
final Charge charge = Charge.fromJson(command, glAccount);
this.chargeRepository.save(charge);
// check if the office specific products are enabled. If yes, then save this savings product against a specific office
// check if the office specific products are enabled. If yes, then
// save this savings product against a specific office
// i.e. this savings product is specific for this office.
mifosEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice(
MifosEntityAccessType.OFFICE_ACCESS_TO_CHARGES,
MifosEntityType.CHARGE,
charge.getId());
mifosEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice(MifosEntityAccessType.OFFICE_ACCESS_TO_CHARGES,
MifosEntityType.CHARGE, charge.getId());
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(charge.getId()).build();
} catch (final DataIntegrityViolationException dve) {
@@ -114,10 +125,21 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
if (isChargeExistWithLoans || isChargeExistWithSavings) { throw new ChargeCannotBeUpdatedException(
"error.msg.charge.cannot.be.updated.it.is.used.in.loan", "This charge cannot be updated, it is used in loan"); }
}
}else if((changes.containsKey("feeFrequency") || changes.containsKey("feeInterval")) && chargeForUpdate.isLoanCharge()){
} else if ((changes.containsKey("feeFrequency") || changes.containsKey("feeInterval")) && chargeForUpdate.isLoanCharge()) {
final Boolean isChargeExistWithLoans = isAnyLoanProductsAssociateWithThisCharge(chargeId);
if (isChargeExistWithLoans) { throw new ChargeCannotBeUpdatedException(
"error.msg.charge.frequency.cannot.be.updated.it.is.used.in.loan", "This charge frequency cannot be updated, it is used in loan"); }
"error.msg.charge.frequency.cannot.be.updated.it.is.used.in.loan",
"This charge frequency cannot be updated, it is used in loan"); }
}
// Has account Id been changed ?
if (changes.containsKey(ChargesApiConstants.glAccountIdParamName)) {
final Long newValue = command.longValueOfParameterNamed(ChargesApiConstants.glAccountIdParamName);
GLAccount newIncomeAccount = null;
if (newValue != null) {
newIncomeAccount = this.gLAccountRepository.findOneWithNotFoundDetection(newValue);
}
chargeForUpdate.setAccount(newIncomeAccount);
}
if (!changes.isEmpty()) {
@@ -144,9 +166,10 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
final Boolean isChargeExistWithSavings = isAnySavingsAssociateWithThisCharge(chargeId);
// TODO: Change error messages around:
if (!loanProducts.isEmpty() || isChargeExistWithLoans || isChargeExistWithSavings) { throw new ChargeCannotBeDeletedException(
"error.msg.charge.cannot.be.deleted.it.is.already.used.in.loan",
"This charge cannot be deleted, it is already used in loan"); }
if (!loanProducts.isEmpty() || isChargeExistWithLoans
|| isChargeExistWithSavings) { throw new ChargeCannotBeDeletedException(
"error.msg.charge.cannot.be.deleted.it.is.already.used.in.loan",
"This charge cannot be deleted, it is already used in loan"); }
chargeForDelete.delete();
@@ -59,6 +59,11 @@ public class ClientChargePaidBy extends AbstractPersistable<Long> {
public void setClientCharge(ClientCharge clientCharge) {
this.clientCharge = clientCharge;
}
public BigDecimal getAmount() {
return this.amount;
}
}
@@ -6,8 +6,12 @@
package org.mifosplatform.portfolio.client.domain;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.CascadeType;
@@ -25,6 +29,8 @@ import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
@@ -123,6 +129,54 @@ public class ClientTransaction extends AbstractPersistable<Long> {
this.reversed = true;
}
/**
* Converts the content of this Client Transaction to a map which can be
* passed to the accounting module
*
* @param currencyData
* @return
*/
public Map<String, Object> toMapData() {
final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
final EnumOptionData transactionType = ClientEnumerations.clientTransactionType(this.typeOf);
Boolean accountingEnabledForAtleastOneCharge = false;
thisTransactionData.put("id", getId());
thisTransactionData.put("officeId", this.office.getId());
thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", Boolean.valueOf(this.reversed));
thisTransactionData.put("date", getTransactionDate());
thisTransactionData.put("currencyCode", this.currencyCode);
thisTransactionData.put("amount", this.amount);
if (this.paymentDetail != null) {
thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId());
}
if (!this.clientChargePaidByCollection.isEmpty()) {
final List<Map<String, Object>> clientChargesPaidData = new ArrayList<>();
for (final ClientChargePaidBy clientChargePaidBy : this.clientChargePaidByCollection) {
final Map<String, Object> clientChargePaidData = new LinkedHashMap<>();
clientChargePaidData.put("chargeId", clientChargePaidBy.getClientCharge().getCharge().getId());
clientChargePaidData.put("isPenalty", clientChargePaidBy.getClientCharge().getCharge().isPenalty());
clientChargePaidData.put("clientChargeId", clientChargePaidBy.getClientCharge().getId());
clientChargePaidData.put("amount", clientChargePaidBy.getAmount());
GLAccount glAccount = clientChargePaidBy.getClientCharge().getCharge().getAccount();
if (glAccount != null) {
accountingEnabledForAtleastOneCharge = true;
clientChargePaidData.put("incomeAccountId", glAccount.getId());
}
clientChargesPaidData.add(clientChargePaidData);
}
thisTransactionData.put("clientChargesPaid", clientChargesPaidData);
}
thisTransactionData.put("accountingEnabled", accountingEnabledForAtleastOneCharge);
return thisTransactionData;
}
public boolean isPayChargeTransaction() {
return ClientTransactionType.PAY_CHARGE.getValue().equals(this.typeOf);
}
@@ -159,4 +213,8 @@ public class ClientTransaction extends AbstractPersistable<Long> {
return this.reversed;
}
public LocalDate getTransactionDate() {
return new LocalDate(this.dateOf);
}
}
@@ -15,6 +15,7 @@ import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
@@ -58,6 +59,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
private final ClientChargeRepositoryWrapper clientChargeRepository;
private final ClientTransactionRepository clientTransactionRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
@Autowired
public ClientChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -65,7 +67,8 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
final ClientRepositoryWrapper clientRepository, final HolidayRepositoryWrapper holidayRepositoryWrapper,
final ConfigurationDomainService configurationDomainService, final ClientChargeRepositoryWrapper clientChargeRepository,
final WorkingDaysRepositoryWrapper workingDaysRepository, final ClientTransactionRepository clientTransactionRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService) {
final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.context = context;
this.chargeRepository = chargeRepository;
this.clientChargeDataValidator = clientChargeDataValidator;
@@ -76,6 +79,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
this.workingDaysRepository = workingDaysRepository;
this.clientTransactionRepository = clientTransactionRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
}
@Override
@@ -134,12 +138,15 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate,
chargePaid, clientCharge.getCurrency().getCode(), getAppUserIfPresent());
this.clientTransactionRepository.save(clientTransaction);
this.clientTransactionRepository.saveAndFlush(clientTransaction);
// update charge paid by associations
final ClientChargePaidBy chargePaidBy = ClientChargePaidBy.instance(clientTransaction, clientCharge, amountPaid);
clientTransaction.getClientChargePaidByCollection().add(chargePaidBy);
// generate accounting entries
generateAccountingEntries(clientTransaction);
return new CommandProcessingResultBuilder() //
.withEntityId(clientCharge.getId()) //
.withOfficeId(clientCharge.getClient().getOffice().getId()) //
@@ -148,6 +155,11 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
}
private void generateAccountingEntries(ClientTransaction clientTransaction) {
Map<String, Object> accountingBridgeData = clientTransaction.toMapData();
journalEntryWritePlatformService.createJournalEntriesForClientTransactions(accountingBridgeData);
}
@Override
public CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId) {
final Client client = this.clientRepository.getActiveClient(clientId);
@@ -294,7 +306,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
*/
@Override
public CommandProcessingResult updateCharge(Long clientId, JsonCommand command) {
this.clientChargeDataValidator.validateAdd(command.json());
// this.clientChargeDataValidator.validateAdd(command.json());
return null;
}
@@ -5,8 +5,10 @@
*/
package org.mifosplatform.portfolio.client.service;
import java.util.Map;
import java.util.Set;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper;
@@ -27,14 +29,17 @@ public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements C
private final ClientRepositoryWrapper clientRepository;
private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
@Autowired
public ClientTransactionWritePlatformServiceJpaRepositoryImpl(final ClientTransactionRepositoryWrapper clientTransactionRepository,
final ClientRepositoryWrapper clientRepositoryWrapper,
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper) {
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper,
JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.clientTransactionRepository = clientTransactionRepository;
this.clientRepository = clientRepositoryWrapper;
this.organisationCurrencyRepository = organisationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
}
@Override
@@ -66,6 +71,10 @@ public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements C
}
}
// generate accounting entries
this.clientTransactionRepository.saveAndFlush(clientTransaction);
generateAccountingEntries(clientTransaction);
return new CommandProcessingResultBuilder() //
.withEntityId(transactionId) //
.withOfficeId(client.officeId()) //
@@ -73,4 +82,9 @@ public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements C
.build();
}
private void generateAccountingEntries(ClientTransaction clientTransaction) {
Map<String, Object> accountingBridgeData = clientTransaction.toMapData();
journalEntryWritePlatformService.createJournalEntriesForClientTransactions(accountingBridgeData);
}
}
@@ -15,6 +15,7 @@ import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
@@ -133,10 +134,13 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
// this field is applicable only for client charges
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions);
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
incomeOrLiabilityAccountOptions);
}
@Override
@@ -10,9 +10,11 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
@@ -142,11 +144,14 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
// this field is applicable only for client charges
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
// TODO AA : revisit for merge conflict - Not sure method signature
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions);
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
incomeOrLiabilityAccountOptions);
}
@Override
@@ -0,0 +1,9 @@
-- Charges have a reference to an Income or Liability account that gets credited when a payment is made
ALTER TABLE `m_charge`
ADD COLUMN `income_or_liability_account_id` BIGINT(20) NULL AFTER `fee_frequency`,
ADD CONSTRAINT `FK_m_charge_acc_gl_account` FOREIGN KEY (`income_or_liability_account_id`) REFERENCES `acc_gl_account` (`id`);
--Journal entries also refer to Client transactions
ALTER TABLE `acc_gl_journal_entry`
ADD COLUMN `client_transaction_id` BIGINT(20) NULL DEFAULT NULL AFTER `savings_transaction_id`,
ADD CONSTRAINT `FK_acc_gl_journal_entry_m_client_transaction` FOREIGN KEY (`client_transaction_id`) REFERENCES `m_client_transaction` (`id`);