From 8d4aa69302235a0bef5be8fd6bf9e891f99d1f9f Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Mon, 4 Mar 2013 13:07:21 +0000 Subject: [PATCH] MIFOSX-175: break out overdue fields into one-to-one table with shared primary key. --- .../portfolio/loanaccount/domain/Loan.java | 8 ++ .../loanaccount/domain/LoanSummary.java | 52 -------- .../domain/LoanSummaryArrearsAging.java | 123 ++++++++++++++++++ .../service/LoanReadPlatformServiceImpl.java | 13 +- 4 files changed, 138 insertions(+), 58 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummaryArrearsAging.java diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 01dcb4f5b..19a19489c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -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 { @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 { 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 { 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); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java index 9318a71ab..646bc7faf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java @@ -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; - } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummaryArrearsAging.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummaryArrearsAging.java new file mode 100644 index 000000000..447d2964f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummaryArrearsAging.java @@ -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; + +/** + *

+ * A specialist view of arrears & aging summary details of a {@link Loan}. + *

+ * + *

+ * 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. + *

+ */ +@Entity +@Table(name = "m_loan_arrears_aging") +public final class LoanSummaryArrearsAging implements Persistable { + + @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 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; + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 7123548fa..0146eec03 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -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"