You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by av...@apache.org on 2021/08/09 12:01:38 UTC

[fineract-cn-deposit-account-management] branch develop updated: [FINCN-346] Transaction execution error fix (#26)

This is an automated email from the ASF dual-hosted git repository.

avikg pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-deposit-account-management.git


The following commit(s) were added to refs/heads/develop by this push:
     new 9196049  [FINCN-346] Transaction execution error fix (#26)
9196049 is described below

commit 9196049ab508de1f5320b89481b8f1942c258adf
Author: Manoj <56...@users.noreply.github.com>
AuthorDate: Mon Aug 9 17:31:34 2021 +0530

    [FINCN-346] Transaction execution error fix (#26)
    
    * AL-52-account-transfer-txn
    
    * balance-api-collection-init
    
    * collections
    
    * transaction-execution-error-fix-by-retry
---
 .../fineract/cn/deposit/api/v1/EventConstants.java |  5 ++
 .../cn/deposit/api/v1/PermittableGroupIds.java     |  3 +
 .../deposit/listener/SubTxnTypeEventListener.java  | 69 ++++++++++++++++++++++
 .../deposit/listener/TransactionEventListener.java | 58 ++++++++++++++++++
 .../command/handler/TransactionCommandHandler.java | 11 ++--
 .../internal/service/TransactionService.java       | 55 ++++++++++-------
 .../service/rest/CollectionsRestController.java    |  8 +--
 .../rest/ProductInstanceRestController.java        | 30 +++++++++-
 .../service/rest/SubTxnTypeRestController.java     |  8 +--
 .../service/rest/TransactionRestController.java    | 32 ++++++++--
 service/src/main/resources/application.yml         |  1 +
 11 files changed, 236 insertions(+), 44 deletions(-)

diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/EventConstants.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/EventConstants.java
index 5bd1063..2c7060f 100644
--- a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/EventConstants.java
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/EventConstants.java
@@ -51,7 +51,12 @@ public interface EventConstants {
   String SELECTOR_PUT_PRODUCT_INSTANCE = SELECTOR_NAME + " = '" + PUT_PRODUCT_INSTANCE + "'";
 
   String POST_SUB_TXN_TYPE = "post-sub-txn-type";
+  String SELECTOR_POST_SUB_TXN_TYPE = SELECTOR_NAME + " = '" + POST_SUB_TXN_TYPE + "'";
   String PUT_SUB_TXN_TYPE = "put-sub-txn-type";
+  String SELECTOR_PUT_SUB_TXN_TYPE = SELECTOR_NAME + " = '" + PUT_SUB_TXN_TYPE + "'";
+
+  String POST_TXN = "post-txn";
+  String SELECTOR_POST_TXN = SELECTOR_NAME + " = '" + POST_TXN + "'";
 
   String ACTIVATE_PRODUCT_INSTANCE_COMMAND = "ACTIVATE";
   String CLOSE_PRODUCT_INSTANCE_COMMAND = "CLOSE";
diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/PermittableGroupIds.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/PermittableGroupIds.java
index 31c90c4..f558738 100644
--- a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/PermittableGroupIds.java
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/PermittableGroupIds.java
@@ -21,5 +21,8 @@ package org.apache.fineract.cn.deposit.api.v1;
 @SuppressWarnings("unused")
 public interface PermittableGroupIds {
   String DEFINITION_MANAGEMENT = "deposit__v1__definition";
+  String DEF_SUB_TXN_MANAGEMENT = "deposit__v1__subtxn";
   String INSTANCE_MANAGEMENT = "deposit__v1__instance";
+  String TXN_MANAGEMENT = "deposit__v1__transaction";
+  String COLLECTION_MANAGEMENT = "deposit__v1__collection";
 }
diff --git a/component-test/src/main/java/org/apache/fineract/cn/deposit/listener/SubTxnTypeEventListener.java b/component-test/src/main/java/org/apache/fineract/cn/deposit/listener/SubTxnTypeEventListener.java
new file mode 100644
index 0000000..1fa5586
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/deposit/listener/SubTxnTypeEventListener.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.fineract.cn.deposit.listener;
+
+import org.apache.fineract.cn.deposit.api.v1.EventConstants;
+import org.apache.fineract.cn.deposit.service.ServiceConstants;
+import org.apache.fineract.cn.lang.config.TenantHeaderFilter;
+import org.apache.fineract.cn.test.listener.EventRecorder;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.messaging.handler.annotation.Header;
+
+/**
+ * @author manoj
+ */
+public class SubTxnTypeEventListener {
+    private final Logger logger;
+    private final EventRecorder eventRecorder;
+
+    @Autowired
+    public SubTxnTypeEventListener(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                                    final EventRecorder eventRecorder) {
+        super();
+        this.logger = logger;
+        this.eventRecorder = eventRecorder;
+    }
+
+    @JmsListener(
+            destination = EventConstants.DESTINATION,
+            selector = EventConstants.SELECTOR_POST_SUB_TXN_TYPE,
+            subscription = EventConstants.DESTINATION
+    )
+    public void onCreateSubTxnType(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                          final String payload) {
+        this.logger.debug("Product definition created.");
+        this.eventRecorder.event(tenant, EventConstants.POST_SUB_TXN_TYPE, payload, String.class);
+    }
+
+
+    @JmsListener(
+            destination = EventConstants.DESTINATION,
+            selector = EventConstants.SELECTOR_PUT_SUB_TXN_TYPE,
+            subscription = EventConstants.DESTINATION
+    )
+    public void onUpdateSubTxnType(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                          final String payload) {
+        this.logger.debug("Product definition created.");
+        this.eventRecorder.event(tenant, EventConstants.PUT_SUB_TXN_TYPE, payload, String.class);
+    }
+
+
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/deposit/listener/TransactionEventListener.java b/component-test/src/main/java/org/apache/fineract/cn/deposit/listener/TransactionEventListener.java
new file mode 100644
index 0000000..6738f76
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/deposit/listener/TransactionEventListener.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.fineract.cn.deposit.listener;
+
+import org.apache.fineract.cn.command.annotation.EventEmitter;
+import org.apache.fineract.cn.deposit.api.v1.EventConstants;
+import org.apache.fineract.cn.deposit.service.ServiceConstants;
+import org.apache.fineract.cn.lang.config.TenantHeaderFilter;
+import org.apache.fineract.cn.test.listener.EventRecorder;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jms.annotation.JmsListener;
+import org.springframework.messaging.handler.annotation.Header;
+
+/**
+ * @author manoj
+ */
+public class TransactionEventListener {
+
+ private final Logger logger;
+ private final EventRecorder eventRecorder;
+
+ @Autowired
+ public TransactionEventListener(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+                                 final EventRecorder eventRecorder) {
+  super();
+  this.logger = logger;
+  this.eventRecorder = eventRecorder;
+ }
+
+ @JmsListener(
+         destination = EventConstants.DESTINATION,
+         selector = EventConstants.SELECTOR_POST_TXN,
+         subscription = EventConstants.DESTINATION
+ )
+ public void onPostTxn(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                                       final String payload) {
+  this.logger.debug("Product definition created.");
+  this.eventRecorder.event(tenant, EventConstants.POST_TXN, payload, String.class);
+ }
+
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/handler/TransactionCommandHandler.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/handler/TransactionCommandHandler.java
index d9a4db0..4851a35 100644
--- a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/handler/TransactionCommandHandler.java
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/handler/TransactionCommandHandler.java
@@ -21,22 +21,19 @@ package org.apache.fineract.cn.deposit.service.internal.command.handler;
 import org.apache.fineract.cn.command.annotation.Aggregate;
 import org.apache.fineract.cn.command.annotation.CommandHandler;
 import org.apache.fineract.cn.command.annotation.CommandLogLevel;
+import org.apache.fineract.cn.command.annotation.EventEmitter;
+import org.apache.fineract.cn.deposit.api.v1.EventConstants;
 import org.apache.fineract.cn.deposit.api.v1.transaction.domain.data.TransactionResponseData;
 import org.apache.fineract.cn.deposit.service.ServiceConstants;
 import org.apache.fineract.cn.deposit.service.internal.command.TransactionCommand;
-import org.apache.fineract.cn.deposit.service.internal.command.TransactionProcessedCommand;
-import org.apache.fineract.cn.deposit.service.internal.repository.ProductInstanceEntity;
 import org.apache.fineract.cn.deposit.service.internal.service.TransactionService;
-import org.apache.fineract.cn.lang.ServiceException;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.validation.constraints.NotNull;
-import java.time.Clock;
-import java.time.LocalDateTime;
-import java.util.Optional;
 
 @Aggregate
 public class TransactionCommandHandler {
@@ -53,7 +50,9 @@ public class TransactionCommandHandler {
     @NotNull
     @Transactional
     @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+    @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.POST_TXN)
     public TransactionResponseData performTransfer(@NotNull TransactionCommand command) {
+
         switch (command.getAction()) {
             case WITHDRAWAL: {
                 //command = dataValidator.validatePrepareTransfer(command);
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/TransactionService.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/TransactionService.java
index 97a3ac0..7c6753c 100644
--- a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/TransactionService.java
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/TransactionService.java
@@ -61,7 +61,6 @@ import java.util.stream.Collectors;
 public class TransactionService {
     private final Logger logger;
     private final LedgerManager ledgerManager;
-    private final ProductInstanceService productInstanceService;
     private final ProductDefinitionService productDefinitionService;
     private final ActionService actionService;
     private final SubTxnTypesService subTxnTypesService;
@@ -73,12 +72,10 @@ public class TransactionService {
 
     @Autowired
     public TransactionService(@Qualifier(ServiceConstants.LOGGER_NAME) Logger logger, LedgerManager ledgerManager,
-                              ProductInstanceService productInstanceService,
                               ProductDefinitionService productDefinitionService, ActionService actionService,
                               SubTxnTypesService subTxnTypesService, TransactionRepository transactionRepository, ProductInstanceRepository productInstanceRepository) {
         this.logger = logger;
         this.ledgerManager = ledgerManager;
-        this.productInstanceService = productInstanceService;
         this.productDefinitionService = productDefinitionService;
         this.actionService = actionService;
         this.subTxnTypesService = subTxnTypesService;
@@ -92,7 +89,7 @@ public class TransactionService {
         AccountWrapper accountWrapper = validateAndGetAccount(request, request.getAccountId(), TransactionTypeEnum.WITHDRAWAL);
         LocalDateTime transactionDate = getNow();
         //get txntype charges
-        List<Charge> charges = getCharges(accountWrapper.account.getIdentifier(), TransactionTypeEnum.WITHDRAWAL);
+        List<Charge> charges = getCharges(accountWrapper.productDefinition, TransactionTypeEnum.WITHDRAWAL);
         //todo: get subTxnType charges
 
         TransactionEntity txn = doWithdraw(request, accountWrapper, charges, getNow(), request.getAccountId());
@@ -109,7 +106,7 @@ public class TransactionService {
         AccountWrapper accountWrapper = validateAndGetAccount(request, request.getAccountId(), TransactionTypeEnum.DEPOSIT);
         LocalDateTime transactionDate = getNow();
         //get txntype charges
-        List<Charge> charges = getCharges(accountWrapper.account.getIdentifier(), TransactionTypeEnum.DEPOSIT);
+        List<Charge> charges = getCharges(accountWrapper.productDefinition, TransactionTypeEnum.DEPOSIT);
         //todo: get subTxnType charges
         TransactionEntity txn = doDeposit(request, accountWrapper, charges, getNow(), request.getAccountId());
 
@@ -124,7 +121,7 @@ public class TransactionService {
         AccountWrapper toAccountWrapper = validateAndGetAccount(request, request.getToAccountId(), TransactionTypeEnum.DEPOSIT);
         LocalDateTime transactionDate = getNow();
         //get txntype charges
-        List<Charge> charges = getCharges(fromAccountWrapper.account.getIdentifier(), TransactionTypeEnum.ACCOUNT_TRANSFER);
+        List<Charge> charges = getCharges(fromAccountWrapper.productDefinition, TransactionTypeEnum.ACCOUNT_TRANSFER);
         //todo: get subTxnType charges
 
         TransactionEntity txn = doWithdraw(request, fromAccountWrapper, charges, getNow(), request.getFromAccountId());
@@ -139,7 +136,9 @@ public class TransactionService {
                                         List<Charge> charges, LocalDateTime transactionDate, String accountId) {
         BigDecimal amount = request.getAmount().getAmount();
 
-        TransactionEntity txn = createTransaction(request,TransactionTypeEnum.DEPOSIT, transactionDate, CREDIT, null, accountId);
+        TransactionEntity txn = createTransaction(request,TransactionTypeEnum.DEPOSIT, transactionDate, CREDIT,
+                null, accountId, accountWrapper.instance);
+
         String debitAccountIdentifier = accountWrapper.productDefinition.getCashAccountIdentifier();
         /* if subtxn is provided and it has an account configured the do debit that account*/
         if(StringUtils.isNotBlank(request.getSubTxnId())){
@@ -149,6 +148,8 @@ public class TransactionService {
                 if (subTxnTypeOpt.get().getLedgerAccount() != null) {
                     debitAccountIdentifier = subTxnTypeOpt.get().getLedgerAccount();
                 }
+            }else{
+                throw ServiceException.notFound("Sub Txn Type {0} not found.", request.getSubTxnId());
             }
         }
         final JournalEntry journalEntry = createJournalEntry(txn.getIdentifier(), TransactionTypeEnum.DEPOSIT.getCode(),
@@ -180,7 +181,8 @@ public class TransactionService {
                                  List<Charge> charges, LocalDateTime transactionDate, String accountId) {
         BigDecimal amount = request.getAmount().getAmount();
 
-        TransactionEntity txn = createTransaction(request, TransactionTypeEnum.WITHDRAWAL, transactionDate, DEBIT, null, accountId);
+        TransactionEntity txn = createTransaction(request, TransactionTypeEnum.WITHDRAWAL, transactionDate, DEBIT,
+                null, accountId, accountWrapper.instance);
 
         String creditAccountIdentifier = accountWrapper.productDefinition.getCashAccountIdentifier();
         /* if subtxn is provided and it has an account configured the do credit that account*/
@@ -191,6 +193,8 @@ public class TransactionService {
                 if (subTxnTypeOpt.get().getLedgerAccount() != null) {
                     creditAccountIdentifier = subTxnTypeOpt.get().getLedgerAccount();
                 }
+            }else{
+                throw ServiceException.notFound("Sub Txn Type {0} not found.", request.getSubTxnId());
             }
         }
         final JournalEntry journalEntry = createJournalEntry(txn.getIdentifier(), TransactionTypeEnum.WITHDRAWAL.getCode(),
@@ -239,7 +243,8 @@ public class TransactionService {
         for(Charge charge : charges){
             addCreditor(charge.getIncomeAccountIdentifier(), calcChargeAmount(amount, charge).doubleValue(), creditors);
             addDebtor(accountWrapper.account.getIdentifier(), calcChargeAmount(amount, charge).doubleValue(), debtors);
-            createTransaction(request,TransactionTypeEnum.CHARGES_PAYMENT, getNow(), DEBIT, txn, accountId);
+            createTransaction(request,TransactionTypeEnum.CHARGES_PAYMENT, getNow(), DEBIT, txn,
+                    accountId, accountWrapper.instance);
         }
 
     }
@@ -250,8 +255,10 @@ public class TransactionService {
         Account account = ledgerManager.findAccount(accountId);
         validateAccount(request.getAmount(), account);
 
-        ProductInstance product = productInstanceService.findByAccountIdentifier(accountId).get();
-        ProductDefinition productDefinition = productDefinitionService.findProductDefinition(product.getProductIdentifier()).get();
+        ProductInstanceEntity instance = productInstanceRepository.findByAccountIdentifier(accountId).orElseThrow(
+                () -> ServiceException.notFound("Account {0} not found", accountId)
+        );
+        ProductDefinition productDefinition = productDefinitionService.findProductDefinition(instance.getProductDefinition().getIdentifier()).get();
 
         Currency currency = productDefinition.getCurrency();
         if (!currency.getCode().equals(request.getAmount().getCurrency()))
@@ -263,7 +270,7 @@ public class TransactionService {
         if (txnType == TransactionTypeEnum.WITHDRAWAL && withdrawableBalance < request.getAmount().getAmount().doubleValue())
             throw new UnsupportedOperationException();
 
-        return new AccountWrapper(account, product, productDefinition, withdrawableBalance);
+        return new AccountWrapper(account, instance, productDefinition, withdrawableBalance);
     }
 
     Double getWithdrawableBalance(Account account, ProductDefinition productDefinition) {
@@ -278,8 +285,10 @@ public class TransactionService {
         String accountId = account.getIdentifier();
 
         if (account.getHolders() != null) { // customer account
-            ProductInstance product = productInstanceService.findByAccountIdentifier(accountId).get();
-            ProductDefinition productDefinition = productDefinitionService.findProductDefinition(product.getProductIdentifier()).get();
+            ProductInstanceEntity instance = productInstanceRepository.findByAccountIdentifier(accountId).orElseThrow(
+                    () -> ServiceException.notFound("Account {0} not found", accountId)
+            );
+            ProductDefinition productDefinition = productDefinitionService.findProductDefinition(instance.getProductDefinition().getIdentifier()).get();
             if (!Boolean.TRUE.equals(productDefinition.getActive()))
                 throw new UnsupportedOperationException("Product Definition is inactive");
 
@@ -296,7 +305,7 @@ public class TransactionService {
             throw new UnsupportedOperationException("Account is in state " + account.getState());
     }
 
-    public List<Charge> getCharges(String accountIdentifier, TransactionTypeEnum transactionType) {
+    public List<Charge> getCharges(ProductDefinition productDefinition, TransactionTypeEnum transactionType) {
         List<Action> actions = actionService.fetchActions();
 
         List<String> actionIds = actions
@@ -305,9 +314,6 @@ public class TransactionService {
                 .map(Action::getIdentifier)
                 .collect(Collectors.toList());
 
-        ProductInstance product = productInstanceService.findByAccountIdentifier(accountIdentifier).get();
-        ProductDefinition productDefinition = productDefinitionService.findProductDefinition(product.getProductIdentifier()).get();
-
         return productDefinition.getCharges()
                 .stream()
                 .filter(charge -> actionIds.contains(charge.getActionIdentifier()))
@@ -374,7 +380,8 @@ public class TransactionService {
 
     private TransactionEntity createTransaction(TransactionRequestData request, TransactionTypeEnum txnType,
                                                 LocalDateTime transactionDate, String tranType,
-                                                TransactionEntity parent, String accountId) {
+                                                TransactionEntity parent, String accountId,
+                                                ProductInstanceEntity productInstanceEntity) {
         TransactionEntity txn = new TransactionEntity();
         UUID uuid=UUID.randomUUID();
 
@@ -401,6 +408,8 @@ public class TransactionService {
         txn.setType(tranType);
         txn.setParentTransaction(parent);
         transactionRepository.save(txn);
+        productInstanceEntity.setLastTransactionDate(transactionDate);
+        this.productInstanceRepository.save(productInstanceEntity);
         return txn;
     }
 
@@ -416,6 +425,7 @@ public class TransactionService {
         this.productInstanceRepository.save(productInstanceEntity);
 
     }
+
     public List<StatementResponse> fetchStatement(String accountId,
                                                   LocalDateTime fromDateTime,
                                                   LocalDateTime toDateTime) {
@@ -447,15 +457,16 @@ public class TransactionService {
         @NotNull
         private final Account account;
         @NotNull
-        private final ProductInstance product;
+        private final ProductInstanceEntity instance;
         @NotNull
         private final ProductDefinition productDefinition;
         @NotNull
         private final Double withdrawableBalance;
 
-        public AccountWrapper(Account account, ProductInstance product, ProductDefinition productDefinition, Double withdrawableBalance) {
+        public AccountWrapper(Account account, ProductInstanceEntity instance,
+                              ProductDefinition productDefinition, Double withdrawableBalance) {
             this.account = account;
-            this.product = product;
+            this.instance = instance;
             this.productDefinition = productDefinition;
             this.withdrawableBalance = withdrawableBalance;
         }
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/CollectionsRestController.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/CollectionsRestController.java
index 3c0b30b..e33616b 100644
--- a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/CollectionsRestController.java
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/CollectionsRestController.java
@@ -50,7 +50,7 @@ public class CollectionsRestController {
         this.commandGateway = commandGateway;
         this.collectionsService = collectionsService;
     }
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.COLLECTION_MANAGEMENT)
     @RequestMapping(
             value = "",
             method = RequestMethod.POST,
@@ -68,7 +68,7 @@ public class CollectionsRestController {
     }
 
 
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.COLLECTION_MANAGEMENT)
     @RequestMapping(
             value = "/{collectionsReference}",
             method = RequestMethod.PUT,
@@ -85,7 +85,7 @@ public class CollectionsRestController {
     }
 
 
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.COLLECTION_MANAGEMENT)
     @RequestMapping(
             value = "/{collectionsReference}",
             method = RequestMethod.GET,
@@ -99,6 +99,4 @@ public class CollectionsRestController {
         CollectionsResponse result = collectionsService.fetchCollection(collectionsReference);
         return ResponseEntity.ok(result);
     }
-
-
 }
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/ProductInstanceRestController.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/ProductInstanceRestController.java
index f0cc9fb..135ebf5 100644
--- a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/ProductInstanceRestController.java
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/ProductInstanceRestController.java
@@ -46,6 +46,7 @@ import org.apache.fineract.cn.lang.ServiceException;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
@@ -68,6 +69,10 @@ public class ProductInstanceRestController {
   private final ProductInstanceService productInstanceService;
   private final TransactionService transactionService;
 
+
+  @Value("${config.txnMaxRetry}")
+  private Integer txnMaxRetry;
+
   @Autowired
   public ProductInstanceRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
                                        final CommandGateway commandGateway,
@@ -88,9 +93,28 @@ public class ProductInstanceRestController {
       produces = MediaType.APPLICATION_JSON_VALUE
   )
   @ResponseBody
-  public ResponseEntity<Void> create(@RequestBody @Valid final ProductInstance productInstance) {
-    this.commandGateway.process(new CreateProductInstanceCommand(productInstance));
-    return ResponseEntity.accepted().build();
+  public ResponseEntity<Void> create(@RequestBody @Valid final ProductInstance productInstance)  throws Throwable{
+    int retryCount = 0;
+    Exception e = null;
+    do {
+      retryCount++;
+      logger.info("Try transaction :  " + retryCount + " of " + txnMaxRetry);
+      System.out.println("*******Try transaction :  " + retryCount + " of " + txnMaxRetry);
+      try {
+        this.commandGateway.process(new CreateProductInstanceCommand(productInstance));
+        return ResponseEntity.accepted().build();
+      } catch (Exception ex) {
+        logger.info(ex.getClass().getCanonicalName());
+        System.out.println(ex.getClass().getCanonicalName());
+        logger.info(ex.getClass().getName());
+        System.out.println(ex.getClass().getName());
+        logger.info(ex.getMessage());
+        System.out.println(ex.getMessage());
+        e=ex;
+      }
+    } while (retryCount < txnMaxRetry);
+    //throw the last exception
+    throw e;
   }
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/SubTxnTypeRestController.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/SubTxnTypeRestController.java
index 8a875f4..53998e2 100644
--- a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/SubTxnTypeRestController.java
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/SubTxnTypeRestController.java
@@ -57,7 +57,7 @@ public class SubTxnTypeRestController {
         this.service = service;
     }
 
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEFINITION_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEF_SUB_TXN_MANAGEMENT)
     @RequestMapping(
             value = "",
             method = RequestMethod.POST,
@@ -78,7 +78,7 @@ public class SubTxnTypeRestController {
         return ResponseEntity.accepted().build();
     }
 
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEFINITION_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEF_SUB_TXN_MANAGEMENT)
     @RequestMapping(
             value = "",
             method = RequestMethod.GET,
@@ -92,7 +92,7 @@ public class SubTxnTypeRestController {
     }
 
 
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEFINITION_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEF_SUB_TXN_MANAGEMENT)
     @RequestMapping(
             value = "/{identifier}",
             method = RequestMethod.GET,
@@ -110,7 +110,7 @@ public class SubTxnTypeRestController {
     }
 
 
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEFINITION_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.DEF_SUB_TXN_MANAGEMENT)
     @RequestMapping(
             value = "/{identifier}",
             method = RequestMethod.PUT,
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/TransactionRestController.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/TransactionRestController.java
index 3b6e278..16ae9f0 100644
--- a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/TransactionRestController.java
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/TransactionRestController.java
@@ -36,6 +36,7 @@ import org.apache.fineract.cn.lang.ServiceException;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
@@ -50,6 +51,10 @@ public class TransactionRestController {
     private final CommandGateway commandGateway;
     private final SubTxnTypesService service;
 
+    @Value("${config.txnMaxRetry}")
+    private Integer txnMaxRetry;
+
+
     @Autowired
     public TransactionRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
                                      CommandGateway commandGateway,
@@ -59,7 +64,7 @@ public class TransactionRestController {
         this.service = service;
     }
 
-    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+    @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.TXN_MANAGEMENT)
     @RequestMapping(
             value = "",
             method = RequestMethod.POST,
@@ -70,10 +75,29 @@ public class TransactionRestController {
     @ResponseBody
     ResponseEntity<TransactionResponseData> performTxn(@RequestParam("action") String action, @RequestBody TransactionRequestData requestData)
             throws Throwable {
-        CommandCallback<TransactionResponseData> result = commandGateway.process(new TransactionCommand(requestData, TransactionActionType.valueOf(action)),
-                TransactionResponseData.class);
+        int retryCount = 0;
+        Exception e = null;
+        do {
+            retryCount++;
+            logger.info("Try transaction :  " + retryCount + " of " + txnMaxRetry);
+            System.out.println("*******Try transaction :  " + retryCount + " of " + txnMaxRetry);
+            try {
+                CommandCallback<TransactionResponseData> result = commandGateway.process(new TransactionCommand(requestData, TransactionActionType.valueOf(action)),
+                        TransactionResponseData.class);
 
-        return ResponseEntity.ok(result.get());
+                return ResponseEntity.ok(result.get());
+            } catch (Exception ex) {
+                logger.info(ex.getClass().getCanonicalName());
+                System.out.println(ex.getClass().getCanonicalName());
+                logger.info(ex.getClass().getName());
+                System.out.println(ex.getClass().getName());
+                logger.info(ex.getMessage());
+                System.out.println(ex.getMessage());
+                e=ex;
+            }
+        } while (retryCount < txnMaxRetry);
+        //throw the last exception
+        throw e;
     }
 
 }
diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml
index 7a6ef48..94b23ec 100644
--- a/service/src/main/resources/application.yml
+++ b/service/src/main/resources/application.yml
@@ -78,5 +78,6 @@ config:
   fixedAccountId: true
   otpTokenLength: 6
   tokenExpiryInSeconds: 172800
+  txnMaxRetry : 5