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

arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new ebe87433e [FINERACT-1678] Inline COB API
ebe87433e is described below

commit ebe87433e764094014085da1a759dcc5588fd43c
Author: taskain7 <[email protected]>
AuthorDate: Sat Oct 15 05:16:06 2022 +0200

    [FINERACT-1678] Inline COB API
---
 .../LoanIdsResponseDTO.java}                       |  18 +--
 ...LoanAccountLockCannotBeOverruledException.java} |  16 +-
 ...Listener.java => AbstractLoanItemListener.java} |   8 +-
 .../ChunkProcessingLoanItemListener.java}          |  21 +--
 .../InlineCOBLoanItemListener.java}                |  21 +--
 ...ocessor.java => AbstractLoanItemProcessor.java} |  13 +-
 ...ItemReader.java => AbstractLoanItemReader.java} |  21 +--
 ...ItemWriter.java => AbstractLoanItemWriter.java} |   7 +-
 ...nstant.java => InlineCOBLoanItemProcessor.java} |  19 +--
 ...temWriter.java => InlineCOBLoanItemReader.java} |  33 ++--
 ...BConstant.java => InlineCOBLoanItemWriter.java} |  18 +--
 .../InlineLoanCOBBuildExecutionContextTasklet.java |  73 +++++++++
 .../apache/fineract/cob/loan/LoanCOBConstant.java  |   1 +
 .../cob/loan/LoanCOBWorkerConfiguration.java       |   6 +-
 .../fineract/cob/loan/LoanInlineCOBConfig.java     | 119 ++++++++++++++
 .../fineract/cob/loan/LoanItemProcessor.java       |  36 +----
 .../apache/fineract/cob/loan/LoanItemReader.java   |  49 +-----
 .../apache/fineract/cob/loan/LoanItemWriter.java   |  23 +--
 .../service/BusinessStepConfigUpdateHandler.java   |   9 +-
 .../service/InlineLoanCOBExecutionDataParser.java  |  53 ++++++
 .../service/InlineLoanCOBExecutorServiceImpl.java  | 177 +++++++++++++++++++++
 .../fineract/commands/domain/CommandSource.java    |   7 +
 .../fineract/commands/domain/CommandWrapper.java   |  10 +-
 .../commands/service/CommandWrapperBuilder.java    |  11 +-
 ...folioCommandSourceWritePlatformServiceImpl.java |   4 +-
 .../importhandler/center/CenterImportHandler.java  |   2 +-
 .../importhandler/group/GroupImportHandler.java    |   2 +-
 .../infrastructure/core/api/JsonCommand.java       |  31 ++--
 ...ccountLockCannotBeOverruledExceptionMapper.java |  45 ++++++
 .../LoanNotFoundExceptionMapper.java               |  45 ++++++
 .../serialization/GoogleGsonSerializerHelper.java  |   4 +
 ...tBureauIntegrationWritePlatformServiceImpl.java |   2 +-
 .../infrastructure/jobs/api/InlineJobResource.java |  69 ++++++++
 .../jobs/api/InlineJobResourceSwagger.java}        |  28 ++--
 .../jobs/domain/CustomJobParameter.java}           |  28 ++--
 .../jobs/domain/CustomJobParameterRepository.java} |  17 +-
 .../jobs/exception/JobNotFoundException.java       |   5 +
 .../jobs/service/InlineExecutorService.java}       |  17 +-
 .../jobs/service/InlineJobExecuteHandler.java}     |  26 ++-
 .../infrastructure/jobs/service/InlineJobType.java |  43 +++++
 .../springbatch/SpringBatchJobConstants.java}      |  16 +-
 .../interoperation/api/InteropWrapperBuilder.java  |   2 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |   2 +-
 .../service/ShareAccountCommandsServiceImpl.java   |   2 +-
 .../service/ShareProductCommandsServiceImpl.java   |   2 +-
 .../db/changelog/tenant/changelog-tenant.xml       |   3 +
 .../0058_add_execute_inline_cob_permission.xml     |  32 ++++
 .../0059_add_spring_batch_loan_id_list_table.xml   |  35 ++++
 .../parts/0060_add_job_name_to_command_source.xml  |  29 ++++
 .../listener/LoanItemListenerStepDefinitions.java  |   3 +-
 .../BusinessStepConfigUpdateHandlerTest.java       |  80 ----------
 .../InlineLoanCOBExecutorServiceImplTest.java      |  61 +++++++
 .../CommandHandlerProviderStepDefinitions.java     |   2 +-
 53 files changed, 1019 insertions(+), 387 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/data/LoanIdsResponseDTO.java
similarity index 60%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/data/LoanIdsResponseDTO.java
index 067eaf05f..76d45c4eb 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/data/LoanIdsResponseDTO.java
@@ -16,19 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.cob.data;
 
