@@ -0,0 +1,19 @@
|
|||||||
|
CREATE TABLE `m_loan_installment_charge` (
|
||||||
|
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`loan_charge_id` BIGINT(20) NOT NULL,
|
||||||
|
`loan_schedule_id` BIGINT(20) NOT NULL,
|
||||||
|
`due_date` DATE NULL DEFAULT NULL,
|
||||||
|
`amount` DECIMAL(19,6) NOT NULL,
|
||||||
|
`amount_paid_derived` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||||
|
`amount_waived_derived` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||||
|
`amount_writtenoff_derived` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||||
|
`amount_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT '0.000000',
|
||||||
|
`is_paid_derived` TINYINT(1) NOT NULL DEFAULT '0',
|
||||||
|
`waived` TINYINT(1) NOT NULL DEFAULT '0',
|
||||||
|
`amount_through_charge_payment` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
CONSTRAINT `FK_loan_charge_id_charge_schedule` FOREIGN KEY (`loan_charge_id`) REFERENCES `m_loan_charge` (`id`),
|
||||||
|
CONSTRAINT `FK_loan_schedule_id_charge_schedule` FOREIGN KEY (`loan_schedule_id`) REFERENCES `m_loan_repayment_schedule` (`id`)
|
||||||
|
)
|
||||||
|
COLLATE='utf8_general_ci'
|
||||||
|
ENGINE=InnoDB;
|
||||||
+8
-1
@@ -23,11 +23,12 @@ public class AccountTransferDTO {
|
|||||||
private final Integer fromTransferType;
|
private final Integer fromTransferType;
|
||||||
private final Integer toTransferType;
|
private final Integer toTransferType;
|
||||||
private final Long chargeId;
|
private final Long chargeId;
|
||||||
|
private final Integer loanInstallmentNumber;
|
||||||
|
|
||||||
public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount,
|
public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount,
|
||||||
final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId,
|
final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId,
|
||||||
final Long toAccountId, final String description, final Locale locale, final DateTimeFormatter fmt,
|
final Long toAccountId, final String description, final Locale locale, final DateTimeFormatter fmt,
|
||||||
final PaymentDetail paymentDetail, final Integer fromTransferType, final Integer toTransferType, final Long chargeId) {
|
final PaymentDetail paymentDetail, final Integer fromTransferType, final Integer toTransferType, final Long chargeId, Integer loanInstallmentNumber) {
|
||||||
this.transactionDate = transactionDate;
|
this.transactionDate = transactionDate;
|
||||||
this.transactionAmount = transactionAmount;
|
this.transactionAmount = transactionAmount;
|
||||||
this.fromAccountType = fromAccountType;
|
this.fromAccountType = fromAccountType;
|
||||||
@@ -41,6 +42,7 @@ public class AccountTransferDTO {
|
|||||||
this.fromTransferType = fromTransferType;
|
this.fromTransferType = fromTransferType;
|
||||||
this.toTransferType = toTransferType;
|
this.toTransferType = toTransferType;
|
||||||
this.chargeId = chargeId;
|
this.chargeId = chargeId;
|
||||||
|
this.loanInstallmentNumber = loanInstallmentNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getTransactionDate() {
|
public LocalDate getTransactionDate() {
|
||||||
@@ -95,4 +97,9 @@ public class AccountTransferDTO {
|
|||||||
return this.chargeId;
|
return this.chargeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Integer getLoanInstallmentNumber() {
|
||||||
|
return this.loanInstallmentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -229,7 +229,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
|
|
||||||
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeChargePayment(toLoanAccount,
|
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeChargePayment(toLoanAccount,
|
||||||
accountTransferDTO.getChargeId(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
accountTransferDTO.getChargeId(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
|
||||||
accountTransferDTO.getPaymentDetail(), null, null, accountTransferDTO.getToTransferType());
|
accountTransferDTO.getPaymentDetail(), null, null, accountTransferDTO.getToTransferType(), accountTransferDTO.getLoanInstallmentNumber());
|
||||||
|
|
||||||
final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO,
|
final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO,
|
||||||
fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction);
|
fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction);
|
||||||
|
|||||||
+7
-2
@@ -36,6 +36,7 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
|
|||||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
@@ -48,7 +49,7 @@ public class LoanChargesApiResource {
|
|||||||
|
|
||||||
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "chargeId", "name", "penalty",
|
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "chargeId", "name", "penalty",
|
||||||
"chargeTimeType", "dueAsOfDate", "chargeCalculationType", "percentage", "amountPercentageAppliedTo", "currency",
|
"chargeTimeType", "dueAsOfDate", "chargeCalculationType", "percentage", "amountPercentageAppliedTo", "currency",
|
||||||
"amountWaived", "amountWrittenOff", "amountOutstanding", "amountOrPercentage", "amount", "amountPaid", "chargeOptions"));
|
"amountWaived", "amountWrittenOff", "amountOutstanding", "amountOrPercentage", "amount", "amountPaid", "chargeOptions","installmentChargeData"));
|
||||||
|
|
||||||
private final String resourceNameForPermissions = "LOAN";
|
private final String resourceNameForPermissions = "LOAN";
|
||||||
|
|
||||||
@@ -116,9 +117,13 @@ public class LoanChargesApiResource {
|
|||||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||||
|
|
||||||
final LoanChargeData loanCharge = this.loanChargeReadPlatformService.retrieveLoanChargeDetails(loanChargeId, loanId);
|
final LoanChargeData loanCharge = this.loanChargeReadPlatformService.retrieveLoanChargeDetails(loanChargeId, loanId);
|
||||||
|
|
||||||
|
final Collection<LoanInstallmentChargeData> installmentChargeData = this.loanChargeReadPlatformService.retrieveInstallmentLoanCharges(loanChargeId, true);
|
||||||
|
|
||||||
|
final LoanChargeData loanChargeData = new LoanChargeData(loanCharge, installmentChargeData);
|
||||||
|
|
||||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
return this.toApiJsonSerializer.serialize(settings, loanCharge, this.RESPONSE_DATA_PARAMETERS);
|
return this.toApiJsonSerializer.serialize(settings, loanChargeData, this.RESPONSE_DATA_PARAMETERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
|
|||||||
+50
-22
@@ -13,6 +13,7 @@ import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
|||||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||||
|
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable data object for loan charge data.
|
* Immutable data object for loan charge data.
|
||||||
@@ -20,45 +21,32 @@ import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
|||||||
public class LoanChargeData {
|
public class LoanChargeData {
|
||||||
|
|
||||||
private final Long id;
|
private final Long id;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final Long chargeId;
|
private final Long chargeId;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final String name;
|
private final String name;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final EnumOptionData chargeTimeType;
|
private final EnumOptionData chargeTimeType;
|
||||||
|
|
||||||
private final LocalDate dueDate;
|
private final LocalDate dueDate;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final EnumOptionData chargeCalculationType;
|
private final EnumOptionData chargeCalculationType;
|
||||||
|
|
||||||
private final BigDecimal percentage;
|
private final BigDecimal percentage;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal amountPercentageAppliedTo;
|
private final BigDecimal amountPercentageAppliedTo;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final CurrencyData currency;
|
private final CurrencyData currency;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal amount;
|
private final BigDecimal amount;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal amountPaid;
|
private final BigDecimal amountPaid;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal amountWaived;
|
private final BigDecimal amountWaived;
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal amountWrittenOff;
|
private final BigDecimal amountWrittenOff;
|
||||||
|
|
||||||
private final BigDecimal amountOutstanding;
|
private final BigDecimal amountOutstanding;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal amountOrPercentage;
|
private final BigDecimal amountOrPercentage;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final Collection<ChargeData> chargeOptions;
|
private final Collection<ChargeData> chargeOptions;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final boolean penalty;
|
private final boolean penalty;
|
||||||
|
|
||||||
private final EnumOptionData chargePaymentMode;
|
private final EnumOptionData chargePaymentMode;
|
||||||
@@ -67,20 +55,20 @@ public class LoanChargeData {
|
|||||||
|
|
||||||
private final boolean waived;
|
private final boolean waived;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final boolean chargePayable;
|
private final boolean chargePayable;
|
||||||
|
|
||||||
private final Long loanId;
|
private final Long loanId;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal minCap;
|
private final BigDecimal minCap;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final BigDecimal maxCap;
|
private final BigDecimal maxCap;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Collection<LoanInstallmentChargeData> installmentChargeData;
|
||||||
|
|
||||||
public static LoanChargeData template(final Collection<ChargeData> chargeOptions) {
|
public static LoanChargeData template(final Collection<ChargeData> chargeOptions) {
|
||||||
return new LoanChargeData(null, null, null, null, null, null, null, null, chargeOptions, false, null, false, false, null, null,
|
return new LoanChargeData(null, null, null, null, null, null, null, null, chargeOptions, false, null, false, false, null, null,
|
||||||
null,null);
|
null,null,null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,7 +80,7 @@ public class LoanChargeData {
|
|||||||
final EnumOptionData chargeCalculationType, final boolean penalty, final EnumOptionData chargePaymentMode,
|
final EnumOptionData chargeCalculationType, final boolean penalty, final EnumOptionData chargePaymentMode,
|
||||||
final BigDecimal minCap, final BigDecimal maxCap) {
|
final BigDecimal minCap, final BigDecimal maxCap) {
|
||||||
return new LoanChargeData(null, chargeId, name, currency, amount, percentage, chargeTimeType, chargeCalculationType, null, penalty,
|
return new LoanChargeData(null, chargeId, name, currency, amount, percentage, chargeTimeType, chargeCalculationType, null, penalty,
|
||||||
chargePaymentMode, false, false, null, minCap, maxCap,null);
|
chargePaymentMode, false, false, null, minCap, maxCap,null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount,
|
public LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount,
|
||||||
@@ -100,7 +88,7 @@ public class LoanChargeData {
|
|||||||
final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueDate,
|
final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueDate,
|
||||||
final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo,
|
final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo,
|
||||||
final boolean penalty, final EnumOptionData chargePaymentMode, final boolean paid, final boolean waived, final Long loanId,
|
final boolean penalty, final EnumOptionData chargePaymentMode, final boolean paid, final boolean waived, final Long loanId,
|
||||||
final BigDecimal minCap, final BigDecimal maxCap,final BigDecimal amountOrPercentage) {
|
final BigDecimal minCap, final BigDecimal maxCap,final BigDecimal amountOrPercentage, Collection<LoanInstallmentChargeData> installmentChargeData) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.chargeId = chargeId;
|
this.chargeId = chargeId;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@@ -134,12 +122,13 @@ public class LoanChargeData {
|
|||||||
this.chargeOptions = null;
|
this.chargeOptions = null;
|
||||||
this.chargePayable = isChargePayable();
|
this.chargePayable = isChargePayable();
|
||||||
this.loanId = loanId;
|
this.loanId = loanId;
|
||||||
|
this.installmentChargeData = installmentChargeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount,
|
private LoanChargeData(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount,
|
||||||
final BigDecimal percentage, final EnumOptionData chargeTimeType, final EnumOptionData chargeCalculationType,
|
final BigDecimal percentage, final EnumOptionData chargeTimeType, final EnumOptionData chargeCalculationType,
|
||||||
final Collection<ChargeData> chargeOptions, final boolean penalty, final EnumOptionData chargePaymentMode, final boolean paid,
|
final Collection<ChargeData> chargeOptions, final boolean penalty, final EnumOptionData chargePaymentMode, final boolean paid,
|
||||||
final boolean waived, final Long loanId, final BigDecimal minCap, final BigDecimal maxCap,final BigDecimal amountOrPercentage) {
|
final boolean waived, final Long loanId, final BigDecimal minCap, final BigDecimal maxCap,final BigDecimal amountOrPercentage, Collection<LoanInstallmentChargeData> installmentChargeData) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.chargeId = chargeId;
|
this.chargeId = chargeId;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@@ -174,9 +163,10 @@ public class LoanChargeData {
|
|||||||
this.loanId = loanId;
|
this.loanId = loanId;
|
||||||
this.minCap = minCap;
|
this.minCap = minCap;
|
||||||
this.maxCap = maxCap;
|
this.maxCap = maxCap;
|
||||||
|
this.installmentChargeData = installmentChargeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOutstanding, final Long loanId) {
|
public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOutstanding, EnumOptionData chargeTimeType, final Long loanId, Collection<LoanInstallmentChargeData> installmentChargeData) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.chargeId = null;
|
this.chargeId = null;
|
||||||
this.name = null;
|
this.name = null;
|
||||||
@@ -186,7 +176,7 @@ public class LoanChargeData {
|
|||||||
this.amountWaived = null;
|
this.amountWaived = null;
|
||||||
this.amountWrittenOff = null;
|
this.amountWrittenOff = null;
|
||||||
this.amountOutstanding = amountOutstanding;
|
this.amountOutstanding = amountOutstanding;
|
||||||
this.chargeTimeType = null;
|
this.chargeTimeType = chargeTimeType;
|
||||||
this.dueDate = dueAsOfDate;
|
this.dueDate = dueAsOfDate;
|
||||||
this.chargeCalculationType = null;
|
this.chargeCalculationType = null;
|
||||||
this.percentage = null;
|
this.percentage = null;
|
||||||
@@ -201,7 +191,37 @@ public class LoanChargeData {
|
|||||||
this.loanId = loanId;
|
this.loanId = loanId;
|
||||||
this.minCap = null;
|
this.minCap = null;
|
||||||
this.maxCap = null;
|
this.maxCap = null;
|
||||||
|
this.installmentChargeData = installmentChargeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanChargeData(LoanChargeData chargeData, Collection<LoanInstallmentChargeData> installmentChargeData) {
|
||||||
|
this.id = chargeData.id;
|
||||||
|
this.chargeId = chargeData.chargeId;
|
||||||
|
this.name = chargeData.name;
|
||||||
|
this.currency = chargeData.currency;
|
||||||
|
this.amount = chargeData.amount;
|
||||||
|
this.amountPaid = chargeData.amountPaid;
|
||||||
|
this.amountWaived = chargeData.amountWaived;
|
||||||
|
this.amountWrittenOff = chargeData.amountWrittenOff;
|
||||||
|
this.amountOutstanding = chargeData.amountOutstanding;
|
||||||
|
this.chargeTimeType = chargeData.chargeTimeType;
|
||||||
|
this.dueDate = chargeData.dueDate;
|
||||||
|
this.chargeCalculationType = chargeData.chargeCalculationType;
|
||||||
|
this.percentage = chargeData.percentage;
|
||||||
|
this.amountPercentageAppliedTo = chargeData.amountPercentageAppliedTo;
|
||||||
|
this.penalty = chargeData.penalty;
|
||||||
|
this.chargePaymentMode = chargeData.chargePaymentMode;
|
||||||
|
this.paid = chargeData.paid;
|
||||||
|
this.waived = chargeData.waived;
|
||||||
|
this.minCap = chargeData.minCap;
|
||||||
|
this.maxCap = chargeData.maxCap;
|
||||||
|
this.amountOrPercentage = chargeData.amountOrPercentage;
|
||||||
|
this.chargeOptions = chargeData.chargeOptions;
|
||||||
|
this.chargePayable = chargeData.chargePayable;
|
||||||
|
this.loanId = chargeData.loanId;
|
||||||
|
this.installmentChargeData = installmentChargeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isChargePayable() {
|
public boolean isChargePayable() {
|
||||||
boolean isAccountTransfer = false;
|
boolean isAccountTransfer = false;
|
||||||
@@ -226,4 +246,12 @@ public class LoanChargeData {
|
|||||||
public BigDecimal getAmountOutstanding() {
|
public BigDecimal getAmountOutstanding() {
|
||||||
return this.amountOutstanding;
|
return this.amountOutstanding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInstallmentFee() {
|
||||||
|
boolean isInstalmentFee = false;
|
||||||
|
if (this.chargeTimeType != null) {
|
||||||
|
isInstalmentFee = ChargeTimeType.fromInt(this.chargeTimeType.getId().intValue()).isInstalmentFee();
|
||||||
|
}
|
||||||
|
return isInstalmentFee;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package org.mifosplatform.portfolio.loanaccount.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
|
||||||
|
public class LoanInstallmentChargeData {
|
||||||
|
|
||||||
|
private final Integer installmentNumber;
|
||||||
|
private final LocalDate dueDate;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final BigDecimal amount;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final BigDecimal amountOutstanding;
|
||||||
|
private final boolean paid;
|
||||||
|
private final boolean waived;
|
||||||
|
|
||||||
|
public LoanInstallmentChargeData(final Integer installmentNumber, final LocalDate dueDate, final BigDecimal amount,
|
||||||
|
final BigDecimal amountOutstanding, final boolean paid, final boolean waived) {
|
||||||
|
this.installmentNumber = installmentNumber;
|
||||||
|
this.dueDate = dueDate;
|
||||||
|
this.amount = amount;
|
||||||
|
this.amountOutstanding = amountOutstanding;
|
||||||
|
this.paid = paid;
|
||||||
|
this.waived = waived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInstallmentNumber() {
|
||||||
|
return this.installmentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getDueDate() {
|
||||||
|
return this.dueDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPaymentPending(){
|
||||||
|
return !(paid || waived);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getAmountOutstanding() {
|
||||||
|
return this.amountOutstanding;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+67
-22
@@ -520,7 +520,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment,
|
private void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, Integer installmentNumber) {
|
||||||
chargesPayment.updateLoan(this);
|
chargesPayment.updateLoan(this);
|
||||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency())
|
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency())
|
||||||
.getAmount());
|
.getAmount());
|
||||||
@@ -535,7 +535,10 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
final List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<LoanRepaymentScheduleInstallment>();
|
final List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<LoanRepaymentScheduleInstallment>();
|
||||||
LocalDate startDate = getDisbursementDate();
|
LocalDate startDate = getDisbursementDate();
|
||||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
if (charge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())) {
|
if (installmentNumber == null && charge.isDueForCollectionFromAndUpToAndIncluding(startDate, installment.getDueDate())) {
|
||||||
|
chargePaymentInstallments.add(installment);
|
||||||
|
break;
|
||||||
|
}else if(installmentNumber != null && installment.getInstallmentNumber().equals(installmentNumber)){
|
||||||
chargePaymentInstallments.add(installment);
|
chargePaymentInstallments.add(installment);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -708,31 +711,45 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public BigDecimal calculatePerInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage) {
|
public BigDecimal calculatePerInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage) {
|
||||||
Money amount = Money.zero(getCurrency());
|
Money amount = Money.zero(getCurrency());
|
||||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
Money percentOf = Money.zero(getCurrency());
|
amount = amount.plus(calculateInstallmentChargeAmount(calculationType, percentage, installment));
|
||||||
switch (calculationType) {
|
|
||||||
case PERCENT_OF_AMOUNT:
|
|
||||||
percentOf = installment.getPrincipal(getCurrency());
|
|
||||||
break;
|
|
||||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
|
||||||
percentOf = installment.getPrincipal(getCurrency()).plus(installment.getInterestCharged(getCurrency()));
|
|
||||||
break;
|
|
||||||
case PERCENT_OF_INTEREST:
|
|
||||||
percentOf = installment.getInterestCharged(getCurrency());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
amount = amount.plus(LoanCharge.percentageOf(percentOf.getAmount(), percentage));
|
|
||||||
}
|
}
|
||||||
return amount.getAmount();
|
return amount.getAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param calculationType
|
||||||
|
* @param percentage
|
||||||
|
* @param installment
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Money calculateInstallmentChargeAmount(final ChargeCalculationType calculationType, final BigDecimal percentage,
|
||||||
|
final LoanRepaymentScheduleInstallment installment) {
|
||||||
|
Money amount = Money.zero(getCurrency());
|
||||||
|
Money percentOf = Money.zero(getCurrency());
|
||||||
|
switch (calculationType) {
|
||||||
|
case PERCENT_OF_AMOUNT:
|
||||||
|
percentOf = installment.getPrincipal(getCurrency());
|
||||||
|
break;
|
||||||
|
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||||
|
percentOf = installment.getPrincipal(getCurrency()).plus(installment.getInterestCharged(getCurrency()));
|
||||||
|
break;
|
||||||
|
case PERCENT_OF_INTEREST:
|
||||||
|
percentOf = installment.getInterestCharged(getCurrency());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
amount = amount.plus(LoanCharge.percentageOf(percentOf.getAmount(), percentage));
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
|
Integer loanInstallmentNumber) {
|
||||||
|
|
||||||
validateLoanIsNotClosed(loanCharge);
|
validateLoanIsNotClosed(loanCharge);
|
||||||
|
|
||||||
final Money amountWaived = loanCharge.waive(loanCurrency());
|
final Money amountWaived = loanCharge.waive(loanCurrency(), loanInstallmentNumber);
|
||||||
|
|
||||||
changes.put("amount", amountWaived.getAmount());
|
changes.put("amount", amountWaived.getAmount());
|
||||||
|
|
||||||
@@ -868,8 +885,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
|
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
|
||||||
|
clearInstallmentLoanCharge();
|
||||||
this.repaymentScheduleInstallments.clear();
|
this.repaymentScheduleInstallments.clear();
|
||||||
|
|
||||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
||||||
|
|
||||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||||
@@ -884,6 +902,15 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
updateLoanScheduleDependentDerivedFields();
|
updateLoanScheduleDependentDerivedFields();
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearInstallmentLoanCharge(){
|
||||||
|
for(LoanCharge loanCharge:this.charges){
|
||||||
|
if(loanCharge.isInstalmentFee()){
|
||||||
|
loanCharge.clearLoanInstallmentCharges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLoanScheduleDependentDerivedFields() {
|
private void updateLoanScheduleDependentDerivedFields() {
|
||||||
@@ -1723,7 +1750,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||||
final boolean allowTransactionsOnNonWorkingDay, final LoanTransaction paymentTransaction) {
|
final boolean allowTransactionsOnNonWorkingDay, final LoanTransaction paymentTransaction, Integer installmentNumber) {
|
||||||
|
|
||||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
||||||
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
||||||
@@ -1742,7 +1769,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
charge = loanCharge;
|
charge = loanCharge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleChargePaidTransaction(charge, paymentTransaction, loanLifecycleStateMachine);
|
handleChargePaidTransaction(charge, paymentTransaction, loanLifecycleStateMachine, installmentNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeRefund(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public void makeRefund(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
@@ -2961,4 +2988,22 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public Set<LoanCharge> charges() {
|
public Set<LoanCharge> charges() {
|
||||||
return this.charges;
|
return this.charges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<LoanInstallmentCharge> generateInstallmentLoanCharges(final LoanCharge loanCharge){
|
||||||
|
Set<LoanInstallmentCharge> loanChargePerInstallments = new HashSet<LoanInstallmentCharge>();
|
||||||
|
if(loanCharge.isInstalmentFee()){
|
||||||
|
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
|
if(loanCharge.getChargeCalculation().isFlat()){
|
||||||
|
amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||||
|
}else{
|
||||||
|
amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(), installment).getAmount();
|
||||||
|
}
|
||||||
|
LoanInstallmentCharge loanInstallmentCharge = new LoanInstallmentCharge(amount, loanCharge, installment);
|
||||||
|
loanChargePerInstallments.add(loanInstallmentCharge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loanChargePerInstallments;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -22,5 +22,5 @@ public interface LoanAccountDomainService {
|
|||||||
void reverseTransfer(LoanTransaction loanTransaction);
|
void reverseTransfer(LoanTransaction loanTransaction);
|
||||||
|
|
||||||
LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount,
|
LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount,
|
||||||
PaymentDetail paymentDetail, String noteText, String txnExternalId, Integer transactionType);
|
PaymentDetail paymentDetail, String noteText, String txnExternalId, Integer transactionType, Integer installmentNumber);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -163,7 +163,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public LoanTransaction makeChargePayment(final Loan loan, final Long chargeId, final LocalDate transactionDate,
|
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 BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, final String txnExternalId,
|
||||||
final Integer transactionType) {
|
final Integer transactionType, Integer installmentNumber) {
|
||||||
|
|
||||||
checkClientOrGroupActive(loan);
|
checkClientOrGroupActive(loan);
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
|
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||||
} else {
|
} else {
|
||||||
loan.makeChargePayment(chargeId, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
|
loan.makeChargePayment(chargeId, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
|
||||||
allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay, newPaymentTransaction);
|
allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay, newPaymentTransaction, installmentNumber);
|
||||||
}
|
}
|
||||||
this.loanTransactionRepository.save(newPaymentTransaction);
|
this.loanTransactionRepository.save(newPaymentTransaction);
|
||||||
this.loanRepository.saveAndFlush(loan);
|
this.loanRepository.saveAndFlush(loan);
|
||||||
|
|||||||
+145
-16
@@ -8,20 +8,27 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||||
|
import org.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
@@ -65,6 +72,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal amountPercentageAppliedTo;
|
private BigDecimal amountPercentageAppliedTo;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
@Column(name = "charge_amount_or_percentage", scale = 6, precision = 19, nullable = false)
|
@Column(name = "charge_amount_or_percentage", scale = 6, precision = 19, nullable = false)
|
||||||
private BigDecimal amountOrPercentage;
|
private BigDecimal amountOrPercentage;
|
||||||
|
|
||||||
@@ -97,6 +105,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
@Column(name = "max_cap", scale = 6, precision = 19, nullable = true)
|
@Column(name = "max_cap", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal maxCap;
|
private BigDecimal maxCap;
|
||||||
|
|
||||||
|
@LazyCollection(LazyCollectionOption.FALSE)
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loancharge", orphanRemoval = true)
|
||||||
|
private Set<LoanInstallmentCharge> loanInstallmentCharge = new HashSet<LoanInstallmentCharge>();
|
||||||
|
|
||||||
public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) {
|
public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) {
|
||||||
|
|
||||||
@@ -236,6 +248,19 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.amountOrPercentage = chargeAmount;
|
this.amountOrPercentage = chargeAmount;
|
||||||
|
if(this.loan!=null && this.isInstalmentFee()){
|
||||||
|
Set<LoanInstallmentCharge> chargePerInstallments = this.loan.generateInstallmentLoanCharges(this);
|
||||||
|
if(this.loanInstallmentCharge.isEmpty()){
|
||||||
|
this.loanInstallmentCharge.addAll(chargePerInstallments);
|
||||||
|
}else{
|
||||||
|
int index = 0;
|
||||||
|
LoanInstallmentCharge[] loanChargePerInstallments = new LoanInstallmentCharge[chargePerInstallments.size()];
|
||||||
|
LoanInstallmentCharge[] loanChargePerInstallmentArray = chargePerInstallments.toArray(loanChargePerInstallments);
|
||||||
|
for(LoanInstallmentCharge chargePerInstallment:this.loanInstallmentCharge){
|
||||||
|
chargePerInstallment.copyFrom(loanChargePerInstallmentArray[index++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markAsFullyPaid() {
|
public void markAsFullyPaid() {
|
||||||
@@ -251,20 +276,41 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
this.amountOutstanding = calculateAmountOutstanding(currency);
|
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||||
this.paid = false;
|
this.paid = false;
|
||||||
this.waived = false;
|
this.waived = false;
|
||||||
|
for(LoanInstallmentCharge installmentCharge:loanInstallmentCharge){
|
||||||
|
installmentCharge.resetPaidAmount(currency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetPaidAmount(final MonetaryCurrency currency) {
|
public void resetPaidAmount(final MonetaryCurrency currency) {
|
||||||
this.amountPaid = BigDecimal.ZERO;
|
this.amountPaid = BigDecimal.ZERO;
|
||||||
this.amountOutstanding = calculateAmountOutstanding(currency);
|
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||||
this.paid = false;
|
this.paid = false;
|
||||||
|
for(LoanInstallmentCharge installmentCharge:loanInstallmentCharge){
|
||||||
|
installmentCharge.resetPaidAmount(currency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money waive(final MonetaryCurrency currency) {
|
public Money waive(final MonetaryCurrency currency, Integer loanInstallmentNumber) {
|
||||||
|
if(isInstalmentFee()){
|
||||||
|
LoanInstallmentCharge chargePerInstallment = getInstallmentLoanCharge(loanInstallmentNumber);
|
||||||
|
Money amountWaived = chargePerInstallment.waive(currency);
|
||||||
|
if(this.amountWaived == null){
|
||||||
|
this.amountWaived = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
this.amountWaived = this.amountWaived.add(amountWaived.getAmount());
|
||||||
|
this.amountOutstanding = this.amountOutstanding.subtract(amountWaived.getAmount());
|
||||||
|
if(determineIfFullyPaid()){
|
||||||
|
this.paid = false;
|
||||||
|
this.waived = true;
|
||||||
|
}
|
||||||
|
return amountWaived;
|
||||||
|
}
|
||||||
this.amountWaived = this.amountOutstanding;
|
this.amountWaived = this.amountOutstanding;
|
||||||
this.amountOutstanding = BigDecimal.ZERO;
|
this.amountOutstanding = BigDecimal.ZERO;
|
||||||
this.paid = false;
|
this.paid = false;
|
||||||
this.waived = true;
|
this.waived = true;
|
||||||
return getAmountWaived(currency);
|
return getAmountWaived(currency);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
|
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
|
||||||
@@ -307,6 +353,19 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.amountOrPercentage = amount;
|
this.amountOrPercentage = amount;
|
||||||
|
if(this.loan !=null && this.isInstalmentFee()){
|
||||||
|
Set<LoanInstallmentCharge> chargePerInstallments = this.loan.generateInstallmentLoanCharges(this);
|
||||||
|
if(this.loanInstallmentCharge.isEmpty()){
|
||||||
|
this.loanInstallmentCharge.addAll(chargePerInstallments);
|
||||||
|
}else{
|
||||||
|
int index = 0;
|
||||||
|
LoanInstallmentCharge[] loanChargePerInstallments = new LoanInstallmentCharge[chargePerInstallments.size()];
|
||||||
|
LoanInstallmentCharge[] loanChargePerInstallmentArray = chargePerInstallments.toArray(loanChargePerInstallments);
|
||||||
|
for(LoanInstallmentCharge chargePerInstallment:this.loanInstallmentCharge){
|
||||||
|
chargePerInstallment.copyFrom(loanChargePerInstallmentArray[index++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,6 +416,19 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.amountOrPercentage = newValue;
|
this.amountOrPercentage = newValue;
|
||||||
|
if(this.isInstalmentFee()){
|
||||||
|
Set<LoanInstallmentCharge> chargePerInstallments = this.loan.generateInstallmentLoanCharges(this);
|
||||||
|
if(this.loanInstallmentCharge.isEmpty()){
|
||||||
|
this.loanInstallmentCharge.addAll(chargePerInstallments);
|
||||||
|
}else{
|
||||||
|
int index = 0;
|
||||||
|
LoanInstallmentCharge[] loanChargePerInstallments = new LoanInstallmentCharge[chargePerInstallments.size()];
|
||||||
|
LoanInstallmentCharge[] loanChargePerInstallmentArray = chargePerInstallments.toArray(loanChargePerInstallments);
|
||||||
|
for(LoanInstallmentCharge chargePerInstallment:this.loanInstallmentCharge){
|
||||||
|
chargePerInstallment.copyFrom(loanChargePerInstallmentArray[index++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return actualChanges;
|
return actualChanges;
|
||||||
}
|
}
|
||||||
@@ -538,28 +610,36 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param incrementBy
|
* @param feeAmount TODO
|
||||||
|
* @param processAmount
|
||||||
* Amount used to pay off this charge
|
* Amount used to pay off this charge
|
||||||
* @return Actual amount paid on this charge
|
* @return Actual amount paid on this charge
|
||||||
*/
|
*/
|
||||||
public Money updatePaidAmountBy(final Money incrementBy) {
|
public Money updatePaidAmountBy(final Money incrementBy,final Integer installmentNumber, Money feeAmount) {
|
||||||
|
Money processAmount = Money.zero(incrementBy.getCurrency());
|
||||||
|
if(this.isInstalmentFee()){
|
||||||
|
if(installmentNumber == null){
|
||||||
|
processAmount = getUnpaidInstallmentLoanCharge().updatePaidAmountBy(incrementBy, feeAmount);
|
||||||
|
}else{
|
||||||
|
processAmount = getInstallmentLoanCharge(installmentNumber).updatePaidAmountBy(incrementBy, feeAmount);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
processAmount = incrementBy;
|
||||||
|
}
|
||||||
|
Money amountPaidToDate = Money.of(processAmount.getCurrency(), this.amountPaid);
|
||||||
|
final Money amountOutstanding = Money.of(processAmount.getCurrency(), this.amountOutstanding);
|
||||||
|
|
||||||
Money amountPaidToDate = Money.of(incrementBy.getCurrency(), this.amountPaid);
|
Money amountPaidOnThisCharge = Money.zero(processAmount.getCurrency());
|
||||||
final Money amountOutstanding = Money.of(incrementBy.getCurrency(), this.amountOutstanding);
|
if (processAmount.isGreaterThanOrEqualTo(amountOutstanding)) {
|
||||||
|
|
||||||
Money amountPaidOnThisCharge = Money.zero(incrementBy.getCurrency());
|
|
||||||
if (incrementBy.isGreaterThanOrEqualTo(amountOutstanding)) {
|
|
||||||
amountPaidOnThisCharge = amountOutstanding;
|
amountPaidOnThisCharge = amountOutstanding;
|
||||||
amountPaidToDate = amountPaidToDate.plus(amountOutstanding);
|
amountPaidToDate = amountPaidToDate.plus(amountOutstanding);
|
||||||
this.amountPaid = amountPaidToDate.getAmount();
|
this.amountPaid = amountPaidToDate.getAmount();
|
||||||
this.amountOutstanding = BigDecimal.ZERO;
|
this.amountOutstanding = BigDecimal.ZERO;
|
||||||
} else {
|
} else {
|
||||||
amountPaidOnThisCharge = incrementBy;
|
amountPaidOnThisCharge = processAmount;
|
||||||
amountPaidToDate = amountPaidToDate.plus(incrementBy);
|
amountPaidToDate = amountPaidToDate.plus(processAmount);
|
||||||
this.amountPaid = amountPaidToDate.getAmount();
|
this.amountPaid = amountPaidToDate.getAmount();
|
||||||
|
this.amountOutstanding = calculateAmountOutstanding(incrementBy.getCurrency());
|
||||||
final Money amountExpected = Money.of(incrementBy.getCurrency(), this.amount);
|
|
||||||
this.amountOutstanding = amountExpected.minus(amountPaidToDate).getAmount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.paid = determineIfFullyPaid();
|
this.paid = determineIfFullyPaid();
|
||||||
@@ -606,19 +686,68 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
return ChargePaymentMode.fromInt(this.chargePaymentMode);
|
return ChargePaymentMode.fromInt(this.chargePaymentMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ChargeCalculationType getChargeCalculation() {
|
public ChargeCalculationType getChargeCalculation() {
|
||||||
return ChargeCalculationType.fromInt(this.chargeCalculation);
|
return ChargeCalculationType.fromInt(this.chargeCalculation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public BigDecimal getPercentage() {
|
public BigDecimal getPercentage() {
|
||||||
return this.percentage;
|
return this.percentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void updateAmount(final BigDecimal amount) {
|
public void updateAmount(final BigDecimal amount) {
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
calculateOutstanding();
|
calculateOutstanding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanInstallmentCharge getUnpaidInstallmentLoanCharge() {
|
||||||
|
LoanInstallmentCharge unpaidChargePerInstallment = null;
|
||||||
|
for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
|
||||||
|
if (loanChargePerInstallment.isPending()
|
||||||
|
&& (unpaidChargePerInstallment == null || unpaidChargePerInstallment.getRepaymentInstallment().getDueDate()
|
||||||
|
.isAfter(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
|
||||||
|
unpaidChargePerInstallment = loanChargePerInstallment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unpaidChargePerInstallment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanInstallmentCharge getInstallmentLoanCharge(LocalDate periodDueDate) {
|
||||||
|
for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
|
||||||
|
if (periodDueDate.isEqual(loanChargePerInstallment.getRepaymentInstallment().getDueDate())) {
|
||||||
|
return loanChargePerInstallment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanInstallmentCharge getInstallmentLoanCharge(Integer installmentNumber) {
|
||||||
|
for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
|
||||||
|
if (installmentNumber.equals(loanChargePerInstallment.getRepaymentInstallment().getInstallmentNumber().intValue()) ){
|
||||||
|
return loanChargePerInstallment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearLoanInstallmentCharges(){
|
||||||
|
this.loanInstallmentCharge.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLoanInstallmentCharges(Collection<LoanInstallmentCharge> installmentCharges){
|
||||||
|
this.loanInstallmentCharge.addAll(installmentCharges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNoLoanInstallmentCharges(){
|
||||||
|
return this.loanInstallmentCharge.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanRepaymentScheduleInstallment fetchRepaymentInstallment(Money trasferedAmount){
|
||||||
|
for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
|
||||||
|
if (loanChargePerInstallment.isPending() &&
|
||||||
|
trasferedAmount.getAmount().equals(loanChargePerInstallment.getAmountThroughChargePayment(trasferedAmount.getCurrency()).getAmount()) ){
|
||||||
|
return loanChargePerInstallment.getRepaymentInstallment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+211
@@ -0,0 +1,211 @@
|
|||||||
|
/**
|
||||||
|
* 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.loanaccount.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
|
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "m_loan_installment_charge")
|
||||||
|
public class LoanInstallmentCharge extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "loan_charge_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private LoanCharge loancharge;
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "loan_schedule_id", referencedColumnName = "id", nullable = false)
|
||||||
|
private LoanRepaymentScheduleInstallment installment;
|
||||||
|
|
||||||
|
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
@Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true)
|
||||||
|
private BigDecimal amountPaid;
|
||||||
|
|
||||||
|
@Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||||
|
private BigDecimal amountWaived;
|
||||||
|
|
||||||
|
@Column(name = "amount_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||||
|
private BigDecimal amountWrittenOff;
|
||||||
|
|
||||||
|
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
|
||||||
|
private BigDecimal amountOutstanding;
|
||||||
|
|
||||||
|
@Column(name = "amount_through_charge_payment", scale = 6, precision = 19, nullable = true)
|
||||||
|
private BigDecimal amountThroughChargePayment;
|
||||||
|
|
||||||
|
@Column(name = "is_paid_derived", nullable = false)
|
||||||
|
private boolean paid = false;
|
||||||
|
|
||||||
|
@Column(name = "waived", nullable = false)
|
||||||
|
private boolean waived = false;
|
||||||
|
|
||||||
|
|
||||||
|
public LoanInstallmentCharge() {
|
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanInstallmentCharge(BigDecimal amount,LoanCharge loanCharge,LoanRepaymentScheduleInstallment installment) {
|
||||||
|
this.loancharge = loanCharge;
|
||||||
|
this.installment = installment;
|
||||||
|
this.amount = amount;
|
||||||
|
this.amountOutstanding = amount;
|
||||||
|
this.amountPaid = null;
|
||||||
|
this.amountWaived = null;
|
||||||
|
this.amountWrittenOff = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyFrom(LoanInstallmentCharge loanChargePerInstallment){
|
||||||
|
this.amount = loanChargePerInstallment.amount;
|
||||||
|
this.installment = loanChargePerInstallment.installment;
|
||||||
|
this.amountOutstanding = calculateOutstanding();
|
||||||
|
this.paid = determineIfFullyPaid();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Money waive(final MonetaryCurrency currency) {
|
||||||
|
this.amountWaived = this.amountOutstanding;
|
||||||
|
this.amountOutstanding = BigDecimal.ZERO;
|
||||||
|
this.paid = false;
|
||||||
|
this.waived = true;
|
||||||
|
return getAmountWaived(currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Money getAmountWaived(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.amountWaived);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean determineIfFullyPaid() {
|
||||||
|
if(this.amount == null){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculateOutstanding() {
|
||||||
|
if(this.amount == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BigDecimal amountPaidLocal = BigDecimal.ZERO;
|
||||||
|
if (this.amountPaid != null) {
|
||||||
|
amountPaidLocal = this.amountPaid;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal amountWaivedLocal = BigDecimal.ZERO;
|
||||||
|
if (this.amountWaived != null) {
|
||||||
|
amountWaivedLocal = this.amountWaived;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal amountWrittenOffLocal = BigDecimal.ZERO;
|
||||||
|
if (this.amountWrittenOff != null) {
|
||||||
|
amountWrittenOffLocal = this.amountWrittenOff;
|
||||||
|
}
|
||||||
|
|
||||||
|
final BigDecimal totalAccountedFor = amountPaidLocal.add(amountWaivedLocal).add(amountWrittenOffLocal);
|
||||||
|
|
||||||
|
return this.amount.subtract(totalAccountedFor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getAmount() {
|
||||||
|
return this.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money getAmount(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Money getAmountPaid(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.amountPaid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getAmountOutstanding() {
|
||||||
|
return this.amountOutstanding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
|
||||||
|
return getAmount(currency).minus(getAmountWaived(currency)).minus(getAmountPaid(currency)).getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isPaid() {
|
||||||
|
return this.paid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWaived() {
|
||||||
|
return this.waived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPending(){
|
||||||
|
return !(isPaid() || isWaived());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LoanRepaymentScheduleInstallment getRepaymentInstallment() {
|
||||||
|
return this.installment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money updatePaidAmountBy(final Money incrementBy, Money feeAmount) {
|
||||||
|
|
||||||
|
Money amountPaidToDate = Money.of(incrementBy.getCurrency(), this.amountPaid);
|
||||||
|
final Money amountOutstanding = Money.of(incrementBy.getCurrency(), this.amountOutstanding);
|
||||||
|
|
||||||
|
Money amountPaidOnThisCharge = Money.zero(incrementBy.getCurrency());
|
||||||
|
if (incrementBy.isGreaterThanOrEqualTo(amountOutstanding)) {
|
||||||
|
amountPaidOnThisCharge = amountOutstanding;
|
||||||
|
amountPaidToDate = amountPaidToDate.plus(amountOutstanding);
|
||||||
|
this.amountPaid = amountPaidToDate.getAmount();
|
||||||
|
this.amountOutstanding = BigDecimal.ZERO;
|
||||||
|
} else {
|
||||||
|
amountPaidOnThisCharge = incrementBy;
|
||||||
|
amountPaidToDate = amountPaidToDate.plus(incrementBy);
|
||||||
|
this.amountPaid = amountPaidToDate.getAmount();
|
||||||
|
this.amountOutstanding = calculateAmountOutstanding(incrementBy.getCurrency());
|
||||||
|
}
|
||||||
|
this.amountThroughChargePayment = feeAmount.getAmount();
|
||||||
|
this.paid = determineIfFullyPaid();
|
||||||
|
|
||||||
|
return amountPaidOnThisCharge;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Money getAmountWrittenOff(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.amountWrittenOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetPaidAmount(final MonetaryCurrency currency) {
|
||||||
|
this.amountPaid = BigDecimal.ZERO;
|
||||||
|
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||||
|
this.paid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetToOriginal(final MonetaryCurrency currency) {
|
||||||
|
this.amountPaid = BigDecimal.ZERO;
|
||||||
|
this.amountWaived = BigDecimal.ZERO;
|
||||||
|
this.amountWrittenOff = BigDecimal.ZERO;
|
||||||
|
this.amountOutstanding = calculateAmountOutstanding(currency);
|
||||||
|
this.paid = false;
|
||||||
|
this.waived = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Money getAmountThroughChargePayment(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.amountThroughChargePayment);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+8
-1
@@ -7,15 +7,22 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.LazyCollection;
|
||||||
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
|
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
@@ -98,7 +105,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
@Temporal(TemporalType.DATE)
|
@Temporal(TemporalType.DATE)
|
||||||
@Column(name = "obligations_met_on_date")
|
@Column(name = "obligations_met_on_date")
|
||||||
private Date obligationsMetOnDate;
|
private Date obligationsMetOnDate;
|
||||||
|
|
||||||
protected LoanRepaymentScheduleInstallment() {
|
protected LoanRepaymentScheduleInstallment() {
|
||||||
this.installmentNumber = null;
|
this.installmentNumber = null;
|
||||||
this.fromDate = null;
|
this.fromDate = null;
|
||||||
|
|||||||
+30
-8
@@ -70,7 +70,8 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
} else {
|
} else {
|
||||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||||
}
|
}
|
||||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) &&
|
||||||
|
loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged());
|
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged());
|
||||||
@@ -96,8 +97,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
|
if(loanCharge.isFeeCharge()){
|
||||||
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
|
if(loanCharge.isInstalmentFee()){
|
||||||
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
|
||||||
|
}else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
|
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +116,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
|
if(loanCharge.isFeeCharge()){
|
||||||
|
if(loanCharge.isInstalmentFee()){
|
||||||
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
|
||||||
|
}else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
|
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +152,8 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
} else {
|
} else {
|
||||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||||
}
|
}
|
||||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) &&
|
||||||
|
loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged());
|
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged());
|
||||||
@@ -167,8 +179,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
|
if(loanCharge.isInstalmentFee()){
|
||||||
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
|
||||||
|
}else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
|
cumulative = cumulative.plus(loanCharge.getAmountWaived(currency));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,8 +198,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
|
if(loanCharge.isInstalmentFee()){
|
||||||
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
|
||||||
|
}else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
|
cumulative = cumulative.plus(loanCharge.getAmountWrittenOff(currency));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-42
@@ -16,6 +16,7 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargePaidBy;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
@@ -68,12 +69,24 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
final List<LoanTransaction> transactionstoBeProcessed = new ArrayList<LoanTransaction>();
|
final List<LoanTransaction> transactionstoBeProcessed = new ArrayList<LoanTransaction>();
|
||||||
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||||
if (loanTransaction.isChargePayment()) {
|
if (loanTransaction.isChargePayment()) {
|
||||||
|
final List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<LoanRepaymentScheduleInstallment>();
|
||||||
final Set<LoanChargePaidBy> chargePaidBies = loanTransaction.getLoanChargesPaid();
|
final Set<LoanChargePaidBy> chargePaidBies = loanTransaction.getLoanChargesPaid();
|
||||||
final Set<LoanCharge> transferCharges = new HashSet<LoanCharge>();
|
final Set<LoanCharge> transferCharges = new HashSet<LoanCharge>();
|
||||||
for (final LoanChargePaidBy chargePaidBy : chargePaidBies) {
|
for (final LoanChargePaidBy chargePaidBy : chargePaidBies) {
|
||||||
transferCharges.add(chargePaidBy.getLoanCharge());
|
LoanCharge loanCharge = chargePaidBy.getLoanCharge();
|
||||||
|
transferCharges.add(loanCharge);
|
||||||
|
if(loanCharge.isInstalmentFee()){
|
||||||
|
LoanRepaymentScheduleInstallment installment = null;
|
||||||
|
if(loanCharge.isFeeCharge()){
|
||||||
|
installment = loanCharge.fetchRepaymentInstallment(loanTransaction.getFeeChargesPortion(currency));
|
||||||
|
}else{
|
||||||
|
installment = loanCharge.fetchRepaymentInstallment(loanTransaction.getPenaltyChargesPortion(currency));
|
||||||
|
}
|
||||||
|
if(installment !=null){
|
||||||
|
chargePaymentInstallments.add(installment);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final List<LoanRepaymentScheduleInstallment> chargePaymentInstallments = new ArrayList<LoanRepaymentScheduleInstallment>();
|
|
||||||
LocalDate startDate = disbursementDate;
|
LocalDate startDate = disbursementDate;
|
||||||
for (final LoanRepaymentScheduleInstallment installment : installments) {
|
for (final LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
for (final LoanCharge loanCharge : transferCharges) {
|
for (final LoanCharge loanCharge : transferCharges) {
|
||||||
@@ -172,16 +185,20 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
final Set<LoanCharge> loanFees = extractFeeCharges(charges);
|
final Set<LoanCharge> loanFees = extractFeeCharges(charges);
|
||||||
final Set<LoanCharge> loanPenalties = extractPenaltyCharges(charges);
|
final Set<LoanCharge> loanPenalties = extractPenaltyCharges(charges);
|
||||||
|
Integer installmentNumber = null;
|
||||||
|
if(loanTransaction.isChargePayment() && installments.size() == 1){
|
||||||
|
installmentNumber = installments.get(0).getInstallmentNumber();
|
||||||
|
}
|
||||||
|
|
||||||
if (loanTransaction.isNotWaiver()) {
|
if (loanTransaction.isNotWaiver()) {
|
||||||
final Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
|
final Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
|
||||||
if (feeCharges.isGreaterThanZero()) {
|
if (feeCharges.isGreaterThanZero()) {
|
||||||
updateFeeChargesPaidAmountBy(loanTransaction, feeCharges, loanFees);
|
updateChargesPaidAmountBy(loanTransaction, feeCharges, loanFees,installmentNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Money penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency);
|
final Money penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency);
|
||||||
if (penaltyCharges.isGreaterThanZero()) {
|
if (penaltyCharges.isGreaterThanZero()) {
|
||||||
updatePenaltyChargesPaidAmountBy(loanTransaction, penaltyCharges, loanPenalties);
|
updateChargesPaidAmountBy(loanTransaction, penaltyCharges, loanPenalties,installmentNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,65 +228,46 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
return penaltyCharges;
|
return penaltyCharges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFeeChargesPaidAmountBy(final LoanTransaction loanTransaction, final Money feeCharges, final Set<LoanCharge> charges) {
|
private void updateChargesPaidAmountBy(final LoanTransaction loanTransaction, final Money feeCharges, final Set<LoanCharge> charges,final Integer installmentNumber) {
|
||||||
|
|
||||||
Money amountRemaining = feeCharges;
|
Money amountRemaining = feeCharges;
|
||||||
for (final LoanCharge loanCharge : charges) {
|
while(amountRemaining.isGreaterThanZero()) {
|
||||||
if (!loanCharge.isDueAtDisbursement()) {
|
final LoanCharge unpaidCharge = findEarliestUnpaidChargeFromUnOrderedSet(charges);
|
||||||
if (loanCharge.isFeeCharge() && loanCharge.isNotFullyPaid() && amountRemaining.isGreaterThanZero()) {
|
Money feeAmount = feeCharges.zero();
|
||||||
final LoanCharge unpaidCharge = findEarliestUnpaidChargeFromUnOrderedSet(charges);
|
if(loanTransaction.isChargePayment()){
|
||||||
final Money amountPaidTowardsCharge = unpaidCharge.updatePaidAmountBy(amountRemaining);
|
feeAmount = feeCharges;
|
||||||
if (!amountPaidTowardsCharge.isZero()) {
|
|
||||||
if (!loanTransaction.isChargePayment()) {
|
|
||||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(loanTransaction, loanCharge,
|
|
||||||
amountPaidTowardsCharge.getAmount());
|
|
||||||
loanTransaction.getLoanChargesPaid().add(loanChargePaidBy);
|
|
||||||
}
|
|
||||||
amountRemaining = amountRemaining.minus(amountPaidTowardsCharge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
final Money amountPaidTowardsCharge = unpaidCharge.updatePaidAmountBy(amountRemaining,installmentNumber, feeAmount);
|
||||||
}
|
if (!amountPaidTowardsCharge.isZero()) {
|
||||||
|
if (!loanTransaction.isChargePayment()) {
|
||||||
private void updatePenaltyChargesPaidAmountBy(final LoanTransaction loanTransaction, final Money feeCharges,
|
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(loanTransaction, unpaidCharge,
|
||||||
final Set<LoanCharge> charges) {
|
amountPaidTowardsCharge.getAmount());
|
||||||
|
loanTransaction.getLoanChargesPaid().add(loanChargePaidBy);
|
||||||
Money amountRemaining = feeCharges;
|
|
||||||
for (final LoanCharge loanCharge : charges) {
|
|
||||||
if (!loanCharge.isDueAtDisbursement()) {
|
|
||||||
|
|
||||||
if (loanCharge.isPenaltyCharge() && loanCharge.isNotFullyPaid() && amountRemaining.isGreaterThanZero()) {
|
|
||||||
final LoanCharge unpaidCharge = findEarliestUnpaidChargeFromUnOrderedSet(charges);
|
|
||||||
final Money amountPaidTowardsCharge = unpaidCharge.updatePaidAmountBy(amountRemaining);
|
|
||||||
if (!amountPaidTowardsCharge.isZero()) {
|
|
||||||
if (!loanTransaction.isChargePayment()) {
|
|
||||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(loanTransaction, loanCharge,
|
|
||||||
amountPaidTowardsCharge.getAmount());
|
|
||||||
loanTransaction.getLoanChargesPaid().add(loanChargePaidBy);
|
|
||||||
}
|
|
||||||
amountRemaining = amountRemaining.minus(amountPaidTowardsCharge);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
amountRemaining = amountRemaining.minus(amountPaidTowardsCharge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(final Set<LoanCharge> charges) {
|
private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(final Set<LoanCharge> charges) {
|
||||||
LoanCharge earliestUnpaidCharge = null;
|
LoanCharge earliestUnpaidCharge = null;
|
||||||
LoanCharge installemntCharge = null;
|
LoanCharge installemntCharge = null;
|
||||||
|
LoanInstallmentCharge chargePerInstallment = null;
|
||||||
for (final LoanCharge loanCharge : charges) {
|
for (final LoanCharge loanCharge : charges) {
|
||||||
if (loanCharge.isNotFullyPaid() && !loanCharge.isDueAtDisbursement()) {
|
if (loanCharge.isNotFullyPaid() && !loanCharge.isDueAtDisbursement()) {
|
||||||
if(loanCharge.isInstalmentFee()){
|
if(loanCharge.isInstalmentFee()){
|
||||||
if(installemntCharge == null){
|
LoanInstallmentCharge unpaidLoanChargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
|
||||||
|
if(chargePerInstallment == null || chargePerInstallment.getRepaymentInstallment().getDueDate().isAfter(unpaidLoanChargePerInstallment.getRepaymentInstallment().getDueDate())){
|
||||||
installemntCharge = loanCharge;
|
installemntCharge = loanCharge;
|
||||||
|
chargePerInstallment = unpaidLoanChargePerInstallment;
|
||||||
}
|
}
|
||||||
}else if (earliestUnpaidCharge == null || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) {
|
}else if (earliestUnpaidCharge == null || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) {
|
||||||
earliestUnpaidCharge = loanCharge;
|
earliestUnpaidCharge = loanCharge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(earliestUnpaidCharge == null || earliestUnpaidCharge.getDueLocalDate().isAfter(new LocalDate())){
|
if(earliestUnpaidCharge == null || (chargePerInstallment!=null && earliestUnpaidCharge.getDueLocalDate().isAfter(chargePerInstallment.getRepaymentInstallment().getDueDate()))){
|
||||||
earliestUnpaidCharge = installemntCharge;
|
earliestUnpaidCharge = installemntCharge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -198,7 +198,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
} else {
|
} else {
|
||||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||||
}
|
}
|
||||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||||
@@ -243,7 +244,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
} else {
|
} else {
|
||||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||||
}
|
}
|
||||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||||
|
|||||||
+24
-2
@@ -300,9 +300,9 @@ public final class LoanEventApiJsonValidator {
|
|||||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
Set<String> transactionParameters = null;
|
Set<String> transactionParameters = null;
|
||||||
if (isChargeIdIncluded) {
|
if (isChargeIdIncluded) {
|
||||||
transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "locale", "dateFormat", "chargeId"));
|
transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "locale", "dateFormat", "chargeId","dueDate","installmentNumber"));
|
||||||
} else {
|
} else {
|
||||||
transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "locale", "dateFormat"));
|
transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "locale", "dateFormat","dueDate","installmentNumber"));
|
||||||
}
|
}
|
||||||
|
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
@@ -319,6 +319,28 @@ public final class LoanEventApiJsonValidator {
|
|||||||
baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter("chargeId").value(chargeId).notNull().integerGreaterThanZero();
|
||||||
}
|
}
|
||||||
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||||
|
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("installmentNumber", element);
|
||||||
|
baseDataValidator.reset().parameter("installmentNumber").value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateInstallmentChargeTransaction(final String json) {
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(json)) { return;}
|
||||||
|
Set<String> transactionParameters = new HashSet<String>(Arrays.asList("dueDate", "locale", "dateFormat", "installmentNumber"));
|
||||||
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||||
|
.resource("loan.charge.waive.transaction");
|
||||||
|
|
||||||
|
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||||
|
|
||||||
|
final Integer installmentNumber = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("installmentNumber", element);
|
||||||
|
baseDataValidator.reset().parameter("installmentNumber").value(installmentNumber).ignoreIfNull().integerGreaterThanZero();
|
||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-2
@@ -5,8 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.service;
|
package org.mifosplatform.portfolio.loanaccount.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -47,6 +49,8 @@ import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||||
@@ -109,6 +113,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||||
private final SavingsAccountAssembler savingsAccountAssembler;
|
private final SavingsAccountAssembler savingsAccountAssembler;
|
||||||
private final AccountAssociationsRepository accountAssociationsRepository;
|
private final AccountAssociationsRepository accountAssociationsRepository;
|
||||||
|
private final LoanChargeRepository loanChargeRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
|
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
|
||||||
@@ -122,7 +127,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
final LoanSummaryWrapper loanSummaryWrapper, final GroupRepositoryWrapper groupRepository,
|
final LoanSummaryWrapper loanSummaryWrapper, final GroupRepositoryWrapper groupRepository,
|
||||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||||
final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository,
|
final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository,
|
||||||
final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository) {
|
final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository,
|
||||||
|
final LoanChargeRepository loanChargeRepository) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.fromJsonHelper = fromJsonHelper;
|
this.fromJsonHelper = fromJsonHelper;
|
||||||
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
|
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
|
||||||
@@ -145,6 +151,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||||
this.savingsAccountAssembler = savingsAccountAssembler;
|
this.savingsAccountAssembler = savingsAccountAssembler;
|
||||||
this.accountAssociationsRepository = accountAssociationsRepository;
|
this.accountAssociationsRepository = accountAssociationsRepository;
|
||||||
|
this.loanChargeRepository = loanChargeRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||||
@@ -333,7 +340,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
|
|
||||||
if (changes.containsKey("recalculateLoanSchedule")) {
|
if (changes.containsKey("recalculateLoanSchedule")) {
|
||||||
changes.remove("recalculateLoanSchedule");
|
changes.remove("recalculateLoanSchedule");
|
||||||
|
|
||||||
final JsonElement parsedQuery = this.fromJsonHelper.parse(command.json());
|
final JsonElement parsedQuery = this.fromJsonHelper.parse(command.json());
|
||||||
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromJsonHelper);
|
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromJsonHelper);
|
||||||
|
|
||||||
@@ -354,6 +361,14 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
final Note note = Note.loanNote(existingLoanApplication, submittedOnNote);
|
final Note note = Note.loanNote(existingLoanApplication, submittedOnNote);
|
||||||
this.noteRepository.save(note);
|
this.noteRepository.save(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final LoanCharge loanCharge : existingLoanApplication.charges()) {
|
||||||
|
if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){
|
||||||
|
Set<LoanInstallmentCharge> chargePerInstallments = existingLoanApplication.generateInstallmentLoanCharges(loanCharge);
|
||||||
|
loanCharge.addLoanInstallmentCharges(chargePerInstallments);
|
||||||
|
this.loanChargeRepository.save(loanCharge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Long calendarId = command.longValueOfParameterNamed("calendarId");
|
final Long calendarId = command.longValueOfParameterNamed("calendarId");
|
||||||
Calendar calendar = null;
|
Calendar calendar = null;
|
||||||
|
|||||||
+3
@@ -9,6 +9,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
|
|
||||||
public interface LoanChargeReadPlatformService {
|
public interface LoanChargeReadPlatformService {
|
||||||
|
|
||||||
@@ -19,4 +20,6 @@ public interface LoanChargeReadPlatformService {
|
|||||||
LoanChargeData retrieveLoanChargeDetails(Long loanChargeId, Long loanId);
|
LoanChargeData retrieveLoanChargeDetails(Long loanChargeId, Long loanId);
|
||||||
|
|
||||||
Collection<LoanChargeData> retrieveLoanChargesForFeePayment(Integer paymentMode, Integer loanStatus);
|
Collection<LoanChargeData> retrieveLoanChargesForFeePayment(Integer paymentMode, Integer loanStatus);
|
||||||
|
|
||||||
|
Collection<LoanInstallmentChargeData> retrieveInstallmentLoanCharges(Long loanChargeId,boolean onlyPaymentPendingCharges);
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-6
@@ -21,6 +21,7 @@ import org.mifosplatform.portfolio.charge.data.ChargeData;
|
|||||||
import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService;
|
import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService;
|
||||||
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
|
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.RowMapper;
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
@@ -104,13 +105,13 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
|||||||
final EnumOptionData paymentMode = ChargeEnumerations.chargePaymentMode(chargePaymentMode);
|
final EnumOptionData paymentMode = ChargeEnumerations.chargePaymentMode(chargePaymentMode);
|
||||||
final boolean paid = rs.getBoolean("paid");
|
final boolean paid = rs.getBoolean("paid");
|
||||||
final boolean waived = rs.getBoolean("waied");
|
final boolean waived = rs.getBoolean("waied");
|
||||||
final BigDecimal minCap = rs.getBigDecimal("minCap");
|
final BigDecimal minCap = rs.getBigDecimal("minCap");
|
||||||
final BigDecimal maxCap = rs.getBigDecimal("maxCap");
|
final BigDecimal maxCap = rs.getBigDecimal("maxCap");
|
||||||
final BigDecimal amountOrPercentage = rs.getBigDecimal("amountOrPercentage");
|
final BigDecimal amountOrPercentage = rs.getBigDecimal("amountOrPercentage");
|
||||||
|
|
||||||
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
|
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
|
||||||
chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty, paymentMode,
|
chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty, paymentMode,
|
||||||
paid, waived, null,minCap,maxCap,amountOrPercentage);
|
paid, waived, null, minCap, maxCap, amountOrPercentage, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +128,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
|||||||
.retrieveSavingsCalculationTypes();
|
.retrieveSavingsCalculationTypes();
|
||||||
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
|
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
|
||||||
.retrieveSavingsCollectionTimeTypes();
|
.retrieveSavingsCollectionTimeTypes();
|
||||||
|
|
||||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
|
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
|
||||||
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
|
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
|
||||||
savingsChargeTimeTypeOptions);
|
savingsChargeTimeTypeOptions);
|
||||||
@@ -168,7 +169,10 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
|||||||
|
|
||||||
public String schema() {
|
public String schema() {
|
||||||
return "lc.id as id, lc.due_for_collection_as_of_date as dueAsOfDate, "
|
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 "
|
+ "lc.amount_outstanding_derived as amountOutstanding, "
|
||||||
|
+ "lc.charge_time_enum as chargeTime, "
|
||||||
|
+ "loan.id as loanId "
|
||||||
|
+ "from m_loan_charge lc "
|
||||||
+ "join m_loan loan on loan.id = lc.loan_id ";
|
+ "join m_loan loan on loan.id = lc.loan_id ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +183,46 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
|||||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||||
final Long loanId = rs.getLong("loanId");
|
final Long loanId = rs.getLong("loanId");
|
||||||
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
|
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
|
||||||
return new LoanChargeData(id, dueAsOfDate, amountOutstanding, loanId);
|
final int chargeTime = rs.getInt("chargeTime");
|
||||||
|
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||||
|
|
||||||
|
return new LoanChargeData(id, dueAsOfDate, amountOutstanding, chargeTimeType, loanId, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<LoanInstallmentChargeData> retrieveInstallmentLoanCharges(Long loanChargeId,boolean onlyPaymentPendingCharges) {
|
||||||
|
final LoanInstallmentChargeMapper rm = new LoanInstallmentChargeMapper();
|
||||||
|
String sql = "select " + rm.schema()
|
||||||
|
+ "where lic.loan_charge_id= ? ";
|
||||||
|
if(onlyPaymentPendingCharges){
|
||||||
|
sql = sql +"and lic.waived =0 and lic.is_paid_derived=0";
|
||||||
|
}
|
||||||
|
return this.jdbcTemplate.query(sql, rm, new Object[] { loanChargeId });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class LoanInstallmentChargeMapper implements RowMapper<LoanInstallmentChargeData> {
|
||||||
|
|
||||||
|
public String schema() {
|
||||||
|
return " lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, "
|
||||||
|
+ "lic.amount_outstanding_derived as amountOutstanding,"
|
||||||
|
+ "lic.amount as amount, "
|
||||||
|
+ "lic.is_paid_derived as paid, "
|
||||||
|
+ "lic.waived as waied "
|
||||||
|
+ "from m_loan_installment_charge lic "
|
||||||
|
+ "join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||||
|
final Integer installmentNumber = rs.getInt("installmentNumber");
|
||||||
|
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||||
|
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
|
||||||
|
final BigDecimal amount = rs.getBigDecimal("amount");
|
||||||
|
final boolean paid = rs.getBoolean("paid");
|
||||||
|
final boolean waived = rs.getBoolean("waied");
|
||||||
|
|
||||||
|
return new LoanInstallmentChargeData(installmentNumber, dueAsOfDate, amount, amountOutstanding, paid, waived);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+112
-23
@@ -77,11 +77,13 @@ import org.mifosplatform.portfolio.group.domain.Group;
|
|||||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||||
@@ -264,6 +266,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||||
disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount());
|
disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount());
|
||||||
|
}else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){
|
||||||
|
Set<LoanInstallmentCharge> chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge);
|
||||||
|
loanCharge.addLoanInstallmentCharges(chargePerInstallments);
|
||||||
|
this.loanChargeRepository.save(loanCharge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Locale locale = command.extractLocale();
|
final Locale locale = command.extractLocale();
|
||||||
@@ -272,7 +278,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||||
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
|
||||||
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loanId, "Loan Charge Payment",
|
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loanId, "Loan Charge Payment",
|
||||||
locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey());
|
locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null);
|
||||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,6 +388,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
}
|
}
|
||||||
|
final Set<LoanCharge> loanCharges = loan.charges();
|
||||||
|
final Map<Long, BigDecimal> disBuLoanCharges = new HashMap<Long, BigDecimal>();
|
||||||
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
|
if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
|
||||||
|
disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount());
|
||||||
|
}else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){
|
||||||
|
Set<LoanInstallmentCharge> chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge);
|
||||||
|
loanCharge.addLoanInstallmentCharges(chargePerInstallments);
|
||||||
|
this.loanChargeRepository.save(loanCharge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Locale locale = command.extractLocale();
|
||||||
|
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||||
|
for (final Map.Entry<Long, BigDecimal> entrySet : disBuLoanCharges.entrySet()) {
|
||||||
|
final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan.getId());
|
||||||
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(),
|
||||||
|
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(), "Loan Charge Payment",
|
||||||
|
locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null);
|
||||||
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
@@ -914,6 +940,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
checkClientOrGroupActive(loan);
|
checkClientOrGroupActive(loan);
|
||||||
|
this.loanEventApiJsonValidator.validateInstallmentChargeTransaction(command.json());
|
||||||
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
||||||
|
|
||||||
// Charges may be waived only when the loan associated with them are
|
// Charges may be waived only when the loan associated with them are
|
||||||
@@ -926,9 +953,28 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||||
} else if (loanCharge.isPaid()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_PAID,
|
} else if (loanCharge.isPaid()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_PAID,
|
||||||
loanCharge.getId());
|
loanCharge.getId());
|
||||||
} else if (loanCharge.isInstalmentFee()){
|
}
|
||||||
throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.WAIVE_NOT_ALLOWED_FOR_CHARGE,
|
Integer loanInstallmentNumber = null;
|
||||||
loanCharge.getId());
|
if (loanCharge.isInstalmentFee()){
|
||||||
|
LoanInstallmentCharge chargePerInstallment = null;
|
||||||
|
if(!StringUtils.isBlank(command.json())){
|
||||||
|
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
||||||
|
final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber");
|
||||||
|
if(dueDate !=null){
|
||||||
|
chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate);
|
||||||
|
}else if(installmentNumber!=null){
|
||||||
|
chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(chargePerInstallment == null){
|
||||||
|
chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
|
||||||
|
}
|
||||||
|
if(chargePerInstallment.isWaived()){
|
||||||
|
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||||
|
}else if(chargePerInstallment.isPaid()){
|
||||||
|
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId());
|
||||||
|
}
|
||||||
|
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>(3);
|
final Map<String, Object> changes = new LinkedHashMap<String, Object>(3);
|
||||||
@@ -937,7 +983,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||||
|
|
||||||
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
|
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
|
||||||
existingTransactionIds, existingReversedTransactionIds);
|
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber);
|
||||||
|
|
||||||
this.loanTransactionRepository.save(waiveTransaction);
|
this.loanTransactionRepository.save(waiveTransaction);
|
||||||
this.loanRepository.save(loan);
|
this.loanRepository.save(loan);
|
||||||
@@ -1006,9 +1052,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
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,
|
} else if (loanCharge.isPaid()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID,
|
||||||
loanCharge.getId());
|
loanCharge.getId());
|
||||||
}else if (loanCharge.isInstalmentFee()){
|
|
||||||
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_PAYABLE,
|
|
||||||
loanCharge.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException(
|
if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException(
|
||||||
@@ -1018,15 +1061,37 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
final Locale locale = command.extractLocale();
|
final Locale locale = command.extractLocale();
|
||||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||||
|
Integer loanInstallmentNumber = null;
|
||||||
|
BigDecimal amount = loanCharge.amountOutstanding();
|
||||||
|
if (loanCharge.isInstalmentFee()){
|
||||||
|
LoanInstallmentCharge chargePerInstallment = null;
|
||||||
|
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
||||||
|
final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber");
|
||||||
|
if(dueDate !=null){
|
||||||
|
chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate);
|
||||||
|
}else if(installmentNumber!=null){
|
||||||
|
chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber);
|
||||||
|
}
|
||||||
|
if(chargePerInstallment == null){
|
||||||
|
chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
|
||||||
|
}
|
||||||
|
if(chargePerInstallment.isWaived()){
|
||||||
|
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||||
|
}else if(chargePerInstallment.isPaid()){
|
||||||
|
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, loanCharge.getId());
|
||||||
|
}
|
||||||
|
loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
|
||||||
|
amount= chargePerInstallment.getAmountOutstanding();
|
||||||
|
}
|
||||||
|
|
||||||
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loanId);
|
||||||
if (portfolioAccountData == null) {
|
if (portfolioAccountData == null) {
|
||||||
final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment";
|
final String errorMessage = "Charge with id:" + loanChargeId + " requires linked savings account for payment";
|
||||||
throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId);
|
throw new LinkedAccountRequiredException("loanCharge.pay", errorMessage, loanChargeId);
|
||||||
}
|
}
|
||||||
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, loanCharge.amountOutstanding(),
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount,
|
||||||
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment",
|
PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, portfolioAccountData.accountId(), loanId, "Loan Charge Payment",
|
||||||
locale, fmt, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId);
|
locale, fmt, null, null, LoanTransactionType.CHARGE_PAYMENT.getValue(), loanChargeId, loanInstallmentNumber);
|
||||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
|
|
||||||
return new CommandProcessingResultBuilder() //
|
return new CommandProcessingResultBuilder() //
|
||||||
@@ -1047,29 +1112,53 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
if (chargeDatas != null) {
|
if (chargeDatas != null) {
|
||||||
for (final LoanChargeData chargeData : chargeDatas) {
|
for (final LoanChargeData chargeData : chargeDatas) {
|
||||||
if (chargeData.getDueDate() != null && !chargeData.getDueDate().isAfter(new LocalDate())) {
|
if(chargeData.isInstallmentFee()){
|
||||||
|
Collection<LoanInstallmentChargeData> chargePerInstallments = this.LoanChargeReadPlatformService.retrieveInstallmentLoanCharges(chargeData.getId(), true);
|
||||||
|
PortfolioAccountData portfolioAccountData = null;
|
||||||
|
for(LoanInstallmentChargeData installmentChargeData : chargePerInstallments){
|
||||||
|
if(!installmentChargeData.getDueDate().isAfter(new LocalDate())){
|
||||||
|
if(portfolioAccountData == null){
|
||||||
|
portfolioAccountData = this.accountAssociationsReadPlatformService
|
||||||
|
.retriveLoanAssociation(chargeData.getLoanId());
|
||||||
|
}
|
||||||
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(),
|
||||||
|
installmentChargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
|
||||||
|
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null,
|
||||||
|
LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), installmentChargeData.getInstallmentNumber());
|
||||||
|
transferFeeCharge(sb, accountTransferDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if (chargeData.getDueDate() != null && !chargeData.getDueDate().isAfter(new LocalDate())) {
|
||||||
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService
|
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService
|
||||||
.retriveLoanAssociation(chargeData.getLoanId());
|
.retriveLoanAssociation(chargeData.getLoanId());
|
||||||
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(),
|
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(new LocalDate(),
|
||||||
chargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
|
chargeData.getAmountOutstanding(), PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN,
|
||||||
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null,
|
portfolioAccountData.accountId(), chargeData.getLoanId(), "Loan Charge Payment", null, null, null, null,
|
||||||
LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId());
|
LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), null);
|
||||||
try {
|
transferFeeCharge(sb, accountTransferDTO);
|
||||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
|
||||||
} catch (final PlatformApiDataValidationException e) {
|
|
||||||
sb.append("Validation exception while paying charge ").append(chargeData.getId()).append(" for loan id:")
|
|
||||||
.append(chargeData.getLoanId()).append("--------");
|
|
||||||
} catch (final 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()); }
|
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sb
|
||||||
|
* @param accountTransferDTO
|
||||||
|
*/
|
||||||
|
private void transferFeeCharge(final StringBuilder sb, final AccountTransferDTO accountTransferDTO) {
|
||||||
|
try {
|
||||||
|
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||||
|
} catch (final PlatformApiDataValidationException e) {
|
||||||
|
sb.append("Validation exception while paying charge ").append(accountTransferDTO.getChargeId()).append(" for loan id:")
|
||||||
|
.append(accountTransferDTO.getToAccountId()).append("--------");
|
||||||
|
} catch (final InsufficientAccountBalanceException e) {
|
||||||
|
sb.append("InsufficientAccountBalance Exception while paying charge ").append(accountTransferDTO.getChargeId())
|
||||||
|
.append("for loan id:").append(accountTransferDTO.getToAccountId()).append("--------");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) {
|
private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) {
|
||||||
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
|
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
|
||||||
if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
|
if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
|
||||||
|
|||||||
Reference in New Issue
Block a user