@@ -0,0 +1,27 @@
|
||||
CREATE TABLE `m_portfolio_account_associations` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`loan_account_id` BIGINT NULL DEFAULT NULL,
|
||||
`savings_account_id` BIGINT NULL DEFAULT NULL,
|
||||
`linked_loan_account_id` BIGINT NULL DEFAULT NULL,
|
||||
`linked_savings_account_id` BIGINT NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `account_association_loan_fk` FOREIGN KEY (`loan_account_id`) REFERENCES `m_loan` (`id`),
|
||||
CONSTRAINT `account_association_savings_fk` FOREIGN KEY (`savings_account_id`) REFERENCES `m_savings_account` (`id`),
|
||||
CONSTRAINT `linked_loan_fk` FOREIGN KEY (`linked_loan_account_id`) REFERENCES `m_loan` (`id`),
|
||||
CONSTRAINT `linked_savings_fk` FOREIGN KEY (`linked_savings_account_id`) REFERENCES `m_savings_account` (`id`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB;
|
||||
|
||||
ALTER TABLE `m_charge`
|
||||
ADD COLUMN `charge_payment_mode_enum` SMALLINT(5) NOT NULL DEFAULT '0' AFTER `charge_calculation_enum`;
|
||||
|
||||
ALTER TABLE `m_loan_charge`
|
||||
ADD COLUMN `charge_payment_mode_enum` SMALLINT(5) NOT NULL DEFAULT '0' AFTER `charge_calculation_enum`;
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_loan', 'PAY_LOANCHARGE', 'LOANCHARGE', 'PAY', 0);
|
||||
|
||||
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`) VALUES ('Transfer Fee For Loans From Savings', 'Transfer Fee For Loans From Savings', '0 1 0 1/1 * ? *', now(), 5, NULL, NULL, NULL, NULL, NULL, 1, 0, 1, 0, 0);
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -54,9 +54,9 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
* Repayments and Repayments at disbursement
|
||||
***/
|
||||
else if (loanTransactionDTO.getTransactionType().isRepayment()
|
||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()) {
|
||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement() || loanTransactionDTO.getTransactionType().isChargePayment()) {
|
||||
createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false, loanTransactionDTO
|
||||
.getTransactionType().isRepaymentAtDisbursement());
|
||||
.getTransactionType().isRepaymentAtDisbursement() || loanTransactionDTO.getTransactionType().isChargePayment());
|
||||
}
|
||||
|
||||
/** Handle Write Offs, waivers and their reversals **/
|
||||
@@ -170,7 +170,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
*
|
||||
*/
|
||||
private void createJournalEntriesForRepaymentsAndWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
||||
final Office office, final boolean writeOff, final boolean repaymentAtDisbursement) {
|
||||
final Office office, final boolean writeOff, final boolean isIncomeFromFee) {
|
||||
// loan properties
|
||||
final Long loanProductId = loanDTO.getLoanProductId();
|
||||
final Long loanId = loanDTO.getLoanId();
|
||||
@@ -208,7 +208,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
|
||||
ACCRUAL_ACCOUNTS_FOR_LOAN feeAccountToCredit = ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE;
|
||||
|
||||
if (repaymentAtDisbursement) {
|
||||
if (isIncomeFromFee) {
|
||||
feeAccountToCredit = ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -51,7 +51,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
||||
* Repayments and Repayments at disbursement
|
||||
***/
|
||||
else if (loanTransactionDTO.getTransactionType().isRepayment()
|
||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()) {
|
||||
|| loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()
|
||||
|| loanTransactionDTO.getTransactionType().isChargePayment()) {
|
||||
createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office);
|
||||
}
|
||||
/***
|
||||
|
||||
@@ -385,6 +385,10 @@ public class CommandWrapper {
|
||||
return this.actionName.equalsIgnoreCase("WAIVE") && this.entityName.equalsIgnoreCase("LOANCHARGE");
|
||||
}
|
||||
|
||||
public boolean isPayLoanCharge() {
|
||||
return this.actionName.equalsIgnoreCase("PAY") && this.entityName.equalsIgnoreCase("LOANCHARGE");
|
||||
}
|
||||
|
||||
public boolean isUpdateLoanOfficer() {
|
||||
return this.actionName.equalsIgnoreCase("UPDATELOANOFFICER") && this.entityName.equalsIgnoreCase("LOAN");
|
||||
}
|
||||
|
||||
+14
-2
@@ -489,9 +489,21 @@ public class CommandWrapperBuilder {
|
||||
this.loanId = loanId;
|
||||
this.href = "/loans/" + loanId + "/charges/" + loanChargeId;
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder payLoanCharge(final Long loanId, final Long loanChargeId) {
|
||||
this.actionName = "PAY";
|
||||
this.entityName = "LOANCHARGE";
|
||||
this.entityId = loanChargeId;
|
||||
this.loanId = loanId;
|
||||
if(loanChargeId == null){
|
||||
this.href = "/loans/" + loanId;
|
||||
}else{
|
||||
this.href = "/loans/" + loanId + "/charges/" + loanChargeId;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteLoanCharge(final Long loanId, final Long loanChargeId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "LOANCHARGE";
|
||||
|
||||
+2
@@ -348,6 +348,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
handler = this.applicationContext.getBean("updateLoanChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isWaiveLoanCharge()) {
|
||||
handler = this.applicationContext.getBean("waiveLoanChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isPayLoanCharge()) {
|
||||
handler = this.applicationContext.getBean("payLoanChargeCommandHandler", NewCommandSourceHandler.class);
|
||||
}
|
||||
} else if (wrapper.isGLAccountResource()) {
|
||||
if (wrapper.isCreate()) {
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package org.mifosplatform.infrastructure.jobs.exception;
|
||||
|
||||
|
||||
public class JobExecutionException extends Exception{
|
||||
|
||||
public JobExecutionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -4,7 +4,8 @@ public enum JobName {
|
||||
|
||||
UPDATE_LOAN_SUMMARY("Update loan Summary"), UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), UPDATE_LOAN_PAID_IN_ADVANCE(
|
||||
"Update Loan Paid In Advance"), APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), APPLY_HOLIDAYS_TO_LOANS(
|
||||
"Apply Holidays To Loans"), POST_INTEREST_FOR_SAVINGS("Post Interest For Savings");
|
||||
"Apply Holidays To Loans"), POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"), TRANSFER_FEE_CHARGE_FOR_LOANS(
|
||||
"Transfer Fee For Loans From Savings");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/data/AccountAssociationsData.java
Executable
+18
@@ -0,0 +1,18 @@
|
||||
package org.mifosplatform.portfolio.account.data;
|
||||
|
||||
public class AccountAssociationsData {
|
||||
|
||||
private final Long id;
|
||||
private final PortfolioAccountData account;
|
||||
private final PortfolioAccountData linkedAccount;
|
||||
|
||||
public AccountAssociationsData(final Long id, final PortfolioAccountData account, final PortfolioAccountData linkedAccount) {
|
||||
this.id = id;
|
||||
this.account = account;
|
||||
this.linkedAccount = linkedAccount;
|
||||
}
|
||||
|
||||
public PortfolioAccountData linkedAccount() {
|
||||
return this.linkedAccount;
|
||||
}
|
||||
}
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
package org.mifosplatform.portfolio.account.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
|
||||
public class AccountTransferDTO {
|
||||
|
||||
private LocalDate transactionDate;
|
||||
private BigDecimal transactionAmount;
|
||||
private PortfolioAccountType fromAccountType;
|
||||
private PortfolioAccountType toAccountType;
|
||||
private Long fromAccountId;
|
||||
private Long toAccountId;
|
||||
private String description;
|
||||
private Locale locale;
|
||||
private DateTimeFormatter fmt;
|
||||
private PaymentDetail paymentDetail;
|
||||
private Integer fromTransferType;
|
||||
private Integer toTransferType;
|
||||
private Long chargeId;
|
||||
|
||||
public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount,
|
||||
final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId,
|
||||
final Long toAccountId, final String description, final Locale locale, final DateTimeFormatter fmt,
|
||||
final PaymentDetail paymentDetail, final Integer fromTransferType, final Integer toTransferType, final Long chargeId) {
|
||||
this.transactionDate = transactionDate;
|
||||
this.transactionAmount = transactionAmount;
|
||||
this.fromAccountType = fromAccountType;
|
||||
this.toAccountType = toAccountType;
|
||||
this.fromAccountId = fromAccountId;
|
||||
this.toAccountId = toAccountId;
|
||||
this.description = description;
|
||||
this.locale = locale;
|
||||
this.fmt = fmt;
|
||||
this.paymentDetail = paymentDetail;
|
||||
this.fromTransferType = fromTransferType;
|
||||
this.toTransferType = toTransferType;
|
||||
this.chargeId = chargeId;
|
||||
}
|
||||
|
||||
public LocalDate getTransactionDate() {
|
||||
return this.transactionDate;
|
||||
}
|
||||
|
||||
public BigDecimal getTransactionAmount() {
|
||||
return this.transactionAmount;
|
||||
}
|
||||
|
||||
public PortfolioAccountType getFromAccountType() {
|
||||
return this.fromAccountType;
|
||||
}
|
||||
|
||||
public PortfolioAccountType getToAccountType() {
|
||||
return this.toAccountType;
|
||||
}
|
||||
|
||||
public Long getFromAccountId() {
|
||||
return this.fromAccountId;
|
||||
}
|
||||
|
||||
public Long getToAccountId() {
|
||||
return this.toAccountId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
public DateTimeFormatter getFmt() {
|
||||
return this.fmt;
|
||||
}
|
||||
|
||||
public PaymentDetail getPaymentDetail() {
|
||||
return this.paymentDetail;
|
||||
}
|
||||
|
||||
public Integer getFromTransferType() {
|
||||
return this.fromTransferType;
|
||||
}
|
||||
|
||||
public Integer getToTransferType() {
|
||||
return this.toTransferType;
|
||||
}
|
||||
|
||||
|
||||
public Long getChargeId() {
|
||||
return this.chargeId;
|
||||
}
|
||||
|
||||
}
|
||||
+4
@@ -103,4 +103,8 @@ public class PortfolioAccountData {
|
||||
public BigDecimal getAmtForTransfer() {
|
||||
return this.amtForTransfer;
|
||||
}
|
||||
|
||||
public Long accountId(){
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
package org.mifosplatform.portfolio.account.domain;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_portfolio_account_associations")
|
||||
public class AccountAssociations extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "loan_account_id", nullable = true)
|
||||
private Loan loanAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "savings_account_id", nullable = true)
|
||||
private SavingsAccount savingsAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "linked_loan_account_id", nullable = true)
|
||||
private Loan linkedLoanAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "linked_savings_account_id", nullable = true)
|
||||
private SavingsAccount linkedSavingsAccount;
|
||||
|
||||
protected AccountAssociations() {}
|
||||
|
||||
private AccountAssociations(final Loan loanAccount, final SavingsAccount savingsAccount, final Loan linkedLoanAccount,
|
||||
final SavingsAccount linkedSavingsAccount) {
|
||||
this.loanAccount = loanAccount;
|
||||
this.savingsAccount = savingsAccount;
|
||||
this.linkedLoanAccount = linkedLoanAccount;
|
||||
this.linkedSavingsAccount = linkedSavingsAccount;
|
||||
}
|
||||
|
||||
public static AccountAssociations associateSavingsAccount(Loan loan, SavingsAccount savingsAccount) {
|
||||
return new AccountAssociations(loan, null, null, savingsAccount);
|
||||
}
|
||||
|
||||
public SavingsAccount linkedSavingsAccount() {
|
||||
return this.linkedSavingsAccount;
|
||||
}
|
||||
|
||||
public void updateLinkedSavingsAccount(final SavingsAccount savingsAccount) {
|
||||
this.linkedSavingsAccount = savingsAccount;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.mifosplatform.portfolio.account.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface AccountAssociationsRepository extends JpaRepository<AccountAssociations, Long>,
|
||||
JpaSpecificationExecutor<AccountAssociations> {
|
||||
|
||||
@Query("from AccountAssociations aa where aa.loanAccount.id= :loanId")
|
||||
AccountAssociations findByLoanId(@Param("loanId") Long loanId);
|
||||
|
||||
}
|
||||
+15
@@ -23,6 +23,7 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.organisation.office.domain.Office;
|
||||
import org.mifosplatform.organisation.office.domain.OfficeRepository;
|
||||
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
@@ -139,4 +140,18 @@ public class AccountTransferAssembler {
|
||||
deposit, loanRefundTransaction, transactionDate, transactionMonetaryAmount, description);
|
||||
}
|
||||
|
||||
public AccountTransfer assembleSavingsToLoanTransfer(final AccountTransferDTO accountTransferDTO,
|
||||
final SavingsAccount fromSavingsAccount, final Loan toLoanAccount, final SavingsAccountTransaction savingsAccountTransaction,
|
||||
final LoanTransaction loanTransaction) {
|
||||
final Office fromOffice = fromSavingsAccount.office();
|
||||
final Client fromClient = fromSavingsAccount.getClient();
|
||||
final Office toOffice = toLoanAccount.getOffice();
|
||||
final Client toClient = toLoanAccount.client();
|
||||
|
||||
final Money transactionMonetaryAmount = Money.of(fromSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
|
||||
return AccountTransfer.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
|
||||
savingsAccountTransaction, loanTransaction, accountTransferDTO.getTransactionDate(), transactionMonetaryAmount,
|
||||
accountTransferDTO.getDescription());
|
||||
}
|
||||
|
||||
}
|
||||
+4
-1
@@ -15,5 +15,8 @@ import org.springframework.data.repository.query.Param;
|
||||
public interface AccountTransferRepository extends JpaRepository<AccountTransfer, Long>, JpaSpecificationExecutor<AccountTransfer> {
|
||||
|
||||
@Query("from AccountTransfer at where at.fromLoanAccount.id= :accountNumber and at.reversed=false")
|
||||
List<AccountTransfer> findByLoanId(@Param("accountNumber") Long accountNumber);
|
||||
List<AccountTransfer> findByFromLoanId(@Param("accountNumber") Long accountNumber);
|
||||
|
||||
@Query("from AccountTransfer at where (at.fromLoanAccount.id= :accountNumber or at.toLoanAccount.id=:accountNumber) and at.reversed=false")
|
||||
List<AccountTransfer> findAllByLoanId(@Param("accountNumber") Long accountNumber);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.mifosplatform.portfolio.account.service;
|
||||
|
||||
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
|
||||
|
||||
|
||||
public interface AccountAssociationsReadPlatformService {
|
||||
|
||||
public PortfolioAccountData retriveLoanAssociation(final Long loanId);
|
||||
|
||||
public boolean isLinkedWithAnyActiveLoan(final Long savingsId);
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package org.mifosplatform.portfolio.account.service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
import org.mifosplatform.portfolio.account.data.AccountAssociationsData;
|
||||
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AccountAssociationsReadPlatformServiceImpl implements AccountAssociationsReadPlatformService {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(AccountAssociationsReadPlatformServiceImpl.class);
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public AccountAssociationsReadPlatformServiceImpl(final RoutingDataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PortfolioAccountData retriveLoanAssociation(final Long loanId) {
|
||||
PortfolioAccountData linkedAccount = null;
|
||||
AccountAssociationsMapper mapper = new AccountAssociationsMapper();
|
||||
String sql = "select " + mapper.schema() + " where aa.loan_account_id = ? ";
|
||||
try {
|
||||
AccountAssociationsData accountAssociationsData = jdbcTemplate.queryForObject(sql, mapper, loanId);
|
||||
if (accountAssociationsData != null) {
|
||||
linkedAccount = accountAssociationsData.linkedAccount();
|
||||
}
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
logger.debug("Linking account is not configured");
|
||||
}
|
||||
return linkedAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinkedWithAnyActiveLoan(final Long savingsId) {
|
||||
boolean hasActiveLoan = false;
|
||||
String sql = "select loanAccount.loan_status_id as status from m_portfolio_account_associations aa " +
|
||||
"left join m_loan loanAccount on loanAccount.id = aa.loan_account_id " +
|
||||
"where aa.linked_savings_account_id = ?";
|
||||
|
||||
List<Integer> statusList= this.jdbcTemplate.queryForList(sql,Integer.class, savingsId);
|
||||
for(Integer status:statusList){
|
||||
LoanStatus loanStatus= LoanStatus.fromInt(status);
|
||||
if(loanStatus.isActiveOrAwaitingApprovalOrDisbursal() || loanStatus.isUnderTransfer()){
|
||||
hasActiveLoan = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hasActiveLoan;
|
||||
}
|
||||
|
||||
private static final class AccountAssociationsMapper implements RowMapper<AccountAssociationsData> {
|
||||
|
||||
private final String schemaSql;
|
||||
|
||||
public AccountAssociationsMapper() {
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
sqlBuilder.append("aa.id as id,");
|
||||
// sqlBuilder.append("savingsAccount.id as savingsAccountId, savingsAccount.account_no as savingsAccountNo,");
|
||||
sqlBuilder.append("loanAccount.id as loanAccountId, loanAccount.account_no as loanAccountNo,");
|
||||
// sqlBuilder.append("linkLoanAccount.id as linkLoanAccountId, linkLoanAccount.account_no as linkLoanAccountNo, ");
|
||||
sqlBuilder.append("linkSavingsAccount.id as linkSavingsAccountId, linkSavingsAccount.account_no as linkSavingsAccountNo ");
|
||||
sqlBuilder.append("from m_portfolio_account_associations aa ");
|
||||
// sqlBuilder.append("left join m_savings_account savingsAccount on savingsAccount.id = aa.savings_account_id ");
|
||||
sqlBuilder.append("left join m_loan loanAccount on loanAccount.id = aa.loan_account_id ");
|
||||
sqlBuilder.append("left join m_savings_account linkSavingsAccount on linkSavingsAccount.id = aa.linked_savings_account_id ");
|
||||
// sqlBuilder.append("left join m_loan linkLoanAccount on linkLoanAccount.id = aa.linked_loan_account_id ");
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountAssociationsData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
// final Long savingsAccountId = JdbcSupport.getLong(rs,
|
||||
// "savingsAccountId");
|
||||
// final String savingsAccountNo = rs.getString("savingsAccountNo");
|
||||
final Long loanAccountId = JdbcSupport.getLong(rs, "loanAccountId");
|
||||
final String loanAccountNo = rs.getString("loanAccountNo");
|
||||
PortfolioAccountData account = PortfolioAccountData.lookup(loanAccountId, loanAccountNo);
|
||||
;
|
||||
/*
|
||||
* if (savingsAccountId != null) { account =
|
||||
* PortfolioAccountData.lookup(savingsAccountId, savingsAccountNo);
|
||||
* } else if (loanAccountId != null) { account =
|
||||
* PortfolioAccountData.lookup(loanAccountId, loanAccountNo); }
|
||||
*/
|
||||
final Long linkSavingsAccountId = JdbcSupport.getLong(rs, "linkSavingsAccountId");
|
||||
final String linkSavingsAccountNo = rs.getString("linkSavingsAccountNo");
|
||||
// final Long linkLoanAccountId = JdbcSupport.getLong(rs,
|
||||
// "linkLoanAccountId");
|
||||
// final String linkLoanAccountNo =
|
||||
// rs.getString("linkLoanAccountNo");
|
||||
PortfolioAccountData linkedAccount = PortfolioAccountData.lookup(linkSavingsAccountId, linkSavingsAccountNo);
|
||||
/*
|
||||
* if (linkSavingsAccountId != null) { linkedAccount =
|
||||
* PortfolioAccountData.lookup(linkSavingsAccountId,
|
||||
* linkSavingsAccountNo); } else if (linkLoanAccountId != null) {
|
||||
* linkedAccount = PortfolioAccountData.lookup(linkLoanAccountId,
|
||||
* linkLoanAccountNo); }
|
||||
*/
|
||||
|
||||
return new AccountAssociationsData(id, account, linkedAccount);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -261,7 +261,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
|
||||
sqlBuilder.append("join m_client fromclient on fromclient.id = sat.from_client_id ");
|
||||
sqlBuilder.append("join m_client toclient on toclient.id = sat.to_client_id ");
|
||||
sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = sat.from_savings_account_id ");
|
||||
sqlBuilder.append("left join m_savings_account fromloanacc on fromloanacc.id = sat.from_loan_account_id ");
|
||||
sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = sat.from_loan_account_id ");
|
||||
sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = sat.to_savings_account_id ");
|
||||
sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = sat.to_loan_account_id ");
|
||||
sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = sat.from_savings_transaction_id ");
|
||||
|
||||
+6
-1
@@ -8,10 +8,15 @@ package org.mifosplatform.portfolio.account.service;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
|
||||
|
||||
public interface AccountTransfersWritePlatformService {
|
||||
|
||||
CommandProcessingResult create(JsonCommand command);
|
||||
|
||||
void reverseTransfers(Long accountNumber, PortfolioAccountType accountTypeId);
|
||||
void reverseTransfersWithFromAccountType(Long accountNumber, PortfolioAccountType accountTypeId);
|
||||
|
||||
Long transferFunds(AccountTransferDTO accountTransferDTO);
|
||||
|
||||
void reverseAllTransactions(Long accountId, PortfolioAccountType accountTypeId);
|
||||
}
|
||||
+67
-10
@@ -23,6 +23,7 @@ import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
|
||||
import org.mifosplatform.portfolio.account.data.AccountTransfersDataValidator;
|
||||
import org.mifosplatform.portfolio.account.domain.AccountTransfer;
|
||||
import org.mifosplatform.portfolio.account.domain.AccountTransferAssembler;
|
||||
@@ -120,7 +121,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
|
||||
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
|
||||
|
||||
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccountId,
|
||||
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount,
|
||||
new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null);
|
||||
|
||||
final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command,
|
||||
@@ -166,23 +167,79 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void reverseTransfers(Long accountNumber, PortfolioAccountType accountTypeId) {
|
||||
public void reverseTransfersWithFromAccountType(Long accountNumber, PortfolioAccountType accountTypeId) {
|
||||
List<AccountTransfer> acccountTransfers = null;
|
||||
if (accountTypeId.isLoanAccount()) {
|
||||
acccountTransfers = this.accountTransferRepository.findByLoanId(accountNumber);
|
||||
acccountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber);
|
||||
}
|
||||
if (acccountTransfers != null && acccountTransfers.size() > 0) {
|
||||
for (AccountTransfer accountTransfer : acccountTransfers) {
|
||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
|
||||
this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.getToSavingsAccount().getId(), accountTransfer
|
||||
.getToSavingsTransaction().getId());
|
||||
accountTransfer.reverse();
|
||||
this.accountTransferRepository.save(accountTransfer);
|
||||
}
|
||||
undoTransactions(acccountTransfers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void reverseAllTransactions(Long accountId, PortfolioAccountType accountTypeId) {
|
||||
List<AccountTransfer> acccountTransfers = null;
|
||||
if (accountTypeId.isLoanAccount()) {
|
||||
acccountTransfers = this.accountTransferRepository.findAllByLoanId(accountId);
|
||||
}
|
||||
if (acccountTransfers != null && acccountTransfers.size() > 0) {
|
||||
undoTransactions(acccountTransfers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param acccountTransfers
|
||||
*/
|
||||
private void undoTransactions(List<AccountTransfer> acccountTransfers) {
|
||||
for (AccountTransfer accountTransfer : acccountTransfers) {
|
||||
if (accountTransfer.getFromLoanTransaction() != null) {
|
||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
|
||||
} else if (accountTransfer.getToLoanTransaction() != null) {
|
||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction());
|
||||
}
|
||||
if (accountTransfer.getFromTransaction() != null) {
|
||||
this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.getFromAccount().getId(), accountTransfer
|
||||
.getFromTransaction().getId(), true);
|
||||
}
|
||||
if (accountTransfer.getToSavingsTransaction() != null) {
|
||||
this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.getToSavingsAccount().getId(), accountTransfer
|
||||
.getToSavingsTransaction().getId(), true);
|
||||
}
|
||||
accountTransfer.reverse();
|
||||
this.accountTransferRepository.save(accountTransfer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Long transferFunds(AccountTransferDTO accountTransferDTO) {
|
||||
Long transferTransactionId = null;
|
||||
if (isSavingsToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||
//
|
||||
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
||||
|
||||
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount,
|
||||
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
||||
accountTransferDTO.getPaymentDetail(), fromSavingsAccount.isWithdrawalFeeApplicableForTransfer());
|
||||
|
||||
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||
|
||||
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeChargePayment(toLoanAccount,
|
||||
accountTransferDTO.getChargeId(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
||||
accountTransferDTO.getPaymentDetail(), null, null, accountTransferDTO.getToTransferType());
|
||||
|
||||
final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO,
|
||||
fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction);
|
||||
this.accountTransferRepository.saveAndFlush(transferTransaction);
|
||||
transferTransactionId = transferTransaction.getId();
|
||||
}
|
||||
|
||||
return transferTransactionId;
|
||||
}
|
||||
|
||||
private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
|
||||
return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount();
|
||||
}
|
||||
|
||||
+21
-14
@@ -28,30 +28,33 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
private final EnumOptionData chargeTimeType;
|
||||
private final EnumOptionData chargeAppliesTo;
|
||||
private final EnumOptionData chargeCalculationType;
|
||||
private final EnumOptionData chargePaymentMode;
|
||||
|
||||
private final Collection<CurrencyData> currencyOptions;
|
||||
private final List<EnumOptionData> chargeCalculationTypeOptions;
|
||||
private final List<EnumOptionData> chargeAppliesToOptions;
|
||||
private final List<EnumOptionData> chargeTimeTypeOptions;
|
||||
private final List<EnumOptionData> chargePaymetModeOptions;
|
||||
|
||||
public static ChargeData template(final Collection<CurrencyData> currencyOptions,
|
||||
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions,
|
||||
final List<EnumOptionData> chargeTimeTypeOptions) {
|
||||
return new ChargeData(null, null, null, null, null, null, null, false, false, currencyOptions, chargeCalculationTypeOptions,
|
||||
chargeAppliesToOptions, chargeTimeTypeOptions);
|
||||
final List<EnumOptionData> chargeTimeTypeOptions, List<EnumOptionData> chargePaymentModeOptions) {
|
||||
return new ChargeData(null, null, null, null, null, null, null, null, false, false, currencyOptions,
|
||||
chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions);
|
||||
}
|
||||
|
||||
public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) {
|
||||
return new ChargeData(charge.id, charge.name, charge.amount, charge.currency, charge.chargeTimeType, charge.chargeAppliesTo,
|
||||
charge.chargeCalculationType, charge.penalty, charge.active, template.currencyOptions,
|
||||
template.chargeCalculationTypeOptions, template.chargeAppliesToOptions, template.chargeTimeTypeOptions);
|
||||
charge.chargeCalculationType, charge.chargePaymentMode, charge.penalty, charge.active,
|
||||
template.currencyOptions, template.chargeCalculationTypeOptions, template.chargeAppliesToOptions, template.chargeTimeTypeOptions,
|
||||
template.chargePaymetModeOptions);
|
||||
}
|
||||
|
||||
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 boolean penalty, final boolean active) {
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, penalty, active, null,
|
||||
null, null, null);
|
||||
final EnumOptionData chargePaymentMode, final boolean penalty, final boolean active) {
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode, penalty, active,
|
||||
null, null, null, null, null);
|
||||
}
|
||||
|
||||
public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) {
|
||||
@@ -60,21 +63,23 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
EnumOptionData chargeTimeType = null;
|
||||
EnumOptionData chargeAppliesTo = null;
|
||||
EnumOptionData chargeCalculationType = null;
|
||||
EnumOptionData chargePaymentMode = null;
|
||||
Boolean penalty = isPenalty;
|
||||
Boolean active = false;
|
||||
Collection<CurrencyData> currencyOptions = null;
|
||||
List<EnumOptionData> chargeCalculationTypeOptions = null;
|
||||
List<EnumOptionData> chargeAppliesToOptions = null;
|
||||
List<EnumOptionData> chargeTimeTypeOptions = null;
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, penalty, active,
|
||||
currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions);
|
||||
List<EnumOptionData> chargePaymentModeOptions = null;
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode, penalty,
|
||||
active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions);
|
||||
}
|
||||
|
||||
private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
|
||||
final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
|
||||
final boolean penalty, final boolean active, final Collection<CurrencyData> currencyOptions,
|
||||
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions,
|
||||
final List<EnumOptionData> chargeTimeTypeOptions) {
|
||||
EnumOptionData chargePaymentMode, final boolean penalty, final boolean active,
|
||||
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> chargeCalculationTypeOptions,
|
||||
final List<EnumOptionData> chargeAppliesToOptions, final List<EnumOptionData> chargeTimeTypeOptions, List<EnumOptionData> chargePaymentModeOptions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
@@ -82,12 +87,14 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.chargeAppliesTo = chargeAppliesTo;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.chargePaymentMode = chargePaymentMode;
|
||||
this.penalty = penalty;
|
||||
this.active = active;
|
||||
this.currencyOptions = currencyOptions;
|
||||
this.chargeCalculationTypeOptions = chargeCalculationTypeOptions;
|
||||
this.chargeAppliesToOptions = chargeAppliesToOptions;
|
||||
this.chargeTimeTypeOptions = chargeTimeTypeOptions;
|
||||
this.chargePaymetModeOptions = chargePaymentModeOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,6 +123,6 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
}
|
||||
|
||||
return LoanChargeData.newLoanChargeDetails(this.id, this.name, this.currency, this.amount, percentage, this.chargeTimeType,
|
||||
this.chargeCalculationType, this.penalty);
|
||||
this.chargeCalculationType, this.penalty,this.chargePaymentMode);
|
||||
}
|
||||
}
|
||||
+24
-4
@@ -46,6 +46,9 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "charge_calculation_enum")
|
||||
private Integer chargeCalculation;
|
||||
|
||||
@Column(name = "charge_payment_mode_enum")
|
||||
private Integer chargePaymentMode;
|
||||
|
||||
@Column(name = "is_penalty", nullable = false)
|
||||
private boolean penalty;
|
||||
@@ -66,11 +69,12 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
final ChargeTimeType chargeTimeType = ChargeTimeType.fromInt(command.integerValueOfParameterNamed("chargeTimeType"));
|
||||
final ChargeCalculationType chargeCalculationType = ChargeCalculationType.fromInt(command
|
||||
.integerValueOfParameterNamed("chargeCalculationType"));
|
||||
final ChargePaymentMode paymentMode = ChargePaymentMode.fromInt(command.integerValueOfParameterNamed("chargePaymentMode"));
|
||||
|
||||
final boolean penalty = command.booleanPrimitiveValueOfParameterNamed("penalty");
|
||||
final boolean active = command.booleanPrimitiveValueOfParameterNamed("active");
|
||||
|
||||
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active);
|
||||
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode);
|
||||
}
|
||||
|
||||
protected Charge() {
|
||||
@@ -78,7 +82,8 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active) {
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active,
|
||||
final ChargePaymentMode paymentMode) {
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
this.currencyCode = currencyCode;
|
||||
@@ -87,6 +92,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
this.chargeCalculation = chargeCalculationType.getValue();
|
||||
this.penalty = penalty;
|
||||
this.active = active;
|
||||
this.chargePaymentMode = paymentMode.getValue();
|
||||
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
|
||||
}
|
||||
|
||||
@@ -191,6 +197,15 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.chargeCalculation = ChargeCalculationType.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
final String paymentModeParamName = "chargePaymentMode";
|
||||
if (command.isChangeInIntegerParameterNamed(paymentModeParamName, this.chargePaymentMode)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(paymentModeParamName);
|
||||
actualChanges.put(paymentModeParamName, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.chargePaymentMode = ChargePaymentMode.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
|
||||
final String penaltyParamName = "penalty";
|
||||
if (command.isChangeInBooleanParameterNamed(penaltyParamName, this.penalty)) {
|
||||
@@ -228,9 +243,14 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(this.chargeTime);
|
||||
EnumOptionData chargeAppliesTo = ChargeEnumerations.chargeAppliesTo(this.chargeAppliesTo);
|
||||
EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
|
||||
|
||||
EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
|
||||
CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
|
||||
return ChargeData.instance(this.getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
|
||||
penalty, active);
|
||||
chargePaymentmode, penalty, active);
|
||||
}
|
||||
|
||||
|
||||
public Integer getChargePaymentMode() {
|
||||
return this.chargePaymentMode;
|
||||
}
|
||||
}
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
package org.mifosplatform.portfolio.charge.domain;
|
||||
|
||||
public enum ChargePaymentMode {
|
||||
|
||||
REGULAR(0, "chargepaymentmode.regular"), //
|
||||
ACCOUNT_TRANSFER(1, "chargepaymentmode.accounttransfer");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private ChargePaymentMode(Integer value, String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static ChargePaymentMode fromInt(final Integer paymentMode) {
|
||||
ChargePaymentMode chargeAppliesToType = ChargePaymentMode.REGULAR;
|
||||
switch (paymentMode) {
|
||||
case 1:
|
||||
chargeAppliesToType = ACCOUNT_TRANSFER;
|
||||
break;
|
||||
default:
|
||||
chargeAppliesToType = REGULAR;
|
||||
break;
|
||||
}
|
||||
return chargeAppliesToType;
|
||||
}
|
||||
|
||||
public boolean isPaymentModeAccountTransfer(){
|
||||
return this.value.equals(ChargePaymentMode.ACCOUNT_TRANSFER.getValue());
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.portfolio.charge.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class LoanChargeCannotBePayedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
/*** enum of reasons of why Loan Charge cannot be waived **/
|
||||
public static enum LOAN_CHARGE_CANNOT_BE_PAYED_REASON {
|
||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, CHARGE_NOT_ACCOUNT_TRANSFER;
|
||||
|
||||
public String errorMessage() {
|
||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||
return "This loan charge has been completely paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "This loan charge has already been waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||
return "This loan charge can be payed as the loan associated with it is currently inactive";
|
||||
}else if(name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) {
|
||||
return "This loan charge can be payed as the charge payment mode is not account transfer";
|
||||
}
|
||||
|
||||
return name().toString();
|
||||
}
|
||||
|
||||
public String errorCode() {
|
||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||
return "error.msg.loan.charge.already.paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "error.msg.loan.charge.already.waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||
return "error.msg.loan.charge.associated.loan.inactive";
|
||||
}else if(name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) {
|
||||
return "error.msg.loan.charge.payment.mode.not.account.transfer";
|
||||
}
|
||||
return name().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public LoanChargeCannotBePayedException(final LOAN_CHARGE_CANNOT_BE_PAYED_REASON reason, final Long loanChargeId) {
|
||||
super(reason.errorCode(), reason.errorMessage(), loanChargeId);
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -35,7 +35,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
*/
|
||||
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "amount", "locale", "currencyCode",
|
||||
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty",
|
||||
"active"));
|
||||
"active","chargePaymentMode"));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -73,6 +73,9 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 4);
|
||||
|
||||
final Integer chargePaymentMode = fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargePaymentMode").value(chargePaymentMode).notNull().inMinMaxRange(0, 1);
|
||||
|
||||
if (fromApiJsonHelper.parameterExists("penalty", element)) {
|
||||
final Boolean penalty = fromApiJsonHelper.extractBooleanNamed("penalty", element);
|
||||
baseDataValidator.reset().parameter("penalty").value(penalty).notNull();
|
||||
@@ -126,6 +129,11 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 4);
|
||||
}
|
||||
|
||||
if (fromApiJsonHelper.parameterExists("chargePaymentMode", element)) {
|
||||
final Integer chargePaymentMode = fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargePaymentMode").value(chargePaymentMode).notNull().inMinMaxRange(0, 1);
|
||||
}
|
||||
|
||||
if (fromApiJsonHelper.parameterExists("penalty", element)) {
|
||||
final Boolean penalty = fromApiJsonHelper.extractBooleanNamed("penalty", element);
|
||||
|
||||
+2
@@ -16,4 +16,6 @@ public interface ChargeDropdownReadPlatformService {
|
||||
List<EnumOptionData> retrieveApplicableToTypes();
|
||||
|
||||
List<EnumOptionData> retrieveCollectionTimeTypes();
|
||||
|
||||
List<EnumOptionData> retrivePaymentModes();
|
||||
}
|
||||
|
||||
+7
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.charge.service;
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeAppliesTo;
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeCalculationType;
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargeTimeType;
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.chargePaymentMode;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -15,6 +16,7 @@ import java.util.List;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -40,4 +42,9 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
public List<EnumOptionData> retrieveCollectionTimeTypes() {
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrivePaymentModes() {
|
||||
return Arrays.asList(chargePaymentMode(ChargePaymentMode.REGULAR), chargePaymentMode(ChargePaymentMode.ACCOUNT_TRANSFER));
|
||||
}
|
||||
}
|
||||
+22
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.charge.service;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
|
||||
public class ChargeEnumerations {
|
||||
@@ -82,4 +83,25 @@ public class ChargeEnumerations {
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData chargePaymentMode(final int id) {
|
||||
return chargePaymentMode(ChargePaymentMode.fromInt(id));
|
||||
}
|
||||
|
||||
|
||||
public static EnumOptionData chargePaymentMode(final ChargePaymentMode type) {
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
case ACCOUNT_TRANSFER:
|
||||
optionData = new EnumOptionData(ChargePaymentMode.ACCOUNT_TRANSFER.getValue().longValue(),
|
||||
ChargePaymentMode.ACCOUNT_TRANSFER.getCode(), ChargePaymentMode.ACCOUNT_TRANSFER.getCode());
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargePaymentMode.REGULAR.getValue().longValue(), ChargePaymentMode.REGULAR.getCode(),
|
||||
ChargePaymentMode.REGULAR.getCode());
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-3
@@ -81,9 +81,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
|
||||
final List<EnumOptionData> allowedChargeAppliesToOptions = this.chargeDropdownReadPlatformService.retrieveApplicableToTypes();
|
||||
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
|
||||
final List<EnumOptionData> chargePaymentOptions = this.chargeDropdownReadPlatformService.retrivePaymentModes();
|
||||
|
||||
return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions,
|
||||
allowedChargeTimeOptions);
|
||||
allowedChargeTimeOptions, chargePaymentOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,6 +131,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
public String chargeSchema() {
|
||||
return "c.id as id, c.name as name, c.amount as amount, c.currency_code as currencyCode, "
|
||||
+ "c.charge_applies_to_enum as chargeAppliesTo, c.charge_time_enum as chargeTime, "
|
||||
+"c.charge_payment_mode_enum as chargePaymentMode, "
|
||||
+ "c.charge_calculation_enum as chargeCalculation, c.is_penalty as penalty, c.is_active as active, oc.name as currencyName, "
|
||||
+ "oc.decimal_places as currencyDecimalPlaces,oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode from m_charge c "
|
||||
@@ -164,11 +166,16 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
|
||||
final int chargeCalculation = rs.getInt("chargeCalculation");
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
|
||||
|
||||
final int paymentMode = rs.getInt("chargePaymentMode");
|
||||
final EnumOptionData chargePaymentMode = ChargeEnumerations.chargePaymentMode(paymentMode);
|
||||
|
||||
|
||||
final boolean penalty = rs.getBoolean("penalty");
|
||||
final boolean active = rs.getBoolean("active");
|
||||
|
||||
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, penalty,
|
||||
active);
|
||||
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-14
@@ -124,11 +124,19 @@ public class LoanChargesApiResource {
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String addLoanCharge(@PathParam("loanId") final Long loanId, final String apiRequestBodyAsJson) {
|
||||
public String executeLoanCharge(@PathParam("loanId") final Long loanId, @QueryParam("command") final String commandParam,
|
||||
final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createLoanCharge(loanId).withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "pay")) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().payLoanCharge(loanId, null).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createLoanCharge(loanId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
@@ -152,21 +160,22 @@ public class LoanChargesApiResource {
|
||||
@Path("{chargeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String waiveLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId,
|
||||
@QueryParam("command") final String commandParam) {
|
||||
public String executeLoanCharge(@PathParam("loanId") final Long loanId, @PathParam("chargeId") final Long loanChargeId,
|
||||
@QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) {
|
||||
|
||||
String json = "";
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "waive")) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().waiveLoanCharge(loanId, loanChargeId).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
json = this.toApiJsonSerializer.serialize(result);
|
||||
final CommandWrapper commandRequest = builder.waiveLoanCharge(loanId, loanChargeId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "pay")) {
|
||||
final CommandWrapper commandRequest = builder.payLoanCharge(loanId, loanChargeId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
|
||||
return json;
|
||||
if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam); }
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
|
||||
+63
-14
@@ -41,7 +41,12 @@ import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSeria
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.core.service.Page;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.staff.data.StaffData;
|
||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
|
||||
import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.mifosplatform.portfolio.account.service.PortfolioAccountReadPlatformService;
|
||||
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
@@ -73,6 +78,7 @@ import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformSe
|
||||
import org.mifosplatform.portfolio.note.data.NoteData;
|
||||
import org.mifosplatform.portfolio.note.domain.NoteType;
|
||||
import org.mifosplatform.portfolio.note.service.NoteReadPlatformServiceImpl;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -89,14 +95,15 @@ public class LoansApiResource {
|
||||
"clientId", "group", "loanProductId", "loanProductName", "loanProductDescription", "fundId", "fundName", "loanPurposeId",
|
||||
"loanPurposeName", "loanOfficerId", "loanOfficerName", "currency", "principal", "totalOverpaid", "inArrearsTolerance",
|
||||
"termFrequency", "termPeriodFrequencyType", "numberOfRepayments", "repaymentEvery", "interestRatePerPeriod",
|
||||
"annualInterestRate", "repaymentFrequencyType", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "interestRateFrequencyType",
|
||||
"amortizationType", "interestType", "interestCalculationPeriodType", "expectedFirstRepaymentOnDate", "graceOnPrincipalPayment",
|
||||
"graceOnInterestPayment", "graceOnInterestCharged", "interestChargedFromDate", "timeline", "totalFeeChargesAtDisbursement",
|
||||
"summary", "repaymentSchedule", "transactions", "charges", "collateral", "guarantors", "meeting", "productOptions",
|
||||
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions",
|
||||
"termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
|
||||
"loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting",
|
||||
"loanCounter", "loanProductCounter", "notes"));
|
||||
"annualInterestRate", "repaymentFrequencyType", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
|
||||
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
|
||||
"expectedFirstRepaymentOnDate", "graceOnPrincipalPayment", "graceOnInterestPayment", "graceOnInterestCharged",
|
||||
"interestChargedFromDate", "timeline", "totalFeeChargesAtDisbursement", "summary", "repaymentSchedule", "transactions",
|
||||
"charges", "collateral", "guarantors", "meeting", "productOptions", "amortizationTypeOptions", "interestTypeOptions",
|
||||
"interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions", "termFrequencyTypeOptions",
|
||||
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions",
|
||||
"loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting",
|
||||
"loanCounter", "loanProductCounter", "notes","accountLinkingOptions","linkedAccount"));
|
||||
|
||||
private final String resourceNameForPermissions = "LOAN";
|
||||
|
||||
@@ -119,6 +126,8 @@ public class LoansApiResource {
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final CalendarReadPlatformService calendarReadPlatformService;
|
||||
private final NoteReadPlatformServiceImpl noteReadPlatformService;
|
||||
private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
|
||||
@@ -133,7 +142,9 @@ public class LoansApiResource {
|
||||
final DefaultToApiJsonSerializer<LoanScheduleData> loanScheduleToApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final FromJsonHelper fromJsonHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService) {
|
||||
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
|
||||
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanProductReadPlatformService = loanProductReadPlatformService;
|
||||
@@ -153,6 +164,8 @@ public class LoansApiResource {
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
this.calendarReadPlatformService = calendarReadPlatformService;
|
||||
this.noteReadPlatformService = noteReadPlatformService;
|
||||
this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl;
|
||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -225,10 +238,23 @@ public class LoansApiResource {
|
||||
}
|
||||
|
||||
allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(officeId, staffInSelectedOfficeOnly);
|
||||
|
||||
Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
if(clientId != null){
|
||||
CurrencyData currencyData = newLoanAccount.currency();
|
||||
String currencyCode = null;
|
||||
if (currencyData != null) {
|
||||
currencyCode = currencyData.code();
|
||||
}
|
||||
long[] accountStatus = { SavingsAccountStatusType.ACTIVE.getValue() };
|
||||
accountLinkingOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(
|
||||
PortfolioAccountType.SAVINGS.getValue(), clientId, currencyCode, accountStatus);
|
||||
|
||||
}
|
||||
|
||||
// add product options, allowed loan officers and calendar options
|
||||
// (calendar options will be null in individual loan)
|
||||
newLoanAccount = LoanAccountData.associationsAndTemplate(newLoanAccount, productOptions, allowedLoanOfficers, calendarOptions);
|
||||
newLoanAccount = LoanAccountData.associationsAndTemplate(newLoanAccount, productOptions, allowedLoanOfficers, calendarOptions,accountLinkingOptions);
|
||||
}
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, newLoanAccount, LOAN_DATA_PARAMETERS);
|
||||
@@ -253,13 +279,15 @@ public class LoansApiResource {
|
||||
Collection<CollateralData> collateral = null;
|
||||
CalendarData meeting = null;
|
||||
Collection<NoteData> notes = null;
|
||||
PortfolioAccountData linkedAccount = null;
|
||||
|
||||
final Set<String> mandatoryResponseParameters = new HashSet<String>();
|
||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (!associationParameters.isEmpty()) {
|
||||
|
||||
if (associationParameters.contains("all")) {
|
||||
associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "charges", "guarantors", "collateral","notes"));
|
||||
associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "charges", "guarantors", "collateral",
|
||||
"notes", "linkedAccount"));
|
||||
}
|
||||
|
||||
if (associationParameters.contains("guarantors")) {
|
||||
@@ -305,7 +333,7 @@ public class LoansApiResource {
|
||||
mandatoryResponseParameters.add("meeting");
|
||||
meeting = this.calendarReadPlatformService.retrieveLoanCalendar(loanId);
|
||||
}
|
||||
|
||||
|
||||
if (associationParameters.contains("notes")) {
|
||||
mandatoryResponseParameters.add("notes");
|
||||
notes = this.noteReadPlatformService.retrieveNotesByResource(loanId, NoteType.LOAN.getValue());
|
||||
@@ -313,6 +341,11 @@ public class LoansApiResource {
|
||||
notes = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (associationParameters.contains("linkedAccount")) {
|
||||
mandatoryResponseParameters.add("linkedAccount");
|
||||
linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<LoanProductData> productOptions = null;
|
||||
@@ -330,6 +363,9 @@ public class LoansApiResource {
|
||||
Collection<CodeValueData> loanPurposeOptions = null;
|
||||
Collection<CodeValueData> loanCollateralOptions = null;
|
||||
Collection<CalendarData> calendarOptions = null;
|
||||
Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
|
||||
|
||||
|
||||
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
|
||||
if (template) {
|
||||
@@ -348,11 +384,24 @@ public class LoansApiResource {
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
|
||||
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
|
||||
|
||||
allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(loanBasicDetails.officeId(), staffInSelectedOfficeOnly);
|
||||
allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(loanBasicDetails.officeId(),
|
||||
staffInSelectedOfficeOnly);
|
||||
|
||||
loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose");
|
||||
loanCollateralOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanCollateral");
|
||||
CurrencyData currencyData = loanBasicDetails.currency();
|
||||
String currencyCode = null;
|
||||
if (currencyData != null) {
|
||||
currencyCode = currencyData.code();
|
||||
}
|
||||
long[] accountStatus = { SavingsAccountStatusType.ACTIVE.getValue() };
|
||||
accountLinkingOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(
|
||||
PortfolioAccountType.SAVINGS.getValue(), loanBasicDetails.clientId(), currencyCode, accountStatus);
|
||||
|
||||
if (!associationParameters.contains("linkedAccount")) {
|
||||
mandatoryResponseParameters.add("linkedAccount");
|
||||
linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||
}
|
||||
if (loanBasicDetails.groupId() != null) {
|
||||
calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.groupId());
|
||||
}
|
||||
@@ -363,7 +412,7 @@ public class LoansApiResource {
|
||||
charges, collateral, guarantors, meeting, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
|
||||
loanCollateralOptions, calendarOptions, notes);
|
||||
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
|
||||
+45
-16
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.codes.data.CodeValueData;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.staff.data.StaffData;
|
||||
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
|
||||
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.collateral.data.CollateralData;
|
||||
@@ -129,6 +130,10 @@ public class LoanAccountData {
|
||||
//loanCycle
|
||||
private final Integer loanCounter;
|
||||
private final Integer loanProductCounter;
|
||||
|
||||
//linkable account details
|
||||
private final PortfolioAccountData linkedAccount;
|
||||
private final Collection<PortfolioAccountData> accountLinkingOptions;
|
||||
|
||||
/**
|
||||
* Used to produce a {@link LoanAccountData} with only collateral options
|
||||
@@ -203,7 +208,8 @@ public class LoanAccountData {
|
||||
final Boolean syncDisbursementWithMeeting = null;
|
||||
final Integer loancounter = null;
|
||||
final Integer loanProductCounter = null;
|
||||
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
final PortfolioAccountData linkedAccount = null;
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, principal, totalOverpaid, inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments,
|
||||
@@ -214,7 +220,7 @@ public class LoanAccountData {
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes);
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes,accountLinkingOptions, linkedAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,6 +296,8 @@ public class LoanAccountData {
|
||||
final Boolean syncDisbursementWithMeeting = null;
|
||||
final Integer loancounter = null;
|
||||
final Integer loanProductCounter = null;
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
final PortfolioAccountData linkedAccount = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -301,7 +309,7 @@ public class LoanAccountData {
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes);
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes,accountLinkingOptions, linkedAccount);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
|
||||
@@ -319,7 +327,7 @@ public class LoanAccountData {
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting,
|
||||
acc.loanCounter, acc.loanProductCounter, acc.notes);
|
||||
acc.loanCounter, acc.loanProductCounter, acc.notes,acc.accountLinkingOptions, acc.linkedAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,6 +404,8 @@ public class LoanAccountData {
|
||||
final Boolean syncDisbursementWithMeeting = null;
|
||||
final Integer loancounter = null;
|
||||
final Integer loanProductCounter = null;
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
final PortfolioAccountData linkedAccount = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -407,7 +417,7 @@ public class LoanAccountData {
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes);
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes,accountLinkingOptions, linkedAccount);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
|
||||
@@ -425,9 +435,9 @@ public class LoanAccountData {
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes);
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes,acc.accountLinkingOptions, acc.linkedAccount);
|
||||
}
|
||||
|
||||
|
||||
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
|
||||
final Collection<EnumOptionData> termFrequencyTypeOptions, final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
|
||||
final Collection<TransactionProcessingStrategyData> repaymentStrategyOptions,
|
||||
@@ -495,6 +505,8 @@ public class LoanAccountData {
|
||||
|
||||
final Integer loancounter = null;
|
||||
final Integer loanProductCounter = null;
|
||||
final PortfolioAccountData linkedAccount = null;
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
|
||||
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId,
|
||||
@@ -508,7 +520,8 @@ public class LoanAccountData {
|
||||
repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions,
|
||||
repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, notes);
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, notes,
|
||||
accountLinkingOptions, linkedAccount);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -556,7 +569,7 @@ public class LoanAccountData {
|
||||
calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes);
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,acc.accountLinkingOptions, acc.linkedAccount);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -600,6 +613,8 @@ public class LoanAccountData {
|
||||
final Collection<CodeValueData> loanPurposeOptions = null;
|
||||
final Collection<CodeValueData> loanCollateralOptions = null;
|
||||
final Collection<CalendarData> calendarOptions = null;
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
final PortfolioAccountData linkedAccount = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -611,7 +626,7 @@ public class LoanAccountData {
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes);
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes,accountLinkingOptions, linkedAccount);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -628,7 +643,8 @@ public class LoanAccountData {
|
||||
final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
|
||||
final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions, final ChargeData chargeTemplate,
|
||||
final Collection<StaffData> loanOfficerOptions, final Collection<CodeValueData> loanPurposeOptions,
|
||||
final Collection<CodeValueData> loanCollateralOptions, final Collection<CalendarData> calendarOptions, final Collection<NoteData> notes) {
|
||||
final Collection<CodeValueData> loanCollateralOptions, final Collection<CalendarData> calendarOptions, final Collection<NoteData> notes,
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions, PortfolioAccountData linkedAccount) {
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
|
||||
@@ -641,16 +657,16 @@ public class LoanAccountData {
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes);
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,accountLinkingOptions, linkedAccount);
|
||||
}
|
||||
|
||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||
final Collection<StaffData> allowedLoanOfficers, final Collection<CalendarData> calendarOptions) {
|
||||
final Collection<StaffData> allowedLoanOfficers, final Collection<CalendarData> calendarOptions,final Collection<PortfolioAccountData> accountLinkingOptions) {
|
||||
return associationsAndTemplate(acc, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors,
|
||||
acc.meeting, productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes);
|
||||
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions, acc.linkedAccount);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
|
||||
@@ -668,7 +684,7 @@ public class LoanAccountData {
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes);
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes,acc.accountLinkingOptions, acc.linkedAccount);
|
||||
}
|
||||
|
||||
private LoanAccountData(
|
||||
@@ -717,7 +733,8 @@ public class LoanAccountData {
|
||||
final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions, final ChargeData chargeTemplate,
|
||||
final Collection<StaffData> loanOfficerOptions, final Collection<CodeValueData> loanPurposeOptions,
|
||||
final Collection<CodeValueData> loanCollateralOptions, final Collection<CalendarData> calendarOptions,
|
||||
final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter, final Collection<NoteData> notes) {
|
||||
final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter, final Collection<NoteData> notes,
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions, PortfolioAccountData linkedAccount) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.status = status;
|
||||
@@ -827,6 +844,9 @@ public class LoanAccountData {
|
||||
|
||||
this.loanCounter = loanCounter;
|
||||
this.loanProductCounter = loanProductCounter;
|
||||
|
||||
this.linkedAccount = linkedAccount;
|
||||
this.accountLinkingOptions = accountLinkingOptions;
|
||||
}
|
||||
|
||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
|
||||
@@ -857,4 +877,13 @@ public class LoanAccountData {
|
||||
public Long groupId() {
|
||||
return group == null ? null : group.getId();
|
||||
}
|
||||
|
||||
|
||||
public CurrencyData currency() {
|
||||
return this.currency;
|
||||
}
|
||||
|
||||
public Long clientId(){
|
||||
return this.clientId;
|
||||
}
|
||||
}
|
||||
+78
-5
@@ -12,6 +12,7 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
|
||||
/**
|
||||
* Immutable data object for loan charge data.
|
||||
@@ -66,8 +67,18 @@ public class LoanChargeData {
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean penalty;
|
||||
|
||||
private final EnumOptionData chargePaymentMode;
|
||||
|
||||
private final boolean paid;
|
||||
|
||||
private final boolean waived;
|
||||
|
||||
private final boolean chargePayable;
|
||||
|
||||
private final Long loanId;
|
||||
|
||||
public static LoanChargeData template(final Collection<ChargeData> chargeOptions) {
|
||||
return new LoanChargeData(null, null, null, null, null, null, null, null, chargeOptions, false);
|
||||
return new LoanChargeData(null, null, null, null, null, null, null, null, chargeOptions, false, null, false, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,15 +87,16 @@ public class LoanChargeData {
|
||||
*/
|
||||
public static LoanChargeData newLoanChargeDetails(final Long chargeId, final String name, final CurrencyData currency,
|
||||
final BigDecimal amount, final BigDecimal percentage, final EnumOptionData chargeTimeType,
|
||||
final EnumOptionData chargeCalculationType, final boolean penalty) {
|
||||
return new LoanChargeData(null, chargeId, name, currency, amount, percentage, chargeTimeType, chargeCalculationType, null, penalty);
|
||||
final EnumOptionData chargeCalculationType, final boolean penalty, final EnumOptionData chargePaymentMode) {
|
||||
return new LoanChargeData(null, chargeId, name, currency, amount, percentage, chargeTimeType, chargeCalculationType, null, penalty,
|
||||
chargePaymentMode, false, false, null);
|
||||
}
|
||||
|
||||
public LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount,
|
||||
final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff,
|
||||
final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueDate,
|
||||
final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo,
|
||||
final boolean penalty) {
|
||||
final boolean penalty, EnumOptionData chargePaymentMode, boolean paid, boolean waived, Long loanId) {
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
this.name = name;
|
||||
@@ -100,6 +112,9 @@ public class LoanChargeData {
|
||||
this.percentage = percentage;
|
||||
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
|
||||
this.penalty = penalty;
|
||||
this.chargePaymentMode = chargePaymentMode;
|
||||
this.paid = paid;
|
||||
this.waived = waived;
|
||||
|
||||
if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) {
|
||||
this.amountOrPercentage = this.percentage;
|
||||
@@ -108,11 +123,14 @@ public class LoanChargeData {
|
||||
}
|
||||
|
||||
this.chargeOptions = null;
|
||||
this.chargePayable = isChargePayable();
|
||||
this.loanId = loanId;
|
||||
}
|
||||
|
||||
private LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount,
|
||||
final BigDecimal pecentage, final EnumOptionData chargeTimeType, final EnumOptionData chargeCalculationType,
|
||||
final Collection<ChargeData> chargeOptions, final boolean penalty) {
|
||||
final Collection<ChargeData> chargeOptions, final boolean penalty, EnumOptionData chargePaymentMode, boolean paid,
|
||||
boolean waived, Long loanId) {
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
this.name = name;
|
||||
@@ -128,6 +146,9 @@ public class LoanChargeData {
|
||||
this.percentage = pecentage;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.penalty = penalty;
|
||||
this.chargePaymentMode = chargePaymentMode;
|
||||
this.paid = paid;
|
||||
this.waived = waived;
|
||||
|
||||
if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) {
|
||||
this.amountOrPercentage = this.percentage;
|
||||
@@ -136,5 +157,57 @@ public class LoanChargeData {
|
||||
}
|
||||
|
||||
this.chargeOptions = chargeOptions;
|
||||
this.chargePayable = isChargePayable();
|
||||
this.loanId = loanId;
|
||||
}
|
||||
|
||||
public LoanChargeData(final Long id,final LocalDate dueAsOfDate,final BigDecimal amountOutstanding,final Long loanId) {
|
||||
this.id = id;
|
||||
this.chargeId = null;
|
||||
this.name = null;
|
||||
this.currency = null;
|
||||
this.amount = null;
|
||||
this.amountPaid = null;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
this.amountOutstanding = amountOutstanding;
|
||||
this.chargeTimeType = null;
|
||||
this.dueDate = dueAsOfDate;
|
||||
this.chargeCalculationType = null;
|
||||
this.percentage = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.penalty = false;
|
||||
this.chargePaymentMode = null;
|
||||
this.paid = false;
|
||||
this.waived = false;
|
||||
this.amountOrPercentage = null;
|
||||
this.chargeOptions = null;
|
||||
this.chargePayable = false;
|
||||
this.loanId = loanId;
|
||||
}
|
||||
|
||||
public boolean isChargePayable() {
|
||||
boolean isAccountTransfer = false;
|
||||
if(this.chargePaymentMode!=null){
|
||||
isAccountTransfer = ChargePaymentMode.fromInt(this.chargePaymentMode.getId().intValue()).isPaymentModeAccountTransfer();
|
||||
}
|
||||
return isAccountTransfer && !this.paid && !this.waived;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public LocalDate getDueDate() {
|
||||
return this.dueDate;
|
||||
}
|
||||
|
||||
public Long getLoanId() {
|
||||
return this.loanId;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getAmountOutstanding() {
|
||||
return this.amountOutstanding;
|
||||
}
|
||||
}
|
||||
+14
@@ -29,6 +29,8 @@ public class LoanTransactionEnumData {
|
||||
private final boolean approveTransfer;
|
||||
private final boolean withdrawTransfer;
|
||||
private final boolean rejectTransfer;
|
||||
private final boolean chargePayment;
|
||||
private final boolean refund;
|
||||
|
||||
public LoanTransactionEnumData(final Long id, final String code, final String value) {
|
||||
this.id = id;
|
||||
@@ -48,6 +50,8 @@ public class LoanTransactionEnumData {
|
||||
this.approveTransfer = Long.valueOf(13).equals(this.id);
|
||||
this.withdrawTransfer = Long.valueOf(14).equals(this.id);
|
||||
this.rejectTransfer = Long.valueOf(15).equals(this.id);
|
||||
this.refund = Long.valueOf(16).equals(this.id);
|
||||
this.chargePayment = Long.valueOf(17).equals(this.id);
|
||||
}
|
||||
|
||||
public Long id() {
|
||||
@@ -117,4 +121,14 @@ public class LoanTransactionEnumData {
|
||||
return this.rejectTransfer;
|
||||
}
|
||||
|
||||
|
||||
public boolean isChargePayment() {
|
||||
return this.chargePayment;
|
||||
}
|
||||
|
||||
|
||||
public boolean isRefund() {
|
||||
return this.refund;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -56,6 +56,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
|
||||
}
|
||||
break;
|
||||
case LOAN_CHARGE_PAYMENT:
|
||||
case LOAN_REPAYMENT_OR_WAIVER:
|
||||
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
|
||||
newState = stateOf(LoanStatus.ACTIVE, allowedLoanStatuses);
|
||||
|
||||
+112
-21
@@ -501,6 +501,32 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return applyLoanChargeTransaction;
|
||||
}
|
||||
|
||||
private void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
chargesPayment.updateLoan(this);
|
||||
this.loanTransactions.add(chargesPayment);
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<LoanRepaymentScheduleInstallment>();
|
||||
LocalDate startDate = getDisbursementDate();
|
||||
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
if (charge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())) {
|
||||
chargePaymentInstallments.add(installment);
|
||||
break;
|
||||
}
|
||||
startDate = installment.getDueDate();
|
||||
}
|
||||
Set<LoanCharge> loanCharges = new HashSet<LoanCharge>(1);
|
||||
loanCharges.add(charge);
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(chargesPayment, getCurrency(), chargePaymentInstallments, loanCharges);
|
||||
updateLoanSummaryDerivedFields();
|
||||
doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
private void validateLoanIsNotClosed(final LoanCharge loanCharge) {
|
||||
if (isClosed()) {
|
||||
final String defaultUserMessage = "This charge cannot be added as the loan is already closed.";
|
||||
@@ -1420,13 +1446,6 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final Money totalFeeChargesDueAtDisbursement = this.summary.getTotalFeeChargesDueAtDisbursement(loanCurrency());
|
||||
if (totalFeeChargesDueAtDisbursement.isGreaterThanZero()) {
|
||||
|
||||
final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(this.getOffice(),
|
||||
totalFeeChargesDueAtDisbursement, null, disbursedOn, txnExternalId);
|
||||
final Money zero = Money.zero(getCurrency());
|
||||
chargesPayment.updateComponents(zero, zero, totalFeeChargesDueAtDisbursement, zero);
|
||||
chargesPayment.updateLoan(this);
|
||||
this.loanTransactions.add(chargesPayment);
|
||||
|
||||
/**
|
||||
* all Charges repaid at disbursal is marked as repaid and
|
||||
* "APPLY Charge" transactions are created for all other fees (
|
||||
@@ -1434,10 +1453,19 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
**/
|
||||
for (final LoanCharge charge : setOfLoanCharges()) {
|
||||
if (charge.isDueAtDisbursement()) {
|
||||
charge.markAsFullyPaid();
|
||||
// Add "Loan Charge Paid By" details to this transaction
|
||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount());
|
||||
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
|
||||
if (!charge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||
final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(this.getOffice(),
|
||||
charge.getAmount(getCurrency()), null, disbursedOn, txnExternalId);
|
||||
final Money zero = Money.zero(getCurrency());
|
||||
chargesPayment.updateComponents(zero, zero, totalFeeChargesDueAtDisbursement, zero);
|
||||
chargesPayment.updateLoan(this);
|
||||
this.loanTransactions.add(chargesPayment);
|
||||
|
||||
charge.markAsFullyPaid();
|
||||
// Add "Loan Charge Paid By" details to this transaction
|
||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount());
|
||||
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
|
||||
}
|
||||
} else {
|
||||
handleChargeAppliedTransaction(charge, disbursedOn);
|
||||
}
|
||||
@@ -1462,6 +1490,24 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return disbursementTransaction;
|
||||
}
|
||||
|
||||
public LoanTransaction handlePayDisbursementTransaction(final Long chargeId, final LoanTransaction chargesPayment) {
|
||||
LoanCharge charge = null;
|
||||
for (LoanCharge loanCharge : this.charges) {
|
||||
if (chargeId.equals(loanCharge.getId())) {
|
||||
charge = loanCharge;
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("null")
|
||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount());
|
||||
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
|
||||
final Money zero = Money.zero(getCurrency());
|
||||
chargesPayment.updateComponents(zero, zero, charge.getAmount(getCurrency()), zero);
|
||||
chargesPayment.updateLoan(this);
|
||||
this.loanTransactions.add(chargesPayment);
|
||||
charge.markAsFullyPaid();
|
||||
return chargesPayment;
|
||||
}
|
||||
|
||||
public Map<String, Object> undoDisbursal(final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
|
||||
@@ -1547,6 +1593,31 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean allowTransactionsOnNonWorkingDay, LoanTransaction paymentTransaction) {
|
||||
|
||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
||||
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
||||
validateRepaymentDateIsOnNonWorkingDay(paymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
||||
|
||||
if (paymentTransaction.getTransactionDate().isAfter(new LocalDate())) {
|
||||
final String errorMessage = "The date on which a loan charge paid cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("charge.payment", "cannot.be.a.future.date", errorMessage,
|
||||
paymentTransaction.getTransactionDate());
|
||||
}
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
LoanCharge charge = null;
|
||||
for (LoanCharge loanCharge : this.charges) {
|
||||
if (chargeId.equals(loanCharge.getId())) {
|
||||
charge = loanCharge;
|
||||
}
|
||||
}
|
||||
handleChargePaidTransaction(charge, paymentTransaction, loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
public void makeRefund(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
@@ -1571,16 +1642,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final String errorMessage = "Transfer funds is allowed only for loan accounts with overpaid status ";
|
||||
throw new InvalidLoanStateTransitionException("transaction", "is.not.a.overpaid.loan", errorMessage);
|
||||
}
|
||||
this.totalOverpaid = this.totalOverpaid.subtract(loanTransaction.getAmount(getCurrency()).getAmount());
|
||||
if (this.totalOverpaid.compareTo(BigDecimal.ZERO) == 0) {
|
||||
handleLoanRepaymentInFull(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
}
|
||||
loanTransaction.updateLoan(this);
|
||||
|
||||
if (loanTransaction.isNotZero(loanCurrency())) {
|
||||
this.loanTransactions.add(loanTransaction);
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail handleRepaymentOrWaiverTransaction(final LoanTransaction loanTransaction,
|
||||
@@ -1676,11 +1744,25 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
private void handleLoanRepaymentInFull(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
boolean isAllChargesPaid = true;
|
||||
for (LoanCharge loanCharge : this.charges) {
|
||||
if (!(loanCharge.isPaid() || loanCharge.isWaived())) {
|
||||
isAllChargesPaid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isAllChargesPaid) {
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
|
||||
this.closedOnDate = transactionDate.toDate();
|
||||
this.actualMaturityDate = transactionDate.toDate();
|
||||
this.closedOnDate = transactionDate.toDate();
|
||||
this.actualMaturityDate = transactionDate.toDate();
|
||||
} else if (LoanStatus.fromInt(this.loanStatus).isOverpaid()) {
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
@@ -2194,7 +2276,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
Money cumulativePaid = Money.zero(loanCurrency());
|
||||
|
||||
for (final LoanTransaction repayment : this.loanTransactions) {
|
||||
if (repayment.isRepayment()) {
|
||||
if (repayment.isRepayment() && !repayment.isReversed()) {
|
||||
cumulativePaid = cumulativePaid.plus(repayment.getAmount(loanCurrency()));
|
||||
}
|
||||
}
|
||||
@@ -2731,6 +2813,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
action = "close";
|
||||
postfix = "cannot.be.undone.before.client.transfer.date";
|
||||
break;
|
||||
case LOAN_CHARGE_PAYMENT:
|
||||
errorMessage = "The date on which a charge payment is made cannot be earlier than client's transfer date to this office";
|
||||
action = "charge.payment";
|
||||
postfix = "cannot.be.made.before.client.transfer.date";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -2738,4 +2825,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<LoanCharge> charges() {
|
||||
return this.charges;
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -13,11 +13,14 @@ import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
|
||||
public interface LoanAccountDomainService {
|
||||
|
||||
LoanTransaction makeRepayment(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||
LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
||||
|
||||
LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
|
||||
|
||||
void reverseTransfer(LoanTransaction loanTransaction);
|
||||
|
||||
LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount,
|
||||
PaymentDetail paymentDetail, String noteText, String txnExternalId, Integer transactionType);
|
||||
}
|
||||
|
||||
+40
-2
@@ -70,11 +70,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public LoanTransaction makeRepayment(final Long accountId, final CommandProcessingResultBuilder builderResult,
|
||||
public LoanTransaction makeRepayment(final Loan loan, final CommandProcessingResultBuilder builderResult,
|
||||
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
|
||||
final String txnExternalId) {
|
||||
|
||||
final Loan loan = this.loanAccountAssembler.assembleFrom(accountId);
|
||||
checkClientOrGroupActive(loan);
|
||||
|
||||
// TODO: Is it required to validate transaction date with meeting dates
|
||||
@@ -137,6 +136,45 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
return newRepaymentTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public LoanTransaction makeChargePayment(final Loan loan, final Long chargeId, final LocalDate transactionDate,
|
||||
final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, final String txnExternalId,
|
||||
final Integer transactionType) {
|
||||
|
||||
checkClientOrGroupActive(loan);
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
|
||||
final Money paymentAmout = Money.of(loan.getCurrency(), transactionAmount);
|
||||
LoanTransactionType loanTransactionType = LoanTransactionType.fromInt(transactionType);
|
||||
|
||||
final LoanTransaction newPaymentTransaction = LoanTransaction.loanPayment(null, loan.getOffice(), paymentAmout, paymentDetail,
|
||||
transactionDate, txnExternalId, loanTransactionType);
|
||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository
|
||||
.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||
if(loanTransactionType.isRepaymentAtDisbursement()){
|
||||
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction);
|
||||
}else{
|
||||
loan.makeChargePayment(chargeId, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
|
||||
allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay, newPaymentTransaction);
|
||||
}
|
||||
this.loanTransactionRepository.save(newPaymentTransaction);
|
||||
this.loanRepository.save(loan);
|
||||
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
final Note note = Note.loanTransactionNote(loan, newPaymentTransaction, noteText);
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
return newPaymentTransaction;
|
||||
}
|
||||
|
||||
private void postJournalEntries(final Loan loanAccount, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
|
||||
+23
-4
@@ -28,6 +28,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeWithoutMandatoryFieldException;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
|
||||
@@ -54,6 +55,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "charge_calculation_enum")
|
||||
private Integer chargeCalculation;
|
||||
|
||||
@Column(name = "charge_payment_mode_enum")
|
||||
private Integer chargePaymentMode;
|
||||
|
||||
@Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal percentage;
|
||||
@@ -92,16 +96,17 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
ChargeTimeType chargeTime = null;
|
||||
ChargeCalculationType chargeCalculation = null;
|
||||
ChargePaymentMode chargePaymentMode = null;
|
||||
|
||||
return new LoanCharge(loan, chargeDefinition, loan.getPrincpal().getAmount(), amount, chargeTime, chargeCalculation, dueDate);
|
||||
return new LoanCharge(loan, chargeDefinition, loan.getPrincpal().getAmount(), amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode);
|
||||
}
|
||||
|
||||
/*
|
||||
* loanPrincipal is required for charges that are percentage based
|
||||
*/
|
||||
public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) {
|
||||
return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate);
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, ChargePaymentMode chargePaymentMode) {
|
||||
return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode);
|
||||
}
|
||||
|
||||
protected LoanCharge() {
|
||||
@@ -109,7 +114,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) {
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, ChargePaymentMode chargePaymentMode) {
|
||||
this.loan = loan;
|
||||
this.charge = chargeDefinition;
|
||||
this.penaltyCharge = chargeDefinition.isPenalty();
|
||||
@@ -141,6 +146,11 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
if (amount != null) {
|
||||
chargeAmount = amount;
|
||||
}
|
||||
|
||||
this.chargePaymentMode = chargeDefinition.getChargePaymentMode();
|
||||
if(chargePaymentMode != null){
|
||||
this.chargePaymentMode = chargePaymentMode.getValue();
|
||||
}
|
||||
|
||||
populateDerivedFields(loanPrincipal, chargeAmount);
|
||||
this.paid = determineIfFullyPaid();
|
||||
@@ -388,6 +398,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public BigDecimal amount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public BigDecimal amountOutstanding(){
|
||||
return this.amountOutstanding;
|
||||
}
|
||||
|
||||
public boolean hasNotLoanIdentifiedBy(final Long loanId) {
|
||||
return !hasLoanIdentifiedBy(loanId);
|
||||
@@ -513,4 +527,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
.append(this.amount).append(this.getDueLocalDate()) //
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
|
||||
public ChargePaymentMode getChargePaymentMode() {
|
||||
return ChargePaymentMode.fromInt(this.chargePaymentMode);
|
||||
}
|
||||
}
|
||||
+12
@@ -58,5 +58,17 @@ public class LoanChargePaidBy extends AbstractPersistable<Long> {
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
boolean isEqual = false;
|
||||
LoanChargePaidBy paidBy = (LoanChargePaidBy)obj;
|
||||
if(this.loanTransaction.getId() == paidBy.loanTransaction.getId()
|
||||
&& this.loanCharge.getId() == paidBy.loanCharge.getId()
|
||||
&& this.amount == paidBy.amount){
|
||||
isEqual = true;
|
||||
}
|
||||
return isEqual;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -22,5 +22,6 @@ public enum LoanEvent {
|
||||
WRITE_OFF_OUTSTANDING, //
|
||||
LOAN_RESCHEDULE, //
|
||||
INTERST_REBATE_OWED, //
|
||||
LOAN_OVERPAYMENT;
|
||||
LOAN_OVERPAYMENT,
|
||||
LOAN_CHARGE_PAYMENT;
|
||||
}
|
||||
|
||||
+21
@@ -108,6 +108,12 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
final LocalDate paymentDate, final String externalId) {
|
||||
return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
|
||||
}
|
||||
|
||||
public static LoanTransaction loanPayment(final Loan loan ,final Office office, final Money amount, final PaymentDetail paymentDetail,
|
||||
final LocalDate paymentDate, final String externalId,final LoanTransactionType transactionType){
|
||||
return new LoanTransaction(loan, office, transactionType, paymentDetail, amount.getAmount(), paymentDate,
|
||||
externalId);
|
||||
}
|
||||
|
||||
public static LoanTransaction repaymentAtDisbursement(final Office office, final Money amount, final PaymentDetail paymentDetail,
|
||||
final LocalDate paymentDate, final String externalId) {
|
||||
@@ -355,6 +361,21 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
public boolean isNotWaiver() {
|
||||
return !isInterestWaiver() && !isChargesWaiver();
|
||||
}
|
||||
|
||||
public boolean isChargePayment(){
|
||||
return getTypeOf().isChargePayment() && isNotReversed();
|
||||
}
|
||||
|
||||
public boolean isPenaltyPayment(){
|
||||
boolean isPenalty = false;
|
||||
if(isChargePayment()){
|
||||
for(LoanChargePaidBy chargePaidBy:this.loanChargesPaid){
|
||||
isPenalty = chargePaidBy.getLoanCharge().isPenaltyCharge();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isPenalty;
|
||||
}
|
||||
|
||||
public boolean isWriteOff() {
|
||||
return getTypeOf().isWriteOff() && isNotReversed();
|
||||
|
||||
+9
-1
@@ -34,7 +34,8 @@ public enum LoanTransactionType {
|
||||
WITHDRAW_TRANSFER(14, "loanTransactionType.withdrawTransfer"), //
|
||||
REJECT_TRANSFER(15, "loanTransactionType.rejectTransfer"),
|
||||
|
||||
REFUND(16,"loanTransactionType.refund");
|
||||
REFUND(16,"loanTransactionType.refund"),
|
||||
CHARGE_PAYMENT(17,"loanTransactionType.chargePayment");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -106,6 +107,9 @@ public enum LoanTransactionType {
|
||||
case 16:
|
||||
loanTransactionType = LoanTransactionType.REFUND;
|
||||
break;
|
||||
case 17:
|
||||
loanTransactionType = LoanTransactionType.CHARGE_PAYMENT;
|
||||
break;
|
||||
default:
|
||||
loanTransactionType = LoanTransactionType.INVALID;
|
||||
break;
|
||||
@@ -144,4 +148,8 @@ public enum LoanTransactionType {
|
||||
public boolean isWriteOff() {
|
||||
return this.value.equals(LoanTransactionType.WRITEOFF.getValue());
|
||||
}
|
||||
|
||||
public boolean isChargePayment(){
|
||||
return this.value.equals(LoanTransactionType.CHARGE_PAYMENT.getValue());
|
||||
}
|
||||
}
|
||||
+27
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -64,7 +65,33 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
wrapper.reprocess(currency, disbursementDate, installments, charges);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
|
||||
List<LoanTransaction> transactionstoBeProcessed = new ArrayList<LoanTransaction>();
|
||||
for (LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||
if (loanTransaction.isChargePayment()){
|
||||
Set<LoanChargePaidBy> chargePaidBies =loanTransaction.getLoanChargesPaid();
|
||||
Set<LoanCharge> transferCharges = new HashSet<LoanCharge>();
|
||||
for(LoanChargePaidBy chargePaidBy: chargePaidBies){
|
||||
transferCharges.add(chargePaidBy.getLoanCharge());
|
||||
}
|
||||
List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<LoanRepaymentScheduleInstallment>();
|
||||
LocalDate startDate = disbursementDate;
|
||||
for(LoanRepaymentScheduleInstallment installment:installments){
|
||||
for(LoanCharge loanCharge :transferCharges){
|
||||
if(loanCharge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())){
|
||||
chargePaymentInstallments.add(installment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
startDate = installment.getDueDate();
|
||||
}
|
||||
loanTransaction.resetDerivedComponents();
|
||||
handleTransaction(loanTransaction, currency, chargePaymentInstallments, transferCharges);
|
||||
}else{
|
||||
transactionstoBeProcessed.add(loanTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
for (LoanTransaction loanTransaction : transactionstoBeProcessed) {
|
||||
|
||||
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver()) {
|
||||
// pass through for new transactions
|
||||
|
||||
+9
-1
@@ -97,7 +97,15 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL
|
||||
} else if (loanTransaction.isInterestWaiver()) {
|
||||
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
} else {
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
}else {
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
|
||||
|
||||
+18
-2
@@ -81,7 +81,15 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
if (loanTransaction.isInterestWaiver()) {
|
||||
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
} else {
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
}else {
|
||||
|
||||
if (currentInstallment.isPrincipalNotCompleted(currency)) {
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||
@@ -141,7 +149,15 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
} else if (loanTransaction.isInterestWaiver()) {
|
||||
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
} else {
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
}else {
|
||||
// 1. pay of principal before interest.
|
||||
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||
|
||||
+10
-1
@@ -71,7 +71,16 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
} else {
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
}else {
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
|
||||
+9
@@ -84,6 +84,15 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends Abstrac
|
||||
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
} else {
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
|
||||
+10
-1
@@ -71,7 +71,16 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
} else {
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
}else {
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
|
||||
+21
-2
@@ -80,7 +80,18 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
||||
|
||||
final Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestWaivedPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
} else {
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
Money principalPortion = Money.zero(currency);
|
||||
Money interestPortion = Money.zero(currency);
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
}else {
|
||||
|
||||
LoanRepaymentScheduleInstallment currentInstallmentBasedOnTransactionDate = nearestInstallment(
|
||||
loanTransaction.getTransactionDate(), installments);
|
||||
@@ -161,7 +172,15 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
||||
} else if (loanTransaction.isInterestWaiver()) {
|
||||
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
} else {
|
||||
} else if(loanTransaction.isChargePayment()){
|
||||
if(loanTransaction.isPenaltyPayment()){
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}else{
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
}
|
||||
}else {
|
||||
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class PayLoanChargeCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final LoanWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public PayLoanChargeCommandHandler(final LoanWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.payLoanCharge(command.getLoanId(), command.entityId(), command, command.entityId() == null);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -42,7 +42,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
"loanTermFrequencyType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType",
|
||||
"interestCalculationPeriodType", "expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment",
|
||||
"graceOnInterestPayment", "graceOnInterestCharged", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale",
|
||||
"dateFormat", "charges", "collateral", "syncDisbursementWithMeeting"));
|
||||
"dateFormat", "charges", "collateral", "syncDisbursementWithMeeting","linkAccountId"));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
|
||||
+40
-1
@@ -23,7 +23,9 @@ import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -48,7 +50,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
"loanPurposeId", "inArrearsTolerance", "charges", "collateral", // optional
|
||||
"transactionProcessingStrategyId", // settings
|
||||
"calendarId", // optional
|
||||
"syncDisbursementWithMeeting"//optional
|
||||
"syncDisbursementWithMeeting",//optional
|
||||
"linkAccountId"
|
||||
));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
@@ -233,6 +236,14 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
element);
|
||||
baseDataValidator.reset().parameter(transactionProcessingStrategyIdParameterName).value(transactionProcessingStrategyId).notNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
final String linkAccountIdParameterName = "linkAccountId";
|
||||
if (fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) {
|
||||
final Long linkAccountId = fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element);
|
||||
baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).ignoreIfNull()
|
||||
.longGreaterThanZero();
|
||||
}
|
||||
|
||||
|
||||
// charges
|
||||
final String chargesParameterName = "charges";
|
||||
@@ -533,6 +544,14 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
baseDataValidator.reset().parameter(submittedOnNoteParameterName).value(submittedOnNote).ignoreIfNull()
|
||||
.notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
final String linkAccountIdParameterName = "linkAccountId";
|
||||
if (fromApiJsonHelper.parameterExists(submittedOnNoteParameterName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final Long linkAccountId = fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element);
|
||||
baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).ignoreIfNull()
|
||||
.longGreaterThanZero();
|
||||
}
|
||||
|
||||
// charges
|
||||
final String chargesParameterName = "charges";
|
||||
@@ -677,5 +696,25 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
|
||||
public void validatelinkedSavingsAccount(final SavingsAccount savingsAccount,final Loan loanApplication) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
if(savingsAccount.isNotActive()){
|
||||
ApiParameterError error = ApiParameterError.parameterError(
|
||||
"validation.msg.loan.linked.savings.account.is.not.active",
|
||||
"Linked Savings account with id:"+savingsAccount.getId()+ " is not in active state", "linkAccountId",
|
||||
savingsAccount.getId());
|
||||
dataValidationErrors.add(error);
|
||||
}else if(loanApplication.getClientId() != savingsAccount.clientId()){
|
||||
ApiParameterError error = ApiParameterError.parameterError(
|
||||
"validation.msg.loan.linked.savings.account.not.belongs.to.same.client",
|
||||
"Linked Savings account with id:"+savingsAccount.getId()+ " is not belongs to the same client", "linkAccountId",
|
||||
savingsAccount.getId());
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+47
-19
@@ -50,8 +50,8 @@ public final class LoanEventApiJsonValidator {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale",
|
||||
"dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
|
||||
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("actualDisbursementDate", "externalId", "note",
|
||||
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
@@ -70,7 +70,7 @@ public final class LoanEventApiJsonValidator {
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
|
||||
public void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance) {
|
||||
if (null != calendarInstance) {
|
||||
Calendar calendar = calendarInstance.getCalendar();
|
||||
@@ -79,21 +79,21 @@ public final class LoanEventApiJsonValidator {
|
||||
if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), actualDisbursementDate)) {
|
||||
final String errorMessage = "Expected disbursement date '" + actualDisbursementDate.toString()
|
||||
+ "' does not fall on a meeting date.";
|
||||
throw new NotValidRecurringDateException("loan.actual.disbursement.date", errorMessage, actualDisbursementDate.toString(),
|
||||
calendar.getTitle());
|
||||
throw new NotValidRecurringDateException("loan.actual.disbursement.date", errorMessage,
|
||||
actualDisbursementDate.toString(), calendar.getTitle());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void validateTransaction(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
|
||||
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
|
||||
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "externalId",
|
||||
"note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber",
|
||||
"bankNumber"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
@@ -119,8 +119,9 @@ public final class LoanEventApiJsonValidator {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "externalId", "note",
|
||||
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
|
||||
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "externalId",
|
||||
"note", "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber",
|
||||
"bankNumber"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
@@ -148,8 +149,7 @@ public final class LoanEventApiJsonValidator {
|
||||
if (calendar != null && repaymentDate != null) {
|
||||
// Disbursement date should fall on a meeting date
|
||||
if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), repaymentDate)) {
|
||||
final String errorMessage = "Transaction date '" + repaymentDate.toString()
|
||||
+ "' does not fall on a meeting date.";
|
||||
final String errorMessage = "Transaction date '" + repaymentDate.toString() + "' does not fall on a meeting date.";
|
||||
throw new NotValidRecurringDateException("loan.transaction.date", errorMessage, repaymentDate.toString(),
|
||||
calendar.getTitle());
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public final class LoanEventApiJsonValidator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
||||
// Validate all string payment detail fields for max length
|
||||
final Integer paymentTypeId = fromApiJsonHelper.extractIntegerWithLocaleNamed("paymentTypeId", element);
|
||||
@@ -260,20 +260,20 @@ public final class LoanEventApiJsonValidator {
|
||||
|
||||
final String assignmentDateStr = this.fromApiJsonHelper.extractStringNamed("assignmentDate", element);
|
||||
baseDataValidator.reset().parameter("assignmentDate").value(assignmentDateStr).notBlank();
|
||||
|
||||
if(!StringUtils.isBlank(assignmentDateStr)){
|
||||
|
||||
if (!StringUtils.isBlank(assignmentDateStr)) {
|
||||
final LocalDate assignmentDate = this.fromApiJsonHelper.extractLocalDateNamed("assignmentDate", element);
|
||||
baseDataValidator.reset().parameter("assignmentDate").value(assignmentDate).notNull();
|
||||
}
|
||||
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
|
||||
public void validateForBulkLoanReassignment(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("assignmentDate", "fromLoanOfficerId",
|
||||
"toLoanOfficerId", "loans", "locale", "dateFormat"));
|
||||
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("assignmentDate", "fromLoanOfficerId", "toLoanOfficerId",
|
||||
"loans", "locale", "dateFormat"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
|
||||
@@ -294,4 +294,32 @@ public final class LoanEventApiJsonValidator {
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateChargePaymentTransaction(final String json,boolean isChargeIdIncluded) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
Set<String> transactionParameters = null;
|
||||
if(isChargeIdIncluded){
|
||||
transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "locale", "dateFormat","chargeId"));
|
||||
}else{
|
||||
transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "locale", "dateFormat"));
|
||||
}
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource("loan.charge.payment.transaction");
|
||||
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||
if(isChargeIdIncluded){
|
||||
final Long chargeId = fromApiJsonHelper.extractLongNamed("chargeId", element);
|
||||
baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero();
|
||||
}
|
||||
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
}
|
||||
+132
-65
@@ -25,12 +25,15 @@ import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityExce
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.account.domain.AccountAssociations;
|
||||
import org.mifosplatform.portfolio.account.domain.AccountAssociationsRepository;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarRepository;
|
||||
import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
|
||||
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
@@ -63,10 +66,13 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductDataValidator;
|
||||
import org.mifosplatform.portfolio.note.domain.Note;
|
||||
import org.mifosplatform.portfolio.note.domain.NoteRepository;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -102,6 +108,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final CalendarRepository calendarRepository;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final SavingsAccountAssembler savingsAccountAssembler;
|
||||
private final AccountAssociationsRepository accountAssociationsRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
|
||||
@@ -114,7 +122,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final LoanProductRepository loanProductRepository, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory,
|
||||
final LoanSummaryWrapper loanSummaryWrapper, final GroupRepositoryWrapper groupRepository,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository) {
|
||||
final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository) {
|
||||
this.context = context;
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
|
||||
@@ -135,6 +144,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.calendarRepository = calendarRepository;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.savingsAccountAssembler = savingsAccountAssembler;
|
||||
this.accountAssociationsRepository = accountAssociationsRepository;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
@@ -147,73 +158,81 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
public CommandProcessingResult submitApplication(final JsonCommand command) {
|
||||
|
||||
try {
|
||||
final AppUser currentUser = context.authenticatedUser();
|
||||
final AppUser currentUser = context.authenticatedUser();
|
||||
|
||||
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
|
||||
final Long productId = fromJsonHelper.extractLongNamed("productId", command.parsedJson());
|
||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator, loanProduct);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
final Long productId = fromJsonHelper.extractLongNamed("productId", command.parsedJson());
|
||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||
|
||||
final Loan newLoanApplication = loanAssembler.assembleFrom(command, currentUser);
|
||||
|
||||
validateSubmittedOnDate(newLoanApplication);
|
||||
|
||||
final LoanProductRelatedDetail productRelatedDetail = newLoanApplication.repaymentScheduleDetail();
|
||||
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(newLoanApplication.getTermFrequency(), newLoanApplication.getTermPeriodFrequencyType(),
|
||||
productRelatedDetail.getNumberOfRepayments(), productRelatedDetail.getRepayEvery(),
|
||||
productRelatedDetail.getRepaymentPeriodFrequencyType().getValue());
|
||||
|
||||
this.loanRepository.save(newLoanApplication);
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator, loanProduct);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
|
||||
final Loan newLoanApplication = loanAssembler.assembleFrom(command, currentUser);
|
||||
|
||||
validateSubmittedOnDate(newLoanApplication);
|
||||
|
||||
final LoanProductRelatedDetail productRelatedDetail = newLoanApplication.repaymentScheduleDetail();
|
||||
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(newLoanApplication.getTermFrequency(),
|
||||
newLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
|
||||
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue());
|
||||
|
||||
if (newLoanApplication.isAccountNumberRequiresAutoGeneration()) {
|
||||
final AccountNumberGenerator accountNoGenerator = this.accountIdentifierGeneratorFactory
|
||||
.determineLoanAccountNoGenerator(newLoanApplication.getId());
|
||||
newLoanApplication.updateAccountNo(accountNoGenerator.generate());
|
||||
this.loanRepository.save(newLoanApplication);
|
||||
}
|
||||
|
||||
final String submittedOnNote = command.stringValueOfParameterNamed("submittedOnNote");
|
||||
if (StringUtils.isNotBlank(submittedOnNote)) {
|
||||
Note note = Note.loanNote(newLoanApplication, submittedOnNote);
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
if (newLoanApplication.isAccountNumberRequiresAutoGeneration()) {
|
||||
final AccountNumberGenerator accountNoGenerator = this.accountIdentifierGeneratorFactory
|
||||
.determineLoanAccountNoGenerator(newLoanApplication.getId());
|
||||
newLoanApplication.updateAccountNo(accountNoGenerator.generate());
|
||||
this.loanRepository.save(newLoanApplication);
|
||||
}
|
||||
|
||||
// Save calendar instance
|
||||
final Long calendarId = command.longValueOfParameterNamed("calendarId");
|
||||
Calendar calendar = null;
|
||||
|
||||
if (calendarId != null && calendarId != 0) {
|
||||
calendar = this.calendarRepository.findOne(calendarId);
|
||||
if (calendar == null) { throw new CalendarNotFoundException(calendarId); }
|
||||
|
||||
CalendarInstance calendarInstance = new CalendarInstance(calendar, newLoanApplication.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
this.calendarInstanceRepository.save(calendarInstance);
|
||||
}
|
||||
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(newLoanApplication.getId()) //
|
||||
.withOfficeId(newLoanApplication.getOfficeId()) //
|
||||
.withClientId(newLoanApplication.getClientId()) //
|
||||
.withGroupId(newLoanApplication.getGroupId()) //
|
||||
.withLoanId(newLoanApplication.getId()) //
|
||||
.build();
|
||||
final String submittedOnNote = command.stringValueOfParameterNamed("submittedOnNote");
|
||||
if (StringUtils.isNotBlank(submittedOnNote)) {
|
||||
Note note = Note.loanNote(newLoanApplication, submittedOnNote);
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
// Save calendar instance
|
||||
final Long calendarId = command.longValueOfParameterNamed("calendarId");
|
||||
Calendar calendar = null;
|
||||
|
||||
if (calendarId != null && calendarId != 0) {
|
||||
calendar = this.calendarRepository.findOne(calendarId);
|
||||
if (calendar == null) { throw new CalendarNotFoundException(calendarId); }
|
||||
|
||||
CalendarInstance calendarInstance = new CalendarInstance(calendar, newLoanApplication.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
this.calendarInstanceRepository.save(calendarInstance);
|
||||
}
|
||||
|
||||
// Save linked account information
|
||||
final Long savingsAccountId = command.longValueOfParameterNamed("linkAccountId");
|
||||
if (savingsAccountId != null) {
|
||||
SavingsAccount savingsAccount = this.savingsAccountAssembler.assembleFrom(savingsAccountId);
|
||||
this.fromApiJsonDeserializer.validatelinkedSavingsAccount(savingsAccount, newLoanApplication);
|
||||
AccountAssociations accountAssociations = AccountAssociations.associateSavingsAccount(newLoanApplication, savingsAccount);
|
||||
this.accountAssociationsRepository.save(accountAssociations);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(newLoanApplication.getId()) //
|
||||
.withOfficeId(newLoanApplication.getOfficeId()) //
|
||||
.withClientId(newLoanApplication.getClientId()) //
|
||||
.withGroupId(newLoanApplication.getGroupId()) //
|
||||
.withLoanId(newLoanApplication.getId()) //
|
||||
.build();
|
||||
} catch (DataIntegrityViolationException dve) {
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult modifyApplication(final Long loanId, final JsonCommand command) {
|
||||
@@ -225,7 +244,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
final Loan existingLoanApplication = retrieveLoanBy(loanId);
|
||||
checkClientOrGroupActive(existingLoanApplication);
|
||||
|
||||
|
||||
if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(
|
||||
loanId); }
|
||||
|
||||
@@ -269,12 +288,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
existingLoanApplication.updateInterestRateFrequencyType();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
validateSubmittedOnDate(existingLoanApplication);
|
||||
final LoanProductRelatedDetail productRelatedDetail = existingLoanApplication.repaymentScheduleDetail();
|
||||
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(), existingLoanApplication.getTermPeriodFrequencyType(),
|
||||
productRelatedDetail.getNumberOfRepayments(), productRelatedDetail.getRepayEvery(),
|
||||
productRelatedDetail.getRepaymentPeriodFrequencyType().getValue());
|
||||
this.fromApiJsonDeserializer.validateLoanTermAndRepaidEveryValues(existingLoanApplication.getTermFrequency(),
|
||||
existingLoanApplication.getTermPeriodFrequencyType(), productRelatedDetail.getNumberOfRepayments(),
|
||||
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue());
|
||||
|
||||
final String fundIdParamName = "fundId";
|
||||
if (changes.containsKey(fundIdParamName)) {
|
||||
@@ -342,7 +361,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
calendar = this.calendarRepository.findOne(calendarId);
|
||||
if (calendar == null) { throw new CalendarNotFoundException(calendarId); }
|
||||
}
|
||||
|
||||
|
||||
List<CalendarInstance> ciList = (List<CalendarInstance>) this.calendarInstanceRepository.findByEntityIdAndEntityTypeId(loanId,
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
if (calendar != null) {
|
||||
@@ -367,6 +386,54 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
this.calendarInstanceRepository.delete(calendarInstance);
|
||||
}
|
||||
|
||||
// Save linked account information
|
||||
final String linkAccountIdParamName = "linkAccountId";
|
||||
final Long savingsAccountId = command.longValueOfParameterNamed(linkAccountIdParamName);
|
||||
AccountAssociations accountAssociations = this.accountAssociationsRepository.findByLoanId(loanId);
|
||||
boolean isLinkedAccPresent = false;
|
||||
if (savingsAccountId == null) {
|
||||
if (accountAssociations != null) {
|
||||
if (this.fromJsonHelper.parameterExists(linkAccountIdParamName, command.parsedJson())) {
|
||||
this.accountAssociationsRepository.delete(accountAssociations);
|
||||
changes.put(linkAccountIdParamName, null);
|
||||
} else {
|
||||
isLinkedAccPresent = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isLinkedAccPresent = true;
|
||||
boolean isModified = false;
|
||||
if (accountAssociations == null) {
|
||||
isModified = true;
|
||||
} else {
|
||||
SavingsAccount savingsAccount = accountAssociations.linkedSavingsAccount();
|
||||
if (savingsAccount == null || savingsAccount.getId() != savingsAccountId) {
|
||||
isModified = true;
|
||||
}
|
||||
}
|
||||
if (isModified) {
|
||||
SavingsAccount savingsAccount = this.savingsAccountAssembler.assembleFrom(savingsAccountId);
|
||||
this.fromApiJsonDeserializer.validatelinkedSavingsAccount(savingsAccount, existingLoanApplication);
|
||||
if (accountAssociations == null) {
|
||||
accountAssociations = AccountAssociations.associateSavingsAccount(existingLoanApplication, savingsAccount);
|
||||
} else {
|
||||
accountAssociations.updateLinkedSavingsAccount(savingsAccount);
|
||||
}
|
||||
changes.put(linkAccountIdParamName, savingsAccountId);
|
||||
this.accountAssociationsRepository.save(accountAssociations);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLinkedAccPresent) {
|
||||
final Set<LoanCharge> charges = existingLoanApplication.charges();
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
if (loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||
final String errorMessage = "one of the charges requires linked savings account for payment";
|
||||
throw new LinkedAccountRequiredException("loanCharge", errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(loanId) //
|
||||
.withOfficeId(existingLoanApplication.getOfficeId()) //
|
||||
@@ -401,8 +468,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
logAsErrorUnexpectedDataIntegrityException(dve);
|
||||
throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue", "Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
|
||||
logger.error(dve.getMessage(), dve);
|
||||
}
|
||||
@@ -572,7 +639,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
loan.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
|
||||
return loan;
|
||||
}
|
||||
|
||||
|
||||
private void validateSubmittedOnDate(final Loan loan) {
|
||||
final LocalDate startDate = loan.loanProduct().getStartDate();
|
||||
final LocalDate closeDate = loan.loanProduct().getCloseDate();
|
||||
@@ -584,7 +651,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
throw new LoanApplicationDateException("submitted.on.date.cannot.be.before.the.loan.product.start.date", defaultUserMessage,
|
||||
submittedOnDate.toString(), startDate.toString());
|
||||
}
|
||||
|
||||
|
||||
if (closeDate != null && submittedOnDate.isAfter(closeDate)) {
|
||||
defaultUserMessage = "submittedOnDate cannot be after the loan product closeDate.";
|
||||
throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.product.close.date", defaultUserMessage,
|
||||
|
||||
+9
@@ -25,6 +25,7 @@ import org.mifosplatform.organisation.staff.exception.StaffRoleException;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
|
||||
@@ -55,6 +56,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
@@ -158,6 +160,13 @@ public class LoanAssembler {
|
||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
|
||||
}
|
||||
if(loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()){
|
||||
final Long savingsAccountId = this.fromApiJsonHelper.extractLongNamed("linkAccountId", element);
|
||||
if(savingsAccountId == null){
|
||||
final String errorMessage = "one of the charges requires linked savings account for payment";
|
||||
throw new LinkedAccountRequiredException("loanCharge", errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loan loanApplication = null;
|
||||
|
||||
+9
-4
@@ -14,6 +14,7 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||
@@ -65,19 +66,23 @@ public class LoanChargeAssembler {
|
||||
locale);
|
||||
final LocalDate dueDate = fromApiJsonHelper.extractLocalDateNamed("dueDate", loanChargeElement,
|
||||
dateFormat, locale);
|
||||
|
||||
final Integer chargePaymentMode = fromApiJsonHelper.extractIntegerNamed("chargePaymentMode", loanChargeElement, locale);
|
||||
if (id == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
ChargeTimeType chargeTime = null;
|
||||
if (chargeTimeType != null) {
|
||||
ChargeTimeType.fromInt(chargeTimeType);
|
||||
chargeTime = ChargeTimeType.fromInt(chargeTimeType);
|
||||
}
|
||||
ChargeCalculationType chargeCalculation = null;
|
||||
if (chargeCalculationType != null) {
|
||||
ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
chargeCalculation = ChargeCalculationType.fromInt(chargeCalculationType);
|
||||
}
|
||||
ChargePaymentMode chargePaymentModeEnum = null;
|
||||
if(chargePaymentMode != null){
|
||||
chargePaymentModeEnum = ChargePaymentMode.fromInt(chargePaymentMode);
|
||||
}
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
|
||||
chargeCalculation, dueDate);
|
||||
chargeCalculation, dueDate, chargePaymentModeEnum);
|
||||
loanCharges.add(loanCharge);
|
||||
} else {
|
||||
final Long loanChargeId = id;
|
||||
|
||||
+2
@@ -17,4 +17,6 @@ public interface LoanChargeReadPlatformService {
|
||||
Collection<LoanChargeData> retrieveLoanCharges(Long loanId);
|
||||
|
||||
LoanChargeData retrieveLoanChargeDetails(Long loanChargeId, Long loanId);
|
||||
|
||||
Collection<LoanChargeData> retrieveLoanChargesForFeePayment(Integer paymentMode, Integer loanStatus);
|
||||
}
|
||||
|
||||
+39
-2
@@ -55,6 +55,9 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
+ "lc.is_penalty as penalty, "
|
||||
+ "lc.due_for_collection_as_of_date as dueAsOfDate, "
|
||||
+ "lc.charge_calculation_enum as chargeCalculation, "
|
||||
+ "lc.charge_payment_mode_enum as chargePaymentMode, "
|
||||
+ "lc.is_paid_derived as paid, "
|
||||
+ "lc.waived as waied, "
|
||||
+ "c.currency_code as currencyCode, oc.name as currencyName, "
|
||||
+ "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode from m_charge c "
|
||||
@@ -95,8 +98,14 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
|
||||
final boolean penalty = rs.getBoolean("penalty");
|
||||
|
||||
final int chargePaymentMode = rs.getInt("chargePaymentMode");
|
||||
final EnumOptionData paymentMode = ChargeEnumerations.chargePaymentMode(chargePaymentMode);
|
||||
final boolean paid = rs.getBoolean("paid");
|
||||
final boolean waived = rs.getBoolean("waied");
|
||||
|
||||
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
|
||||
chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty);
|
||||
chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty, paymentMode,
|
||||
paid, waived, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +116,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
|
||||
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
|
||||
|
||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions);
|
||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,4 +141,32 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanChargeData> retrieveLoanChargesForFeePayment(final Integer paymentMode, final Integer loanStatus) {
|
||||
final LoanChargeMapperWithLoanId rm = new LoanChargeMapperWithLoanId();
|
||||
final String sql = "select " + rm.schema()
|
||||
+ "where loan.loan_status_id= ? and lc.charge_payment_mode_enum=? and lc.waived =0 and lc.is_paid_derived=0";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanStatus, paymentMode });
|
||||
}
|
||||
|
||||
private static final class LoanChargeMapperWithLoanId implements RowMapper<LoanChargeData> {
|
||||
|
||||
public String schema() {
|
||||
return "lc.id as id, lc.due_for_collection_as_of_date as dueAsOfDate, "
|
||||
+ "lc.amount_outstanding_derived as amountOutstanding, "
|
||||
+ "loan.id as loanId " + "from m_loan_charge lc "
|
||||
+ "join m_loan loan on loan.id = lc.loan_id ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
final Long loanId = rs.getLong("loanId");
|
||||
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
|
||||
return new LoanChargeData(id, dueAsOfDate, amountOutstanding, loanId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -11,6 +11,7 @@ import java.util.Map;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.mifosplatform.organisation.office.domain.Office;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
@@ -67,4 +68,9 @@ public interface LoanWritePlatformService {
|
||||
|
||||
LoanTransaction acceptLoanTransfer(Long accountId, LocalDate transferDate, Office acceptedInOffice, Staff loanOfficer);
|
||||
|
||||
CommandProcessingResult payLoanCharge(Long loanId, Long loanChargeId, JsonCommand command, boolean isChargeIdIncludedInJson);
|
||||
|
||||
void transferFeeCharges() throws JobExecutionException;
|
||||
|
||||
|
||||
}
|
||||
+127
-14
@@ -11,18 +11,23 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
|
||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.mifosplatform.infrastructure.jobs.service.JobName;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
@@ -36,6 +41,9 @@ import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
|
||||
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
|
||||
import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
@@ -45,9 +53,12 @@ import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBePayedException.LOAN_CHARGE_CANNOT_BE_PAYED_REASON;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException.LOAN_CHARGE_CANNOT_BE_UPDATED_REASON;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException;
|
||||
@@ -62,6 +73,7 @@ import org.mifosplatform.portfolio.collectionsheet.command.SingleRepaymentComman
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
@@ -73,6 +85,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanDisbursalException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
|
||||
@@ -88,6 +101,7 @@ import org.mifosplatform.portfolio.note.domain.Note;
|
||||
import org.mifosplatform.portfolio.note.domain.NoteRepository;
|
||||
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
||||
import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -118,6 +132,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final LoanProductReadPlatformService loanProductReadPlatformService;
|
||||
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
private final LoanChargeReadPlatformService LoanChargeReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -133,7 +149,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final ConfigurationDomainService configurationDomainService, final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final LoanProductReadPlatformService loanProductReadPlatformService,
|
||||
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
|
||||
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
|
||||
final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
||||
final LoanChargeReadPlatformService loanChargeReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
|
||||
this.loanAssembler = loanAssembler;
|
||||
@@ -155,6 +173,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.loanProductReadPlatformService = loanProductReadPlatformService;
|
||||
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
|
||||
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
|
||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
||||
this.LoanChargeReadPlatformService = loanChargeReadPlatformService;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
@@ -212,7 +232,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
workingDays, allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay);
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
this.loanRepository.save(loan);
|
||||
this.loanRepository.saveAndFlush(loan);
|
||||
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
@@ -223,6 +243,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
|
||||
Set<LoanCharge> loanCharges = loan.charges();
|
||||
final Locale locale = command.extractLocale();
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||
PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, loanCharge.amount(),
|
||||
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loanId,
|
||||
"Loan Charge Payment", locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(),
|
||||
loanCharge.getId());
|
||||
accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
}
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(loan.getId()) //
|
||||
@@ -332,8 +366,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
final Map<String, Object> changes = loan.undoDisbursal(existingTransactionIds, existingReversedTransactionIds);
|
||||
if (!changes.isEmpty()) {
|
||||
this.loanRepository.save(loan);
|
||||
|
||||
this.loanRepository.saveAndFlush(loan);
|
||||
this.accountTransfersWritePlatformService.reverseAllTransactions(loanId, PortfolioAccountType.LOAN);
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
final Note note = Note.loanNote(loan, noteText);
|
||||
@@ -381,11 +415,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
changes.put("note", noteText);
|
||||
}
|
||||
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
|
||||
|
||||
final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder();
|
||||
this.loanAccountDomainService.makeRepayment(loanId, commandProcessingResultBuilder, transactionDate, transactionAmount,
|
||||
this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder, transactionDate, transactionAmount,
|
||||
paymentDetail, noteText, txnExternalId);
|
||||
|
||||
return commandProcessingResultBuilder.withCommandId(command.commandId()) //
|
||||
@@ -409,11 +443,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
* TODO Vishwas, have a re-look at this implementation, defaulting
|
||||
* it to null for now
|
||||
***/
|
||||
final Loan loan = this.loanAssembler.assembleFrom(singleLoanRepaymentCommand.getLoanId());
|
||||
final PaymentDetail paymentDetail = null;
|
||||
final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder();
|
||||
this.loanAccountDomainService.makeRepayment(singleLoanRepaymentCommand.getLoanId(), commandProcessingResultBuilder,
|
||||
bulkRepaymentCommand.getTransactionDate(), singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail,
|
||||
bulkRepaymentCommand.getNote(), null);
|
||||
this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder, bulkRepaymentCommand.getTransactionDate(),
|
||||
singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail, bulkRepaymentCommand.getNote(), null);
|
||||
|
||||
changes.put("bulkTransations", singleLoanRepaymentCommand);
|
||||
}
|
||||
@@ -433,10 +467,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(transactionId);
|
||||
if (transactionToAdjust == null) { throw new LoanTransactionNotFoundException(transactionId); }
|
||||
|
||||
if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)){
|
||||
throw new PlatformServiceUnavailableException("error.msg.loan.transfer.transaction.update.not.allowed","Loan transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId);
|
||||
}
|
||||
|
||||
if (accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)) { throw new PlatformServiceUnavailableException(
|
||||
"error.msg.loan.transfer.transaction.update.not.allowed", "Loan transaction:" + transactionId
|
||||
+ " update not allowed as it involves in account transfer", transactionId); }
|
||||
|
||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||
@@ -505,7 +539,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
this.accountTransfersWritePlatformService.reverseTransfers(loanId, PortfolioAccountType.LOAN);
|
||||
this.accountTransfersWritePlatformService.reverseTransfersWithFromAccountType(loanId, PortfolioAccountType.LOAN);
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
@@ -898,6 +932,85 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult payLoanCharge(final Long loanId, Long loanChargeId, final JsonCommand command,
|
||||
boolean isChargeIdIncludedInJson) {
|
||||
|
||||
this.loanEventApiJsonValidator.validateChargePaymentTransaction(command.json(), isChargeIdIncludedInJson);
|
||||
if (isChargeIdIncludedInJson) {
|
||||
loanChargeId = command.longValueOfParameterNamed("chargeId");
|
||||
}
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
||||
|
||||
// Charges may be waived only when the loan associated with them are
|
||||
// active
|
||||
if (!loan.status().isActive()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.LOAN_INACTIVE,
|
||||
loanCharge.getId()); }
|
||||
|
||||
// validate loan charge is not already paid or waived
|
||||
if (loanCharge.isWaived()) {
|
||||
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||
} else if (loanCharge.isPaid()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID,
|
||||
loanCharge.getId()); }
|
||||
|
||||
if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException(
|
||||
LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_ACCOUNT_TRANSFER, loanCharge.getId()); }
|
||||
|
||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
|
||||
final Locale locale = command.extractLocale();
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
|
||||
PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, loanCharge.amountOutstanding(),
|
||||
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment",
|
||||
locale, fmt, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId);
|
||||
accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(loanChargeId) //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
.withClientId(loan.getClientId()) //
|
||||
.withGroupId(loan.getGroupId()) //
|
||||
.withLoanId(loanId) //
|
||||
.withSavingsId(portfolioAccountData.accountId()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CronTarget(jobName = JobName.TRANSFER_FEE_CHARGE_FOR_LOANS)
|
||||
public void transferFeeCharges() throws JobExecutionException {
|
||||
Collection<LoanChargeData> chargeDatas = this.LoanChargeReadPlatformService.retrieveLoanChargesForFeePayment(
|
||||
ChargePaymentMode.ACCOUNT_TRANSFER.getValue(), LoanStatus.ACTIVE.getValue());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (chargeDatas != null) {
|
||||
for (LoanChargeData chargeData : chargeDatas) {
|
||||
if (chargeData.getDueDate()!=null && !chargeData.getDueDate().isAfter(new LocalDate())) {
|
||||
PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService
|
||||
.retriveLoanAssociation(chargeData.getLoanId());
|
||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(), chargeData.getAmountOutstanding(),
|
||||
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(),
|
||||
chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null,
|
||||
LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId());
|
||||
try {
|
||||
accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
} catch (PlatformApiDataValidationException e) {
|
||||
sb.append("Validation exception while paying charge ").append(chargeData.getId()).append(" for loan id:")
|
||||
.append(chargeData.getLoanId()).append("--------");
|
||||
} catch (InsufficientAccountBalanceException e) {
|
||||
sb.append("InsufficientAccountBalance Exception while paying charge ").append(chargeData.getId())
|
||||
.append("for loan id:").append(chargeData.getLoanId()).append("--------");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
||||
}
|
||||
|
||||
private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) {
|
||||
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
|
||||
if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.mifosplatform.portfolio.loanproduct.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
|
||||
public class LinkedAccountRequiredException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public LinkedAccountRequiredException(final String entity, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg." + entity + ".requires.linked.account", defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
|
||||
}
|
||||
+4
@@ -302,6 +302,10 @@ public class LoanEnumerations {
|
||||
optionData = new LoanTransactionEnumData(LoanTransactionType.REFUND.getValue().longValue(),
|
||||
LoanTransactionType.REFUND.getCode(), "Transfer Refund");
|
||||
break;
|
||||
case CHARGE_PAYMENT:
|
||||
optionData = new LoanTransactionEnumData(LoanTransactionType.CHARGE_PAYMENT.getValue().longValue(),
|
||||
LoanTransactionType.CHARGE_PAYMENT.getCode(), "Charge Payment");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.mifosplatform.portfolio.savings.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
|
||||
public class SavingsAccountClosingNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public SavingsAccountClosingNotAllowedException(final String entity, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg." + entity + ".saving.account.close.notallowed", defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -27,6 +27,6 @@ public class UndoTransactionSavingsAccountCommandHandler implements NewCommandSo
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
final Long transactionId = Long.valueOf(command.getTransactionId());
|
||||
return this.writePlatformService.undoTransaction(command.getSavingsId(), transactionId);
|
||||
return this.writePlatformService.undoTransaction(command.getSavingsId(), transactionId, false);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@ public interface SavingsAccountWritePlatformService {
|
||||
|
||||
CommandProcessingResult postInterest(Long savingsId);
|
||||
|
||||
CommandProcessingResult undoTransaction(Long savingsId, Long transactionId);
|
||||
CommandProcessingResult undoTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification);
|
||||
|
||||
void postInterestForAccounts();
|
||||
|
||||
|
||||
+14
-3
@@ -38,6 +38,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.office.domain.Office;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.account.PortfolioAccountType;
|
||||
import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
|
||||
@@ -57,6 +58,7 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository;
|
||||
import org.mifosplatform.portfolio.savings.exception.SavingsAccountClosingNotAllowedException;
|
||||
import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
|
||||
import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
@@ -78,6 +80,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final SavingsAccountDomainService savingsAccountDomainService;
|
||||
private final NoteRepository noteRepository;
|
||||
private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -89,7 +92,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService,
|
||||
final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository,
|
||||
final AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
|
||||
final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService) {
|
||||
this.context = context;
|
||||
this.savingAccountRepository = savingAccountRepository;
|
||||
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
|
||||
@@ -101,6 +105,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
this.savingsAccountDomainService = savingsAccountDomainService;
|
||||
this.noteRepository = noteRepository;
|
||||
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
|
||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -299,7 +304,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId) {
|
||||
public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId, boolean allowAccountTransferModification) {
|
||||
|
||||
final List<Long> newTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> reversedTransactionIds = new ArrayList<Long>();
|
||||
@@ -308,7 +313,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
transactionId, savingsId);
|
||||
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
|
||||
|
||||
if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)){
|
||||
if(!allowAccountTransferModification && accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)){
|
||||
throw new PlatformServiceUnavailableException("error.msg.saving.account.transfer.transaction.update.not.allowed","Savings account transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId);
|
||||
}
|
||||
|
||||
@@ -451,6 +456,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
this.savingsAccountTransactionDataValidator.validateClosing(command);
|
||||
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
|
||||
final boolean isLinkedWithAnyActiveLoan = accountAssociationsReadPlatformService.isLinkedWithAnyActiveLoan(savingsId);
|
||||
if(isLinkedWithAnyActiveLoan){
|
||||
String defaultUserMessage = "Closing savings account with id:"+savingsId+" is not allowed, since it is linked with one of the active loans";
|
||||
throw new SavingsAccountClosingNotAllowedException("linked", defaultUserMessage, savingsId);
|
||||
}
|
||||
|
||||
final Map<String, Object> changes = account.close(user, command, DateUtils.getLocalDateOfTenant());
|
||||
if (!changes.isEmpty()) {
|
||||
this.savingAccountRepository.save(account);
|
||||
|
||||
Reference in New Issue
Block a user