MIFOSX-172: WIP support loan writeoff, closure scenarios with accounting.
This commit is contained in:
-4
@@ -7,7 +7,6 @@ package org.mifosplatform.accounting.journalentry.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mifosplatform.accounting.glaccount.data.LoanDTO;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
@@ -18,7 +17,4 @@ public interface JournalEntryWritePlatformService {
|
||||
CommandProcessingResult revertJournalEntry(JsonCommand command);
|
||||
|
||||
void createJournalEntriesForLoan(Map<String, Object> accountingBridgeData);
|
||||
|
||||
void createJournalEntriesForLoan(LoanDTO loanDTO);
|
||||
|
||||
}
|
||||
|
||||
-11
@@ -140,17 +140,6 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
||||
return new CommandProcessingResultBuilder().withTransactionId(reversalTransactionId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void createJournalEntriesForLoan(final LoanDTO loanDTO) {
|
||||
// shouldn't be before an accounting closure
|
||||
if (loanDTO.isCashBasedAccountingEnabled()) {
|
||||
createJournalEntriesUsingCashRules(loanDTO);
|
||||
} else if (loanDTO.isAccrualBasedAccountingEnabled()) {
|
||||
// TODO Vishwas: add accrual based accounting rules
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void createJournalEntriesForLoan(final Map<String, Object> accountingBridgeData) {
|
||||
|
||||
+4
-3
@@ -33,10 +33,11 @@ public class ApiRequestJsonSerializationSettings {
|
||||
public static ApiRequestJsonSerializationSettings from(final boolean prettyPrint, final Set<String> parametersForPartialResponse,
|
||||
final boolean template, final Long commandId, final boolean makerCheckerable, final boolean includeJson) {
|
||||
|
||||
// FIXME - kw - rather than always creating new objects for this could
|
||||
// FIXME - KW - rather than always creating new objects for this could
|
||||
// just send by common ones like, prettyprint=false, empty response
|
||||
// parameters
|
||||
return new ApiRequestJsonSerializationSettings(prettyPrint, parametersForPartialResponse, template, commandId, makerCheckerable, includeJson);
|
||||
return new ApiRequestJsonSerializationSettings(prettyPrint, parametersForPartialResponse, template, commandId, makerCheckerable,
|
||||
includeJson);
|
||||
}
|
||||
|
||||
public boolean isPrettyPrint() {
|
||||
@@ -50,7 +51,7 @@ public class ApiRequestJsonSerializationSettings {
|
||||
public boolean isMakerCheckerable() {
|
||||
return this.makerCheckerable;
|
||||
}
|
||||
|
||||
|
||||
public boolean isIncludeJson() {
|
||||
return this.includeJson;
|
||||
}
|
||||
|
||||
+4
-4
@@ -27,7 +27,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.sun.rowset.CachedRowSetImpl;
|
||||
|
||||
//TODO - Performance Item - most items (code values etc) can be cached but not doing that yet
|
||||
//TODO - JW - Performance Item - most items (code values etc) can be cached but not doing that yet
|
||||
@Service
|
||||
public class GenericDataServiceImpl implements GenericDataService {
|
||||
|
||||
@@ -41,8 +41,8 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
|
||||
@Override
|
||||
public CachedRowSet getCachedResultSet(final String sql, final String sqlErrorMsg) {
|
||||
// FIXME - Need to reimplement this away from Sun library - could be
|
||||
// mixture of Lists and jdbcTemplate.query
|
||||
// FIXME - KW - Need to reimplement this away from Sun library - could
|
||||
// be mixture of Lists and jdbcTemplate.query
|
||||
|
||||
// long startTime = System.currentTimeMillis();
|
||||
Connection db_connection = null;
|
||||
@@ -195,7 +195,7 @@ public class GenericDataServiceImpl implements GenericDataService {
|
||||
writer.append("[" + localDate.getYear() + ", " + localDate.getMonthOfYear() + ", " + localDate.getDayOfMonth()
|
||||
+ "]");
|
||||
} else
|
||||
writer.append(doubleQuote + replace(currVal, doubleQuote,slashDoubleQuote) + doubleQuote);
|
||||
writer.append(doubleQuote + replace(currVal, doubleQuote, slashDoubleQuote) + doubleQuote);
|
||||
}
|
||||
} else
|
||||
writer.append("null");
|
||||
|
||||
+7
-7
@@ -82,7 +82,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
return new StreamingOutput() {
|
||||
|
||||
@Override
|
||||
public void write(OutputStream out) {
|
||||
public void write(final OutputStream out) {
|
||||
try {
|
||||
|
||||
GenericResultsetData result = retrieveGenericResultset(name, type, queryParams);
|
||||
@@ -108,7 +108,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
|
||||
}
|
||||
|
||||
private static StringBuffer generateCsvFileBuffer(GenericResultsetData result) {
|
||||
private static StringBuffer generateCsvFileBuffer(final GenericResultsetData result) {
|
||||
StringBuffer writer = new StringBuffer();
|
||||
|
||||
List<ResultsetColumnHeader> columnHeaders = result.getColumnHeaders();
|
||||
@@ -206,7 +206,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
|
||||
}
|
||||
|
||||
private String getSql(String name, String type) {
|
||||
private String getSql(final String name, final String type) {
|
||||
|
||||
String inputSql = "select " + type + "_sql as the_sql from stretchy_" + type + " where " + type + "_name = '" + name + "'";
|
||||
inputSql = genericDataService.wrapSQL(inputSql);
|
||||
@@ -226,7 +226,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReportType(String reportName) {
|
||||
public String getReportType(final String reportName) {
|
||||
String sql = "SELECT ifnull(report_type,'') as report_type FROM `stretchy_report` where report_name = '" + reportName + "'";
|
||||
|
||||
sql = genericDataService.wrapSQL(sql);
|
||||
@@ -246,7 +246,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response processPentahoRequest(String reportName, String outputTypeParam, Map<String, String> queryParams) {
|
||||
public Response processPentahoRequest(final String reportName, final String outputTypeParam, final Map<String, String> queryParams) {
|
||||
|
||||
String outputType = "HTML";
|
||||
if (StringUtils.isNotBlank(outputTypeParam)) outputType = outputTypeParam;
|
||||
@@ -258,7 +258,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
if (noPentaho)
|
||||
throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled", "Pentaho is not enabled");
|
||||
|
||||
// TODO - use pentaho location finder like Pawel does in Mifos
|
||||
// TODO - JW - use pentaho location finder like Pawel does in Mifos
|
||||
// String reportPath =
|
||||
// "C:\\dev\\apache-tomcat-7.0.25\\webapps\\ROOT\\PentahoReports\\"
|
||||
// + reportName + ".prpt";
|
||||
@@ -313,7 +313,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
|
||||
}
|
||||
|
||||
private void addParametersToReport(MasterReport report, Map<String, String> queryParams) {
|
||||
private void addParametersToReport(final MasterReport report, final Map<String, String> queryParams) {
|
||||
|
||||
try {
|
||||
ReportParameterValues rptParamValues = report.getParameterValues();
|
||||
|
||||
+4
-3
@@ -103,7 +103,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
String updatePermissionChecker = "'UPDATE_" + datatable + "_CHECKER'";
|
||||
String deletePermission = "'DELETE_" + datatable + "'";
|
||||
String deletePermissionChecker = "'DELETE_" + datatable + "_CHECKER'";
|
||||
// TODO - put in batch command later
|
||||
// TODO - JW - put in batch command later
|
||||
String sql = "insert into x_registered_table (registered_table_name, application_table_name) values ('" + datatable + "', '"
|
||||
+ appTable + "')";
|
||||
|
||||
@@ -125,7 +125,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
|
||||
@Override
|
||||
public void deregisterDatatable(final String datatable) {
|
||||
// TODO - put in batch command later
|
||||
// TODO - JW - put in batch command later
|
||||
|
||||
String permissionList = "('CREATE_" + datatable + "', 'CREATE_" + datatable + "_CHECKER', 'READ_" + datatable + "', 'UPDATE_"
|
||||
+ datatable + "', 'UPDATE_" + datatable + "_CHECKER', 'DELETE_" + datatable + "', 'DELETE_" + datatable + "_CHECKER')";
|
||||
@@ -356,7 +356,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
|
||||
}
|
||||
|
||||
private String getApplicationTableName(final String datatable) {
|
||||
// TODO - only used for update... can probably remove this after as its
|
||||
// TODO - JW - only used for update... can probably remove this after as
|
||||
// its
|
||||
// a reread
|
||||
String sql = "SELECT application_table_name FROM x_registered_table where registered_table_name = '" + datatable + "'";
|
||||
|
||||
|
||||
+7
-7
@@ -37,7 +37,7 @@ import com.sun.jersey.core.header.FormDataContentDisposition;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
|
||||
// FIXME - kw- image should be images to keep consitent with rest of API - even if we only support one image per client.
|
||||
// FIXME - KW - image should be images to keep consistent with rest of API - even if we only support one image per client.
|
||||
@Path("/clients/{clientId}/image")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
@@ -63,9 +63,9 @@ public class ClientImagesApiResource {
|
||||
@POST
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String addNewClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") Long fileSize,
|
||||
@FormDataParam("file") InputStream inputStream, @FormDataParam("file") FormDataContentDisposition fileDetails,
|
||||
@FormDataParam("file") FormDataBodyPart bodyPart) {
|
||||
public String addNewClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") final Long fileSize,
|
||||
@FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition fileDetails,
|
||||
@FormDataParam("file") final FormDataBodyPart bodyPart) {
|
||||
|
||||
// TODO: vishwas might need more advances validation (like reading magic
|
||||
// number) for handling malicious clients
|
||||
@@ -119,9 +119,9 @@ public class ClientImagesApiResource {
|
||||
@PUT
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String updateClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") Long fileSize,
|
||||
@FormDataParam("file") InputStream inputStream, @FormDataParam("file") FormDataContentDisposition fileDetails,
|
||||
@FormDataParam("file") FormDataBodyPart bodyPart) {
|
||||
public String updateClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") final Long fileSize,
|
||||
@FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition fileDetails,
|
||||
@FormDataParam("file") final FormDataBodyPart bodyPart) {
|
||||
|
||||
return addNewClientImage(clientId, fileSize, inputStream, fileDetails, bodyPart);
|
||||
}
|
||||
|
||||
+5
-5
@@ -50,7 +50,7 @@ public class ClientIdentifierCommand {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
|
||||
|
||||
public void validateForUpdate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
@@ -58,10 +58,10 @@ public class ClientIdentifierCommand {
|
||||
|
||||
baseDataValidator.reset().parameter("documentKey").value(this.documentKey).ignoreIfNull().notBlank();
|
||||
|
||||
// FIXME - kw
|
||||
// if (command.isDocumentTypeChanged()) {
|
||||
// baseDataValidator.reset().parameter("documentTypeId").value(command.getDocumentTypeId()).notNull().integerGreaterThanZero();
|
||||
// }
|
||||
// FIXME - KW - add in validation
|
||||
// if (command.isDocumentTypeChanged()) {
|
||||
// baseDataValidator.reset().parameter("documentTypeId").value(command.getDocumentTypeId()).notNull().integerGreaterThanZero();
|
||||
// }
|
||||
|
||||
baseDataValidator.reset().anyOfNotNull(this.documentTypeId, this.documentKey);
|
||||
|
||||
|
||||
+9
-8
@@ -96,7 +96,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ClientLookup> retrieveAllIndividualClientsForLookup(String extraCriteria) {
|
||||
public Collection<ClientLookup> retrieveAllIndividualClientsForLookup(final String extraCriteria) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
@@ -112,7 +112,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ClientLookup> retrieveAllIndividualClientsForLookupByOfficeId(Long officeId) {
|
||||
public Collection<ClientLookup> retrieveAllIndividualClientsForLookupByOfficeId(final Long officeId) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
ClientLookupMapper rm = new ClientLookupMapper();
|
||||
@@ -290,7 +290,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ClientAccountSummaryData> retrieveClientLoanAccountsByLoanOfficerId(Long clientId, Long loanOfficerId) {
|
||||
public Collection<ClientAccountSummaryData> retrieveClientLoanAccountsByLoanOfficerId(final Long clientId, final Long loanOfficerId) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
@@ -357,7 +357,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoteData retrieveClientNote(Long clientId, Long noteId) {
|
||||
public NoteData retrieveClientNote(final Long clientId, final Long noteId) {
|
||||
|
||||
try {
|
||||
context.authenticatedUser();
|
||||
@@ -365,7 +365,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
// Check if client exists
|
||||
retrieveIndividualClient(clientId);
|
||||
|
||||
// FIXME - use join on sql query to fetch user information for note
|
||||
// FIXME - KW - use join on sql query to fetch user information for
|
||||
// note
|
||||
// rather than fetching users?
|
||||
Collection<AppUserData> allUsers = this.appUserReadPlatformService.retrieveAllUsers();
|
||||
|
||||
@@ -380,14 +381,14 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<NoteData> retrieveAllClientNotes(Long clientId) {
|
||||
public Collection<NoteData> retrieveAllClientNotes(final Long clientId) {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
// Check if client exists
|
||||
retrieveIndividualClient(clientId);
|
||||
|
||||
// FIXME - use join on sql query to fetch user information for note
|
||||
// FIXME - KW - use join on sql query to fetch user information for note
|
||||
// rather than fetching users?
|
||||
Collection<AppUserData> allUsers = this.appUserReadPlatformService.retrieveAllUsers();
|
||||
|
||||
@@ -402,7 +403,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
|
||||
private final Collection<AppUserData> allUsers;
|
||||
|
||||
public NoteMapper(Collection<AppUserData> allUsers) {
|
||||
public NoteMapper(final Collection<AppUserData> allUsers) {
|
||||
this.allUsers = allUsers;
|
||||
}
|
||||
|
||||
|
||||
-6
@@ -36,12 +36,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
// FIXME: - undo disbursal fails when transactions have notes against them
|
||||
// FIXME: - modifying loan after disbursement
|
||||
// FIXME: - does not take into account 'actual disbursement' date being different to 'expected'
|
||||
// FIXME: - the schedule is 'generated' rather than been read from current loan schedule
|
||||
// FIXME: - should not allow modify loan after disbursal (should be only restructure/reschedule)
|
||||
// FIXME: - ui problem with default date for disburse dialog on some screens
|
||||
@Path("/loans/{loanId}/transactions")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.command;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
|
||||
/**
|
||||
* Immutable command for loan transactions.
|
||||
*/
|
||||
public class LoanTransactionCommand {
|
||||
|
||||
private final LocalDate transactionDate;
|
||||
private final BigDecimal transactionAmount;
|
||||
private final String note;
|
||||
|
||||
public LoanTransactionCommand(final LocalDate paymentDate, final BigDecimal paymentAmount, final String note) {
|
||||
this.transactionDate = paymentDate;
|
||||
this.transactionAmount = paymentAmount;
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
|
||||
baseDataValidator.reset().parameter("transactionDate").value(this.transactionDate).notNull();
|
||||
baseDataValidator.reset().parameter("transactionAmount").value(this.transactionAmount).notNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("note").value(this.note).notExceedingLengthOf(1000);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
|
||||
public void validateNonMonetaryTransaction() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
|
||||
baseDataValidator.reset().parameter("transactionDate").value(this.transactionDate).notNull();
|
||||
baseDataValidator.reset().parameter("note").value(this.note).notExceedingLengthOf(1000);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -144,7 +144,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
}
|
||||
}
|
||||
|
||||
private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(Set<LoanCharge> charges) {
|
||||
private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(final Set<LoanCharge> charges) {
|
||||
LoanCharge earliestUnpaidCharge = null;
|
||||
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
@@ -175,6 +175,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
if (currentInstallment.isNotFullyCompleted()) {
|
||||
principalPortion = principalPortion.plus(currentInstallment.writeOffOutstandingPrincipal(currency));
|
||||
interestPortion = interestPortion.plus(currentInstallment.writeOffOutstandingInterest(currency));
|
||||
feeChargesPortion = feeChargesPortion.plus(currentInstallment.writeOffOutstandingFeeCharges(currency));
|
||||
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.writeOffOutstandingPenaltyCharges(currency));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+55
-58
@@ -387,8 +387,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
|
||||
// TODO - only need to reprocess transactions against loan schedule
|
||||
// if loan charge is added with due date before latest transaction.
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||
@@ -1351,7 +1349,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
loanTransactionDate, this.getDisbursementDate());
|
||||
}
|
||||
|
||||
if (loanTransactionDate.isAfter(new LocalDate())) {
|
||||
if (loanTransactionDate.isAfter(DateUtils.getLocalDateOfTenant())) {
|
||||
final String errorMessage = "The transaction date cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage, loanTransactionDate);
|
||||
}
|
||||
@@ -1359,12 +1357,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
if (loanTransaction.isInterestWaiver()) {
|
||||
Money totalInterestOutstandingOnLoan = getTotalInterestOutstandingOnLoan();
|
||||
if (adjustedTransaction != null) {
|
||||
totalInterestOutstandingOnLoan = totalInterestOutstandingOnLoan.plus(adjustedTransaction.getAmount());
|
||||
totalInterestOutstandingOnLoan = totalInterestOutstandingOnLoan.plus(adjustedTransaction.getAmount(loanCurrency()));
|
||||
}
|
||||
if (loanTransaction.getAmount(loanCurrency()).isGreaterThan(totalInterestOutstandingOnLoan)) {
|
||||
final String errorMessage = "The amount of interest to waive cannot be greater than total interest outstanding on loan.";
|
||||
throw new InvalidLoanStateTransitionException("waive.interest", "amount.exceeds.total.outstanding.interest", errorMessage,
|
||||
loanTransaction.getAmount(), totalInterestOutstandingOnLoan.getAmount());
|
||||
loanTransaction.getAmount(loanCurrency()), totalInterestOutstandingOnLoan.getAmount());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1616,53 +1614,61 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
|
||||
public LoanTransaction closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final Map<String, Object> changes) {
|
||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
|
||||
LoanTransaction loanTransaction = null;
|
||||
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
changes.put("status", LoanEnumerations.status(this.loanStatus));
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
|
||||
this.closedOnDate = writtenOffOnLocalDate.toDate();
|
||||
this.writtenOffOnDate = writtenOffOnLocalDate.toDate();
|
||||
changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("writtenOffOnDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
|
||||
if (writtenOffOnLocalDate.isBefore(this.getDisbursementDate())) {
|
||||
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(DateUtils.getLocalDateOfTenant())) {
|
||||
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);
|
||||
}
|
||||
|
||||
loanTransaction = LoanTransaction.writeoff(this, writtenOffOnLocalDate);
|
||||
final 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);
|
||||
}
|
||||
|
||||
final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
|
||||
this.closedOnDate = writtenOffOnLocalDate.toDate();
|
||||
this.writtenOffOnDate = writtenOffOnLocalDate.toDate();
|
||||
changes.put("closedOnDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("writtenOffOnDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
|
||||
if (writtenOffOnLocalDate.isBefore(this.getDisbursementDate())) {
|
||||
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 LoanTransaction close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final Map<String, Object> changes) {
|
||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
final LocalDate closureDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
|
||||
@@ -1676,7 +1682,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
getDisbursementDate());
|
||||
}
|
||||
|
||||
if (closureDate.isAfter(new LocalDate())) {
|
||||
if (closureDate.isAfter(DateUtils.getLocalDateOfTenant())) {
|
||||
final String errorMessage = "The date on which a loan is closed cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("close", "cannot.be.a.future.date", errorMessage, closureDate);
|
||||
}
|
||||
@@ -1712,10 +1718,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
if (isOverPaid()) {
|
||||
final Money totalLoanOverpayment = calculateTotalOverpayment();
|
||||
// FIXME - kw - use overpaymentTolerance setting when in place on
|
||||
// loan product settings.
|
||||
if (totalLoanOverpayment.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(totalLoanOverpayment)) {
|
||||
// TODO - technically should set somewhere that this loan has
|
||||
// TODO - KW - technically should set somewhere that this loan
|
||||
// has
|
||||
// 'overpaid' amount
|
||||
updateLoanForClosure(closureDate, loanLifecycleStateMachine, changes);
|
||||
} else if (totalLoanOverpayment.isGreaterThanZero()) {
|
||||
@@ -1729,11 +1734,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
private Money getTotalOutstanding() {
|
||||
Money totalOutstanding = Money.zero(loanCurrency());
|
||||
// 1. check totalOutstanding value on loan
|
||||
|
||||
// 2. if null calculate from loan schedule
|
||||
final LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
|
||||
totalOutstanding = wrapper.calculateTotalOutstanding(loanCurrency(), this.repaymentScheduleInstallments);
|
||||
|
||||
return totalOutstanding;
|
||||
}
|
||||
|
||||
@@ -1751,8 +1755,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
/**
|
||||
* Behaviour added to comply with capability of previous mifos product to
|
||||
* support easier transition to mifosx platform.
|
||||
*
|
||||
* @param changes
|
||||
*/
|
||||
public void closeAsMarkedForReschedule(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final Map<String, Object> changes) {
|
||||
@@ -2013,11 +2015,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
|
||||
private Money getTotalPaidInRepayments() {
|
||||
Money cumulativePaid = Money.zero(this.loanRepaymentScheduleDetail.getPrincipal().getCurrency());
|
||||
Money cumulativePaid = Money.zero(loanCurrency());
|
||||
|
||||
for (LoanTransaction repayment : this.loanTransactions) {
|
||||
if (repayment.isRepayment()) {
|
||||
cumulativePaid = cumulativePaid.plus(repayment.getAmount());
|
||||
cumulativePaid = cumulativePaid.plus(repayment.getAmount(loanCurrency()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2209,20 +2211,15 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
return officeId;
|
||||
}
|
||||
|
||||
public boolean isAccountingEnabledOnLoanProduct() {
|
||||
return this.loanProduct.isAccountingEnabled();
|
||||
}
|
||||
|
||||
public Boolean isCashBasedAccountingEnabledOnLoanProduct() {
|
||||
private Boolean isCashBasedAccountingEnabledOnLoanProduct() {
|
||||
return this.loanProduct.isCashBasedAccountingEnabled();
|
||||
}
|
||||
|
||||
public Boolean isAccrualBasedAccountingEnabledOnLoanProduct() {
|
||||
private Boolean isAccrualBasedAccountingEnabledOnLoanProduct() {
|
||||
return this.loanProduct.isAccrualBasedAccountingEnabled();
|
||||
}
|
||||
|
||||
// TODO - make private if possible
|
||||
public Long productId() {
|
||||
private Long productId() {
|
||||
return this.loanProduct.getId();
|
||||
}
|
||||
|
||||
|
||||
+16
@@ -433,6 +433,22 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
return interestDue;
|
||||
}
|
||||
|
||||
public Money writeOffOutstandingFeeCharges(final MonetaryCurrency currency) {
|
||||
final Money feeChargesOutstanding = getFeeChargesOutstanding(currency);
|
||||
this.feeChargesWrittenOff = defaultToNullIfZero(feeChargesOutstanding.getAmount());
|
||||
this.completed = getTotalOutstanding(currency).isZero();
|
||||
|
||||
return feeChargesOutstanding;
|
||||
}
|
||||
|
||||
public Money writeOffOutstandingPenaltyCharges(final MonetaryCurrency currency) {
|
||||
final Money penaltyChargesOutstanding = getPenaltyChargesOutstanding(currency);
|
||||
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesOutstanding.getAmount());
|
||||
this.completed = getTotalOutstanding(currency).isZero();
|
||||
|
||||
return penaltyChargesOutstanding;
|
||||
}
|
||||
|
||||
public boolean isOverdueOn(final LocalDate transactionDate) {
|
||||
return this.getDueDate().isBefore(transactionDate);
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ public class LoanScheduleWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME - this is not a side effect free approach so move this into a
|
||||
* FIXME - KW - this is not a side effect free approach so move this into a
|
||||
* specific wrapper than makes this obvious from the read-only wrapper.
|
||||
*/
|
||||
public void reprocess(final MonetaryCurrency currency, final LocalDate disbursementDate,
|
||||
|
||||
+4
-20
@@ -50,16 +50,16 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "principal_portion_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principalPortion = BigDecimal.ZERO;
|
||||
private BigDecimal principalPortion;
|
||||
|
||||
@Column(name = "interest_portion_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestPortion = BigDecimal.ZERO;
|
||||
private BigDecimal interestPortion;
|
||||
|
||||
@Column(name = "fee_charges_portion_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesPortion = BigDecimal.ZERO;
|
||||
private BigDecimal feeChargesPortion;
|
||||
|
||||
@Column(name = "penalty_charges_portion_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyChargesPortion = BigDecimal.ZERO;
|
||||
private BigDecimal penaltyChargesPortion;
|
||||
|
||||
@Column(name = "is_reversed", nullable = false)
|
||||
private boolean reversed;
|
||||
@@ -160,30 +160,14 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
return Money.of(currency, this.interestPortion);
|
||||
}
|
||||
|
||||
public BigDecimal getInterestPortion() {
|
||||
return this.interestPortion;
|
||||
}
|
||||
|
||||
public Money getFeeChargesPortion(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.feeChargesPortion);
|
||||
}
|
||||
|
||||
public BigDecimal getFeePortion() {
|
||||
return this.feeChargesPortion;
|
||||
}
|
||||
|
||||
public Money getPenaltyChargesPortion(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.penaltyChargesPortion);
|
||||
}
|
||||
|
||||
public BigDecimal getPenaltyChargesPortion() {
|
||||
return this.penaltyChargesPortion;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public Money getAmount(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.amount);
|
||||
}
|
||||
|
||||
+3
-3
@@ -89,7 +89,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
baseDataValidator.reset().parameter(repaymentEveryFrequencyTypeParameterName).value(repaymentEveryType).notNull()
|
||||
.inMinMaxRange(0, 3);
|
||||
|
||||
// FIXME - this constraint doesnt really need to be here. should be
|
||||
// FIXME - KW - this constraint doesnt really need to be here. should be
|
||||
// possible to express loan term as say 12 months whilst also saying
|
||||
// - that the repayment structure is 6 repayments every bi-monthly.
|
||||
validateSelectedPeriodFrequencyTypeIsTheSame(dataValidationErrors, loanTermFrequency, loanTermFrequencyType, numberOfRepayments,
|
||||
@@ -207,7 +207,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
}
|
||||
|
||||
private void validateRepaymentsStartingFromDateAndInterestChargedFromDate(final List<ApiParameterError> dataValidationErrors,
|
||||
final LocalDate expectedDisbursementDate, LocalDate repaymentsStartingFromDate, LocalDate interestChargedFromDate) {
|
||||
final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, final LocalDate interestChargedFromDate) {
|
||||
if (repaymentsStartingFromDate != null && interestChargedFromDate == null) {
|
||||
|
||||
ApiParameterError error = ApiParameterError.parameterError(
|
||||
@@ -228,7 +228,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
}
|
||||
|
||||
private void validateRepaymentsStartingFromDateIsAfterDisbursementDate(final List<ApiParameterError> dataValidationErrors,
|
||||
final LocalDate expectedDisbursementDate, LocalDate repaymentsStartingFromDate) {
|
||||
final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate) {
|
||||
if (expectedDisbursementDate != null) {
|
||||
if (repaymentsStartingFromDate != null && expectedDisbursementDate.isAfter(repaymentsStartingFromDate)) {
|
||||
ApiParameterError error = ApiParameterError.parameterError(
|
||||
|
||||
+51
-3
@@ -6,6 +6,7 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.serialization;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@@ -36,6 +37,11 @@ public final class LoanEventApiJsonValidator {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
|
||||
public void validateDisbursement(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
@@ -59,8 +65,50 @@ public final class LoanEventApiJsonValidator {
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
public void validateTransaction(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "note",
|
||||
"locale", "dateFormat"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||
|
||||
final BigDecimal transactionAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
|
||||
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).notNull().positiveAmount();
|
||||
|
||||
final String note = fromApiJsonHelper.extractStringNamed("note", element);
|
||||
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateTransactionWithNoAmount(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("transactionDate", "note", "locale", "dateFormat"));
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||
|
||||
final String note = fromApiJsonHelper.extractStringNamed("note", element);
|
||||
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.serialization;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Implementation of {@link FromApiJsonDeserializer} for
|
||||
* {@link LoanTransactionCommand}'s.
|
||||
*/
|
||||
@Component
|
||||
public final class LoanTransactionCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<LoanTransactionCommand> {
|
||||
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "note", "dateFormat",
|
||||
"locale" ,"status", "closedOnDate", "writtenOffOnDate", "rescheduledOnDate"));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public LoanTransactionCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionCommand commandFromApiJson(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
|
||||
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||
final BigDecimal transactionAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
|
||||
final String note = fromApiJsonHelper.extractStringNamed("note", element);
|
||||
|
||||
return new LoanTransactionCommand(transactionDate, transactionAmount, note);
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -248,7 +248,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
// TODO - OPTIMIZE - write simple sql query to fetch back date of
|
||||
// TODO - KW - OPTIMIZE - write simple sql query to fetch back date of
|
||||
// possible next transaction date.
|
||||
Loan loan = this.loanRepository.findOne(loanId);
|
||||
if (loan == null) { throw new LoanNotFoundException(loanId); }
|
||||
@@ -273,7 +273,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
// TODO - OPTIMIZE - write simple sql query to fetch back overdue
|
||||
// TODO - KW -OPTIMIZE - write simple sql query to fetch back overdue
|
||||
// interest that can be waived along with the date of repayment period
|
||||
// interest is overdue.
|
||||
final Loan loan = this.loanRepository.findOne(loanId);
|
||||
@@ -290,8 +290,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WAIVE_INTEREST);
|
||||
|
||||
return new LoanTransactionData(null, transactionType, currencyData, waiveOfInterest.getTransactionDate(),
|
||||
waiveOfInterest.getAmount(), null, null, null, null);
|
||||
final MonetaryCurrency monetaryCurrency = new MonetaryCurrency(currency.getCode(), currency.getDecimalPlaces());
|
||||
final BigDecimal amount = waiveOfInterest.getAmount(monetaryCurrency).getAmount();
|
||||
return new LoanTransactionData(null, transactionType, currencyData, waiveOfInterest.getTransactionDate(), amount, null, null, null,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -299,8 +301,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF);
|
||||
return new LoanTransactionData(null, transactionType, null, new LocalDate(), null, null, null, null, null);
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF);
|
||||
return new LoanTransactionData(null, transactionType, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -429,7 +431,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final String closedByUsername = rs.getString("closedByUsername");
|
||||
final String closedByFirstname = rs.getString("closedByFirstname");
|
||||
final String closedByLastname = rs.getString("closedByLastname");
|
||||
|
||||
|
||||
final LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate");
|
||||
|
||||
final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
|
||||
|
||||
+38
-79
@@ -14,14 +14,10 @@ import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.accounting.glaccount.data.LoanDTO;
|
||||
import org.mifosplatform.accounting.glaccount.data.LoanTransactionDTO;
|
||||
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepository;
|
||||
@@ -34,7 +30,6 @@ import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||
import org.mifosplatform.portfolio.client.domain.Note;
|
||||
import org.mifosplatform.portfolio.client.domain.NoteRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanTransactionCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
@@ -51,7 +46,6 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignment
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanChargeCommandFromApiJsonDeserializer;
|
||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
|
||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanTransactionCommandFromApiJsonDeserializer;
|
||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
@@ -64,7 +58,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final LoanEventApiJsonValidator loanEventApiJsonValidator;
|
||||
private final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer;
|
||||
private final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer;
|
||||
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
|
||||
private final LoanRepository loanRepository;
|
||||
@@ -79,7 +72,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Autowired
|
||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
final LoanEventApiJsonValidator loanEventApiJsonValidator,
|
||||
final LoanTransactionCommandFromApiJsonDeserializer loanTransactionCommandFromApiJsonDeserializer,
|
||||
final LoanChargeCommandFromApiJsonDeserializer loanChargeCommandFromApiJsonDeserializer,
|
||||
final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler,
|
||||
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
|
||||
@@ -88,7 +80,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
|
||||
this.context = context;
|
||||
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
|
||||
this.loanTransactionCommandFromApiJsonDeserializer = loanTransactionCommandFromApiJsonDeserializer;
|
||||
this.loanChargeCommandFromApiJsonDeserializer = loanChargeCommandFromApiJsonDeserializer;
|
||||
this.loanAssembler = loanAssembler;
|
||||
this.loanRepository = loanRepository;
|
||||
@@ -101,10 +92,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer;
|
||||
}
|
||||
|
||||
private boolean isBeforeToday(final LocalDate date) {
|
||||
return date.isBefore(DateUtils.getLocalDateOfTenant());
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
|
||||
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
|
||||
@@ -193,11 +180,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Override
|
||||
public CommandProcessingResult makeLoanRepayment(final Long loanId, final JsonCommand command) {
|
||||
|
||||
final AppUser currentUser = context.authenticatedUser();
|
||||
context.authenticatedUser();
|
||||
|
||||
final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command
|
||||
.json());
|
||||
loanTransactionCommand.validate();
|
||||
this.loanEventApiJsonValidator.validateTransaction(command.json());
|
||||
|
||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||
@@ -205,12 +190,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
final Loan loan = retrieveLoanBy(loanId);
|
||||
|
||||
if (this.isBeforeToday(transactionDate) && currentUser.canNotMakeRepaymentOnLoanInPast()) { throw new NoAuthorizationException(
|
||||
"error.msg.no.permission.to.make.repayment.on.loan.in.past"); }
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
|
||||
@@ -226,9 +210,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
@@ -251,9 +232,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command
|
||||
.json());
|
||||
loanTransactionCommand.validate();
|
||||
this.loanEventApiJsonValidator.validateTransaction(command.json());
|
||||
|
||||
final Loan loan = retrieveLoanBy(loanId);
|
||||
|
||||
@@ -266,6 +245,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
@@ -286,9 +267,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
@@ -311,13 +289,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command
|
||||
.json());
|
||||
loanTransactionCommand.validate();
|
||||
this.loanEventApiJsonValidator.validateTransaction(command.json());
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount"));
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
final Loan loan = retrieveLoanBy(loanId);
|
||||
|
||||
@@ -333,9 +311,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(waiveTransaction.getId()) //
|
||||
@@ -352,16 +327,19 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
public CommandProcessingResult writeOff(final Long loanId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command
|
||||
.json());
|
||||
loanTransactionCommand.validateNonMonetaryTransaction();
|
||||
this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json());
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
final Loan loan = retrieveLoanBy(loanId);
|
||||
|
||||
final LoanTransaction writeoff = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine(), changes);
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
final LoanTransaction writeoff = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine(), changes,
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.loanTransactionRepository.save(writeoff);
|
||||
this.loanRepository.save(loan);
|
||||
@@ -373,14 +351,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
// make a call to accounting
|
||||
if (loan.isAccountingEnabledOnLoanProduct()) {
|
||||
LoanDTO loanDTO = populateLoanData(loan, writeoff);
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(loanDTO);
|
||||
}
|
||||
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
@@ -399,16 +373,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command
|
||||
.json());
|
||||
loanTransactionCommand.validateNonMonetaryTransaction();
|
||||
this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json());
|
||||
|
||||
final Loan loan = retrieveLoanBy(loanId);
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
final LoanTransaction possibleClosingTransaction = loan.close(command, defaultLoanLifecycleStateMachine(), changes);
|
||||
final List<Long> existingTransactionIds = new ArrayList<Long>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
|
||||
|
||||
final LoanTransaction possibleClosingTransaction = loan.close(command, defaultLoanLifecycleStateMachine(), changes,
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
if (possibleClosingTransaction != null) {
|
||||
this.loanTransactionRepository.save(possibleClosingTransaction);
|
||||
}
|
||||
@@ -421,8 +399,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
if (possibleClosingTransaction != null) {
|
||||
final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode());
|
||||
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (possibleClosingTransaction != null) {
|
||||
@@ -456,14 +438,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
public CommandProcessingResult closeAsRescheduled(final Long loanId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
|
||||
final LoanTransactionCommand loanTransactionCommand = this.loanTransactionCommandFromApiJsonDeserializer.commandFromApiJson(command
|
||||
.json());
|
||||
loanTransactionCommand.validateNonMonetaryTransaction();
|
||||
this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json());
|
||||
|
||||
final Loan loan = retrieveLoanBy(loanId);
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
changes.put("transactionDate", command.stringValueOfParameterNamed("transactionDate"));
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
loan.closeAsMarkedForReschedule(command, defaultLoanLifecycleStateMachine(), changes);
|
||||
|
||||
@@ -476,9 +458,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(loanId) //
|
||||
@@ -719,24 +698,4 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.withLoanId(loanId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
private LoanDTO populateLoanDTO(final Loan loan, final List<LoanTransaction> loanTransactions) {
|
||||
List<LoanTransactionDTO> loanTransactionDTOs = new ArrayList<LoanTransactionDTO>();
|
||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||
LoanTransactionDTO loanTransactionDTO = new LoanTransactionDTO(loanTransaction.getId().toString(), loanTransaction.getDateOf(),
|
||||
loanTransaction.getAmount(), loanTransaction.getPrincipalPortion(), loanTransaction.getInterestPortion(),
|
||||
loanTransaction.getFeePortion(), loanTransaction.getPenaltyChargesPortion(), loanTransaction.isDisbursement(),
|
||||
loanTransaction.isRepayment(), loanTransaction.isRepaymentAtDisbursement(), loanTransaction.isReversed(),
|
||||
loanTransaction.isWriteOff());
|
||||
loanTransactionDTOs.add(loanTransactionDTO);
|
||||
}
|
||||
return new LoanDTO(loan.getId(), loan.productId(), loan.getOfficeId(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
|
||||
loan.isAccrualBasedAccountingEnabledOnLoanProduct(), loanTransactionDTOs);
|
||||
}
|
||||
|
||||
private LoanDTO populateLoanData(final Loan loan, final LoanTransaction loanTransaction) {
|
||||
List<LoanTransaction> loanTransactions = new ArrayList<LoanTransaction>();
|
||||
loanTransactions.add(loanTransaction);
|
||||
return populateLoanDTO(loan, loanTransactions);
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -625,11 +625,10 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
|
||||
public void postInterest() {
|
||||
|
||||
LocalDate lastInterestPostedDate = getLastInterestPostedDate();
|
||||
@SuppressWarnings("unused")
|
||||
LocalDate nextInterestPostingDate = getNextInterestPostedDate();
|
||||
@SuppressWarnings("unused")
|
||||
SavingInterestCalculationMethod savingInterestCalculationMethod = SavingInterestCalculationMethod
|
||||
.fromInt(this.interestCalculationMethod);
|
||||
// LocalDate nextInterestPostingDate = getNextInterestPostedDate();
|
||||
// SavingInterestCalculationMethod savingInterestCalculationMethod =
|
||||
// SavingInterestCalculationMethod
|
||||
// .fromInt(this.interestCalculationMethod);
|
||||
|
||||
Integer monthsForInterestCalculation = Months.monthsBetween(lastInterestPostedDate, new LocalDate()).getMonths();
|
||||
Integer postInterestItereations = monthsForInterestCalculation / this.interestPostEvery;
|
||||
@@ -644,7 +643,7 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
while (postInterestItereations > 0) {
|
||||
lastInterestPostedDate = getLastInterestPostedDate();
|
||||
nextInterestPostingDate = getNextInterestPostedDate();
|
||||
// nextInterestPostingDate = getNextInterestPostedDate();
|
||||
LocalDate transactionStartDate = lastInterestPostedDate;
|
||||
|
||||
BigDecimal termTotalAmount = BigDecimal.ZERO;
|
||||
@@ -687,7 +686,7 @@ public class SavingAccount extends AbstractAuditableCustom<AppUser, Long> {
|
||||
isOutstandingAmountCalculated = true;
|
||||
|
||||
}
|
||||
// TODO- calculate interest on calculation method
|
||||
// TODO - HUGO - calculate interest on calculation method
|
||||
|
||||
// if (savingInterestCalculationMethod.isAverageBalance()) {
|
||||
averageBalanceForTerm = BigDecimal.valueOf(termTotalAmount.doubleValue() / this.interestPostEvery);
|
||||
|
||||
+2
-2
@@ -45,12 +45,12 @@ public class CalculateSavingScheduleImpl implements CalculateSavingSchedule {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingScheduleData calculateSavingSchedule(CalculateSavingScheduleCommand command) {
|
||||
public SavingScheduleData calculateSavingSchedule(final CalculateSavingScheduleCommand command) {
|
||||
|
||||
context.authenticatedUser();
|
||||
SavingScheduleData savingScheduleData = null;
|
||||
|
||||
// FIXME Validations should write here
|
||||
// FIXME - HUGO - Validations should write here
|
||||
final SavingProduct savingProduct = this.savingProductRepository.findOne(command.getProductId());
|
||||
if (savingProduct == null) throw new SavingProductNotFoundException(command.getProductId());
|
||||
|
||||
|
||||
+4
-5
@@ -39,7 +39,7 @@ public class SavingProductEnumerations {
|
||||
return savingsLockinPeriod(SavingsLockinPeriodEnum.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData savingsLockinPeriod(SavingsLockinPeriodEnum type) {
|
||||
public static EnumOptionData savingsLockinPeriod(final SavingsLockinPeriodEnum type) {
|
||||
final String codePrefix = "savings.product.";
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
@@ -67,7 +67,7 @@ public class SavingProductEnumerations {
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData savingInterestCalculationMethod(SavingInterestCalculationMethod type) {
|
||||
public static EnumOptionData savingInterestCalculationMethod(final SavingInterestCalculationMethod type) {
|
||||
final String codePrefix = "savings.interestCalculation.method.";
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
@@ -95,7 +95,7 @@ public class SavingProductEnumerations {
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData savingInterestType(SavingsInterestType type) {
|
||||
public static EnumOptionData savingInterestType(final SavingsInterestType type) {
|
||||
final String codePrefix = "savings.interest.type.";
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
@@ -115,7 +115,7 @@ public class SavingProductEnumerations {
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData interestFrequencyType(SavingFrequencyType type) {
|
||||
public static EnumOptionData interestFrequencyType(final SavingFrequencyType type) {
|
||||
final String codePrefix = "saving.product.frequency.period.";
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
@@ -171,7 +171,6 @@ public class SavingProductEnumerations {
|
||||
final String codePrefix = "savings.product.";
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
// FIXME - Madhukar - spelling mistake on 'Recurring'
|
||||
case RECURRING:
|
||||
optionData = new EnumOptionData(SavingProductType.RECURRING.getValue().longValue(), codePrefix
|
||||
+ SavingProductType.RECURRING.getCode(), "Recurring");
|
||||
|
||||
+8
-8
@@ -16,11 +16,11 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class FixedTermDepositInterestCalculator {
|
||||
|
||||
// FIXME - MADHUKAR - Are the unused field here needed?
|
||||
// FIXME - HUGO - Are the unused field here needed?
|
||||
public Money calculateInterestOnMaturityFor(final Money deposit, final Integer tenureInMonths, final BigDecimal maturityInterestRate,
|
||||
final Integer interestCompoundedEvery, final PeriodFrequencyType interestCompoundedFrequencyPeriodType) {
|
||||
|
||||
// TODO - Can change 'interest compounding' fields into something
|
||||
// TODO - HUGO - Can change 'interest compounding' fields into something
|
||||
// simpler like interestCompoundedFrequency {None (Simple Interest),
|
||||
// Monthly, Quarterly, Half-Yearly, Annually}
|
||||
// - the 'interestCompoundedEvery' would then be inferred form the
|
||||
@@ -28,7 +28,7 @@ public class FixedTermDepositInterestCalculator {
|
||||
// - the special case of 'simple interest' or 'no compounding' would use
|
||||
// simple formula rather than 'future value' function.
|
||||
|
||||
// FIXME - assuming only monthly compounding period for now
|
||||
// FIXME - HUGO - assuming only monthly compounding period for now
|
||||
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
|
||||
Integer monthsInYear = 12;
|
||||
|
||||
@@ -59,8 +59,8 @@ public class FixedTermDepositInterestCalculator {
|
||||
* payments are due at the beginning of period (false => payments are due at
|
||||
* end of period)
|
||||
*/
|
||||
private static double fv(double ratePeriodCompoundingPeriod, double numberOfCompoundingPeriods, double pmtPerPeriod,
|
||||
double presentValue, boolean type) {
|
||||
private static double fv(final double ratePeriodCompoundingPeriod, final double numberOfCompoundingPeriods, final double pmtPerPeriod,
|
||||
final double presentValue, final boolean type) {
|
||||
double retval = 0;
|
||||
if (ratePeriodCompoundingPeriod == 0) {
|
||||
retval = -1 * (presentValue + (numberOfCompoundingPeriods * pmtPerPeriod));
|
||||
@@ -72,8 +72,8 @@ public class FixedTermDepositInterestCalculator {
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Money calculateInterestOnMaturityForSimpleInterest(Money deposit, Integer tenureInMonths, BigDecimal maturityInterestRate,
|
||||
Integer interestCompoundedEvery, PeriodFrequencyType interestCompoundedFrequencyPeriodType) {
|
||||
public Money calculateInterestOnMaturityForSimpleInterest(final Money deposit, final Integer tenureInMonths, final BigDecimal maturityInterestRate,
|
||||
final Integer interestCompoundedEvery, final PeriodFrequencyType interestCompoundedFrequencyPeriodType) {
|
||||
|
||||
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
|
||||
Integer monthsInYear = 12;
|
||||
@@ -91,7 +91,7 @@ public class FixedTermDepositInterestCalculator {
|
||||
return Money.of(deposit.getCurrency(), totalAmount);
|
||||
}
|
||||
|
||||
public Money calculateRemainInterest(Money deposit, Integer days, BigDecimal interestRate) {
|
||||
public Money calculateRemainInterest(final Money deposit, final Integer days, final BigDecimal interestRate) {
|
||||
|
||||
MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
|
||||
Integer daysInYear = 365;
|
||||
|
||||
Reference in New Issue
Block a user