Adding command history, and reconstructing mark late to go to
command history rather than transaction history. This in
preparation for arrears handling.
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java b/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java
index bdcdd3b..b476d47 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/handler/BeatPublishCommandHandler.java
@@ -37,6 +37,8 @@
import io.mifos.portfolio.api.v1.domain.Case;
import io.mifos.portfolio.service.config.PortfolioProperties;
import io.mifos.portfolio.service.internal.command.CreateBeatPublishCommand;
+import io.mifos.portfolio.service.internal.repository.CaseCommandEntity;
+import io.mifos.portfolio.service.internal.repository.CaseCommandRepository;
import io.mifos.portfolio.service.internal.repository.CaseEntity;
import io.mifos.portfolio.service.internal.repository.CaseRepository;
import io.mifos.portfolio.service.internal.util.AccountingAdapter;
@@ -44,6 +46,10 @@
import io.mifos.rhythm.spi.v1.events.BeatPublishEvent;
import io.mifos.rhythm.spi.v1.events.EventConstants;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@@ -61,6 +67,7 @@
@Aggregate
public class BeatPublishCommandHandler {
private final CaseRepository caseRepository;
+ private final CaseCommandRepository caseCommandRepository;
private final PortfolioProperties portfolioProperties;
private final DataContextService dataContextService;
private final ApplicationName applicationName;
@@ -70,12 +77,14 @@
@Autowired
public BeatPublishCommandHandler(
final CaseRepository caseRepository,
+ final CaseCommandRepository caseCommandRepository,
final PortfolioProperties portfolioProperties,
final DataContextService dataContextService,
final ApplicationName applicationName,
final CommandBus commandBus,
final AccountingAdapter accountingAdapter) {
this.caseRepository = caseRepository;
+ this.caseCommandRepository = caseCommandRepository;
this.portfolioProperties = portfolioProperties;
this.dataContextService = dataContextService;
this.applicationName = applicationName;
@@ -131,6 +140,7 @@
final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
= new DesignatorToAccountIdentifierMapper(dataContextOfAction);
final String customerLoanPrincipalAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL);
+ final String customerLoanInterestAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.CUSTOMER_LOAN_INTEREST);
final String lateFeeAccrualAccountIdentifier = designatorToAccountIdentifierMapper.mapOrThrow(AccountDesignators.LATE_FEE_ACCRUAL);
final BigDecimal currentBalance = accountingAdapter.getCurrentAccountBalance(customerLoanPrincipalAccountIdentifier);
@@ -157,18 +167,23 @@
.getPaymentSize()
.multiply(BigDecimal.valueOf(repaymentPeriodsBetweenBeginningAndToday));
- final BigDecimal paymentsSum = accountingAdapter.sumMatchingEntriesSinceDate(
+ final BigDecimal principalSum = accountingAdapter.sumMatchingEntriesSinceDate(
customerLoanPrincipalAccountIdentifier,
dateOfMostRecentDisbursement.toLocalDate(),
dataContextOfAction.getMessageForCharge(Action.ACCEPT_PAYMENT));
+ final BigDecimal interestSum = accountingAdapter.sumMatchingEntriesSinceDate(
+ customerLoanInterestAccountIdentifier,
+ dateOfMostRecentDisbursement.toLocalDate(),
+ dataContextOfAction.getMessageForCharge(Action.ACCEPT_PAYMENT));
+ final BigDecimal paymentsSum = principalSum.add(interestSum);
final BigDecimal lateFeesAccrued = accountingAdapter.sumMatchingEntriesSinceDate(
lateFeeAccrualAccountIdentifier,
dateOfMostRecentDisbursement.toLocalDate(),
dataContextOfAction.getMessageForCharge(Action.MARK_LATE));
- if (paymentsSum.compareTo(expectedPaymentSum.add(lateFeesAccrued)) < 0) {
- final Optional<LocalDateTime> dateOfMostRecentLateFee = accountingAdapter.getDateOfMostRecentEntryContainingMessage(customerLoanPrincipalAccountIdentifier, dataContextOfAction.getMessageForCharge(Action.MARK_LATE));
+ if (paymentsSum.compareTo(expectedPaymentSum) < 0) {
+ final Optional<LocalDateTime> dateOfMostRecentLateFee = dateOfMostRecentMarkLate(dataContextOfAction.getCustomerCaseEntity().getId());
if (!dateOfMostRecentLateFee.isPresent() ||
mostRecentLateFeeIsBeforeMostRecentRepaymentPeriod(repaymentPeriods, dateOfMostRecentLateFee.get())) {
commandBus.dispatch(new MarkLateCommand(productIdentifier, caseIdentifier, command.getForTime()));
@@ -178,6 +193,16 @@
return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, command.getForTime());
}
+ private Optional<LocalDateTime> dateOfMostRecentMarkLate(final Long caseId) {
+ final Pageable pageRequest = new PageRequest(0, 10, Sort.Direction.DESC, "createdOn");
+ final Page<CaseCommandEntity> page = caseCommandRepository.findByCaseIdAndActionName(
+ caseId,
+ Action.MARK_LATE.name(),
+ pageRequest);
+
+ return page.getContent().stream().findFirst().map(CaseCommandEntity::getCreatedOn);
+ }
+
private boolean mostRecentLateFeeIsBeforeMostRecentRepaymentPeriod(
final List<Period> repaymentPeriods,
final LocalDateTime dateOfMostRecentLateFee) {
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java b/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
index 9bf8a8f..46f7689 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
@@ -16,6 +16,7 @@
package io.mifos.individuallending.internal.command.handler;
+import io.mifos.core.api.util.UserContextHolder;
import io.mifos.core.command.annotation.Aggregate;
import io.mifos.core.command.annotation.CommandHandler;
import io.mifos.core.command.annotation.CommandLogLevel;
@@ -39,9 +40,7 @@
import io.mifos.portfolio.api.v1.domain.CostComponent;
import io.mifos.portfolio.api.v1.events.EventConstants;
import io.mifos.portfolio.service.internal.mapper.CaseMapper;
-import io.mifos.portfolio.service.internal.repository.CaseEntity;
-import io.mifos.portfolio.service.internal.repository.CaseRepository;
-import io.mifos.portfolio.service.internal.repository.TaskInstanceRepository;
+import io.mifos.portfolio.service.internal.repository.*;
import io.mifos.portfolio.service.internal.util.AccountingAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -77,6 +76,7 @@
private final WriteOffPaymentBuilderService writeOffPaymentBuilderService;
private final RecoverPaymentBuilderService recoverPaymentBuilderService;
private final AccountingAdapter accountingAdapter;
+ private final CaseCommandRepository caseCommandRepository;
private final TaskInstanceRepository taskInstanceRepository;
private final CaseParametersRepository caseParametersRepository;
@@ -95,7 +95,7 @@
final WriteOffPaymentBuilderService writeOffPaymentBuilderService,
final RecoverPaymentBuilderService recoverPaymentBuilderService,
final AccountingAdapter accountingAdapter,
- final TaskInstanceRepository taskInstanceRepository,
+ CaseCommandRepository caseCommandRepository, final TaskInstanceRepository taskInstanceRepository,
final CaseParametersRepository caseParametersRepository) {
this.caseRepository = caseRepository;
this.dataContextService = dataContextService;
@@ -110,6 +110,7 @@
this.writeOffPaymentBuilderService = writeOffPaymentBuilderService;
this.recoverPaymentBuilderService = recoverPaymentBuilderService;
this.accountingAdapter = accountingAdapter;
+ this.caseCommandRepository = caseCommandRepository;
this.taskInstanceRepository = taskInstanceRepository;
this.caseParametersRepository = caseParametersRepository;
}
@@ -139,14 +140,22 @@
final LocalDateTime today = today();
- accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
designatorToAccountIdentifierMapper,
command.getCommand().getNote(),
command.getCommand().getCreatedOn(),
dataContextOfAction.getMessageForCharge(Action.OPEN),
Action.OPEN.getTransactionType());
- //Only move to new state if book charges command was accepted.
+
final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getCommand().getCreatedOn(),
+ customerCase.getId(),
+ Action.OPEN,
+ transactionUniqueifier);
+
+ //Only move to new state if book charges command was accepted.
customerCase.setCurrentState(Case.State.PENDING.name());
caseRepository.save(customerCase);
@@ -178,7 +187,21 @@
final LocalDateTime today = today();
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ designatorToAccountIdentifierMapper,
+ command.getCommand().getNote(),
+ command.getCommand().getCreatedOn(),
+ dataContextOfAction.getMessageForCharge(Action.DENY),
+ Action.DENY.getTransactionType());
+
final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getCommand().getCreatedOn(),
+ customerCase.getId(),
+ Action.DENY,
+ transactionUniqueifier);
+
customerCase.setCurrentState(Case.State.CLOSED.name());
caseRepository.save(customerCase);
@@ -259,15 +282,22 @@
final LocalDateTime today = today();
- accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
designatorToAccountIdentifierMapper,
command.getCommand().getNote(),
command.getCommand().getCreatedOn(),
dataContextOfAction.getMessageForCharge(Action.APPROVE),
Action.APPROVE.getTransactionType());
- //Only move to new state if book charges command was accepted.
final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getCommand().getCreatedOn(),
+ customerCase.getId(),
+ Action.APPROVE,
+ transactionUniqueifier);
+
+ //Only move to new state if book charges command was accepted.
customerCase.setCurrentState(Case.State.APPROVED.name());
caseRepository.save(customerCase);
@@ -299,15 +329,23 @@
final LocalDateTime today = today();
- accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
designatorToAccountIdentifierMapper,
command.getCommand().getNote(),
command.getCommand().getCreatedOn(),
dataContextOfAction.getMessageForCharge(Action.DISBURSE),
Action.DISBURSE.getTransactionType());
+
+ final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getCommand().getCreatedOn(),
+ customerCase.getId(),
+ Action.DISBURSE,
+ transactionUniqueifier);
+
//Only move to new state if book charges command was accepted.
if (Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()) != Case.State.ACTIVE) {
- final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
final LocalDateTime endOfTerm
= ScheduledActionHelpers.getRoughEndDate(today.toLocalDate(), dataContextOfAction.getCaseParameters())
.atTime(LocalTime.MIDNIGHT);
@@ -352,13 +390,21 @@
final PaymentBuilder paymentBuilder =
applyInterestPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today(), runningBalances);
- accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
designatorToAccountIdentifierMapper,
"Applied interest on " + command.getForTime(),
command.getForTime(),
dataContextOfAction.getMessageForCharge(Action.APPLY_INTEREST),
Action.APPLY_INTEREST.getTransactionType());
+ final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getForTime(),
+ customerCase.getId(),
+ Action.APPLY_INTEREST,
+ transactionUniqueifier);
+
return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, command.getForTime());
}
@@ -395,13 +441,21 @@
final LocalDateTime today = today();
- accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
designatorToAccountIdentifierMapper,
command.getCommand().getNote(),
command.getCommand().getCreatedOn(),
dataContextOfAction.getMessageForCharge(Action.ACCEPT_PAYMENT),
Action.ACCEPT_PAYMENT.getTransactionType());
+ final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getCommand().getCreatedOn(),
+ customerCase.getId(),
+ Action.ACCEPT_PAYMENT,
+ transactionUniqueifier);
+
return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
}
@@ -435,13 +489,21 @@
final LocalDateTime today = today();
- accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
designatorToAccountIdentifierMapper,
"Marked late on " + command.getForTime(),
command.getForTime(),
dataContextOfAction.getMessageForCharge(Action.MARK_LATE),
Action.MARK_LATE.getTransactionType());
+ final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getForTime(),
+ customerCase.getId(),
+ Action.MARK_LATE,
+ transactionUniqueifier);
+
return new IndividualLoanCommandEvent(productIdentifier, caseIdentifier, DateConverter.toIsoString(today));
}
@@ -456,10 +518,35 @@
IndividualLendingPatternFactory.checkActionCanBeExecuted(Case.State.valueOf(dataContextOfAction.getCustomerCaseEntity().getCurrentState()), Action.WRITE_OFF);
checkIfTasksAreOutstanding(dataContextOfAction, Action.WRITE_OFF);
+ final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
+ = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+ final RealRunningBalances runningBalances = new RealRunningBalances(
+ accountingAdapter,
+ dataContextOfAction);
+
+ final PaymentBuilder paymentBuilder =
+ writeOffPaymentBuilderService.getPaymentBuilder(
+ dataContextOfAction,
+ command.getCommand().getPaymentSize(),
+ DateConverter.fromIsoString(command.getCommand().getCreatedOn()).toLocalDate(), runningBalances);
final LocalDateTime today = today();
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ designatorToAccountIdentifierMapper,
+ command.getCommand().getNote(),
+ command.getCommand().getCreatedOn(),
+ dataContextOfAction.getMessageForCharge(Action.WRITE_OFF),
+ Action.WRITE_OFF.getTransactionType());
+
final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getCommand().getCreatedOn(),
+ customerCase.getId(),
+ Action.WRITE_OFF,
+ transactionUniqueifier);
+
customerCase.setCurrentState(Case.State.CLOSED.name());
caseRepository.save(customerCase);
@@ -489,7 +576,7 @@
final LocalDateTime today = today();
- accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ final Optional<String> transactionIdentifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
designatorToAccountIdentifierMapper,
command.getCommand().getNote(),
command.getCommand().getCreatedOn(),
@@ -515,9 +602,32 @@
checkIfTasksAreOutstanding(dataContextOfAction, Action.RECOVER);
+ final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper
+ = new DesignatorToAccountIdentifierMapper(dataContextOfAction);
+ final RealRunningBalances runningBalances = new RealRunningBalances(
+ accountingAdapter,
+ dataContextOfAction);
+
+ final PaymentBuilder paymentBuilder =
+ recoverPaymentBuilderService.getPaymentBuilder(dataContextOfAction, BigDecimal.ZERO, CostComponentService.today(), runningBalances);
+
final LocalDateTime today = today();
+ final Optional<String> transactionUniqueifier = accountingAdapter.bookCharges(paymentBuilder.getBalanceAdjustments(),
+ designatorToAccountIdentifierMapper,
+ command.getCommand().getNote(),
+ command.getCommand().getCreatedOn(),
+ dataContextOfAction.getMessageForCharge(Action.RECOVER),
+ Action.CLOSE.getTransactionType());
+
final CaseEntity customerCase = dataContextOfAction.getCustomerCaseEntity();
+
+ recordCommand(
+ command.getCommand().getCreatedOn(),
+ customerCase.getId(),
+ Action.RECOVER,
+ transactionUniqueifier);
+
customerCase.setCurrentState(Case.State.CLOSED.name());
caseRepository.save(customerCase);
@@ -546,6 +656,20 @@
action.name(), productIdentifier, caseIdentifier);
}
+ private void recordCommand(
+ final String when,
+ final Long caseId,
+ final Action action,
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") final Optional<String> transactionUniqueifier) {
+ final CaseCommandEntity caseCommandEntity = new CaseCommandEntity();
+ caseCommandEntity.setCaseId(caseId);
+ caseCommandEntity.setActionName(action.name());
+ caseCommandEntity.setCreatedBy(UserContextHolder.checkedGetUser());
+ caseCommandEntity.setCreatedOn(DateConverter.fromIsoString(when));
+ caseCommandEntity.setTransactionUniqueifier(transactionUniqueifier.orElse(""));
+ caseCommandRepository.save(caseCommandEntity);
+ }
+
private static LocalDateTime today() {
return LocalDate.now(Clock.systemUTC()).atStartOfDay();
}
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java
index 2ca9814..f79fa28 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/RecoverPaymentBuilderService.java
@@ -15,25 +15,58 @@
*/
package io.mifos.individuallending.internal.service.costcomponent;
+import io.mifos.individuallending.api.v1.domain.workflow.Action;
+import io.mifos.individuallending.internal.repository.CaseParametersEntity;
import io.mifos.individuallending.internal.service.DataContextOfAction;
+import io.mifos.individuallending.internal.service.schedule.ScheduledAction;
+import io.mifos.individuallending.internal.service.schedule.ScheduledCharge;
+import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
/**
* @author Myrle Krantz
*/
@Service
public class RecoverPaymentBuilderService implements PaymentBuilderService {
+ private final ScheduledChargesService scheduledChargesService;
+
+ @Autowired
+ public RecoverPaymentBuilderService(final ScheduledChargesService scheduledChargesService) {
+ this.scheduledChargesService = scheduledChargesService;
+ }
+
@Override
public PaymentBuilder getPaymentBuilder(
final @Nonnull DataContextOfAction dataContextOfAction,
final @Nullable BigDecimal requestedDisbursalSize,
final LocalDate forDate,
final RunningBalances runningBalances) {
- return null;
+ final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+ final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+ final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
+ final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.RECOVER, forDate));
+ final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
+ productIdentifier, scheduledActions);
+
+ final BigDecimal loanPaymentSize = dataContextOfAction.getCaseParametersEntity().getPaymentSize();
+
+ return CostComponentService.getCostComponentsForScheduledCharges(
+ scheduledCharges,
+ caseParameters.getBalanceRangeMaximum(),
+ runningBalances,
+ loanPaymentSize,
+ BigDecimal.ZERO,
+ BigDecimal.ZERO,
+ dataContextOfAction.getInterest(),
+ minorCurrencyUnitDigits,
+ true);
}
}
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java
index 41faf76..3dab27b 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/WriteOffPaymentBuilderService.java
@@ -15,19 +15,34 @@
*/
package io.mifos.individuallending.internal.service.costcomponent;
+import io.mifos.individuallending.api.v1.domain.workflow.Action;
+import io.mifos.individuallending.internal.repository.CaseParametersEntity;
import io.mifos.individuallending.internal.service.DataContextOfAction;
+import io.mifos.individuallending.internal.service.schedule.ScheduledAction;
+import io.mifos.individuallending.internal.service.schedule.ScheduledCharge;
+import io.mifos.individuallending.internal.service.schedule.ScheduledChargesService;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.time.LocalDate;
+import java.util.Collections;
+import java.util.List;
/**
* @author Myrle Krantz
*/
@Service
public class WriteOffPaymentBuilderService implements PaymentBuilderService {
+ private final ScheduledChargesService scheduledChargesService;
+
+ @Autowired
+ public WriteOffPaymentBuilderService(final ScheduledChargesService scheduledChargesService) {
+ this.scheduledChargesService = scheduledChargesService;
+ }
+
@Override
public PaymentBuilder getPaymentBuilder(
final @Nonnull DataContextOfAction dataContextOfAction,
@@ -35,6 +50,24 @@
final LocalDate forDate,
final RunningBalances runningBalances)
{
- return null;
+ final CaseParametersEntity caseParameters = dataContextOfAction.getCaseParametersEntity();
+ final String productIdentifier = dataContextOfAction.getProductEntity().getIdentifier();
+ final int minorCurrencyUnitDigits = dataContextOfAction.getProductEntity().getMinorCurrencyUnitDigits();
+ final List<ScheduledAction> scheduledActions = Collections.singletonList(new ScheduledAction(Action.WRITE_OFF, forDate));
+ final List<ScheduledCharge> scheduledCharges = scheduledChargesService.getScheduledCharges(
+ productIdentifier, scheduledActions);
+
+ final BigDecimal loanPaymentSize = dataContextOfAction.getCaseParametersEntity().getPaymentSize();
+
+ return CostComponentService.getCostComponentsForScheduledCharges(
+ scheduledCharges,
+ caseParameters.getBalanceRangeMaximum(),
+ runningBalances,
+ loanPaymentSize,
+ BigDecimal.ZERO,
+ BigDecimal.ZERO,
+ dataContextOfAction.getInterest(),
+ minorCurrencyUnitDigits,
+ true);
}
}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseCommandEntity.java b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseCommandEntity.java
new file mode 100644
index 0000000..062726e
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseCommandEntity.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.portfolio.service.internal.repository;
+
+import io.mifos.core.mariadb.util.LocalDateTimeConverter;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+@Entity
+@Table(name = "bastet_case_commands")
+public class CaseCommandEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
+ private Long id;
+
+ @Column(name = "case_id")
+ private Long caseId;
+
+ @Column(name = "action_name")
+ private String actionName;
+
+ @Column(name = "created_on")
+ @Convert(converter = LocalDateTimeConverter.class)
+ private LocalDateTime createdOn;
+
+ @Column(name = "created_by")
+ private String createdBy;
+
+ @Column(name = "thoth_transaction_uq")
+ private String transactionUniqueifier;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getCaseId() {
+ return caseId;
+ }
+
+ public void setCaseId(Long caseId) {
+ this.caseId = caseId;
+ }
+
+ public String getActionName() {
+ return actionName;
+ }
+
+ public void setActionName(String actionName) {
+ this.actionName = actionName;
+ }
+
+ public LocalDateTime getCreatedOn() {
+ return createdOn;
+ }
+
+ public void setCreatedOn(LocalDateTime createdOn) {
+ this.createdOn = createdOn;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public String getTransactionUniqueifier() {
+ return transactionUniqueifier;
+ }
+
+ public void setTransactionUniqueifier(String transactionUniqueifier) {
+ this.transactionUniqueifier = transactionUniqueifier;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CaseCommandEntity that = (CaseCommandEntity) o;
+ return Objects.equals(caseId, that.caseId) &&
+ Objects.equals(actionName, that.actionName) &&
+ Objects.equals(transactionUniqueifier, that.transactionUniqueifier);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(caseId, actionName, transactionUniqueifier);
+ }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseCommandRepository.java b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseCommandRepository.java
new file mode 100644
index 0000000..fa78532
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/repository/CaseCommandRepository.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 Kuelap, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.portfolio.service.internal.repository;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Myrle Krantz
+ */
+@Repository
+public interface CaseCommandRepository extends JpaRepository<CaseCommandEntity, Long> {
+ Page<CaseCommandEntity> findByCaseIdAndActionName(Long caseId, String actionName, Pageable pageable);
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java b/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
index b2a2067..a0a136b 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
@@ -69,12 +69,13 @@
this.logger = logger;
}
- public void bookCharges(final Map<String, BigDecimal> balanceAdjustments,
- final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper,
- final String note,
- final String transactionDate,
- final String message,
- final String transactionType) {
+ public Optional<String> bookCharges(
+ final Map<String, BigDecimal> balanceAdjustments,
+ final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper,
+ final String note,
+ final String transactionDate,
+ final String message,
+ final String transactionType) {
final JournalEntry journalEntry = new JournalEntry();
final Set<Creditor> creditors = new HashSet<>();
journalEntry.setCreditors(creditors);
@@ -106,8 +107,10 @@
//noinspection ConstantConditions
if (creditors.isEmpty() && debtors.isEmpty())
- return;
+ return Optional.empty();
+ final String transactionUniqueifier = RandomStringUtils.random(26, true, true);
+ final String transactionIdentifier = "portfolio." + message + "." + transactionUniqueifier;
journalEntry.setCreditors(creditors);
journalEntry.setDebtors(debtors);
journalEntry.setClerk(UserContextHolder.checkedGetUser());
@@ -115,9 +118,10 @@
journalEntry.setMessage(message);
journalEntry.setTransactionType(transactionType);
journalEntry.setNote(note);
- journalEntry.setTransactionIdentifier("portfolio." + message + "." + RandomStringUtils.random(26, true, true));
+ journalEntry.setTransactionIdentifier(transactionIdentifier);
ledgerManager.createJournalEntry(journalEntry);
+ return Optional.of(transactionUniqueifier);
}
public Optional<LocalDateTime> getDateOfOldestEntryContainingMessage(final String accountIdentifier,
diff --git a/service/src/main/resources/db/migrations/mariadb/V9__arrears_determination.sql b/service/src/main/resources/db/migrations/mariadb/V9__arrears_determination.sql
new file mode 100644
index 0000000..2c91530
--- /dev/null
+++ b/service/src/main/resources/db/migrations/mariadb/V9__arrears_determination.sql
@@ -0,0 +1,28 @@
+--
+-- Copyright 2017 Kuelap, Inc.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE TABLE bastet_case_commands (
+ id BIGINT NOT NULL AUTO_INCREMENT,
+ case_id BIGINT NOT NULL,
+ action_name VARCHAR(32) NOT NULL,
+ created_on TIMESTAMP(3) NOT NULL,
+ created_by VARCHAR(32) NOT NULL,
+ thoth_transaction_uq VARCHAR(26) NOT NULL,
+
+ CONSTRAINT bastet_case_commands_pk PRIMARY KEY (id),
+ CONSTRAINT bastet_case_commands_uq UNIQUE (thoth_transaction_uq, action_name, case_id),
+ CONSTRAINT bastet_case_commands_fk FOREIGN KEY (case_id) REFERENCES bastet_cases (id)
+);
\ No newline at end of file