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 { public static enum FINANCIAL_ACTIVITY {
ASSET_TRANSFER(100, "assetTransfer", GLAccountType.ASSET), LIABILITY_TRANSFER(200, "liabilityTransfer", GLAccountType.LIABILITY), ASSET_TRANSFER(100, "assetTransfer", GLAccountType.ASSET), LIABILITY_TRANSFER(200, "liabilityTransfer", GLAccountType.LIABILITY),
CASH_AT_MAINVAULT (101, "cashAtMainVault", GLAccountType.ASSET), 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 Integer value;
private final String code; private final String code;
@@ -25,6 +25,8 @@ public interface AccountingDropdownReadPlatformService {
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForSavingsProducts(); public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForSavingsProducts();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForCharges();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptions(); public Map<String, List<GLAccountData>> retrieveAccountMappingOptions();
} }
@@ -58,42 +58,79 @@ public class AccountingDropdownReadPlatformServiceImpl implements AccountingDrop
return retrieveAccountMappingOptions(); 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 @Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptions() { 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<>(); 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 (includeAssetAccounts) {
if (incomeAccountOptions.isEmpty()) { List<GLAccountData> assetAccountOptions = this.accountReadPlatformService
incomeAccountOptions = null; .retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null;
}
accountOptions.put("assetAccountOptions", assetAccountOptions);
} }
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
List<GLAccountData> expenseAccountOptions = this.accountReadPlatformService if (includeIncomeAccounts) {
.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE); List<GLAccountData> incomeAccountOptions = this.accountReadPlatformService
if (expenseAccountOptions.isEmpty()) { .retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
expenseAccountOptions = null; if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null;
}
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
} }
accountOptions.put("expenseAccountOptions", expenseAccountOptions);
List<GLAccountData> liabilityAccountOptions = this.accountReadPlatformService if (includeExpenseAccounts) {
.retrieveAllEnabledDetailGLAccounts(GLAccountType.LIABILITY); List<GLAccountData> expenseAccountOptions = this.accountReadPlatformService
if (liabilityAccountOptions.isEmpty()) { .retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
liabilityAccountOptions = null; 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; 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.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.organisation.office.domain.Office; 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.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; 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) @JoinColumn(name = "savings_transaction_id", nullable = false)
private SavingsAccountTransaction savingsTransaction; private SavingsAccountTransaction savingsTransaction;
@ManyToOne
@JoinColumn(name = "client_transaction_id", nullable = false)
private ClientTransaction clientTransaction;
@Column(name = "reversed", nullable = false) @Column(name = "reversed", nullable = false)
private boolean reversed = 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 String currencyCode, final String transactionId, final boolean manualEntry, final Date transactionDate,
final JournalEntryType journalEntryType, final BigDecimal amount, final String description, final Integer entityType, final JournalEntryType journalEntryType, final BigDecimal amount, final String description, final Integer entityType,
final Long entityId, final String referenceNumber, final LoanTransaction loanTransaction, 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, return new JournalEntry(office, paymentDetail, glAccount, currencyCode, transactionId, manualEntry, transactionDate,
journalEntryType.getValue(), amount, description, entityType, entityId, referenceNumber, loanTransaction, journalEntryType.getValue(), amount, description, entityType, entityId, referenceNumber, loanTransaction,
savingsTransaction); savingsTransaction, clientTransaction);
} }
protected JournalEntry() { 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, 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 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 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.office = office;
this.glAccount = glAccount; this.glAccount = glAccount;
this.reversalJournalEntry = null; this.reversalJournalEntry = null;
@@ -122,6 +128,7 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
this.loanTransaction = loanTransaction; this.loanTransaction = loanTransaction;
this.savingsTransaction = savingsTransaction; this.savingsTransaction = savingsTransaction;
this.clientTransaction = clientTransaction;
this.paymentDetail = paymentDetail; this.paymentDetail = paymentDetail;
} }
@@ -181,4 +188,8 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
return transactionId; 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.FinancialActivityAccount;
import org.mifosplatform.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper; import org.mifosplatform.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
import org.mifosplatform.accounting.glaccount.domain.GLAccount; 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.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.LoanDTO;
import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO; import org.mifosplatform.accounting.journalentry.data.LoanTransactionDTO;
import org.mifosplatform.accounting.journalentry.data.SavingsDTO; 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.ProductToGLAccountMapping;
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException; import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.portfolio.account.PortfolioAccountType; import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; 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.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository; 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 LOAN_TRANSACTION_IDENTIFIER = "L";
public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S"; public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
public static final String CLIENT_TRANSACTION_IDENTIFIER = "C";
private final JournalEntryRepository glJournalEntryRepository; private final JournalEntryRepository glJournalEntryRepository;
private final ProductToGLAccountMappingRepository accountMappingRepository; private final ProductToGLAccountMappingRepository accountMappingRepository;
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository; private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
private final GLClosureRepository closureRepository; private final GLClosureRepository closureRepository;
private final GLAccountRepositoryWrapper accountRepositoryWrapper;
private final OfficeRepository officeRepository; private final OfficeRepository officeRepository;
private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionRepository loanTransactionRepository;
private final ClientTransactionRepositoryWrapper clientTransactionRepository;
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
@@ -73,7 +82,9 @@ public class AccountingProcessorHelper {
final OfficeRepository officeRepository, final LoanTransactionRepository loanTransactionRepository, final OfficeRepository officeRepository, final LoanTransactionRepository loanTransactionRepository,
final SavingsAccountTransactionRepository savingsAccountTransactionRepository, final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository, final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository,
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
final GLAccountRepositoryWrapper accountRepositoryWrapper,
final ClientTransactionRepositoryWrapper clientTransactionRepositoryWrapper) {
this.glJournalEntryRepository = glJournalEntryRepository; this.glJournalEntryRepository = glJournalEntryRepository;
this.accountMappingRepository = accountMappingRepository; this.accountMappingRepository = accountMappingRepository;
this.closureRepository = closureRepository; this.closureRepository = closureRepository;
@@ -82,6 +93,8 @@ public class AccountingProcessorHelper {
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
this.financialActivityAccountRepository = financialActivityAccountRepository; this.financialActivityAccountRepository = financialActivityAccountRepository;
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
this.accountRepositoryWrapper = accountRepositoryWrapper;
this.clientTransactionRepository = clientTransactionRepositoryWrapper;
} }
public LoanDTO populateLoanDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled, public LoanDTO populateLoanDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
@@ -202,6 +215,45 @@ public class AccountingProcessorHelper {
accrualBasedAccountingEnabled, newSavingsTransactions); 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 * Convenience method that creates a pair of related Debits and Credits for
* Accrual Based accounting. * Accrual Based accounting.
@@ -223,9 +275,8 @@ public class AccountingProcessorHelper {
* @param isReversal * @param isReversal
*/ */
public void createAccrualBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode, public void createAccrualBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
final Boolean isReversal) {
int accountTypeToDebitId = accountTypeToBeDebited; int accountTypeToDebitId = accountTypeToBeDebited;
int accountTypeToCreditId = accountTypeToBeCredited; int accountTypeToCreditId = accountTypeToBeCredited;
// reverse debits and credits for reversals // reverse debits and credits for reversals
@@ -233,8 +284,8 @@ public class AccountingProcessorHelper {
accountTypeToDebitId = accountTypeToBeCredited; accountTypeToDebitId = accountTypeToBeCredited;
accountTypeToCreditId = accountTypeToBeDebited; accountTypeToCreditId = accountTypeToBeDebited;
} }
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
loanId, transactionId, transactionDate, amount); transactionId, transactionDate, amount);
} }
/** /**
@@ -354,9 +405,8 @@ public class AccountingProcessorHelper {
* @param isReversal * @param isReversal
*/ */
public void createCashBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode, public void createCashBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
final Boolean isReversal) {
int accountTypeToDebitId = accountTypeToBeDebited; int accountTypeToDebitId = accountTypeToBeDebited;
int accountTypeToCreditId = accountTypeToBeCredited; int accountTypeToCreditId = accountTypeToBeCredited;
// reverse debits and credits for reversals // reverse debits and credits for reversals
@@ -364,8 +414,8 @@ public class AccountingProcessorHelper {
accountTypeToDebitId = accountTypeToBeCredited; accountTypeToDebitId = accountTypeToBeCredited;
accountTypeToCreditId = accountTypeToBeDebited; accountTypeToCreditId = accountTypeToBeDebited;
} }
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
loanId, transactionId, transactionDate, amount); transactionId, transactionDate, amount);
} }
public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode, public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode,
@@ -394,8 +444,9 @@ public class AccountingProcessorHelper {
* transaction Date * transaction Date
**/ **/
if (latestGLClosure != null) { if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(transactionDate) || latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException( if (latestGLClosure.getClosingDate().after(transactionDate)
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); } || 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, private void createCreditJournalEntryForClientPayments(final Office office, final String currencyCode, final GLAccount account,
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount) { final Long clientId, final Long transactionId, final Date transactionDate, final BigDecimal amount) {
final boolean manualEntry = false; final boolean manualEntry = false;
LoanTransaction loanTransaction = null; LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null; SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) { clientTransaction = this.clientTransactionRepository.findOneWithNotFoundDetection(clientId, transactionId);
long id = Long.parseLong(transactionId);
loanTransaction = this.loanTransactionRepository.findOne(id); String modifiedTransactionId = transactionId.toString();
modifiedTransactionId = LOAN_TRANSACTION_IDENTIFIER + transactionId; modifiedTransactionId = CLIENT_TRANSACTION_IDENTIFIER + transactionId;
}
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId, final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null, manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.CLIENT.getValue(), clientId, null,
loanTransaction, savingsAccountTransaction); loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry); this.glJournalEntryRepository.saveAndFlush(journalEntry);
} }
@@ -576,6 +627,7 @@ public class AccountingProcessorHelper {
final boolean manualEntry = false; final boolean manualEntry = false;
LoanTransaction loanTransaction = null; LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null; SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId; String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) { if (StringUtils.isNumeric(transactionId)) {
@@ -585,7 +637,26 @@ public class AccountingProcessorHelper {
} }
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId, final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, 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); this.glJournalEntryRepository.saveAndFlush(journalEntry);
} }
@@ -594,6 +665,7 @@ public class AccountingProcessorHelper {
final boolean manualEntry = false; final boolean manualEntry = false;
LoanTransaction loanTransaction = null; LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null; SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId; String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) { if (StringUtils.isNumeric(transactionId)) {
@@ -603,7 +675,7 @@ public class AccountingProcessorHelper {
} }
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId, final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null, manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.LOAN.getValue(), loanId, null,
loanTransaction, savingsAccountTransaction); loanTransaction, savingsAccountTransaction, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(journalEntry); this.glJournalEntryRepository.saveAndFlush(journalEntry);
} }
@@ -612,6 +684,7 @@ public class AccountingProcessorHelper {
final boolean manualEntry = false; final boolean manualEntry = false;
LoanTransaction loanTransaction = null; LoanTransaction loanTransaction = null;
SavingsAccountTransaction savingsAccountTransaction = null; SavingsAccountTransaction savingsAccountTransaction = null;
ClientTransaction clientTransaction = null;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
String modifiedTransactionId = transactionId; String modifiedTransactionId = transactionId;
if (StringUtils.isNumeric(transactionId)) { if (StringUtils.isNumeric(transactionId)) {
@@ -620,8 +693,26 @@ public class AccountingProcessorHelper {
modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId; modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId;
} }
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId, final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null,
null, loanTransaction, savingsAccountTransaction); 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); this.glJournalEntryRepository.saveAndFlush(journalEntry);
} }
@@ -659,12 +750,13 @@ public class AccountingProcessorHelper {
private GLAccount getLinkedGLAccountForLoanCharges(final Long loanProductId, final int accountMappingTypeId, final Long chargeId) { private GLAccount getLinkedGLAccountForLoanCharges(final Long loanProductId, final int accountMappingTypeId, final Long chargeId) {
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(loanProductId, ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(loanProductId,
PortfolioProductType.LOAN.getValue(), accountMappingTypeId); PortfolioProductType.LOAN.getValue(), accountMappingTypeId);
/***** /*****
* Get more specific mappings for Charges and penalties (based on the * Get more specific mappings for Charges and penalties (based
* actual charge /penalty coupled with the loan product). Note the * on the actual charge /penalty coupled with the loan product).
* income from fees and income from penalties placeholder ID would be * Note the income from fees and income from penalties
* the same for both cash and accrual based accounts * placeholder ID would be the same for both cash and accrual
*****/ * based accounts
*****/
// Vishwas TODO: remove this condition as it should always be true // Vishwas TODO: remove this condition as it should always be true
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue() if (accountMappingTypeId == CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue()
@@ -679,15 +771,17 @@ public class AccountingProcessorHelper {
return accountMapping.getGlAccount(); 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, ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(savingsProductId,
PortfolioProductType.SAVING.getValue(), accountMappingTypeId); PortfolioProductType.SAVING.getValue(), accountMappingTypeId);
/***** /*****
* Get more specific mappings for Charges and penalties (based on the * Get more specific mappings for Charges and penalties (based
* actual charge /penalty coupled with the loan product). Note the * on the actual charge /penalty coupled with the loan product).
* income from fees and income from penalties placeholder ID would be * Note the income from fees and income from penalties
* the same for both cash and accrual based accounts * placeholder ID would be the same for both cash and accrual
*****/ * based accounts
*****/
// Vishwas TODO: remove this condition as it should always be true // Vishwas TODO: remove this condition as it should always be true
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES.getValue() if (accountMappingTypeId == CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES.getValue()
@@ -737,4 +831,56 @@ public class AccountingProcessorHelper {
} }
return isOrganizationAccount; 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 createJournalEntriesForSavings(Map<String, Object> accountingBridgeData);
void createJournalEntriesForClientTransactions(Map<String, Object> accountingBridgeData);
CommandProcessingResult defineOpeningBalance(JsonCommand command); 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.api.JournalEntryJsonInputParams;
import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand; import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand;
import org.mifosplatform.accounting.journalentry.command.SingleDebitOrCreditEntryCommand; 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.LoanDTO;
import org.mifosplatform.accounting.journalentry.data.SavingsDTO; import org.mifosplatform.accounting.journalentry.data.SavingsDTO;
import org.mifosplatform.accounting.journalentry.domain.JournalEntry; 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.OfficeRepository;
import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; 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.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
@@ -82,6 +84,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper; private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper;
private final CashBasedAccountingProcessorForClientTransactions accountingProcessorForClientTransactions;
@Autowired @Autowired
public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository, public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
@@ -93,7 +96,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final GLAccountReadPlatformService glAccountReadPlatformService, final GLAccountReadPlatformService glAccountReadPlatformService,
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository, final PlatformSecurityContext context, final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository, final PlatformSecurityContext context,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper) { final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper,
final CashBasedAccountingProcessorForClientTransactions accountingProcessorForClientTransactions) {
this.glClosureRepository = glClosureRepository; this.glClosureRepository = glClosureRepository;
this.officeRepository = officeRepository; this.officeRepository = officeRepository;
this.glJournalEntryRepository = glJournalEntryRepository; this.glJournalEntryRepository = glJournalEntryRepository;
@@ -108,6 +112,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
this.context = context; this.context = context;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
this.financialActivityAccountRepositoryWrapper = financialActivityAccountRepositoryWrapper; this.financialActivityAccountRepositoryWrapper = financialActivityAccountRepositoryWrapper;
this.accountingProcessorForClientTransactions = accountingProcessorForClientTransactions;
} }
@Transactional @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 // sum of all debits must be = sum of all credits
BigDecimal creditsSum = BigDecimal.ZERO; BigDecimal creditsSum = BigDecimal.ZERO;
BigDecimal debitsSum = BigDecimal.ZERO; BigDecimal debitsSum = BigDecimal.ZERO;
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) { for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException( if (creditEntryCommand.getAmount() == null
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, 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()); creditsSum = creditsSum.add(creditEntryCommand.getAmount());
} }
for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) { for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException( if (debitEntryCommand.getAmount() == null
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, 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()); debitsSum = debitsSum.add(debitEntryCommand.getAmount());
} }
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException( if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
@@ -283,8 +291,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
@Override @Override
public CommandProcessingResult revertJournalEntry(final JsonCommand command) { public CommandProcessingResult revertJournalEntry(final JsonCommand command) {
// is the transaction Id valid // is the transaction Id valid
final List<JournalEntry> journalEntries = this.glJournalEntryRepository.findUnReversedManualJournalEntriesByTransactionId(command final List<JournalEntry> journalEntries = this.glJournalEntryRepository
.getTransactionId()); .findUnReversedManualJournalEntriesByTransactionId(command.getTransactionId());
String reversalComment = command.stringValueOfParameterNamed("comments"); String reversalComment = command.stringValueOfParameterNamed("comments");
if (journalEntries.size() <= 1) { throw new JournalEntriesNotFoundException(command.getTransactionId()); } 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(), reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry, journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry,
journalEntry.getTransactionDate(), JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment, null, null, journalEntry.getTransactionDate(), JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment, null, null,
journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction()); journalEntry.getReferenceNumber(), journalEntry.getLoanTransaction(), journalEntry.getSavingsTransaction(),
journalEntry.getClientTransaction());
} else { } else {
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(), reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetails(),
journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry, journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), reversalTransactionId, manualEntry,
journalEntry.getTransactionDate(), JournalEntryType.DEBIT, journalEntry.getAmount(), reversalComment, null, null, 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 // save the reversal entry
this.glJournalEntryRepository.saveAndFlush(reversalJournalEntry); this.glJournalEntryRepository.saveAndFlush(reversalJournalEntry);
@@ -384,8 +394,9 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
// shouldn't be before an accounting closure // shouldn't be before an accounting closure
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId()); final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId());
if (latestGLClosure != null) { if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(transactionDate) || latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException( if (latestGLClosure.getClosingDate().after(transactionDate)
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); } || 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 **/ /*** check if credits and debits are valid **/
@@ -393,16 +404,16 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final SingleDebitOrCreditEntryCommand[] debits = command.getDebits(); final SingleDebitOrCreditEntryCommand[] debits = command.getDebits();
// atleast one debit or credit must be present // atleast one debit or credit must be present
if (credits == null || credits.length <= 0 || debits == null || debits.length <= 0) { throw new JournalEntryInvalidException( if (credits == null || credits.length <= 0 || debits == null
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); } || debits.length <= 0) { throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null,
null, null); }
checkDebitAndCreditAmounts(credits, debits); checkDebitAndCreditAmounts(credits, debits);
} }
private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, final Office office, final PaymentDetail paymentDetail, private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, final Office office, final PaymentDetail paymentDetail,
final String currencyCode, final Date transactionDate, final String currencyCode, final Date transactionDate, final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId, final String transactionId, final JournalEntryType type, final String referenceNumber) {
final JournalEntryType type, final String referenceNumber) {
final boolean manualEntry = true; final boolean manualEntry = true;
for (final SingleDebitOrCreditEntryCommand singleDebitOrCreditEntryCommand : singleDebitOrCreditEntryCommands) { for (final SingleDebitOrCreditEntryCommand singleDebitOrCreditEntryCommand : singleDebitOrCreditEntryCommands) {
final GLAccount glAccount = this.glAccountRepository.findOne(singleDebitOrCreditEntryCommand.getGlAccountId()); final GLAccount glAccount = this.glAccountRepository.findOne(singleDebitOrCreditEntryCommand.getGlAccountId());
@@ -418,9 +429,10 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
/** Validate current code is appropriate **/ /** Validate current code is appropriate **/
this.organisationCurrencyRepository.findOneWithNotFoundDetection(currencyCode); this.organisationCurrencyRepository.findOneWithNotFoundDetection(currencyCode);
final ClientTransaction clientTransaction = null;
final JournalEntry glJournalEntry = JournalEntry.createNew(office, paymentDetail, glAccount, currencyCode, transactionId, final JournalEntry glJournalEntry = JournalEntry.createNew(office, paymentDetail, glAccount, currencyCode, transactionId,
manualEntry, transactionDate, type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, referenceNumber, manualEntry, transactionDate, type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, referenceNumber,
null, null); null, null, clientTransaction);
this.glJournalEntryRepository.saveAndFlush(glJournalEntry); this.glJournalEntryRepository.saveAndFlush(glJournalEntry);
} }
} }
@@ -499,9 +511,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
} }
private void saveAllDebitOrCreditOpeningBalanceEntries(final JournalEntryCommand command, final Office office, private void saveAllDebitOrCreditOpeningBalanceEntries(final JournalEntryCommand command, final Office office,
final String currencyCode, final Date transactionDate, final String currencyCode, final Date transactionDate, final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId, final String transactionId, final JournalEntryType type, final Long contraAccountId) {
final JournalEntryType type, final Long contraAccountId) {
final boolean manualEntry = true; final boolean manualEntry = true;
final GLAccount contraAccount = this.glAccountRepository.findOne(contraAccountId); final GLAccount contraAccount = this.glAccountRepository.findOne(contraAccountId);
@@ -526,12 +537,15 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
comments = singleDebitOrCreditEntryCommand.getComments(); comments = singleDebitOrCreditEntryCommand.getComments();
} }
final ClientTransaction clientTransaction = null;
final JournalEntry glJournalEntry = JournalEntry.createNew(office, null, glAccount, currencyCode, transactionId, manualEntry, 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); this.glJournalEntryRepository.saveAndFlush(glJournalEntry);
final JournalEntry contraEntry = JournalEntry.createNew(office, null, contraAccount, currencyCode, transactionId, manualEntry, 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); this.glJournalEntryRepository.saveAndFlush(contraEntry);
} }
} }
@@ -553,4 +567,10 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
"Defining Opening balances not allowed after journal entries posted", transactionIds); } "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; import java.util.Map;
public enum PortfolioProductType { 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 Integer value;
private final String code; private final String code;
@@ -33,6 +33,7 @@ public enum PortfolioProductType {
} }
private static final Map<Integer, PortfolioProductType> intToEnumMap = new HashMap<>(); private static final Map<Integer, PortfolioProductType> intToEnumMap = new HashMap<>();
static { static {
for (final PortfolioProductType type : PortfolioProductType.values()) { for (final PortfolioProductType type : PortfolioProductType.values()) {
intToEnumMap.put(type.value, type); intToEnumMap.put(type.value, type);
@@ -52,4 +53,8 @@ public enum PortfolioProductType {
return this.value.equals(PortfolioProductType.LOAN.getValue()); 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.CashierNotFoundException;
import org.mifosplatform.organisation.teller.exception.TellerNotFoundException; import org.mifosplatform.organisation.teller.exception.TellerNotFoundException;
import org.mifosplatform.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer; import org.mifosplatform.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -177,8 +178,8 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
Set<Cashier> isTellerIdPresentInCashier = teller.getCashiers(); Set<Cashier> isTellerIdPresentInCashier = teller.getCashiers();
for (final Cashier tellerIdInCashier : isTellerIdPresentInCashier) { for (final Cashier tellerIdInCashier : isTellerIdPresentInCashier) {
if (tellerIdInCashier.getTeller().getId().toString().equalsIgnoreCase(tellerId.toString())) { throw new CashierExistForTellerException( if (tellerIdInCashier.getTeller().getId().toString()
tellerId); } .equalsIgnoreCase(tellerId.toString())) { throw new CashierExistForTellerException(tellerId); }
} }
tellerRepository.delete(teller); tellerRepository.delete(teller);
@@ -407,6 +408,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
final Long time = System.currentTimeMillis(); final Long time = System.currentTimeMillis();
final String uniqueVal = String.valueOf(time) + currentUser.getId() + cashierOffice.getId(); final String uniqueVal = String.valueOf(time) + currentUser.getId() + cashierOffice.getId();
final String transactionId = Long.toHexString(Long.parseLong(uniqueVal)); final String transactionId = Long.toHexString(Long.parseLong(uniqueVal));
ClientTransaction clientTransaction = null;
final JournalEntry debitJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment final JournalEntry debitJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
// detail // detail
@@ -415,7 +417,8 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
transactionId, false, // manual entry transactionId, false, // manual entry
cashierTxn.getTxnDate(), JournalEntryType.DEBIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description cashierTxn.getTxnDate(), JournalEntryType.DEBIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
null, null, null, // entity Type, entityId, reference number 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 final JournalEntry creditJournalEntry = JournalEntry.createNew(cashierOffice, null, // payment
// detail // detail
creditAccount, "USD", // FIXME: Take currency code from the creditAccount, "USD", // FIXME: Take currency code from the
@@ -423,7 +426,7 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
transactionId, false, // manual entry transactionId, false, // manual entry
cashierTxn.getTxnDate(), JournalEntryType.CREDIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description cashierTxn.getTxnDate(), JournalEntryType.CREDIT, cashierTxn.getTxnAmount(), cashierTxn.getTxnNote(), // Description
null, null, null, // entity Type, entityId, reference number 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(debitJournalEntry);
this.glJournalEntryRepository.saveAndFlush(creditJournalEntry); 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") @Scope("singleton")
public class ChargesApiResource { public class ChargesApiResource {
private final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "amount", "currency", "penalty", private final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "amount", "currency", "penalty", "active",
"active", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "chargeAppliesToOptions",
"chargeAppliesToOptions", "chargeTimeTypeOptions", "currencyOptions", "loanChargeCalculationTypeOptions", "chargeTimeTypeOptions", "currencyOptions", "loanChargeCalculationTypeOptions", "loanChargeTimeTypeOptions",
"loanChargeTimeTypeOptions", "savingsChargeCalculationTypeOptions", "savingsChargeTimeTypeOptions")); "savingsChargeCalculationTypeOptions", "savingsChargeTimeTypeOptions", "incomeAccount", "clientChargeCalculationTypeOptions",
"clientChargeTimeTypeOptions"));
private final String resourceNameForPermissions = "CHARGE"; private final String resourceNameForPermissions = "CHARGE";
@@ -9,9 +9,11 @@ import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay; import org.joda.time.MonthDay;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; 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 minCap;
private final BigDecimal maxCap; private final BigDecimal maxCap;
private final EnumOptionData feeFrequency; private final EnumOptionData feeFrequency;
private final GLAccountData incomeOrLiabilityAccount;
private final Collection<CurrencyData> currencyOptions; private final Collection<CurrencyData> currencyOptions;
private final List<EnumOptionData> chargeCalculationTypeOptions;// 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> clientChargeTimeTypeOptions;
private final List<EnumOptionData> feeFrequencyOptions; private final List<EnumOptionData> feeFrequencyOptions;
private final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions;
public static ChargeData template(final Collection<CurrencyData> currencyOptions, public static ChargeData template(final Collection<CurrencyData> currencyOptions,
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions, final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions,
final List<EnumOptionData> chargeTimeTypeOptions, final List<EnumOptionData> chargePaymentModeOptions, final List<EnumOptionData> chargeTimeTypeOptions, final List<EnumOptionData> chargePaymentModeOptions,
final List<EnumOptionData> loansChargeCalculationTypeOptions, final List<EnumOptionData> loansChargeTimeTypeOptions, final List<EnumOptionData> loansChargeCalculationTypeOptions, final List<EnumOptionData> loansChargeTimeTypeOptions,
final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions, final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions,
final List<EnumOptionData> clientChargeCalculationTypeOptions, final List<EnumOptionData> clientChargeTimeTypeOptions, 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, return new ChargeData(null, null, null, null, null, null, null, null, false, false, currencyOptions, chargeCalculationTypeOptions,
chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions,
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, 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) { 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.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions,
template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions, template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions,
template.clientChargeCalculationTypeOptions, template.clientChargeTimeTypeOptions, charge.feeOnMonthDay, charge.feeInterval, 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, 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 chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
final EnumOptionData chargePaymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final boolean penalty, 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 Collection<CurrencyData> currencyOptions = null;
final List<EnumOptionData> chargeCalculationTypeOptions = 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> feeFrequencyOptions = null;
final List<EnumOptionData> clientChargeCalculationTypeOptions = null; final List<EnumOptionData> clientChargeCalculationTypeOptions = null;
final List<EnumOptionData> clientChargeTimeTypeOptions = null; final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode, return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, 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) { 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 List<EnumOptionData> clientChargeTimeTypeOptions = null;
final EnumOptionData feeFrequency = null; final EnumOptionData feeFrequency = null;
final List<EnumOptionData> feeFrequencyOptions = 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, return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, 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, 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> savingsChargeTimeTypeOptions, final List<EnumOptionData> clientChargeCalculationTypeOptions,
final List<EnumOptionData> clientChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval, final List<EnumOptionData> clientChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval,
final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency, 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.id = id;
this.name = name; this.name = name;
this.amount = amount; this.amount = amount;
@@ -174,6 +188,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
this.loanChargeTimeTypeOptions = loansChargeTimeTypeOptions; this.loanChargeTimeTypeOptions = loansChargeTimeTypeOptions;
this.feeFrequency = feeFrequency; this.feeFrequency = feeFrequency;
this.feeFrequencyOptions = feeFrequencyOptions; this.feeFrequencyOptions = feeFrequencyOptions;
this.incomeOrLiabilityAccount = account;
this.incomeOrLiabilityAccountOptions = incomeOrLiabilityAccountOptions;
} }
@Override @Override
@@ -13,18 +13,24 @@ import java.util.Map;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint; import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.HashCodeBuilder;
import org.joda.time.MonthDay; 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.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.organisation.monetary.data.CurrencyData; 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.data.ChargeData;
import org.mifosplatform.portfolio.charge.exception.ChargeDueAtDisbursementCannotBePenaltyException; import org.mifosplatform.portfolio.charge.exception.ChargeDueAtDisbursementCannotBePenaltyException;
import org.mifosplatform.portfolio.charge.exception.ChargeMustBePenaltyException; import org.mifosplatform.portfolio.charge.exception.ChargeMustBePenaltyException;
@@ -85,7 +91,11 @@ public class Charge extends AbstractPersistable<Long> {
@Column(name = "fee_frequency", nullable = true) @Column(name = "fee_frequency", nullable = true)
private Integer feeFrequency; 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 String name = command.stringValueOfParameterNamed("name");
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount"); final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount");
@@ -108,7 +118,7 @@ public class Charge extends AbstractPersistable<Long> {
final Integer feeFrequency = command.integerValueOfParameterNamed("feeFrequency"); final Integer feeFrequency = command.integerValueOfParameterNamed("feeFrequency");
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode, 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() { 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, 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 ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active,
final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final BigDecimal minCap, 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.name = name;
this.amount = amount; this.amount = amount;
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
@@ -127,6 +137,7 @@ public class Charge extends AbstractPersistable<Long> {
this.chargeCalculation = chargeCalculationType.getValue(); this.chargeCalculation = chargeCalculationType.getValue();
this.penalty = penalty; this.penalty = penalty;
this.active = active; this.active = active;
this.account = account;
this.chargePaymentMode = paymentMode == null ? null : paymentMode.getValue(); this.chargePaymentMode = paymentMode == null ? null : paymentMode.getValue();
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
@@ -461,6 +472,11 @@ public class Charge extends AbstractPersistable<Long> {
.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(this.name); } .isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(this.name); }
if (!penalty && ChargeTimeType.fromInt(this.chargeTime).isOverdueInstallment()) { throw new ChargeMustBePenaltyException(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); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
return actualChanges; return actualChanges;
@@ -484,10 +500,14 @@ public class Charge extends AbstractPersistable<Long> {
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation); final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
final EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode); final EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
final EnumOptionData feeFrequencyType = ChargeEnumerations.chargePaymentMode(this.feeFrequency); 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); final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.minCap, this.maxCap, chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.minCap, this.maxCap,
feeFrequencyType); feeFrequencyType, accountData);
} }
public Integer getChargePaymentMode() { public Integer getChargePaymentMode() {
@@ -525,4 +545,20 @@ public class Charge extends AbstractPersistable<Long> {
public Integer feeFrequency() { public Integer feeFrequency() {
return this.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.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; 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.ChargeAppliesTo;
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType; import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode; import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
@@ -39,9 +40,10 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
/** /**
* The parameters supported for this command. * The parameters supported for this command.
*/ */
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("name", "amount", "locale", "currencyCode", private final Set<String> supportedParameters = new HashSet<>(
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty", Arrays.asList("name", "amount", "locale", "currencyCode", "currencyOptions", "chargeAppliesTo", "chargeTimeType",
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency")); "chargeCalculationType", "chargeCalculationTypeOptions", "penalty", "active", "chargePaymentMode", "feeOnMonthDay",
"feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency", ChargesApiConstants.glAccountIdParamName));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -145,13 +147,18 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
.isOneOfTheseValues(ChargeTimeType.validClientValues()); .isOneOfTheseValues(ChargeTimeType.validClientValues());
} }
// final ChargeTimeType ctt =
// ChargeTimeType.fromInt(chargeTimeType);
if (chargeCalculationType != null) { if (chargeCalculationType != null) {
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType) 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); 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); 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); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -11,8 +11,11 @@ import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import org.joda.time.MonthDay; 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.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
@@ -45,16 +48,19 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService; private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService; private final DropdownReadPlatformService dropdownReadPlatformService;
private final MifosEntityAccessUtil mifosEntityAccessUtil; private final MifosEntityAccessUtil mifosEntityAccessUtil;
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
@Autowired @Autowired
public ChargeReadPlatformServiceImpl(final CurrencyReadPlatformService currencyReadPlatformService, public ChargeReadPlatformServiceImpl(final CurrencyReadPlatformService currencyReadPlatformService,
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource, 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.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
this.currencyReadPlatformService = currencyReadPlatformService; this.currencyReadPlatformService = currencyReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService;
this.mifosEntityAccessUtil = mifosEntityAccessUtil; this.mifosEntityAccessUtil = mifosEntityAccessUtil;
this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
} }
@Override @Override
@@ -118,11 +124,13 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
.retrieveClientCalculationTypes(); .retrieveClientCalculationTypes();
final List<EnumOptionData> clientChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveClientCollectionTimeTypes(); final List<EnumOptionData> clientChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveClientCollectionTimeTypes();
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = this.accountingDropdownReadPlatformService
.retrieveAccountMappingOptionsForCharges();
return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions, return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions,
allowedChargeTimeOptions, chargePaymentOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, allowedChargeTimeOptions, chargePaymentOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
clientChargeTimeTypeOptions, feeFrequencyOptions); clientChargeTimeTypeOptions, feeFrequencyOptions, incomeOrLiabilityAccountOptions);
} }
@Override @Override
@@ -245,8 +253,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
+ "c.is_active as active, oc.name as currencyName, oc.decimal_places as currencyDecimalPlaces, " + "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.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, " + "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 " + "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.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() { public String loanProductChargeSchema() {
@@ -303,8 +313,17 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final BigDecimal minCap = rs.getBigDecimal("minCap"); final BigDecimal minCap = rs.getBigDecimal("minCap");
final BigDecimal maxCap = rs.getBigDecimal("maxCap"); 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, 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 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.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; 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.domain.MifosEntityType;
import org.mifosplatform.infrastructure.entityaccess.service.MifosEntityAccessUtil; import org.mifosplatform.infrastructure.entityaccess.service.MifosEntityAccessUtil;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; 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.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargeRepository; import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
import org.mifosplatform.portfolio.charge.exception.ChargeCannotBeDeletedException; import org.mifosplatform.portfolio.charge.exception.ChargeCannotBeDeletedException;
@@ -47,20 +50,21 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
private final ChargeRepository chargeRepository; private final ChargeRepository chargeRepository;
private final LoanProductRepository loanProductRepository; private final LoanProductRepository loanProductRepository;
private final MifosEntityAccessUtil mifosEntityAccessUtil; private final MifosEntityAccessUtil mifosEntityAccessUtil;
private final GLAccountRepositoryWrapper gLAccountRepository;
@Autowired @Autowired
public ChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public ChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final ChargeDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer, final ChargeDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer, final ChargeRepository chargeRepository,
final ChargeRepository chargeRepository, final LoanProductRepository loanProductRepository, final RoutingDataSource dataSource, final LoanProductRepository loanProductRepository, final RoutingDataSource dataSource,
final MifosEntityAccessUtil mifosEntityAccessUtil final MifosEntityAccessUtil mifosEntityAccessUtil, final GLAccountRepositoryWrapper glAccountRepository) {
) { this.context = context;
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.dataSource = dataSource; this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource); this.jdbcTemplate = new JdbcTemplate(this.dataSource);
this.chargeRepository = chargeRepository; this.chargeRepository = chargeRepository;
this.loanProductRepository = loanProductRepository; this.loanProductRepository = loanProductRepository;
this.mifosEntityAccessUtil = mifosEntityAccessUtil; this.mifosEntityAccessUtil = mifosEntityAccessUtil;
this.gLAccountRepository = glAccountRepository;
} }
@Transactional @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')") @CacheEvict(value = "charges", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('ch')")
public CommandProcessingResult createCharge(final JsonCommand command) { public CommandProcessingResult createCharge(final JsonCommand command) {
try { try {
this.context.authenticatedUser(); this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForCreate(command.json()); 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); 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. // i.e. this savings product is specific for this office.
mifosEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice( mifosEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice(MifosEntityAccessType.OFFICE_ACCESS_TO_CHARGES,
MifosEntityAccessType.OFFICE_ACCESS_TO_CHARGES, MifosEntityType.CHARGE, charge.getId());
MifosEntityType.CHARGE,
charge.getId());
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(charge.getId()).build(); return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(charge.getId()).build();
} catch (final DataIntegrityViolationException dve) { } catch (final DataIntegrityViolationException dve) {
@@ -114,10 +125,21 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
if (isChargeExistWithLoans || isChargeExistWithSavings) { throw new ChargeCannotBeUpdatedException( 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"); } "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); final Boolean isChargeExistWithLoans = isAnyLoanProductsAssociateWithThisCharge(chargeId);
if (isChargeExistWithLoans) { throw new ChargeCannotBeUpdatedException( 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()) { if (!changes.isEmpty()) {
@@ -144,9 +166,10 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
final Boolean isChargeExistWithSavings = isAnySavingsAssociateWithThisCharge(chargeId); final Boolean isChargeExistWithSavings = isAnySavingsAssociateWithThisCharge(chargeId);
// TODO: Change error messages around: // TODO: Change error messages around:
if (!loanProducts.isEmpty() || isChargeExistWithLoans || isChargeExistWithSavings) { throw new ChargeCannotBeDeletedException( if (!loanProducts.isEmpty() || isChargeExistWithLoans
"error.msg.charge.cannot.be.deleted.it.is.already.used.in.loan", || isChargeExistWithSavings) { throw new ChargeCannotBeDeletedException(
"This charge cannot be deleted, it is already used in loan"); } "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(); chargeForDelete.delete();
@@ -59,6 +59,11 @@ public class ClientChargePaidBy extends AbstractPersistable<Long> {
public void setClientCharge(ClientCharge clientCharge) { public void setClientCharge(ClientCharge clientCharge) {
this.clientCharge = clientCharge; this.clientCharge = clientCharge;
}
public BigDecimal getAmount() {
return this.amount;
} }
} }
@@ -6,8 +6,12 @@
package org.mifosplatform.portfolio.client.domain; package org.mifosplatform.portfolio.client.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
@@ -25,6 +29,8 @@ import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption; import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.LocalDate; 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.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.monetary.domain.Money;
@@ -123,6 +129,54 @@ public class ClientTransaction extends AbstractPersistable<Long> {
this.reversed = true; 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() { public boolean isPayChargeTransaction() {
return ClientTransactionType.PAY_CHARGE.getValue().equals(this.typeOf); return ClientTransactionType.PAY_CHARGE.getValue().equals(this.typeOf);
} }
@@ -159,4 +213,8 @@ public class ClientTransaction extends AbstractPersistable<Long> {
return this.reversed; 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.LocalDate;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
@@ -58,6 +59,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
private final ClientChargeRepositoryWrapper clientChargeRepository; private final ClientChargeRepositoryWrapper clientChargeRepository;
private final ClientTransactionRepository clientTransactionRepository; private final ClientTransactionRepository clientTransactionRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
@Autowired @Autowired
public ClientChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public ClientChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -65,7 +67,8 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
final ClientRepositoryWrapper clientRepository, final HolidayRepositoryWrapper holidayRepositoryWrapper, final ClientRepositoryWrapper clientRepository, final HolidayRepositoryWrapper holidayRepositoryWrapper,
final ConfigurationDomainService configurationDomainService, final ClientChargeRepositoryWrapper clientChargeRepository, final ConfigurationDomainService configurationDomainService, final ClientChargeRepositoryWrapper clientChargeRepository,
final WorkingDaysRepositoryWrapper workingDaysRepository, final ClientTransactionRepository clientTransactionRepository, final WorkingDaysRepositoryWrapper workingDaysRepository, final ClientTransactionRepository clientTransactionRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService) { final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.context = context; this.context = context;
this.chargeRepository = chargeRepository; this.chargeRepository = chargeRepository;
this.clientChargeDataValidator = clientChargeDataValidator; this.clientChargeDataValidator = clientChargeDataValidator;
@@ -76,6 +79,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
this.workingDaysRepository = workingDaysRepository; this.workingDaysRepository = workingDaysRepository;
this.clientTransactionRepository = clientTransactionRepository; this.clientTransactionRepository = clientTransactionRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
} }
@Override @Override
@@ -134,12 +138,15 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate, ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate,
chargePaid, clientCharge.getCurrency().getCode(), getAppUserIfPresent()); chargePaid, clientCharge.getCurrency().getCode(), getAppUserIfPresent());
this.clientTransactionRepository.save(clientTransaction); this.clientTransactionRepository.saveAndFlush(clientTransaction);
// update charge paid by associations // update charge paid by associations
final ClientChargePaidBy chargePaidBy = ClientChargePaidBy.instance(clientTransaction, clientCharge, amountPaid); final ClientChargePaidBy chargePaidBy = ClientChargePaidBy.instance(clientTransaction, clientCharge, amountPaid);
clientTransaction.getClientChargePaidByCollection().add(chargePaidBy); clientTransaction.getClientChargePaidByCollection().add(chargePaidBy);
// generate accounting entries
generateAccountingEntries(clientTransaction);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withEntityId(clientCharge.getId()) // .withEntityId(clientCharge.getId()) //
.withOfficeId(clientCharge.getClient().getOffice().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 @Override
public CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId) { public CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId) {
final Client client = this.clientRepository.getActiveClient(clientId); final Client client = this.clientRepository.getActiveClient(clientId);
@@ -294,7 +306,7 @@ public class ClientChargeWritePlatformServiceJpaRepositoryImpl implements Client
*/ */
@Override @Override
public CommandProcessingResult updateCharge(Long clientId, JsonCommand command) { public CommandProcessingResult updateCharge(Long clientId, JsonCommand command) {
this.clientChargeDataValidator.validateAdd(command.json()); // this.clientChargeDataValidator.validateAdd(command.json());
return null; return null;
} }
@@ -5,8 +5,10 @@
*/ */
package org.mifosplatform.portfolio.client.service; package org.mifosplatform.portfolio.client.service;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.office.domain.OrganisationCurrencyRepositoryWrapper;
@@ -27,14 +29,17 @@ public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements C
private final ClientRepositoryWrapper clientRepository; private final ClientRepositoryWrapper clientRepository;
private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository; private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
@Autowired @Autowired
public ClientTransactionWritePlatformServiceJpaRepositoryImpl(final ClientTransactionRepositoryWrapper clientTransactionRepository, public ClientTransactionWritePlatformServiceJpaRepositoryImpl(final ClientTransactionRepositoryWrapper clientTransactionRepository,
final ClientRepositoryWrapper clientRepositoryWrapper, final ClientRepositoryWrapper clientRepositoryWrapper,
final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper) { final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper,
JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.clientTransactionRepository = clientTransactionRepository; this.clientTransactionRepository = clientTransactionRepository;
this.clientRepository = clientRepositoryWrapper; this.clientRepository = clientRepositoryWrapper;
this.organisationCurrencyRepository = organisationCurrencyRepositoryWrapper; this.organisationCurrencyRepository = organisationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
} }
@Override @Override
@@ -66,6 +71,10 @@ public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements C
} }
} }
// generate accounting entries
this.clientTransactionRepository.saveAndFlush(clientTransaction);
generateAccountingEntries(clientTransaction);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withEntityId(transactionId) // .withEntityId(transactionId) //
.withOfficeId(client.officeId()) // .withOfficeId(client.officeId()) //
@@ -73,4 +82,9 @@ public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements C
.build(); .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 java.util.Map;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
@@ -133,10 +134,13 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
final List<EnumOptionData> clientChargeTimeTypeOptions = null; final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); 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, return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions); savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
incomeOrLiabilityAccountOptions);
} }
@Override @Override
@@ -10,9 +10,11 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay; import org.joda.time.MonthDay;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
@@ -142,11 +144,14 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
final List<EnumOptionData> clientChargeTimeTypeOptions = null; final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions(); 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 // TODO AA : revisit for merge conflict - Not sure method signature
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null, return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions); savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
incomeOrLiabilityAccountOptions);
} }
@Override @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`);