added savings transaction adjustment API

added Interest posting for undo transactions

rebase changes
This commit is contained in:
Pramod Nuthakki
2013-08-08 12:26:53 +05:30
parent f260591ea9
commit 9e6108c11e
14 changed files with 227 additions and 49 deletions
@@ -0,0 +1 @@
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_savings', 'ADJUSTTRANSACTION_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'ADJUSTTRANSACTION', 0);
@@ -478,6 +478,10 @@ public class CommandWrapper {
return this.actionName.equalsIgnoreCase("UNDOTRANSACTION") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isAdjustSavingsAccountTransaction() {
return this.actionName.equalsIgnoreCase("ADJUSTTRANSACTION") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isCalendarResource() {
return this.entityName.equalsIgnoreCase("CALENDAR");
}
@@ -342,7 +342,7 @@ public class CommandWrapperBuilder {
this.href = "/clients/" + clientId + "?command=activate&template=true";
return this;
}
public CommandWrapperBuilder closeClient(final Long clientId) {
this.actionName = "CLOSE";
this.entityName = "CLIENT";
@@ -372,30 +372,30 @@ public class CommandWrapperBuilder {
}
public CommandWrapperBuilder createDBDatatable(final String json) {
this.actionName = "CREATE";
this.entityName = "DATATABLE";
this.entityId = null;
this.href = "/datatables/";
this.json = json;
return this;
this.actionName = "CREATE";
this.entityName = "DATATABLE";
this.entityId = null;
this.href = "/datatables/";
this.json = json;
return this;
}
public CommandWrapperBuilder updateDBDatatable(final String datatable, final String json) {
this.actionName = "UPDATE";
this.entityName = "DATATABLE";
this.entityId = null;
this.href = "/datatables/" + datatable;
this.json = json;
return this;
this.actionName = "UPDATE";
this.entityName = "DATATABLE";
this.entityId = null;
this.href = "/datatables/" + datatable;
this.json = json;
return this;
}
public CommandWrapperBuilder deleteDBDatatable(final String datatable, final String json) {
this.actionName = "DELETE";
this.entityName = "DATATABLE";
this.entityId = null;
this.href = "/datatables/" + datatable;
this.json = json;
return this;
this.actionName = "DELETE";
this.entityName = "DATATABLE";
this.entityId = null;
this.href = "/datatables/" + datatable;
this.json = json;
return this;
}
public CommandWrapperBuilder createDatatable(final String datatable, final Long apptableId, final Long datatableId) {
@@ -845,6 +845,17 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder adjustSavingsAccountTransaction(final Long accountId, final Long transactionId) {
this.actionName = "ADJUSTTRANSACTION";
this.entityName = "SAVINGSACCOUNT";
this.savingsId = accountId;
this.entityId = accountId;
this.subentityId = transactionId;
this.transactionId = transactionId.toString();
this.href = "/savingsaccounts/" + accountId + "/transactions/" + transactionId + "?command=modify";
return this;
}
public CommandWrapperBuilder savingsAccountInterestCalculation(final Long accountId) {
this.actionName = "CALCULATEINTEREST";
this.entityName = "SAVINGSACCOUNT";
@@ -953,7 +964,7 @@ public class CommandWrapperBuilder {
this.href = "/groups/" + groupId + "?command=activate";
return this;
}
public CommandWrapperBuilder saveGroupCollectionSheet(final Long groupId) {
this.actionName = "SAVECOLLECTIONSHEET";
this.entityName = "GROUP";
@@ -962,7 +973,7 @@ public class CommandWrapperBuilder {
this.href = "/groups/" + groupId + "?command=saveCollectionSheet";
return this;
}
public CommandWrapperBuilder deleteGroup(final Long groupId) {
this.actionName = "DELETE";
this.entityName = "GROUP";
@@ -998,16 +1009,16 @@ public class CommandWrapperBuilder {
this.href = "/groups/" + groupId;
return this;
}
public CommandWrapperBuilder assignGroupStaff(final Long groupId) {
this.actionName = "ASSIGNSTAFF";
this.entityName = "GROUP";
this.entityId = groupId;
this.groupId = groupId;
this.href = "/groups/" + groupId +"?command=assignStaff";
this.href = "/groups/" + groupId + "?command=assignStaff";
return this;
}
public CommandWrapperBuilder createCollateral(final Long loanId) {
this.actionName = "CREATE";
this.entityName = "COLLATERAL";
@@ -1106,7 +1117,7 @@ public class CommandWrapperBuilder {
this.href = "/accountingrules/" + accountingRuleId;
return this;
}
public CommandWrapperBuilder createHoliday() {
this.actionName = "CREATE";
this.entityName = "HOLIDAY";
@@ -1120,7 +1131,7 @@ public class CommandWrapperBuilder {
this.entityName = "GROUP";
this.groupId = groupId;
this.entityId = null;
this.href = "/groups/"+groupId+"?command=assignRole";
this.href = "/groups/" + groupId + "?command=assignRole";
return this;
}
@@ -1129,7 +1140,7 @@ public class CommandWrapperBuilder {
this.entityName = "GROUP";
this.groupId = groupId;
this.entityId = roleId;
this.href = "/groups/"+groupId+"?command=unassignRole";
this.href = "/groups/" + groupId + "?command=unassignRole";
return this;
}
@@ -1138,10 +1149,10 @@ public class CommandWrapperBuilder {
this.entityName = "GROUP";
this.groupId = groupId;
this.entityId = roleId;
this.href = "/groups/"+groupId+"?command=updateRole";
this.href = "/groups/" + groupId + "?command=updateRole";
return this;
}
public CommandWrapperBuilder unassignClientStaff(final Long clientId) {
this.actionName = "UNASSIGNSTAFF";
this.entityName = "CLIENT";
@@ -1177,7 +1188,7 @@ public class CommandWrapperBuilder {
this.href = "/loanproducts/" + productId + "/productmix";
return this;
}
public CommandWrapperBuilder deleteProductMix(final Long productId) {
this.actionName = "DELETE";
this.entityName = "PRODUCTMIX";
@@ -1186,17 +1197,17 @@ public class CommandWrapperBuilder {
this.href = "/loanproducts/" + productId + "/productmix";
return this;
}
public CommandWrapperBuilder withProduct(final Long productId) {
this.productId = productId;
return this;
}
public CommandWrapperBuilder updateJobDetail(final Long jobId) {
this.actionName = "UPDATE";
this.entityName = "SCHEDULER";
this.entityId = jobId;
this.href = "/updateJobDetail/" + jobId+"/updateJobDetail";
this.href = "/updateJobDetail/" + jobId + "/updateJobDetail";
return this;
}
}
@@ -412,6 +412,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
handler = this.applicationContext.getBean("applyAnnualFeeSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountUndoTransaction()) {
handler = this.applicationContext.getBean("undoTransactionSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isAdjustSavingsAccountTransaction()) {
handler = this.applicationContext.getBean("savingsTransactionAdjustmentCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
@@ -30,6 +30,11 @@ public class SavingsApiConstants {
public static String deleteApplicationAction = ".delete";
public static String undoTransactionAction = ".undotransaction";
public static String applyAnnualFeeTransactionAction = ".applyannualfee";
public static String adjustTransactionAction = ".adjusttransaction";
// command
public static String COMMAND_UNDO_TRANSACTION = "undo";
public static String COMMAND_ADJUST_TRANSACTION = "modify";
// general
public static final String localeParamName = "locale";
@@ -81,6 +86,10 @@ public class SavingsApiConstants {
// transaction parameters
public static final String transactionDateParamName = "transactionDate";
public static final String transactionAmountParamName = "transactionAmount";
public static final String paymentDetailDataParamName = "paymentDetailData";
public static final String runningBalanceParamName = "runningBalance";
public static final String reversedParamName = "reversed";
public static final String dateParamName = "date";
public static final Set<String> SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName,
@@ -135,7 +144,8 @@ public class SavingsApiConstants {
transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName));
public static final Set<String> SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName,
"accountId", accountNoParamName, "currency", "amount"));
"accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName, runningBalanceParamName,
reversedParamName));
public static final Set<String> SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, activatedOnDateParamName));
@@ -91,6 +91,27 @@ public class SavingsAccountTransactionsApiResource {
SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveOne(@PathParam("savingsId") final Long savingsId, @PathParam("transactionId") final Long transactionId,
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
SavingsAccountTransactionData transactionData = this.savingsAccountReadPlatformService.retrieveSavingsTransaction(savingsId,
transactionId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) {
final Collection<CodeValueData> paymentTypeOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
transactionData = SavingsAccountTransactionData.templateOnTop(transactionData, paymentTypeOptions);
}
return this.toApiJsonSerializer.serialize(settings, transactionData,
SavingsApiConstants.SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@@ -120,7 +141,7 @@ public class SavingsAccountTransactionsApiResource {
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String undoTransaction(@PathParam("savingsId") final Long savingsId, @PathParam("transactionId") final Long transactionId,
public String adjustTransaction(@PathParam("savingsId") final Long savingsId, @PathParam("transactionId") final Long transactionId,
@QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) {
String jsonApiRequest = apiRequestBodyAsJson;
@@ -131,9 +152,12 @@ public class SavingsAccountTransactionsApiResource {
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
CommandProcessingResult result = null;
if (is(commandParam, "undo")) {
if (is(commandParam, SavingsApiConstants.COMMAND_UNDO_TRANSACTION)) {
final CommandWrapper commandRequest = builder.undoSavingsAccountTransaction(savingsId, transactionId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, SavingsApiConstants.COMMAND_ADJUST_TRANSACTION)) {
final CommandWrapper commandRequest = builder.adjustSavingsAccountTransaction(savingsId, transactionId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
if (result == null) {
@@ -719,8 +719,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
}
private void validateAccountBalanceDoesNotBecomeNegativeDueToUndoAction(final List<SavingsAccountTransaction> transactionsSortedByDate) {
public void validateAccountBalanceDoesNotBecomeNegative(String transactionAction) {
final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
Money runningBalance = Money.zero(this.currency);
for (final SavingsAccountTransaction transaction : transactionsSortedByDate) {
@@ -734,7 +735,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
//
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.undoTransactionAction);
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + transactionAction);
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("results.in.balance.going.negative");
@@ -1396,8 +1397,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return actualChanges;
}
public void undoTransaction(final Long transactionId, final List<Long> reversedTransactionIds, final MathContext mc,
final LocalDate upToInterestCalculationDate) {
public void undoTransaction(final Long transactionId, final List<Long> reversedTransactionIds) {
SavingsAccountTransaction transactionToUndo = null;
for (final SavingsAccountTransaction transaction : this.transactions) {
@@ -1424,9 +1424,6 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
}
calculateInterestUsing(mc, upToInterestCalculationDate);
validateAccountBalanceDoesNotBecomeNegativeDueToUndoAction(retreiveListOfTransactions());
}
}
@@ -1616,8 +1613,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName).value(dateAsString)
.failWithCodeNoParameterAddedToErrorCode("cannot.be.before.client.activation.date");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
if (this.group != null && this.group.isActivatedAfter(activationDate)) {
final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale());
final String dateAsString = formatter.print(this.client.getActivationLocalDate());
@@ -0,0 +1,12 @@
package org.mifosplatform.portfolio.savings.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class SavingsAccountTransactionNotFoundException extends AbstractPlatformResourceNotFoundException {
public SavingsAccountTransactionNotFoundException(final Long savingsId, final Long transactionId) {
super("error.msg.saving.account.trasaction.id.invalid", "Savings account with savings identifier " + savingsId
+ " and trasaction identifier " + transactionId + " does not exist", savingsId, transactionId);
}
}
@@ -0,0 +1,15 @@
package org.mifosplatform.portfolio.savings.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformServiceUnavailableException;
/**
* A {@link RuntimeException} thrown when update not allowed.
*/
public class TransactionUpdateNotAllowedException extends AbstractPlatformServiceUnavailableException {
public TransactionUpdateNotAllowedException(final Long savingsId, final Long transactionId) {
super("error.msg.saving.account.trasaction.update.notallowed",
"Savings Account transaction update not allowed with savings identifier " + savingsId + " and trasaction identifier "
+ transactionId, savingsId, transactionId);
}
}
@@ -0,0 +1,28 @@
package org.mifosplatform.portfolio.savings.handler;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SavingsTransactionAdjustmentCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService writePlatformService;
@Autowired
public SavingsTransactionAdjustmentCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
final Long transactionId = Long.valueOf(command.getTransactionId());
return this.writePlatformService.adjustSavingsTransaction(command.getSavingsId(), transactionId, command);
}
}
@@ -28,4 +28,6 @@ public interface SavingsAccountReadPlatformService {
Collection<SavingsAccountTransactionData> retrieveAllTransactions(Long savingsId);
Collection<SavingsAccountAnnualFeeData> retrieveAccountsWithAnnualFeeDue();
SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId);
}
@@ -507,6 +507,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId });
}
@Override
public SavingsAccountTransactionData retrieveSavingsTransaction(final Long savingsId, final Long transactionId) {
final String sql = "select " + this.transactionsMapper.schema() + " where sa.id = ? and tr.id= ?";
return this.jdbcTemplate.queryForObject(sql, this.transactionsMapper, new Object[] { savingsId, transactionId });
}
@Override
public Collection<SavingsAccountAnnualFeeData> retrieveAccountsWithAnnualFeeDue() {
final String sql = "select " + annualFeeMapper.schema()
@@ -26,4 +26,6 @@ public interface SavingsAccountWritePlatformService {
CommandProcessingResult undoTransaction(Long savingsId, Long transactionId);
void postInterestForAccounts();
CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command);
}
@@ -30,6 +30,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyReposit
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
@@ -38,6 +39,8 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -274,15 +277,20 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId(
transactionId, savingsId);
if (savingsAccountTransaction == null) {
// throw new not found exception
}
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
account.undoTransaction(transactionId, reversedTransactionIds, mc, today);
account.undoTransaction(transactionId, reversedTransactionIds);
if ((savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal())
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, newTransactionIds, reversedTransactionIds);
} else {
account.calculateInterestUsing(mc, today);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
postJournalEntries(account, newTransactionIds, reversedTransactionIds);
@@ -295,6 +303,60 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.build();
}
@Override
public CommandProcessingResult adjustSavingsTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) {
SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId(
transactionId, savingsId);
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(
savingsId, transactionId); }
this.savingsAccountTransactionDataValidator.validate(command);
final LocalDate today = DateUtils.getLocalDateOfTenant();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(SavingsApiConstants.transactionDateParamName);
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(SavingsApiConstants.transactionAmountParamName);
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
account.undoTransaction(transactionId, existingReversedTransactionIds);
SavingsAccountTransaction transaction = null;
if (savingsAccountTransaction.isDeposit()) {
transaction = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds, existingReversedTransactionIds,
paymentDetail);
} else {
transaction = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds, existingReversedTransactionIds,
paymentDetail);
}
final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction);
if (account.isBeforeLastPostingPeriod(transactionDate)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) {
account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds);
} else {
account.calculateInterestUsing(mc, today);
}
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
.withEntityId(newtransactionId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.with(changes)//
.build();
}
private void postJournalEntries(final SavingsAccount savingsAccount, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {