mifosx-58: handle charges at disbursement for loan creation.

This commit is contained in:
Keith Woodlock
2012-09-27 11:24:30 +01:00
parent d5f697962c
commit f737336dc3
11 changed files with 293 additions and 70 deletions
@@ -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; convenienceDataRequired = true;
} }
charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId); if (responseParameters.contains("charges")) {
if (CollectionUtils.isEmpty(charges)) { charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId);
charges = null; if (CollectionUtils.isEmpty(charges)) {
charges = null; // set back to null so doesnt appear in JSON is no charges exist.
}
} }
} }
@@ -67,8 +67,9 @@ public class LoanBasicDetailsData {
final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery(); final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery();
final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType(); final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType();
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>(); 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())); charge.getAmount(), charge.getChargeTimeType(), charge.getChargeCalculationType()));
} }
@@ -15,13 +15,21 @@ public class LoanChargeData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final String name; private final String name;
@SuppressWarnings("unused") @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; private final CurrencyData currency;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final BigDecimal amount; private final BigDecimal amount;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final EnumOptionData chargeTimeType; private final BigDecimal amountPaid;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final EnumOptionData chargeCalculationType; private final BigDecimal amountOutstanding;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<ChargeData> chargeOptions; private final Collection<ChargeData> chargeOptions;
@@ -30,21 +38,42 @@ public class LoanChargeData {
return new LoanChargeData(null,null,null,null,null,null,null, 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( public LoanChargeData(
final Long id, final Long id,
final Long chargeId, final Long chargeId,
final String name, final String name,
final CurrencyData currency, final CurrencyData currency,
final BigDecimal amount, final BigDecimal amount,
final BigDecimal amountPaid,
final BigDecimal amountOutstanding,
final EnumOptionData chargeTimeType, final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType) { final EnumOptionData chargeCalculationType,
final BigDecimal percentage,
final BigDecimal amountPercentageAppliedTo) {
this.id = id; this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = name; this.name = name;
this.currency = currency; this.currency = currency;
this.amount = amount; this.amount = amount;
this.amountPaid = amountPaid;
this.amountOutstanding = amountOutstanding;
this.chargeTimeType = chargeTimeType; this.chargeTimeType = chargeTimeType;
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.percentage = percentage;
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
this.chargeOptions = null; this.chargeOptions = null;
} }
@@ -63,8 +92,12 @@ public class LoanChargeData {
this.name = name; this.name = name;
this.currency = currency; this.currency = currency;
this.amount = amount; this.amount = amount;
this.amountPaid = BigDecimal.ZERO;
this.amountOutstanding = amount;
this.chargeTimeType = chargeTimeType; this.chargeTimeType = chargeTimeType;
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.percentage = null;
this.amountPercentageAppliedTo = null;
this.chargeOptions = chargeOptions; this.chargeOptions = chargeOptions;
} }
} }
@@ -195,9 +195,12 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
private static final class LoanChargeMapper implements RowMapper<LoanChargeData> { private static final class LoanChargeMapper implements RowMapper<LoanChargeData> {
public String loanChargeSchema(){ 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, " + return "lc.id as id, c.id as chargeId, c.name as name, " +
"lc.charge_time_enum as chargeTime, " + "lc.amount as amountDue, lc.amount_paid_derived as amountPaid, lc.amount_outstanding_derived as amountOutstanding, " +
"lc.charge_calculation_enum as chargeCalculation, oc.name as currencyName, " + "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.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, " +
"oc.internationalized_name_code as currencyNameCode from m_charge c " + "oc.internationalized_name_code as currencyNameCode from m_charge c " +
"join m_organisation_currency oc on c.currency_code = oc.code " + "join m_organisation_currency oc on c.currency_code = oc.code " +
@@ -205,28 +208,37 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
} }
@Override @Override
public LoanChargeData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException { public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
Long id = rs.getLong("id");
Long chargeId = rs.getLong("chargeId");
String name = rs.getString("name");
BigDecimal amount = rs.getBigDecimal("amount");
String currencyCode = rs.getString("currencyCode"); final Long id = rs.getLong("id");
String currencyName = rs.getString("currencyName"); final Long chargeId = rs.getLong("chargeId");
String currencyNameCode = rs.getString("currencyNameCode"); final String name = rs.getString("name");
String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); final BigDecimal amount = rs.getBigDecimal("amountDue");
Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces"); BigDecimal amountPaid = rs.getBigDecimal("amountPaid");
if (amountPaid == null) {
amountPaid = BigDecimal.ZERO;
}
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, final BigDecimal percentageOf = rs.getBigDecimal("percentageOf");
final BigDecimal amountPercentageAppliedTo = rs.getBigDecimal("amountPercentageAppliedTo");
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");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces,
currencyDisplaySymbol, currencyNameCode); currencyDisplaySymbol, currencyNameCode);
int chargeTime = rs.getInt("chargeTime"); final int chargeTime = rs.getInt("chargeTime");
EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime); final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
int chargeCalculation = rs.getInt("chargeCalculation"); final int chargeCalculation = rs.getInt("chargeCalculation");
EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(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) // add repayment transaction to track incoming money from client to mfi for (charges due at time of disbursement)
if (getTotalChargesDueAtDisbursement().isGreaterThanZero()) { if (getTotalChargesDueAtDisbursement().isGreaterThanZero()) {
// FIXME - add charges component to 'transactions'
LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getTotalChargesDueAtDisbursement(), disbursedOn); LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getTotalChargesDueAtDisbursement(), disbursedOn);
Money zero = Money.zero(getCurrency()); Money zero = Money.zero(getCurrency());
chargesPayment.updateComponents(zero, zero, zero, getTotalChargesDueAtDisbursement()); chargesPayment.updateComponents(zero, zero, zero, getTotalChargesDueAtDisbursement());
chargesPayment.updateLoan(this); chargesPayment.updateLoan(this);
this.loanTransactions.add(chargesPayment); 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())) { if (disbursedOn.isBefore(getApprovedOnDate())) {
@@ -515,8 +518,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
"cannot.be.a.future.date", errorMessage, disbursedOn); "cannot.be.a.future.date", errorMessage, disbursedOn);
} }
LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments.get(0).getDueDate();
.get(0).getDueDate();
if (disbursedOn.isAfter(firstRepaymentDueDate)) { if (disbursedOn.isAfter(firstRepaymentDueDate)) {
final String errorMessage = "The date on which a loan is disbursed cannot be after the first expected repayment date: " final String errorMessage = "The date on which a loan is disbursed cannot be after the first expected repayment date: "
+ firstRepaymentDueDate.toString(); + firstRepaymentDueDate.toString();
@@ -526,14 +528,19 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
} }
public void undoDisbursal( public void undoDisbursal(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
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.loanStatus = statusEnum.getValue();
this.loanTransactions.clear(); this.loanTransactions.clear();
this.disbursedOnDate = null; this.disbursedOnDate = null;
for (LoanCharge charge : this.charges) {
if (charge.isDueAtDisbursement()) {
charge.resetToOriginal();
}
}
} }
public void waive(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) { public void waive(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
@@ -1,6 +1,7 @@
package org.mifosng.platform.loan.domain; package org.mifosng.platform.loan.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -30,21 +31,41 @@ public class LoanCharge extends AbstractPersistable<Long> {
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable=false) @JoinColumn(name = "charge_id", referencedColumnName = "id", nullable=false)
private Charge charge; private Charge charge;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "charge_time_enum", nullable = false) @Column(name = "charge_time_enum", nullable = false)
private Integer chargeTime; private Integer chargeTime;
@Column(name = "charge_calculation_enum") @Column(name = "charge_calculation_enum")
private Integer chargeCalculation; 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) { public static LoanCharge createNew(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
return new LoanCharge(loan, chargeDefinition, 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) { 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) { private LoanCharge(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
this.loan = loan; this.loan = loan;
this.charge = chargeDefinition; this.charge = chargeDefinition;
if (command.isAmountChanged()){
this.amount = command.getAmount();
} else {
this.amount = chargeDefinition.getAmount();
}
if (command.isChargeTimeTypeChanged()){ if (command.isChargeTimeTypeChanged()){
this.chargeTime = command.getChargeTimeType(); this.chargeTime = command.getChargeTimeType();
} else { } else {
@@ -76,9 +117,58 @@ public class LoanCharge extends AbstractPersistable<Long> {
} else { } else {
this.chargeCalculation = chargeDefinition.getChargeCalculation(); 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()); this(loan, chargeDefinition, chargeDefinition.getAmount(), chargeDefinition.getChargeTime(), chargeDefinition.getChargeCalculation());
} }
@@ -107,7 +197,6 @@ public class LoanCharge extends AbstractPersistable<Long> {
if (command.isChargeCalculationTypeChanged()){ if (command.isChargeCalculationTypeChanged()){
this.chargeCalculation = ChargeCalculationType.fromInt(command.getChargeCalculationType()).getValue(); this.chargeCalculation = ChargeCalculationType.fromInt(command.getChargeCalculationType()).getValue();
} }
} }
public boolean isDueAtDisbursement() { public boolean isDueAtDisbursement() {
@@ -117,18 +206,36 @@ public class LoanCharge extends AbstractPersistable<Long> {
public BigDecimal calculateMonetaryAmount(final Money principalDisbursed) { public BigDecimal calculateMonetaryAmount(final Money principalDisbursed) {
BigDecimal calculatedAmount = BigDecimal.ZERO; BigDecimal calculatedAmount = BigDecimal.ZERO;
if (ChargeCalculationType.fromInt(chargeCalculation).equals(ChargeCalculationType.FLAT)) { switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
calculatedAmount = this.amount; case INVALID:
} else if (ChargeCalculationType.fromInt(chargeCalculation).equals(ChargeCalculationType.PERCENT_OF_AMOUNT)) { calculatedAmount = this.amount;
break;
BigDecimal amountAsPercentageFactor = BigDecimal.ZERO; case FLAT:
if (isGreaterThanZero(this.amount)) { calculatedAmount = this.amount;
amountAsPercentageFactor = this.amount.divide(BigDecimal.valueOf(Double.valueOf("100"))); 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; return calculatedAmount;
} }
@@ -145,4 +252,43 @@ public class LoanCharge extends AbstractPersistable<Long> {
return new LoanChargeCommand(modifiedParameters, this.getId(), this.loan.getId(), this.charge.getId(), return new LoanChargeCommand(modifiedParameters, this.getId(), this.loan.getId(), this.charge.getId(),
this.amount, this.chargeTime, this.chargeCalculation); 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;
}
} }
@@ -68,7 +68,7 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(loanScheduleRelatedDetails.getCurrency().getCode()); 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, return loanScheduleGenerator.generate(applicationCurrency,
loanScheduleRelatedDetails, loanTermFrequency, loanScheduleRelatedDetails, loanTermFrequency,
@@ -104,7 +104,7 @@ public class LoanAssembler {
Staff loanOfficer= findLoanOfficerByIdIfProvided(command.getLoanOfficerId()); Staff loanOfficer= findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
// optionally, see if charges are associated with loan on creation (through loan product or by being directly added) // 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 loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
loan.setExternalId(command.getExternalId()); loan.setExternalId(command.getExternalId());
@@ -130,8 +130,11 @@ public class LoanAssembler {
return loan; return loan;
} }
public Set<LoanCharge> assembleSetOfLoanCharges(LoanChargeCommand[] chargesPassedAtCreation, public Set<LoanCharge> assembleSetOfLoanCharges(final LoanChargeCommand[] chargesPassedAtCreation,
Set<Charge> chargesInheritedFromProduct, String loanCurrencyCode) { final Set<Charge> chargesInheritedFromProduct,
final String loanCurrencyCode,
final BigDecimal loanPrincipal) {
Set<LoanCharge> loanCharges = new HashSet<LoanCharge>(); Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
if (!ObjectUtils.isEmpty(chargesPassedAtCreation)) { if (!ObjectUtils.isEmpty(chargesPassedAtCreation)) {
@@ -152,7 +155,7 @@ public class LoanAssembler {
throw new InvalidCurrencyException("charge", "attach.to.loan", errorMessage); 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) { } else if (chargesPassedAtCreation == null) {
for (Charge productCharge : chargesInheritedFromProduct) { for (Charge productCharge : chargesInheritedFromProduct) {
@@ -1,5 +1,6 @@
package org.mifosng.platform.loan.service; package org.mifosng.platform.loan.service;
import java.math.BigDecimal;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; 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.Charge;
import org.mifosng.platform.charge.domain.ChargeRepository; import org.mifosng.platform.charge.domain.ChargeRepository;
import org.mifosng.platform.loan.domain.LoanCharge; import org.mifosng.platform.loan.domain.LoanCharge;
import org.mifosng.platform.loan.domain.LoanChargeRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -14,22 +16,29 @@ import org.springframework.stereotype.Service;
public class LoanChargeAssembler { public class LoanChargeAssembler {
private ChargeRepository chargeRepository; private ChargeRepository chargeRepository;
private final LoanChargeRepository loanChargeRepository;
@Autowired @Autowired
public LoanChargeAssembler(final ChargeRepository chargeRepository) { public LoanChargeAssembler(final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository) {
this.chargeRepository = chargeRepository; 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>(); 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) { if (charges != null) {
for (LoanChargeCommand loanChargeCommand : charges) { for (LoanChargeCommand loanChargeCommand : charges) {
Charge chargeDefinition = this.chargeRepository.findOne(loanChargeCommand.getChargeId());
LoanCharge loanCharge = LoanCharge.createNew(chargeDefinition, loanChargeCommand); if (loanChargeCommand.getId() == null) {
loanCharges.add(loanCharge); 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);
}
} }
} }
@@ -156,7 +156,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
Fund fund = this.loanAssembler.findFundByIdIfProvided(command.getFundId()); Fund fund = this.loanAssembler.findFundByIdIfProvided(command.getFundId());
Staff loanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(command.getLoanOfficerId()); Staff loanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId()); 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()); final LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer); loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer);