mifosx-58: loan charges at time of disbursement
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `m_loan`
|
||||||
|
ADD COLUMN `total_charges_due_at_disbursement_derived` bigint(20) DEFAULT NULL AFTER `interest_rebate_amount`;
|
||||||
@@ -47,7 +47,7 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
public Charge(final String name, final BigDecimal amount, final String currencyCode,
|
private Charge(final String name, final BigDecimal amount, final String currencyCode,
|
||||||
final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime,
|
final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime,
|
||||||
final ChargeCalculationType chargeCalculationType, final boolean active) {
|
final ChargeCalculationType chargeCalculationType, final boolean active) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|||||||
+4
-4
@@ -2,10 +2,10 @@ package org.mifosng.platform.charge.domain;
|
|||||||
|
|
||||||
public enum ChargeCalculationType {
|
public enum ChargeCalculationType {
|
||||||
|
|
||||||
INVALID(0, "chargeCalculationType.invalid"),
|
INVALID(0, "chargeCalculationType.invalid"), //
|
||||||
FLAT(1, "chargeCalculationType.flat"),
|
FLAT(1, "chargeCalculationType.flat"), //
|
||||||
PERCENT_OF_AMOUNT(2, "chargeCalculationType.percent.of.amount"),
|
PERCENT_OF_AMOUNT(2, "chargeCalculationType.percent.of.amount"), //
|
||||||
PERCENT_OF_AMOUNT_AND_INTEREST(3, "chargeCalculationType.percent.of.amount.and.interest"),
|
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");
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
|
|||||||
@@ -156,6 +156,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
@Column(name = "interest_rebate_amount", scale = 6, precision = 19)
|
@Column(name = "interest_rebate_amount", scale = 6, precision = 19)
|
||||||
private BigDecimal interestRebateOwed;
|
private BigDecimal interestRebateOwed;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") // derived field used in GETs only at present
|
||||||
|
@Column(name = "total_charges_due_at_disbursement_derived", scale = 6, precision = 19)
|
||||||
|
private BigDecimal totalChargesDueAtDisbursement;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private final InterestRebateCalculatorFactory interestRebateCalculatorFactory = new DailyEquivalentInterestRebateCalculatorFactory();
|
private final InterestRebateCalculatorFactory interestRebateCalculatorFactory = new DailyEquivalentInterestRebateCalculatorFactory();
|
||||||
|
|
||||||
@@ -164,8 +168,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
|
|
||||||
public static Loan createNew(final Fund fund,final Staff loanOfficer, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
public static Loan createNew(final Fund fund,final Staff loanOfficer, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||||
final LoanProduct loanProduct, final Client client,
|
final LoanProduct loanProduct, final Client client,
|
||||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail) {
|
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges) {
|
||||||
return new Loan(client, fund,loanOfficer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null);
|
return new Loan(client, fund,loanOfficer, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null, loanCharges);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Loan() {
|
protected Loan() {
|
||||||
@@ -175,9 +179,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
this.charges = null;
|
this.charges = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Loan(final Client client, Fund fund, Staff loanOfficer, LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct,
|
private Loan(
|
||||||
|
final Client client, Fund fund, Staff loanOfficer,
|
||||||
|
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||||
|
final LoanProduct loanProduct,
|
||||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||||
final LoanStatus loanStatus) {
|
final LoanStatus loanStatus, final Set<LoanCharge> loanCharges) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.fund = fund;
|
this.fund = fund;
|
||||||
this.loanofficer = loanOfficer;
|
this.loanofficer = loanOfficer;
|
||||||
@@ -189,9 +196,37 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
} else {
|
} else {
|
||||||
this.loanStatus = null;
|
this.loanStatus = null;
|
||||||
}
|
}
|
||||||
|
if (loanCharges != null && !loanCharges.isEmpty()) {
|
||||||
|
this.charges = associateChargesWithThisLoan(loanCharges);
|
||||||
|
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
|
||||||
|
} else {
|
||||||
|
this.charges = null;
|
||||||
|
}
|
||||||
this.interestRebateOwed = BigDecimal.ZERO;
|
this.interestRebateOwed = BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
|
||||||
|
|
||||||
|
Money chargesDue = Money.of(getCurrency(), BigDecimal.ZERO);
|
||||||
|
|
||||||
|
for (LoanCharge charge : this.charges) {
|
||||||
|
if (charge.isDueAtDisbursement()) {
|
||||||
|
chargesDue = chargesDue.plus(charge.calculateMonetaryAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chargesDue.getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<LoanCharge> associateChargesWithThisLoan(Set<LoanCharge> loanCharges) {
|
||||||
|
|
||||||
|
for (LoanCharge loanCharge : loanCharges) {
|
||||||
|
loanCharge.update(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loanCharges;
|
||||||
|
}
|
||||||
|
|
||||||
public Client client() {
|
public Client client() {
|
||||||
return this.client;
|
return this.client;
|
||||||
}
|
}
|
||||||
@@ -266,7 +301,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
|
|
||||||
if (command.isChargesChanged()) {
|
if (command.isChargesChanged()) {
|
||||||
this.charges.clear();
|
this.charges.clear();
|
||||||
this.charges.addAll(charges);
|
|
||||||
|
this.charges = associateChargesWithThisLoan(charges);
|
||||||
|
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loanRepaymentScheduleDetail.update(command.toLoanProductCommand());
|
this.loanRepaymentScheduleDetail.update(command.toLoanProductCommand());
|
||||||
@@ -1217,4 +1254,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
return this.loanRepaymentScheduleDetail.getPrincipal()
|
return this.loanRepaymentScheduleDetail.getPrincipal()
|
||||||
.getCurrencyCode();
|
.getCurrencyCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money getPrincpal() {
|
||||||
|
return this.loanRepaymentScheduleDetail.getPrincipal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,27 @@
|
|||||||
package org.mifosng.platform.loan.domain;
|
package org.mifosng.platform.loan.domain;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
import org.mifosng.platform.api.commands.LoanChargeCommand;
|
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.ChargeCalculationType;
|
||||||
import javax.persistence.*;
|
import org.mifosng.platform.charge.domain.ChargeTimeType;
|
||||||
import java.io.Serializable;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@IdClass(LoanCharge.LoanChargePK.class)
|
@IdClass(LoanCharge.LoanChargePK.class)
|
||||||
@Table(name = "m_loan_charge")
|
@Table(name = "m_loan_charge")
|
||||||
public class LoanCharge {
|
public class LoanCharge {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Id
|
@Id
|
||||||
private Loan loan;
|
private Loan loan;
|
||||||
|
|
||||||
@@ -20,50 +29,55 @@ public class LoanCharge {
|
|||||||
@Id
|
@Id
|
||||||
private Charge charge;
|
private Charge charge;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Column(name = "charge_time_enum", nullable = false)
|
@Column(name = "charge_time_enum", nullable = false)
|
||||||
private Integer chargeTime;
|
private Integer chargeTime;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Column(name = "charge_calculation_enum")
|
@Column(name = "charge_calculation_enum")
|
||||||
private Integer chargeCalculation;
|
private Integer chargeCalculation;
|
||||||
|
|
||||||
|
public static LoanCharge createNew(final Charge chargeDefinition, final LoanChargeCommand command) {
|
||||||
|
return new LoanCharge(null, chargeDefinition, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LoanCharge createNew(final Charge chargeDefinition) {
|
||||||
|
return new LoanCharge(null, chargeDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
protected LoanCharge() {
|
protected LoanCharge() {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanCharge(Loan loan, Charge charge, LoanChargeCommand command) {
|
private LoanCharge(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
|
||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
this.charge = charge;
|
this.charge = chargeDefinition;
|
||||||
|
|
||||||
if (command.isAmountChanged()){
|
if (command.isAmountChanged()){
|
||||||
this.amount = command.getAmount();
|
this.amount = command.getAmount();
|
||||||
} else {
|
} else {
|
||||||
this.amount = charge.getAmount();
|
this.amount = chargeDefinition.getAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChargeTimeTypeChanged()){
|
if (command.isChargeTimeTypeChanged()){
|
||||||
this.chargeTime = command.getChargeTimeType();
|
this.chargeTime = command.getChargeTimeType();
|
||||||
} else {
|
} else {
|
||||||
this.chargeTime = charge.getChargeTime();
|
this.chargeTime = chargeDefinition.getChargeTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isChargeCalculationTypeChanged()){
|
if (command.isChargeCalculationTypeChanged()){
|
||||||
this.chargeCalculation = command.getChargeCalculationType();
|
this.chargeCalculation = command.getChargeCalculationType();
|
||||||
} else {
|
} else {
|
||||||
this.chargeCalculation = charge.getChargeCalculation();
|
this.chargeCalculation = chargeDefinition.getChargeCalculation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanCharge(final Loan loan, final Charge charge) {
|
private LoanCharge(final Loan loan, final Charge chargeDefinition) {
|
||||||
this(loan, charge, charge.getAmount(), charge.getChargeTime(), charge.getChargeCalculation());
|
this(loan, chargeDefinition, chargeDefinition.getAmount(), chargeDefinition.getChargeTime(), chargeDefinition.getChargeCalculation());
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanCharge(final Loan loan, final Charge charge, final BigDecimal amount, final Integer chargeTime, final Integer chargeCalculation) {
|
private LoanCharge(final Loan loan, final Charge charge, final BigDecimal amount, final Integer chargeTime, final Integer chargeCalculation) {
|
||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
this.charge = charge;
|
this.charge = charge;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
@@ -84,4 +98,34 @@ public class LoanCharge {
|
|||||||
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable=false)
|
@JoinColumn(name = "charge_id", referencedColumnName = "id", nullable=false)
|
||||||
private Charge charge;
|
private Charge charge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update(final Loan loan) {
|
||||||
|
this.loan = loan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDueAtDisbursement() {
|
||||||
|
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.DISBURSEMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal calculateMonetaryAmount() {
|
||||||
|
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")));
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatedAmount = this.loan.getPrincpal().multiplyRetainScale(amountAsPercentageFactor, RoundingMode.HALF_EVEN).getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculatedAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGreaterThanZero(final BigDecimal value) {
|
||||||
|
return value.compareTo(BigDecimal.ZERO) == 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+11
-13
@@ -52,7 +52,7 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
|
|
||||||
@Enumerated(EnumType.ORDINAL)
|
@Enumerated(EnumType.ORDINAL)
|
||||||
@Column(name = "transaction_type_enum", nullable = false)
|
@Column(name = "transaction_type_enum", nullable = false)
|
||||||
private LoanTransactionType typeOf;
|
private Integer typeOf;
|
||||||
|
|
||||||
@OneToOne(optional=true, cascade={CascadeType.PERSIST})
|
@OneToOne(optional=true, cascade={CascadeType.PERSIST})
|
||||||
@JoinColumn(name="contra_id")
|
@JoinColumn(name="contra_id")
|
||||||
@@ -65,32 +65,30 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
this.typeOf = null;
|
this.typeOf = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction disbursement(Money amount, LocalDate disbursementDate) {
|
public static LoanTransaction disbursement(final Money amount, final LocalDate disbursementDate) {
|
||||||
return new LoanTransaction(LoanTransactionType.DISBURSEMENT, amount.getAmount(), disbursementDate);
|
return new LoanTransaction(LoanTransactionType.DISBURSEMENT, amount.getAmount(), disbursementDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction repayment(Money amount, LocalDate paymentDate) {
|
public static LoanTransaction repayment(final Money amount, final LocalDate paymentDate) {
|
||||||
return new LoanTransaction(LoanTransactionType.REPAYMENT, amount.getAmount(), paymentDate);
|
return new LoanTransaction(LoanTransactionType.REPAYMENT, amount.getAmount(), paymentDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanTransaction waiver(Money waived, LocalDate waiveDate) {
|
public static LoanTransaction waiver(final Money waived, final LocalDate waiveDate) {
|
||||||
return new LoanTransaction(LoanTransactionType.WAIVED, waived.getAmount(), waiveDate);
|
return new LoanTransaction(LoanTransactionType.WAIVED, waived.getAmount(), waiveDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LoanTransaction contra(LoanTransaction originalTransaction) {
|
private static LoanTransaction contra(LoanTransaction originalTransaction) {
|
||||||
|
|
||||||
LoanTransaction contra = new LoanTransaction(LoanTransactionType.REVERSAL, originalTransaction.getAmount().negate(), new LocalDate(originalTransaction.getDateOf()));
|
LoanTransaction contra = new LoanTransaction(LoanTransactionType.REVERSAL, originalTransaction.getAmount().negate(), new LocalDate(originalTransaction.getDateOf()));
|
||||||
contra.updateContra(originalTransaction);
|
contra.updateContra(originalTransaction);
|
||||||
|
|
||||||
return contra;
|
return contra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateContra(LoanTransaction transaction) {
|
public void updateContra(final LoanTransaction transaction) {
|
||||||
this.contra = transaction;
|
this.contra = transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanTransaction(LoanTransactionType type, final BigDecimal amount, final LocalDate date) {
|
private LoanTransaction(final LoanTransactionType type, final BigDecimal amount, final LocalDate date) {
|
||||||
this.typeOf = type;
|
this.typeOf = type.getValue();
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.dateOf = date.toDateMidnight().toDate();
|
this.dateOf = date.toDateMidnight().toDate();
|
||||||
}
|
}
|
||||||
@@ -113,11 +111,11 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LoanTransactionType getTypeOf() {
|
public LoanTransactionType getTypeOf() {
|
||||||
return typeOf;
|
return LoanTransactionType.fromInt(this.typeOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRepayment() {
|
public boolean isRepayment() {
|
||||||
return LoanTransactionType.REPAYMENT.equals(typeOf) && isNotContra();
|
return LoanTransactionType.REPAYMENT.equals(getTypeOf()) && isNotContra();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotRepayment() {
|
public boolean isNotRepayment() {
|
||||||
@@ -129,11 +127,11 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisbursement() {
|
public boolean isDisbursement() {
|
||||||
return LoanTransactionType.DISBURSEMENT.equals(typeOf);
|
return LoanTransactionType.DISBURSEMENT.equals(getTypeOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWaiver() {
|
public boolean isWaiver() {
|
||||||
return LoanTransactionType.WAIVED.equals(typeOf) && isNotContra();
|
return LoanTransactionType.WAIVED.equals(getTypeOf()) && isNotContra();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotWaiver() {
|
public boolean isNotWaiver() {
|
||||||
|
|||||||
+6
-1
@@ -1,7 +1,12 @@
|
|||||||
package org.mifosng.platform.loan.domain;
|
package org.mifosng.platform.loan.domain;
|
||||||
|
|
||||||
public enum LoanTransactionType {
|
public enum LoanTransactionType {
|
||||||
INVALID(0, "loanTransactionType.invalid"), DISBURSEMENT(1, "loanTransactionType.disbursement"), REPAYMENT(2, "loanTransactionType.repayment"), REVERSAL(3, "loanTransactionType.reversal"), WAIVED(4, "loanTransactionType.waiver");
|
|
||||||
|
INVALID(0, "loanTransactionType.invalid"), //
|
||||||
|
DISBURSEMENT(1, "loanTransactionType.disbursement"), //
|
||||||
|
REPAYMENT(2, "loanTransactionType.repayment"), //
|
||||||
|
REVERSAL(3, "loanTransactionType.reversal"), //
|
||||||
|
WAIVED(4, "loanTransactionType.waiver");
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
private final String code;
|
private final String code;
|
||||||
|
|||||||
+35
-43
@@ -6,8 +6,8 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.mifosng.platform.api.commands.LoanChargeCommand;
|
|
||||||
import org.mifosng.platform.api.commands.LoanApplicationCommand;
|
import org.mifosng.platform.api.commands.LoanApplicationCommand;
|
||||||
|
import org.mifosng.platform.api.commands.LoanChargeCommand;
|
||||||
import org.mifosng.platform.api.data.LoanSchedule;
|
import org.mifosng.platform.api.data.LoanSchedule;
|
||||||
import org.mifosng.platform.api.data.MoneyData;
|
import org.mifosng.platform.api.data.MoneyData;
|
||||||
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
||||||
@@ -20,7 +20,6 @@ import org.mifosng.platform.exceptions.ChargeIsNotActiveException;
|
|||||||
import org.mifosng.platform.exceptions.ChargeNotFoundException;
|
import org.mifosng.platform.exceptions.ChargeNotFoundException;
|
||||||
import org.mifosng.platform.exceptions.ClientNotFoundException;
|
import org.mifosng.platform.exceptions.ClientNotFoundException;
|
||||||
import org.mifosng.platform.exceptions.FundNotFoundException;
|
import org.mifosng.platform.exceptions.FundNotFoundException;
|
||||||
import org.mifosng.platform.exceptions.InvalidCurrencyException;
|
|
||||||
import org.mifosng.platform.exceptions.LoanProductNotFoundException;
|
import org.mifosng.platform.exceptions.LoanProductNotFoundException;
|
||||||
import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException;
|
import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException;
|
||||||
import org.mifosng.platform.exceptions.StaffNotFoundException;
|
import org.mifosng.platform.exceptions.StaffNotFoundException;
|
||||||
@@ -94,9 +93,6 @@ public class LoanAssembler {
|
|||||||
|
|
||||||
LoanProductRelatedDetail loanRepaymentScheduleDetail = assembleLoanProductRelatedDetailFrom(command, currency);
|
LoanProductRelatedDetail loanRepaymentScheduleDetail = assembleLoanProductRelatedDetailFrom(command, currency);
|
||||||
|
|
||||||
LoanSchedule loanSchedule = command.getLoanSchedule();
|
|
||||||
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments();
|
|
||||||
|
|
||||||
// associating fund with loan product at creation is optional for now.
|
// associating fund with loan product at creation is optional for now.
|
||||||
Fund fund = findFundByIdIfProvided(command.getFundId());
|
Fund fund = findFundByIdIfProvided(command.getFundId());
|
||||||
LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
|
LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
|
||||||
@@ -104,11 +100,15 @@ public class LoanAssembler {
|
|||||||
//optionally associate a loan officer to the loan
|
//optionally associate a loan officer to the loan
|
||||||
Staff loanOfficer= findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
|
Staff loanOfficer= findLoanOfficerByIdIfProvided(command.getLoanOfficerId());
|
||||||
|
|
||||||
Loan loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail);
|
// 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());
|
||||||
|
|
||||||
|
Loan loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
|
||||||
loan.setExternalId(command.getExternalId());
|
loan.setExternalId(command.getExternalId());
|
||||||
|
|
||||||
final Set<LoanCharge> charges = this.assembleSetOfCharges(command, loan, loanProduct);
|
// TODO - user service to calculate loan schedule at this point
|
||||||
loan.setCharges(charges);
|
LoanSchedule loanSchedule = command.getLoanSchedule();
|
||||||
|
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments();
|
||||||
|
|
||||||
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
|
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
|
||||||
|
|
||||||
@@ -130,6 +130,33 @@ public class LoanAssembler {
|
|||||||
return loan;
|
return loan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<LoanCharge> assembleSetOfLoanCharges(LoanChargeCommand[] chargesPassedAtCreation, Set<Charge> chargesInheritedFromProduct) {
|
||||||
|
Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
|
||||||
|
|
||||||
|
if (!ObjectUtils.isEmpty(chargesPassedAtCreation)) {
|
||||||
|
for (LoanChargeCommand loanChargeCommand : chargesPassedAtCreation) {
|
||||||
|
|
||||||
|
Long chargeDefinitionId = loanChargeCommand.getId();
|
||||||
|
Charge chargeDefinition = this.chargeRepository.findOne(chargeDefinitionId);
|
||||||
|
if (chargeDefinition == null || chargeDefinition.isDeleted()) {
|
||||||
|
throw new ChargeNotFoundException(chargeDefinitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chargeDefinition.isActive()) {
|
||||||
|
throw new ChargeIsNotActiveException(chargeDefinitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
loanCharges.add(LoanCharge.createNew(chargeDefinition, loanChargeCommand));
|
||||||
|
}
|
||||||
|
} else if (chargesPassedAtCreation == null) {
|
||||||
|
for (Charge productCharge : chargesInheritedFromProduct) {
|
||||||
|
loanCharges.add(LoanCharge.createNew(productCharge));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return loanCharges;
|
||||||
|
}
|
||||||
|
|
||||||
public LoanProductRelatedDetail assembleLoanProductRelatedDetailFrom(final LoanApplicationCommand command, MonetaryCurrency currency) {
|
public LoanProductRelatedDetail assembleLoanProductRelatedDetailFrom(final LoanApplicationCommand command, MonetaryCurrency currency) {
|
||||||
final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriod();
|
final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriod();
|
||||||
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
|
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
|
||||||
@@ -193,39 +220,4 @@ public class LoanAssembler {
|
|||||||
}
|
}
|
||||||
return strategy;
|
return strategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<LoanCharge> assembleSetOfCharges(final LoanApplicationCommand command, final Loan loan,
|
|
||||||
final LoanProduct product) {
|
|
||||||
|
|
||||||
Set<LoanCharge> charges = new HashSet<LoanCharge>();
|
|
||||||
LoanChargeCommand[] loanChargesArray = command.getCharges();
|
|
||||||
String loanCurrencyCode = loan.getCurrencyCode();
|
|
||||||
if (loanCurrencyCode == null){
|
|
||||||
loanCurrencyCode = product.getCurrency().getCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ObjectUtils.isEmpty(loanChargesArray)) {
|
|
||||||
for (LoanChargeCommand loanChargeCommand : loanChargesArray) {
|
|
||||||
Long id = loanChargeCommand.getId();
|
|
||||||
Charge charge = this.chargeRepository.findOne(id);
|
|
||||||
if (charge == null || charge.isDeleted()) {
|
|
||||||
throw new ChargeNotFoundException(id);
|
|
||||||
}
|
|
||||||
if (!charge.isActive()){
|
|
||||||
throw new ChargeIsNotActiveException(id);
|
|
||||||
}
|
|
||||||
if (!loanCurrencyCode.equals(charge.getCurrencyCode())){
|
|
||||||
String errorMessage = "Charge and Loan must have the same currency.";
|
|
||||||
throw new InvalidCurrencyException("charge", "attach.to.loan", errorMessage);
|
|
||||||
}
|
|
||||||
charges.add(new LoanCharge(loan, charge, loanChargeCommand));
|
|
||||||
}
|
|
||||||
} else if (loanChargesArray == null) {
|
|
||||||
for (Charge productCharge : product.getCharges()){
|
|
||||||
charges.add(new LoanCharge(loan, productCharge));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return charges;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -145,7 +145,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.assembleSetOfCharges(command, loan, loanProduct) ;
|
Set<LoanCharge> charges = this.loanAssembler.assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges()) ;
|
||||||
|
|
||||||
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command.toCalculateLoanScheduleCommand());
|
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command.toCalculateLoanScheduleCommand());
|
||||||
loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer);
|
loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer);
|
||||||
|
|||||||
Reference in New Issue
Block a user