MIFOSX-60 added loan charges api methods.

This commit is contained in:
Michal Dudzinski
2012-09-21 11:05:29 +02:00
parent 4f98144896
commit 3a89d10454
11 changed files with 243 additions and 10 deletions
@@ -24,6 +24,7 @@ import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand; import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand;
import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand; import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
import org.mifosng.platform.api.commands.LoanApplicationCommand; import org.mifosng.platform.api.commands.LoanApplicationCommand;
import org.mifosng.platform.api.commands.LoanChargeCommand;
import org.mifosng.platform.api.commands.LoanStateTransitionCommand; import org.mifosng.platform.api.commands.LoanStateTransitionCommand;
import org.mifosng.platform.api.commands.LoanTransactionCommand; import org.mifosng.platform.api.commands.LoanTransactionCommand;
import org.mifosng.platform.api.commands.UndoStateTransitionCommand; import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
@@ -481,4 +482,49 @@ public class LoansApiResource {
return Response.ok().entity(identifier).build(); return Response.ok().entity(identifier).build();
} }
@POST
@Path("{loanId}/charges")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response addLoanCharge(
@PathParam("loanId") final Long loanId,
final String jsonRequestBody){
LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(null, loanId, jsonRequestBody);
EntityIdentifier identifier = this.loanWritePlatformService.addLoanCharge(command);
return Response.ok().entity(identifier).build();
}
@PUT
@Path("{loanId}/charges/{chargeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response updateLoanCharge(
@PathParam("loanId") final Long loanId,
@PathParam("chargeId") final Long loanChargeId,
final String jsonRequestBody){
LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(loanChargeId, loanId, jsonRequestBody);
EntityIdentifier identifier = this.loanWritePlatformService.updateLoanCharge(command);
return Response.ok().entity(identifier).build();
}
@DELETE
@Path("{loanId}/charges/{chargeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response deleteLoanCharge(
@PathParam("loanId") final Long loanId,
@PathParam("chargeId") final Long loanChargeId){
EntityIdentifier identifier = this.loanWritePlatformService.deleteLoanCharge(loanId, loanChargeId);
return Response.ok().entity(identifier).build();
}
} }
@@ -7,6 +7,7 @@ public class LoanChargeCommand {
private final Long id; private final Long id;
private final Long chargeId; private final Long chargeId;
private final Long loanId;
private final BigDecimal amount; private final BigDecimal amount;
private final Integer chargeTimeType; private final Integer chargeTimeType;
@@ -14,11 +15,11 @@ public class LoanChargeCommand {
private final Set<String> modifiedParameters; private final Set<String> modifiedParameters;
public LoanChargeCommand(final Set<String> modifiedParameters, final Long id, final Long chargeId, public LoanChargeCommand(Set<String> modifiedParameters, Long id, Long loanId, Long chargeId, BigDecimal amount, Integer chargeTimeType, Integer chargeCalculationType) {
final BigDecimal amount, final Integer chargeTimeType, final Integer chargeCalculationType) {
this.modifiedParameters = modifiedParameters; this.modifiedParameters = modifiedParameters;
this.id = id; this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.loanId = loanId;
this.amount = amount; this.amount = amount;
this.chargeTimeType = chargeTimeType; this.chargeTimeType = chargeTimeType;
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
@@ -32,6 +33,10 @@ public class LoanChargeCommand {
return chargeId; return chargeId;
} }
public Long getLoanId() {
return loanId;
}
public BigDecimal getAmount() { public BigDecimal getAmount() {
return amount; return amount;
} }
@@ -11,6 +11,7 @@ import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionCommand; import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.api.commands.FundCommand;
import org.mifosng.platform.api.commands.GroupCommand; import org.mifosng.platform.api.commands.GroupCommand;
import org.mifosng.platform.api.commands.LoanChargeCommand;
import org.mifosng.platform.api.commands.LoanProductCommand; import org.mifosng.platform.api.commands.LoanProductCommand;
import org.mifosng.platform.api.commands.LoanStateTransitionCommand; import org.mifosng.platform.api.commands.LoanStateTransitionCommand;
import org.mifosng.platform.api.commands.LoanTransactionCommand; import org.mifosng.platform.api.commands.LoanTransactionCommand;
@@ -49,6 +50,8 @@ public interface ApiDataConversionService {
LoanApplicationCommand convertJsonToLoanApplicationCommand(Long resourceIdentifier, String jsonRequestBody); LoanApplicationCommand convertJsonToLoanApplicationCommand(Long resourceIdentifier, String jsonRequestBody);
LoanChargeCommand convertJsonToLoanChargeCommand(Long loanChargeId, Long loanId, String jsonRequestBody);
LoanStateTransitionCommand convertJsonToLoanStateTransitionCommand(Long resourceIdentifier, String jsonRequestBody); LoanStateTransitionCommand convertJsonToLoanStateTransitionCommand(Long resourceIdentifier, String jsonRequestBody);
LoanTransactionCommand convertJsonToLoanTransactionCommand(Long resourceIdentifier, String jsonRequestBody); LoanTransactionCommand convertJsonToLoanTransactionCommand(Long resourceIdentifier, String jsonRequestBody);
@@ -506,7 +506,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
Integer chargeTimeType = extractIntegerParameter("chargeTimeType", chargeRequestMap, chargeModifiedParameters); Integer chargeTimeType = extractIntegerParameter("chargeTimeType", chargeRequestMap, chargeModifiedParameters);
Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", chargeRequestMap, chargeModifiedParameters); Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", chargeRequestMap, chargeModifiedParameters);
charges[i] = new LoanChargeCommand(chargeModifiedParameters, null, chargeId, amount, charges[i] = new LoanChargeCommand(chargeModifiedParameters, null, null,chargeId, amount,
chargeTimeType, chargeCalculationType); chargeTimeType, chargeCalculationType);
} }
} }
@@ -522,7 +522,33 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
inArrearsToleranceValue, charges,loanOfficerId); inArrearsToleranceValue, charges,loanOfficerId);
} }
@Override @Override
public LoanChargeCommand convertJsonToLoanChargeCommand(Long loanChargeId, Long loanId, String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
Set<String> supportedParams = new HashSet<String>(
Arrays.asList("chargeId", "amount", "chargeTimeType", "chargeCalculationType", "locale")
);
checkForUnsupportedParameters(requestMap, supportedParams);
Set<String> modifiedParameters = new HashSet<String>();
Long chargeId = extractLongParameter("chargeId", requestMap, modifiedParameters);
BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters);
Integer chargeTimeType = extractIntegerParameter("chargeTimeType", requestMap, modifiedParameters);
Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", requestMap, modifiedParameters);
return new LoanChargeCommand(modifiedParameters, loanChargeId, loanId, chargeId, amount,
chargeTimeType, chargeCalculationType);
}
@Override
public LoanStateTransitionCommand convertJsonToLoanStateTransitionCommand(final Long resourceIdentifier, final String json) { public LoanStateTransitionCommand convertJsonToLoanStateTransitionCommand(final Long resourceIdentifier, final String json) {
if (StringUtils.isBlank(json)) { if (StringUtils.isBlank(json)) {
throw new InvalidJsonException(); throw new InvalidJsonException();
@@ -577,6 +577,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
doPostLoanTransactionChecks(loanTransaction, loanLifecycleStateMachine); doPostLoanTransactionChecks(loanTransaction, loanLifecycleStateMachine);
} }
public void updateTotalChargesDueAtDisbursement(){
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
}
public void makeRepayment(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) { public void makeRepayment(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
handleRepaymentTransaction(loanTransaction, loanLifecycleStateMachine, false); handleRepaymentTransaction(loanTransaction, loanLifecycleStateMachine, false);
} }
@@ -39,6 +39,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
@Column(name = "charge_calculation_enum") @Column(name = "charge_calculation_enum")
private Integer chargeCalculation; private Integer chargeCalculation;
public static LoanCharge createNew(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
return new LoanCharge(loan, chargeDefinition, command);
}
public static LoanCharge createNew(final Charge chargeDefinition, final LoanChargeCommand command) { public static LoanCharge createNew(final Charge chargeDefinition, final LoanChargeCommand command) {
return new LoanCharge(null, chargeDefinition, command); return new LoanCharge(null, chargeDefinition, command);
} }
@@ -90,6 +94,22 @@ public class LoanCharge extends AbstractPersistable<Long> {
this.loan = loan; this.loan = loan;
} }
public void update(final LoanChargeCommand command){
if (command.isAmountChanged()) {
this.amount = command.getAmount();
}
if (command.isChargeTimeTypeChanged()){
this.chargeTime = ChargeTimeType.fromInt(command.getChargeTimeType()).getValue();
}
if (command.isChargeCalculationTypeChanged()){
this.chargeCalculation = ChargeCalculationType.fromInt(command.getChargeCalculationType()).getValue();
}
}
public boolean isDueAtDisbursement() { public boolean isDueAtDisbursement() {
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.DISBURSEMENT); return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.DISBURSEMENT);
} }
@@ -122,6 +142,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
public LoanChargeCommand toData() { public LoanChargeCommand toData() {
Set<String> modifiedParameters = new HashSet<String>(); Set<String> modifiedParameters = new HashSet<String>();
return new LoanChargeCommand(modifiedParameters, this.getId(), this.charge.getId(), this.amount, this.chargeTime, this.chargeCalculation); return new LoanChargeCommand(modifiedParameters, this.getId(), this.loan.getId(), this.charge.getId(),
this.amount, this.chargeTime, this.chargeCalculation);
} }
} }
@@ -0,0 +1,8 @@
package org.mifosng.platform.loan.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LoanChargeRepository extends JpaRepository<LoanCharge, Long>, JpaSpecificationExecutor<LoanCharge> {
// no added behaviour
}
@@ -58,7 +58,7 @@ public class LoanApplicationCommandValidator {
for (LoanChargeCommand loanChargeCommand : this.command.getCharges()){ for (LoanChargeCommand loanChargeCommand : this.command.getCharges()){
try { try {
LoanChargeCommandValidator validator = new LoanChargeCommandValidator(loanChargeCommand); LoanChargeCommandValidator validator = new LoanChargeCommandValidator(loanChargeCommand);
validator.validate(); validator.validateForCreate();
} catch (PlatformApiDataValidationException e) { } catch (PlatformApiDataValidationException e) {
dataValidationErrors.addAll(e.getErrors()); dataValidationErrors.addAll(e.getErrors());
} }
@@ -17,7 +17,7 @@ public class LoanChargeCommandValidator {
this.command = command; this.command = command;
} }
public void validate(){ public void validateForCreate(){
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge"); DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
@@ -31,4 +31,18 @@ public class LoanChargeCommandValidator {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
} }
} }
public void validateForUpdate(){
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
baseDataValidator.reset().parameter("amount").value(command.getAmount()).ignoreIfNull().positiveAmount();
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().inMinMaxRange(1, 1);
baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).ignoreIfNull().inMinMaxRange(1, 4);
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; package org.mifosng.platform.loan.service;
import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand; import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand;
import org.mifosng.platform.api.commands.LoanChargeCommand;
import org.mifosng.platform.api.commands.LoanStateTransitionCommand; import org.mifosng.platform.api.commands.LoanStateTransitionCommand;
import org.mifosng.platform.api.commands.LoanTransactionCommand; import org.mifosng.platform.api.commands.LoanTransactionCommand;
import org.mifosng.platform.api.commands.LoanApplicationCommand; import org.mifosng.platform.api.commands.LoanApplicationCommand;
@@ -45,4 +46,13 @@ public interface LoanWritePlatformService {
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')") @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command); EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
@PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')")
EntityIdentifier addLoanCharge(LoanChargeCommand command);
@PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')")
EntityIdentifier updateLoanCharge(LoanChargeCommand command);
@PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')")
EntityIdentifier deleteLoanCharge(final Long loanId, final Long loanChargeId);
} }
@@ -18,12 +18,17 @@ import org.mifosng.platform.api.commands.LoanTransactionCommand;
import org.mifosng.platform.api.commands.UndoStateTransitionCommand; import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.LoanSchedulePeriodData; import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.charge.domain.Charge;
import org.mifosng.platform.charge.domain.ChargeRepository;
import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.client.domain.ClientRepository; import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.client.domain.Note; import org.mifosng.platform.client.domain.Note;
import org.mifosng.platform.client.domain.NoteRepository; import org.mifosng.platform.client.domain.NoteRepository;
import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.ChargeIsNotActiveException;
import org.mifosng.platform.exceptions.ChargeNotFoundException;
import org.mifosng.platform.exceptions.ClientNotFoundException; import org.mifosng.platform.exceptions.ClientNotFoundException;
import org.mifosng.platform.exceptions.InvalidCurrencyException;
import org.mifosng.platform.exceptions.LoanNotFoundException; import org.mifosng.platform.exceptions.LoanNotFoundException;
import org.mifosng.platform.exceptions.LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted; import org.mifosng.platform.exceptions.LoanNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosng.platform.exceptions.LoanProductNotFoundException; import org.mifosng.platform.exceptions.LoanProductNotFoundException;
@@ -33,6 +38,7 @@ import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine; import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.Loan; import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanCharge; import org.mifosng.platform.loan.domain.LoanCharge;
import org.mifosng.platform.loan.domain.LoanChargeRepository;
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine; import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.LoanProduct; import org.mifosng.platform.loan.domain.LoanProduct;
import org.mifosng.platform.loan.domain.LoanProductRepository; import org.mifosng.platform.loan.domain.LoanProductRepository;
@@ -62,12 +68,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanAssembler loanAssembler; private final LoanAssembler loanAssembler;
private final ClientRepository clientRepository; private final ClientRepository clientRepository;
private final LoanProductRepository loanProductRepository; private final LoanProductRepository loanProductRepository;
private final ChargeRepository chargeRepository;
private final LoanChargeRepository loanChargeRepository;
@Autowired @Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanAssembler loanAssembler, public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanAssembler loanAssembler,
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository, final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
final NoteRepository noteRepository, final CalculationPlatformService calculationPlatformService, final NoteRepository noteRepository, final CalculationPlatformService calculationPlatformService,
final ClientRepository clientRepository, final LoanProductRepository loanProductRepository) { final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
final ChargeRepository chargeRepository, final LoanChargeRepository loanChargeRepository) {
this.context = context; this.context = context;
this.loanAssembler = loanAssembler; this.loanAssembler = loanAssembler;
this.loanRepository = loanRepository; this.loanRepository = loanRepository;
@@ -76,6 +85,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.calculationPlatformService = calculationPlatformService; this.calculationPlatformService = calculationPlatformService;
this.clientRepository = clientRepository; this.clientRepository = clientRepository;
this.loanProductRepository = loanProductRepository; this.loanProductRepository = loanProductRepository;
this.chargeRepository = chargeRepository;
this.loanChargeRepository = loanChargeRepository;
} }
private boolean isBeforeToday(final LocalDate date) { private boolean isBeforeToday(final LocalDate date) {
@@ -533,4 +544,89 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return new EntityIdentifier(loan.getId()); return new EntityIdentifier(loan.getId());
} }
@Override
public EntityIdentifier addLoanCharge(LoanChargeCommand command) {
this.context.authenticatedUser();
LoanChargeCommandValidator validator = new LoanChargeCommandValidator(command);
validator.validateForCreate();
Loan loan = this.loanRepository.findOne(command.getLoanId());
if (loan == null) {
throw new LoanNotFoundException(command.getLoanId());
}
Charge chargeDefinition = this.chargeRepository.findOne(command.getChargeId());
if (chargeDefinition == null) {
throw new ChargeNotFoundException(command.getChargeId());
}
LoanCharge loanCharge = LoanCharge.createNew(loan, chargeDefinition, command);
if (!chargeDefinition.isActive()) {
throw new ChargeIsNotActiveException(chargeDefinition.getId());
}
if (!loan.getCurrency().getCode().equals(chargeDefinition.getCurrencyCode())){
String errorMessage = "Charge and Loan must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.loan", errorMessage);
}
this.loanChargeRepository.saveAndFlush(loanCharge);
loan.getCharges().add(loanCharge);
loan.updateTotalChargesDueAtDisbursement();
this.loanRepository.saveAndFlush(loan);
return new EntityIdentifier(loanCharge.getId());
}
@Override
public EntityIdentifier updateLoanCharge(LoanChargeCommand command) {
this.context.authenticatedUser();
LoanChargeCommandValidator validator = new LoanChargeCommandValidator(command);
validator.validateForUpdate();
Loan loan = this.loanRepository.findOne(command.getLoanId());
if (loan == null) {
throw new LoanNotFoundException(command.getLoanId());
}
for (LoanCharge loanCharge : loan.getCharges()){
if (loanCharge.getId().equals(command.getId())){
loanCharge.update(command);
loan.updateTotalChargesDueAtDisbursement();
this.loanRepository.saveAndFlush(loan);
return new EntityIdentifier(loanCharge.getId());
}
}
throw new ChargeNotFoundException(command.getId());
}
@Override
public EntityIdentifier deleteLoanCharge(Long loanId, Long loanChargeId) {
this.context.authenticatedUser();
Loan loan = this.loanRepository.findOne(loanId);
if (loan == null) {
throw new LoanNotFoundException(loanId);
}
for (LoanCharge loanCharge : loan.getCharges()){
if (loanCharge.getId().equals(loanChargeId)){
loan.getCharges().remove(loanCharge);
loan.updateTotalChargesDueAtDisbursement();
this.loanRepository.saveAndFlush(loan);
return new EntityIdentifier(loanCharge.getId());
}
}
throw new ChargeNotFoundException(loanChargeId);
}
} }