merging MIFOSX-1923
This commit is contained in:
+5
@@ -203,6 +203,11 @@ public class LoanTransactionHelper {
|
||||
return (Integer) response.get("resourceId");
|
||||
}
|
||||
|
||||
public Object addChargesForAllreadyDisursedLoan(final Integer loanId, final String request, final ResponseSpecification responseSpecification) {
|
||||
final String ADD_CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges?" + Utils.TENANT_IDENTIFIER;
|
||||
return Utils.performServerPost(this.requestSpec, responseSpecification, ADD_CHARGES_URL, request, "");
|
||||
}
|
||||
|
||||
public Integer updateChargesForLoan(final Integer loanId, final Integer loanchargeId, final String request) {
|
||||
System.out.println("--------------------------------- ADD CHARGES FOR LOAN --------------------------------");
|
||||
final String UPDATE_CHARGES_URL = "/mifosng-provider/api/v1/loans/" + loanId + "/charges/" + loanchargeId + "?"
|
||||
|
||||
@@ -140,7 +140,6 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> feeFrequencyOptions = null;
|
||||
final GLAccountData account = null;
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
|
||||
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
|
||||
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
|
||||
+38
-26
@@ -56,7 +56,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
private Integer chargeAppliesTo;
|
||||
|
||||
@Column(name = "charge_time_enum", nullable = false)
|
||||
private Integer chargeTime;
|
||||
private Integer chargeTimeType;
|
||||
|
||||
@Column(name = "charge_calculation_enum")
|
||||
private Integer chargeCalculation;
|
||||
@@ -133,7 +133,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
this.amount = amount;
|
||||
this.currencyCode = currencyCode;
|
||||
this.chargeAppliesTo = chargeAppliesTo.getValue();
|
||||
this.chargeTime = chargeTime.getValue();
|
||||
this.chargeTimeType = chargeTime.getValue();
|
||||
this.chargeCalculation = chargeCalculationType.getValue();
|
||||
this.penalty = penalty;
|
||||
this.active = active;
|
||||
@@ -154,7 +154,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
// TODO vishwas, this validation seems unnecessary as identical
|
||||
// validation is performed in the write service
|
||||
if (!isAllowedSavingsChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.savings");
|
||||
}
|
||||
// TODO vishwas, this validation seems unnecessary as identical
|
||||
@@ -164,7 +164,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings");
|
||||
}
|
||||
|
||||
if (!ChargeTimeType.fromInt(getChargeTime()).isWithdrawalFee()
|
||||
if (!ChargeTimeType.fromInt(getChargeTimeType()).isWithdrawalFee()
|
||||
&& ChargeCalculationType.fromInt(getChargeCalculation()).isPercentageOfAmount()) {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation)
|
||||
.failWithCodeNoParameterAddedToErrorCode("savings.charge.calculation.type.percentage.allowed.only.for.withdrawal");
|
||||
@@ -172,16 +172,17 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
} else if (isLoanCharge()) {
|
||||
|
||||
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
|
||||
if (penalty && (chargeTime.isTimeOfDisbursement()
|
||||
|| chargeTime.isTrancheDisbursement())) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
|
||||
if (!penalty && chargeTime.isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
// TODO vishwas, this validation seems unnecessary as identical
|
||||
// validation is performed in the write service
|
||||
if (!isAllowedLoanChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan");
|
||||
}
|
||||
}
|
||||
if (isPercentageOfAmount()) {
|
||||
if (isPercentageOfApprovedAmount()) {
|
||||
this.minCap = minCap;
|
||||
this.maxCap = maxCap;
|
||||
}
|
||||
@@ -219,8 +220,8 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
return this.currencyCode;
|
||||
}
|
||||
|
||||
public Integer getChargeTime() {
|
||||
return this.chargeTime;
|
||||
public Integer getChargeTimeType() {
|
||||
return this.chargeTimeType;
|
||||
}
|
||||
|
||||
public Integer getChargeCalculation() {
|
||||
@@ -244,11 +245,11 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public boolean isAllowedLoanChargeTime() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAllowedLoanChargeTime();
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).isAllowedLoanChargeTime();
|
||||
}
|
||||
|
||||
public boolean isAllowedClientChargeTime() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAllowedClientChargeTime();
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).isAllowedClientChargeTime();
|
||||
}
|
||||
|
||||
public boolean isSavingsCharge() {
|
||||
@@ -260,7 +261,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeTime() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAllowedSavingsChargeTime();
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).isAllowedSavingsChargeTime();
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeCalculationType() {
|
||||
@@ -271,10 +272,14 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
return ChargeCalculationType.fromInt(this.chargeCalculation).isAllowedClientChargeCalculationType();
|
||||
}
|
||||
|
||||
public boolean isPercentageOfAmount() {
|
||||
public boolean isPercentageOfApprovedAmount() {
|
||||
return ChargeCalculationType.fromInt(this.chargeCalculation).isPercentageOfAmount();
|
||||
}
|
||||
|
||||
public boolean isPercentageOfDisbursementAmount() {
|
||||
return ChargeCalculationType.fromInt(this.chargeCalculation).isPercentageOfDisbursementAmount();
|
||||
}
|
||||
|
||||
public BigDecimal getMinCap() {
|
||||
return this.minCap;
|
||||
}
|
||||
@@ -315,15 +320,15 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
final String chargeTimeParamName = "chargeTimeType";
|
||||
if (command.isChangeInIntegerParameterNamed(chargeTimeParamName, this.chargeTime)) {
|
||||
if (command.isChangeInIntegerParameterNamed(chargeTimeParamName, this.chargeTimeType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(chargeTimeParamName);
|
||||
actualChanges.put(chargeTimeParamName, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.chargeTime = ChargeTimeType.fromInt(newValue).getValue();
|
||||
this.chargeTimeType = ChargeTimeType.fromInt(newValue).getValue();
|
||||
|
||||
if (isSavingsCharge()) {
|
||||
if (!isAllowedSavingsChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.savings");
|
||||
}
|
||||
// if charge time is changed to monthly then validate for
|
||||
@@ -337,12 +342,12 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
} else if (isLoanCharge()) {
|
||||
if (!isAllowedLoanChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan");
|
||||
}
|
||||
} else if (isClientCharge()) {
|
||||
if (!isAllowedLoanChargeTime()) {
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTime)
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType)
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.client");
|
||||
}
|
||||
}
|
||||
@@ -376,7 +381,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
.failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings");
|
||||
}
|
||||
|
||||
if (!ChargeTimeType.fromInt(getChargeTime()).isWithdrawalFee()
|
||||
if (!ChargeTimeType.fromInt(getChargeTimeType()).isWithdrawalFee()
|
||||
&& ChargeCalculationType.fromInt(getChargeCalculation()).isPercentageOfAmount()) {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation)
|
||||
.failWithCodeNoParameterAddedToErrorCode("charge.calculation.type.percentage.allowed.only.for.withdrawal");
|
||||
@@ -451,7 +456,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
this.active = newValue;
|
||||
}
|
||||
// allow min and max cap to be only added to PERCENT_OF_AMOUNT for now
|
||||
if (isPercentageOfAmount()) {
|
||||
if (isPercentageOfApprovedAmount()) {
|
||||
final String minCapParamName = "minCap";
|
||||
if (command.isChangeInBigDecimalParameterNamed(minCapParamName, this.minCap)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minCapParamName);
|
||||
@@ -466,11 +471,13 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.maxCap = newValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (this.penalty && ChargeTimeType.fromInt(this.chargeTime)
|
||||
if (this.penalty && ChargeTimeType.fromInt(this.chargeTimeType)
|
||||
.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(this.name); }
|
||||
if (!penalty && ChargeTimeType.fromInt(this.chargeTime).isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
if (!penalty
|
||||
&& ChargeTimeType.fromInt(this.chargeTimeType).isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ChargesApiConstants.glAccountIdParamName, getIncomeAccountId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ChargesApiConstants.glAccountIdParamName);
|
||||
@@ -495,7 +502,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
public ChargeData toData() {
|
||||
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(this.chargeTime);
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(this.chargeTimeType);
|
||||
final EnumOptionData chargeAppliesTo = ChargeEnumerations.chargeAppliesTo(this.chargeAppliesTo);
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
|
||||
final EnumOptionData chargePaymentmode = ChargeEnumerations.chargePaymentMode(this.chargePaymentMode);
|
||||
@@ -519,15 +526,15 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public boolean isMonthlyFee() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isMonthlyFee();
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).isMonthlyFee();
|
||||
}
|
||||
|
||||
public boolean isAnnualFee() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isAnnualFee();
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).isAnnualFee();
|
||||
}
|
||||
|
||||
public boolean isOverdueInstallment() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).isOverdueInstallment();
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).isOverdueInstallment();
|
||||
}
|
||||
|
||||
public MonthDay getFeeOnMonthDay() {
|
||||
@@ -561,4 +568,9 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
return incomeAccountId;
|
||||
}
|
||||
|
||||
public boolean isDisbursementCharge() {
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).equals(ChargeTimeType.DISBURSEMENT)
|
||||
|| ChargeTimeType.fromInt(this.chargeTimeType).equals(ChargeTimeType.TRANCHE_DISBURSEMENT);
|
||||
}
|
||||
}
|
||||
+12
-3
@@ -11,7 +11,8 @@ public enum ChargeCalculationType {
|
||||
FLAT(1, "chargeCalculationType.flat"), //
|
||||
PERCENT_OF_AMOUNT(2, "chargeCalculationType.percent.of.amount"), //
|
||||
PERCENT_OF_AMOUNT_AND_INTEREST(3, "chargeCalculationType.percent.of.amount.and.interest"), //
|
||||
PERCENT_OF_INTEREST(4, "chargeCalculationType.percent.of.interest");
|
||||
PERCENT_OF_INTEREST(4, "chargeCalculationType.percent.of.interest"),
|
||||
PERCENT_OF_DISBURSEMENT_AMOUNT(5,"chargeCalculationType.percent.of.disbursement.amount");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -31,7 +32,8 @@ public enum ChargeCalculationType {
|
||||
|
||||
public static Object[] validValuesForLoan() {
|
||||
return new Integer[] { ChargeCalculationType.FLAT.getValue(), ChargeCalculationType.PERCENT_OF_AMOUNT.getValue(),
|
||||
ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST.getValue(), ChargeCalculationType.PERCENT_OF_INTEREST.getValue() };
|
||||
ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST.getValue(), ChargeCalculationType.PERCENT_OF_INTEREST.getValue(),
|
||||
ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT.getValue()};
|
||||
}
|
||||
|
||||
public static Object[] validValuesForSavings() {
|
||||
@@ -57,6 +59,9 @@ public enum ChargeCalculationType {
|
||||
case 4:
|
||||
chargeCalculationType = PERCENT_OF_INTEREST;
|
||||
break;
|
||||
case 5:
|
||||
chargeCalculationType = PERCENT_OF_DISBURSEMENT_AMOUNT;
|
||||
break;
|
||||
}
|
||||
return chargeCalculationType;
|
||||
}
|
||||
@@ -86,6 +91,10 @@ public enum ChargeCalculationType {
|
||||
}
|
||||
|
||||
public boolean isPercentageBased() {
|
||||
return isPercentageOfAmount() || isPercentageOfAmountAndInterest() || isPercentageOfInterest();
|
||||
return isPercentageOfAmount() || isPercentageOfAmountAndInterest() || isPercentageOfInterest() || isPercentageOfDisbursementAmount();
|
||||
}
|
||||
|
||||
public boolean isPercentageOfDisbursementAmount(){
|
||||
return this.value.equals(ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT.getValue());
|
||||
}
|
||||
}
|
||||
+12
-7
@@ -10,18 +10,16 @@ public enum ChargeTimeType {
|
||||
INVALID(0, "chargeTimeType.invalid"), //
|
||||
DISBURSEMENT(1, "chargeTimeType.disbursement"), // only for loan charges
|
||||
SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate"), // for loan and
|
||||
// savings charges
|
||||
SAVINGS_ACTIVATION(3, "chargeTimeType.savingsActivation"), // only for
|
||||
// savings
|
||||
SAVINGS_CLOSURE(4, "chargeTimeType.savingsClosure"), // only for savings
|
||||
WITHDRAWAL_FEE(5, "chargeTimeType.withdrawalFee"), // only for savings
|
||||
ANNUAL_FEE(6, "chargeTimeType.annualFee"), // only for savings
|
||||
MONTHLY_FEE(7, "chargeTimeType.monthlyFee"), // only for savings
|
||||
INSTALMENT_FEE(8, "chargeTimeType.instalmentFee"), // only for loan charges
|
||||
OVERDUE_INSTALLMENT(9, "chargeTimeType.overdueInstallment"), // only for
|
||||
// loan charges
|
||||
OVERDRAFT_FEE(10, "chargeTimeType.overdraftFee"), // only for savings
|
||||
WEEKLY_FEE(11, "chargeTimeType.weeklyFee"); // only for savings
|
||||
OVERDRAFT_FEE(10, "chargeTimeType.overdraftFee"),// only for savings
|
||||
WEEKLY_FEE(11, "chargeTimeType.weeklyFee"), // only for savings
|
||||
TRANCHE_DISBURSEMENT(12,"chargeTimeType.tranchedisbursement"); // only for loan
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -41,7 +39,7 @@ public enum ChargeTimeType {
|
||||
|
||||
public static Object[] validLoanValues() {
|
||||
return new Integer[] { ChargeTimeType.DISBURSEMENT.getValue(), ChargeTimeType.SPECIFIED_DUE_DATE.getValue(),
|
||||
ChargeTimeType.INSTALMENT_FEE.getValue(), ChargeTimeType.OVERDUE_INSTALLMENT.getValue() };
|
||||
ChargeTimeType.INSTALMENT_FEE.getValue(), ChargeTimeType.OVERDUE_INSTALLMENT.getValue(), ChargeTimeType.TRANCHE_DISBURSEMENT.getValue() };
|
||||
}
|
||||
|
||||
public static Object[] validLoanChargeValues() {
|
||||
@@ -96,6 +94,9 @@ public enum ChargeTimeType {
|
||||
case 11:
|
||||
chargeTimeType = WEEKLY_FEE;
|
||||
break;
|
||||
case 12:
|
||||
chargeTimeType = TRANCHE_DISBURSEMENT;
|
||||
break;
|
||||
default:
|
||||
chargeTimeType = INVALID;
|
||||
break;
|
||||
@@ -145,7 +146,7 @@ public enum ChargeTimeType {
|
||||
}
|
||||
|
||||
public boolean isAllowedLoanChargeTime() {
|
||||
return isTimeOfDisbursement() || isOnSpecifiedDueDate() || isInstalmentFee() || isOverdueInstallment();
|
||||
return isTimeOfDisbursement() || isOnSpecifiedDueDate() || isInstalmentFee() || isOverdueInstallment() || isTrancheDisbursement();
|
||||
}
|
||||
|
||||
public boolean isAllowedClientChargeTime() {
|
||||
@@ -160,5 +161,9 @@ public enum ChargeTimeType {
|
||||
public boolean isOverdraftFee() {
|
||||
return this.value.equals(ChargeTimeType.OVERDRAFT_FEE.getValue());
|
||||
}
|
||||
|
||||
public boolean isTrancheDisbursement(){
|
||||
return this.value.equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -104,7 +104,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType)
|
||||
.isOneOfTheseValues(ChargeCalculationType.validValuesForLoan());
|
||||
}
|
||||
|
||||
|
||||
} else if (appliesTo.isSavingsCharge()) {
|
||||
// savings applicable validation
|
||||
final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeTimeType", element);
|
||||
@@ -263,7 +263,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
if (this.fromApiJsonHelper.parameterExists("chargeCalculationType", element)) {
|
||||
final Integer chargeCalculationType = this.fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element,
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 4);
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 5);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("chargePaymentMode", element)) {
|
||||
|
||||
-2
@@ -26,9 +26,7 @@ public interface ChargeDropdownReadPlatformService {
|
||||
List<EnumOptionData> retrieveSavingsCalculationTypes();
|
||||
|
||||
List<EnumOptionData> retrieveSavingsCollectionTimeTypes();
|
||||
|
||||
List<EnumOptionData> retrieveClientCalculationTypes();
|
||||
|
||||
List<EnumOptionData> retrieveClientCollectionTimeTypes();
|
||||
|
||||
}
|
||||
|
||||
+7
-3
@@ -16,6 +16,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import static org.mifosplatform.portfolio.charge.service.ChargeEnumerations.*;
|
||||
|
||||
@Service
|
||||
@@ -27,7 +28,8 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST));
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,13 +66,15 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST));
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveLoanCollectionTimeTypes() {
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE),
|
||||
chargeTimeType(ChargeTimeType.INSTALMENT_FEE), chargeTimeType(ChargeTimeType.OVERDUE_INSTALLMENT));
|
||||
chargeTimeType(ChargeTimeType.INSTALMENT_FEE), chargeTimeType(ChargeTimeType.OVERDUE_INSTALLMENT),
|
||||
chargeTimeType(ChargeTimeType.TRANCHE_DISBURSEMENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+10
-2
@@ -64,6 +64,10 @@ public class ChargeEnumerations {
|
||||
optionData = new EnumOptionData(ChargeTimeType.OVERDRAFT_FEE.getValue().longValue(), ChargeTimeType.OVERDRAFT_FEE.getCode(),
|
||||
"Overdraft Fee");
|
||||
break;
|
||||
case TRANCHE_DISBURSEMENT:
|
||||
optionData = new EnumOptionData(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue().longValue(),
|
||||
ChargeTimeType.TRANCHE_DISBURSEMENT.getCode(), "Tranche Disbursement");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
@@ -109,7 +113,7 @@ public class ChargeEnumerations {
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
optionData = new EnumOptionData(ChargeCalculationType.PERCENT_OF_AMOUNT.getValue().longValue(),
|
||||
ChargeCalculationType.PERCENT_OF_AMOUNT.getCode(), "% Amount");
|
||||
ChargeCalculationType.PERCENT_OF_AMOUNT.getCode(), "% Approved Amount");
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
optionData = new EnumOptionData(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST.getValue().longValue(),
|
||||
@@ -119,6 +123,10 @@ public class ChargeEnumerations {
|
||||
optionData = new EnumOptionData(ChargeCalculationType.PERCENT_OF_INTEREST.getValue().longValue(),
|
||||
ChargeCalculationType.PERCENT_OF_INTEREST.getCode(), "% Interest");
|
||||
break;
|
||||
case PERCENT_OF_DISBURSEMENT_AMOUNT:
|
||||
optionData = new EnumOptionData(ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT.getValue().longValue(),
|
||||
ChargeCalculationType.PERCENT_OF_DISBURSEMENT_AMOUNT.getCode(), "% Disbursement Amount");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeCalculationType.INVALID.getValue().longValue(),
|
||||
ChargeCalculationType.INVALID.getCode(), "Invalid");
|
||||
@@ -145,5 +153,5 @@ public class ChargeEnumerations {
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+21
-17
@@ -8,8 +8,8 @@ package org.mifosplatform.portfolio.charge.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@@ -49,6 +50,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final MifosEntityAccessUtil mifosEntityAccessUtil;
|
||||
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
|
||||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public ChargeReadPlatformServiceImpl(final CurrencyReadPlatformService currencyReadPlatformService,
|
||||
@@ -61,6 +63,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.mifosEntityAccessUtil = mifosEntityAccessUtil;
|
||||
this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
|
||||
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,14 +175,15 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
public Collection<ChargeData> retrieveLoanAccountApplicableCharges(final Long loanId, ChargeTimeType[] excludeChargeTimes) {
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
StringBuilder excludeClause = new StringBuilder("");
|
||||
Object[] params = new Object[] { loanId, ChargeAppliesTo.LOAN.getValue() };
|
||||
params = processChargeExclusionsForLoans(excludeChargeTimes, excludeClause, params);
|
||||
String sql = "select " + rm.chargeSchema() + " join m_loan la on la.currency_code = c.currency_code" + " where la.id=?"
|
||||
+ " and c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=?" + excludeClause + " ";
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("loanId", loanId);
|
||||
paramMap.put("chargeAppliesTo", ChargeAppliesTo.LOAN.getValue());
|
||||
processChargeExclusionsForLoans(excludeChargeTimes, excludeClause);
|
||||
String sql = "select " + rm.chargeSchema() + " join m_loan la on la.currency_code = c.currency_code" + " where la.id=:loanId"
|
||||
+ " and c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=:chargeAppliesTo" + excludeClause + " ";
|
||||
sql += addInClauseToSQL_toLimitChargesMappedToOffice_ifOfficeSpecificProductsEnabled();
|
||||
sql += " order by c.name ";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, params);
|
||||
return this.namedParameterJdbcTemplate.query(sql, paramMap, rm);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,9 +192,8 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
private Object[] processChargeExclusionsForLoans(ChargeTimeType[] excludeChargeTimes, StringBuilder excludeClause, Object[] params) {
|
||||
private void processChargeExclusionsForLoans(ChargeTimeType[] excludeChargeTimes, StringBuilder excludeClause) {
|
||||
if (excludeChargeTimes != null && excludeChargeTimes.length > 0) {
|
||||
excludeClause = excludeClause.append(" and c.charge_time_enum not in(?) ");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < excludeChargeTimes.length; i++) {
|
||||
if (i != 0) {
|
||||
@@ -198,24 +201,25 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
}
|
||||
sb.append(excludeChargeTimes[i].getValue());
|
||||
}
|
||||
params = Arrays.copyOf(params, params.length + 1);
|
||||
params[params.length - 1] = sb.toString();
|
||||
excludeClause = excludeClause.append(" and c.charge_time_enum not in(" + sb.toString() + ") ");
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ChargeData> retrieveLoanProductApplicableCharges(final Long loanProductId, ChargeTimeType[] excludeChargeTimes) {
|
||||
final ChargeMapper rm = new ChargeMapper();
|
||||
StringBuilder excludeClause = new StringBuilder("");
|
||||
Object[] params = new Object[] { loanProductId, ChargeAppliesTo.LOAN.getValue() };
|
||||
params = processChargeExclusionsForLoans(excludeChargeTimes, excludeClause, params);
|
||||
String sql = "select " + rm.chargeSchema() + " join m_product_loan lp on lp.currency_code = c.currency_code" + " where lp.id=?"
|
||||
+ " and c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=?" + excludeClause + " ";
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("productId", loanProductId);
|
||||
paramMap.put("chargeAppliesTo", ChargeAppliesTo.LOAN.getValue());
|
||||
processChargeExclusionsForLoans(excludeChargeTimes, excludeClause);
|
||||
String sql = "select " + rm.chargeSchema() + " join m_product_loan lp on lp.currency_code = c.currency_code"
|
||||
+ " where lp.id=:productId" + " and c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=:chargeAppliesTo"
|
||||
+ excludeClause + " ";
|
||||
sql += addInClauseToSQL_toLimitChargesMappedToOffice_ifOfficeSpecificProductsEnabled();
|
||||
sql += " order by c.name ";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, params);
|
||||
return this.namedParameterJdbcTemplate.query(sql, paramMap, rm);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ public class ClientCharge extends AbstractPersistable<Long> {
|
||||
this.client = client;
|
||||
this.charge = charge;
|
||||
this.penaltyCharge = charge.isPenalty();
|
||||
this.chargeTime = charge.getChargeTime();
|
||||
this.chargeTime = charge.getChargeTimeType();
|
||||
this.dueDate = (dueDate == null) ? null : dueDate.toDate();
|
||||
this.chargeCalculation = charge.getChargeCalculation();
|
||||
|
||||
|
||||
+3
-1
@@ -15,8 +15,10 @@ public interface LoanApiConstants {
|
||||
public static final String updatedDisbursementDateParameterName = "updatedExpectedDisbursementDate";
|
||||
public static final String updatedDisbursementPrincipalParameterName = "updatedPrincipal";
|
||||
public static final String disbursementIdParameterName = "id";
|
||||
public static final String loanChargeIdParameterName = "loanChargeId";
|
||||
public static final String principalDisbursedParameterName = "transactionAmount";
|
||||
|
||||
public static final String chargesParameterName = "charges" ;
|
||||
|
||||
public static final String approvedLoanAmountParameterName = "approvedLoanAmount";
|
||||
public static final String approvedOnDateParameterName = "approvedOnDate";
|
||||
public static final String noteParameterName = "note";
|
||||
|
||||
+8
-4
@@ -405,10 +405,9 @@ public class LoansApiResource {
|
||||
|
||||
if (associationParameters.contains("repaymentSchedule")) {
|
||||
mandatoryResponseParameters.add("repaymentSchedule");
|
||||
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.repaymentScheduleRelatedData();
|
||||
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, repaymentScheduleRelatedData,
|
||||
disbursementData, loanBasicDetails.isInterestRecalculationEnabled());
|
||||
disbursementData, loanBasicDetails.isInterestRecalculationEnabled(), loanBasicDetails.getTotalPaidFeeCharges());
|
||||
|
||||
if (associationParameters.contains("futureSchedule") && loanBasicDetails.isInterestRecalculationEnabled()) {
|
||||
mandatoryResponseParameters.add("futureSchedule");
|
||||
@@ -494,8 +493,13 @@ public class LoansApiResource {
|
||||
|
||||
fundOptions = this.fundReadPlatformService.retrieveAllFunds();
|
||||
repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(loanId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
|
||||
if(product.getMultiDisburseLoan()){
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(loanId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
|
||||
}else{
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(loanId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT, ChargeTimeType.TRANCHE_DISBURSEMENT });
|
||||
}
|
||||
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
|
||||
|
||||
allowedLoanOfficers = this.loanReadPlatformService.retrieveAllowedLoanOfficers(loanBasicDetails.officeId(),
|
||||
|
||||
+9
-1
@@ -19,13 +19,17 @@ public class DisbursementData implements Comparable<DisbursementData> {
|
||||
private final LocalDate expectedDisbursementDate;
|
||||
private final LocalDate actualDisbursementDate;
|
||||
private final BigDecimal principal;
|
||||
private final String loanChargeId;
|
||||
private final BigDecimal chargeAmount;
|
||||
|
||||
public DisbursementData(Long id, final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate,
|
||||
final BigDecimal principalDisbursed) {
|
||||
final BigDecimal principalDisbursed, final String loanChargeId, BigDecimal chargeAmount) {
|
||||
this.id = id;
|
||||
this.expectedDisbursementDate = expectedDisbursementDate;
|
||||
this.actualDisbursementDate = actualDisbursementDate;
|
||||
this.principal = principalDisbursed;
|
||||
this.loanChargeId = loanChargeId;
|
||||
this.chargeAmount = chargeAmount;
|
||||
}
|
||||
|
||||
public LocalDate disbursementDate() {
|
||||
@@ -40,6 +44,10 @@ public class DisbursementData implements Comparable<DisbursementData> {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
public BigDecimal getChargeAmount(){
|
||||
return this.chargeAmount;
|
||||
}
|
||||
|
||||
public boolean isDisbursed() {
|
||||
return this.actualDisbursementDate != null;
|
||||
}
|
||||
|
||||
+8
@@ -1387,4 +1387,12 @@ public class LoanAccountData {
|
||||
return LoanStatus.fromInt(this.status.id().intValue()).isActive();
|
||||
}
|
||||
|
||||
public boolean isMultiDisburseLoan() {
|
||||
return multiDisburseLoan ;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalPaidFeeCharges() {
|
||||
if(this.summary != null) return this.summary.getTotalPaidFeeCharges() ;
|
||||
return BigDecimal.ZERO ;
|
||||
}
|
||||
}
|
||||
+1
@@ -68,6 +68,7 @@ public class LoanChargeData {
|
||||
private BigDecimal amountAccrued;
|
||||
|
||||
private BigDecimal amountUnrecognized;
|
||||
|
||||
|
||||
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,
|
||||
|
||||
+4
@@ -102,4 +102,8 @@ public class LoanSummaryData {
|
||||
public BigDecimal getTotalOutstanding() {
|
||||
return this.totalOutstanding;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalPaidFeeCharges() {
|
||||
return feeChargesPaid ;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -62,6 +62,6 @@ public class RepaymentScheduleRelatedLoanData {
|
||||
}
|
||||
|
||||
public DisbursementData disbursementData() {
|
||||
return new DisbursementData(null, this.expectedDisbursementDate, this.actualDisbursementDate, this.principal);
|
||||
return new DisbursementData(null, this.expectedDisbursementDate, this.actualDisbursementDate, this.principal, null, null);
|
||||
}
|
||||
}
|
||||
+445
-216
File diff suppressed because it is too large
Load Diff
+47
-19
@@ -7,7 +7,6 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
@@ -30,7 +29,6 @@ import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.LocalDate;
|
||||
@@ -121,6 +119,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private LoanOverdueInstallmentCharge overdueInstallmentCharge;
|
||||
|
||||
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
private LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
|
||||
|
||||
public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) {
|
||||
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
||||
return createNewFromJson(loan, chargeDefinition, command, dueDate);
|
||||
@@ -144,8 +145,8 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
if (command.hasParameter("principal") && command.hasParameter("interest")) {
|
||||
amountPercentageAppliedTo = command.bigDecimalValueOfParameterNamed("principal")
|
||||
.add(command.bigDecimalValueOfParameterNamed("interest"));
|
||||
amountPercentageAppliedTo = command.bigDecimalValueOfParameterNamed("principal").add(
|
||||
command.bigDecimalValueOfParameterNamed("interest"));
|
||||
} else {
|
||||
amountPercentageAppliedTo = loan.getPrincpal().getAmount().add(loan.getTotalInterest());
|
||||
}
|
||||
@@ -162,7 +163,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
BigDecimal loanCharge = BigDecimal.ZERO;
|
||||
if (ChargeTimeType.fromInt(chargeDefinition.getChargeTime()).equals(ChargeTimeType.INSTALMENT_FEE)) {
|
||||
if (ChargeTimeType.fromInt(chargeDefinition.getChargeTimeType()).equals(ChargeTimeType.INSTALMENT_FEE)) {
|
||||
BigDecimal percentage = amount;
|
||||
if (percentage == null) {
|
||||
percentage = chargeDefinition.getAmount();
|
||||
@@ -198,7 +199,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.minCap = chargeDefinition.getMinCap();
|
||||
this.maxCap = chargeDefinition.getMaxCap();
|
||||
|
||||
this.chargeTime = chargeDefinition.getChargeTime();
|
||||
this.chargeTime = chargeDefinition.getChargeTimeType();
|
||||
if (chargeTime != null) {
|
||||
this.chargeTime = chargeTime.getValue();
|
||||
}
|
||||
@@ -267,6 +268,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
case PERCENT_OF_INTEREST:
|
||||
case PERCENT_OF_DISBURSEMENT_AMOUNT:
|
||||
this.percentage = chargeAmount;
|
||||
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
|
||||
if (loanCharge.compareTo(BigDecimal.ZERO) == 0) {
|
||||
@@ -291,6 +293,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.paid = true;
|
||||
}
|
||||
|
||||
public boolean isFullyPaid() {
|
||||
return this.paid;
|
||||
}
|
||||
|
||||
public void resetToOriginal(final MonetaryCurrency currency) {
|
||||
this.amountPaid = BigDecimal.ZERO;
|
||||
this.amountWaived = BigDecimal.ZERO;
|
||||
@@ -335,6 +341,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
}
|
||||
|
||||
public BigDecimal getAmountPercentageAppliedTo() {
|
||||
return this.amountPercentageAppliedTo;
|
||||
}
|
||||
|
||||
private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) {
|
||||
return getAmount(currency).minus(getAmountWaived(currency)).minus(getAmountPaid(currency)).getAmount();
|
||||
}
|
||||
@@ -366,6 +376,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
case PERCENT_OF_INTEREST:
|
||||
case PERCENT_OF_DISBURSEMENT_AMOUNT:
|
||||
this.percentage = amount;
|
||||
this.amountPercentageAppliedTo = loanPrincipal;
|
||||
if (loanCharge.compareTo(BigDecimal.ZERO) == 0) {
|
||||
@@ -395,6 +406,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case PERCENT_OF_INTEREST:
|
||||
amountPercentageAppliedTo = this.loan.getTotalInterest();
|
||||
break;
|
||||
case PERCENT_OF_DISBURSEMENT_AMOUNT:
|
||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = this.loanTrancheDisbursementCharge;
|
||||
amountPercentageAppliedTo = loanTrancheDisbursementCharge.getloanDisbursementDetails().principal();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -423,6 +438,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
final String amountParamName = "amount";
|
||||
if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||
BigDecimal loanCharge = null;
|
||||
actualChanges.put(amountParamName, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
@@ -439,9 +455,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
case PERCENT_OF_INTEREST:
|
||||
case PERCENT_OF_DISBURSEMENT_AMOUNT:
|
||||
this.percentage = newValue;
|
||||
this.amountPercentageAppliedTo = amount;
|
||||
BigDecimal loanCharge = BigDecimal.ZERO;
|
||||
loanCharge = BigDecimal.ZERO;
|
||||
if (isInstalmentFee()) {
|
||||
loanCharge = this.loan.calculatePerInstallmentChargeAmount(ChargeCalculationType.fromInt(this.chargeCalculation),
|
||||
this.percentage);
|
||||
@@ -486,7 +503,8 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public boolean isDueAtDisbursement() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.DISBURSEMENT);
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.DISBURSEMENT)
|
||||
|| ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.TRANCHE_DISBURSEMENT);
|
||||
}
|
||||
|
||||
public boolean isSpecifiedDueDate() {
|
||||
@@ -737,11 +755,12 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(3, 5) //
|
||||
.append(getId()) //
|
||||
.append(this.charge.getId()) //
|
||||
.append(this.amount).append(getDueLocalDate()) //
|
||||
.toHashCode();
|
||||
return 1;
|
||||
/*
|
||||
* return new HashCodeBuilder(3, 5) // .append(getId()) //
|
||||
* .append(this.charge.getId()) //
|
||||
* .append(this.amount).append(getDueLocalDate()) // .toHashCode();
|
||||
*/
|
||||
}
|
||||
|
||||
public ChargePaymentMode getChargePaymentMode() {
|
||||
@@ -764,8 +783,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public LoanInstallmentCharge getUnpaidInstallmentLoanCharge() {
|
||||
LoanInstallmentCharge unpaidChargePerInstallment = null;
|
||||
for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
|
||||
if (loanChargePerInstallment.isPending() && (unpaidChargePerInstallment == null || unpaidChargePerInstallment
|
||||
.getRepaymentInstallment().getDueDate().isAfter(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
|
||||
if (loanChargePerInstallment.isPending()
|
||||
&& (unpaidChargePerInstallment == null || unpaidChargePerInstallment.getRepaymentInstallment().getDueDate()
|
||||
.isAfter(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
|
||||
unpaidChargePerInstallment = loanChargePerInstallment;
|
||||
}
|
||||
}
|
||||
@@ -781,8 +801,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
public LoanInstallmentCharge getInstallmentLoanCharge(final Integer installmentNumber) {
|
||||
for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
|
||||
if (installmentNumber.equals(loanChargePerInstallment.getRepaymentInstallment().getInstallmentNumber()
|
||||
.intValue())) { return loanChargePerInstallment; }
|
||||
if (installmentNumber.equals(loanChargePerInstallment.getRepaymentInstallment().getInstallmentNumber().intValue())) { return loanChargePerInstallment; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -851,6 +870,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.overdueInstallmentCharge = overdueInstallmentCharge;
|
||||
}
|
||||
|
||||
public void updateLoanTrancheDisbursementCharge(final LoanTrancheDisbursementCharge loanTrancheDisbursementCharge) {
|
||||
this.loanTrancheDisbursementCharge = loanTrancheDisbursementCharge;
|
||||
}
|
||||
|
||||
public void updateWaivedAmount(MonetaryCurrency currency) {
|
||||
if (isInstalmentFee()) {
|
||||
this.amountWaived = BigDecimal.ZERO;
|
||||
@@ -885,6 +908,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
return this.overdueInstallmentCharge;
|
||||
}
|
||||
|
||||
public LoanTrancheDisbursementCharge getTrancheDisbursementCharge() {
|
||||
return this.loanTrancheDisbursementCharge;
|
||||
}
|
||||
|
||||
public Money undoPaidOrPartiallyAmountBy(final Money incrementBy, final Integer installmentNumber, final Money feeAmount) {
|
||||
Money processAmount = Money.zero(incrementBy.getCurrency());
|
||||
if (isInstalmentFee()) {
|
||||
@@ -922,8 +949,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
Money outstanding = Money.of(currency, loanChargePerInstallment.getAmountOutstanding());
|
||||
final boolean partiallyPaid = outstanding.isGreaterThanZero()
|
||||
&& outstanding.isLessThan(loanChargePerInstallment.getAmount(currency));
|
||||
if ((partiallyPaid || loanChargePerInstallment.isPaid()) && (paidChargePerInstallment == null || paidChargePerInstallment
|
||||
.getRepaymentInstallment().getDueDate().isBefore(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
|
||||
if ((partiallyPaid || loanChargePerInstallment.isPaid())
|
||||
&& (paidChargePerInstallment == null || paidChargePerInstallment.getRepaymentInstallment().getDueDate()
|
||||
.isBefore(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
|
||||
paidChargePerInstallment = loanChargePerInstallment;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
@@ -23,7 +24,7 @@ public class LoanChargePaidBy extends AbstractPersistable<Long> {
|
||||
@JoinColumn(name = "loan_transaction_id", nullable = false)
|
||||
private LoanTransaction loanTransaction;
|
||||
|
||||
@ManyToOne
|
||||
@ManyToOne(cascade= CascadeType.ALL)
|
||||
@JoinColumn(name = "loan_charge_id", nullable = false)
|
||||
private LoanCharge loanCharge;
|
||||
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
|
||||
if (this.actualDisbursementDate != null) {
|
||||
actualDisburseDate = new LocalDate(this.actualDisbursementDate);
|
||||
}
|
||||
return new DisbursementData(getId(), expectedDisburseDate, actualDisburseDate, this.principal);
|
||||
return new DisbursementData(getId(), expectedDisburseDate, actualDisburseDate, this.principal, null, null);
|
||||
}
|
||||
|
||||
public void updateActualDisbursementDate(Date actualDisbursementDate) {
|
||||
|
||||
+3
-3
@@ -60,7 +60,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isFeeCharge()) {
|
||||
if (loanCharge.isFeeCharge() && !loanCharge.isDueAtDisbursement()) {
|
||||
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
@@ -108,7 +108,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
Money cumulative = Money.zero(currency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isFeeCharge()) {
|
||||
if (loanCharge.isFeeCharge() && !loanCharge.isDueAtDisbursement()) {
|
||||
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||
if (loanChargePerInstallment != null) {
|
||||
@@ -129,7 +129,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
Money cumulative = Money.zero(currency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isFeeCharge()) {
|
||||
if (loanCharge.isFeeCharge() && !loanCharge.isDueAtDisbursement()) {
|
||||
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||
if (loanChargePerInstallment != null) {
|
||||
|
||||
+10
-7
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
@@ -192,7 +193,7 @@ public final class LoanSummary {
|
||||
|
||||
public void updateSummary(final MonetaryCurrency currency, final Money principal,
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LoanSummaryWrapper summaryWrapper,
|
||||
final Boolean disbursed) {
|
||||
final Boolean disbursed, Set<LoanCharge> charges) {
|
||||
|
||||
this.totalPrincipalDisbursed = principal.getAmount();
|
||||
this.totalPrincipalRepaid = summaryWrapper.calculateTotalPrincipalRepaid(repaymentScheduleInstallments, currency).getAmount();
|
||||
@@ -214,13 +215,15 @@ public final class LoanSummary {
|
||||
this.totalFeeChargesDueAtDisbursement);
|
||||
this.totalFeeChargesCharged = totalFeeChargesCharged.getAmount();
|
||||
|
||||
Money totalFeeChargesRepaid = summaryWrapper.calculateTotalFeeChargesRepaid(repaymentScheduleInstallments, currency);
|
||||
if (disbursed) {
|
||||
totalFeeChargesRepaid = totalFeeChargesRepaid.plus(this.totalFeeChargesDueAtDisbursement);
|
||||
Money totalFeeChargesRepaidAtDisbursement = summaryWrapper.calculateTotalChargesRepaidAtDisbursement(charges,currency);
|
||||
this.totalFeeChargesRepaid = totalFeeChargesRepaidAtDisbursement.getAmount();
|
||||
|
||||
if(charges != null) {
|
||||
this.totalFeeChargesWaived = summaryWrapper.calculateTotalFeeChargesWaived(charges, currency).getAmount();
|
||||
}else {
|
||||
this.totalFeeChargesWaived = BigDecimal.ZERO;
|
||||
}
|
||||
this.totalFeeChargesRepaid = totalFeeChargesRepaid.getAmount();
|
||||
|
||||
this.totalFeeChargesWaived = summaryWrapper.calculateTotalFeeChargesWaived(repaymentScheduleInstallments, currency).getAmount();
|
||||
|
||||
this.totalFeeChargesWrittenOff = summaryWrapper.calculateTotalFeeChargesWrittenOff(repaymentScheduleInstallments, currency)
|
||||
.getAmount();
|
||||
|
||||
|
||||
+18
-3
@@ -6,6 +6,7 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
@@ -113,11 +114,13 @@ public final class LoanSummaryWrapper {
|
||||
return total;
|
||||
}
|
||||
|
||||
public Money calculateTotalFeeChargesWaived(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
|
||||
public Money calculateTotalFeeChargesWaived(Set<LoanCharge> charges,
|
||||
final MonetaryCurrency currency) {
|
||||
Money total = Money.zero(currency);
|
||||
for (final LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
|
||||
total = total.plus(installment.getFeeChargesWaived(currency));
|
||||
for (final LoanCharge charge : charges) {
|
||||
if(charge.isActive()){
|
||||
total = total.plus(charge.getAmountWaived(currency));
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -217,4 +220,16 @@ public final class LoanSummaryWrapper {
|
||||
|
||||
return overdueSince;
|
||||
}
|
||||
|
||||
public Money calculateTotalChargesRepaidAtDisbursement(Set<LoanCharge> charges, MonetaryCurrency currency) {
|
||||
Money total = Money.zero(currency);
|
||||
if(charges == null) return total ;
|
||||
for (final LoanCharge loanCharge : charges) {
|
||||
if (!loanCharge.isPenaltyCharge() && loanCharge.getAmountPaid(currency).isGreaterThanZero()) {
|
||||
total = total.plus(loanCharge.getAmountPaid(currency));
|
||||
}
|
||||
}
|
||||
return total;
|
||||
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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 javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_tranche_charges")
|
||||
public class LoanTrancheCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL, optional = false)
|
||||
@JoinColumn(name = "loan_id", nullable = false)
|
||||
private Loan loan;
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL, optional = false)
|
||||
@JoinColumn(name = "charge_id", nullable = false)
|
||||
private Charge charge;
|
||||
|
||||
LoanTrancheCharge() {
|
||||
|
||||
}
|
||||
|
||||
LoanTrancheCharge(Charge chargeDefinition) {
|
||||
this.charge = chargeDefinition ;
|
||||
}
|
||||
public LoanTrancheCharge(Charge charge, Loan loan) {
|
||||
this.charge = charge;
|
||||
this.loan = loan ;
|
||||
}
|
||||
|
||||
public static LoanTrancheCharge createLoanTrancheCharge(Charge chargeDefinition) {
|
||||
return new LoanTrancheCharge(chargeDefinition) ;
|
||||
}
|
||||
public static LoanTrancheCharge createLoanTrancheChargeWithLoan(Charge chargeDefinition, Loan loan) {
|
||||
return new LoanTrancheCharge(chargeDefinition, loan) ;
|
||||
}
|
||||
|
||||
public Charge getCharge() {
|
||||
return charge ;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name="m_loan_tranche_disbursement_charge")
|
||||
public class LoanTrancheDisbursementCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL, optional = false)
|
||||
@JoinColumn(name = "loan_charge_id", referencedColumnName = "id", nullable = false)
|
||||
private LoanCharge loancharge;
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL, optional = false)
|
||||
@JoinColumn(name="disbursement_detail_id", referencedColumnName = "id", nullable = false)
|
||||
private LoanDisbursementDetails loanDisbursementDetails;
|
||||
|
||||
public LoanTrancheDisbursementCharge(){
|
||||
|
||||
}
|
||||
|
||||
public LoanTrancheDisbursementCharge(final LoanCharge loanCharge, final LoanDisbursementDetails loanDisbursementDetails){
|
||||
this.loancharge = loanCharge;
|
||||
this.loanDisbursementDetails = loanDisbursementDetails;
|
||||
}
|
||||
|
||||
public LoanDisbursementDetails getloanDisbursementDetails(){
|
||||
return this.loanDisbursementDetails;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -327,6 +327,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
if (loanTransaction.isChargePayment()) {
|
||||
feeAmount = feeCharges;
|
||||
}
|
||||
if(unpaidCharge == null) break ; //All are trache charges
|
||||
final Money amountPaidTowardsCharge = unpaidCharge.updatePaidAmountBy(amountRemaining, installmentNumber, feeAmount);
|
||||
if (!amountPaidTowardsCharge.isZero()) {
|
||||
Set<LoanChargePaidBy> chargesPaidBies = loanTransaction.getLoanChargesPaid();
|
||||
|
||||
+9
-3
@@ -63,7 +63,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
// 3. setup variables for tracking important facts required for loan
|
||||
// schedule generation.
|
||||
Money principalDisbursed = loanApplicationTerms.getPrincipal();
|
||||
Money principalDisbursed ;
|
||||
|
||||
if(loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) {
|
||||
principalDisbursed = loanApplicationTerms.getApprovedPrincipal();
|
||||
}else {
|
||||
principalDisbursed = loanApplicationTerms.getPrincipal();
|
||||
}
|
||||
final MonetaryCurrency currency = principalDisbursed.getCurrency();
|
||||
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
|
||||
|
||||
@@ -482,7 +488,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap), compoundingMap,
|
||||
periodStartDateApplicableForInterest, scheduledDueDate, daysInPeriodApplicableForInterest);
|
||||
|
||||
if (loanApplicationTerms.getFixedEmiAmount() != null
|
||||
if (loanApplicationTerms.getFixedEmiAmount() != null
|
||||
&& loanApplicationTerms.getFixedEmiAmount().compareTo(principalInterestForThisPeriod.interest().getAmount()) != 1) {
|
||||
String errorMsg = "EMI amount must be greater than : " + principalInterestForThisPeriod.interest().getAmount();
|
||||
throw new MultiDisbursementEmiAmountException(errorMsg, principalInterestForThisPeriod.interest().getAmount(),
|
||||
@@ -1644,7 +1650,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isFeeCharge()) {
|
||||
if (!loanCharge.isDueAtDisbursement() && loanCharge.isFeeCharge()) {
|
||||
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||
cumulative = calculateInstallmentCharge(principalInterestForThisPeriod, numberOfRepayments, cumulative, loanCharge);
|
||||
} else if (loanCharge.isOverdueInstallmentCharge()
|
||||
|
||||
+17
-11
@@ -148,6 +148,8 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy;
|
||||
|
||||
private Money approvedPrincipal = null ;
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType,
|
||||
@@ -164,7 +166,7 @@ public final class LoanApplicationTerms {
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar) {
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar, BigDecimal approvedAmount) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
||||
@@ -176,7 +178,7 @@ public final class LoanApplicationTerms {
|
||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar);
|
||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -191,7 +193,7 @@ public final class LoanApplicationTerms {
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod) {
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount) {
|
||||
final Calendar loanCalendar = null;
|
||||
|
||||
return assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDay, dayOfWeek,
|
||||
@@ -199,7 +201,7 @@ public final class LoanApplicationTerms {
|
||||
loanProductRelatedDetail, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
|
||||
interestChargedFromDate, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType,
|
||||
restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
loanPreClosureInterestCalculationStrategy, rescheduleStrategyMethod, loanCalendar);
|
||||
loanPreClosureInterestCalculationStrategy, rescheduleStrategyMethod, loanCalendar, approvedAmount);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -214,7 +216,7 @@ public final class LoanApplicationTerms {
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar) {
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -246,7 +248,7 @@ public final class LoanApplicationTerms {
|
||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar);
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -259,7 +261,7 @@ public final class LoanApplicationTerms {
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType, final BigDecimal principalThresholdForLastInstalment,
|
||||
final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy) {
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, BigDecimal approvedAmount) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -298,11 +300,11 @@ public final class LoanApplicationTerms {
|
||||
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
|
||||
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar);
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||
final LoanApplicationTerms applicationTerms) {
|
||||
final LoanApplicationTerms applicationTerms, BigDecimal approvedAmount) {
|
||||
return new LoanApplicationTerms(applicationTerms.currency, applicationTerms.loanTermFrequency,
|
||||
applicationTerms.loanTermPeriodFrequencyType, applicationTerms.numberOfRepayments, repaymentEvery,
|
||||
repaymentPeriodFrequencyType, null, null, applicationTerms.amortizationMethod, applicationTerms.interestMethod,
|
||||
@@ -319,7 +321,7 @@ public final class LoanApplicationTerms {
|
||||
applicationTerms.recalculationFrequencyType, applicationTerms.compoundingCalendarInstance,
|
||||
applicationTerms.compoundingFrequencyType, applicationTerms.principalThresholdForLastInstalment,
|
||||
applicationTerms.installmentAmountInMultiplesOf, applicationTerms.preClosureInterestCalculationStrategy,
|
||||
applicationTerms.loanCalendar);
|
||||
applicationTerms.loanCalendar, approvedAmount);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -339,7 +341,7 @@ public final class LoanApplicationTerms {
|
||||
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar) {
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar, BigDecimal approvedAmount) {
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
||||
@@ -388,6 +390,7 @@ public final class LoanApplicationTerms {
|
||||
this.preClosureInterestCalculationStrategy = preClosureInterestCalculationStrategy;
|
||||
|
||||
this.loanCalendar = loanCalendar;
|
||||
this.approvedPrincipal = Money.of(principal.getCurrency(), approvedAmount) ;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -1043,6 +1046,9 @@ public final class LoanApplicationTerms {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
public Money getApprovedPrincipal() {
|
||||
return this.approvedPrincipal ;
|
||||
}
|
||||
public List<DisbursementData> getDisbursementDatas() {
|
||||
return this.disbursementDatas;
|
||||
}
|
||||
|
||||
+6
-5
@@ -52,6 +52,7 @@ import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -303,7 +304,7 @@ public class LoanScheduleAssembler {
|
||||
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData,
|
||||
graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType,
|
||||
restCalendarInstance, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar);
|
||||
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
private CalendarInstance createInterestRecalculationCalendarInstance(final LocalDate calendarStartDate,
|
||||
@@ -353,7 +354,7 @@ public class LoanScheduleAssembler {
|
||||
.isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) {
|
||||
principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal();
|
||||
}
|
||||
disbursementDatas.add(new DisbursementData(null, expectedDisbursementDate, null, principal));
|
||||
disbursementDatas.add(new DisbursementData(null, expectedDisbursementDate, null, principal, null, null));
|
||||
i++;
|
||||
} while (i < disbursementDataArray.size());
|
||||
}
|
||||
@@ -434,13 +435,13 @@ public class LoanScheduleAssembler {
|
||||
validateDisbursementDateIsOnNonWorkingDay(loanApplicationTerms.getExpectedDisbursementDate(), workingDays);
|
||||
validateDisbursementDateIsOnHoliday(loanApplicationTerms.getExpectedDisbursementDate(), isHolidayEnabled, holidays);
|
||||
|
||||
return assembleLoanScheduleFrom(loanApplicationTerms, isHolidayEnabled, holidays, workingDays, element);
|
||||
return assembleLoanScheduleFrom(loanApplicationTerms, isHolidayEnabled, holidays, workingDays, element, null);
|
||||
}
|
||||
|
||||
public LoanScheduleModel assembleLoanScheduleFrom(final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final JsonElement element) {
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final JsonElement element, Set<LoanDisbursementDetails> disbursementDetails) {
|
||||
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element);
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element,disbursementDetails);
|
||||
|
||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||
|
||||
|
||||
+1
-1
@@ -372,7 +372,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
waiveLoanCharges(loan, waiveLoanCharges);
|
||||
|
||||
// update the Loan summary
|
||||
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true);
|
||||
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true, null);
|
||||
|
||||
// update the total number of schedule repayments
|
||||
loan.updateNumberOfRepayments(periods.size());
|
||||
|
||||
+12
-4
@@ -43,6 +43,7 @@ import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarRepository;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarType;
|
||||
import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
@@ -63,6 +64,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateM
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
@@ -484,15 +486,21 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
LoanChargeData chargeData = new LoanChargeData(charge.getId(), charge.getDueLocalDate(), charge.amountOrPercentage());
|
||||
chargesMap.put(charge.getId(), chargeData);
|
||||
}
|
||||
Set<LoanDisbursementDetails> disbursementDetails = this.loanAssembler.fetchDisbursementData(command.parsedJson().getAsJsonObject());
|
||||
|
||||
/**
|
||||
* Stores all charges which are passed in during modify loan
|
||||
* application
|
||||
**/
|
||||
final Set<LoanCharge> possiblyModifedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson());
|
||||
final Set<LoanCharge> possiblyModifedLoanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson(),disbursementDetails);
|
||||
/** Boolean determines if any charge has been modified **/
|
||||
boolean isChargeModified = false;
|
||||
|
||||
Set<Charge> newTrancheChages = this.loanChargeAssembler.getNewLoanTrancheCharges(command.parsedJson()) ;
|
||||
for (Charge charge : newTrancheChages) {
|
||||
existingLoanApplication.addTrancheLoanCharge(charge) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are any charges already present, which are now not
|
||||
* passed in as a part of the request, deem the charges as modified
|
||||
@@ -621,7 +629,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final Set<LoanCollateral> loanCollateral = this.loanCollateralAssembler.fromParsedJson(command.parsedJson());
|
||||
existingLoanApplication.updateLoanCollateral(loanCollateral);
|
||||
}
|
||||
|
||||
|
||||
if (changes.containsKey("recalculateLoanSchedule")) {
|
||||
changes.remove("recalculateLoanSchedule");
|
||||
|
||||
@@ -632,7 +640,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
existingLoanApplication.updateLoanSchedule(loanSchedule, currentUser);
|
||||
existingLoanApplication.recalculateAllCharges();
|
||||
}
|
||||
|
||||
|
||||
final String chargesParamName = "charges";
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
existingLoanApplication.updateLoanCharges(possiblyModifedLoanCharges);
|
||||
@@ -872,7 +880,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
// If loan approved amount less than loan demanded amount, then need
|
||||
// to recompute the schedule
|
||||
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName)) {
|
||||
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName) || changes.containsKey("recalculateLoanSchedule")) {
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanAccountDomainService.buildScheduleGeneratorDTO(loan);
|
||||
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||
}
|
||||
|
||||
+26
-25
@@ -172,8 +172,30 @@ public class LoanAssembler {
|
||||
if (loanPurposeId != null) {
|
||||
loanPurpose = this.codeValueRepository.findOneWithNotFoundDetection(loanPurposeId);
|
||||
}
|
||||
Set<LoanDisbursementDetails> disbursementDetails = null;
|
||||
BigDecimal fixedEmiAmount = null;
|
||||
if (loanProduct.isMultiDisburseLoan() || loanProduct.canDefineInstallmentAmount()) {
|
||||
fixedEmiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element);
|
||||
}
|
||||
BigDecimal maxOutstandingLoanBalance = null;
|
||||
if (loanProduct.isMultiDisburseLoan()) {
|
||||
disbursementDetails = fetchDisbursementData(element.getAsJsonObject());
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
||||
maxOutstandingLoanBalance = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
element, locale);
|
||||
if (disbursementDetails.isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details must be provided";
|
||||
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
|
||||
if (disbursementDetails.size() > loanProduct.maxTrancheCount()) {
|
||||
final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
|
||||
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
|
||||
loanProduct.maxTrancheCount(), disbursementDetails.size());
|
||||
}
|
||||
}
|
||||
final Set<LoanCollateral> collateral = this.loanCollateralAssembler.fromParsedJson(element);
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element);
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element,disbursementDetails);
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (!loanProduct.hasCurrencyCodeOf(loanCharge.currencyCode())) {
|
||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||
@@ -197,28 +219,7 @@ public class LoanAssembler {
|
||||
final String loanTypeParameterName = "loanType";
|
||||
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
|
||||
final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeStr);
|
||||
Set<LoanDisbursementDetails> disbursementDetails = null;
|
||||
BigDecimal fixedEmiAmount = null;
|
||||
if (loanProduct.isMultiDisburseLoan() || loanProduct.canDefineInstallmentAmount()) {
|
||||
fixedEmiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element);
|
||||
}
|
||||
BigDecimal maxOutstandingLoanBalance = null;
|
||||
if (loanProduct.isMultiDisburseLoan()) {
|
||||
disbursementDetails = fetchDisbursementData(element.getAsJsonObject());
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
||||
maxOutstandingLoanBalance = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
element, locale);
|
||||
if (disbursementDetails.isEmpty()) {
|
||||
final String errorMessage = "For this loan product, disbursement details must be provided";
|
||||
throw new MultiDisbursementDataRequiredException(LoanApiConstants.disbursementDataParameterName, errorMessage);
|
||||
}
|
||||
|
||||
if (disbursementDetails.size() > loanProduct.maxTrancheCount()) {
|
||||
final String errorMessage = "Number of tranche shouldn't be greter than " + loanProduct.maxTrancheCount();
|
||||
throw new ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, errorMessage,
|
||||
loanProduct.maxTrancheCount(), disbursementDetails.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (clientId != null) {
|
||||
client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
@@ -281,7 +282,7 @@ public class LoanAssembler {
|
||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||
final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays, element);
|
||||
isHolidayEnabled, holidays, workingDays, element,disbursementDetails);
|
||||
loanApplication.loanApplicationSubmittal(currentUser, loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine(),
|
||||
submittedOnDate, externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay,
|
||||
recalculationRestFrequencyDate, recalculationCompoundingFrequencyDate);
|
||||
@@ -289,7 +290,7 @@ public class LoanAssembler {
|
||||
return loanApplication;
|
||||
}
|
||||
|
||||
private Set<LoanDisbursementDetails> fetchDisbursementData(final JsonObject command) {
|
||||
public Set<LoanDisbursementDetails> fetchDisbursementData(final JsonObject command) {
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(command);
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(command);
|
||||
Set<LoanDisbursementDetails> disbursementDatas = new HashSet<>();
|
||||
|
||||
+139
-12
@@ -6,7 +6,9 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -19,8 +21,15 @@ import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTrancheCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTrancheDisbursementCharge;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -34,21 +43,49 @@ public class LoanChargeAssembler {
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final LoanChargeRepository loanChargeRepository;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanChargeAssembler(final FromJsonHelper fromApiJsonHelper, final ChargeRepositoryWrapper chargeRepository,
|
||||
final LoanChargeRepository loanChargeRepository) {
|
||||
final LoanChargeRepository loanChargeRepository, final LoanProductRepository loanProductRepository) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.loanChargeRepository = loanChargeRepository;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
}
|
||||
|
||||
public Set<LoanCharge> fromParsedJson(final JsonElement element) {
|
||||
|
||||
public Set<LoanCharge> fromParsedJson(final JsonElement element, Set<LoanDisbursementDetails> disbursementDetails) {
|
||||
JsonArray jsonDisbursement = this.fromApiJsonHelper.extractJsonArrayNamed("disbursementData", element) ;
|
||||
List<Long> disbursementChargeIds = new ArrayList<>();
|
||||
|
||||
if(jsonDisbursement != null && jsonDisbursement.size() > 0) {
|
||||
for(int i = 0 ; i < jsonDisbursement.size(); i++) {
|
||||
final JsonObject jsonObject = jsonDisbursement.get(i).getAsJsonObject();
|
||||
if(jsonObject != null && jsonObject.getAsJsonPrimitive(LoanApiConstants.loanChargeIdParameterName) != null){
|
||||
String chargeIds = jsonObject.getAsJsonPrimitive(LoanApiConstants.loanChargeIdParameterName).getAsString();
|
||||
if(chargeIds != null) {
|
||||
if (chargeIds.indexOf(",") != -1) {
|
||||
String[] chargeId = chargeIds.split(",");
|
||||
for (String loanChargeId : chargeId) {
|
||||
disbursementChargeIds.add(Long.parseLong(loanChargeId));
|
||||
}
|
||||
} else {
|
||||
disbursementChargeIds.add(Long.parseLong(chargeIds)) ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Set<LoanCharge> loanCharges = new HashSet<>();
|
||||
|
||||
final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
|
||||
final Integer numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
|
||||
final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
|
||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||
final boolean isMultiDisbursal = loanProduct.isMultiDisburseLoan();
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
@@ -92,17 +129,85 @@ public class LoanChargeAssembler {
|
||||
if (chargePaymentMode != null) {
|
||||
chargePaymentModeEnum = ChargePaymentMode.fromInt(chargePaymentMode);
|
||||
}
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
|
||||
chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
if (!isMultiDisbursal) {
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
|
||||
chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
}
|
||||
if (topLevelJsonElement.has("disbursementData") && topLevelJsonElement.get("disbursementData").isJsonArray()) {
|
||||
final JsonArray disbursementArray = topLevelJsonElement.get("disbursementData").getAsJsonArray();
|
||||
JsonObject disbursementDataElement = disbursementArray.get(0).getAsJsonObject();
|
||||
expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||
LoanApiConstants.disbursementDateParameterName, disbursementDataElement, dateFormat, locale);
|
||||
}
|
||||
|
||||
if ((chargeDefinition.isPercentageOfDisbursementAmount() || chargeDefinition.isPercentageOfApprovedAmount() || chargeDefinition
|
||||
.getChargeCalculation() == ChargeCalculationType.FLAT.getValue()) && disbursementDetails != null) {
|
||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
||||
for (LoanDisbursementDetails disbursementDetail : disbursementDetails) {
|
||||
if (chargeDefinition.getChargeTimeType() == ChargeTimeType.DISBURSEMENT.getValue()) {
|
||||
if (chargeDefinition.isPercentageOfApprovedAmount()
|
||||
&& disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate)) {
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount,
|
||||
chargeTime, chargeCalculation, dueDate, chargePaymentModeEnum, numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
|
||||
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
|
||||
} else {
|
||||
if (disbursementDetail.expectedDisbursementDateAsLocalDate().equals(expectedDisbursementDate)) {
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition,
|
||||
disbursementDetail.principal(), amount, chargeTime, chargeCalculation,
|
||||
disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum,
|
||||
numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
|
||||
disbursementDetail);
|
||||
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (chargeDefinition.getChargeTimeType() == ChargeTimeType.TRANCHE_DISBURSEMENT.getValue()) {
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition,
|
||||
disbursementDetail.principal(), amount, chargeTime, chargeCalculation,
|
||||
disbursementDetail.expectedDisbursementDateAsLocalDate(), chargePaymentModeEnum,
|
||||
numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
|
||||
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
||||
if (disbursementDetails != null) {
|
||||
for (LoanDisbursementDetails disbursementDetail : disbursementDetails) {
|
||||
if (!(disbursementDetail.expectedDisbursementDateAsLocalDate().isAfter(expectedDisbursementDate))) {
|
||||
if (chargeDefinition.getChargeCalculation() == ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST
|
||||
.getValue()
|
||||
|| chargeDefinition.getChargeTimeType() == ChargeTimeType.SPECIFIED_DUE_DATE.getValue()
|
||||
|| chargeDefinition.getChargeCalculation() == ChargeCalculationType.PERCENT_OF_INTEREST
|
||||
.getValue()) {
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal,
|
||||
amount, chargeTime, chargeCalculation, dueDate, chargePaymentModeEnum,
|
||||
numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge,
|
||||
disbursementDetail);
|
||||
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final Long loanChargeId = id;
|
||||
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
|
||||
if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
|
||||
|
||||
loanCharge.update(amount, dueDate, numberOfRepayments);
|
||||
|
||||
loanCharges.add(loanCharge);
|
||||
if(disbursementChargeIds.contains(loanChargeId) && loanCharge == null) {
|
||||
//throw new LoanChargeNotFoundException(loanChargeId);
|
||||
}
|
||||
if(loanCharge != null) {
|
||||
loanCharge.update(amount, dueDate, numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,4 +215,26 @@ public class LoanChargeAssembler {
|
||||
|
||||
return loanCharges;
|
||||
}
|
||||
|
||||
public Set<Charge> getNewLoanTrancheCharges(final JsonElement element) {
|
||||
final Set<Charge> associatedChargesForLoan = new HashSet<>();
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
|
||||
final Long id = this.fromApiJsonHelper.extractLongNamed("id", loanChargeElement);
|
||||
final Long chargeId = this.fromApiJsonHelper.extractLongNamed("chargeId", loanChargeElement);
|
||||
if (id == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId);
|
||||
if (chargeDefinition.getChargeTimeType() == ChargeTimeType.TRANCHE_DISBURSEMENT.getValue()) {
|
||||
associatedChargesForLoan.add(chargeDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return associatedChargesForLoan;
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -64,9 +64,11 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
+ "lc.charge_payment_mode_enum as chargePaymentMode, " + "lc.is_paid_derived as paid, " + "lc.waived as waied, "
|
||||
+ "lc.min_cap as minCap, lc.max_cap as maxCap, " + "lc.charge_amount_or_percentage as amountOrPercentage, "
|
||||
+ "c.currency_code as currencyCode, oc.name as currencyName, "
|
||||
+ "date(ifnull(dd.disbursedon_date,dd.expected_disburse_date)) as disbursementDate, "
|
||||
+ "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode from m_charge c "
|
||||
+ "join m_organisation_currency oc on c.currency_code = oc.code " + "join m_loan_charge lc on lc.charge_id = c.id ";
|
||||
+ "join m_organisation_currency oc on c.currency_code = oc.code " + "join m_loan_charge lc on lc.charge_id = c.id "
|
||||
+ "left join m_loan_tranche_disbursement_charge dc on dc.loan_charge_id=lc.id left join m_loan_disbursement_detail dd on dd.id=dc.disbursement_detail_id ";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,7 +99,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final int chargeTime = rs.getInt("chargeTime");
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||
|
||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
|
||||
final int chargeCalculation = rs.getInt("chargeCalculation");
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
|
||||
@@ -110,6 +112,11 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final BigDecimal minCap = rs.getBigDecimal("minCap");
|
||||
final BigDecimal maxCap = rs.getBigDecimal("maxCap");
|
||||
final BigDecimal amountOrPercentage = rs.getBigDecimal("amountOrPercentage");
|
||||
final LocalDate disbursementDate = JdbcSupport.getLocalDate(rs, "disbursementDate");
|
||||
|
||||
if (disbursementDate != null) {
|
||||
dueAsOfDate = disbursementDate;
|
||||
}
|
||||
|
||||
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
|
||||
chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty, paymentMode, paid,
|
||||
@@ -161,7 +168,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final LoanChargeMapper rm = new LoanChargeMapper();
|
||||
|
||||
final String sql = "select " + rm.schema() + " where lc.loan_id=? AND lc.is_active = 1"
|
||||
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
|
||||
+ " order by ifnull(lc.due_for_collection_as_of_date,date(ifnull(dd.disbursedon_date,dd.expected_disburse_date))),lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
|
||||
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
|
||||
}
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
@@ -30,7 +31,7 @@ public interface LoanReadPlatformService {
|
||||
LoanAccountData retrieveOne(Long loanId);
|
||||
|
||||
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData,
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled);
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, BigDecimal totalPaidFeeCharges);
|
||||
|
||||
Collection<LoanTransactionData> retrieveLoanTransactions(Long loanId);
|
||||
|
||||
|
||||
+47
-20
@@ -207,13 +207,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
@Override
|
||||
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
|
||||
boolean isInterestRecalculationEnabled) {
|
||||
boolean isInterestRecalculationEnabled, BigDecimal totalPaidFeeCharges) {
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
|
||||
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
|
||||
repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled);
|
||||
repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled ,totalPaidFeeCharges);
|
||||
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
|
||||
|
||||
return this.jdbcTemplate.query(sql, fullResultsetExtractor, new Object[] { loanId });
|
||||
@@ -954,9 +954,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
private LocalDate lastDueDate;
|
||||
private BigDecimal outstandingLoanPrincipalBalance;
|
||||
private boolean excludePastUndisbursed;
|
||||
|
||||
private BigDecimal totalPaidFeeCharges ;
|
||||
public LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled) {
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, BigDecimal totalPaidFeeCharges) {
|
||||
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
|
||||
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
|
||||
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
|
||||
@@ -964,6 +964,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
this.outstandingLoanPrincipalBalance = this.disbursement.amount();
|
||||
this.disbursementData = disbursementData;
|
||||
this.excludePastUndisbursed = isInterestRecalculationEnabled;
|
||||
this.totalPaidFeeCharges = totalPaidFeeCharges ;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
@@ -1014,7 +1015,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
// update totals with details of fees charged during disbursement
|
||||
totalFeeChargesCharged = totalFeeChargesCharged.plus(disbursementPeriod.feeChargesDue());
|
||||
totalRepaymentExpected = totalRepaymentExpected.plus(disbursementPeriod.feeChargesDue());
|
||||
totalRepayment = totalRepayment.plus(disbursementPeriod.feeChargesPaid());
|
||||
totalRepayment = totalRepayment.plus(totalPaidFeeCharges);
|
||||
totalOutstanding = totalOutstanding.plus(disbursementPeriod.feeChargesDue()).minus(disbursementPeriod.feeChargesPaid());
|
||||
|
||||
Integer loanTermInDays = Integer.valueOf(0);
|
||||
@@ -1031,17 +1032,29 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
for (DisbursementData data : disbursementData) {
|
||||
if (fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate)) {
|
||||
principal = principal.add(data.amount());
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), this.totalFeeChargesDueAtDisbursement, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
if(data.getChargeAmount() == null){
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
}else{
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), data.getChargeAmount(), data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
}
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
|
||||
} else if (data.isDueForDisbursement(fromDate, dueDate)) {
|
||||
if (!excludePastUndisbursed
|
||||
|| (excludePastUndisbursed && (data.isDisbursed() || !data.disbursementDate().isBefore(LocalDate.now())))) {
|
||||
principal = principal.add(data.amount());
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
if(data.getChargeAmount() == null){
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
}else{
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), data.getChargeAmount(), data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
}
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
|
||||
}
|
||||
}
|
||||
@@ -1329,8 +1342,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final Collection<CodeValueData> loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose");
|
||||
final Collection<CodeValueData> loanCollateralOptions = this.codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode("LoanCollateral");
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanProductApplicableCharges(productId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
|
||||
Collection<ChargeData> chargeOptions = null;
|
||||
if(loanProduct.getMultiDisburseLoan()){
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanProductApplicableCharges(productId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
|
||||
}else{
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanProductApplicableCharges(productId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT, ChargeTimeType.TRANCHE_DISBURSEMENT });
|
||||
}
|
||||
|
||||
Integer loanCycleCounter = null;
|
||||
if (loanProduct.useBorrowerCycle()) {
|
||||
if (clientId == null) {
|
||||
@@ -1441,17 +1461,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<DisbursementData> retrieveLoanDisbursementDetails(final Long loanId) {
|
||||
public Collection<DisbursementData> retrieveLoanDisbursementDetails
|
||||
(final Long loanId) {
|
||||
final LoanDisbursementDetailMapper rm = new LoanDisbursementDetailMapper();
|
||||
final String sql = "select " + rm.schema() + " where dd.loan_id=? order by dd.expected_disburse_date";
|
||||
final String sql = "select " + rm.schema() + " where dd.loan_id=? group by dd.id order by dd.expected_disburse_date";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
|
||||
}
|
||||
|
||||
private static final class LoanDisbursementDetailMapper implements RowMapper<DisbursementData> {
|
||||
|
||||
public String schema() {
|
||||
return "dd.id as id,dd.expected_disburse_date as expectedDisbursementdate,dd.disbursedon_date as actualDisbursementdate,dd.principal as principal "
|
||||
+ "from m_loan_disbursement_detail dd";
|
||||
return "dd.id as id,dd.expected_disburse_date as expectedDisbursementdate, dd.disbursedon_date as actualDisbursementdate,dd.principal as principal,sum(lc.amount) chargeAmount, lc.amount_waived_derived waivedAmount,group_concat(lc.id) loanChargeId "+
|
||||
"from m_loan l inner join m_loan_disbursement_detail dd on dd.loan_id = l.id left join m_loan_tranche_disbursement_charge tdc on tdc.disbursement_detail_id=dd.id " +
|
||||
"left join m_loan_charge lc on lc.id=tdc.loan_charge_id and lc.is_active=1";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1460,8 +1482,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final LocalDate expectedDisbursementdate = JdbcSupport.getLocalDate(rs, "expectedDisbursementdate");
|
||||
final LocalDate actualDisbursementdate = JdbcSupport.getLocalDate(rs, "actualDisbursementdate");
|
||||
final BigDecimal principal = rs.getBigDecimal("principal");
|
||||
|
||||
final DisbursementData disbursementData = new DisbursementData(id, expectedDisbursementdate, actualDisbursementdate, principal);
|
||||
final String loanChargeId = rs.getString("loanChargeId");
|
||||
BigDecimal chargeAmount = rs.getBigDecimal("chargeAmount");
|
||||
final BigDecimal waivedAmount = rs.getBigDecimal("waivedAmount") ;
|
||||
if(chargeAmount != null && waivedAmount != null)
|
||||
chargeAmount = chargeAmount.subtract(waivedAmount) ;
|
||||
final DisbursementData disbursementData = new DisbursementData(id, expectedDisbursementdate, actualDisbursementdate, principal, loanChargeId,
|
||||
chargeAmount);
|
||||
return disbursementData;
|
||||
}
|
||||
|
||||
|
||||
+51
-12
@@ -76,6 +76,7 @@ import org.mifosplatform.portfolio.calendar.exception.CalendarParameterUpdateNot
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.charge.exception.ChargeCannotBeUpdatedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
|
||||
@@ -120,6 +121,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInsta
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTrancheDisbursementCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
@@ -1301,6 +1303,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.build();
|
||||
}
|
||||
|
||||
private void validateAddingNewChargeAllowed(Set<LoanDisbursementDetails> loanDisburseDetails) {
|
||||
boolean pendingDisbursementAvailable = false;
|
||||
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
|
||||
if (disbursementDetail.actualDisbursementDate() == null) {
|
||||
pendingDisbursementAvailable = true ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
if(!pendingDisbursementAvailable) {
|
||||
throw new ChargeCannotBeUpdatedException(
|
||||
"error.msg.charge.cannot.be.updated.no.pending.disbursements.in.loan", "This charge cannot be added, No disbursement is pending");
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult addLoanCharge(final Long loanId, final JsonCommand command) {
|
||||
@@ -1310,19 +1326,42 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
|
||||
Set<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails();
|
||||
final Long chargeDefinitionId = command.longValueOfParameterNamed("chargeId");
|
||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId);
|
||||
|
||||
final LoanCharge loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command);
|
||||
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE,
|
||||
constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));
|
||||
|
||||
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
|
||||
|
||||
if(loan.isDisbursed() && chargeDefinition.isDisbursementCharge()) {
|
||||
validateAddingNewChargeAllowed(loanDisburseDetails) ; //validates whether any pending disbursements are available to apply this charge
|
||||
}
|
||||
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
|
||||
|
||||
boolean isAppliedOnBackDate = false;
|
||||
LoanCharge loanCharge = null;
|
||||
if (chargeDefinition.isPercentageOfDisbursementAmount()) {
|
||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
||||
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
|
||||
if (disbursementDetail.actualDisbursementDate() == null) {
|
||||
loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(), null, null, null,
|
||||
disbursementDetail.expectedDisbursementDateAsLocalDate(), null, null);
|
||||
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
|
||||
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
|
||||
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE,
|
||||
constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));
|
||||
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
|
||||
isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||
}
|
||||
}
|
||||
loan.addTrancheLoanCharge(chargeDefinition);
|
||||
} else {
|
||||
loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command);
|
||||
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE,
|
||||
constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge));
|
||||
|
||||
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
|
||||
isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||
}
|
||||
|
||||
boolean isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||
boolean reprocessRequired = true;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
||||
@@ -1331,7 +1370,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
updateOriginalSchedule(loan);
|
||||
}
|
||||
|
||||
if (reprocessRequired) {
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
@@ -1480,10 +1518,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
this.loanChargeRepository.save(loanCharge);
|
||||
|
||||
loan.addLoanCharge(loanCharge);
|
||||
|
||||
this.loanChargeRepository.save(loanCharge);
|
||||
|
||||
/**
|
||||
* we want to apply charge transactions only for those loans charges
|
||||
* that are applied when a loan is active and the loan product uses
|
||||
@@ -2349,7 +2387,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas) {
|
||||
@@ -2652,10 +2689,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
changedTransactionDetail = loan.updateDisbursementDateAndAmountForTranche(loanDisbursementDetails, command,
|
||||
existingTransactionIds, existingReversedTransactionIds, changes, scheduleGeneratorDTO, currentUser);
|
||||
} else {
|
||||
// BigDecimal setAmount = loan.getApprovedPrincipal();
|
||||
Collection<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails();
|
||||
BigDecimal setAmount = BigDecimal.ZERO;
|
||||
for (LoanDisbursementDetails details : loanDisburseDetails) {
|
||||
if (details.actualDisbursementDate() != null) {
|
||||
if (details.expectedDisbursementDate() != null) {
|
||||
setAmount = setAmount.add(details.principal());
|
||||
}
|
||||
}
|
||||
@@ -2666,6 +2704,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
|
||||
} else {
|
||||
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||
loan.processPostDisbursementTransactions() ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -19,6 +19,7 @@ import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
@@ -83,7 +84,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@ManyToMany
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
|
||||
private List<Charge> charges;
|
||||
|
||||
@@ -602,6 +603,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
public Integer getAccountingType() {
|
||||
return this.accountingRule;
|
||||
}
|
||||
|
||||
public List<Charge> getLoanProductCharges(){
|
||||
return this.charges;
|
||||
}
|
||||
|
||||
public void update(final LoanProductConfigurableAttributes loanConfigurableAttributes) {
|
||||
this.loanConfigurableAttributes = loanConfigurableAttributes;
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
|
||||
this.savingsAccount = savingsAccount;
|
||||
this.charge = chargeDefinition;
|
||||
this.penaltyCharge = chargeDefinition.isPenalty();
|
||||
this.chargeTime = (chargeTime == null) ? chargeDefinition.getChargeTime() : chargeTime.getValue();
|
||||
this.chargeTime = (chargeTime == null) ? chargeDefinition.getChargeTimeType() : chargeTime.getValue();
|
||||
|
||||
if (isOnSpecifiedDueDate()) {
|
||||
if (dueDate == null) {
|
||||
|
||||
+2
-2
@@ -134,8 +134,8 @@ public class SavingsAccountChargeAssembler {
|
||||
Set<Charge> productCharges = savingsProduct.charges();
|
||||
for (Charge charge : productCharges) {
|
||||
ChargeTimeType chargeTime = null;
|
||||
if (charge.getChargeTime() != null) {
|
||||
chargeTime = ChargeTimeType.fromInt(charge.getChargeTime());
|
||||
if (charge.getChargeTimeType() != null) {
|
||||
chargeTime = ChargeTimeType.fromInt(charge.getChargeTimeType());
|
||||
}
|
||||
if (chargeTime != null && chargeTime.isOnSpecifiedDueDate()) {
|
||||
continue;
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
CREATE TABLE `m_loan_tranche_disbursement_charge` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`loan_charge_id` BIGINT(20) NOT NULL,
|
||||
`disbursement_detail_id` BIGINT(20) NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=1
|
||||
;
|
||||
|
||||
ALTER TABLE `m_loan_tranche_disbursement_charge`
|
||||
ADD CONSTRAINT `FK_m_loan_tranche_disbursement_charge_m_loan_charge` FOREIGN KEY (`loan_charge_id`) REFERENCES `m_loan_charge` (`id`),
|
||||
ADD CONSTRAINT `FK_m_loan_tranche_disbursement_charge_m_loan_disbursement_detail` FOREIGN KEY (`disbursement_detail_id`) REFERENCES `m_loan_disbursement_detail` (`id`);
|
||||
|
||||
|
||||
CREATE TABLE `m_loan_tranche_charges` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`loan_id` BIGINT(20) NOT NULL,
|
||||
`charge_id` BIGINT(20) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ;
|
||||
|
||||
ALTER TABLE `m_loan_tranche_charges`
|
||||
ADD CONSTRAINT `FK_m_loan_tranche_charges_m_loan` FOREIGN KEY (`loan_id`) REFERENCES `m_loan` (`id`),
|
||||
ADD CONSTRAINT `FK_m_loan_tranche_charges_m_charge` FOREIGN KEY (`charge_id`) REFERENCES `m_charge` (`id`);
|
||||
Reference in New Issue
Block a user