-public final class LoanCOBConstant {
+import java.util.List;
+import lombok.Data;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+@Data
+public class LoanIdsResponseDTO {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
-
-    }
+    private List<Long> loanIds;
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountLockCannotBeOverruledException.java
similarity index 60%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountLockCannotBeOverruledException.java
index 067eaf05f..686ae4c86 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/exceptions/LoanAccountLockCannotBeOverruledException.java
@@ -16,19 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.cob.exceptions;
 
-public final class LoanCOBConstant {
-
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
-
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
+public class LoanAccountLockCannotBeOverruledException extends 
RuntimeException {
 
+    public LoanAccountLockCannotBeOverruledException(String message) {
+        super(message);
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/LoanItemListener.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/AbstractLoanItemListener.java
similarity index 96%
rename from 
fineract-provider/src/main/java/org/apache/fineract/cob/listener/LoanItemListener.java
rename to 
fineract-provider/src/main/java/org/apache/fineract/cob/listener/AbstractLoanItemListener.java
index 12a55ae4b..e8246ccd8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/listener/LoanItemListener.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/AbstractLoanItemListener.java
@@ -44,7 +44,7 @@ import 
org.springframework.transaction.support.TransactionTemplate;
 
 @Slf4j
 @RequiredArgsConstructor
-public class LoanItemListener {
+public abstract class AbstractLoanItemListener {
 
     private final LoanAccountLockRepository accountLockRepository;
 
@@ -57,8 +57,7 @@ public class LoanItemListener {
             @Override
             protected void doInTransactionWithoutResult(@NotNull 
TransactionStatus status) {
                 for (Long loanId : loanIds) {
-                    Optional<LoanAccountLock> loanAccountLock = 
accountLockRepository.findByLoanIdAndLockOwner(loanId,
-                            LockOwner.LOAN_COB_CHUNK_PROCESSING);
+                    Optional<LoanAccountLock> loanAccountLock = 
accountLockRepository.findByLoanIdAndLockOwner(loanId, getLockOwner());
                     loanAccountLock.ifPresent(
                             accountLock -> 
accountLock.setError(String.format(msg, loanId), 
ThrowableSerialization.serialize(e)));
                 }
@@ -101,4 +100,7 @@ public class LoanItemListener {
     public void onSkipInWrite(@NotNull Loan item, @NotNull Exception e) {
         log.warn("Skipping was triggered during writing of Loan (id={})", 
item.getId());
     }
+
+    protected abstract LockOwner getLockOwner();
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/ChunkProcessingLoanItemListener.java
similarity index 57%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/listener/ChunkProcessingLoanItemListener.java
index 067eaf05f..378083684 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/ChunkProcessingLoanItemListener.java
@@ -16,19 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.cob.listener;
 
-public final class LoanCOBConstant {
+import org.apache.fineract.cob.domain.LoanAccountLockRepository;
+import org.apache.fineract.cob.domain.LockOwner;
+import org.springframework.transaction.support.TransactionTemplate;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+public class ChunkProcessingLoanItemListener extends AbstractLoanItemListener {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
+    public ChunkProcessingLoanItemListener(LoanAccountLockRepository 
accountLockRepository, TransactionTemplate transactionTemplate) {
+        super(accountLockRepository, transactionTemplate);
+    }
 
+    @Override
+    protected LockOwner getLockOwner() {
+        return LockOwner.LOAN_COB_CHUNK_PROCESSING;
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/InlineCOBLoanItemListener.java
similarity index 57%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/listener/InlineCOBLoanItemListener.java
index 067eaf05f..cedeab776 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/listener/InlineCOBLoanItemListener.java
@@ -16,19 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.cob.listener;
 
-public final class LoanCOBConstant {
+import org.apache.fineract.cob.domain.LoanAccountLockRepository;
+import org.apache.fineract.cob.domain.LockOwner;
+import org.springframework.transaction.support.TransactionTemplate;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+public class InlineCOBLoanItemListener extends AbstractLoanItemListener {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
+    public InlineCOBLoanItemListener(LoanAccountLockRepository 
accountLockRepository, TransactionTemplate transactionTemplate) {
+        super(accountLockRepository, transactionTemplate);
+    }
 
+    @Override
+    protected LockOwner getLockOwner() {
+        return LockOwner.LOAN_INLINE_COB_PROCESSING;
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemProcessor.java
similarity index 83%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemProcessor.java
index bd88f5b15..9c936f991 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemProcessor.java
@@ -19,7 +19,9 @@
 package org.apache.fineract.cob.loan;
 
 import java.util.TreeMap;
+import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.cob.COBBusinessStepService;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -27,25 +29,20 @@ import org.jetbrains.annotations.NotNull;
 import org.springframework.batch.core.ExitStatus;
 import org.springframework.batch.core.StepExecution;
 import org.springframework.batch.core.annotation.AfterStep;
-import org.springframework.batch.core.annotation.BeforeStep;
 import org.springframework.batch.item.ExecutionContext;
 import org.springframework.batch.item.ItemProcessor;
 
 @RequiredArgsConstructor
 @Slf4j
-public class LoanItemProcessor implements ItemProcessor<Loan, Loan> {
+public abstract class AbstractLoanItemProcessor implements ItemProcessor<Loan, 
Loan> {
 
     private final COBBusinessStepService cobBusinessStepService;
-    private StepExecution stepExecution;
 
-    @BeforeStep
-    public void beforeStep(@NotNull StepExecution stepExecution) {
-        this.stepExecution = stepExecution;
-    }
+    @Setter(AccessLevel.PROTECTED)
+    private ExecutionContext executionContext;
 
     @Override
     public Loan process(@NotNull Loan item) throws Exception {
-        ExecutionContext executionContext = 
stepExecution.getExecutionContext();
         TreeMap<Long, String> businessStepMap = (TreeMap<Long, String>) 
executionContext.get(LoanCOBConstant.BUSINESS_STEP_MAP);
 
         return cobBusinessStepService.run(businessStepMap, item);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemReader.java
similarity index 77%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemReader.java
index e9336f00c..3a4499185 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemReader.java
@@ -18,9 +18,10 @@
  */
 package org.apache.fineract.cob.loan;
 
-import java.util.ArrayList;
 import java.util.List;
+import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.cob.exceptions.LoanAccountWasAlreadyLocked;
 import org.apache.fineract.cob.exceptions.LoanReadException;
@@ -31,28 +32,20 @@ import org.jetbrains.annotations.NotNull;
 import org.springframework.batch.core.ExitStatus;
 import org.springframework.batch.core.StepExecution;
 import org.springframework.batch.core.annotation.AfterStep;
-import org.springframework.batch.core.annotation.BeforeStep;
-import org.springframework.batch.item.ExecutionContext;
 import org.springframework.batch.item.ItemReader;
 
 @Slf4j
 @RequiredArgsConstructor
-public class LoanItemReader implements ItemReader<Loan> {
+public abstract class AbstractLoanItemReader implements ItemReader<Loan> {
 
     private final LoanRepository loanRepository;
+
+    @Setter(AccessLevel.PROTECTED)
     private List<Long> alreadyLockedAccounts;
+    @Setter(AccessLevel.PROTECTED)
     private List<Long> remainingData;
     private Long loanId;
 
-    @BeforeStep
-    public void beforeStep(@NotNull StepExecution stepExecution) {
-        ExecutionContext executionContext = 
stepExecution.getExecutionContext();
-        ExecutionContext jobExecutionContext = 
stepExecution.getJobExecution().getExecutionContext();
-        List<Long> loanIds = (List<Long>) 
executionContext.get(LoanCOBConstant.LOAN_IDS);
-        alreadyLockedAccounts = (List<Long>) 
jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS);
-        remainingData = new ArrayList<>(loanIds);
-    }
-
     @Override
     public Loan read() throws Exception {
         try {
@@ -61,7 +54,6 @@ public class LoanItemReader implements ItemReader<Loan> {
                 if (alreadyLockedAccounts != null && 
alreadyLockedAccounts.remove(loanId)) {
                     throw new LoanAccountWasAlreadyLocked(loanId);
                 }
-
                 return loanRepository.findById(loanId).orElseThrow(() -> new 
LoanNotFoundException(loanId));
             }
         } catch (Exception e) {
@@ -75,4 +67,5 @@ public class LoanItemReader implements ItemReader<Loan> {
     public ExitStatus afterStep(@NotNull StepExecution stepExecution) {
         return ExitStatus.COMPLETED;
     }
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemWriter.java
similarity index 91%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemWriter.java
index bb5d2e6e9..95dc8953b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AbstractLoanItemWriter.java
@@ -30,7 +30,7 @@ import 
org.springframework.batch.item.data.RepositoryItemWriter;
 
 @Slf4j
 @RequiredArgsConstructor
-public class LoanItemWriter extends RepositoryItemWriter<Loan> {
+public abstract class AbstractLoanItemWriter extends 
RepositoryItemWriter<Loan> {
 
     private final LoanAccountLockRepository accountLockRepository;
 
@@ -38,8 +38,9 @@ public class LoanItemWriter extends 
RepositoryItemWriter<Loan> {
     public void write(@NotNull List<? extends Loan> items) throws Exception {
         super.write(items);
         List<Long> loanIds = 
items.stream().map(AbstractPersistableCustom::getId).toList();
-
-        accountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, 
LockOwner.LOAN_COB_CHUNK_PROCESSING);
+        accountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, 
getLockOwner());
     }
 
+    protected abstract LockOwner getLockOwner();
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemProcessor.java
similarity index 61%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemProcessor.java
index 067eaf05f..88e365373 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemProcessor.java
@@ -18,17 +18,18 @@
  */
 package org.apache.fineract.cob.loan;
 
-public final class LoanCOBConstant {
+import org.apache.fineract.cob.COBBusinessStepService;
+import org.springframework.batch.core.StepExecution;
+import org.springframework.batch.core.annotation.BeforeStep;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+public class InlineCOBLoanItemProcessor extends AbstractLoanItemProcessor {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
+    public InlineCOBLoanItemProcessor(COBBusinessStepService 
cobBusinessStepService) {
+        super(cobBusinessStepService);
+    }
 
+    @BeforeStep
+    public void beforeStep(StepExecution stepExecution) {
+        
setExecutionContext(stepExecution.getJobExecution().getExecutionContext());
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemReader.java
similarity index 51%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemReader.java
index bb5d2e6e9..4da7b1398 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemReader.java
@@ -18,28 +18,25 @@
  */
 package org.apache.fineract.cob.loan;
 
+import java.util.ArrayList;
 import java.util.List;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fineract.cob.domain.LoanAccountLockRepository;
-import org.apache.fineract.cob.domain.LockOwner;
-import 
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
-import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
 import org.jetbrains.annotations.NotNull;
-import org.springframework.batch.item.data.RepositoryItemWriter;
+import org.springframework.batch.core.StepExecution;
+import org.springframework.batch.core.annotation.BeforeStep;
+import org.springframework.batch.item.ExecutionContext;
 
-@Slf4j
-@RequiredArgsConstructor
-public class LoanItemWriter extends RepositoryItemWriter<Loan> {
+public class InlineCOBLoanItemReader extends AbstractLoanItemReader {
 
-    private final LoanAccountLockRepository accountLockRepository;
-
-    @Override
-    public void write(@NotNull List<? extends Loan> items) throws Exception {
-        super.write(items);
-        List<Long> loanIds = 
items.stream().map(AbstractPersistableCustom::getId).toList();
-
-        accountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, 
LockOwner.LOAN_COB_CHUNK_PROCESSING);
+    public InlineCOBLoanItemReader(LoanRepository loanRepository) {
+        super(loanRepository);
     }
 
+    @BeforeStep
+    @SuppressWarnings({ "unchecked" })
+    public void beforeStep(@NotNull StepExecution stepExecution) {
+        ExecutionContext executionContext = 
stepExecution.getJobExecution().getExecutionContext();
+        List<Long> loanIds = (List<Long>) 
executionContext.get(LoanCOBConstant.LOAN_IDS);
+        setRemainingData(new ArrayList<>(loanIds));
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemWriter.java
similarity index 64%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemWriter.java
index 067eaf05f..9092cede0 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineCOBLoanItemWriter.java
@@ -18,17 +18,17 @@
  */
 package org.apache.fineract.cob.loan;
 
-public final class LoanCOBConstant {
+import org.apache.fineract.cob.domain.LoanAccountLockRepository;
+import org.apache.fineract.cob.domain.LockOwner;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+public class InlineCOBLoanItemWriter extends AbstractLoanItemWriter {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
+    public InlineCOBLoanItemWriter(LoanAccountLockRepository 
accountLockRepository) {
+        super(accountLockRepository);
+    }
 
+    @Override
+    protected LockOwner getLockOwner() {
+        return LockOwner.LOAN_INLINE_COB_PROCESSING;
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineLoanCOBBuildExecutionContextTasklet.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineLoanCOBBuildExecutionContextTasklet.java
new file mode 100644
index 000000000..37bf40e51
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/InlineLoanCOBBuildExecutionContextTasklet.java
@@ -0,0 +1,73 @@
+/**
+ * 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.cob.loan;
+
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.cob.COBBusinessStepService;
+import 
org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
+import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameter;
+import 
org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
+import org.apache.fineract.infrastructure.springbatch.SpringBatchJobConstants;
+import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
+import org.springframework.batch.core.StepContribution;
+import org.springframework.batch.core.scope.context.ChunkContext;
+import org.springframework.batch.core.step.tasklet.Tasklet;
+import org.springframework.batch.repeat.RepeatStatus;
+import org.springframework.beans.factory.InitializingBean;
+
+@Slf4j
+@RequiredArgsConstructor
+public class InlineLoanCOBBuildExecutionContextTasklet implements Tasklet, 
InitializingBean {
+
+    private final GoogleGsonSerializerHelper gsonFactory;
+    private final COBBusinessStepService cobBusinessStepService;
+    private final CustomJobParameterRepository customJobParameterRepository;
+
+    private Gson gson;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.gson = gsonFactory.createSimpleGson();
+    }
+
+    @Override
+    public RepeatStatus execute(StepContribution contribution, ChunkContext 
chunkContext) throws Exception {
+        TreeMap<Long, String> cobBusinessStepMap = 
cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class,
+                LoanCOBConstant.LOAN_COB_JOB_NAME);
+        
contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.LOAN_IDS,
 getLoanIdsFromJobParameters(chunkContext));
+        
contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_STEP_MAP,
 cobBusinessStepMap);
+
+        return RepeatStatus.FINISHED;
+    }
+
+    private List<Long> getLoanIdsFromJobParameters(ChunkContext chunkContext) {
+        Long customJobParameterId = (Long) 
chunkContext.getStepContext().getJobParameters()
+                .get(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY);
+        CustomJobParameter customJobParameter = 
customJobParameterRepository.findById(customJobParameterId)
+                .orElseThrow(() -> new 
LoanNotFoundException(customJobParameterId));
+        String parameterJson = customJobParameter.getParameterJson();
+        return gson.fromJson(parameterJson, new TypeToken<ArrayList<Long>>() 
{}.getType());
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
index 067eaf05f..ccb889b12 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
@@ -27,6 +27,7 @@ public final class LoanCOBConstant {
     public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
 
     public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
+    public static final String INLINE_LOAN_COB_JOB_NAME = "INLINE_LOAN_COB";
 
     private LoanCOBConstant() {
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java
index c74006872..0a3bd5a33 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBWorkerConfiguration.java
@@ -21,7 +21,7 @@ package org.apache.fineract.cob.loan;
 import org.apache.fineract.cob.COBBusinessStepService;
 import org.apache.fineract.cob.common.InitialisationTasklet;
 import org.apache.fineract.cob.domain.LoanAccountLockRepository;
-import org.apache.fineract.cob.listener.LoanItemListener;
+import org.apache.fineract.cob.listener.ChunkProcessingLoanItemListener;
 import org.apache.fineract.infrastructure.jobs.service.JobName;
 import org.apache.fineract.infrastructure.springbatch.PropertyService;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -105,8 +105,8 @@ public class LoanCOBWorkerConfiguration {
     }
 
     @Bean
-    public LoanItemListener loanItemListener() {
-        return new LoanItemListener(accountLockRepository, 
transactionTemplate);
+    public ChunkProcessingLoanItemListener loanItemListener() {
+        return new ChunkProcessingLoanItemListener(accountLockRepository, 
transactionTemplate);
     }
 
     @Bean
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java
new file mode 100644
index 000000000..cbaa5bc0d
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanInlineCOBConfig.java
@@ -0,0 +1,119 @@
+/**
+ * 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.cob.loan;
+
+import org.apache.fineract.cob.COBBusinessStepService;
+import org.apache.fineract.cob.domain.LoanAccountLockRepository;
+import org.apache.fineract.cob.listener.InlineCOBLoanItemListener;
+import 
org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
+import 
org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.infrastructure.springbatch.PropertyService;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.Step;
+import 
org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
+import 
org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
+import org.springframework.batch.core.launch.support.RunIdIncrementer;
+import 
org.springframework.batch.core.listener.ExecutionContextPromotionListener;
+import 
org.springframework.batch.integration.config.annotation.EnableBatchIntegration;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.support.TransactionTemplate;
+
+@Configuration
+@EnableBatchIntegration
+public class LoanInlineCOBConfig {
+
+    @Autowired
+    private JobBuilderFactory jobBuilderFactory;
+    @Autowired
+    private StepBuilderFactory stepBuilderFactory;
+    @Autowired
+    private PropertyService propertyService;
+    @Autowired
+    private LoanRepository loanRepository;
+    @Autowired
+    private COBBusinessStepService cobBusinessStepService;
+    @Autowired
+    private LoanAccountLockRepository accountLockRepository;
+    @Autowired
+    private TransactionTemplate transactionTemplate;
+    @Autowired
+    private CustomJobParameterRepository loanIdListRepository;
+    @Autowired
+    private GoogleGsonSerializerHelper googleGsonSerializerHelper;
+
+    @Bean
+    public InlineLoanCOBBuildExecutionContextTasklet 
inlineLoanCOBBuildExecutionContextTasklet() {
+        return new 
InlineLoanCOBBuildExecutionContextTasklet(googleGsonSerializerHelper, 
cobBusinessStepService, loanIdListRepository);
+    }
+
+    @Bean
+    protected Step inlineCOBBuildExecutionContextStep() {
+        return stepBuilderFactory.get("Inline COB build execution context 
step").tasklet(inlineLoanCOBBuildExecutionContextTasklet())
+                .listener(inlineCobPromotionListener()).build();
+    }
+
+    @Bean
+    public Step inlineLoanCOBStep() {
+        return stepBuilderFactory.get("Inline Loan COB Step").<Loan, 
Loan>chunk(propertyService.getChunkSize(JobName.LOAN_COB.name()))
+                
.reader(inlineCobWorkerItemReader()).processor(inlineCobWorkerItemProcessor()).writer(inlineCobWorkerItemWriter())
+                .listener(inlineCobLoanItemListener()).build();
+    }
+
+    @Bean(name = "loanInlineCOBJob")
+    public Job loanInlineCOBJob() {
+        return jobBuilderFactory.get(LoanCOBConstant.INLINE_LOAN_COB_JOB_NAME) 
//
+                
.start(inlineCOBBuildExecutionContextStep()).next(inlineLoanCOBStep()) //
+                .incrementer(new RunIdIncrementer()) //
+                .build();
+    }
+
+    @Bean
+    public InlineCOBLoanItemReader inlineCobWorkerItemReader() {
+        return new InlineCOBLoanItemReader(loanRepository);
+    }
+
+    @Bean
+    public InlineCOBLoanItemProcessor inlineCobWorkerItemProcessor() {
+        return new InlineCOBLoanItemProcessor(cobBusinessStepService);
+    }
+
+    @Bean
+    public InlineCOBLoanItemWriter inlineCobWorkerItemWriter() {
+        InlineCOBLoanItemWriter repositoryItemWriter = new 
InlineCOBLoanItemWriter(accountLockRepository);
+        repositoryItemWriter.setRepository(loanRepository);
+        return repositoryItemWriter;
+    }
+
+    @Bean
+    public InlineCOBLoanItemListener inlineCobLoanItemListener() {
+        return new InlineCOBLoanItemListener(accountLockRepository, 
transactionTemplate);
+    }
+
+    @Bean
+    public ExecutionContextPromotionListener inlineCobPromotionListener() {
+        ExecutionContextPromotionListener listener = new 
ExecutionContextPromotionListener();
+        listener.setKeys(new String[] { LoanCOBConstant.LOAN_IDS, 
LoanCOBConstant.BUSINESS_STEP_MAP });
+        return listener;
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java
index bd88f5b15..daea5f970 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemProcessor.java
@@ -18,42 +18,18 @@
  */
 package org.apache.fineract.cob.loan;
 
-import java.util.TreeMap;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.cob.COBBusinessStepService;
-import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import org.jetbrains.annotations.NotNull;
-import org.springframework.batch.core.ExitStatus;
 import org.springframework.batch.core.StepExecution;
-import org.springframework.batch.core.annotation.AfterStep;
 import org.springframework.batch.core.annotation.BeforeStep;
-import org.springframework.batch.item.ExecutionContext;
-import org.springframework.batch.item.ItemProcessor;
 
-@RequiredArgsConstructor
-@Slf4j
-public class LoanItemProcessor implements ItemProcessor<Loan, Loan> {
+public class LoanItemProcessor extends AbstractLoanItemProcessor {
 
-    private final COBBusinessStepService cobBusinessStepService;
-    private StepExecution stepExecution;
-
-    @BeforeStep
-    public void beforeStep(@NotNull StepExecution stepExecution) {
-        this.stepExecution = stepExecution;
+    public LoanItemProcessor(COBBusinessStepService cobBusinessStepService) {
+        super(cobBusinessStepService);
     }
 
-    @Override
-    public Loan process(@NotNull Loan item) throws Exception {
-        ExecutionContext executionContext = 
stepExecution.getExecutionContext();
-        TreeMap<Long, String> businessStepMap = (TreeMap<Long, String>) 
executionContext.get(LoanCOBConstant.BUSINESS_STEP_MAP);
-
-        return cobBusinessStepService.run(businessStepMap, item);
-    }
-
-    @AfterStep
-    public ExitStatus afterStep(@NotNull StepExecution stepExecution) {
-        return ExitStatus.COMPLETED;
+    @BeforeStep
+    public void beforeStep(StepExecution stepExecution) {
+        setExecutionContext(stepExecution.getExecutionContext());
     }
-
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java
index e9336f00c..8237ba928 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemReader.java
@@ -20,59 +20,26 @@ package org.apache.fineract.cob.loan;
 
 import java.util.ArrayList;
 import java.util.List;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fineract.cob.exceptions.LoanAccountWasAlreadyLocked;
-import org.apache.fineract.cob.exceptions.LoanReadException;
-import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import org.jetbrains.annotations.NotNull;
-import org.springframework.batch.core.ExitStatus;
 import org.springframework.batch.core.StepExecution;
-import org.springframework.batch.core.annotation.AfterStep;
 import org.springframework.batch.core.annotation.BeforeStep;
 import org.springframework.batch.item.ExecutionContext;
-import org.springframework.batch.item.ItemReader;
 
-@Slf4j
-@RequiredArgsConstructor
-public class LoanItemReader implements ItemReader<Loan> {
+public class LoanItemReader extends AbstractLoanItemReader {
 
-    private final LoanRepository loanRepository;
-    private List<Long> alreadyLockedAccounts;
-    private List<Long> remainingData;
-    private Long loanId;
+    public LoanItemReader(LoanRepository loanRepository) {
+        super(loanRepository);
+    }
 
     @BeforeStep
+    @SuppressWarnings({ "unchecked" })
     public void beforeStep(@NotNull StepExecution stepExecution) {
+
         ExecutionContext executionContext = 
stepExecution.getExecutionContext();
         ExecutionContext jobExecutionContext = 
stepExecution.getJobExecution().getExecutionContext();
         List<Long> loanIds = (List<Long>) 
executionContext.get(LoanCOBConstant.LOAN_IDS);
-        alreadyLockedAccounts = (List<Long>) 
jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS);
-        remainingData = new ArrayList<>(loanIds);
-    }
-
-    @Override
-    public Loan read() throws Exception {
-        try {
-            if (remainingData.size() > 0) {
-                loanId = remainingData.remove(0);
-                if (alreadyLockedAccounts != null && 
alreadyLockedAccounts.remove(loanId)) {
-                    throw new LoanAccountWasAlreadyLocked(loanId);
-                }
-
-                return loanRepository.findById(loanId).orElseThrow(() -> new 
LoanNotFoundException(loanId));
-            }
-        } catch (Exception e) {
-            throw new LoanReadException(loanId, e);
-        }
-        return null;
-
-    }
-
-    @AfterStep
-    public ExitStatus afterStep(@NotNull StepExecution stepExecution) {
-        return ExitStatus.COMPLETED;
+        setAlreadyLockedAccounts((List<Long>) 
jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_LOAN_IDS));
+        setRemainingData(new ArrayList<>(loanIds));
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
index bb5d2e6e9..709e2bdd8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanItemWriter.java
@@ -18,28 +18,17 @@
  */
 package org.apache.fineract.cob.loan;
 
-import java.util.List;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.cob.domain.LoanAccountLockRepository;
 import org.apache.fineract.cob.domain.LockOwner;
-import 
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
-import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import org.jetbrains.annotations.NotNull;
-import org.springframework.batch.item.data.RepositoryItemWriter;
 
-@Slf4j
-@RequiredArgsConstructor
-public class LoanItemWriter extends RepositoryItemWriter<Loan> {
+public class LoanItemWriter extends AbstractLoanItemWriter {
 
-    private final LoanAccountLockRepository accountLockRepository;
+    public LoanItemWriter(LoanAccountLockRepository accountLockRepository) {
+        super(accountLockRepository);
+    }
 
     @Override
-    public void write(@NotNull List<? extends Loan> items) throws Exception {
-        super.write(items);
-        List<Long> loanIds = 
items.stream().map(AbstractPersistableCustom::getId).toList();
-
-        accountLockRepository.deleteByLoanIdInAndLockOwner(loanIds, 
LockOwner.LOAN_COB_CHUNK_PROCESSING);
+    protected LockOwner getLockOwner() {
+        return LockOwner.LOAN_COB_CHUNK_PROCESSING;
     }
-
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandler.java
index c552a3fea..b2922369c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandler.java
@@ -18,8 +18,6 @@
  */
 package org.apache.fineract.cob.service;
 
-import com.google.common.base.Splitter;
-import java.util.List;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.cob.exceptions.BusinessStepException;
 import org.apache.fineract.cob.exceptions.BusinessStepNotBelongsToJobException;
@@ -40,11 +38,6 @@ public class BusinessStepConfigUpdateHandler implements 
NewCommandSourceHandler
     @Override
     @Transactional
     public CommandProcessingResult processCommand(JsonCommand command) throws 
BusinessStepNotBelongsToJobException, BusinessStepException {
-        List<String> split = Splitter.on("/").splitToList(command.getUrl());
-        String jobName = split.get(split.size() - 2);
-        if ("null".equals(jobName)) {
-            jobName = null;
-        }
-        return configJobParameterService.updateStepConfigByJobName(command, 
jobName);
+        return configJobParameterService.updateStepConfigByJobName(command, 
command.getJobName());
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutionDataParser.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutionDataParser.java
new file mode 100644
index 000000000..17c4afd32
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutionDataParser.java
@@ -0,0 +1,53 @@
+/**
+ * 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.cob.service;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.util.Arrays;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class InlineLoanCOBExecutionDataParser {
+
+    private final FromJsonHelper jsonHelper;
+
+    public List<Long> parseExecution(JsonCommand command) {
+        JsonObject element = extractJsonObject(command);
+        String[] loanArray = jsonHelper.extractArrayNamed("loanIds", element);
+        return Arrays.stream(loanArray).map(Long::parseLong).toList();
+    }
+
+    private JsonObject extractJsonObject(JsonCommand command) {
+        String json = command.json();
+        if (StringUtils.isBlank(json)) {
+            throw new InvalidJsonException();
+        }
+
+        final JsonElement element = jsonHelper.parse(json);
+        return element.getAsJsonObject();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java
new file mode 100644
index 000000000..b8e10bfaa
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java
@@ -0,0 +1,177 @@
+/**
+ * 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.cob.service;
+
+import static 
org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW;
+
+import com.google.gson.Gson;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.cob.domain.LoanAccountLock;
+import org.apache.fineract.cob.domain.LoanAccountLockRepository;
+import org.apache.fineract.cob.domain.LockOwner;
+import 
org.apache.fineract.cob.exceptions.LoanAccountLockCannotBeOverruledException;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import 
org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import 
org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
+import 
org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
+import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameter;
+import 
org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
+import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
+import org.apache.fineract.infrastructure.jobs.service.InlineExecutorService;
+import org.apache.fineract.infrastructure.springbatch.SpringBatchJobConstants;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.batch.core.BatchStatus;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobParameter;
+import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.JobParametersBuilder;
+import org.springframework.batch.core.configuration.JobLocator;
+import org.springframework.batch.core.explore.JobExplorer;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.core.launch.NoSuchJobException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import 
org.springframework.transaction.support.TransactionCallbackWithoutResult;
+import org.springframework.transaction.support.TransactionTemplate;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class InlineLoanCOBExecutorServiceImpl implements 
InlineExecutorService, InitializingBean {
+
+    private static final String JOB_EXECUTION_FAILED_MESSAGE = "Job execution 
failed for job with name: ";
+
+    private final GoogleGsonSerializerHelper gsonFactory;
+    private final LoanAccountLockRepository loanAccountLockRepository;
+    private final InlineLoanCOBExecutionDataParser dataParser;
+    private final JobLauncher jobLauncher;
+    private final JobLocator jobLocator;
+    private final JobExplorer jobExplorer;
+    private final TransactionTemplate transactionTemplate;
+    private final CustomJobParameterRepository customJobParameterRepository;
+
+    private Gson gson;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.gson = gsonFactory.createSimpleGson();
+    }
+
+    @Override
+    @Transactional(propagation = Propagation.NOT_SUPPORTED)
+    public CommandProcessingResult executeInlineJob(JsonCommand command, 
String jobName) throws LoanAccountLockCannotBeOverruledException {
+        List<Long> loanIds = dataParser.parseExecution(command);
+        lockLoanAccounts(loanIds);
+        execute(loanIds, jobName);
+        return new 
CommandProcessingResultBuilder().withCommandId(command.commandId()).build();
+    }
+
+    private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds) {
+        List<LoanAccountLock> loanAccountLocks = new ArrayList<>();
+        List<Long> alreadyLockedLoanIds = new ArrayList<>();
+        loanIds.forEach(loanId -> {
+            Optional<LoanAccountLock> loanLockOptional = 
loanAccountLockRepository.findById(loanId);
+            if (loanLockOptional.isPresent()) {
+                LoanAccountLock loanAccountLock = loanLockOptional.get();
+                if (isLockOverrulable(loanAccountLock)) {
+                    loanAccountLocks.add(loanAccountLock);
+                } else {
+                    alreadyLockedLoanIds.add(loanId);
+                }
+            } else {
+                loanAccountLocks.add(new LoanAccountLock(loanId, 
LockOwner.LOAN_INLINE_COB_PROCESSING));
+            }
+        });
+        if (!alreadyLockedLoanIds.isEmpty()) {
+            String message = "There is a hard lock on the loan account without 
any error, so it can't be overruled.";
+            String loanIdsMessage = " Locked loan IDs: " + 
alreadyLockedLoanIds;
+            throw new LoanAccountLockCannotBeOverruledException(message + 
loanIdsMessage);
+        }
+
+        return loanAccountLocks;
+    }
+
+    private void execute(List<Long> loanIds, String jobName) {
+        Job inlineLoanCOBJob;
+        try {
+            inlineLoanCOBJob = jobLocator.getJob(jobName);
+        } catch (NoSuchJobException e) {
+            throw new JobNotFoundException(jobName, e);
+        }
+        JobParameters jobParameters = new 
JobParametersBuilder(jobExplorer).getNextJobParameters(inlineLoanCOBJob)
+                .addJobParameters(new 
JobParameters(getJobParametersMap(loanIds))).toJobParameters();
+        JobExecution jobExecution;
+        try {
+            jobExecution = jobLauncher.run(inlineLoanCOBJob, jobParameters);
+        } catch (Exception e) {
+            log.error("{}{}", JOB_EXECUTION_FAILED_MESSAGE, jobName, e);
+            throw new 
PlatformInternalServerException("error.msg.sheduler.job.execution.failed", 
JOB_EXECUTION_FAILED_MESSAGE, jobName, e);
+        }
+        if (!BatchStatus.COMPLETED.equals(jobExecution.getStatus())) {
+            log.error("{}{}", JOB_EXECUTION_FAILED_MESSAGE, jobName);
+            throw new 
PlatformInternalServerException("error.msg.sheduler.job.execution.failed", 
JOB_EXECUTION_FAILED_MESSAGE, jobName);
+        }
+    }
+
+    private Map<String, JobParameter> getJobParametersMap(List<Long> loanIds) {
+        // TODO: refactor for a more generic solution
+        String parameterJson = gson.toJson(loanIds);
+        CustomJobParameter customJobParameter = new CustomJobParameter();
+        customJobParameter.setParameterJson(parameterJson);
+        Long customJobParameterId = 
customJobParameterRepository.saveAndFlush(customJobParameter).getId();
+        Map<String, JobParameter> jobParameterMap = new HashMap<>();
+        
jobParameterMap.put(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY, new 
JobParameter(customJobParameterId));
+        return jobParameterMap;
+    }
+
+    private void lockLoanAccounts(List<Long> loanIds) {
+        transactionTemplate.setPropagationBehavior(PROPAGATION_REQUIRES_NEW);
+        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+
+            @Override
+            protected void doInTransactionWithoutResult(@NotNull 
TransactionStatus status) {
+                List<LoanAccountLock> loanAccountLocks = 
getLoanAccountLocks(loanIds);
+                loanAccountLocks.forEach(loanAccountLock -> {
+                    
loanAccountLock.setNewLockOwner(LockOwner.LOAN_INLINE_COB_PROCESSING);
+                    loanAccountLockRepository.save(loanAccountLock);
+                });
+            }
+        });
+    }
+
+    private boolean isLockOverrulable(LoanAccountLock loanAccountLock) {
+        if 
(LockOwner.LOAN_COB_PARTITIONING.equals(loanAccountLock.getLockOwner())) {
+            return true;
+        } else {
+            return StringUtils.isNotBlank(loanAccountLock.getError());
+        }
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
index bfd66e18a..60027871f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandSource.java
@@ -104,6 +104,9 @@ public class CommandSource extends 
AbstractPersistableCustom {
     @Column(name = "organisation_creditbureau_id")
     private Long organisationCreditBureauId;
 
+    @Column(name = "job_name")
+    private String jobName;
+
     public static CommandSource fullEntryFrom(final CommandWrapper wrapper, 
final JsonCommand command, final AppUser maker) {
         return new CommandSource(wrapper.actionName(), wrapper.entityName(), 
wrapper.getHref(), command.entityId(), command.subentityId(),
                 command.json(), maker);
@@ -138,6 +141,10 @@ public class CommandSource extends 
AbstractPersistableCustom {
         return this.organisationCreditBureauId;
     }
 
+    public String getJobName() {
+        return this.jobName;
+    }
+
     public void setOrganisationCreditBureauId(Long organisationCreditBureauId) 
{
         this.organisationCreditBureauId = organisationCreditBureauId;
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
index 7dac6ee67..cb09f24f6 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandWrapper.java
@@ -40,6 +40,7 @@ public class CommandWrapper {
     private final Long productId;
     private final Long creditBureauId;
     private final Long organisationCreditBureauId;
+    private final String jobName;
 
     @SuppressWarnings("unused")
     private Long templateId;
@@ -80,12 +81,13 @@ public class CommandWrapper {
         this.productId = productId;
         this.creditBureauId = null;
         this.organisationCreditBureauId = null;
+        this.jobName = null;
     }
 
     public CommandWrapper(final Long officeId, final Long groupId, final Long 
clientId, final Long loanId, final Long savingsId,
             final String actionName, final String entityName, final Long 
entityId, final Long subentityId, final String href,
             final String json, final String transactionId, final Long 
productId, final Long templateId, final Long creditBureauId,
-            final Long organisationCreditBureauId) {
+            final Long organisationCreditBureauId, final String jobName) {
 
         this.commandId = null;
         this.officeId = officeId;
@@ -105,6 +107,7 @@ public class CommandWrapper {
         this.templateId = templateId;
         this.creditBureauId = creditBureauId;
         this.organisationCreditBureauId = organisationCreditBureauId;
+        this.jobName = jobName;
     }
 
     private CommandWrapper(final Long commandId, final String actionName, 
final String entityName, final Long resourceId,
@@ -129,6 +132,7 @@ public class CommandWrapper {
         this.productId = productId;
         this.creditBureauId = creditBureauId;
         this.organisationCreditBureauId = organisationCreditBureauId;
+        this.jobName = null;
     }
 
     public Long getCreditBureauId() {
@@ -338,4 +342,8 @@ public class CommandWrapper {
         return this.actionName.equalsIgnoreCase("UPDATE") && 
this.entityName.equalsIgnoreCase("DISBURSEMENTDETAIL")
                 && this.entityId == null;
     }
+
+    public String getJobName() {
+        return jobName;
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
 
b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index 28d523242..56892ae07 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -44,11 +44,12 @@ public class CommandWrapperBuilder {
     private Long templateId;
     private Long creditBureauId;
     private Long organisationCreditBureauId;
+    private String jobName;
 
     public CommandWrapper build() {
         return new CommandWrapper(this.officeId, this.groupId, this.clientId, 
this.loanId, this.savingsId, this.actionName, this.entityName,
                 this.entityId, this.subentityId, this.href, this.json, 
this.transactionId, this.productId, this.templateId,
-                this.creditBureauId, this.organisationCreditBureauId);
+                this.creditBureauId, this.organisationCreditBureauId, 
this.jobName);
     }
 
     public CommandWrapperBuilder updateCreditBureau() {
@@ -3562,7 +3563,15 @@ public class CommandWrapperBuilder {
         this.actionName = "UPDATE";
         this.entityName = "BATCH_BUSINESS_STEP";
         this.href = "/jobs/" + jobName + "/steps";
+        this.jobName = jobName;
         return this;
     }
 
+    public CommandWrapperBuilder executeInlineJob(String jobName) {
+        this.actionName = "EXECUTE";
+        this.entityName = "INLINE_JOB";
+        this.href = "/jobs/" + jobName + "/inline";
+        this.jobName = jobName;
+        return this;
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java
index 7c8ceb4e9..0552172e7 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java
@@ -85,7 +85,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl 
implements Portfolio
         command = JsonCommand.from(json, parsedCommand, 
this.fromApiJsonHelper, wrapper.getEntityName(), wrapper.getEntityId(),
                 wrapper.getSubentityId(), wrapper.getGroupId(), 
wrapper.getClientId(), wrapper.getLoanId(), wrapper.getSavingsId(),
                 wrapper.getTransactionId(), wrapper.getHref(), 
wrapper.getProductId(), wrapper.getCreditBureauId(),
-                wrapper.getOrganisationCreditBureauId());
+                wrapper.getOrganisationCreditBureauId(), wrapper.getJobName());
         while (numberOfRetries <= maxNumberOfRetries) {
             try {
                 result = 
this.processAndLogCommandService.executeCommand(wrapper, command, 
isApprovedByChecker);
@@ -137,7 +137,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl 
implements Portfolio
                 commandSourceInput.subresourceId(), 
commandSourceInput.getGroupId(), commandSourceInput.getClientId(),
                 commandSourceInput.getLoanId(), 
commandSourceInput.getSavingsId(), commandSourceInput.getTransactionId(),
                 commandSourceInput.getResourceGetUrl(), 
commandSourceInput.getProductId(), commandSourceInput.getCreditBureauId(),
-                commandSourceInput.getOrganisationCreditBureauId());
+                commandSourceInput.getOrganisationCreditBureauId(), 
commandSourceInput.getJobName());
 
         final boolean makerCheckerApproval = true;
         return this.processAndLogCommandService.executeCommand(wrapper, 
command, makerCheckerApproval);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/center/CenterImportHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/center/CenterImportHandler.java
index c2f0a6a43..d38440250 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/center/CenterImportHandler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/center/CenterImportHandler.java
@@ -262,7 +262,7 @@ public class CenterImportHandler implements ImportHandler {
         String payload = gsonBuilder.create().toJson(calendarData);
         CommandWrapper commandWrapper = new 
CommandWrapper(result.getOfficeId(), result.getGroupId(), result.getClientId(),
                 result.getLoanId(), result.getSavingsId(), null, null, null, 
null, null, payload, result.getTransactionId(),
-                result.getProductId(), null, null, null);
+                result.getProductId(), null, null, null, null);
         final CommandWrapper commandRequest = new CommandWrapperBuilder() //
                 .createCalendar(commandWrapper, 
TemplatePopulateImportConstants.CENTER_ENTITY_TYPE, result.getGroupId()) //
                 .withJson(payload) //
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/group/GroupImportHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/group/GroupImportHandler.java
index 3d166ee81..b8835a9a3 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/group/GroupImportHandler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/group/GroupImportHandler.java
@@ -241,7 +241,7 @@ public class GroupImportHandler implements ImportHandler {
         String payload = gsonBuilder.create().toJson(calendarData);
         CommandWrapper commandWrapper = new 
CommandWrapper(result.getOfficeId(), result.getGroupId(), result.getClientId(),
                 result.getLoanId(), result.getSavingsId(), null, null, null, 
null, null, payload, result.getTransactionId(),
-                result.getProductId(), null, null, null);
+                result.getProductId(), null, null, null, null);
         final CommandWrapper commandRequest = new CommandWrapperBuilder() //
                 .createCalendar(commandWrapper, 
TemplatePopulateImportConstants.CENTER_ENTITY_TYPE, result.getGroupId()) //
                 .withJson(payload) //
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
index 31e333935..49ebc937d 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
@@ -64,29 +64,31 @@ public final class JsonCommand {
     private final Long productId;
     private final Long creditBureauId;
     private final Long organisationCreditBureauId;
+    private final String jobName;
 
     public static JsonCommand from(final String jsonCommand, final JsonElement 
parsedCommand, final FromJsonHelper fromApiJsonHelper,
             final String entityName, final Long resourceId, final Long 
subresourceId, final Long groupId, final Long clientId,
             final Long loanId, final Long savingsId, final String 
transactionId, final String url, final Long productId,
-            final Long creditBureauId, final Long organisationCreditBureauId) {
+            final Long creditBureauId, final Long organisationCreditBureauId, 
final String jobName) {
         return new JsonCommand(null, jsonCommand, parsedCommand, 
fromApiJsonHelper, entityName, resourceId, subresourceId, groupId,
-                clientId, loanId, savingsId, transactionId, url, productId, 
creditBureauId, organisationCreditBureauId);
+                clientId, loanId, savingsId, transactionId, url, productId, 
creditBureauId, organisationCreditBureauId, jobName);
 
     }
 
     public static JsonCommand fromExistingCommand(final Long commandId, final 
String jsonCommand, final JsonElement parsedCommand,
             final FromJsonHelper fromApiJsonHelper, final String entityName, 
final Long resourceId, final Long subresourceId,
-            final String url, final Long productId, final Long creditBureauId, 
final Long organisationCreditBureauId) {
+            final String url, final Long productId, final Long creditBureauId, 
final Long organisationCreditBureauId,
+            final String jobName) {
         return new JsonCommand(commandId, jsonCommand, parsedCommand, 
fromApiJsonHelper, entityName, resourceId, subresourceId, null, null,
-                null, null, null, url, productId, creditBureauId, 
organisationCreditBureauId);
+                null, null, null, url, productId, creditBureauId, 
organisationCreditBureauId, jobName);
     }
 
     public static JsonCommand fromExistingCommand(final Long commandId, final 
String jsonCommand, final JsonElement parsedCommand,
             final FromJsonHelper fromApiJsonHelper, final String entityName, 
final Long resourceId, final Long subresourceId,
             final Long groupId, final Long clientId, final Long loanId, final 
Long savingsId, final String transactionId, final String url,
-            final Long productId, Long creditBureauId, final Long 
organisationCreditBureauId) {
+            final Long productId, Long creditBureauId, final Long 
organisationCreditBureauId, final String jobName) {
         return new JsonCommand(commandId, jsonCommand, parsedCommand, 
fromApiJsonHelper, entityName, resourceId, subresourceId, groupId,
-                clientId, loanId, savingsId, transactionId, url, productId, 
creditBureauId, organisationCreditBureauId);
+                clientId, loanId, savingsId, transactionId, url, productId, 
creditBureauId, organisationCreditBureauId, jobName);
 
     }
 
@@ -94,20 +96,22 @@ public final class JsonCommand {
         final String jsonCommand = 
command.fromApiJsonHelper.toJson(parsedCommand);
         return new JsonCommand(command.commandId, jsonCommand, parsedCommand, 
command.fromApiJsonHelper, command.entityName,
                 command.resourceId, command.subresourceId, command.groupId, 
command.clientId, command.loanId, command.savingsId,
-                command.transactionId, command.url, command.productId, 
command.creditBureauId, command.organisationCreditBureauId);
+                command.transactionId, command.url, command.productId, 
command.creditBureauId, command.organisationCreditBureauId,
+                command.jobName);
     }
 
     public static JsonCommand fromExistingCommand(JsonCommand command, final 
JsonElement parsedCommand, final Long clientId) {
         final String jsonCommand = 
command.fromApiJsonHelper.toJson(parsedCommand);
         return new JsonCommand(command.commandId, jsonCommand, parsedCommand, 
command.fromApiJsonHelper, command.entityName,
                 command.resourceId, command.subresourceId, command.groupId, 
clientId, command.loanId, command.savingsId,
-                command.transactionId, command.url, command.productId, 
command.creditBureauId, command.organisationCreditBureauId);
+                command.transactionId, command.url, command.productId, 
command.creditBureauId, command.organisationCreditBureauId,
+                command.jobName);
     }
 
     public JsonCommand(final Long commandId, final String jsonCommand, final 
JsonElement parsedCommand,
             final FromJsonHelper fromApiJsonHelper, final String entityName, 
final Long resourceId, final Long subresourceId,
             final Long groupId, final Long clientId, final Long loanId, final 
Long savingsId, final String transactionId, final String url,
-            final Long productId, final Long creditBureauId, final Long 
organisationCreditBureauId) {
+            final Long productId, final Long creditBureauId, final Long 
organisationCreditBureauId, final String jobName) {
 
         this.commandId = commandId;
         this.jsonCommand = jsonCommand;
@@ -125,6 +129,7 @@ public final class JsonCommand {
         this.productId = productId;
         this.creditBureauId = creditBureauId;
         this.organisationCreditBureauId = organisationCreditBureauId;
+        this.jobName = jobName;
     }
 
     public static JsonCommand fromJsonElement(final Long resourceId, final 
JsonElement parsedCommand) {
@@ -153,6 +158,7 @@ public final class JsonCommand {
         this.productId = null;
         this.creditBureauId = null;
         this.organisationCreditBureauId = null;
+        this.jobName = null;
     }
 
     public JsonCommand(final Long resourceId, final JsonElement parsedCommand, 
final FromJsonHelper fromApiJsonHelper) {
@@ -172,10 +178,11 @@ public final class JsonCommand {
         this.productId = null;
         this.creditBureauId = null;
         this.organisationCreditBureauId = null;
+        this.jobName = null;
     }
 
     public static JsonCommand from(final String jsonCommand) {
-        return new JsonCommand(null, jsonCommand, null, null, null, null, 
null, null, null, null, null, null, null, null, null, null);
+        return new JsonCommand(null, jsonCommand, null, null, null, null, 
null, null, null, null, null, null, null, null, null, null, null);
 
     }
 
@@ -255,6 +262,10 @@ public final class JsonCommand {
         return this.productId;
     }
 
+    public String getJobName() {
+        return this.jobName;
+    }
+
     private boolean differenceExists(final TemporalAccessor baseValue, final 
TemporalAccessor workingCopyValue) {
         boolean differenceExists = false;
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/LoanAccountLockCannotBeOverruledExceptionMapper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/LoanAccountLockCannotBeOverruledExceptionMapper.java
new file mode 100644
index 000000000..483f13c50
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/LoanAccountLockCannotBeOverruledExceptionMapper.java
@@ -0,0 +1,45 @@
+/**
+ * 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.infrastructure.core.exceptionmapper;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import lombok.extern.slf4j.Slf4j;
+import 
org.apache.fineract.cob.exceptions.LoanAccountLockCannotBeOverruledException;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.springframework.stereotype.Component;
+
+@Provider
+@Component
+@Slf4j
+public class LoanAccountLockCannotBeOverruledExceptionMapper implements 
ExceptionMapper<LoanAccountLockCannotBeOverruledException> {
+
+    @Override
+    public Response toResponse(LoanAccountLockCannotBeOverruledException 
exception) {
+        final String globalisationMessageCode = 
"error.msg.invalid.request.body";
+        final String defaultUserMessage = exception.getMessage();
+        log.warn("Exception: {}, Message: {}", exception.getClass().getName(), 
defaultUserMessage);
+
+        final ApiParameterError error = 
ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage);
+
+        return 
Response.status(Response.Status.CONFLICT).entity(error).type(MediaType.APPLICATION_JSON).build();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/LoanNotFoundExceptionMapper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/LoanNotFoundExceptionMapper.java
new file mode 100644
index 000000000..45f0dcb42
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/LoanNotFoundExceptionMapper.java
@@ -0,0 +1,45 @@
+/**
+ * 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.infrastructure.core.exceptionmapper;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
+import org.springframework.stereotype.Component;
+
+@Provider
+@Component
+@Slf4j
+public class LoanNotFoundExceptionMapper implements 
ExceptionMapper<LoanNotFoundException> {
+
+    @Override
+    public Response toResponse(LoanNotFoundException exception) {
+        final String globalisationMessageCode = 
"error.msg.invalid.request.body";
+        final String defaultUserMessage = exception.getMessage();
+        log.warn("Exception: {}, Message: {}", exception.getClass().getName(), 
defaultUserMessage);
+
+        final ApiParameterError error = 
ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage);
+
+        return 
Response.status(Response.Status.NOT_FOUND).entity(error).type(MediaType.APPLICATION_JSON).build();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/GoogleGsonSerializerHelper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/GoogleGsonSerializerHelper.java
index b1af11537..1d561e873 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/GoogleGsonSerializerHelper.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/GoogleGsonSerializerHelper.java
@@ -96,6 +96,10 @@ public final class GoogleGsonSerializerHelper {
         return serializer.toJson(singleDataObject);
     }
 
+    public Gson createSimpleGson() {
+        return createGsonBuilder().create();
+    }
+
     public static GsonBuilder createGsonBuilder() {
         return createGsonBuilder(false);
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.java
index 9de336bbb..f4ff81dcc 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/creditbureau/service/ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl.java
@@ -450,7 +450,7 @@ public class 
ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl implemen
             apicommand = JsonCommand.from(json, parsedCommand, 
this.fromApiJsonHelper, wrapper.getEntityName(), wrapper.getEntityId(),
                     wrapper.getSubentityId(), wrapper.getGroupId(), 
wrapper.getClientId(), wrapper.getLoanId(), wrapper.getSavingsId(),
                     wrapper.getTransactionId(), wrapper.getHref(), 
wrapper.getProductId(), wrapper.getCreditBureauId(),
-                    wrapper.getOrganisationCreditBureauId());
+                    wrapper.getOrganisationCreditBureauId(), 
wrapper.getJobName());
 
             this.fromApiJsonDeserializer.validateForCreate(apicommand.json());
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/InlineJobResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/InlineJobResource.java
new file mode 100644
index 000000000..a931b4390
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/InlineJobResource.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.infrastructure.jobs.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.cob.data.LoanIdsResponseDTO;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import 
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import 
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.springframework.stereotype.Component;
+
+@Path("/jobs")
+@Component
+@Tag(name = "Inline Job", description = "")
+@RequiredArgsConstructor
+public class InlineJobResource {
+
+    private final PortfolioCommandSourceWritePlatformService 
commandWritePlatformService;
+    private final DefaultToApiJsonSerializer<LoanIdsResponseDTO> serializer;
+
+    @POST
+    @Path("{jobName}/inline")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Starts an inline Job", description = "Starts an 
inline Job")
+    @RequestBody(content = @Content(schema = @Schema(implementation = 
InlineJobResourceSwagger.InlineJobRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(array = @ArraySchema(schema = @Schema(implementation = 
InlineJobResourceSwagger.InlineJobResponse.class)))) })
+    public String executeInlineJob(@PathParam("jobName") 
@Parameter(description = "jobName") final String jobName,
+            @Parameter(hidden = true) final String jsonRequestBody) {
+
+        final CommandWrapper commandRequest = new 
CommandWrapperBuilder().executeInlineJob(jobName).withJson(jsonRequestBody).build();
+        CommandProcessingResult result = 
commandWritePlatformService.logCommandSource(commandRequest);
+        return serializer.serialize(result);
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/InlineJobResourceSwagger.java
similarity index 59%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/InlineJobResourceSwagger.java
index 067eaf05f..5eccec61e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/api/InlineJobResourceSwagger.java
@@ -16,19 +16,29 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.infrastructure.jobs.api;
 
-public final class LoanCOBConstant {
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.List;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+final class InlineJobResourceSwagger {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
+    private InlineJobResourceSwagger() {}
 
-    private LoanCOBConstant() {
+    @Schema(description = "InlineJobRequest")
+    public static final class InlineJobRequest {
+
+        private InlineJobRequest() {}
+
+        public List<Long> loanIds;
+    }
+
+    @Schema(description = "InlineJobResponse")
+    public static final class InlineJobResponse {
+
+        private InlineJobResponse() {}
+
+        public List<Long> loanIds;
 
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/CustomJobParameter.java
similarity index 59%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/CustomJobParameter.java
index 067eaf05f..11a8fd9dd 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/CustomJobParameter.java
@@ -16,19 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.infrastructure.jobs.domain;
 
-public final class LoanCOBConstant {
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import 
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+@Entity
+@Table(name = "batch_custom_job_parameters")
+@NoArgsConstructor
+@Getter
+@Setter
+public class CustomJobParameter extends AbstractPersistableCustom {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
-
-    }
+    @Column(name = "parameter_json", nullable = false)
+    private String parameterJson;
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/CustomJobParameterRepository.java
similarity index 60%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/CustomJobParameterRepository.java
index 067eaf05f..2cb820e0e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/CustomJobParameterRepository.java
@@ -16,19 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.infrastructure.jobs.domain;
 
-public final class LoanCOBConstant {
+import org.springframework.data.jpa.repository.JpaRepository;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
-
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
-
-    }
-}
+public interface CustomJobParameterRepository extends 
JpaRepository<CustomJobParameter, Long> {}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobNotFoundException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobNotFoundException.java
index b4bd7e589..becff1983 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobNotFoundException.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobNotFoundException.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.infrastructure.jobs.exception;
 
 import 
org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
+import org.springframework.batch.core.launch.NoSuchJobException;
 import org.springframework.dao.EmptyResultDataAccessException;
 
 /**
@@ -30,6 +31,10 @@ public class JobNotFoundException extends 
AbstractPlatformResourceNotFoundExcept
         super("error.msg.sheduler.job.id.invalid", "Job with identifier " + 
identifier + " does not exist", identifier);
     }
 
+    public JobNotFoundException(final String identifier, NoSuchJobException e) 
{
+        super("error.msg.sheduler.job.id.invalid", "Job with identifier " + 
identifier + " does not exist", identifier, e);
+    }
+
     public JobNotFoundException(String identifier, 
EmptyResultDataAccessException e) {
         super("error.msg.sheduler.job.id.invalid", "Job with identifier " + 
identifier + " does not exist", identifier, e);
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineExecutorService.java
similarity index 60%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineExecutorService.java
index 067eaf05f..2b96a1b3a 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineExecutorService.java
@@ -16,19 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.infrastructure.jobs.service;
 
-public final class LoanCOBConstant {
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
+public interface InlineExecutorService {
 
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
-
-    }
+    CommandProcessingResult executeInlineJob(JsonCommand command, String 
jobName);
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineJobExecuteHandler.java
similarity index 58%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandler.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineJobExecuteHandler.java
index c552a3fea..aef62e1c4 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineJobExecuteHandler.java
@@ -16,35 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.service;
+package org.apache.fineract.infrastructure.jobs.service;
 
-import com.google.common.base.Splitter;
-import java.util.List;
 import lombok.RequiredArgsConstructor;
-import org.apache.fineract.cob.exceptions.BusinessStepException;
-import org.apache.fineract.cob.exceptions.BusinessStepNotBelongsToJobException;
 import org.apache.fineract.commands.annotation.CommandType;
 import org.apache.fineract.commands.handler.NewCommandSourceHandler;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
 @RequiredArgsConstructor
 @Service
-@CommandType(entity = "BATCH_BUSINESS_STEP", action = "UPDATE")
-public class BusinessStepConfigUpdateHandler implements 
NewCommandSourceHandler {
+@CommandType(entity = "INLINE_JOB", action = "EXECUTE")
+public class InlineJobExecuteHandler implements NewCommandSourceHandler {
 
-    private final ConfigJobParameterService configJobParameterService;
+    private final ApplicationContext applicationContext;
 
     @Override
-    @Transactional
-    public CommandProcessingResult processCommand(JsonCommand command) throws 
BusinessStepNotBelongsToJobException, BusinessStepException {
-        List<String> split = Splitter.on("/").splitToList(command.getUrl());
-        String jobName = split.get(split.size() - 2);
-        if ("null".equals(jobName)) {
-            jobName = null;
-        }
-        return configJobParameterService.updateStepConfigByJobName(command, 
jobName);
+    public CommandProcessingResult processCommand(JsonCommand command) {
+        InlineJobType inlineJobType = 
InlineJobType.getInlineJobType(command.getJobName());
+        InlineExecutorService inlineJobExecutorService = 
applicationContext.getBean(inlineJobType.getExecutorServiceClass());
+        return inlineJobExecutorService.executeInlineJob(command, 
inlineJobType.getInlineJobName());
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineJobType.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineJobType.java
new file mode 100644
index 000000000..e8b2ce18c
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/InlineJobType.java
@@ -0,0 +1,43 @@
+/**
+ * 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.infrastructure.jobs.service;
+
+import java.util.Arrays;
+import java.util.Optional;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl;
+
+@RequiredArgsConstructor
+public enum InlineJobType {
+
+    LOAN_COB("LOAN_COB", "INLINE_LOAN_COB", 
InlineLoanCOBExecutorServiceImpl.class);
+
+    private final String jobName;
+    @Getter
+    private final String inlineJobName;
+    @Getter
+    private final Class<? extends InlineExecutorService> executorServiceClass;
+
+    public static InlineJobType getInlineJobType(String jobName) {
+        Optional<InlineJobType> optionalInlineJobType = 
Arrays.stream(InlineJobType.values())
+                .filter(inlineCOBType -> 
jobName.equals(inlineCOBType.jobName)).findAny();
+        return optionalInlineJobType.orElseThrow(() -> new 
IllegalArgumentException("Inline Job is not found by job name: " + jobName));
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/springbatch/SpringBatchJobConstants.java
similarity index 60%
copy from 
fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/springbatch/SpringBatchJobConstants.java
index 067eaf05f..016e9ba15 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBConstant.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/springbatch/SpringBatchJobConstants.java
@@ -16,19 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.cob.loan;
+package org.apache.fineract.infrastructure.springbatch;
 
-public final class LoanCOBConstant {
+public abstract class SpringBatchJobConstants {
 
-    public static final String JOB_NAME = "LOAN_COB";
-    public static final String LOAN_COB_JOB_NAME = "LOAN_CLOSE_OF_BUSINESS";
-    public static final String LOAN_IDS = "loanIds";
-    public static final String BUSINESS_STEP_MAP = "businessStepMap";
-    public static final String LOAN_COB_WORKER_STEP = "loanCOBWorkerStep";
-
-    public static final String ALREADY_LOCKED_LOAN_IDS = 
"alreadyLockedLoanIds";
-
-    private LoanCOBConstant() {
-
-    }
+    public static final String CUSTOM_JOB_PARAMETER_ID_KEY = 
"CUSTOM_JOB_PARAMETER_ID";
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java
 
b/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java
index fd92896fc..0f847dabf 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java
@@ -37,7 +37,7 @@ public class InteropWrapperBuilder {
 
     public CommandWrapper build() {
         return new CommandWrapper(null, null, null, null, null, actionName, 
entityName, null, null, href, json, null, null, null, null,
-                null);
+                null, null);
     }
 
     public InteropWrapperBuilder withJson(final String json) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index ee6ba0cd8..07f22ce99 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -2857,7 +2857,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
 
             final JsonElement parsedCommand = 
this.fromApiJsonHelper.parse(overdueInstallment.toString());
             final JsonCommand command = 
JsonCommand.from(overdueInstallment.toString(), parsedCommand, 
this.fromApiJsonHelper, null, null,
-                    null, null, null, loanId, null, null, null, null, null, 
null);
+                    null, null, null, loanId, null, null, null, null, null, 
null, null);
             LoanOverdueDTO overdueDTO = 
applyChargeToOverdueLoanInstallment(loan, overdueInstallment.getChargeId(),
                     overdueInstallment.getPeriodNumber(), command);
             loan = overdueDTO.getLoan();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountCommandsServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountCommandsServiceImpl.java
index f56785424..04fdadb3f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountCommandsServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountCommandsServiceImpl.java
@@ -46,7 +46,7 @@ public class ShareAccountCommandsServiceImpl implements 
AccountsCommandsService
     public Object handleCommand(Long accountId, String command, String 
jsonBody) {
         final JsonElement parsedCommand = 
this.fromApiJsonHelper.parse(jsonBody);
         final JsonCommand jsonCommand = JsonCommand.from(jsonBody, 
parsedCommand, this.fromApiJsonHelper, null, null, null, null, null,
-                null, null, null, null, null, null, null);
+                null, null, null, null, null, null, null, null);
         if (ShareAccountApiConstants.APPROVE_COMMAND.equals(command)) {
             return approveShareAccount(accountId, jsonCommand);
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java
index d0b765381..52c6bf3e8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductCommandsServiceImpl.java
@@ -45,7 +45,7 @@ public class ShareProductCommandsServiceImpl implements 
ProductCommandsService {
     public Object handleCommand(Long productId, String command, String 
jsonBody) {
         final JsonElement parsedCommand = 
this.fromApiJsonHelper.parse(jsonBody);
         final JsonCommand jsonCommand = JsonCommand.from(jsonBody, 
parsedCommand, this.fromApiJsonHelper, null, null, null, null, null,
-                null, null, null, null, null, null, null);
+                null, null, null, null, null, null, null, null);
         if 
(ShareProductApiConstants.PREIEW_DIVIDENDS_COMMAND_STRING.equals(command)) {
             return null;
         } else if 
(ShareProductApiConstants.POST_DIVIDENdS_COMMAND_STRING.equals(command)) {
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index d4d072406..31941ab88 100644
--- 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -77,4 +77,7 @@
     <include file="parts/0055_add_submitted_on_date_to_loan_charge.xml" 
relativeToChangelogFile="true"/>
     <include file="parts/0056_add_external_event_default_configuration.xml" 
relativeToChangelogFile="true"/>
     <include file="parts/0057_add_principal_adjustments_to_loan.xml" 
relativeToChangelogFile="true"/>
+    <include file="parts/0058_add_execute_inline_cob_permission.xml" 
relativeToChangelogFile="true"/>
+    <include file="parts/0059_add_spring_batch_loan_id_list_table.xml" 
relativeToChangelogFile="true"/>
+    <include file="parts/0060_add_job_name_to_command_source.xml" 
relativeToChangelogFile="true"/>
 </databaseChangeLog>
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0058_add_execute_inline_cob_permission.xml
 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0058_add_execute_inline_cob_permission.xml
new file mode 100644
index 000000000..69ee92526
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0058_add_execute_inline_cob_permission.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";>
+    <changeSet author="fineract" id="1">
+        <insert tableName="m_permission">
+            <column name="grouping" value="organisation"/>
+            <column name="code" value="EXECUTE_INLINE_JOB"/>
+            <column name="entity_name" value="INLINE_JOB"/>
+            <column name="action_name" value="EXECUTE"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0059_add_spring_batch_loan_id_list_table.xml
 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0059_add_spring_batch_loan_id_list_table.xml
new file mode 100644
index 000000000..494891018
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0059_add_spring_batch_loan_id_list_table.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog";
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+                   
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";>
+    <changeSet author="fineract" id="1">
+        <createTable tableName="batch_custom_job_parameters">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="parameter_json" type="TEXT">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0060_add_job_name_to_command_source.xml
 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0060_add_job_name_to_command_source.xml
new file mode 100644
index 000000000..245a2daa9
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0060_add_job_name_to_command_source.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";>
+    <changeSet author="fineract" id="1">
+        <addColumn tableName="m_portfolio_command_source">
+            <column name="job_name" type="VARCHAR(100)">
+            </column>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java
 
b/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java
index 757669ff2..b855d7060 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/cob/listener/LoanItemListenerStepDefinitions.java
@@ -46,7 +46,8 @@ public class LoanItemListenerStepDefinitions implements En {
     private LoanAccountLockRepository accountLockRepository = 
mock(LoanAccountLockRepository.class);
     private TransactionTemplate transactionTemplate = 
spy(TransactionTemplate.class);
 
-    private LoanItemListener loanItemListener = new 
LoanItemListener(accountLockRepository, transactionTemplate);
+    private ChunkProcessingLoanItemListener loanItemListener = new 
ChunkProcessingLoanItemListener(accountLockRepository,
+            transactionTemplate);
 
     private Exception exception;
 
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandlerTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandlerTest.java
deleted file mode 100644
index c20ab8948..000000000
--- 
a/fineract-provider/src/test/java/org/apache/fineract/cob/service/BusinessStepConfigUpdateHandlerTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * 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.cob.service;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.apache.fineract.infrastructure.core.api.JsonCommand;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.quality.Strictness;
-
-@ExtendWith(MockitoExtension.class)
-@MockitoSettings(strictness = Strictness.LENIENT)
-class BusinessStepConfigUpdateHandlerTest {
-
-    @InjectMocks
-    private BusinessStepConfigUpdateHandler testObj;
-
-    @Mock
-    private ConfigJobParameterService configJobParameterService;
-
-    @Captor
-    private ArgumentCaptor<String> argumentCaptor;
-
-    @Test
-    void shouldProcessCommandGetsJobName() {
-        JsonCommand command = mock(JsonCommand.class);
-        given(command.getUrl()).willReturn("/jobs/jobName/steps");
-        testObj.processCommand(command);
-
-        
verify(configJobParameterService).updateStepConfigByJobName(eq(command), 
argumentCaptor.capture());
-        assertEquals("jobName", argumentCaptor.getValue());
-    }
-
-    @Test
-    void shouldProcessCommandGetsJobNameEmptyString() {
-        JsonCommand command = mock(JsonCommand.class);
-        given(command.getUrl()).willReturn("/jobs//steps");
-        testObj.processCommand(command);
-
-        
verify(configJobParameterService).updateStepConfigByJobName(eq(command), 
argumentCaptor.capture());
-        assertEquals("", argumentCaptor.getValue());
-    }
-
-    @Test
-    void shouldProcessCommandGetsJobNameNull() {
-        JsonCommand command = mock(JsonCommand.class);
-        given(command.getUrl()).willReturn("/jobs/" + null + "/steps");
-        testObj.processCommand(command);
-
-        
verify(configJobParameterService).updateStepConfigByJobName(eq(command), 
argumentCaptor.capture());
-        assertEquals(null, argumentCaptor.getValue());
-    }
-}
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImplTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImplTest.java
new file mode 100644
index 000000000..00ee9da1f
--- /dev/null
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImplTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.cob.service;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import 
org.apache.fineract.cob.exceptions.LoanAccountLockCannotBeOverruledException;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.springframework.transaction.support.TransactionTemplate;
+
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+class InlineLoanCOBExecutorServiceImplTest {
+
+    @InjectMocks
+    private InlineLoanCOBExecutorServiceImpl testObj;
+    @Mock
+    private TransactionTemplate transactionTemplate;
+
+    @Mock
+    private InlineLoanCOBExecutionDataParser dataParser;
+
+    @Test
+    void shouldExceptionThrownIfLoanIsAlreadyLocked() {
+        JsonCommand command = mock(JsonCommand.class);
+        ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, 
"default", "Default", "Asia/Kolkata", null));
+
+        when(dataParser.parseExecution(any())).thenReturn(List.of(3L));
+        when(transactionTemplate.execute(any())).thenThrow(new 
LoanAccountLockCannotBeOverruledException(""));
+        assertThrows(LoanAccountLockCannotBeOverruledException.class, () -> 
testObj.executeInlineJob(command, "INLINE_LOAN_COB"));
+    }
+}
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/commands/provider/CommandHandlerProviderStepDefinitions.java
 
b/fineract-provider/src/test/java/org/apache/fineract/commands/provider/CommandHandlerProviderStepDefinitions.java
index dc865c20a..a422ef191 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/commands/provider/CommandHandlerProviderStepDefinitions.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/commands/provider/CommandHandlerProviderStepDefinitions.java
@@ -42,7 +42,7 @@ public class CommandHandlerProviderStepDefinitions implements 
En {
 
         When("The user processes the command with ID {long}", (Long id) -> {
             this.result = commandHandler
-                    .processCommand(JsonCommand.fromExistingCommand(id, null, 
null, null, null, null, null, null, null, null, null));
+                    .processCommand(JsonCommand.fromExistingCommand(id, null, 
null, null, null, null, null, null, null, null, null, null));
         });
 
         Then("The command ID matches {long}", (Long id) -> {

Reply via email to