diff --git a/mifosng-db/migrations/core_db/V98__add_permission_for_adjust_savings_transaction.sql b/mifosng-db/migrations/core_db/V98__add_permission_for_adjust_savings_transaction.sql new file mode 100755 index 000000000..906bb1497 --- /dev/null +++ b/mifosng-db/migrations/core_db/V98__add_permission_for_adjust_savings_transaction.sql @@ -0,0 +1 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('transaction_savings', 'ADJUSTTRANSACTION_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'ADJUSTTRANSACTION', 0); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 3e973eddc..2cc730d21 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -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"); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 445fd23c1..357a9150c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -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; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index eabf31970..e1208c0a4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -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()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index b2ba454b1..cade9ee85 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -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 SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, @@ -135,7 +144,8 @@ public class SavingsApiConstants { transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName)); public static final Set SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, - "accountId", accountNoParamName, "currency", "amount")); + "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName, runningBalanceParamName, + reversedParamName)); public static final Set SAVINGS_ACCOUNT_ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, activatedOnDateParamName)); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java index d02f65cad..8a4f9f87e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountTransactionsApiResource.java @@ -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 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) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 7c1c11af3..c4be8705b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -719,8 +719,9 @@ public class SavingsAccount extends AbstractPersistable { } } - private void validateAccountBalanceDoesNotBecomeNegativeDueToUndoAction(final List transactionsSortedByDate) { + public void validateAccountBalanceDoesNotBecomeNegative(String transactionAction) { + final List transactionsSortedByDate = retreiveListOfTransactions(); Money runningBalance = Money.zero(this.currency); for (final SavingsAccountTransaction transaction : transactionsSortedByDate) { @@ -734,7 +735,7 @@ public class SavingsAccount extends AbstractPersistable { // final List dataValidationErrors = new ArrayList(); 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 { return actualChanges; } - public void undoTransaction(final Long transactionId, final List reversedTransactionIds, final MathContext mc, - final LocalDate upToInterestCalculationDate) { + public void undoTransaction(final Long transactionId, final List reversedTransactionIds) { SavingsAccountTransaction transactionToUndo = null; for (final SavingsAccountTransaction transaction : this.transactions) { @@ -1424,9 +1424,6 @@ public class SavingsAccount extends AbstractPersistable { } } - calculateInterestUsing(mc, upToInterestCalculationDate); - - validateAccountBalanceDoesNotBecomeNegativeDueToUndoAction(retreiveListOfTransactions()); } } @@ -1616,8 +1613,8 @@ public class SavingsAccount extends AbstractPersistable { 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()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsAccountTransactionNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsAccountTransactionNotFoundException.java new file mode 100755 index 000000000..10ac75ad7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/SavingsAccountTransactionNotFoundException.java @@ -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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/TransactionUpdateNotAllowedException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/TransactionUpdateNotAllowedException.java new file mode 100755 index 000000000..7635aa184 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/TransactionUpdateNotAllowedException.java @@ -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); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/SavingsTransactionAdjustmentCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/SavingsTransactionAdjustmentCommandHandler.java new file mode 100755 index 000000000..f6571df9e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/SavingsTransactionAdjustmentCommandHandler.java @@ -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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java index 684a5a487..f09125c94 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -28,4 +28,6 @@ public interface SavingsAccountReadPlatformService { Collection retrieveAllTransactions(Long savingsId); Collection retrieveAccountsWithAnnualFeeDue(); + + SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 75c7a0561..5f0e4c5df 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -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 retrieveAccountsWithAnnualFeeDue() { final String sql = "select " + annualFeeMapper.schema() diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java index cf8570b32..283c358ab 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java @@ -26,4 +26,6 @@ public interface SavingsAccountWritePlatformService { CommandProcessingResult undoTransaction(Long savingsId, Long transactionId); void postInterestForAccounts(); + + CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 119915d13..1f67eeff7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -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 existingTransactionIds = new ArrayList(); + final List existingReversedTransactionIds = new ArrayList(); + final Map changes = new LinkedHashMap(); + 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 existingTransactionIds, final List existingReversedTransactionIds) {