diff --git a/mifosng-db/patches/patch-0003-add-extra-fields-for-written-off-values.sql b/mifosng-db/patches/patch-0003-add-extra-fields-for-written-off-values.sql
new file mode 100644
index 000000000..c03f5921b
--- /dev/null
+++ b/mifosng-db/patches/patch-0003-add-extra-fields-for-written-off-values.sql
@@ -0,0 +1,3 @@
+alter table m_loan_repayment_schedule add column principal_writtenoff_derived decimal(19,6) DEFAULT NULL after principal_completed_derived;
+alter table m_loan_repayment_schedule add column interest_writtenoff_derived decimal(19,6) DEFAULT NULL after interest_completed_derived;
+
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanScheduleNewData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanScheduleData.java
similarity index 81%
rename from mifosng-provider/src/main/java/org/mifosng/platform/api/LoanScheduleNewData.java
rename to mifosng-provider/src/main/java/org/mifosng/platform/api/LoanScheduleData.java
index ec987e8fc..be031a8b9 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanScheduleNewData.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoanScheduleData.java
@@ -14,42 +14,57 @@ import org.mifosng.platform.api.data.LoanSchedulePeriodData;
*
repayment schedule - the principal due, outstanding balance and cost of loan items such as interest and charges
*
*/
-// TODO - KW - rename file
-@SuppressWarnings("unused")
-public class LoanScheduleNewData {
+public class LoanScheduleData {
+ @SuppressWarnings("unused")
private final Integer loanTermInDays;
/**
* The currency associated with all monetary values in loan schedule.
*/
+ @SuppressWarnings("unused")
private final CurrencyData currency;
/**
* The cumulative total of all money (principal) disbursed to the loan applicant.
*/
+ @SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalDisbursed;
/**
* The cumulative total of all principalDue for each period of loan schedule. (originalPrincipal)
*/
+ @SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalDue;
+ @SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalPaid;
+ @SuppressWarnings("unused")
+ private final BigDecimal cumulativePrincipalWrittenOff;
+ @SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalOutstanding;
/**
* The cumulative total of interest expected on any principal disbursed. (originalInterest)
*/
+ @SuppressWarnings("unused")
private final BigDecimal cumulativeInterestExpected;
+ @SuppressWarnings("unused")
private final BigDecimal cumulativeInterestPaid;
+ @SuppressWarnings("unused")
private final BigDecimal cumulativeInterestWaived;
+ @SuppressWarnings("unused")
+ private final BigDecimal cumulativeInterestWrittenOff;
+ @SuppressWarnings("unused")
private final BigDecimal cumulativeInterestOutstanding;
/**
* The cumulative total of all charges applied on the loan to date.
*/
+ @SuppressWarnings("unused")
private final BigDecimal cumulativeChargesToDate;
+ @SuppressWarnings("unused")
private final BigDecimal cumulativeChargesPaid;
+ @SuppressWarnings("unused")
private final BigDecimal cumulativeChargesOutstanding;
/**
@@ -60,23 +75,33 @@ public class LoanScheduleNewData {
* Charges (fees and penalties)
*
*/
+ @SuppressWarnings("unused")
private final BigDecimal totalCostOfLoan;
/**
* The sum of principalDisbursed and totalCostOfLoan. (originalTotal)
*/
+ @SuppressWarnings("unused")
private final BigDecimal totalExpectedRepayment;
/**
* The cumulative sum of all repayments to date. (totalPaid)
*/
+ @SuppressWarnings("unused")
private final BigDecimal totalPaidToDate;
/**
* The cumulative sum of all waivers to date.
*/
+ @SuppressWarnings("unused")
private final BigDecimal totalWaivedToDate;
+ /**
+ * The cumulative sum of all waivers to date.
+ */
+ @SuppressWarnings("unused")
+ private final BigDecimal totalWrittenOffToDate;
+
/**
* The cumulative sum of all principal, interest & charges outstanding.
*/
@@ -87,6 +112,7 @@ public class LoanScheduleNewData {
*
* Different to outstanding as arrears is based on whether a portion of the total outstanding is late and above any tolerance setting.
*/
+ @SuppressWarnings("unused")
private final BigDecimal totalInArrears;
/**
@@ -96,15 +122,17 @@ public class LoanScheduleNewData {
*/
private final Collection periods;
- public LoanScheduleNewData() {
+ public LoanScheduleData() {
this.cumulativePrincipalDisbursed = null;
this.cumulativePrincipalPaid = null;
+ this.cumulativePrincipalWrittenOff = null;
this.cumulativePrincipalDue = null;
this.cumulativePrincipalOutstanding = null;
this.cumulativeInterestExpected = null;
this.cumulativeInterestPaid = null;
this.cumulativeInterestWaived = null;
+ this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null;
this.cumulativeChargesToDate = null;
@@ -116,6 +144,7 @@ public class LoanScheduleNewData {
this.totalExpectedRepayment = null;
this.totalPaidToDate = null;
this.totalWaivedToDate = null;
+ this.totalWrittenOffToDate = null;
this.totalOutstanding = null;
this.totalInArrears = null;
@@ -124,7 +153,7 @@ public class LoanScheduleNewData {
this.loanTermInDays = null;
}
- public LoanScheduleNewData(
+ public LoanScheduleData(
final CurrencyData currency,
final Collection periods,
final Integer loanTermInDays,
@@ -141,11 +170,13 @@ public class LoanScheduleNewData {
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
this.cumulativePrincipalDue = cumulativePrincipalDue;
this.cumulativePrincipalPaid = null;
+ this.cumulativePrincipalWrittenOff = null;
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
this.cumulativeInterestExpected = cumulativeInterestExpected;
this.cumulativeInterestPaid = null;
this.cumulativeInterestWaived = null;
+ this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null;
this.cumulativeChargesToDate = cumulativeChargesToDate;
@@ -163,6 +194,7 @@ public class LoanScheduleNewData {
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalPaidToDate = BigDecimal.ZERO;
this.totalWaivedToDate = BigDecimal.ZERO;
+ this.totalWrittenOffToDate = BigDecimal.ZERO;
this.totalOutstanding = totalExpectedRepayment;
this.totalInArrears = BigDecimal.ZERO;
}
@@ -170,15 +202,17 @@ public class LoanScheduleNewData {
/*
* Used when fully populating
*/
- public LoanScheduleNewData(final CurrencyData currency, final Collection periods,
+ public LoanScheduleData(final CurrencyData currency, final Collection periods,
final Integer loanTermInDays,
final BigDecimal cumulativePrincipalDisbursed,
final BigDecimal cumulativePrincipalDue,
final BigDecimal cumulativePrincipalPaid,
+ final BigDecimal cumulativePrincipalWrittenOff,
final BigDecimal cumulativePrincipalOutstanding,
final BigDecimal cumulativeInterestExpected,
final BigDecimal cumulativeInterestPaid,
final BigDecimal cumulativeInterestWaived,
+ final BigDecimal cumulativeInterestWrittenOff,
final BigDecimal cumulativeInterestOutstanding,
final BigDecimal cumulativeChargesToDate,
final BigDecimal cumulativeChargesPaid,
@@ -187,6 +221,7 @@ public class LoanScheduleNewData {
final BigDecimal totalExpectedRepayment,
final BigDecimal totalPaidToDate,
final BigDecimal totalWaivedToDate,
+ final BigDecimal totalWrittenOffToDate,
final BigDecimal totalOutstanding,
final BigDecimal totalInArrears) {
this.currency = currency;
@@ -196,9 +231,11 @@ public class LoanScheduleNewData {
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
this.cumulativePrincipalDue = cumulativePrincipalDue;
this.cumulativePrincipalPaid = cumulativePrincipalPaid;
+ this.cumulativePrincipalWrittenOff = cumulativePrincipalWrittenOff;
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
this.cumulativeInterestExpected = cumulativeInterestExpected;
this.cumulativeInterestPaid = cumulativeInterestPaid;
+ this.cumulativeInterestWrittenOff = cumulativeInterestWrittenOff;
this.cumulativeInterestWaived = cumulativeInterestWaived;
this.cumulativeInterestOutstanding = cumulativeInterestOutstanding;
this.cumulativeChargesToDate = cumulativeChargesToDate;
@@ -208,6 +245,7 @@ public class LoanScheduleNewData {
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalPaidToDate = totalPaidToDate;
this.totalWaivedToDate = totalWaivedToDate;
+ this.totalWrittenOffToDate = totalWrittenOffToDate;
this.totalOutstanding = totalOutstanding;
this.totalInArrears = totalInArrears;
}
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 c6478ffc3..25ceeae22 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
@@ -38,7 +38,6 @@ import org.mifosng.platform.api.data.LoanBasicDetailsData;
import org.mifosng.platform.api.data.LoanChargeData;
import org.mifosng.platform.api.data.LoanPermissionData;
import org.mifosng.platform.api.data.LoanProductLookup;
-import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.LoanTransactionNewData;
import org.mifosng.platform.api.data.MoneyData;
@@ -191,8 +190,8 @@ public class LoansApiResource {
LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId);
int loanRepaymentsCount = 0;
- Collection loanRepayments = null;
- LoanScheduleNewData repaymentSchedule = null;
+ Collection loanRepayments = null;
+ LoanScheduleData repaymentSchedule = null;
LoanPermissionData permissions = null;
Collection charges = null;
@@ -201,18 +200,21 @@ public class LoansApiResource {
if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) {
- responseParameters.addAll(Arrays.asList("repaymentSchedule", "loanRepayments", "permissions", "convenienceData", "charges"));
+ responseParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", "charges"));
} else {
responseParameters.addAll(associationParameters);
}
- Collection currentLoanRepayments = this.loanReadPlatformService.retrieveLoanPayments(loanId);
- if (!CollectionUtils.isEmpty(currentLoanRepayments)) {
- loanRepayments = currentLoanRepayments;
- loanRepaymentsCount = loanRepayments.size();
+ if (responseParameters.contains("transactions")) {
+ Collection currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(loanId);
+ if (!CollectionUtils.isEmpty(currentLoanRepayments)) {
+ loanRepayments = currentLoanRepayments;
+ loanRepaymentsCount = loanRepayments.size();
+ }
}
if (responseParameters.contains("repaymentSchedule")) {
+
DisbursementData singleDisbursement = loanBasicDetails.toDisburementData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(),
singleDisbursement, loanBasicDetails.getTotalDisbursementCharges(), loanBasicDetails.getInArrearsTolerance());
@@ -275,7 +277,8 @@ public class LoansApiResource {
repaymentSchedule, loanRepayments, permissions, charges,
productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions,
- amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers);
+ amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
+ fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers);
return this.apiJsonSerializerService.serializeLoanAccountDataToJson(prettyPrint, responseParameters, loanAccount);
}
@@ -302,7 +305,7 @@ public class LoansApiResource {
private String calculateLoanSchedule(final UriInfo uriInfo, final CalculateLoanScheduleCommand command) {
- final LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command);
+ final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command);
final Set typicalLoanScheduleResponseParameters = new HashSet(
Arrays.asList("periods", "cumulativePrincipalDisbursed"));
@@ -406,26 +409,24 @@ public class LoansApiResource {
@QueryParam("command") final String commandParam,
final String jsonRequestBody) {
- final LoanTransactionCommand command = this.apiDataConversionService
- .convertJsonToLoanTransactionCommand(loanId, jsonRequestBody);
-
- Response response = null;
+ final LoanTransactionCommand command = this.apiDataConversionService.convertJsonToLoanTransactionCommand(loanId, jsonRequestBody);
+ EntityIdentifier identifier = null;
if (is(commandParam, "repayment")) {
- EntityIdentifier identifier = this.loanWritePlatformService
- .makeLoanRepayment(command);
- response = Response.ok().entity(identifier).build();
+ identifier = this.loanWritePlatformService.makeLoanRepayment(command);
} else if (is(commandParam, "waiveinterest")) {
- EntityIdentifier identifier = this.loanWritePlatformService
- .waiveInterestOnLoan(command);
- response = Response.ok().entity(identifier).build();
+ identifier = this.loanWritePlatformService.waiveInterestOnLoan(command);
+ } else if (is(commandParam, "writeoff")) {
+ identifier = this.loanWritePlatformService.writeOff(command);
+ } else if (is(commandParam, "closeasrescheduled")) {
+ identifier = this.loanWritePlatformService.closeAsRescheduled(command);
}
- if (response == null) {
+ if (identifier == null) {
throw new UnrecognizedQueryParamException("command", commandParam);
}
- return response;
+ return Response.ok().entity(identifier).build();
}
@GET
@@ -440,8 +441,8 @@ public class LoansApiResource {
LoanTransactionData transactionData = null;
if (is(commandParam, "repayment")) {
Set typicalResponseParameters = new HashSet(
- Arrays.asList("id", "transactionType", "date", "principal", "interest", "total", "totalWaived", "overpaid")
- );
+ Arrays.asList("id", "transactionType", "date", "principal",
+ "interest", "total", "totalWaived", "overpaid"));
Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
@@ -460,7 +461,29 @@ public class LoansApiResource {
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
- LoanTransactionNewData newTransactionData = this.loanReadPlatformService.retrieveNewLoanWaiveInterestDetails(loanId);
+ final LoanTransactionNewData newTransactionData = this.loanReadPlatformService.retrieveNewLoanWaiveInterestDetails(loanId);
+ return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, newTransactionData);
+ } else if (is(commandParam, "writeoff")) {
+ final Set typicalResponseParameters = new HashSet(Arrays.asList("id", "type", "date", "currency", "amount"));
+
+ Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
+ if (responseParameters.isEmpty()) {
+ responseParameters.addAll(typicalResponseParameters);
+ }
+ boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
+
+ final LoanTransactionNewData newTransactionData = this.loanReadPlatformService.retrieveNewClosureDetails();
+ return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, newTransactionData);
+ } else if (is(commandParam, "closeasrescheduled")) {
+ final Set typicalResponseParameters = new HashSet(Arrays.asList("id", "type", "date", "currency", "amount"));
+
+ Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
+ if (responseParameters.isEmpty()) {
+ responseParameters.addAll(typicalResponseParameters);
+ }
+ boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
+
+ final LoanTransactionNewData newTransactionData = this.loanReadPlatformService.retrieveNewClosureDetails();
return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, newTransactionData);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java
index fd6e22237..3408203eb 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java
@@ -4,7 +4,7 @@ import java.math.BigDecimal;
import java.util.Collection;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
/**
* Immutable data object representing loan account data.
@@ -55,8 +55,8 @@ public class LoanAccountData {
private final LocalDate lifeCycleStatusDate;
// associations
- private final LoanScheduleNewData repaymentSchedule;
- private final Collection loanRepayments;
+ private final LoanScheduleData repaymentSchedule;
+ private final Collection transactions;
private final Collection charges;
private final LoanPermissionData permissions;
private final LoanConvenienceData convenienceData;
@@ -78,8 +78,8 @@ public class LoanAccountData {
public LoanAccountData(
final LoanBasicDetailsData basicDetails,
final boolean convenienceDataRequired,
- final LoanScheduleNewData repaymentSchedule,
- final Collection loanRepayments,
+ final LoanScheduleData repaymentSchedule,
+ final Collection transactions,
final LoanPermissionData permissions,
final Collection charges,
final Collection productOptions,
@@ -95,7 +95,7 @@ public class LoanAccountData {
final ChargeData chargeTemplate,
final Collection loanOfficerOptions) {
this.repaymentSchedule = repaymentSchedule;
- this.loanRepayments = loanRepayments;
+ this.transactions = transactions;
this.permissions = permissions;
this.charges = charges;
this.productOptions = productOptions;
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanRepaymentTransactionData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanRepaymentTransactionData.java
deleted file mode 100644
index 5db7e0458..000000000
--- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanRepaymentTransactionData.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.mifosng.platform.api.data;
-
-import org.joda.time.LocalDate;
-
-/**
- * This data object replaces {@link LoanTransactionData}.
- */
-public class LoanRepaymentTransactionData {
-
- private Long id;
- private EnumOptionData transactionType;
- private LocalDate date;
- private MoneyData total;
-
- public LoanRepaymentTransactionData() {
- //
- }
-
- public LoanRepaymentTransactionData(Long id, EnumOptionData transactionType,
- final LocalDate date, final MoneyData total) {
- this.id = id;
- this.transactionType = transactionType;
- this.date = date;
- this.total = total;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public EnumOptionData getTransactionType() {
- return transactionType;
- }
-
- public void setTransactionType(EnumOptionData transactionType) {
- this.transactionType = transactionType;
- }
-
- public LocalDate getDate() {
- return date;
- }
-
- public void setDate(LocalDate date) {
- this.date = date;
- }
-
- public MoneyData getTotal() {
- return total;
- }
-
- public void setTotal(MoneyData total) {
- this.total = total;
- }
-
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java
index 40645bc63..e8620d7ef 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java
@@ -9,7 +9,6 @@ import org.joda.time.LocalDate;
* Immutable data object that represents a period of a loan schedule.
*
*/
-@SuppressWarnings("unused")
public class LoanSchedulePeriodData {
private final Integer period;
@@ -17,26 +16,37 @@ public class LoanSchedulePeriodData {
private final LocalDate dueDate;
private final Integer daysInPeriod;
private final BigDecimal principalDisbursed;
+ @SuppressWarnings("unused")
private final BigDecimal principalOriginalDue;
private final BigDecimal principalDue;
private final BigDecimal principalPaid;
+ private final BigDecimal principalWrittenOff;
private final BigDecimal principalOutstanding;
+ @SuppressWarnings("unused")
private final BigDecimal principalLoanBalanceOutstanding;
+ @SuppressWarnings("unused")
private final BigDecimal interestOriginalDue;
private final BigDecimal interestDue;
private final BigDecimal interestPaid;
private final BigDecimal interestWaived;
+ private final BigDecimal interestWrittenOff;
private final BigDecimal interestOutstanding;
private final BigDecimal chargesDue;
private final BigDecimal chargesPaid;
private final BigDecimal chargesOutstanding;
+ @SuppressWarnings("unused")
private final BigDecimal totalOriginalDueForPeriod;
+ @SuppressWarnings("unused")
private final BigDecimal totalDueForPeriod;
+ @SuppressWarnings("unused")
private final BigDecimal totalPaidForPeriod;
+ @SuppressWarnings("unused")
private final BigDecimal totalWaivedForPeriod;
+ @SuppressWarnings("unused")
+ private final BigDecimal totalWrittenOffForPeriod;
private final BigDecimal totalOutstandingForPeriod;
private final BigDecimal totalOverdue;
@@ -54,32 +64,34 @@ public class LoanSchedulePeriodData {
final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principalDue,
final BigDecimal principalOutstanding, final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal totalDueForPeriod) {
- BigDecimal principalDisbursed = null;
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding, interestDueOnPrincipalOutstanding, totalDueForPeriod);
}
public static LoanSchedulePeriodData repaymentPeriodWithPayments(
- final Long loanId,
+ @SuppressWarnings("unused") final Long loanId,
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalOriginalDue,
final BigDecimal principalPaid,
+ final BigDecimal principalWrittenOff,
final BigDecimal principalOutstanding,
final BigDecimal outstandingPrincipalBalanceOfLoan,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal interestPaid,
final BigDecimal interestWaived,
+ final BigDecimal interestWrittenOff,
final BigDecimal interestOutstanding,
final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid,
final BigDecimal totalWaived,
+ final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding) {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate,
- principalOriginalDue, principalPaid, principalOutstanding, outstandingPrincipalBalanceOfLoan,
- interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestOutstanding,
- totalDueForPeriod, totalPaid, totalWaived, totalOutstanding);
+ principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
+ interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
+ totalDueForPeriod, totalPaid, totalWaived, totalWrittenOff, totalOutstanding);
}
/*
@@ -96,7 +108,7 @@ public class LoanSchedulePeriodData {
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
- this.daysInPeriod = Days.daysBetween(fromDate, dueDate).getDays();
+ this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
@@ -104,6 +116,7 @@ public class LoanSchedulePeriodData {
this.principalOriginalDue = null;
this.principalDue = null;
this.principalPaid = null;
+ this.principalWrittenOff = null;
this.principalOutstanding = null;
this.principalLoanBalanceOutstanding = principalDisbursed;
@@ -111,6 +124,7 @@ public class LoanSchedulePeriodData {
this.interestDue = null;
this.interestPaid = null;
this.interestWaived = null;
+ this.interestWrittenOff = null;
this.interestOutstanding = null;
this.chargesDue = chargesDueAtTimeOfDisbursement;
@@ -126,6 +140,7 @@ public class LoanSchedulePeriodData {
this.totalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalPaidForPeriod = this.chargesPaid;
this.totalWaivedForPeriod = null;
+ this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = this.chargesOutstanding;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
@@ -146,7 +161,7 @@ public class LoanSchedulePeriodData {
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
- this.daysInPeriod = Days.daysBetween(fromDate, dueDate).getDays();
+ this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
@@ -154,6 +169,7 @@ public class LoanSchedulePeriodData {
this.principalOriginalDue = principalOriginalDue;
this.principalDue = principalOriginalDue;
this.principalPaid = null;
+ this.principalWrittenOff = null;
this.principalOutstanding = principalOriginalDue;
this.principalLoanBalanceOutstanding = principalOutstanding;
@@ -161,6 +177,7 @@ public class LoanSchedulePeriodData {
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = null;
this.interestWaived = null;
+ this.interestWrittenOff = null;
this.interestOutstanding = interestDueOnPrincipalOutstanding;
this.chargesDue = BigDecimal.ZERO;
@@ -171,6 +188,7 @@ public class LoanSchedulePeriodData {
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = null;
+ this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = totalDueForPeriod;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
@@ -188,21 +206,24 @@ public class LoanSchedulePeriodData {
final LocalDate dueDate,
final BigDecimal principalOriginalDue,
final BigDecimal principalPaid,
+ final BigDecimal principalWrittenOff,
final BigDecimal principalOutstanding,
final BigDecimal principalLoanBalanceOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal interestPaid,
- final BigDecimal interestWaived,
+ final BigDecimal interestWaived,
+ final BigDecimal interestWrittenOff,
final BigDecimal interestOutstanding,
final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid,
final BigDecimal totalWaived,
+ final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
- this.daysInPeriod = Days.daysBetween(fromDate, dueDate).getDays();
+ this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
@@ -210,6 +231,7 @@ public class LoanSchedulePeriodData {
this.principalOriginalDue = principalOriginalDue;
this.principalDue = principalOriginalDue;
this.principalPaid = principalPaid;
+ this.principalWrittenOff = principalWrittenOff;
this.principalOutstanding = principalOutstanding;
this.principalLoanBalanceOutstanding = principalLoanBalanceOutstanding;
@@ -217,6 +239,7 @@ public class LoanSchedulePeriodData {
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = interestPaid;
this.interestWaived = interestWaived;
+ this.interestWrittenOff = interestWrittenOff;
this.interestOutstanding = interestOutstanding;
this.chargesDue = BigDecimal.ZERO;
@@ -227,6 +250,7 @@ public class LoanSchedulePeriodData {
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = totalPaid;
this.totalWaivedForPeriod = totalWaived;
+ this.totalWrittenOffForPeriod = totalWrittenOff;
this.totalOutstandingForPeriod = totalOutstanding;
if (dueDate.isBefore(new LocalDate())) {
@@ -276,6 +300,10 @@ public class LoanSchedulePeriodData {
return defaultToZeroIfNull(this.principalPaid);
}
+ public BigDecimal principalWrittenOff() {
+ return defaultToZeroIfNull(this.principalWrittenOff);
+ }
+
public BigDecimal principalOutstanding() {
return defaultToZeroIfNull(this.principalOutstanding);
}
@@ -291,6 +319,10 @@ public class LoanSchedulePeriodData {
public BigDecimal interestWaived() {
return defaultToZeroIfNull(this.interestWaived);
}
+
+ public BigDecimal interestWrittenOff() {
+ return defaultToZeroIfNull(this.interestWrittenOff);
+ }
public BigDecimal interestOutstanding() {
return defaultToZeroIfNull(this.interestOutstanding);
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanTransactionNewData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanTransactionNewData.java
index 91ca339ca..10c84bf96 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanTransactionNewData.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanTransactionNewData.java
@@ -25,11 +25,13 @@ public class LoanTransactionNewData {
private BigDecimal amount;
public LoanTransactionNewData(
- final EnumOptionData transactionsType,
+ final Long id,
+ final EnumOptionData transactionType,
final CurrencyData currency,
final LocalDate date,
final BigDecimal amount) {
- this.type = transactionsType;
+ this.id = id;
+ this.type = transactionType;
this.currency = currency;
this.date = date;
this.amount = amount;
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java
index 4f744313b..6bb839384 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java
@@ -3,7 +3,7 @@ package org.mifosng.platform.api.infrastructure;
import java.util.Collection;
import java.util.Set;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.data.AdditionalFieldsSetData;
import org.mifosng.platform.api.data.AppUserData;
import org.mifosng.platform.api.data.AuthenticatedUserData;
@@ -99,7 +99,7 @@ public interface ApiJsonSerializerService {
String serializeNoteDataToJson(boolean prettyPrint, Set responseParameters, NoteData note);
- String serializeLoanScheduleDataToJson(boolean prettyPrint, Set responseParameters, LoanScheduleNewData loanSchedule);
+ String serializeLoanScheduleDataToJson(boolean prettyPrint, Set responseParameters, LoanScheduleData loanSchedule);
String serializeLoanAccountDataToJson(boolean prettyPrint, Set responseParameters, LoanAccountData loanAccount);
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java
index af82dedce..bc2d02300 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java
@@ -5,7 +5,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.data.AdditionalFieldsSetData;
import org.mifosng.platform.api.data.AppUserData;
import org.mifosng.platform.api.data.AuthenticatedUserData;
@@ -172,7 +172,7 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
"closedOnDate", "expectedMaturityDate",
"status",
"lifeCycleStatusDate", "repaymentSchedule",
- "loanRepayments", "permissions", "convenienceData", "charges",
+ "transactions", "permissions", "convenienceData", "charges",
"productOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
"loanOfficerId", "loanOfficerName", "loanOfficerOptions", "chargeTemplate"));
@@ -535,7 +535,7 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
}
@Override
- public String serializeLoanScheduleDataToJson(final boolean prettyPrint, final Set responseParameters, final LoanScheduleNewData loanSchedule) {
+ public String serializeLoanScheduleDataToJson(final boolean prettyPrint, final Set responseParameters, final LoanScheduleData loanSchedule) {
final Gson gsonDeserializer = helper
.createGsonBuilderWithParameterExclusionSerializationStrategy(LOAN_SCHEDULE_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, loanSchedule);
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java
index 2b3c8d337..3cc07e0a8 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -23,7 +23,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
*/
@Override
public void handleTransaction(
- final List repaymentsOrWaivers,
+ final List transactionsPostDisbursement,
final MonetaryCurrency currency,
final List installments) {
@@ -31,10 +31,15 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
currentInstallment.resetDerivedComponents();
}
- for (LoanTransaction loanTransaction : repaymentsOrWaivers) {
+ for (LoanTransaction loanTransaction : transactionsPostDisbursement) {
- loanTransaction.resetDerivedComponents();
- handleTransaction(loanTransaction, currency, installments);
+ if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver()) {
+ loanTransaction.resetDerivedComponents();
+ handleTransaction(loanTransaction, currency, installments);
+ } else if (loanTransaction.isWriteOff()) {
+ loanTransaction.resetDerivedComponents();
+ handleWriteOff(loanTransaction, currency, installments);
+ }
}
}
@@ -76,6 +81,29 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
}
}
+
+ @Override
+ public void handleWriteOff(
+ final LoanTransaction loanTransaction,
+ final MonetaryCurrency currency,
+ final List installments) {
+
+ final Money interestWaivedPortion = Money.zero(currency);
+ Money principalPortion = Money.zero(currency);
+ Money interestPortion = Money.zero(currency);
+ Money chargesPortion = Money.zero(currency);
+
+ // determine how much is written off in total and breakdown for principal, interest and charges
+ for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
+
+ if (currentInstallment.isNotFullyCompleted()) {
+ principalPortion = principalPortion.plus(currentInstallment.writeOffOutstandingPrincipal(currency));
+ interestPortion = interestPortion.plus(currentInstallment.writeOffOutstandingInterest(currency));
+ }
+ }
+
+ loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
+ }
// abstract interface
/**
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java
index db265d84e..7575b0f61 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java
@@ -68,7 +68,7 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
- if (loanTransaction.isWaiver()) {
+ if (loanTransaction.isInterestWaiver()) {
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
@@ -135,7 +135,7 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
- if (loanTransaction.isWaiver()) {
+ if (loanTransaction.isInterestWaiver()) {
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
} else {
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java
index 3900077c7..bbaee3124 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java
@@ -84,7 +84,7 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
- if (loanTransaction.isWaiver()) {
+ if (loanTransaction.isInterestWaiver()) {
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
} else {
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java
index d34c04fdc..a2ab6a9c9 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java
@@ -53,7 +53,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
}
break;
case LOAN_REPAYMENT_OR_WAIVER:
- if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED, LoanStatus.OVERPAID)) {
+ if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
newState = stateOf(LoanStatus.ACTIVE, allowedLoanStatuses);
} else {
newState = from;
@@ -61,26 +61,26 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
break;
case REPAID_IN_FULL:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
- newState = stateOf(LoanStatus.CLOSED, allowedLoanStatuses);
+ newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, allowedLoanStatuses);
}
break;
- case LOAN_WRITE_OFF:
+ case WRITE_OFF_OUTSTANDING:
if (this.anyOfAllowedWhenComingFrom(from,LoanStatus.ACTIVE)) {
- newState = stateOf(LoanStatus.CLOSED, allowedLoanStatuses);
+ newState = stateOf(LoanStatus.CLOSED_WRITTEN_OFF, allowedLoanStatuses);
}
break;
case LOAN_RESCHEDULE:
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
- newState = stateOf(LoanStatus.CLOSED, allowedLoanStatuses);
+ newState = stateOf(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT, allowedLoanStatuses);
}
break;
case INTERST_REBATE_OWED:
- if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED)) {
- newState = stateOf(LoanStatus.CLOSED, allowedLoanStatuses);
+ if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
+ newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, allowedLoanStatuses);
}
break;
case LOAN_OVERPAYMENT:
- if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED, LoanStatus.ACTIVE)) {
+ if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.ACTIVE)) {
newState = stateOf(LoanStatus.OVERPAID, allowedLoanStatuses);
}
break;
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
index 925f23dc5..dc9d945d1 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java
@@ -73,7 +73,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
- if (loanTransaction.isWaiver()) {
+ if (loanTransaction.isInterestWaiver()) {
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
} else {
@@ -116,7 +116,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
- if (loanTransaction.isWaiver()) {
+ if (loanTransaction.isInterestWaiver()) {
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
} else {
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 fb282980e..3497a9d5d 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
@@ -27,7 +27,7 @@ import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.commands.LoanApplicationCommand;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.client.domain.Client;
@@ -282,7 +282,7 @@ public class Loan extends AbstractAuditableCustom {
}
public void modifyLoanApplication(final LoanApplicationCommand command, final Client client, final LoanProduct loanProduct,
- final Fund fund, final LoanTransactionProcessingStrategy strategy, final LoanScheduleNewData modifiedLoanSchedule, final Set charges,
+ final Fund fund, final LoanTransactionProcessingStrategy strategy, final LoanScheduleData modifiedLoanSchedule, final Set charges,
final Staff loanOfficer) {
if (command.isClientChanged()) {
@@ -361,7 +361,7 @@ public class Loan extends AbstractAuditableCustom {
}
// if the loan application/contract is modified when repayments are already against it - then need to re-process it
- final List repaymentsOrWaivers = retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement();
+ final List repaymentsOrWaivers = retreiveListOfTransactionsPostDisbursement();
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy);
loanRepaymentScheduleTransactionProcessor.handleTransaction(repaymentsOrWaivers, getCurrency(), this.repaymentScheduleInstallments);
@@ -617,7 +617,7 @@ public class Loan extends AbstractAuditableCustom {
final LoanLifecycleStateMachine loanLifecycleStateMachine,
final LoanTransaction adjustedTransaction) {
- LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, LoanStatus.fromInt(this.loanStatus));
+ final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue();
loanTransaction.updateLoan(this);
@@ -626,7 +626,7 @@ public class Loan extends AbstractAuditableCustom {
this.loanTransactions.add(loanTransaction);
- if (loanTransaction.isNotRepayment() && loanTransaction.isNotWaiver()) {
+ if (loanTransaction.isNotRepayment() && loanTransaction.isNotInterestWaiver()) {
final String errorMessage = "A transaction of type repayment or waiver was expected but not received.";
throw new InvalidLoanTransactionTypeException("transaction",
"is.not.a.repayment.or.waiver.transaction", errorMessage);
@@ -647,7 +647,7 @@ public class Loan extends AbstractAuditableCustom {
"cannot.be.a.future.date", errorMessage, loanTransactionDate);
}
- if (loanTransaction.isWaiver()) {
+ if (loanTransaction.isInterestWaiver()) {
Money totalInterestOutstandingOnLoan = getTotalInterestOutstandingOnLoan();
if (adjustedTransaction != null) {
totalInterestOutstandingOnLoan = totalInterestOutstandingOnLoan.plus(adjustedTransaction.getAmount());
@@ -663,14 +663,14 @@ public class Loan extends AbstractAuditableCustom {
if (isTransactionChronologicallyLatest && adjustedTransaction == null) {
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments);
} else {
- final List repaymentsOrWaivers = retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement();
+ final List repaymentsOrWaivers = retreiveListOfTransactionsPostDisbursement();
loanRepaymentScheduleTransactionProcessor.handleTransaction(repaymentsOrWaivers, getCurrency(), this.repaymentScheduleInstallments);
}
doPostLoanTransactionChecks(loanTransaction, loanLifecycleStateMachine);
}
- private List retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement() {
+ private List retreiveListOfTransactionsPostDisbursement() {
List repaymentsOrWaivers = new ArrayList();
for (LoanTransaction transaction : this.loanTransactions) {
if (!transaction.isDisbursement() && transaction.isNotContra()) {
@@ -738,6 +738,26 @@ public class Loan extends AbstractAuditableCustom {
return isChronologicallyLatestRepaymentOrWaiver;
}
+
+ private boolean isChronologicallyLatestTransaction(
+ final LoanTransaction loanTransaction,
+ final List loanTransactions) {
+
+ boolean isChronologicallyLatestRepaymentOrWaiver = true;
+
+ LocalDate currentTransactionDate = loanTransaction.getTransactionDate();
+ for (LoanTransaction previousTransaction : loanTransactions) {
+ if (previousTransaction.isNotContra()) {
+ if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate()) ||
+ currentTransactionDate.isEqual(previousTransaction.getTransactionDate())) {
+ isChronologicallyLatestRepaymentOrWaiver = false;
+ break;
+ }
+ }
+ }
+
+ return isChronologicallyLatestRepaymentOrWaiver;
+ }
public LocalDate possibleNextRepaymentDate() {
LocalDate earliestUnpaidInstallmentDate = new LocalDate();
@@ -770,7 +790,7 @@ public class Loan extends AbstractAuditableCustom {
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
if (installment.isNotFullyCompleted()) {
- possibleNextRepaymentAmount = installment.getTotalDue(currency);
+ possibleNextRepaymentAmount = installment.getTotalOutstanding(currency);
break;
}
}
@@ -805,14 +825,20 @@ public class Loan extends AbstractAuditableCustom {
final LoanTransaction newTransactionDetail,
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
- if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()) {
- final String errorMessage = "A transaction of type repayment or waiver was expected but not received.";
+ if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotInterestWaiver()) {
+ final String errorMessage = "Only transactions of type repayment or interest waiver can be adjusted.";
throw new InvalidLoanTransactionTypeException("transaction",
- "is.not.a.repayment.or.waiver.transaction", errorMessage);
+ "adjustment.is.only.allowed.to.repayment.or.waiver.transaction", errorMessage);
+ }
+
+ if (isClosed()) {
+ final String errorMessage = "Transactions of a closed loan cannot be adjusted.";
+ throw new InvalidLoanTransactionTypeException("transaction",
+ "adjustment.is.not.allowed.on.closed.loan", errorMessage);
}
transactionForAdjustment.contra();
- if (newTransactionDetail.isRepayment() || newTransactionDetail.isWaiver()) {
+ if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) {
handleRepaymentOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment);
}
}
@@ -867,32 +893,49 @@ public class Loan extends AbstractAuditableCustom {
return this.loanRepaymentScheduleDetail.getCurrency();
}
- public void writeOff(final DateTime writtenOffOn,
+ public LoanTransaction closeAsWrittenOff(
+ final LocalDate writtenOffOnLocalDate,
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
- LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WRITE_OFF, LoanStatus.fromInt(this.loanStatus));
+ final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue();
- this.closedOnDate = writtenOffOn.toDate();
- this.writtenOffOnDate = writtenOffOn.toDate();
+ this.closedOnDate = writtenOffOnLocalDate.toDate();
+ this.writtenOffOnDate = writtenOffOnLocalDate.toDate();
- LocalDate writtenOffOnLocalDate = new LocalDate(writtenOffOnDate);
if (writtenOffOnLocalDate.isBefore(this.getDisbursementDate())) {
- final String errorMessage = "The date on which a loan is withdrawn cannot be before the loan disbursement date: "
+ final String errorMessage = "The date on which a loan is written off cannot be before the loan disbursement date: "
+ getDisbursementDate().toString();
throw new InvalidLoanStateTransitionException("writeoff",
"cannot.be.before.submittal.date", errorMessage,
writtenOffOnLocalDate, getDisbursementDate());
}
+
if (writtenOffOnLocalDate.isAfter(new LocalDate())) {
final String errorMessage = "The date on which a loan is written off cannot be in the future.";
throw new InvalidLoanStateTransitionException("writeoff",
"cannot.be.a.future.date", errorMessage,
writtenOffOnLocalDate);
}
+
+ final LoanTransaction loanTransaction = LoanTransaction.writeoff(this, writtenOffOnLocalDate);
+ boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions);
+ if (!isLastTransaction) {
+ final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions.";
+ throw new InvalidLoanStateTransitionException("writeoff","must.occur.on.or.after.other.transaction.dates", errorMessage, writtenOffOnLocalDate);
+ }
+
+ this.loanTransactions.add(loanTransaction);
+
+ final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy);
+ loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, loanCurrency(), this.repaymentScheduleInstallments);
+
+ return loanTransaction;
}
- public void reschedule(final DateTime rescheduledOn,
+ // FIXME - KW - finish off reschedule/restructure capability for loans.
+ public void reschedule(
+ final DateTime rescheduledOn,
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RESCHEDULE, LoanStatus.fromInt(this.loanStatus));
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java
index 54a6c5ba2..588e4c7cb 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java
@@ -1,8 +1,21 @@
package org.mifosng.platform.loan.domain;
+/**
+ *
+ */
public enum LoanEvent {
- LOAN_CREATED, LOAN_REJECTED, LOAN_WITHDRAWN, LOAN_APPROVED, LOAN_APPROVAL_UNDO, LOAN_DISBURSED, LOAN_DISBURSAL_UNDO, LOAN_REPAYMENT_OR_WAIVER,
- //
- REPAID_IN_FULL, LOAN_WRITE_OFF, LOAN_RESCHEDULE, INTERST_REBATE_OWED, LOAN_OVERPAYMENT;
+ LOAN_CREATED, //
+ LOAN_REJECTED, //
+ LOAN_WITHDRAWN, //
+ LOAN_APPROVED, //
+ LOAN_APPROVAL_UNDO, //
+ LOAN_DISBURSED, //
+ LOAN_DISBURSAL_UNDO, //
+ LOAN_REPAYMENT_OR_WAIVER, //
+ REPAID_IN_FULL, //
+ WRITE_OFF_OUTSTANDING, //
+ LOAN_RESCHEDULE, //
+ INTERST_REBATE_OWED, //
+ LOAN_OVERPAYMENT;
}
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java
index 60ca9d134..c5f9b9d35 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java
@@ -19,7 +19,7 @@ import org.mifosng.platform.user.domain.AppUser;
@Entity
@Table(name = "m_loan_repayment_schedule")
-public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom {
+public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom {
@ManyToOne(optional = false)
@JoinColumn(name = "loan_id")
@@ -37,11 +37,17 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom repaymentsOrWaivers, MonetaryCurrency currency, List repaymentScheduleInstallments);
+ void handleWriteOff(LoanTransaction loanTransaction,
+ MonetaryCurrency loanCurrency,
+ List repaymentScheduleInstallments);
+
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanStatus.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanStatus.java
index dc18ea989..7540d526e 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanStatus.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanStatus.java
@@ -11,7 +11,9 @@ public enum LoanStatus {
ACTIVE(300, "loanStatusType.active"), //
WITHDRAWN_BY_CLIENT(400, "loanStatusType.withdrawn.by.client"), //
REJECTED(500, "loanStatusType.rejected"), //
- CLOSED(600, "loanStatusType.closed"), //
+ CLOSED_OBLIGATIONS_MET(600, "loanStatusType.closed.obligations.met"), //
+ CLOSED_WRITTEN_OFF(601, "loanStatusType.closed.written.off"), //
+ CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT(602, "loanStatusType.closed.reschedule.outstanding.amount"), //
OVERPAID(700, "loanStatusType.overpaid");
private final Integer value;
@@ -37,7 +39,13 @@ public enum LoanStatus {
enumeration = LoanStatus.REJECTED;
break;
case 600:
- enumeration = LoanStatus.CLOSED;
+ enumeration = LoanStatus.CLOSED_OBLIGATIONS_MET;
+ break;
+ case 601:
+ enumeration = LoanStatus.CLOSED_WRITTEN_OFF;
+ break;
+ case 602:
+ enumeration = LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT;
break;
case 700:
enumeration = LoanStatus.OVERPAID;
@@ -76,9 +84,21 @@ public enum LoanStatus {
}
public boolean isClosed() {
- return this.value.equals(LoanStatus.CLOSED.getValue());
+ return isClosedObligationsMet() || isClosedWrittenOff() || isClosedWithOutsandingAmountMarkedForReschedule();
+ }
+
+ public boolean isClosedObligationsMet() {
+ return this.value.equals(LoanStatus.CLOSED_OBLIGATIONS_MET.getValue());
+ }
+
+ public boolean isClosedWrittenOff() {
+ return this.value.equals(LoanStatus.CLOSED_WRITTEN_OFF.getValue());
}
+ public boolean isClosedWithOutsandingAmountMarkedForReschedule() {
+ return this.value.equals(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT.getValue());
+ }
+
public boolean isWithdrawnByClient() {
return this.value.equals(LoanStatus.WITHDRAWN_BY_CLIENT.getValue());
}
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java
index 87f1ea05c..a5548c35d 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java
@@ -35,7 +35,7 @@ public class LoanTransaction extends AbstractAuditableCustom {
private Loan loan;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
- private final BigDecimal amount;
+ private BigDecimal amount;
@Column(name = "principal_portion_derived", scale = 6, precision = 19, nullable = false)
private BigDecimal principalPortion = BigDecimal.ZERO;
@@ -63,7 +63,6 @@ public class LoanTransaction extends AbstractAuditableCustom {
protected LoanTransaction() {
this.loan = null;
- this.amount = null;
this.dateOf = null;
this.typeOf = null;
}
@@ -81,15 +80,19 @@ public class LoanTransaction extends AbstractAuditableCustom {
}
public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) {
- return new LoanTransaction(loan, LoanTransactionType.WAIVED, waived.getAmount(), waiveDate);
+ return new LoanTransaction(loan, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate);
}
private static LoanTransaction contra(final LoanTransaction originalTransaction) {
- LoanTransaction contra = new LoanTransaction(null, LoanTransactionType.REVERSAL, originalTransaction.getAmount().negate(), new LocalDate(originalTransaction.getDateOf()));
+ LoanTransaction contra = new LoanTransaction(null, LoanTransactionType.CONTRA, originalTransaction.getAmount().negate(), new LocalDate(originalTransaction.getDateOf()));
contra.updateContra(originalTransaction);
return contra;
}
+ public static LoanTransaction writeoff(final Loan loan, final LocalDate writeOffDate) {
+ return new LoanTransaction(loan, LoanTransactionType.WRITEOFF, null, writeOffDate);
+ }
+
public void updateContra(final LoanTransaction transaction) {
this.contra = transaction;
}
@@ -142,23 +145,27 @@ public class LoanTransaction extends AbstractAuditableCustom {
return LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.equals(getTypeOf());
}
- public boolean isWaiver() {
- return LoanTransactionType.WAIVED.equals(getTypeOf()) && isNotContra();
+ public boolean isInterestWaiver() {
+ return LoanTransactionType.WAIVE_INTEREST.equals(getTypeOf()) && isNotContra();
}
- public boolean isNotWaiver() {
- return !isWaiver();
+ public boolean isNotInterestWaiver() {
+ return !isInterestWaiver();
+ }
+
+ public boolean isWriteOff() {
+ return getTypeOf().isWriteOff() && isNotContra();
}
- public boolean isIdentifiedBy(Long identifier) {
+ public boolean isIdentifiedBy(final Long identifier) {
return this.getId().equals(identifier);
}
- public boolean isBelongingToLoanOf(Loan check) {
+ public boolean isBelongingToLoanOf(final Loan check) {
return this.loan.getId().equals(check.getId());
}
- public boolean isNotBelongingToLoanOf(Loan check) {
+ public boolean isNotBelongingToLoanOf(final Loan check) {
return !isBelongingToLoanOf(check);
}
@@ -187,6 +194,13 @@ public class LoanTransaction extends AbstractAuditableCustom {
this.interestWaivedPortion = getInterestWaivedPortion(currency).plus(interestWaivedPortion).getAmount();
this.chargesPortion = getChargesPortion(currency).plus(charges).getAmount();
}
+
+ public void updateComponentsAndTotal(final Money principal, final Money interest, final Money interestWaivedPortion, final Money charges) {
+ updateComponents(principal, interest, interestWaivedPortion, charges);
+
+ final MonetaryCurrency currency = principal.getCurrency();
+ this.amount = getPrincipalPortion(currency).plus(getInterestPortion(currency)).plus(this.chargesPortion).getAmount();
+ }
public Money getPrincipalPortion(final MonetaryCurrency currency) {
return Money.of(currency, principalPortion);
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java
index c90e157d2..68d1a58af 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java
@@ -13,9 +13,9 @@ public class LoanTransactionComparator implements Comparator {
final int comparsion = o1.getTransactionDate().compareTo(o2.getTransactionDate());
if (comparsion == 0) {
// equal transaction dates
- if (o1.isWaiver() && o2.isNotWaiver()) {
+ if (o1.isInterestWaiver() && o2.isNotInterestWaiver()) {
compareResult = -1;
- } else if (o1.isNotWaiver() && o2.isWaiver()) {
+ } else if (o1.isNotInterestWaiver() && o2.isInterestWaiver()) {
compareResult = 1;
} else {
compareResult = 0;
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionType.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionType.java
index a63137fa1..1e9e51876 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionType.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionType.java
@@ -5,9 +5,15 @@ public enum LoanTransactionType {
INVALID(0, "loanTransactionType.invalid"), //
DISBURSEMENT(1, "loanTransactionType.disbursement"), //
REPAYMENT(2, "loanTransactionType.repayment"), //
- REVERSAL(3, "loanTransactionType.reversal"), //
- WAIVED(4, "loanTransactionType.waiver"), //
- REPAYMENT_AT_DISBURSEMENT(5, "loanTransactionType.repaymentAtDisbursement");
+ CONTRA(3, "loanTransactionType.contra"), //
+ WAIVE_INTEREST(4, "loanTransactionType.waiver"), //
+ REPAYMENT_AT_DISBURSEMENT(5, "loanTransactionType.repaymentAtDisbursement"), //
+ WRITEOFF(6, "loanTransactionType.writeOff"),
+ MARKED_FOR_RESCHEDULING(7, "loanTransactionType.marked.for.rescheduling"), //
+ /**
+ * This type of transactions is allowed on written-off loans where mfi still attempts to recover payments from applicant after writing-off.
+ */
+ RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment");
private final Integer value;
private final String code;
@@ -40,18 +46,28 @@ public enum LoanTransactionType {
loanTransactionType = LoanTransactionType.REPAYMENT;
break;
case 3:
- loanTransactionType = LoanTransactionType.REVERSAL;
+ loanTransactionType = LoanTransactionType.CONTRA;
break;
case 4:
- loanTransactionType = LoanTransactionType.WAIVED;
+ loanTransactionType = LoanTransactionType.WAIVE_INTEREST;
break;
case 5:
loanTransactionType = LoanTransactionType.REPAYMENT_AT_DISBURSEMENT;
break;
+ case 6:
+ loanTransactionType = LoanTransactionType.WRITEOFF;
+ break;
+ case 7:
+ loanTransactionType = LoanTransactionType.MARKED_FOR_RESCHEDULING;
+ break;
default:
loanTransactionType = LoanTransactionType.INVALID;
break;
}
return loanTransactionType;
}
+
+ public boolean isWriteOff() {
+ return this.value.equals(LoanTransactionType.WRITEOFF.getValue());
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java
index b02ae62fa..c9105c07d 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java
@@ -75,7 +75,7 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
- if (loanTransaction.isWaiver()) {
+ if (loanTransaction.isInterestWaiver()) {
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
} else {
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformService.java
index ccf07bcca..f1e73c2dc 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/CalculationPlatformService.java
@@ -1,12 +1,12 @@
package org.mifosng.platform.loan.service;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
public interface CalculationPlatformService {
- LoanScheduleNewData calculateLoanScheduleNew(CalculateLoanScheduleCommand command);
+ LoanScheduleData calculateLoanScheduleNew(CalculateLoanScheduleCommand command);
/**
* Not used at present from application.
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 87b762fbd..d1cc16cde 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
@@ -3,7 +3,7 @@ package org.mifosng.platform.loan.service;
import java.util.Set;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.MoneyData;
@@ -51,7 +51,7 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic
}
@Override
- public LoanScheduleNewData calculateLoanScheduleNew(final CalculateLoanScheduleCommand command) {
+ public LoanScheduleData calculateLoanScheduleNew(final CalculateLoanScheduleCommand command) {
context.authenticatedUser();
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 cf2b2e227..9dadc5253 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
@@ -5,7 +5,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Set;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.commands.LoanApplicationCommand;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.client.domain.Client;
@@ -122,7 +122,7 @@ public class LoanAssembler {
loan.setExternalId(command.getExternalId());
- final LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
+ final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
for (LoanSchedulePeriodData scheduledLoanInstallment : loanSchedule.getPeriods()) {
if (scheduledLoanInstallment.isRepaymentPeriod()) {
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java
index 42a48c5fa..da9d622ec 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java
@@ -3,12 +3,11 @@ package org.mifosng.platform.loan.service;
import java.math.BigDecimal;
import java.util.Collection;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.DisbursementData;
import org.mifosng.platform.api.data.LoanBasicDetailsData;
import org.mifosng.platform.api.data.LoanPermissionData;
-import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.LoanTransactionNewData;
@@ -16,9 +15,9 @@ public interface LoanReadPlatformService {
LoanBasicDetailsData retrieveLoanAccountDetails(Long loanId);
- LoanScheduleNewData retrieveRepaymentSchedule(Long loanId, CurrencyData currency, DisbursementData disbursement, BigDecimal totalChargesAtDisbursement, BigDecimal inArrearsTolerance);
+ LoanScheduleData retrieveRepaymentSchedule(Long loanId, CurrencyData currency, DisbursementData disbursement, BigDecimal totalChargesAtDisbursement, BigDecimal inArrearsTolerance);
- Collection retrieveLoanPayments(Long loanId);
+ Collection retrieveLoanTransactions(Long loanId);
LoanPermissionData retrieveLoanPermissions(
LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed,
@@ -33,4 +32,6 @@ public interface LoanReadPlatformService {
LoanTransactionNewData retrieveNewLoanWaiveInterestDetails(Long loanId);
LoanTransactionData retrieveLoanTransactionDetails(Long loanId, Long transactionId);
+
+ LoanTransactionNewData retrieveNewClosureDetails();
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java
index c3381891a..f7f23b256 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java
@@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.Collection;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.data.ClientData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.DisbursementData;
@@ -17,7 +17,6 @@ import org.mifosng.platform.api.data.LoanBasicDetailsData;
import org.mifosng.platform.api.data.LoanChargeData;
import org.mifosng.platform.api.data.LoanPermissionData;
import org.mifosng.platform.api.data.LoanProductData;
-import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.LoanTransactionNewData;
@@ -97,7 +96,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
@Override
- public LoanScheduleNewData retrieveRepaymentSchedule(final Long loanId,
+ public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
final CurrencyData currency,
final DisbursementData disbursement,
final BigDecimal totalChargesAtDisbursement,
@@ -123,10 +122,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal cumulativePrincipalDisbursed = wrapper.deriveCumulativePrincipalDisbursed();
final BigDecimal cumulativePrincipalDue = wrapper.deriveCumulativePrincipalDue();
final BigDecimal cumulativePrincipalPaid = wrapper.deriveCumulativePrincipalPaid();
+ final BigDecimal cumulativePrincipalWrittenOff = wrapper.deriveCumulativePrincipalWrittenOff();
final BigDecimal cumulativePrincipalOutstanding = wrapper.deriveCumulativePrincipalOutstanding();
final BigDecimal cumulativeInterestExpected = wrapper.deriveCumulativeInterestExpected();
final BigDecimal cumulativeInterestPaid = wrapper.deriveCumulativeInterestPaid();
final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived();
+ final BigDecimal cumulativeInterestWrittenOff = wrapper.deriveCumulativeInterestWrittenOff();
final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding();
final BigDecimal cumulativeChargesToDate = wrapper.deriveCumulativeChargesToDate();
final BigDecimal cumulativeChargesPaid = wrapper.deriveCumulativeChargesPaid();
@@ -136,6 +137,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal totalExpectedRepayment = cumulativePrincipalDisbursed.add(totalCostOfLoan);
final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeChargesPaid);
final BigDecimal totalWaivedToDate = cumulativeInterestWaived;
+ final BigDecimal totalWrittenOffToDate = cumulativePrincipalWrittenOff.add(cumulativeInterestWrittenOff);
final BigDecimal totalOutstanding = cumulativePrincipalOutstanding.add(cumulativeInterestOutstanding).add(cumulativeChargesOutstanding);
final BigDecimal totalOverdue = wrapper.deriveCumulativeTotalOverdue();
@@ -149,31 +151,29 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
totalInArrears = totalOverdueMoney.getAmount();
}
- return new LoanScheduleNewData(currency, periods, loanTermInDays,
- cumulativePrincipalDisbursed, cumulativePrincipalDue, cumulativePrincipalPaid, cumulativePrincipalOutstanding,
- cumulativeInterestExpected, cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestOutstanding,
+ return new LoanScheduleData(currency, periods, loanTermInDays,
+ cumulativePrincipalDisbursed, cumulativePrincipalDue, cumulativePrincipalPaid, cumulativePrincipalWrittenOff, cumulativePrincipalOutstanding,
+ cumulativeInterestExpected, cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestWrittenOff, cumulativeInterestOutstanding,
cumulativeChargesToDate, cumulativeChargesPaid, cumulativeChargesOutstanding,
- totalCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalOutstanding, totalInArrears);
+ totalCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalWrittenOffToDate, totalOutstanding, totalInArrears);
} catch (EmptyResultDataAccessException e) {
throw new LoanNotFoundException(loanId);
}
}
@Override
- public Collection retrieveLoanPayments(Long loanId) {
+ public Collection retrieveLoanTransactions(final Long loanId) {
try {
context.authenticatedUser();
- LoanPaymentsMapper rm = new LoanPaymentsMapper();
+ LoanTransactionsMapper rm = new LoanTransactionsMapper();
- // retrieve all loan transactions that are not invalid (0) and not
- // disbursements (1) and have not been 'contra'ed by another transaction
+ // retrieve all loan transactions that are not invalid and have not been 'contra'ed by another transaction
// repayments at time of disbursement (e.g. charges)
String sql = "select "
+ rm.LoanPaymentsSchema()
- + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 1, 5) and tr.contra_id is null order by tr.transaction_date ASC";
+ + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and tr.contra_id is null order by tr.transaction_date ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
-
} catch (EmptyResultDataAccessException e) {
return null;
}
@@ -181,8 +181,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public LoanPermissionData retrieveLoanPermissions(
- LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed,
- int repaymentAndWaiveCount) {
+ final LoanBasicDetailsData loanBasicDetails,
+ final boolean isWaiverAllowed,
+ final int repaymentAndWaiveCount) {
boolean pendingApproval = (loanBasicDetails.getStatus().getId().equals(100L));
boolean waitingForDisbursal = (loanBasicDetails.getStatus().getId().equals(200L));
@@ -307,9 +308,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
LoanTransaction waiveOfInterest = loan.deriveDefaultInterestWaiverTransaction();
- EnumOptionData waiverEnum = LoanEnumerations.transactionType(LoanTransactionType.WAIVED);
+ EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WAIVE_INTEREST);
- return new LoanTransactionNewData(waiverEnum, currencyData, waiveOfInterest.getTransactionDate(), waiveOfInterest.getAmount());
+ return new LoanTransactionNewData(null, transactionType, currencyData, waiveOfInterest.getTransactionDate(), waiveOfInterest.getAmount());
+ }
+
+ @Override
+ public LoanTransactionNewData retrieveNewClosureDetails() {
+
+ context.authenticatedUser();
+
+ EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF);
+
+ return new LoanTransactionNewData(null, transactionType, null, new LocalDate(), null);
}
@Override
@@ -512,8 +523,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public String loanScheduleSchema() {
return " ls.loan_id as loanId, ls.installment as period, ls.duedate as dueDate, "
- + " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, "
- + " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived "
+ + " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, "
+ + " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff "
+ " from m_loan l "
+ " join m_loan_repayment_schedule ls on ls.loan_id = l.id ";
}
@@ -526,18 +537,32 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal principalDue = rs.getBigDecimal("principalDue");
final BigDecimal principalPaid = rs.getBigDecimal("principalPaid");
- final BigDecimal principalOutstanding = principalDue.subtract(principalPaid);
+ BigDecimal principalWrittenOff = rs.getBigDecimal("principalWrittenOff");
+ if (principalWrittenOff == null) {
+ principalWrittenOff = BigDecimal.ZERO;
+ }
+
+ // TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns?
+ final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff);
+
final BigDecimal interestDue = rs.getBigDecimal("interestDue");
final BigDecimal interestPaid = rs.getBigDecimal("interestPaid");
BigDecimal interestWaived = rs.getBigDecimal("interestWaived");
if (interestWaived == null) {
interestWaived = BigDecimal.ZERO;
}
- final BigDecimal interestOutstanding = interestDue.subtract(interestPaid).subtract(interestWaived);
+ BigDecimal interestWrittenOff = rs.getBigDecimal("interestWrittenOff");
+ if (interestWrittenOff == null) {
+ interestWrittenOff = BigDecimal.ZERO;
+ }
+
+ // TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns?
+ final BigDecimal interestOutstanding = interestDue.subtract(interestPaid).subtract(interestWaived).subtract(interestWrittenOff);
final BigDecimal totalDueForPeriod = principalDue.add(interestDue);
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid);
final BigDecimal totalWaivedForPeriod = interestWaived;
+ final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff);
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding);
final LocalDate fromDate = this.lastDueDate;
@@ -548,50 +573,44 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
outstandingLoanBalance = outstandingLoanBalance.subtract(principalDue);
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate,
- principalDue, principalPaid, principalOutstanding, outstandingPrincipleBalanceOfLoan, interestDue,
- interestPaid, interestWaived, interestOutstanding, totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod,
+ principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan, interestDue,
+ interestPaid, interestWaived, interestWrittenOff, interestOutstanding, totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod,
totalOutstandingForPeriod);
}
}
- private static final class LoanPaymentsMapper implements
- RowMapper {
+ private static final class LoanTransactionsMapper implements RowMapper {
public String LoanPaymentsSchema() {
return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, "
- + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode "
+ + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, "
+ + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode "
+ " from m_loan l "
+ " join m_loan_transaction tr on tr.loan_id = l.id"
+ " join m_currency rc on rc.`code` = l.currency_code ";
}
@Override
- public LoanRepaymentTransactionData mapRow(final ResultSet rs,
+ public LoanTransactionNewData mapRow(final ResultSet rs,
@SuppressWarnings("unused") final int rowNum) throws SQLException {
String currencyCode = rs.getString("currencyCode");
String currencyName = rs.getString("currencyName");
String currencyNameCode = rs.getString("currencyNameCode");
- String currencyDisplaySymbol = rs
- .getString("currencyDisplaySymbol");
- Integer currencyDigits = JdbcSupport.getInteger(rs,
- "currencyDigits");
+ String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
+ Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
CurrencyData currencyData = new CurrencyData(currencyCode,
currencyName, currencyDigits, currencyDisplaySymbol,
currencyNameCode);
Long id = rs.getLong("id");
- int transactionTypeInt = JdbcSupport.getInteger(rs,
- "transactionType");
- EnumOptionData transactionType = LoanEnumerations
- .transactionType(transactionTypeInt);
+ int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType");
+ EnumOptionData transactionType = LoanEnumerations.transactionType(transactionTypeInt);
LocalDate date = JdbcSupport.getLocalDate(rs, "date");
- BigDecimal totalBD = rs.getBigDecimal("total");
- MoneyData total = MoneyData.of(currencyData, totalBD);
+ BigDecimal totalAmount = rs.getBigDecimal("total");
- return new LoanRepaymentTransactionData(id, transactionType, date, total);
+ return new LoanTransactionNewData(id, transactionType, currencyData, date, totalAmount);
}
}
-
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java
index 113812148..5bd501c1f 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java
@@ -36,6 +36,14 @@ public class LoanSchedulePeriodDataWrapper {
}
return cumulativeValue;
}
+
+ public BigDecimal deriveCumulativePrincipalWrittenOff() {
+ BigDecimal cumulativeValue = BigDecimal.ZERO;
+ for (LoanSchedulePeriodData period : this.periods) {
+ cumulativeValue = cumulativeValue.add(period.principalWrittenOff());
+ }
+ return cumulativeValue;
+ }
public BigDecimal deriveCumulativePrincipalOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
@@ -68,6 +76,14 @@ public class LoanSchedulePeriodDataWrapper {
}
return cumulativeValue;
}
+
+ public BigDecimal deriveCumulativeInterestWrittenOff() {
+ BigDecimal cumulativeValue = BigDecimal.ZERO;
+ for (LoanSchedulePeriodData period : this.periods) {
+ cumulativeValue = cumulativeValue.add(period.interestWrittenOff());
+ }
+ return cumulativeValue;
+ }
public BigDecimal deriveCumulativeInterestOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java
index df76781d0..4b26988f1 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java
@@ -12,7 +12,7 @@ public class LoanTransactionCommandValidator {
private final LoanTransactionCommand command;
- public LoanTransactionCommandValidator(LoanTransactionCommand command) {
+ public LoanTransactionCommandValidator(final LoanTransactionCommand command) {
this.command = command;
}
@@ -31,4 +31,18 @@ public class LoanTransactionCommandValidator {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
+
+ public void validateNonMonetaryTransaction() {
+ List dataValidationErrors = new ArrayList();
+
+ DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
+
+ baseDataValidator.reset().parameter("loanId").value(command.getLoanId()).notNull().integerGreaterThanZero();
+ baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionDate()).notNull();
+ baseDataValidator.reset().parameter("note").value(command.getNote()).notExceedingLengthOf(1000);
+
+ if (!dataValidationErrors.isEmpty()) {
+ throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
+ }
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java
index dac17f5c5..5e861f10f 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java
@@ -46,7 +46,13 @@ public interface LoanWritePlatformService {
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
EntityIdentifier waiveInterestOnLoan(LoanTransactionCommand command);
+
+ @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
+ EntityIdentifier writeOff(LoanTransactionCommand command);
+ @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
+ EntityIdentifier closeAsRescheduled(LoanTransactionCommand command);
+
@PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')")
EntityIdentifier addLoanCharge(LoanChargeCommand command);
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 67fd1e55a..0eea6663d 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
@@ -8,7 +8,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand;
import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
import org.mifosng.platform.api.commands.LoanApplicationCommand;
@@ -161,7 +161,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
final Set charges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanProduct.getCharges(), loan.getPrincpal().getAmount());
- final LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
+ final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer);
this.loanRepository.save(loan);
@@ -373,7 +373,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanTermFrequency, loanTermFrequencyType,
actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate, loanChargeCommands);
- LoanScheduleNewData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(calculateCommand);
+ LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(calculateCommand);
List modifiedLoanRepaymentSchedule = new ArrayList();
@@ -494,7 +494,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// adjustment is only supported for repayments and waivers at present
LocalDate transactionDate = command.getTransactionDate();
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate);
- if (transactionToAdjust.isWaiver()) {
+ if (transactionToAdjust.isInterestWaiver()) {
newTransactionDetail = LoanTransaction.waiver(loan, transactionAmount, transactionDate);
}
@@ -540,7 +540,63 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return new EntityIdentifier(loan.getId());
}
+
+ @Transactional
+ @Override
+ public EntityIdentifier writeOff(final LoanTransactionCommand command) {
+ context.authenticatedUser();
+ final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
+ validator.validateNonMonetaryTransaction();
+
+ final Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) {
+ throw new LoanNotFoundException(command.getLoanId());
+ }
+
+ final LoanTransaction writeoff = loan.closeAsWrittenOff(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
+
+ this.loanTransactionRepository.save(writeoff);
+ this.loanRepository.save(loan);
+
+ final String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ final Note note = Note.loanTransactionNote(loan, writeoff, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
+ @Override
+ public EntityIdentifier closeAsRescheduled(final LoanTransactionCommand command) {
+ context.authenticatedUser();
+
+ final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
+ validator.validateNonMonetaryTransaction();
+
+ final Loan loan = this.loanRepository.findOne(command.getLoanId());
+ if (loan == null) {
+ throw new LoanNotFoundException(command.getLoanId());
+ }
+
+ // FIXME - kw - HANDLE closed and rescheduled case.
+ final LoanTransaction writeoff = loan.closeAsWrittenOff(command.getTransactionDate(), defaultLoanLifecycleStateMachine());
+
+ this.loanTransactionRepository.save(writeoff);
+ this.loanRepository.save(loan);
+
+ final String noteText = command.getNote();
+ if (StringUtils.isNotBlank(noteText)) {
+ final Note note = Note.loanTransactionNote(loan, writeoff, noteText);
+ this.noteRepository.save(note);
+ }
+
+ return new EntityIdentifier(loan.getId());
+ }
+
+ @Transactional
@Override
public EntityIdentifier addLoanCharge(LoanChargeCommand command) {
this.context.authenticatedUser();
@@ -579,6 +635,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return new EntityIdentifier(loanCharge.getId());
}
+ @Transactional
@Override
public EntityIdentifier updateLoanCharge(final LoanChargeCommand command) {
@@ -604,6 +661,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
throw new ChargeNotFoundException(command.getId());
}
+ @Transactional
@Override
public EntityIdentifier deleteLoanCharge(Long loanId, Long loanChargeId) {
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanEnumerations.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanEnumerations.java
index 1a7ca6f78..c561142b6 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanEnumerations.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanEnumerations.java
@@ -173,20 +173,32 @@ public class LoanEnumerations {
public static EnumOptionData transactionType(final LoanTransactionType type) {
EnumOptionData optionData = null;
switch (type) {
+ case INVALID:
+ optionData = new EnumOptionData(LoanTransactionType.INVALID.getValue().longValue(), LoanTransactionType.INVALID.getCode(), "Invalid");
+ break;
case DISBURSEMENT:
optionData= new EnumOptionData(LoanTransactionType.DISBURSEMENT.getValue().longValue(), LoanTransactionType.DISBURSEMENT.getCode(), "Dibursement");
break;
case REPAYMENT:
optionData = new EnumOptionData(LoanTransactionType.REPAYMENT.getValue().longValue(), LoanTransactionType.REPAYMENT.getCode(), "Repayment");
break;
- case REVERSAL:
- optionData = new EnumOptionData(LoanTransactionType.REVERSAL.getValue().longValue(), LoanTransactionType.REVERSAL.getCode(), "Reversal");
+ case REPAYMENT_AT_DISBURSEMENT:
+ optionData = new EnumOptionData(LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue().longValue(), LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getCode(), "Repayment (at time of disbursement)");
break;
- case WAIVED:
- optionData = new EnumOptionData(LoanTransactionType.WAIVED.getValue().longValue(), LoanTransactionType.WAIVED.getCode(), "Waiver");
+ case CONTRA:
+ optionData = new EnumOptionData(LoanTransactionType.CONTRA.getValue().longValue(), LoanTransactionType.CONTRA.getCode(), "Reversal");
break;
- default:
- optionData = new EnumOptionData(LoanTransactionType.INVALID.getValue().longValue(), LoanTransactionType.INVALID.getCode(), "Invalid");
+ case WAIVE_INTEREST:
+ optionData = new EnumOptionData(LoanTransactionType.WAIVE_INTEREST.getValue().longValue(), LoanTransactionType.WAIVE_INTEREST.getCode(), "Waive interest");
+ break;
+ case MARKED_FOR_RESCHEDULING:
+ optionData = new EnumOptionData(LoanTransactionType.MARKED_FOR_RESCHEDULING.getValue().longValue(), LoanTransactionType.MARKED_FOR_RESCHEDULING.getCode(), "Close (as rescheduled)");
+ break;
+ case WRITEOFF:
+ optionData = new EnumOptionData(LoanTransactionType.WRITEOFF.getValue().longValue(), LoanTransactionType.WRITEOFF.getCode(), "Close (as written-off)");
+ break;
+ case RECOVERY_REPAYMENT:
+ optionData = new EnumOptionData(LoanTransactionType.RECOVERY_REPAYMENT.getValue().longValue(), LoanTransactionType.RECOVERY_REPAYMENT.getCode(), "Repayment (after write-off)");
break;
}
return optionData;
@@ -197,8 +209,11 @@ public class LoanEnumerations {
}
public static EnumOptionData status(final LoanStatus status) {
- EnumOptionData optionData = null;
+ EnumOptionData optionData = new EnumOptionData(LoanStatus.INVALID.getValue().longValue(), LoanStatus.INVALID.getCode(), "Invalid");
switch (status) {
+ case INVALID:
+ optionData = new EnumOptionData(LoanStatus.INVALID.getValue().longValue(), LoanStatus.INVALID.getCode(), "Invalid");
+ break;
case SUBMITED_AND_PENDING_APPROVAL:
optionData = new EnumOptionData(LoanStatus.SUBMITED_AND_PENDING_APPROVAL.getValue().longValue(), LoanStatus.SUBMITED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval");
break;
@@ -214,16 +229,20 @@ public class LoanEnumerations {
case WITHDRAWN_BY_CLIENT:
optionData = new EnumOptionData(LoanStatus.WITHDRAWN_BY_CLIENT.getValue().longValue(), LoanStatus.WITHDRAWN_BY_CLIENT.getCode(), "Withdrawn by applicant");
break;
- case CLOSED:
- optionData = new EnumOptionData(LoanStatus.CLOSED.getValue().longValue(), LoanStatus.CLOSED.getCode(), "Closed");
+ case CLOSED_OBLIGATIONS_MET:
+ optionData = new EnumOptionData(LoanStatus.CLOSED_OBLIGATIONS_MET.getValue().longValue(), LoanStatus.CLOSED_OBLIGATIONS_MET.getCode(), "Closed (obligations met)");
+ break;
+ case CLOSED_WRITTEN_OFF:
+ optionData = new EnumOptionData(LoanStatus.CLOSED_WRITTEN_OFF.getValue().longValue(), LoanStatus.CLOSED_WRITTEN_OFF.getCode(), "Closed (written off)");
+ break;
+ case CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT:
+ optionData = new EnumOptionData(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT.getValue().longValue(), LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT.getCode(), "Closed (rescheduled)");
break;
case OVERPAID:
optionData = new EnumOptionData(LoanStatus.OVERPAID.getValue().longValue(), LoanStatus.OVERPAID.getCode(), "Overpaid");
break;
- default:
- optionData = new EnumOptionData(LoanStatus.INVALID.getValue().longValue(), LoanStatus.INVALID.getCode(), "Invalid");
- break;
}
+
return optionData;
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/AmortizationLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/AmortizationLoanScheduleGenerator.java
index e64ff0354..0db242566 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/AmortizationLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/AmortizationLoanScheduleGenerator.java
@@ -5,14 +5,14 @@ import java.util.List;
import java.util.Set;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
import org.mifosng.platform.loan.domain.LoanCharge;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
public interface AmortizationLoanScheduleGenerator {
- LoanScheduleNewData generate(
+ LoanScheduleData generate(
ApplicationCurrency applicationCurrency,
LoanProductRelatedDetail loanScheduleInfo,
LocalDate disbursementDate,
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/DecliningBalanceMethodLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/DecliningBalanceMethodLoanScheduleGenerator.java
index c8929c1b2..5d0556c58 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/DecliningBalanceMethodLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/DecliningBalanceMethodLoanScheduleGenerator.java
@@ -5,7 +5,7 @@ import java.util.List;
import java.util.Set;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.LoanCharge;
@@ -36,7 +36,7 @@ public class DecliningBalanceMethodLoanScheduleGenerator implements LoanSchedule
private final AmortizationLoanScheduleGeneratorFactory amortizationLoanScheduleGeneratorFactory = new AmortizationLoanScheduleGeneratorFactory();
@Override
- public LoanScheduleNewData generate(
+ public LoanScheduleData generate(
final ApplicationCurrency applicationCurrency,
final LoanProductRelatedDetail loanScheduleInfo,
final Integer loanTermFrequency,
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualInstallmentsAmortizationLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualInstallmentsAmortizationLoanScheduleGenerator.java
index a875890f6..edf05bd40 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualInstallmentsAmortizationLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualInstallmentsAmortizationLoanScheduleGenerator.java
@@ -8,7 +8,7 @@ import java.util.Set;
import org.joda.time.Days;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
@@ -27,7 +27,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
private final PmtCalculator pmtCalculator = new PmtCalculator();
@Override
- public LoanScheduleNewData generate(
+ public LoanScheduleData generate(
final ApplicationCurrency currency,
final LoanProductRelatedDetail loanScheduleInfo,
final LocalDate disbursementDate,
@@ -150,7 +150,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
currency.getDisplaySymbol(),
currency.getNameCode());
- return new LoanScheduleNewData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
+ return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualPrincipalAmortizationLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualPrincipalAmortizationLoanScheduleGenerator.java
index 06d2a3f1b..37f9b75df 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualPrincipalAmortizationLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/EqualPrincipalAmortizationLoanScheduleGenerator.java
@@ -8,7 +8,7 @@ import java.util.Set;
import org.joda.time.Days;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
@@ -23,7 +23,7 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
@Override
- public LoanScheduleNewData generate(
+ public LoanScheduleData generate(
final ApplicationCurrency currency,
final LoanProductRelatedDetail loanScheduleInfo,
final LocalDate disbursementDate,
@@ -131,7 +131,7 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
currency.getDisplaySymbol(),
currency.getNameCode());
- return new LoanScheduleNewData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
+ return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/FlatMethodLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
index b1bf2f0b5..4178d1549 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
@@ -10,7 +10,7 @@ import java.util.Set;
import org.joda.time.Days;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
@@ -26,7 +26,7 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
@Override
- public LoanScheduleNewData generate(
+ public LoanScheduleData generate(
final ApplicationCurrency currency,
final LoanProductRelatedDetail loanScheduleInfo,
final Integer loanTermFrequency,
@@ -138,7 +138,7 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
currency.getDisplaySymbol(),
currency.getNameCode());
- return new LoanScheduleNewData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
+ return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/LoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/LoanScheduleGenerator.java
index abb97800d..12b65e6e9 100644
--- a/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/LoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanschedule/domain/LoanScheduleGenerator.java
@@ -3,7 +3,7 @@ package org.mifosng.platform.loanschedule.domain;
import java.util.Set;
import org.joda.time.LocalDate;
-import org.mifosng.platform.api.LoanScheduleNewData;
+import org.mifosng.platform.api.LoanScheduleData;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
import org.mifosng.platform.loan.domain.LoanCharge;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
@@ -11,7 +11,7 @@ import org.mifosng.platform.loan.domain.PeriodFrequencyType;
public interface LoanScheduleGenerator {
- LoanScheduleNewData generate(ApplicationCurrency applicationCurrency,
+ LoanScheduleData generate(ApplicationCurrency applicationCurrency,
LoanProductRelatedDetail loanScheduleInfo,
Integer loanTermFrequency,
PeriodFrequencyType loanTermFrequencyType,