tidy up state transitions and transactions support through api.

This commit is contained in:
Keith Woodlock
2012-06-06 17:09:08 +01:00
parent 22d20a2273
commit 28b26d2463
19 changed files with 169 additions and 81 deletions
@@ -26,7 +26,7 @@ import org.mifosng.platform.api.commands.SubmitLoanApplicationCommand;
import org.mifosng.platform.api.commands.UndoStateTransitionCommand; import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanAccountData;
import org.mifosng.platform.api.data.LoanRepaymentData; import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.LoanSchedule; import org.mifosng.platform.api.data.LoanSchedule;
import org.mifosng.platform.api.data.NewLoanData; import org.mifosng.platform.api.data.NewLoanData;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService; import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
@@ -49,7 +49,7 @@ public class LoansApiResource {
private String loanFilterName = "loanFilter"; private String loanFilterName = "loanFilter";
private String loanAllowedFieldList = ""; private String loanAllowedFieldList = "";
private String loanRepaymentFilterName = "loanRepaymentFilter"; private String loanRepaymentFilterName = "loanRepaymentFilter";
private String loanRepaymentDefaultFieldList = "date,total"; private String loanRepaymentDefaultFieldList = "transactionType,date,total";
private String loanRepaymentAllowedFieldList = ""; private String loanRepaymentAllowedFieldList = "";
@Autowired @Autowired
@@ -280,13 +280,13 @@ public class LoansApiResource {
String json = ""; String json = "";
String selectedFields = loanRepaymentDefaultFieldList; String selectedFields = loanRepaymentDefaultFieldList;
if (is(commandParam, "repayment")) { if (is(commandParam, "repayment")) {
LoanRepaymentData loanRepaymentData = this.loanReadPlatformService LoanTransactionData loanRepaymentData = this.loanReadPlatformService
.retrieveNewLoanRepaymentDetails(loanId); .retrieveNewLoanRepaymentDetails(loanId);
json = this.jsonFormattingService.convertRequest(loanRepaymentData, json = this.jsonFormattingService.convertRequest(loanRepaymentData,
loanRepaymentFilterName, loanRepaymentAllowedFieldList, selectedFields, loanRepaymentFilterName, loanRepaymentAllowedFieldList, selectedFields,
uriInfo.getQueryParameters()); uriInfo.getQueryParameters());
} else if (is(commandParam, "waiver")) { } else if (is(commandParam, "waiver")) {
LoanRepaymentData loanWaiverData = this.loanReadPlatformService LoanTransactionData loanWaiverData = this.loanReadPlatformService
.retrieveNewLoanWaiverDetails(loanId); .retrieveNewLoanWaiverDetails(loanId);
json = this.jsonFormattingService.convertRequest(loanWaiverData, json = this.jsonFormattingService.convertRequest(loanWaiverData,
loanRepaymentFilterName, loanRepaymentAllowedFieldList, selectedFields, loanRepaymentFilterName, loanRepaymentAllowedFieldList, selectedFields,
@@ -304,15 +304,15 @@ public class LoansApiResource {
@Path("{loanId}/transactions/{transactionId}") @Path("{loanId}/transactions/{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({ MediaType.APPLICATION_JSON}) @Produces({ MediaType.APPLICATION_JSON})
public String retrieveRepaymentDetails( public String retrieveTransaction(
@PathParam("loanId") final Long loanId, @PathParam("loanId") final Long loanId,
@PathParam("transactionId") final Long transactionId, @PathParam("transactionId") final Long transactionId,
@Context UriInfo uriInfo) { @Context UriInfo uriInfo) {
LoanRepaymentData loanRepaymentData = this.loanReadPlatformService LoanTransactionData loanRepaymentData = this.loanReadPlatformService
.retrieveLoanRepaymentDetails(loanId, transactionId); .retrieveLoanTransactionDetails(loanId, transactionId);
String selectedFields = ""; String selectedFields = loanRepaymentDefaultFieldList;
return this.jsonFormattingService.convertRequest(loanRepaymentData, return this.jsonFormattingService.convertRequest(loanRepaymentData,
loanRepaymentFilterName, loanRepaymentAllowedFieldList, selectedFields, loanRepaymentFilterName, loanRepaymentAllowedFieldList, selectedFields,
uriInfo.getQueryParameters()); uriInfo.getQueryParameters());
@@ -99,7 +99,7 @@ public class ApiGlobalErrorResponse {
public static ApiGlobalErrorResponse dataIntegrityError(final String globalisationMessageCode, final String defaultUserMessage, final String parameterName, final Object... defaultUserMessageArgs) { public static ApiGlobalErrorResponse dataIntegrityError(final String globalisationMessageCode, final String defaultUserMessage, final String parameterName, final Object... defaultUserMessageArgs) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("400"); globalErrorResponse.setHttpStatusCode("403");
globalErrorResponse.setDeveloperMessage("The request caused a data integrity issue to be fired by the database."); globalErrorResponse.setDeveloperMessage("The request caused a data integrity issue to be fired by the database.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode); globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode);
@@ -6,13 +6,13 @@ public class DerivedLoanData {
private LoanRepaymentScheduleData repaymentSchedule; private LoanRepaymentScheduleData repaymentSchedule;
private LoanAccountSummaryData summary; private LoanAccountSummaryData summary;
private List<LoanRepaymentData> loanRepayments; private List<LoanTransactionData> loanRepayments;
public DerivedLoanData() { public DerivedLoanData() {
// //
} }
public DerivedLoanData(LoanRepaymentScheduleData repaymentScheduleData, LoanAccountSummaryData summaryData, List<LoanRepaymentData> loanRepayments) { public DerivedLoanData(LoanRepaymentScheduleData repaymentScheduleData, LoanAccountSummaryData summaryData, List<LoanTransactionData> loanRepayments) {
this.repaymentSchedule = repaymentScheduleData; this.repaymentSchedule = repaymentScheduleData;
this.summary = summaryData; this.summary = summaryData;
this.loanRepayments = loanRepayments; this.loanRepayments = loanRepayments;
@@ -34,11 +34,11 @@ public class DerivedLoanData {
this.summary = summary; this.summary = summary;
} }
public List<LoanRepaymentData> getLoanRepayments() { public List<LoanTransactionData> getLoanRepayments() {
return loanRepayments; return loanRepayments;
} }
public void setLoanRepayments(List<LoanRepaymentData> loanRepayments) { public void setLoanRepayments(List<LoanTransactionData> loanRepayments) {
this.loanRepayments = loanRepayments; this.loanRepayments = loanRepayments;
} }
} }
@@ -41,7 +41,7 @@ public class LoanAccountData {
private LoanAccountSummaryData summary; private LoanAccountSummaryData summary;
private LoanRepaymentScheduleData repaymentSchedule; private LoanRepaymentScheduleData repaymentSchedule;
private List<LoanRepaymentData> loanRepayments; private List<LoanTransactionData> loanRepayments;
private LoanPermissionData permissions; private LoanPermissionData permissions;
@@ -55,7 +55,7 @@ public class LoanAccountData {
public LoanAccountData(LoanBasicDetailsData basicDetails, public LoanAccountData(LoanBasicDetailsData basicDetails,
LoanAccountSummaryData summary, LoanAccountSummaryData summary,
LoanRepaymentScheduleData repaymentSchedule, LoanRepaymentScheduleData repaymentSchedule,
List<LoanRepaymentData> loanRepayments, LoanPermissionData permissions) { List<LoanTransactionData> loanRepayments, LoanPermissionData permissions) {
this.summary = summary; this.summary = summary;
this.repaymentSchedule = repaymentSchedule; this.repaymentSchedule = repaymentSchedule;
this.loanRepayments = loanRepayments; this.loanRepayments = loanRepayments;
@@ -130,11 +130,11 @@ public class LoanAccountData {
this.repaymentSchedule = repaymentSchedule; this.repaymentSchedule = repaymentSchedule;
} }
public List<LoanRepaymentData> getLoanRepayments() { public List<LoanTransactionData> getLoanRepayments() {
return loanRepayments; return loanRepayments;
} }
public void setLoanRepayments(List<LoanRepaymentData> loanRepayments) { public void setLoanRepayments(List<LoanTransactionData> loanRepayments) {
this.loanRepayments = loanRepayments; this.loanRepayments = loanRepayments;
} }
@@ -4,9 +4,10 @@ import org.codehaus.jackson.map.annotate.JsonFilter;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
@JsonFilter("loanRepaymentFilter") @JsonFilter("loanRepaymentFilter")
public class LoanRepaymentData { public class LoanTransactionData {
private Long id; private Long id;
private EnumOptionData transactionType;
private LocalDate date; private LocalDate date;
private MoneyData principal; private MoneyData principal;
private MoneyData interest; private MoneyData interest;
@@ -14,16 +15,17 @@ public class LoanRepaymentData {
private MoneyData totalWaived; private MoneyData totalWaived;
private MoneyData overpaid; private MoneyData overpaid;
public LoanRepaymentData() { public LoanTransactionData() {
// //
} }
public LoanRepaymentData( public LoanTransactionData(
Long id, final LocalDate date, Long id, EnumOptionData transactionType, final LocalDate date,
final MoneyData principal, final MoneyData principal,
final MoneyData interest, final MoneyData interest,
final MoneyData total, final MoneyData overpaid) { final MoneyData total, final MoneyData overpaid) {
this.id = id; this.id = id;
this.transactionType = transactionType;
this.date = date; this.date = date;
this.principal = principal; this.principal = principal;
this.interest = interest; this.interest = interest;
@@ -38,15 +40,19 @@ public class LoanRepaymentData {
public void setId(Long id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public EnumOptionData getTransactionType() {
return transactionType;
}
public void setTransactionType(EnumOptionData transactionType) {
this.transactionType = transactionType;
}
public LocalDate getDate() { public LocalDate getDate() {
return date; return date;
} }
// public int getTransactionOffsetFromToday() {
// return Days.daysBetween(new DateMidnight().toDateTime(), this.date.toDateMidnight().toDateTime()).getDays();
// }
public void setDate(LocalDate date) { public void setDate(LocalDate date) {
this.date = date; this.date = date;
} }
@@ -26,6 +26,6 @@ public class PlatformDataIntegrityExceptionMapper implements ExceptionMapper<Pla
ApiGlobalErrorResponse dataIntegrityError = ApiGlobalErrorResponse.dataIntegrityError(exception.getGlobalisationMessageCode(), exception.getDefaultUserMessage(), ApiGlobalErrorResponse dataIntegrityError = ApiGlobalErrorResponse.dataIntegrityError(exception.getGlobalisationMessageCode(), exception.getDefaultUserMessage(),
exception.getParameterName(), exception.getDefaultUserMessageArgs()); exception.getParameterName(), exception.getDefaultUserMessageArgs());
return Response.status(Status.BAD_REQUEST).entity(dataIntegrityError).build(); return Response.status(Status.FORBIDDEN).entity(dataIntegrityError).build();
} }
} }
@@ -6,6 +6,6 @@ package org.mifosng.platform.exceptions;
public class InvalidLoanStateTransitionException extends AbstractPlatformDomainRuleException { public class InvalidLoanStateTransitionException extends AbstractPlatformDomainRuleException {
public InvalidLoanStateTransitionException(final String action, String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) { public InvalidLoanStateTransitionException(final String action, String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
super("error.msg.loan." + action + postFix, defaultUserMessage, defaultUserMessageArgs); super("error.msg.loan." + action + "." + postFix, defaultUserMessage, defaultUserMessageArgs);
} }
} }
@@ -0,0 +1,11 @@
package org.mifosng.platform.exceptions;
/**
* {@link AbstractPlatformDomainRuleException} thrown an action to transition a loan from one state to another violates a domain rule.
*/
public class InvalidLoanTransactionTypeException extends AbstractPlatformDomainRuleException {
public InvalidLoanTransactionTypeException(final String action, String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
super("error.msg.loan." + action + "." + postFix, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -6,6 +6,10 @@ package org.mifosng.platform.exceptions;
public class LoanTransactionNotFoundException extends AbstractPlatformResourceNotFoundException { public class LoanTransactionNotFoundException extends AbstractPlatformResourceNotFoundException {
public LoanTransactionNotFoundException(Long id) { public LoanTransactionNotFoundException(Long id) {
super("error.msg.loan.id.invalid", "Loan with identifier " + id + " does not exist", id); super("error.msg.loan.id.invalid", "Transaction with identifier " + id + " does not exist", id);
}
public LoanTransactionNotFoundException(Long id, Long loanId) {
super("error.msg.loan.id.invalid", "Transaction with identifier " + id + " does not exist for loan with identifier " + loanId + "." , id, loanId);
} }
} }
@@ -7,13 +7,15 @@ import java.util.List;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.mifosng.platform.api.data.CurrencyData; import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.DerivedLoanData; import org.mifosng.platform.api.data.DerivedLoanData;
import org.mifosng.platform.api.data.EnumOptionData;
import org.mifosng.platform.api.data.LoanAccountSummaryData; import org.mifosng.platform.api.data.LoanAccountSummaryData;
import org.mifosng.platform.api.data.LoanRepaymentData; import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.LoanRepaymentPeriodData; import org.mifosng.platform.api.data.LoanRepaymentPeriodData;
import org.mifosng.platform.api.data.LoanRepaymentScheduleData; import org.mifosng.platform.api.data.LoanRepaymentScheduleData;
import org.mifosng.platform.api.data.MoneyData; import org.mifosng.platform.api.data.MoneyData;
import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.loanproduct.service.LoanEnumerations;
// FIXME - The derived details of the loan schedule are now updated on the fly when someone does a transaction against a loan. So this is not fully needed. // FIXME - The derived details of the loan schedule are now updated on the fly when someone does a transaction against a loan. So this is not fully needed.
// - we still use this to figure out how much of a given transactions amount was principal versus interest but might not need this and we should probably // - we still use this to figure out how much of a given transactions amount was principal versus interest but might not need this and we should probably
@@ -28,7 +30,7 @@ public class DerivedLoanDataProcessor {
List<LoanRepaymentPeriodData> loanRepaymentPeriods = repaymentScheduleDetails.getPeriods(); List<LoanRepaymentPeriodData> loanRepaymentPeriods = repaymentScheduleDetails.getPeriods();
// break up payments based on principal and interest // break up payments based on principal and interest
List<LoanRepaymentData> loanRepayments = new ArrayList<LoanRepaymentData>(); List<LoanTransactionData> loanRepayments = new ArrayList<LoanTransactionData>();
int repaymentScheduleIndex = 0; int repaymentScheduleIndex = 0;
for (LoanTransaction transaction : loanTransactions) { for (LoanTransaction transaction : loanTransactions) {
if (transaction.isRepayment() || transaction.isWaiver()) { if (transaction.isRepayment() || transaction.isWaiver()) {
@@ -40,7 +42,7 @@ public class DerivedLoanDataProcessor {
.get(repaymentScheduleIndex); .get(repaymentScheduleIndex);
if (transaction.isRepayment()) { if (transaction.isRepayment()) {
LoanRepaymentData repaymentData = processLoanRepayment( LoanTransactionData repaymentData = processLoanRepayment(
transaction, scheduledRepaymentPeriod, transaction, scheduledRepaymentPeriod,
repaymentScheduleIndex, loanRepaymentPeriods, repaymentScheduleIndex, loanRepaymentPeriods,
arrearsTolerance, currencyDetail); arrearsTolerance, currencyDetail);
@@ -48,7 +50,7 @@ public class DerivedLoanDataProcessor {
} }
if (transaction.isWaiver()) { if (transaction.isWaiver()) {
LoanRepaymentData repaymentData = processWaiver(transaction, scheduledRepaymentPeriod, currencyDetail); LoanTransactionData repaymentData = processWaiver(transaction, scheduledRepaymentPeriod, currencyDetail);
loanRepayments.add(repaymentData); loanRepayments.add(repaymentData);
} }
@@ -73,7 +75,7 @@ public class DerivedLoanDataProcessor {
return new DerivedLoanData(processRepaymentScheduleData, summaryData, loanRepayments); return new DerivedLoanData(processRepaymentScheduleData, summaryData, loanRepayments);
} }
private LoanRepaymentData processWaiver(LoanTransaction transaction, private LoanTransactionData processWaiver(LoanTransaction transaction,
LoanRepaymentPeriodData scheduledRepaymentPeriod, CurrencyData currencyData) { LoanRepaymentPeriodData scheduledRepaymentPeriod, CurrencyData currencyData) {
MonetaryCurrency currency = new MonetaryCurrency(currencyData.getCode(), currencyData.getDecimalPlaces()); MonetaryCurrency currency = new MonetaryCurrency(currencyData.getCode(), currencyData.getDecimalPlaces());
@@ -125,7 +127,8 @@ public class DerivedLoanDataProcessor {
Money remainingOutstanding = totalOutstanding.minus(moneyFrom(totalWaived)); Money remainingOutstanding = totalOutstanding.minus(moneyFrom(totalWaived));
scheduledRepaymentPeriod.setTotalOutstanding(moneyDataFrom(currencyData, remainingOutstanding)); scheduledRepaymentPeriod.setTotalOutstanding(moneyDataFrom(currencyData, remainingOutstanding));
LoanRepaymentData loanRepaymentData = new LoanRepaymentData(transaction.getId(), transaction.getTransactionDate(), moneyDataFrom(currencyData, principalWaived), EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WAIVED);
LoanTransactionData loanRepaymentData = new LoanTransactionData(transaction.getId(), transactionType, transaction.getTransactionDate(), moneyDataFrom(currencyData, principalWaived),
moneyDataFrom(currencyData, interestWaived), totalWaived, zero); moneyDataFrom(currencyData, interestWaived), totalWaived, zero);
loanRepaymentData.setTotalWaived(totalWaived); loanRepaymentData.setTotalWaived(totalWaived);
return loanRepaymentData; return loanRepaymentData;
@@ -270,7 +273,7 @@ public class DerivedLoanDataProcessor {
/** /**
* TODO - pays off interest first then principal so should make this configurable when generating 'derived view of loan account' * TODO - pays off interest first then principal so should make this configurable when generating 'derived view of loan account'
*/ */
private LoanRepaymentData processLoanRepayment(LoanTransaction repayment, private LoanTransactionData processLoanRepayment(LoanTransaction repayment,
LoanRepaymentPeriodData scheduledRepayment, LoanRepaymentPeriodData scheduledRepayment,
int repaymentScheduleIndex, int repaymentScheduleIndex,
List<LoanRepaymentPeriodData> repaymentSchedulePeriods, List<LoanRepaymentPeriodData> repaymentSchedulePeriods,
@@ -443,7 +446,8 @@ public class DerivedLoanDataProcessor {
MoneyData totalPaid = moneyDataFrom(currencyDetail, totalPrincipalComponent.plus(totalInterestComponent)); MoneyData totalPaid = moneyDataFrom(currencyDetail, totalPrincipalComponent.plus(totalInterestComponent));
LoanRepaymentData loanRepaymentData = new LoanRepaymentData(repayment.getId(), repayment.getTransactionDate(), cumulativePrincipalPaid, cumulativeInterestPaid, totalPaid, overpaid); EnumOptionData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
LoanTransactionData loanRepaymentData = new LoanTransactionData(repayment.getId(), transactionType, repayment.getTransactionDate(), cumulativePrincipalPaid, cumulativeInterestPaid, totalPaid, overpaid);
return loanRepaymentData; return loanRepaymentData;
} }
@@ -32,13 +32,14 @@ import org.mifosng.platform.api.data.LoanAccountData;
import org.mifosng.platform.api.data.LoanAccountSummaryData; import org.mifosng.platform.api.data.LoanAccountSummaryData;
import org.mifosng.platform.api.data.LoanBasicDetailsData; import org.mifosng.platform.api.data.LoanBasicDetailsData;
import org.mifosng.platform.api.data.LoanPermissionData; import org.mifosng.platform.api.data.LoanPermissionData;
import org.mifosng.platform.api.data.LoanRepaymentData;
import org.mifosng.platform.api.data.LoanRepaymentScheduleData; import org.mifosng.platform.api.data.LoanRepaymentScheduleData;
import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.MoneyData; import org.mifosng.platform.api.data.MoneyData;
import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.InvalidLoanStateTransitionException; import org.mifosng.platform.exceptions.InvalidLoanStateTransitionException;
import org.mifosng.platform.exceptions.InvalidLoanTransactionTypeException;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.loanproduct.service.LoanEnumerations; import org.mifosng.platform.loanproduct.service.LoanEnumerations;
import org.mifosng.platform.organisation.domain.Organisation; import org.mifosng.platform.organisation.domain.Organisation;
@@ -336,6 +337,17 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
throw new InvalidLoanStateTransitionException("waive", "cannot.be.a.futre.date", errorMessage, loanTransactionDate); throw new InvalidLoanStateTransitionException("waive", "cannot.be.a.futre.date", errorMessage, loanTransactionDate);
} }
if (getTotalOutstanding().isGreaterThan(this.getInArrearsTolerance())) {
final String errorMessage = "Waiver is only allowed when the total outstanding amount left on loan (" + getTotalOutstanding() +") is less than thethe in arrears tolerance setting of " + getInArrearsTolerance().getAmount();
throw new InvalidLoanStateTransitionException("waive", "cannot.exceed.in.arrears.tolerance.setting", errorMessage, getTotalOutstanding(), getInArrearsTolerance());
}
Money waived = Money.of(getCurrency(), loanTransaction.getAmount());
if (waived.isGreaterThan(this.getInArrearsTolerance())) {
final String errorMessage = "The amount being waived cannot exceed the in arrears tolerance setting of " + getInArrearsTolerance().getAmount();
throw new InvalidLoanStateTransitionException("waive", "cannot.exceed.in.arrears.tolerance.setting", errorMessage, waived, getInArrearsTolerance());
}
if (this.isRepaidInFull()) { if (this.isRepaidInFull()) {
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this.loanStatus); this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this.loanStatus);
this.closedOnDate = loanTransaction.getTransactionDate().toDate(); this.closedOnDate = loanTransaction.getTransactionDate().toDate();
@@ -361,8 +373,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
deriveLoanRepaymentScheduleCompletedData(); deriveLoanRepaymentScheduleCompletedData();
if (loanTransaction.isNotRepayment()) { if (loanTransaction.isNotRepayment()) {
throw new IllegalArgumentException( final String errorMessage = "A transaction of type repayment was expected but not received.";
"Only repayment transactions can be passed to makeRepayment."); throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.transaction", errorMessage);
} }
LocalDate loanTransactionDate = loanTransaction.getTransactionDate(); LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
@@ -469,7 +481,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
LoanTransaction newTransactionDetail, LoanLifecycleStateMachine loanLifecycleStateMachine) { LoanTransaction newTransactionDetail, LoanLifecycleStateMachine loanLifecycleStateMachine) {
if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()) { if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()) {
throw new IllegalArgumentException("Only repayment and waiver transactions can be adjusted."); 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);
} }
transactionForAdjustment.contra(); transactionForAdjustment.contra();
@@ -982,7 +995,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
public LoanAccountData toLoanAccountData(LoanAccountSummaryData summary, LoanRepaymentScheduleData repaymentSchedule, public LoanAccountData toLoanAccountData(LoanAccountSummaryData summary, LoanRepaymentScheduleData repaymentSchedule,
List<LoanRepaymentData> loanRepayments, List<LoanTransactionData> loanRepayments,
CurrencyData currencyData) { CurrencyData currencyData) {
LoanBasicDetailsData basicDetails = toBasicDetailsData(currencyData); LoanBasicDetailsData basicDetails = toBasicDetailsData(currencyData);
@@ -2,13 +2,13 @@ package org.mifosng.platform.loan.domain;
import java.util.Comparator; import java.util.Comparator;
import org.mifosng.platform.api.data.LoanRepaymentData; import org.mifosng.platform.api.data.LoanTransactionData;
public class LoanRepaymentDataComparator implements Comparator<LoanRepaymentData> { public class LoanRepaymentDataComparator implements Comparator<LoanTransactionData> {
@Override @Override
public int compare(final LoanRepaymentData o1, final LoanRepaymentData o2) { public int compare(final LoanTransactionData o1, final LoanTransactionData o2) {
return o2.getDate().compareTo(o1.getDate()); return o2.getDate().compareTo(o1.getDate());
} }
@@ -140,6 +140,14 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
public boolean isIdentifiedBy(Long identifier) { public boolean isIdentifiedBy(Long identifier) {
return this.getId().equals(identifier); return this.getId().equals(identifier);
} }
public boolean isBelongingToLoanOf(Loan check) {
return this.loan.getId().equals(check.getId());
}
public boolean isNotBelongingToLoanOf(Loan check) {
return !isBelongingToLoanOf(check);
}
public void contra() { public void contra() {
this.contra = LoanTransaction.contra(this); this.contra = LoanTransaction.contra(this);
@@ -1,22 +1,28 @@
package org.mifosng.platform.loan.domain; package org.mifosng.platform.loan.domain;
public enum LoanTransactionType { public enum LoanTransactionType {
UNKNOWN(0), DISBURSEMENT(1), REPAYMENT(2), REVERSAL(3), WAIVED(4); INVALID(0, "loanTransactionType.invalid"), DISBURSEMENT(1, "loanTransactionType.disbursement"), REPAYMENT(2, "loanTransactionType.repayment"), REVERSAL(3, "loanTransactionType.reversal"), WAIVED(4, "loanTransactionType.waiver");
private final Integer value; private final Integer value;
private final String code;
private LoanTransactionType(final Integer value) { private LoanTransactionType(final Integer value, final String code) {
this.value = value; this.value = value;
this.code = code;
} }
public Integer getValue() { public Integer getValue() {
return this.value; return this.value;
} }
public String getCode() {
return code;
}
public static LoanTransactionType fromInt(final Integer transactionType) { public static LoanTransactionType fromInt(final Integer transactionType) {
if (transactionType == null) { if (transactionType == null) {
return LoanTransactionType.UNKNOWN; return LoanTransactionType.INVALID;
} }
LoanTransactionType loanTransactionType = null; LoanTransactionType loanTransactionType = null;
@@ -34,7 +40,7 @@ public enum LoanTransactionType {
loanTransactionType = LoanTransactionType.WAIVED; loanTransactionType = LoanTransactionType.WAIVED;
break; break;
default: default:
loanTransactionType = LoanTransactionType.UNKNOWN; loanTransactionType = LoanTransactionType.INVALID;
break; break;
} }
return loanTransactionType; return loanTransactionType;
@@ -1,7 +1,7 @@
package org.mifosng.platform.loan.service; package org.mifosng.platform.loan.service;
import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanAccountData;
import org.mifosng.platform.api.data.LoanRepaymentData; import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.NewLoanData; import org.mifosng.platform.api.data.NewLoanData;
public interface LoanReadPlatformService { public interface LoanReadPlatformService {
@@ -10,9 +10,9 @@ public interface LoanReadPlatformService {
NewLoanData retrieveClientAndProductDetails(Long clientId, Long productId); NewLoanData retrieveClientAndProductDetails(Long clientId, Long productId);
LoanRepaymentData retrieveNewLoanRepaymentDetails(Long loanId); LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId);
LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId); LoanTransactionData retrieveNewLoanWaiverDetails(Long loanId);
LoanRepaymentData retrieveLoanRepaymentDetails(Long loanId, Long transactionId); LoanTransactionData retrieveLoanTransactionDetails(Long loanId, Long transactionId);
} }
@@ -13,7 +13,7 @@ import org.mifosng.platform.api.data.DerivedLoanData;
import org.mifosng.platform.api.data.LoanAccountData; import org.mifosng.platform.api.data.LoanAccountData;
import org.mifosng.platform.api.data.LoanProductData; import org.mifosng.platform.api.data.LoanProductData;
import org.mifosng.platform.api.data.LoanProductLookup; import org.mifosng.platform.api.data.LoanProductLookup;
import org.mifosng.platform.api.data.LoanRepaymentData; import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.MoneyData; import org.mifosng.platform.api.data.MoneyData;
import org.mifosng.platform.api.data.NewLoanData; import org.mifosng.platform.api.data.NewLoanData;
import org.mifosng.platform.client.service.ClientReadPlatformService; import org.mifosng.platform.client.service.ClientReadPlatformService;
@@ -27,6 +27,8 @@ import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanRepository; import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.LoanTransaction; import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.LoanTransactionRepository; import org.mifosng.platform.loan.domain.LoanTransactionRepository;
import org.mifosng.platform.loan.domain.LoanTransactionType;
import org.mifosng.platform.loanproduct.service.LoanEnumerations;
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService; import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
import org.mifosng.platform.security.PlatformSecurityContext; import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUser;
@@ -137,7 +139,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
} }
@Override @Override
public LoanRepaymentData retrieveNewLoanRepaymentDetails(Long loanId) { public LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId) {
AppUser currentUser = context.authenticatedUser(); AppUser currentUser = context.authenticatedUser();
@@ -167,7 +169,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
MoneyData possibleNextRepayment = MoneyData.of(currencyData, MoneyData possibleNextRepayment = MoneyData.of(currencyData,
possibleNextRepaymentAmount.getAmount()); possibleNextRepaymentAmount.getAmount());
LoanRepaymentData newRepaymentDetails = new LoanRepaymentData(); LoanTransactionData newRepaymentDetails = new LoanTransactionData();
newRepaymentDetails.setTransactionType(LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT));
newRepaymentDetails.setDate(earliestUnpaidInstallmentDate); newRepaymentDetails.setDate(earliestUnpaidInstallmentDate);
newRepaymentDetails.setTotal(possibleNextRepayment); newRepaymentDetails.setTotal(possibleNextRepayment);
@@ -175,7 +178,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
} }
@Override @Override
public LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId) { public LoanTransactionData retrieveNewLoanWaiverDetails(Long loanId) {
AppUser currentUser = context.authenticatedUser(); AppUser currentUser = context.authenticatedUser();
@@ -203,7 +206,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
MoneyData totalOutstandingData = MoneyData.of(currencyData, MoneyData totalOutstandingData = MoneyData.of(currencyData,
totalOutstanding.getAmount()); totalOutstanding.getAmount());
LoanRepaymentData newWaiverDetails = new LoanRepaymentData(); LoanTransactionData newWaiverDetails = new LoanTransactionData();
newWaiverDetails.setTransactionType(LoanEnumerations.transactionType(LoanTransactionType.WAIVED));
newWaiverDetails.setDate(new LocalDate()); newWaiverDetails.setDate(new LocalDate());
newWaiverDetails.setTotal(totalOutstandingData); newWaiverDetails.setTotal(totalOutstandingData);
@@ -211,7 +215,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
} }
@Override @Override
public LoanRepaymentData retrieveLoanRepaymentDetails(Long loanId, public LoanTransactionData retrieveLoanTransactionDetails(Long loanId,
Long transactionId) { Long transactionId) {
AppUser currentUser = context.authenticatedUser(); AppUser currentUser = context.authenticatedUser();
@@ -236,6 +240,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
if (transaction == null) { if (transaction == null) {
throw new LoanTransactionNotFoundException(transactionId); throw new LoanTransactionNotFoundException(transactionId);
} }
if (transaction.isNotBelongingToLoanOf(loan)) {
throw new LoanTransactionNotFoundException(transactionId, loanId);
}
CurrencyData currencyData = new CurrencyData(currency.getCode(), CurrencyData currencyData = new CurrencyData(currency.getCode(),
currency.getName(), currency.getDecimalPlaces(), currency.getName(), currency.getDecimalPlaces(),
@@ -243,7 +251,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
MoneyData total = MoneyData.of(currencyData, transaction.getAmount()); MoneyData total = MoneyData.of(currencyData, transaction.getAmount());
LocalDate date = transaction.getTransactionDate(); LocalDate date = transaction.getTransactionDate();
LoanRepaymentData loanRepaymentData = new LoanRepaymentData(); LoanTransactionData loanRepaymentData = new LoanTransactionData();
loanRepaymentData.setTransactionType(LoanEnumerations.transactionType(transaction.getTypeOf()));
loanRepaymentData.setId(transactionId); loanRepaymentData.setId(transactionId);
loanRepaymentData.setTotal(total); loanRepaymentData.setTotal(total);
loanRepaymentData.setDate(date); loanRepaymentData.setDate(date);
@@ -152,7 +152,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -175,7 +175,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -206,7 +206,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -237,7 +237,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -324,7 +324,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -352,7 +352,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -389,7 +389,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -435,7 +435,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
@Transactional @Transactional
@@ -470,6 +470,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note); this.noteRepository.save(note);
} }
return new EntityIdentifier(loan.getClient().getId()); return new EntityIdentifier(loan.getId());
} }
} }
@@ -4,6 +4,7 @@ import org.mifosng.platform.api.data.EnumOptionData;
import org.mifosng.platform.loan.domain.AmortizationMethod; import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod; import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod; import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.LoanTransactionType;
import org.mifosng.platform.loan.domain.PeriodFrequencyType; import org.mifosng.platform.loan.domain.PeriodFrequencyType;
public class LoanEnumerations { public class LoanEnumerations {
@@ -87,7 +88,7 @@ public class LoanEnumerations {
optionData = new EnumOptionData(InterestMethod.DECLINING_BALANCE.getValue().longValue(), InterestMethod.DECLINING_BALANCE.getCode(), "Declining Balance"); optionData = new EnumOptionData(InterestMethod.DECLINING_BALANCE.getValue().longValue(), InterestMethod.DECLINING_BALANCE.getCode(), "Declining Balance");
break; break;
default: default:
optionData = new EnumOptionData(AmortizationMethod.INVALID.getValue().longValue(), AmortizationMethod.INVALID.getCode(), "Invalid"); optionData = new EnumOptionData(InterestMethod.INVALID.getValue().longValue(), InterestMethod.INVALID.getCode(), "Invalid");
break; break;
} }
return optionData; return optionData;
@@ -107,7 +108,33 @@ public class LoanEnumerations {
optionData = new EnumOptionData(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD.getValue().longValue(), InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD.getCode(), "Same as repayment period"); optionData = new EnumOptionData(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD.getValue().longValue(), InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD.getCode(), "Same as repayment period");
break; break;
default: default:
optionData = new EnumOptionData(AmortizationMethod.INVALID.getValue().longValue(), AmortizationMethod.INVALID.getCode(), "Invalid"); optionData = new EnumOptionData(InterestCalculationPeriodMethod.INVALID.getValue().longValue(), InterestCalculationPeriodMethod.INVALID.getCode(), "Invalid");
break;
}
return optionData;
}
public static EnumOptionData transactionType(Integer id) {
return transactionType(LoanTransactionType.fromInt(id));
}
public static EnumOptionData transactionType(LoanTransactionType type) {
EnumOptionData optionData = null;
switch (type) {
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");
break;
case WAIVED:
optionData = new EnumOptionData(LoanTransactionType.WAIVED.getValue().longValue(), LoanTransactionType.WAIVED.getCode(), "Waiver");
break;
default:
optionData = new EnumOptionData(LoanTransactionType.INVALID.getValue().longValue(), LoanTransactionType.INVALID.getCode(), "Invalid");
break; break;
} }
return optionData; return optionData;
@@ -12,7 +12,7 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mifosng.platform.api.data.CurrencyData; import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.DerivedLoanData; import org.mifosng.platform.api.data.DerivedLoanData;
import org.mifosng.platform.api.data.LoanRepaymentData; import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.LoanRepaymentPeriodData; import org.mifosng.platform.api.data.LoanRepaymentPeriodData;
import org.mifosng.platform.api.data.LoanRepaymentScheduleData; import org.mifosng.platform.api.data.LoanRepaymentScheduleData;
import org.mifosng.platform.api.data.MoneyData; import org.mifosng.platform.api.data.MoneyData;
@@ -81,11 +81,11 @@ public class DerivedLoanDataProcessorTest {
assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding)); assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding));
// assert repayments are ok // assert repayments are ok
List<LoanRepaymentData> loanRepayments = derivedData.getLoanRepayments(); List<LoanTransactionData> loanRepayments = derivedData.getLoanRepayments();
assertThat(loanRepayments, is(notNullValue())); assertThat(loanRepayments, is(notNullValue()));
assertThat(loanRepayments.size(), is(1)); assertThat(loanRepayments.size(), is(1));
LoanRepaymentData firstRepayment = loanRepayments.get(0); LoanTransactionData firstRepayment = loanRepayments.get(0);
assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid)); assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid));
assertThat(firstRepayment.getInterest(), is(expectedInterestPaid)); assertThat(firstRepayment.getInterest(), is(expectedInterestPaid));
assertThat(firstRepayment.getTotal(), is(expectedTotal)); assertThat(firstRepayment.getTotal(), is(expectedTotal));
@@ -147,11 +147,11 @@ public class DerivedLoanDataProcessorTest {
assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding)); assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding));
// assert repayments are ok // assert repayments are ok
List<LoanRepaymentData> loanRepayments = derivedData.getLoanRepayments(); List<LoanTransactionData> loanRepayments = derivedData.getLoanRepayments();
assertThat(loanRepayments, is(notNullValue())); assertThat(loanRepayments, is(notNullValue()));
assertThat(loanRepayments.size(), is(1)); assertThat(loanRepayments.size(), is(1));
LoanRepaymentData firstRepayment = loanRepayments.get(0); LoanTransactionData firstRepayment = loanRepayments.get(0);
assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid)); assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid));
assertThat(firstRepayment.getInterest(), is(expectedInterestPaid)); assertThat(firstRepayment.getInterest(), is(expectedInterestPaid));
assertThat(firstRepayment.getTotal(), is(expectedTotal)); assertThat(firstRepayment.getTotal(), is(expectedTotal));
@@ -210,11 +210,11 @@ public class DerivedLoanDataProcessorTest {
assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding)); assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding));
// assert repayments are ok // assert repayments are ok
List<LoanRepaymentData> loanRepayments = derivedData.getLoanRepayments(); List<LoanTransactionData> loanRepayments = derivedData.getLoanRepayments();
assertThat(loanRepayments, is(notNullValue())); assertThat(loanRepayments, is(notNullValue()));
assertThat(loanRepayments.size(), is(1)); assertThat(loanRepayments.size(), is(1));
LoanRepaymentData firstRepayment = loanRepayments.get(0); LoanTransactionData firstRepayment = loanRepayments.get(0);
assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid)); assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid));
assertThat(firstRepayment.getInterest(), is(expectedInterestPaid)); assertThat(firstRepayment.getInterest(), is(expectedInterestPaid));
assertThat(firstRepayment.getTotal(), is(expectedTotal)); assertThat(firstRepayment.getTotal(), is(expectedTotal));
@@ -273,11 +273,11 @@ public class DerivedLoanDataProcessorTest {
assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding)); assertThat(periodData.getInterestOutstanding(), is(expectedInterestOutstanding));
// assert repayments are ok // assert repayments are ok
List<LoanRepaymentData> loanRepayments = derivedData.getLoanRepayments(); List<LoanTransactionData> loanRepayments = derivedData.getLoanRepayments();
assertThat(loanRepayments, is(notNullValue())); assertThat(loanRepayments, is(notNullValue()));
assertThat(loanRepayments.size(), is(1)); assertThat(loanRepayments.size(), is(1));
LoanRepaymentData firstRepayment = loanRepayments.get(0); LoanTransactionData firstRepayment = loanRepayments.get(0);
assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid)); assertThat(firstRepayment.getPrincipal(), is(expectedPrincipalPaid));
assertThat(firstRepayment.getInterest(), is(expectedInterestPaid)); assertThat(firstRepayment.getInterest(), is(expectedInterestPaid));
assertThat(firstRepayment.getTotal(), is(expectedTotal)); assertThat(firstRepayment.getTotal(), is(expectedTotal));
@@ -359,11 +359,11 @@ public class DerivedLoanDataProcessorTest {
assertThat(secondPeriod.getInterestOutstanding(), is(expectedInterestOutstanding)); assertThat(secondPeriod.getInterestOutstanding(), is(expectedInterestOutstanding));
// assert repayments are ok // assert repayments are ok
List<LoanRepaymentData> loanRepayments = derivedData.getLoanRepayments(); List<LoanTransactionData> loanRepayments = derivedData.getLoanRepayments();
assertThat(loanRepayments, is(notNullValue())); assertThat(loanRepayments, is(notNullValue()));
assertThat(loanRepayments.size(), is(1)); assertThat(loanRepayments.size(), is(1));
LoanRepaymentData firstRepayment = loanRepayments.get(0); LoanTransactionData firstRepayment = loanRepayments.get(0);
assertThat(firstRepayment.getPrincipal(), is(moneyDataFrom(new MoneyBuilder().with(cfaCurrency).with("1180").build()))); assertThat(firstRepayment.getPrincipal(), is(moneyDataFrom(new MoneyBuilder().with(cfaCurrency).with("1180").build())));
assertThat(firstRepayment.getInterest(), is(moneyDataFrom(new MoneyBuilder().with(cfaCurrency).with("20").build()))); assertThat(firstRepayment.getInterest(), is(moneyDataFrom(new MoneyBuilder().with(cfaCurrency).with("20").build())));
assertThat(firstRepayment.getTotal(), is(moneyDataFrom(new MoneyBuilder().with(cfaCurrency).with("1200").build()))); assertThat(firstRepayment.getTotal(), is(moneyDataFrom(new MoneyBuilder().with(cfaCurrency).with("1200").build())));