mifosx-58: handle charges at disbursement for loan creation.
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
ALTER TABLE `m_loan_charge`
|
||||
ADD COLUMN `calculation_percentage` decimal(19,6) DEFAULT NULL AFTER `charge_calculation_enum`;
|
||||
ALTER TABLE `m_loan_charge`
|
||||
ADD COLUMN `calculation_on_amount` decimal(19,6) DEFAULT NULL AFTER `calculation_percentage`;
|
||||
ALTER TABLE `m_loan_charge`
|
||||
ADD COLUMN `amount_paid_derived` decimal(19,6) DEFAULT NULL AFTER `amount`;
|
||||
ALTER TABLE `m_loan_charge`
|
||||
ADD COLUMN `amount_outstanding_derived` decimal(19,6) NOT NULL default 0 AFTER `amount_paid_derived`;
|
||||
ALTER TABLE `m_loan_charge`
|
||||
ADD COLUMN `is_paid_derived` tinyint(1) NOT NULL default 0 AFTER `amount_outstanding_derived`;
|
||||
@@ -214,9 +214,11 @@ public class LoansApiResource {
|
||||
convenienceDataRequired = true;
|
||||
}
|
||||
|
||||
charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId);
|
||||
if (CollectionUtils.isEmpty(charges)) {
|
||||
charges = null;
|
||||
if (responseParameters.contains("charges")) {
|
||||
charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId);
|
||||
if (CollectionUtils.isEmpty(charges)) {
|
||||
charges = null; // set back to null so doesnt appear in JSON is no charges exist.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -67,8 +67,9 @@ public class LoanBasicDetailsData {
|
||||
final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery();
|
||||
final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType();
|
||||
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>();
|
||||
for (ChargeData charge : product.getCharges()){
|
||||
charges.add(new LoanChargeData(null, charge.getId(), charge.getName(), charge.getCurrency(),
|
||||
|
||||
for (ChargeData charge : product.getCharges()) {
|
||||
charges.add(LoanChargeData.newChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(),
|
||||
charge.getAmount(), charge.getChargeTimeType(), charge.getChargeCalculationType()));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,20 +15,41 @@ public class LoanChargeData {
|
||||
@SuppressWarnings("unused")
|
||||
private final String name;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData chargeTimeType;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData chargeCalculationType;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal percentage;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountPercentageAppliedTo;
|
||||
@SuppressWarnings("unused")
|
||||
private final CurrencyData currency;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amount;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData chargeTimeType;
|
||||
private final BigDecimal amountPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData chargeCalculationType;
|
||||
|
||||
private final BigDecimal amountOutstanding;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
|
||||
public static LoanChargeData template(final Collection<ChargeData> chargeOptions){
|
||||
return new LoanChargeData(null,null,null,null,null,null,null, chargeOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* used when populating with details from charge definition (for crud on charges)
|
||||
*/
|
||||
public static LoanChargeData newChargeDetails(
|
||||
final Long chargeId,
|
||||
final String name,
|
||||
final CurrencyData currency,
|
||||
final BigDecimal value,
|
||||
final EnumOptionData chargeTimeType,
|
||||
final EnumOptionData chargeCalculationType) {
|
||||
return new LoanChargeData(null, chargeId, name, currency, value, chargeTimeType, chargeCalculationType, null);
|
||||
}
|
||||
|
||||
public LoanChargeData(
|
||||
final Long id,
|
||||
@@ -36,15 +57,23 @@ public class LoanChargeData {
|
||||
final String name,
|
||||
final CurrencyData currency,
|
||||
final BigDecimal amount,
|
||||
final BigDecimal amountPaid,
|
||||
final BigDecimal amountOutstanding,
|
||||
final EnumOptionData chargeTimeType,
|
||||
final EnumOptionData chargeCalculationType) {
|
||||
final EnumOptionData chargeCalculationType,
|
||||
final BigDecimal percentage,
|
||||
final BigDecimal amountPercentageAppliedTo) {
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
this.name = name;
|
||||
this.currency = currency;
|
||||
this.amount = amount;
|
||||
this.amountPaid = amountPaid;
|
||||
this.amountOutstanding = amountOutstanding;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.percentage = percentage;
|
||||
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
|
||||
|
||||
this.chargeOptions = null;
|
||||
}
|
||||
@@ -63,8 +92,12 @@ public class LoanChargeData {
|
||||
this.name = name;
|
||||
this.currency = currency;
|
||||
this.amount = amount;
|
||||
this.amountPaid = BigDecimal.ZERO;
|
||||
this.amountOutstanding = amount;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.percentage = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.chargeOptions = chargeOptions;
|
||||
}
|
||||
}
|
||||
+31
-19
@@ -195,9 +195,12 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
private static final class LoanChargeMapper implements RowMapper<LoanChargeData> {
|
||||
|
||||
public String loanChargeSchema(){
|
||||
return "lc.id as id, c.id as chargeId, c.name as name, lc.amount as amount, c.currency_code as currencyCode, " +
|
||||
"lc.charge_time_enum as chargeTime, " +
|
||||
"lc.charge_calculation_enum as chargeCalculation, oc.name as currencyName, " +
|
||||
return "lc.id as id, c.id as chargeId, c.name as name, " +
|
||||
"lc.amount as amountDue, lc.amount_paid_derived as amountPaid, lc.amount_outstanding_derived as amountOutstanding, " +
|
||||
"lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " +
|
||||
"lc.charge_time_enum as chargeTime, " +
|
||||
"lc.charge_calculation_enum as chargeCalculation, " +
|
||||
"c.currency_code as currencyCode, oc.name as currencyName, " +
|
||||
"oc.decimal_places as currencyDecimalPlaces, 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 " +
|
||||
@@ -205,28 +208,37 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanChargeData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
Long id = rs.getLong("id");
|
||||
Long chargeId = rs.getLong("chargeId");
|
||||
String name = rs.getString("name");
|
||||
BigDecimal amount = rs.getBigDecimal("amount");
|
||||
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = rs.getLong("id");
|
||||
final Long chargeId = rs.getLong("chargeId");
|
||||
final String name = rs.getString("name");
|
||||
final BigDecimal amount = rs.getBigDecimal("amountDue");
|
||||
BigDecimal amountPaid = rs.getBigDecimal("amountPaid");
|
||||
if (amountPaid == null) {
|
||||
amountPaid = BigDecimal.ZERO;
|
||||
}
|
||||
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
|
||||
|
||||
final BigDecimal percentageOf = rs.getBigDecimal("percentageOf");
|
||||
final BigDecimal amountPercentageAppliedTo = rs.getBigDecimal("amountPercentageAppliedTo");
|
||||
|
||||
String currencyCode = rs.getString("currencyCode");
|
||||
String currencyName = rs.getString("currencyName");
|
||||
String currencyNameCode = rs.getString("currencyNameCode");
|
||||
String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||
Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces");
|
||||
final String currencyCode = rs.getString("currencyCode");
|
||||
final String currencyName = rs.getString("currencyName");
|
||||
final String currencyNameCode = rs.getString("currencyNameCode");
|
||||
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
|
||||
final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces");
|
||||
|
||||
CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces,
|
||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces,
|
||||
currencyDisplaySymbol, currencyNameCode);
|
||||
|
||||
int chargeTime = rs.getInt("chargeTime");
|
||||
EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||
final int chargeTime = rs.getInt("chargeTime");
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||
|
||||
int chargeCalculation = rs.getInt("chargeCalculation");
|
||||
EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
|
||||
final int chargeCalculation = rs.getInt("chargeCalculation");
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
|
||||
|
||||
return new LoanChargeData(id, chargeId, name, currency, amount, chargeTimeType, chargeCalculationType);
|
||||
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountOutstanding, chargeTimeType, chargeCalculationType, percentageOf, amountPercentageAppliedTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,14 +491,17 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
// add repayment transaction to track incoming money from client to mfi for (charges due at time of disbursement)
|
||||
if (getTotalChargesDueAtDisbursement().isGreaterThanZero()) {
|
||||
|
||||
// FIXME - add charges component to 'transactions'
|
||||
LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getTotalChargesDueAtDisbursement(), disbursedOn);
|
||||
Money zero = Money.zero(getCurrency());
|
||||
chargesPayment.updateComponents(zero, zero, zero, getTotalChargesDueAtDisbursement());
|
||||
chargesPayment.updateLoan(this);
|
||||
this.loanTransactions.add(chargesPayment);
|
||||
|
||||
// should also pay-off the time-of-disbursement charges
|
||||
for (LoanCharge charge : this.charges) {
|
||||
if (charge.isDueAtDisbursement()) {
|
||||
charge.markAsFullyPaid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (disbursedOn.isBefore(getApprovedOnDate())) {
|
||||
@@ -515,8 +518,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
"cannot.be.a.future.date", errorMessage, disbursedOn);
|
||||
}
|
||||
|
||||
LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments
|
||||
.get(0).getDueDate();
|
||||
LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments.get(0).getDueDate();
|
||||
if (disbursedOn.isAfter(firstRepaymentDueDate)) {
|
||||
final String errorMessage = "The date on which a loan is disbursed cannot be after the first expected repayment date: "
|
||||
+ firstRepaymentDueDate.toString();
|
||||
@@ -526,14 +528,19 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
}
|
||||
|
||||
public void undoDisbursal(
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public void undoDisbursal(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, LoanStatus.fromInt(this.loanStatus));
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
|
||||
this.loanTransactions.clear();
|
||||
this.disbursedOnDate = null;
|
||||
|
||||
for (LoanCharge charge : this.charges) {
|
||||
if (charge.isDueAtDisbursement()) {
|
||||
charge.resetToOriginal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void waive(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -30,21 +31,41 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable=false)
|
||||
private Charge charge;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "charge_time_enum", nullable = false)
|
||||
private Integer chargeTime;
|
||||
|
||||
@Column(name = "charge_calculation_enum")
|
||||
private Integer chargeCalculation;
|
||||
|
||||
@Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal percentage;
|
||||
|
||||
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal amountPercentageAppliedTo;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal amountPaid;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amountOutstanding;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "is_paid_derived", nullable=false)
|
||||
private boolean paid = false;
|
||||
|
||||
public static LoanCharge createNew(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
|
||||
return new LoanCharge(loan, chargeDefinition, command);
|
||||
}
|
||||
|
||||
public static LoanCharge createNew(final Charge chargeDefinition, final LoanChargeCommand command) {
|
||||
return new LoanCharge(null, chargeDefinition, command);
|
||||
/*
|
||||
* loanPrincipal is required for charges that are percentage based
|
||||
*/
|
||||
public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final LoanChargeCommand command, final BigDecimal loanPrincipal) {
|
||||
return new LoanCharge(chargeDefinition, command, loanPrincipal);
|
||||
}
|
||||
|
||||
public static LoanCharge createNew(final Charge chargeDefinition) {
|
||||
@@ -55,16 +76,36 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
public LoanCharge(final Charge chargeDefinition, final LoanChargeCommand command, final BigDecimal loanPrincipal) {
|
||||
this.loan = null;
|
||||
this.charge = chargeDefinition;
|
||||
|
||||
if (command.isChargeTimeTypeChanged()){
|
||||
this.chargeTime = command.getChargeTimeType();
|
||||
} else {
|
||||
this.chargeTime = chargeDefinition.getChargeTime();
|
||||
}
|
||||
|
||||
if (command.isChargeCalculationTypeChanged()){
|
||||
this.chargeCalculation = command.getChargeCalculationType();
|
||||
} else {
|
||||
this.chargeCalculation = chargeDefinition.getChargeCalculation();
|
||||
}
|
||||
|
||||
BigDecimal chargeAmount = chargeDefinition.getAmount();
|
||||
if (command.isAmountChanged()){
|
||||
chargeAmount = command.getAmount();
|
||||
}
|
||||
|
||||
populateDerivedFields(loanPrincipal, chargeAmount);
|
||||
this.paid = determineIfFullyPaid();
|
||||
}
|
||||
|
||||
private LoanCharge(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
|
||||
this.loan = loan;
|
||||
this.charge = chargeDefinition;
|
||||
|
||||
if (command.isAmountChanged()){
|
||||
this.amount = command.getAmount();
|
||||
} else {
|
||||
this.amount = chargeDefinition.getAmount();
|
||||
}
|
||||
|
||||
if (command.isChargeTimeTypeChanged()){
|
||||
this.chargeTime = command.getChargeTimeType();
|
||||
} else {
|
||||
@@ -76,9 +117,58 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
} else {
|
||||
this.chargeCalculation = chargeDefinition.getChargeCalculation();
|
||||
}
|
||||
|
||||
BigDecimal chargeAmount = chargeDefinition.getAmount();
|
||||
if (command.isAmountChanged()){
|
||||
chargeAmount = command.getAmount();
|
||||
}
|
||||
|
||||
populateDerivedFields(loan.getPrincpal().getAmount(), chargeAmount);
|
||||
this.paid = determineIfFullyPaid();
|
||||
}
|
||||
|
||||
private LoanCharge(final Loan loan, final Charge chargeDefinition) {
|
||||
private void populateDerivedFields(final BigDecimal loanPrincipal, final BigDecimal chargeAmount) {
|
||||
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case FLAT:
|
||||
this.percentage = null;
|
||||
this.amount = chargeAmount;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = chargeAmount;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
this.percentage = chargeAmount;
|
||||
this.amountPercentageAppliedTo = loanPrincipal;
|
||||
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private LoanCharge(final Loan loan, final Charge chargeDefinition) {
|
||||
this(loan, chargeDefinition, chargeDefinition.getAmount(), chargeDefinition.getChargeTime(), chargeDefinition.getChargeCalculation());
|
||||
}
|
||||
|
||||
@@ -107,7 +197,6 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
if (command.isChargeCalculationTypeChanged()){
|
||||
this.chargeCalculation = ChargeCalculationType.fromInt(command.getChargeCalculationType()).getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isDueAtDisbursement() {
|
||||
@@ -117,17 +206,35 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public BigDecimal calculateMonetaryAmount(final Money principalDisbursed) {
|
||||
BigDecimal calculatedAmount = BigDecimal.ZERO;
|
||||
|
||||
if (ChargeCalculationType.fromInt(chargeCalculation).equals(ChargeCalculationType.FLAT)) {
|
||||
calculatedAmount = this.amount;
|
||||
} else if (ChargeCalculationType.fromInt(chargeCalculation).equals(ChargeCalculationType.PERCENT_OF_AMOUNT)) {
|
||||
|
||||
BigDecimal amountAsPercentageFactor = BigDecimal.ZERO;
|
||||
if (isGreaterThanZero(this.amount)) {
|
||||
amountAsPercentageFactor = this.amount.divide(BigDecimal.valueOf(Double.valueOf("100")));
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
calculatedAmount = this.amount;
|
||||
break;
|
||||
case FLAT:
|
||||
calculatedAmount = this.amount;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
this.amountPercentageAppliedTo = principalDisbursed.getAmount();
|
||||
this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
calculatedAmount = this.amount;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
}
|
||||
|
||||
calculatedAmount = principalDisbursed.multiplyRetainScale(amountAsPercentageFactor, RoundingMode.HALF_EVEN).getAmount();
|
||||
}
|
||||
|
||||
return calculatedAmount;
|
||||
}
|
||||
@@ -145,4 +252,43 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
return new LoanChargeCommand(modifiedParameters, this.getId(), this.loan.getId(), this.charge.getId(),
|
||||
this.amount, this.chargeTime, this.chargeCalculation);
|
||||
}
|
||||
|
||||
private boolean determineIfFullyPaid() {
|
||||
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
|
||||
}
|
||||
|
||||
private BigDecimal calculateOutstanding() {
|
||||
|
||||
BigDecimal amountPaidLocal = BigDecimal.ZERO;
|
||||
if (this.amountPaid != null) {
|
||||
amountPaidLocal = this.amountPaid;
|
||||
}
|
||||
|
||||
return this.amount.subtract(amountPaidLocal);
|
||||
}
|
||||
|
||||
private BigDecimal percentageOf(final BigDecimal value, final BigDecimal percentage) {
|
||||
|
||||
BigDecimal percentageOf = BigDecimal.ZERO;
|
||||
|
||||
if (isGreaterThanZero(value)) {
|
||||
final MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
|
||||
BigDecimal multiplicand = percentage.divide(BigDecimal.valueOf(100l), mc);
|
||||
percentageOf = value.multiply(multiplicand, mc);
|
||||
}
|
||||
|
||||
return percentageOf;
|
||||
}
|
||||
|
||||
public void markAsFullyPaid() {
|
||||
this.amountPaid = this.amount;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.paid = true;
|
||||
}
|
||||
|
||||
public void resetToOriginal() {
|
||||
this.amountPaid = BigDecimal.ZERO;
|
||||
this.amountOutstanding = this.amount;
|
||||
this.paid = false;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -68,7 +68,7 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic
|
||||
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(loanScheduleRelatedDetails.getCurrency().getCode());
|
||||
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges());
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanScheduleRelatedDetails.getPrincipal().getAmount());
|
||||
|
||||
return loanScheduleGenerator.generate(applicationCurrency,
|
||||
loanScheduleRelatedDetails, loanTermFrequency,
|
||||
|
||||
@@ -104,7 +104,7 @@ public class LoanAssembler {
|
||||
Staff loanOfficer= findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
|
||||
|
||||
// optionally, see if charges are associated with loan on creation (through loan product or by being directly added)
|
||||
final Set<LoanCharge> loanCharges = assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges(), loanProduct.getCurrency().getCode());
|
||||
final Set<LoanCharge> loanCharges = assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges(), loanProduct.getCurrency().getCode(), command.getPrincipal());
|
||||
|
||||
Loan loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
@@ -130,8 +130,11 @@ public class LoanAssembler {
|
||||
return loan;
|
||||
}
|
||||
|
||||
public Set<LoanCharge> assembleSetOfLoanCharges(LoanChargeCommand[] chargesPassedAtCreation,
|
||||
Set<Charge> chargesInheritedFromProduct, String loanCurrencyCode) {
|
||||
public Set<LoanCharge> assembleSetOfLoanCharges(final LoanChargeCommand[] chargesPassedAtCreation,
|
||||
final Set<Charge> chargesInheritedFromProduct,
|
||||
final String loanCurrencyCode,
|
||||
final BigDecimal loanPrincipal) {
|
||||
|
||||
Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
|
||||
|
||||
if (!ObjectUtils.isEmpty(chargesPassedAtCreation)) {
|
||||
@@ -152,7 +155,7 @@ public class LoanAssembler {
|
||||
throw new InvalidCurrencyException("charge", "attach.to.loan", errorMessage);
|
||||
}
|
||||
|
||||
loanCharges.add(LoanCharge.createNew(chargeDefinition, loanChargeCommand));
|
||||
loanCharges.add(LoanCharge.createNewWithoutLoan(chargeDefinition, loanChargeCommand, loanPrincipal));
|
||||
}
|
||||
} else if (chargesPassedAtCreation == null) {
|
||||
for (Charge productCharge : chargesInheritedFromProduct) {
|
||||
|
||||
+15
-6
@@ -1,5 +1,6 @@
|
||||
package org.mifosng.platform.loan.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -7,6 +8,7 @@ import org.mifosng.platform.api.commands.LoanChargeCommand;
|
||||
import org.mifosng.platform.charge.domain.Charge;
|
||||
import org.mifosng.platform.charge.domain.ChargeRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanCharge;
|
||||
import org.mifosng.platform.loan.domain.LoanChargeRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -14,22 +16,29 @@ import org.springframework.stereotype.Service;
|
||||
public class LoanChargeAssembler {
|
||||
|
||||
private ChargeRepository chargeRepository;
|
||||
private final LoanChargeRepository loanChargeRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanChargeAssembler(final ChargeRepository chargeRepository) {
|
||||
public LoanChargeAssembler(final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository) {
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.loanChargeRepository = loanChargeRepository;
|
||||
}
|
||||
|
||||
public Set<LoanCharge> assembleFrom(final LoanChargeCommand[] charges) {
|
||||
public Set<LoanCharge> assembleFrom(final LoanChargeCommand[] charges, final BigDecimal loanPrincipal) {
|
||||
|
||||
final Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
|
||||
|
||||
// FIXME - KW - what if loanChargeCommands contains the actual loanCharge id, use loanChargeRepository to fetch it then.
|
||||
if (charges != null) {
|
||||
for (LoanChargeCommand loanChargeCommand : charges) {
|
||||
Charge chargeDefinition = this.chargeRepository.findOne(loanChargeCommand.getChargeId());
|
||||
LoanCharge loanCharge = LoanCharge.createNew(chargeDefinition, loanChargeCommand);
|
||||
loanCharges.add(loanCharge);
|
||||
|
||||
if (loanChargeCommand.getId() == null) {
|
||||
final Charge chargeDefinition = this.chargeRepository.findOne(loanChargeCommand.getChargeId());
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, loanChargeCommand, loanPrincipal);
|
||||
loanCharges.add(loanCharge);
|
||||
} else {
|
||||
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeCommand.getId());
|
||||
loanCharges.add(loanCharge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
Fund fund = this.loanAssembler.findFundByIdIfProvided(command.getFundId());
|
||||
Staff loanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
|
||||
LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
|
||||
Set<LoanCharge> charges = this.loanAssembler.assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges(), loan.getCurrency().getCode());
|
||||
Set<LoanCharge> charges = this.loanAssembler.assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges(), loan.getCurrency().getCode(), loan.getPrincpal().getAmount());
|
||||
|
||||
final LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
|
||||
loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer);
|
||||
|
||||
Reference in New Issue
Block a user