diff --git a/mifosng-db/patches/patch-0009-add_extra_columns_to_loan_charges_to_facilitate_percentage_charge.sql b/mifosng-db/patches/patch-0009-add_extra_columns_to_loan_charges_to_facilitate_percentage_charge.sql new file mode 100644 index 000000000..6f752130f --- /dev/null +++ b/mifosng-db/patches/patch-0009-add_extra_columns_to_loan_charges_to_facilitate_percentage_charge.sql @@ -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`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java index 772e6196c..2998b8275 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java @@ -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. + } } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java index e4728bc23..81b67ef11 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java @@ -67,8 +67,9 @@ public class LoanBasicDetailsData { final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery(); final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType(); final Collection charges = new ArrayList(); - 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())); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanChargeData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanChargeData.java index 7004a43a6..69f0e50c0 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanChargeData.java @@ -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 chargeOptions; public static LoanChargeData template(final Collection 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; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java index ff2e846cf..5813da4f2 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/charge/service/ChargeReadPlatformServiceImpl.java @@ -195,9 +195,12 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService private static final class LoanChargeMapper implements RowMapper { 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); } } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java index 3e76a0525..9053e69b4 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java @@ -491,14 +491,17 @@ public class Loan extends AbstractAuditableCustom { // 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 { "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 { } } - 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) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanCharge.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanCharge.java index 2992c4286..18125335f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanCharge.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanCharge.java @@ -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 { @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 { // } + + 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 { } 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 { if (command.isChargeCalculationTypeChanged()){ this.chargeCalculation = ChargeCalculationType.fromInt(command.getChargeCalculationType()).getValue(); } - } public boolean isDueAtDisbursement() { @@ -117,17 +206,35 @@ public class LoanCharge extends AbstractPersistable { 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 { 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; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformServiceImpl.java index 9b041eaf8..87b762fbd 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformServiceImpl.java @@ -68,7 +68,7 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(loanScheduleRelatedDetails.getCurrency().getCode()); - final Set loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges()); + final Set loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanScheduleRelatedDetails.getPrincipal().getAmount()); return loanScheduleGenerator.generate(applicationCurrency, loanScheduleRelatedDetails, loanTermFrequency, diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java index ccf04c79f..e9415aa65 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanAssembler.java @@ -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 loanCharges = assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges(), loanProduct.getCurrency().getCode()); + final Set 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 assembleSetOfLoanCharges(LoanChargeCommand[] chargesPassedAtCreation, - Set chargesInheritedFromProduct, String loanCurrencyCode) { + public Set assembleSetOfLoanCharges(final LoanChargeCommand[] chargesPassedAtCreation, + final Set chargesInheritedFromProduct, + final String loanCurrencyCode, + final BigDecimal loanPrincipal) { + Set loanCharges = new HashSet(); 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) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanChargeAssembler.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanChargeAssembler.java index d9e4d3425..082669df4 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanChargeAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanChargeAssembler.java @@ -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 assembleFrom(final LoanChargeCommand[] charges) { + public Set assembleFrom(final LoanChargeCommand[] charges, final BigDecimal loanPrincipal) { final Set loanCharges = new HashSet(); - // 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); + } } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 2a6025418..8dc22ea16 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -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 charges = this.loanAssembler.assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges(), loan.getCurrency().getCode()); + Set 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);