MIFOSX-175: break out overdue fields into one-to-one table with shared primary key.

This commit is contained in:
Keith Woodlock
2013-03-04 13:48:47 +00:00
parent e491859cf5
commit 8d4aa69302
4 changed files with 138 additions and 58 deletions
@@ -24,6 +24,7 @@ import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
@@ -242,6 +243,9 @@ public class Loan extends AbstractPersistable<Long> {
@Embedded
private LoanSummary summary;
@OneToOne(mappedBy = "loan", cascade = CascadeType.ALL)
private LoanSummaryArrearsAging summaryArrearsAging;
@Transient
private boolean accountNumberRequiresAutoGeneration = false;
@Transient
@@ -315,9 +319,11 @@ public class Loan extends AbstractPersistable<Long> {
if (loanCharges != null && !loanCharges.isEmpty()) {
this.charges = associateChargesWithThisLoan(loanCharges);
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
this.summaryArrearsAging = new LoanSummaryArrearsAging(this);
} else {
this.charges = null;
this.summary = new LoanSummary();
this.summaryArrearsAging = new LoanSummaryArrearsAging(this);
}
if (collateral != null && !collateral.isEmpty()) {
this.collateral = associateWithThisLoan(collateral);
@@ -668,9 +674,11 @@ public class Loan extends AbstractPersistable<Long> {
if (isNotDisbursed()) {
this.summary.zeroFields();
this.summaryArrearsAging.zeroFields();
} else {
final Money principal = this.loanRepaymentScheduleDetail.getPrincipal();
this.summary.updateSummary(loanCurrency(), principal, this.repaymentScheduleInstallments, this.loanSummaryWrapper);
this.summaryArrearsAging.updateSummary(loanCurrency(), this.repaymentScheduleInstallments, this.loanSummaryWrapper);
}
}
@@ -1,16 +1,11 @@
package org.mifosplatform.portfolio.loanaccount.domain;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
@@ -33,9 +28,6 @@ public final class LoanSummary {
@Column(name = "principal_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalOutstanding;
@Column(name = "principal_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalOverdue;
@Column(name = "interest_charged_derived", scale = 6, precision = 19)
private BigDecimal totalInterestCharged;
@@ -51,9 +43,6 @@ public final class LoanSummary {
@Column(name = "interest_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalInterestOutstanding;
@Column(name = "interest_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalInterestOverdue;
@Column(name = "fee_charges_charged_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesCharged;
@@ -72,9 +61,6 @@ public final class LoanSummary {
@Column(name = "fee_charges_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesOutstanding;
@Column(name = "fee_charges_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesOverdue;
@Column(name = "penalty_charges_charged_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesCharged;
@@ -90,9 +76,6 @@ public final class LoanSummary {
@Column(name = "penalty_charges_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesOutstanding;
@Column(name = "penalty_charges_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesOverdue;
@SuppressWarnings("unused")
@Column(name = "total_expected_repayment_derived", scale = 6, precision = 19)
private BigDecimal totalExpectedRepayment;
@@ -120,15 +103,6 @@ public final class LoanSummary {
@Column(name = "total_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalOutstanding;
@SuppressWarnings("unused")
@Column(name = "total_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalOverdue;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "overdue_since_date_derived")
private Date overdueSinceDate;
public static LoanSummary create(final BigDecimal totalFeeChargesDueAtDisbursement) {
return new LoanSummary(totalFeeChargesDueAtDisbursement);
}
@@ -166,25 +140,21 @@ public final class LoanSummary {
this.totalPrincipalRepaid = BigDecimal.ZERO;
this.totalPrincipalWrittenOff = BigDecimal.ZERO;
this.totalPrincipalOutstanding = BigDecimal.ZERO;
this.totalPrincipalOverdue = BigDecimal.ZERO;
this.totalInterestCharged = BigDecimal.ZERO;
this.totalInterestRepaid = BigDecimal.ZERO;
this.totalInterestWaived = BigDecimal.ZERO;
this.totalInterestWrittenOff = BigDecimal.ZERO;
this.totalInterestOutstanding = BigDecimal.ZERO;
this.totalInterestOverdue = BigDecimal.ZERO;
this.totalFeeChargesCharged = BigDecimal.ZERO;
this.totalFeeChargesRepaid = BigDecimal.ZERO;
this.totalFeeChargesWaived = BigDecimal.ZERO;
this.totalFeeChargesWrittenOff = BigDecimal.ZERO;
this.totalFeeChargesOutstanding = BigDecimal.ZERO;
this.totalFeeChargesOverdue = BigDecimal.ZERO;
this.totalPenaltyChargesCharged = BigDecimal.ZERO;
this.totalPenaltyChargesRepaid = BigDecimal.ZERO;
this.totalPenaltyChargesWaived = BigDecimal.ZERO;
this.totalPenaltyChargesWrittenOff = BigDecimal.ZERO;
this.totalPenaltyChargesOutstanding = BigDecimal.ZERO;
this.totalPenaltyChargesOverdue = BigDecimal.ZERO;
this.totalExpectedRepayment = BigDecimal.ZERO;
this.totalRepayment = BigDecimal.ZERO;
this.totalExpectedCostOfLoan = BigDecimal.ZERO;
@@ -192,8 +162,6 @@ public final class LoanSummary {
this.totalWaived = BigDecimal.ZERO;
this.totalWrittenOff = BigDecimal.ZERO;
this.totalOutstanding = BigDecimal.ZERO;
this.totalOverdue = BigDecimal.ZERO;
this.overdueSinceDate = null;
}
public void updateSummary(final MonetaryCurrency currency, final Money principal,
@@ -205,8 +173,6 @@ public final class LoanSummary {
.getAmount();
this.totalPrincipalOutstanding = principal.minus(totalPrincipalRepaid).minus(totalPrincipalWrittenOff).getAmount();
this.totalPrincipalOverdue = summaryWrapper.calculateTotalPrincipalOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
final Money totalInterestCharged = summaryWrapper.calculateTotalInterestCharged(repaymentScheduleInstallments, currency);
this.totalInterestCharged = totalInterestCharged.getAmount();
@@ -217,8 +183,6 @@ public final class LoanSummary {
if (totalInterestCharged.isGreaterThanZero()) {
this.totalInterestOutstanding = totalInterestCharged.minus(this.totalInterestRepaid).minus(this.totalInterestWaived)
.minus(this.totalInterestWrittenOff).getAmount();
this.totalInterestOverdue = summaryWrapper.calculateTotalInterestOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
}
final Money totalFeeChargesCharged = summaryWrapper.calculateTotalFeeChargesCharged(repaymentScheduleInstallments, currency);
@@ -231,8 +195,6 @@ public final class LoanSummary {
if (totalFeeChargesCharged.isGreaterThanZero()) {
this.totalFeeChargesOutstanding = totalFeeChargesCharged.minus(this.totalFeeChargesRepaid).minus(this.totalFeeChargesWaived)
.minus(this.totalFeeChargesWrittenOff).getAmount();
this.totalFeeChargesOverdue = summaryWrapper.calculateTotalFeeChargesOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
}
final Money totalPenaltyChargesCharged = summaryWrapper
@@ -248,8 +210,6 @@ public final class LoanSummary {
if (totalPenaltyChargesCharged.isGreaterThanZero()) {
this.totalPenaltyChargesOutstanding = totalPenaltyChargesCharged.minus(this.totalPenaltyChargesRepaid)
.minus(this.totalPenaltyChargesWaived).minus(this.totalPenaltyChargesWrittenOff).getAmount();
this.totalPenaltyChargesOverdue = summaryWrapper.calculateTotalPenaltyChargesOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
}
final Money totalExpectedRepayment = Money.of(currency, this.totalPrincipalDisbursed).plus(this.totalInterestCharged)
@@ -279,17 +239,5 @@ public final class LoanSummary {
final Money totalOutstanding = Money.of(currency, this.totalPrincipalOutstanding).plus(this.totalInterestOutstanding)
.plus(this.totalFeeChargesOutstanding).plus(this.totalPenaltyChargesOutstanding);
this.totalOutstanding = totalOutstanding.getAmount();
final Money totalOverdue = Money.of(currency, this.totalPrincipalOverdue).plus(this.totalInterestOverdue)
.plus(this.totalFeeChargesOverdue).plus(this.totalPenaltyChargesOverdue);
this.totalOverdue = totalOverdue.getAmount();
final LocalDate overdueSinceLocalDate = summaryWrapper.determineOverdueSinceDateFrom(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant());
if (overdueSinceLocalDate != null) {
this.overdueSinceDate = overdueSinceLocalDate.toDate();
} else {
this.overdueSinceDate = null;
}
}
}
@@ -0,0 +1,123 @@
package org.mifosplatform.portfolio.loanaccount.domain;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.springframework.data.domain.Persistable;
/**
* <p>
* A specialist view of arrears & aging summary details of a {@link Loan}.
* <p>
*
* <p>
* This is intentionally split from the loan summary concept and maps to its own
* table that contains only arrears and aging information. The reason is to make
* any overnight batch update of these details simpler and improve performance.
* </p>
*/
@Entity
@Table(name = "m_loan_arrears_aging")
public final class LoanSummaryArrearsAging implements Persistable<Long> {
@Id
@GeneratedValue(generator = "SharedPrimaryKeyGenerator")
@GenericGenerator(name = "SharedPrimaryKeyGenerator", strategy = "foreign", parameters = @Parameter(name = "property", value = "loan"))
@Column(name = "loan_id", unique = true, nullable = false)
private Long id;
@Column(name = "principal_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalOverdue;
@Column(name = "interest_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalInterestOverdue;
@Column(name = "fee_charges_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesOverdue;
@Column(name = "penalty_charges_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesOverdue;
@SuppressWarnings("unused")
@Column(name = "total_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalOverdue;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "overdue_since_date_derived")
private Date overdueSinceDate;
@OneToOne
@PrimaryKeyJoinColumn
private Loan loan;
protected LoanSummaryArrearsAging() {
//
}
public LoanSummaryArrearsAging(final Loan loan) {
this.loan = loan;
zeroFields();
}
@Override
public Long getId() {
return this.id;
}
@Override
public boolean isNew() {
return null == getId();
}
public void zeroFields() {
this.totalPrincipalOverdue = BigDecimal.ZERO;
this.totalInterestOverdue = BigDecimal.ZERO;
this.totalFeeChargesOverdue = BigDecimal.ZERO;
this.totalPenaltyChargesOverdue = BigDecimal.ZERO;
this.totalOverdue = BigDecimal.ZERO;
this.overdueSinceDate = null;
}
public void updateSummary(final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final LoanSummaryWrapper summaryWrapper) {
this.totalPrincipalOverdue = summaryWrapper.calculateTotalPrincipalOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
this.totalInterestOverdue = summaryWrapper.calculateTotalInterestOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
this.totalFeeChargesOverdue = summaryWrapper.calculateTotalFeeChargesOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
this.totalPenaltyChargesOverdue = summaryWrapper.calculateTotalPenaltyChargesOverdueOn(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
final Money totalOverdue = Money.of(currency, this.totalPrincipalOverdue).plus(this.totalInterestOverdue)
.plus(this.totalFeeChargesOverdue).plus(this.totalPenaltyChargesOverdue);
this.totalOverdue = totalOverdue.getAmount();
final LocalDate overdueSinceLocalDate = summaryWrapper.determineOverdueSinceDateFrom(repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant());
if (overdueSinceLocalDate != null) {
this.overdueSinceDate = overdueSinceLocalDate.toDate();
} else {
this.overdueSinceDate = null;
}
}
}
@@ -283,26 +283,22 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.principal_repaid_derived as principalPaid,"
+ " l.principal_writtenoff_derived as principalWrittenOff,"
+ " l.principal_outstanding_derived as principalOutstanding,"
+ " l.principal_overdue_derived as principalOverdue,"
+ " l.interest_charged_derived as interestCharged,"
+ " l.interest_repaid_derived as interestPaid,"
+ " l.interest_waived_derived as interestWaived,"
+ " l.interest_writtenoff_derived as interestWrittenOff,"
+ " l.interest_outstanding_derived as interestOutstanding,"
+ " l.interest_overdue_derived as interestOverdue,"
+ " l.fee_charges_charged_derived as feeChargesCharged,"
+ " l.total_charges_due_at_disbursement_derived as feeChargesDueAtDisbursementCharged,"
+ " l.fee_charges_repaid_derived as feeChargesPaid,"
+ " l.fee_charges_waived_derived as feeChargesWaived,"
+ " l.fee_charges_writtenoff_derived as feeChargesWrittenOff,"
+ " l.fee_charges_outstanding_derived as feeChargesOutstanding,"
+ " l.fee_charges_overdue_derived as feeChargesOverdue,"
+ " l.penalty_charges_charged_derived as penaltyChargesCharged,"
+ " l.penalty_charges_repaid_derived as penaltyChargesPaid,"
+ " l.penalty_charges_waived_derived as penaltyChargesWaived,"
+ " l.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff,"
+ " l.penalty_charges_outstanding_derived as penaltyChargesOutstanding,"
+ " l.penalty_charges_overdue_derived as penaltyChargesOverdue,"
+ " l.total_expected_repayment_derived as totalExpectedRepayment,"
+ " l.total_repayment_derived as totalRepayment,"
+ " l.total_expected_costofloan_derived as totalExpectedCostOfLoan,"
@@ -310,13 +306,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.total_waived_derived as totalWaived,"
+ " l.total_writtenoff_derived as totalWrittenOff,"
+ " l.total_outstanding_derived as totalOutstanding,"
+ " l.total_overdue_derived as totalOverdue,"
+ " l.overdue_since_date_derived as overdueSinceDate"
+ " la.principal_overdue_derived as principalOverdue,"
+ " la.interest_overdue_derived as interestOverdue,"
+ " la.fee_charges_overdue_derived as feeChargesOverdue,"
+ " la.penalty_charges_overdue_derived as penaltyChargesOverdue,"
+ " la.total_overdue_derived as totalOverdue,"
+ " la.overdue_since_date_derived as overdueSinceDate"
+ " from m_loan l" //
+ " left join m_client c on c.id = l.client_id" //
+ " left join m_group g on g.id = l.group_id" //
+ " join m_product_loan lp on lp.id = l.product_id" //
+ " join m_currency rc on rc.`code` = l.currency_code" //
+ " join m_loan_arrears_aging la on la.loan_id = l.id" //
+ " left join m_fund f on f.id = l.fund_id" //
+ " left join m_staff s on s.id = l.loan_officer_id" //
+ " left join m_appuser sbu on sbu.id = l.submittedon_userid"