tidy of old error handling classes and read and write monolithic platform services.

This commit is contained in:
Keith Woodlock
2012-05-17 02:45:26 +01:00
parent 7bb8b9359e
commit 6693b45003
25 changed files with 420 additions and 849 deletions
@@ -1,64 +0,0 @@
package org.mifosng.data;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Being removed in favour of {@link ApiGlobalErrorResponse} and {@link ApiParameterError}
*/
@Deprecated
@XmlRootElement(name="errorResponse")
public class ErrorResponse {
private String code;
private String field;
private String value;
private Object[] args;
public ErrorResponse() {
//
}
public ErrorResponse(final String code, final String field, final String value) {
this.code = code;
this.field = field;
this.value = value;
}
public ErrorResponse(final String code, final String field, final Object... args) {
this.code = code;
this.field = field;
this.args = args;
}
public String getCode() {
return this.code;
}
public void setCode(final String code) {
this.code = code;
}
public String getField() {
return this.field;
}
public void setField(final String field) {
this.field = field;
}
public String getValue() {
return this.value;
}
public void setValue(final String value) {
this.value = value;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
}
@@ -1,32 +0,0 @@
package org.mifosng.data;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class ErrorResponseList {
private List<ErrorResponse> errors = new ArrayList<ErrorResponse>();
public ErrorResponseList() {
//
}
public ErrorResponseList(final List<ErrorResponse> errors) {
this.errors = errors;
}
@XmlElementWrapper(name="errors")
@XmlElement(name="errorResponse")
public List<ErrorResponse> getErrors() {
return errors;
}
public void setErrors(List<ErrorResponse> errors) {
this.errors = errors;
}
}
@@ -1,52 +0,0 @@
package org.mifosng.platform;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.platform.exceptions.NewDataValidationException;
public class AdjustLoanTransactionCommandValidator {
private final AdjustLoanTransactionCommand command;
public AdjustLoanTransactionCommandValidator(AdjustLoanTransactionCommand command) {
this.command = command;
}
public void validate() {
List<ErrorResponse> dataValidationErrors = new ArrayList<ErrorResponse>();
if (command.getLoanId() == null || command.getLoanId().longValue() <= 0) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.id.is.invalid", "loanId");
dataValidationErrors.add(error);
}
if (command.getRepaymentId() == null || command.getRepaymentId().longValue() <= 0) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.transaction.id.is.invalid", "repaymentId");
dataValidationErrors.add(error);
}
if (command.getTransactionDate() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.repayment.date.cannot.be.blank", "paymentDate");
dataValidationErrors.add(error);
}
if (command.getTransactionAmount() == null || command.getTransactionAmount().doubleValue() < 0) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.adjustment.must.be.zero.or.greater", "paymentAmount");
dataValidationErrors.add(error);
}
if (StringUtils.isNotBlank(command.getComment()) && command.getComment().length() > 1000) {
ErrorResponse error = new ErrorResponse("validation.msg.note.exceeds.max.length", "comment");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new NewDataValidationException(dataValidationErrors, "Data validation errors exist.");
}
}
}
@@ -1,9 +0,0 @@
package org.mifosng.platform;
public class CannotDeleteLoanException extends RuntimeException {
public CannotDeleteLoanException(final String message) {
super(message);
}
}
@@ -40,7 +40,6 @@ import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanBuilder;
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.LoanProduct;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
@@ -187,11 +186,9 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule
.getScheduledLoanInstallments();
Loan loan = new LoanBuilder().with(currentUser.getOrganisation())
.with(loanProduct).with(client)
.withExternalSystemId(command.getExternalId())
.with(loanRepaymentScheduleDetail).build();
Loan loan = Loan.createNew(currentUser.getOrganisation(), loanProduct, client, loanRepaymentScheduleDetail);
loan.setExternalId(command.getExternalId());
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
MoneyData readPrincipalDue = scheduledLoanInstallment
@@ -1,42 +0,0 @@
package org.mifosng.platform;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.platform.exceptions.NewDataValidationException;
public class LoanStateTransitionCommandValidator {
private final LoanStateTransitionCommand command;
public LoanStateTransitionCommandValidator(LoanStateTransitionCommand command) {
this.command = command;
}
public void validate() {
List<ErrorResponse> dataValidationErrors = new ArrayList<ErrorResponse>();
if (command.getLoanId() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.id.is.invalid", "loanId");
dataValidationErrors.add(error);
}
if (command.getEventDate() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.state.transition.date.cannot.be.blank", "eventDate");
dataValidationErrors.add(error);
}
if (StringUtils.isNotBlank(command.getComment()) && command.getComment().length() > 1000) {
ErrorResponse error = new ErrorResponse("validation.msg.note.exceeds.max.length", "comment");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new NewDataValidationException(dataValidationErrors, "Data validation errors exist.");
}
}
}
@@ -1,47 +0,0 @@
package org.mifosng.platform;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.platform.exceptions.NewDataValidationException;
public class LoanTransactionValidator {
private final LoanTransactionCommand command;
public LoanTransactionValidator(LoanTransactionCommand command) {
this.command = command;
}
public void validate() {
List<ErrorResponse> dataValidationErrors = new ArrayList<ErrorResponse>();
if (command.getLoanId() == null || command.getLoanId().doubleValue() <= 0) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.id.is.invalid", "loanId");
dataValidationErrors.add(error);
}
if (command.getTransactionDate() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.repayment.date.cannot.be.blank", "transactionDate");
dataValidationErrors.add(error);
}
if (command.getTransactionAmount() == null || command.getTransactionAmount().doubleValue() <= 0) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.repayment.must.be.greater.than.zero", "transactionAmount");
dataValidationErrors.add(error);
}
if (StringUtils.isNotBlank(command.getComment()) && command.getComment().length() > 1000) {
ErrorResponse error = new ErrorResponse("validation.msg.note.exceeds.max.length", "transactionComment");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new NewDataValidationException(dataValidationErrors, "Data validation errors exist.");
}
}
}
@@ -1,17 +0,0 @@
package org.mifosng.platform;
import java.util.Collection;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.OrganisationReadModel;
public interface ReadPlatformService {
Collection<OrganisationReadModel> retrieveAll();
LoanRepaymentData retrieveNewLoanRepaymentDetails(Long loanId);
LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId);
LoanRepaymentData retrieveLoanRepaymentDetails(Long loanId, Long repaymentId);
}
@@ -1,195 +0,0 @@
package org.mifosng.platform;
import static org.mifosng.platform.Specifications.loanTransactionsThatMatch;
import static org.mifosng.platform.Specifications.loansThatMatch;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import javax.sql.DataSource;
import org.joda.time.LocalDate;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.MoneyData;
import org.mifosng.data.OrganisationReadModel;
import org.mifosng.platform.client.service.ClientReadPlatformService;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
import org.mifosng.platform.currency.domain.ApplicationCurrencyRepository;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.LoanTransactionRepository;
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
@Service
public class ReadPlatformServiceImpl implements ReadPlatformService {
private final SimpleJdbcTemplate jdbcTemplate;
private final LoanRepository loanRepository;
private final LoanTransactionRepository loanTransactionRepository;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final LoanProductReadPlatformService loanProductReadPlatformService;
private final ClientReadPlatformService clientReadPlatformService;
@Autowired
public ReadPlatformServiceImpl(
final LoanProductReadPlatformService loanProductReadPlatformService,
final ClientReadPlatformService clientReadPlatformService,
final DataSource dataSource,
final LoanRepository loanRepository,
final LoanTransactionRepository loanTransactionRepository,
final ApplicationCurrencyRepository applicationCurrencyRepository) {
this.loanProductReadPlatformService = loanProductReadPlatformService;
this.clientReadPlatformService = clientReadPlatformService;
this.loanTransactionRepository = loanTransactionRepository;
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.loanRepository = loanRepository;
}
private AppUser extractAuthenticatedUser() {
AppUser currentUser = null;
SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
Authentication auth = context.getAuthentication();
if (auth != null) {
currentUser = (AppUser) auth.getPrincipal();
}
}
if (currentUser == null) {
throw new UnAuthenticatedUserException();
}
return currentUser;
}
@Override
public Collection<OrganisationReadModel> retrieveAll() {
String sql = "select o.id as id, o.name as name, o.contact_name as contactName, o.opening_date as openingDate from org_organisation o";
RowMapper<OrganisationReadModel> rm = new OrganisationMapper();
return this.jdbcTemplate.query(sql, rm);
}
protected static final class OrganisationMapper implements
RowMapper<OrganisationReadModel> {
@Override
public OrganisationReadModel mapRow(final ResultSet rs, final int rowNum)
throws SQLException {
Long id = rs.getLong("id");
String name = rs.getString("name");
String contactName = rs.getString("contactName");
LocalDate openingDate = new LocalDate(rs.getDate("openingDate"));
return new OrganisationReadModel(id, name, contactName, openingDate);
}
}
@Override
public LoanRepaymentData retrieveNewLoanRepaymentDetails(Long loanId) {
AppUser currentUser = extractAuthenticatedUser();
// TODO - OPTIMIZE - write simple sql query to fetch back date of
// possible next transaction date.
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), loanId));
ApplicationCurrency currency = this.applicationCurrencyRepository
.findOneByCode(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(),
currency.getName(), currency.getDecimalPlaces(),
currency.getDisplaySymbol(), currency.getNameCode());
LocalDate earliestUnpaidInstallmentDate = loan
.possibleNextRepaymentDate();
Money possibleNextRepaymentAmount = loan.possibleNextRepaymentAmount();
MoneyData possibleNextRepayment = MoneyData.of(currencyData,
possibleNextRepaymentAmount.getAmount());
LoanRepaymentData newRepaymentDetails = new LoanRepaymentData();
newRepaymentDetails.setDate(earliestUnpaidInstallmentDate);
newRepaymentDetails.setTotal(possibleNextRepayment);
return newRepaymentDetails;
}
@Override
public LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId) {
AppUser currentUser = extractAuthenticatedUser();
// TODO - OPTIMIZE - write simple sql query to fetch back date of
// possible next transaction date.
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), loanId));
ApplicationCurrency currency = this.applicationCurrencyRepository
.findOneByCode(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(),
currency.getName(), currency.getDecimalPlaces(),
currency.getDisplaySymbol(), currency.getNameCode());
Money totalOutstanding = loan.getTotalOutstanding();
MoneyData totalOutstandingData = MoneyData.of(currencyData,
totalOutstanding.getAmount());
LoanRepaymentData newWaiverDetails = new LoanRepaymentData();
newWaiverDetails.setDate(new LocalDate());
newWaiverDetails.setTotal(totalOutstandingData);
return newWaiverDetails;
}
@Override
public LoanRepaymentData retrieveLoanRepaymentDetails(Long loanId,
Long repaymentId) {
AppUser currentUser = extractAuthenticatedUser();
LoanTransaction transaction = this.loanTransactionRepository
.findOne(loanTransactionsThatMatch(
currentUser.getOrganisation(), repaymentId));
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), loanId));
ApplicationCurrency currency = this.applicationCurrencyRepository
.findOneByCode(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(),
currency.getName(), currency.getDecimalPlaces(),
currency.getDisplaySymbol(), currency.getNameCode());
MoneyData total = MoneyData.of(currencyData, transaction.getAmount());
LocalDate date = transaction.getTransactionDate();
LoanRepaymentData loanRepaymentData = new LoanRepaymentData();
loanRepaymentData.setId(repaymentId);
loanRepaymentData.setTotal(total);
loanRepaymentData.setDate(date);
return loanRepaymentData;
}
}
@@ -1,19 +1,8 @@
package org.mifosng.platform;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.UpdateUsernamePasswordCommand;
import org.springframework.security.access.prepost.PreAuthorize;
public interface WritePlatformService {
void updateUsernamePasswordOnFirstTimeLogin(UpdateUsernamePasswordCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_MAKE_LOAN_REPAYMENT_ROLE', 'CAN_MAKE_LOAN_REPAYMENT_IN_THE_PAST_ROLE')")
public EntityIdentifier makeLoanRepayment(LoanTransactionCommand command);
EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command);
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
}
@@ -1,36 +1,13 @@
package org.mifosng.platform;
import static org.mifosng.platform.Specifications.loansThatMatch;
import static org.mifosng.platform.Specifications.usersThatMatch;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.UpdateUsernamePasswordCommand;
import org.mifosng.platform.client.domain.Note;
import org.mifosng.platform.client.domain.NoteRepository;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.ApplicationDomainRuleException;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.mifosng.platform.infrastructure.BasicPasswordEncodablePlatformUser;
import org.mifosng.platform.infrastructure.PlatformPasswordEncoder;
import org.mifosng.platform.infrastructure.PlatformUser;
import org.mifosng.platform.infrastructure.UsernameAlreadyExistsException;
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.LoanStatus;
import org.mifosng.platform.loan.domain.LoanStatusRepository;
import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.LoanTransactionRepository;
import org.mifosng.platform.loan.service.CalculationPlatformService;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.domain.AppUserRepository;
import org.mifosng.platform.user.domain.PlatformUserRepository;
@@ -47,29 +24,14 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
private final PlatformUserRepository platformUserRepository;
private final PlatformPasswordEncoder platformPasswordEncoder;
private final LoanRepository loanRepository;
private final LoanStatusRepository loanStatusRepository;
private final CalculationPlatformService calculationPlatformService;
private final NoteRepository noteRepository;
private final LoanTransactionRepository loanTransactionRepository;
@Autowired
public WritePlatformServiceJpaRepositoryImpl(
final PlatformUserRepository platformUserRepository,
final PlatformPasswordEncoder platformPasswordEncoder,
final NoteRepository noteRepository,
final LoanRepository loanRepository,
final LoanTransactionRepository loanTransactionRepository,
final LoanStatusRepository loanStatusRepository,
final CalculationPlatformService calculationPlatformService
final PlatformPasswordEncoder platformPasswordEncoder
) {
this.platformUserRepository = platformUserRepository;
this.platformPasswordEncoder = platformPasswordEncoder;
this.noteRepository = noteRepository;
this.loanRepository = loanRepository;
this.loanTransactionRepository = loanTransactionRepository;
this.loanStatusRepository = loanStatusRepository;
this.calculationPlatformService = calculationPlatformService;
}
@Transactional
@@ -116,136 +78,4 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
return currentUser;
}
private boolean isBeforeToday(final LocalDate date) {
return date.isBefore(new LocalDate());
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
List<LoanStatus> allowedLoanStatuses = this.loanStatusRepository.findAll();
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
private List<ErrorResponse> loanIdentifierDoesNotExistError(Long loanId) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.loan.with.identifier.exists", "id", loanId);
return Arrays.asList(errorResponse);
}
@Transactional
@Override
public EntityIdentifier makeLoanRepayment(final LoanTransactionCommand command) {
AppUser currentUser = extractAuthenticatedUser();
LoanTransactionValidator validator = new LoanTransactionValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(command.getLoanId()), "No loan exists with id: " + command.getLoanId());
}
if (this.isBeforeToday(command.getTransactionDate()) && currentUser.canNotMakeRepaymentOnLoanInPast()) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission.to.make.repayment.on.loan.in.past", "eventDate");
throw new ApplicationDomainRuleException(Arrays.asList(errorResponse));
}
Money repayment = Money.of(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrency(),
command.getTransactionAmount());
LoanTransaction loanRepayment = LoanTransaction.repayment(repayment, command.getTransactionDate());
loan.makeRepayment(loanRepayment, defaultLoanLifecycleStateMachine());
this.loanTransactionRepository.save(loanRepayment);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanTransactionNote(currentUser.getOrganisation(), loan, loanRepayment, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command) {
AppUser currentUser = extractAuthenticatedUser();
AdjustLoanTransactionCommandValidator validator = new AdjustLoanTransactionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new ApplicationDomainRuleException(
loanIdentifierDoesNotExistError(command.getLoanId()),
"No loan exists with id: " + command.getLoanId());
}
LoanTransaction transactionToAdjust = this.loanTransactionRepository
.findOne(command.getRepaymentId());
Money transactionAmount = Money.of(loan
.getLoanRepaymentScheduleDetail().getPrincipal()
.getCurrency(), command.getTransactionAmount());
// adjustment is only supported for repayments and waivers at present
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, command.getTransactionDate());
if (transactionToAdjust.isWaiver()) {
newTransactionDetail = LoanTransaction.waiver(transactionAmount, command.getTransactionDate());
}
loan.adjustExistingTransaction(transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine());
this.loanTransactionRepository.save(newTransactionDetail);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanTransactionNote(currentUser.getOrganisation(),
loan, newTransactionDetail, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier waiveLoanAmount(LoanTransactionCommand command) {
AppUser currentUser = extractAuthenticatedUser();
LoanTransactionValidator validator = new LoanTransactionValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(command.getLoanId()), "No loan exists with id: " + command.getLoanId());
}
Money waived = Money.of(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrency(),
command.getTransactionAmount());
LoanTransaction waiver = LoanTransaction.waiver(waived, command.getTransactionDate());
loan.waive(waiver, defaultLoanLifecycleStateMachine());
this.loanTransactionRepository.save(waiver);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanTransactionNote(currentUser.getOrganisation(), loan, waiver, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
}
@@ -23,12 +23,12 @@ import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.mifosng.data.AdditionalFieldsSets;
import org.mifosng.data.ApiParameterError;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ErrorResponseList;
import org.mifosng.data.reports.GenericResultset;
import org.mifosng.platform.InvalidSqlException;
import org.mifosng.platform.ReadExtraDataAndReportingService;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -66,7 +66,7 @@ public class AdditionalFieldsApiResource {
@Produces({ MediaType.APPLICATION_JSON })
public Response extraData(@PathParam("type") final String type,
@PathParam("set") final String set,
@PathParam("id") final String id, @Context UriInfo uriInfo) {
@PathParam("id") final String id) {
try {
GenericResultset result = this.readExtraDataAndReportingService
@@ -74,15 +74,10 @@ public class AdditionalFieldsApiResource {
return Response.ok().entity(result).build();
} catch (InvalidSqlException e) {
List<ErrorResponse> allErrors = new ArrayList<ErrorResponse>();
ErrorResponse err = new ErrorResponse("extradata.invalid.sql",
"sql", e.getSql());
allErrors.add(err);
throw new WebApplicationException(Response
.status(Status.BAD_REQUEST)
.entity(new ErrorResponseList(allErrors)).build());
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("extradata.invalid.sql", "The sql is invalid.", "sql", e.getSql());
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
@@ -128,16 +123,11 @@ public class AdditionalFieldsApiResource {
return Response.ok().entity(entityIdentifier).build();
} catch (InvalidSqlException e) {
List<ErrorResponse> allErrors = new ArrayList<ErrorResponse>();
ErrorResponse err = new ErrorResponse("extradata.invalid.sql",
"sql", e.getSql());
allErrors.add(err);
logger.info("way bad: " + err.toString());
throw new WebApplicationException(Response
.status(Status.BAD_REQUEST)
.entity(new ErrorResponseList(allErrors)).build());
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("extradata.invalid.sql", "The sql is invalid.", "sql", e.getSql());
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
}
@@ -29,8 +29,6 @@ import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.WritePlatformService;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
import org.mifosng.platform.loan.service.CalculationPlatformService;
import org.mifosng.platform.loan.service.LoanReadPlatformService;
@@ -56,12 +54,6 @@ public class LoansApiResource {
@Autowired
private LoanWritePlatformService loanWritePlatformService;
@Autowired
private ReadPlatformService readPlatformService;
@Autowired
private WritePlatformService writePlatformService;
@Autowired
private CalculationPlatformService calculationPlatformService;
@@ -229,11 +221,11 @@ public class LoansApiResource {
command.setTransactionAmount(transactionAmount);
if (StringUtils.isNotBlank(transactionType) && transactionType.trim().equalsIgnoreCase("waiver")) {
EntityIdentifier identifier = this.writePlatformService.waiveLoanAmount(command);
EntityIdentifier identifier = this.loanWritePlatformService.waiveLoanAmount(command);
return Response.ok().entity(identifier).build();
}
EntityIdentifier identifier = this.writePlatformService.makeLoanRepayment(command);
EntityIdentifier identifier = this.loanWritePlatformService.makeLoanRepayment(command);
return Response.ok().entity(identifier).build();
}
@@ -246,11 +238,11 @@ public class LoansApiResource {
hardcodeUserIntoSecurityContext();
if (StringUtils.isNotBlank(transactionType) && transactionType.trim().equalsIgnoreCase("waiver")) {
LoanRepaymentData loanWaiverData = this.readPlatformService.retrieveNewLoanWaiverDetails(loanId);
LoanRepaymentData loanWaiverData = this.loanReadPlatformService.retrieveNewLoanWaiverDetails(loanId);
return Response.ok().entity(loanWaiverData).build();
}
LoanRepaymentData loanRepaymentData = this.readPlatformService.retrieveNewLoanRepaymentDetails(loanId);
LoanRepaymentData loanRepaymentData = this.loanReadPlatformService.retrieveNewLoanRepaymentDetails(loanId);
return Response.ok().entity(loanRepaymentData).build();
}
@@ -262,7 +254,7 @@ public class LoansApiResource {
hardcodeUserIntoSecurityContext();
LoanRepaymentData loanRepaymentData = this.readPlatformService.retrieveLoanRepaymentDetails(loanId, repaymentId);
LoanRepaymentData loanRepaymentData = this.loanReadPlatformService.retrieveLoanRepaymentDetails(loanId, repaymentId);
return Response.ok().entity(loanRepaymentData).build();
}
@@ -284,7 +276,7 @@ public class LoansApiResource {
BigDecimal transactionAmount = apiDataConversionService.convertFrom(command.getTransactionAmountFormatted(), "transactionAmountFormatted", Locale.UK);
command.setTransactionAmount(transactionAmount);
EntityIdentifier identifier = this.writePlatformService.adjustLoanTransaction(command);
EntityIdentifier identifier = this.loanWritePlatformService.adjustLoanTransaction(command);
return Response.ok().entity(identifier).build();
}
@@ -1,29 +0,0 @@
package org.mifosng.platform.exceptions;
import java.util.ArrayList;
import java.util.List;
import org.mifosng.data.ErrorResponse;
public class ApplicationDomainRuleException extends RuntimeException {
private final List<ErrorResponse> errors;
public ApplicationDomainRuleException(final String message) {
super(message);
this.errors = new ArrayList<ErrorResponse>();
}
public ApplicationDomainRuleException(List<ErrorResponse> errors, final String message) {
super(message);
this.errors = errors;
}
public ApplicationDomainRuleException(List<ErrorResponse> errors) {
this.errors = errors;
}
public List<ErrorResponse> getErrors() {
return errors;
}
}
@@ -1,25 +0,0 @@
package org.mifosng.platform.exceptions;
import java.util.ArrayList;
import java.util.List;
import org.mifosng.data.ErrorResponse;
public class NewDataValidationException extends RuntimeException {
private final List<ErrorResponse> validationErrors;
public NewDataValidationException(final String message) {
super(message);
this.validationErrors = new ArrayList<ErrorResponse>();
}
public NewDataValidationException(List<ErrorResponse> errors, final String message) {
super(message);
this.validationErrors = errors;
}
public List<ErrorResponse> getValidationErrors() {
return validationErrors;
}
}
@@ -1,25 +1,12 @@
package org.mifosng.platform.exceptions;
import java.util.ArrayList;
import java.util.List;
import org.mifosng.data.ErrorResponse;
/**
* A {@link RuntimeException} that is thrown in the case where a user does not have sufficient authorization to execute operation on platform.
*/
public class NoAuthorizationException extends RuntimeException {
private final List<ErrorResponse> authorizationErrors;
public NoAuthorizationException(final String message) {
super(message);
this.authorizationErrors = new ArrayList<ErrorResponse>();
}
public NoAuthorizationException(List<ErrorResponse> authorizationErrors, final String message) {
super(message);
this.authorizationErrors = authorizationErrors;
}
public List<ErrorResponse> getAuthorizationErrors() {
return authorizationErrors;
}
}
@@ -1,49 +0,0 @@
package org.mifosng.platform.loan.domain;
import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.organisation.domain.Organisation;
/**
* remove in favour of static factory method on {@link Loan}. No advantage to builder patter in this case?
*/
@Deprecated
public class LoanBuilder {
private Organisation organisation;
private Client client;
private LoanProduct loanProduct;
private LoanProductRelatedDetail loanRepaymentScheduleDetail;
private LoanStatus loanStatus;
private String externalSystemId;
public Loan build() {
Loan loan = new Loan(this.organisation, this.client, this.loanProduct, this.loanRepaymentScheduleDetail, this.loanStatus);
loan.setExternalId(this.externalSystemId);
return loan;
}
public LoanBuilder with(final Organisation withOrg) {
this.organisation = withOrg;
return this;
}
public LoanBuilder with(final LoanProduct withLoanProduct) {
this.loanProduct = withLoanProduct;
return this;
}
public LoanBuilder with(final Client withClient) {
this.client = withClient;
return this;
}
public LoanBuilder with(final LoanProductRelatedDetail withLoanRepaymentScheduleDetail) {
this.loanRepaymentScheduleDetail = withLoanRepaymentScheduleDetail;
return this;
}
public LoanBuilder withExternalSystemId(final String withExternalSystemId) {
this.externalSystemId = withExternalSystemId;
return this;
}
}
@@ -0,0 +1,52 @@
package org.mifosng.platform.loan.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ApiParameterError;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
public class AdjustLoanTransactionCommandValidator {
private final AdjustLoanTransactionCommand command;
public AdjustLoanTransactionCommandValidator(AdjustLoanTransactionCommand command) {
this.command = command;
}
public void validate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
if (command.getRepaymentId() == null || command.getRepaymentId().longValue() <= 0) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.id.is.invalid", "The parameter repaymentId is invalid.", "repaymentId", command.getRepaymentId());
dataValidationErrors.add(error);
}
if (command.getLoanId() == null || command.getLoanId().doubleValue() <= 0) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.id.is.invalid", "The parameter loanId is invalid.", "loanId", command.getLoanId());
dataValidationErrors.add(error);
}
if (command.getTransactionDate() == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.repayment.date.cannot.be.blank", "The parameter transactionDateFormatted cannot be blank.", "transactionDateFormatted");
dataValidationErrors.add(error);
}
if (command.getTransactionAmount() == null || command.getTransactionAmount().doubleValue() <= 0) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.repayment.must.be.greater.than.zero", "The parameter transactionAmountFormatted less than zero.", "transactionAmountFormatted", command.getTransactionAmountFormatted());
dataValidationErrors.add(error);
}
if (StringUtils.isNotBlank(command.getComment()) && command.getComment().length() > 1000) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.note.exceeds.max.length", "The parameter comment exceeds max allowed size of {0}", "comment", 1000);
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
}
@@ -1,6 +1,7 @@
package org.mifosng.platform.loan.service;
import org.mifosng.data.LoanAccountData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.NewLoanWorkflowStepOneData;
public interface LoanReadPlatformService {
@@ -8,4 +9,10 @@ public interface LoanReadPlatformService {
LoanAccountData retrieveLoanAccountDetails(Long loanId);
NewLoanWorkflowStepOneData retrieveClientAndProductDetails(Long clientId, Long productId);
LoanRepaymentData retrieveNewLoanRepaymentDetails(Long loanId);
LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId);
LoanRepaymentData retrieveLoanRepaymentDetails(Long loanId, Long repaymentId);
}
@@ -1,18 +1,18 @@
package org.mifosng.platform.loan.service;
import static org.mifosng.platform.Specifications.loanTransactionsThatMatch;
import static org.mifosng.platform.Specifications.loansThatMatch;
import java.util.ArrayList;
import java.util.Collection;
import javax.sql.DataSource;
import org.joda.time.LocalDate;
import org.mifosng.data.ClientData;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.DerivedLoanData;
import org.mifosng.data.LoanAccountData;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.MoneyData;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.platform.client.service.ClientReadPlatformService;
@@ -21,33 +21,37 @@ import org.mifosng.platform.currency.domain.ApplicationCurrencyRepository;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.LoanTransactionRepository;
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private final SimpleJdbcTemplate jdbcTemplate;
// private final SimpleJdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final LoanRepository loanRepository;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final LoanProductReadPlatformService loanProductReadPlatformService;
private final ClientReadPlatformService clientReadPlatformService;
private final LoanTransactionRepository loanTransactionRepository;
@Autowired
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final DataSource dataSource,
final LoanRepository loanRepository, final ApplicationCurrencyRepository applicationCurrencyRepository,
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context,
// final DataSource dataSource,
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository, final ApplicationCurrencyRepository applicationCurrencyRepository,
final LoanProductReadPlatformService loanProductReadPlatformService, final ClientReadPlatformService clientReadPlatformService) {
this.context = context;
this.loanRepository = loanRepository;
this.loanTransactionRepository = loanTransactionRepository;
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.loanProductReadPlatformService = loanProductReadPlatformService;
this.clientReadPlatformService = clientReadPlatformService;
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
// this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@Override
@@ -205,4 +209,95 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
return match;
}
@Override
public LoanRepaymentData retrieveNewLoanRepaymentDetails(Long loanId) {
AppUser currentUser = context.authenticatedUser();
// TODO - OPTIMIZE - write simple sql query to fetch back date of
// possible next transaction date.
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), loanId));
ApplicationCurrency currency = this.applicationCurrencyRepository
.findOneByCode(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(),
currency.getName(), currency.getDecimalPlaces(),
currency.getDisplaySymbol(), currency.getNameCode());
LocalDate earliestUnpaidInstallmentDate = loan
.possibleNextRepaymentDate();
Money possibleNextRepaymentAmount = loan.possibleNextRepaymentAmount();
MoneyData possibleNextRepayment = MoneyData.of(currencyData,
possibleNextRepaymentAmount.getAmount());
LoanRepaymentData newRepaymentDetails = new LoanRepaymentData();
newRepaymentDetails.setDate(earliestUnpaidInstallmentDate);
newRepaymentDetails.setTotal(possibleNextRepayment);
return newRepaymentDetails;
}
@Override
public LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId) {
AppUser currentUser = context.authenticatedUser();
// TODO - OPTIMIZE - write simple sql query to fetch back date of
// possible next transaction date.
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), loanId));
ApplicationCurrency currency = this.applicationCurrencyRepository
.findOneByCode(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(),
currency.getName(), currency.getDecimalPlaces(),
currency.getDisplaySymbol(), currency.getNameCode());
Money totalOutstanding = loan.getTotalOutstanding();
MoneyData totalOutstandingData = MoneyData.of(currencyData,
totalOutstanding.getAmount());
LoanRepaymentData newWaiverDetails = new LoanRepaymentData();
newWaiverDetails.setDate(new LocalDate());
newWaiverDetails.setTotal(totalOutstandingData);
return newWaiverDetails;
}
@Override
public LoanRepaymentData retrieveLoanRepaymentDetails(Long loanId,
Long repaymentId) {
AppUser currentUser = context.authenticatedUser();
LoanTransaction transaction = this.loanTransactionRepository
.findOne(loanTransactionsThatMatch(
currentUser.getOrganisation(), repaymentId));
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), loanId));
ApplicationCurrency currency = this.applicationCurrencyRepository
.findOneByCode(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrencyCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(),
currency.getName(), currency.getDecimalPlaces(),
currency.getDisplaySymbol(), currency.getNameCode());
MoneyData total = MoneyData.of(currencyData, transaction.getAmount());
LocalDate date = transaction.getTransactionDate();
LoanRepaymentData loanRepaymentData = new LoanRepaymentData();
loanRepaymentData.setId(repaymentId);
loanRepaymentData.setTotal(total);
loanRepaymentData.setDate(date);
return loanRepaymentData;
}
}
@@ -0,0 +1,42 @@
package org.mifosng.platform.loan.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ApiParameterError;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
public class LoanStateTransitionCommandValidator {
private final LoanStateTransitionCommand command;
public LoanStateTransitionCommandValidator(LoanStateTransitionCommand command) {
this.command = command;
}
public void validate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
if (command.getLoanId() == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.id.is.invalid", "The parameter loanId is invalid.", "loanId", command.getLoanId());
dataValidationErrors.add(error);
}
if (command.getEventDate() == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.state.transition.date.cannot.be.blank", "The parameter eventDate cannot be blank.", "eventDate");
dataValidationErrors.add(error);
}
if (StringUtils.isNotBlank(command.getComment()) && command.getComment().length() > 1000) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.note.exceeds.max.length", "The parameter comment exceeds mas allowed length of {0}.", "comment");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
}
@@ -0,0 +1,47 @@
package org.mifosng.platform.loan.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ApiParameterError;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
public class LoanTransactionCommandValidator {
private final LoanTransactionCommand command;
public LoanTransactionCommandValidator(LoanTransactionCommand command) {
this.command = command;
}
public void validate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
if (command.getLoanId() == null || command.getLoanId().doubleValue() <= 0) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.id.is.invalid", "The parameter loanId is invalid.", "loanId", command.getLoanId());
dataValidationErrors.add(error);
}
if (command.getTransactionDate() == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.repayment.date.cannot.be.blank", "The parameter transactionDateFormatted cannot be blank.", "transactionDateFormatted");
dataValidationErrors.add(error);
}
if (command.getTransactionAmount() == null || command.getTransactionAmount().doubleValue() <= 0) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.repayment.must.be.greater.than.zero", "The parameter transactionAmountFormatted less than zero.", "transactionAmountFormatted", command.getTransactionAmountFormatted());
dataValidationErrors.add(error);
}
if (StringUtils.isNotBlank(command.getComment()) && command.getComment().length() > 1000) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.note.exceeds.max.length", "The parameter comment exceeds max allowed size of {0}", "comment", 1000);
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
}
@@ -1,7 +1,9 @@
package org.mifosng.platform.loan.service;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
@@ -32,4 +34,11 @@ public interface LoanWritePlatformService {
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_UNDO_LOAN_DISBURSAL_ROLE')")
public EntityIdentifier undloLoanDisbursal(UndoLoanDisbursalCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_MAKE_LOAN_REPAYMENT_ROLE', 'CAN_MAKE_LOAN_REPAYMENT_IN_THE_PAST_ROLE')")
public EntityIdentifier makeLoanRepayment(LoanTransactionCommand command);
EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command);
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
}
@@ -12,12 +12,13 @@ import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.MoneyData;
import org.mifosng.data.ScheduledLoanInstallment;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.platform.LoanStateTransitionCommandValidator;
import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.client.domain.Note;
@@ -40,6 +41,8 @@ import org.mifosng.platform.loan.domain.LoanRepaymentScheduleInstallment;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.LoanStatus;
import org.mifosng.platform.loan.domain.LoanStatusRepository;
import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.LoanTransactionRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loanschedule.domain.AprCalculator;
import org.mifosng.platform.security.PlatformSecurityContext;
@@ -60,13 +63,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final NoteRepository noteRepository;
private final CalculationPlatformService calculationPlatformService;
private final AprCalculator aprCalculator = new AprCalculator();
private final LoanTransactionRepository loanTransactionRepository;
@Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final LoanRepository loanRepository, final LoanStatusRepository loanStatusRepository, final LoanProductRepository loanProductRepository,
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
final LoanStatusRepository loanStatusRepository, final LoanProductRepository loanProductRepository,
final ClientRepository clientRepository, final NoteRepository noteRepository, final CalculationPlatformService calculationPlatformService) {
this.context = context;
this.loanRepository = loanRepository;
this.loanTransactionRepository = loanTransactionRepository;
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
this.loanStatusRepository = loanStatusRepository;
@@ -128,6 +134,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments();
Loan loan = Loan.createNew(currentUser.getOrganisation(), loanProduct, client, loanRepaymentScheduleDetail);
loan.setExternalId(command.getExternalId());
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
@@ -400,4 +407,115 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier makeLoanRepayment(final LoanTransactionCommand command) {
AppUser currentUser = context.authenticatedUser();
LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", command.getLoanId());
}
if (this.isBeforeToday(command.getTransactionDate()) && currentUser.canNotMakeRepaymentOnLoanInPast()) {
throw new NoAuthorizationException("error.msg.no.permission.to.make.repayment.on.loan.in.past");
}
Money repayment = Money.of(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrency(),
command.getTransactionAmount());
LoanTransaction loanRepayment = LoanTransaction.repayment(repayment, command.getTransactionDate());
loan.makeRepayment(loanRepayment, defaultLoanLifecycleStateMachine());
this.loanTransactionRepository.save(loanRepayment);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanTransactionNote(currentUser.getOrganisation(), loan, loanRepayment, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command) {
AppUser currentUser = context.authenticatedUser();
AdjustLoanTransactionCommandValidator validator = new AdjustLoanTransactionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", command.getLoanId());
}
LoanTransaction transactionToAdjust = this.loanTransactionRepository
.findOne(command.getRepaymentId());
Money transactionAmount = Money.of(loan
.getLoanRepaymentScheduleDetail().getPrincipal()
.getCurrency(), command.getTransactionAmount());
// adjustment is only supported for repayments and waivers at present
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, command.getTransactionDate());
if (transactionToAdjust.isWaiver()) {
newTransactionDetail = LoanTransaction.waiver(transactionAmount, command.getTransactionDate());
}
loan.adjustExistingTransaction(transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine());
this.loanTransactionRepository.save(newTransactionDetail);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanTransactionNote(currentUser.getOrganisation(),
loan, newTransactionDetail, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
@Transactional
@Override
public EntityIdentifier waiveLoanAmount(LoanTransactionCommand command) {
AppUser currentUser = context.authenticatedUser();
LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
validator.validate();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), command.getLoanId()));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", command.getLoanId());
}
Money waived = Money.of(loan.getLoanRepaymentScheduleDetail()
.getPrincipal().getCurrency(),
command.getTransactionAmount());
LoanTransaction waiver = LoanTransaction.waiver(waived, command.getTransactionDate());
loan.waive(waiver, defaultLoanLifecycleStateMachine());
this.loanTransactionRepository.save(waiver);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getComment())) {
Note note = Note.loanTransactionNote(currentUser.getOrganisation(), loan, waiver, command.getComment());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getClient().getId());
}
}
@@ -4,17 +4,13 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.ImportClientCommand;
import org.mifosng.data.command.ImportLoanCommand;
import org.mifosng.data.command.ImportLoanRepaymentsCommand;
import org.mifosng.platform.ImportPlatformService;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -26,56 +22,37 @@ public class ImportResource {
@Autowired
private ImportPlatformService importPlatformService;
@POST
@Path("clients")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response importClients(final ImportClientCommand command) {
try {
this.importPlatformService.importClients(command);
return Response.ok().build();
} catch (UnAuthenticatedUserException e) {
ErrorResponse err = new ErrorResponse("client.not.authenticated",
"authentication", "");
throw new WebApplicationException(Response
.status(Status.UNAUTHORIZED).entity(err).build());
}
this.importPlatformService.importClients(command);
return Response.ok().build();
}
@POST
@Path("loans")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response importLoans(final ImportLoanCommand command) {
try {
this.importPlatformService.importLoans(command);
return Response.ok().build();
} catch (UnAuthenticatedUserException e) {
ErrorResponse err = new ErrorResponse("client.not.authenticated",
"authentication", "");
throw new WebApplicationException(Response
.status(Status.UNAUTHORIZED).entity(err).build());
}
this.importPlatformService.importLoans(command);
return Response.ok().build();
}
@POST
@Path("loanrepayments")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response importLoanRepayments(final ImportLoanRepaymentsCommand command) {
try {
this.importPlatformService.importLoanRepayments(command);
return Response.ok().build();
} catch (UnAuthenticatedUserException e) {
ErrorResponse err = new ErrorResponse("client.not.authenticated",
"authentication", "");
throw new WebApplicationException(Response
.status(Status.UNAUTHORIZED).entity(err).build());
}
public Response importLoanRepayments(
final ImportLoanRepaymentsCommand command) {
this.importPlatformService.importLoanRepayments(command);
return Response.ok().build();
}
}