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

adamsaghy 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 42d419864 FINERACT-1992 Delinquency Pause API Changes
42d419864 is described below

commit 42d41986414e328b657030b7b6b15859bd35d98b
Author: Peter Bagrij <[email protected]>
AuthorDate: Mon Nov 13 18:09:11 2023 +0100

    FINERACT-1992 Delinquency Pause API Changes
---
 .../commands/service/CommandWrapperBuilder.java    |   9 +
 .../api/DelinquencyApiResourceSwagger.java         |  52 ++++
 .../domain/LoanDelinquencyActionRepository.java    |   5 +
 .../CreateDelinquencyActionCommandHandler.java     |  42 +++
 .../service/DelinquencyReadPlatformService.java    |   3 +
 .../DelinquencyReadPlatformServiceImpl.java        |  71 +++++
 .../service/DelinquencyWritePlatformService.java   |   2 +
 .../DelinquencyWritePlatformServiceImpl.java       |  27 ++
 .../starter/DelinquencyConfiguration.java          |  14 +-
 .../validator/DelinquencyActionParameters.java     |  31 +++
 .../DelinquencyActionParseAndValidator.java        | 199 ++++++++++++++
 .../validator/LoanDelinquencyActionData.java       |  51 ++++
 .../loanaccount/api/LoansApiResource.java          |  77 ++++++
 .../loanaccount/api/LoansApiResourceSwagger.java   |  16 +-
 .../portfolio/loanaccount/data/CollectionData.java |   6 +-
 .../DelinquencyReadPlatformServiceImplTest.java    | 204 ++++++++++++++
 .../DelinquencyActionParseAndValidatorTest.java    | 293 +++++++++++++++++++++
 ...cyWritePlatformServiceRangeChangeEventTest.java |  10 +-
 .../integrationtests/BaseLoanIntegrationTest.java  |  11 +-
 .../DelinquencyActionIntegrationTests.java         | 259 ++++++++++++++++++
 .../DelinquencyBucketsIntegrationTest.java         |   6 +-
 .../common/loans/LoanTransactionHelper.java        |  34 ++-
 .../common/products/DelinquencyBucketsHelper.java  |   4 +-
 23 files changed, 1403 insertions(+), 23 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
 
b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index 22fcc7237..486abf6c9 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -3653,4 +3653,13 @@ public class CommandWrapperBuilder {
         this.href = "/loans/" + loanId + "/transactions?command=downPayment";
         return this;
     }
+
+    public CommandWrapperBuilder createDelinquencyAction(final Long loanId) {
+        this.actionName = "CREATE";
+        this.entityName = "DELINQUENCY_ACTION";
+        this.entityId = null;
+        this.loanId = loanId;
+        this.href = "/loans/" + loanId + "/delinquency-action";
+        return this;
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResourceSwagger.java
index 65b8ccdcb..48413f8d1 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/api/DelinquencyApiResourceSwagger.java
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.delinquency.api;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import java.time.LocalDate;
+import java.time.OffsetDateTime;
 
 public final class DelinquencyApiResourceSwagger {
 
@@ -150,4 +151,55 @@ public final class DelinquencyApiResourceSwagger {
         public LocalDate liftedOnDate;
     }
 
+    @Schema(description = "GetDelinquencyActionsResponse")
+    public static final class GetDelinquencyActionsResponse {
+
+        private GetDelinquencyActionsResponse() {}
+
+        @Schema(example = "1")
+        public Long id;
+        @Schema(example = "pause")
+        public String action;
+        @Schema(example = "2013,1,2")
+        public LocalDate startDate;
+        @Schema(example = "2013,2,20")
+        public LocalDate endDate;
+        @Schema(example = "1")
+        public Long createdById;
+        @Schema(example = "1359463135000")
+        public OffsetDateTime createdOn;
+        @Schema(example = "1")
+        public Long updatedById;
+        @Schema(example = "1359463135000")
+        public OffsetDateTime lastModifiedOn;
+    }
+
+    @Schema(description = "PostLoansDelinquencyActionRequest")
+    public static final class PostLoansDelinquencyActionRequest {
+
+        @Schema(example = "pause")
+        public String action;
+        @Schema(example = "2013-01-02")
+        public String startDate;
+        @Schema(example = "2013-02-20")
+        public String endDate;
+        @Schema(example = "yyyy-MM-dd")
+        public String dateFormat;
+        @Schema(example = "en")
+        public String locale;
+    }
+
+    @Schema(description = "PostLoansDelinquencyActionResponse")
+    public static final class PostLoansDelinquencyActionResponse {
+
+        @Schema(example = "1")
+        public Long officeId;
+
+        @Schema(example = "1")
+        public Long clientId;
+
+        @Schema(example = "1")
+        public Long resourceId;
+    }
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyActionRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyActionRepository.java
index d8bef639e..8167de590 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyActionRepository.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanDelinquencyActionRepository.java
@@ -19,6 +19,8 @@
 package org.apache.fineract.portfolio.delinquency.domain;
 
 import java.time.LocalDate;
+import java.util.List;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -35,4 +37,7 @@ public interface LoanDelinquencyActionRepository
             + "    da.loan.id = :loan_id  order by da.createdDate desc")
     Page<LoanDelinquencyAction> 
getEffectiveDelinquencyActionForLoan(@Param("loan_id") Long loan_id,
             @Param("business_date") LocalDate business_date, Pageable page);
+
+    List<LoanDelinquencyAction> findByLoanOrderById(Loan loan);
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/handler/CreateDelinquencyActionCommandHandler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/handler/CreateDelinquencyActionCommandHandler.java
new file mode 100644
index 000000000..3d2160ae1
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/handler/CreateDelinquencyActionCommandHandler.java
@@ -0,0 +1,42 @@
+/**
+ * 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.portfolio.delinquency.handler;
+
+import lombok.RequiredArgsConstructor;
+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.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@CommandType(entity = "DELINQUENCY_ACTION", action = "CREATE")
+public class CreateDelinquencyActionCommandHandler implements 
NewCommandSourceHandler {
+
+    private final DelinquencyWritePlatformService writePlatformService;
+
+    @Transactional
+    @Override
+    public CommandProcessingResult processCommand(final JsonCommand command) {
+        return 
this.writePlatformService.createDelinquencyAction(command.getLoanId(), command);
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java
index 9f3510cce..174cc1283 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java
@@ -23,6 +23,7 @@ import 
org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
 import 
org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
 import 
org.apache.fineract.portfolio.delinquency.data.LoanInstallmentDelinquencyTagData;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 
 public interface DelinquencyReadPlatformService {
@@ -43,4 +44,6 @@ public interface DelinquencyReadPlatformService {
 
     Collection<LoanInstallmentDelinquencyTagData> 
retrieveLoanInstallmentsCurrentDelinquencyTag(Long loanId);
 
+    Collection<LoanDelinquencyAction> retrieveLoanDelinquencyActions(Long 
loanId);
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
index 7e1d9c145..4ce2c9f74 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
@@ -18,24 +18,37 @@
  */
 package org.apache.fineract.portfolio.delinquency.service;
 
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
 import 
org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
 import 
org.apache.fineract.portfolio.delinquency.data.LoanInstallmentDelinquencyTagData;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
 import 
org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
 import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
 import 
org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper;
+import 
org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
@@ -55,6 +68,7 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
     private final LoanRepository loanRepository;
     private final LoanDelinquencyDomainService loanDelinquencyDomainService;
     private final LoanInstallmentDelinquencyTagRepository 
repositoryLoanInstallmentDelinquencyTag;
+    private final LoanDelinquencyActionRepository 
loanDelinquencyActionRepository;
 
     @Override
     public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
@@ -124,14 +138,71 @@ public class DelinquencyReadPlatformServiceImpl 
implements DelinquencyReadPlatfo
                 
collectionData.setLastRepaymentDate(lastRepaymentTransaction.getTransactionDate());
                 
collectionData.setLastRepaymentAmount(lastRepaymentTransaction.getAmount());
             }
+
+            enrichWithDelinquencyPausePeriodInfo(collectionData, 
retrieveLoanDelinquencyActions(loanId),
+                    ThreadLocalContextUtil.getBusinessDate());
         }
 
         return collectionData;
     }
 
+    void enrichWithDelinquencyPausePeriodInfo(CollectionData collectionData, 
Collection<LoanDelinquencyAction> delinquencyActions,
+            LocalDate businessDate) {
+        // partition them based on type
+        Map<DelinquencyAction, List<LoanDelinquencyAction>> partitioned = 
delinquencyActions.stream()
+                
.collect(Collectors.groupingBy(LoanDelinquencyAction::getAction));
+
+        // add the possible resumes to it to create the effective pause periods
+        if (partitioned.containsKey(DelinquencyAction.PAUSE)) {
+            List<LoanDelinquencyActionData> effective = new ArrayList<>();
+            List<LoanDelinquencyAction> pauses = 
partitioned.get(DelinquencyAction.PAUSE);
+            for (LoanDelinquencyAction loanDelinquencyAction : pauses) {
+                Optional<LoanDelinquencyAction> resume = 
findMatchingResume(loanDelinquencyAction, partitioned.get(RESUME));
+                LoanDelinquencyActionData loanDelinquencyActionData = new 
LoanDelinquencyActionData(loanDelinquencyAction);
+                resume.ifPresent(r -> 
loanDelinquencyActionData.setEndDate(r.getStartDate()));
+                effective.add(loanDelinquencyActionData);
+            }
+
+            // order them by start date, filter out the future items
+            Optional<LoanDelinquencyActionData> last = effective.stream()
+                    
.sorted(Comparator.comparing(LoanDelinquencyActionData::getStartDate))
+                    .filter(e -> !DateUtils.isAfter(e.getStartDate(), 
businessDate)) // keep only the present and the
+                                                                               
      // past ones
+                    .reduce((first, second) -> second);
+
+            // enrich collectionData
+            last.ifPresent(action -> {
+                collectionData
+                        
.setDelinquencyCalculationPaused(!action.startDate.isAfter(businessDate) && 
!businessDate.isAfter(action.endDate));
+                
collectionData.setDelinquencyPausePeriodStartDate(action.getStartDate());
+                
collectionData.setDelinquencyPausePeriodEndDate(action.getEndDate());
+            });
+        }
+    }
+
+    private Optional<LoanDelinquencyAction> 
findMatchingResume(LoanDelinquencyAction pause, List<LoanDelinquencyAction> 
resumes) {
+        if (resumes != null && resumes.size() > 0) {
+            for (LoanDelinquencyAction resume : resumes) {
+                if (!pause.getStartDate().isAfter(resume.getStartDate()) && 
!resume.getStartDate().isAfter(pause.getEndDate())) {
+                    return Optional.of(resume);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
     @Override
     public Collection<LoanInstallmentDelinquencyTagData> 
retrieveLoanInstallmentsCurrentDelinquencyTag(Long loanId) {
         return 
repositoryLoanInstallmentDelinquencyTag.findInstallmentDelinquencyTags(loanId);
     }
 
+    @Override
+    public Collection<LoanDelinquencyAction> 
retrieveLoanDelinquencyActions(Long loanId) {
+        final Optional<Loan> optLoan = this.loanRepository.findById(loanId);
+        if (optLoan.isPresent()) {
+            return 
loanDelinquencyActionRepository.findByLoanOrderById(optLoan.get());
+        }
+        return List.of();
+    }
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java
index d5f6a1b06..9db4b45df 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java
@@ -47,4 +47,6 @@ public interface DelinquencyWritePlatformService {
 
     void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData 
loanDelinquencyData);
 
+    CommandProcessingResult createDelinquencyAction(Long loanId, JsonCommand 
command);
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
index 0520c36d1..af58629fe 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
@@ -44,12 +44,15 @@ import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMapping
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTag;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
 import 
org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketAgesOverlapedException;
 import 
org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeInvalidAgesException;
+import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParseAndValidator;
 import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
 import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
@@ -76,6 +79,8 @@ public class DelinquencyWritePlatformServiceImpl implements 
DelinquencyWritePlat
     private final BusinessEventNotifierService businessEventNotifierService;
     private final LoanDelinquencyDomainService loanDelinquencyDomainService;
     private final LoanInstallmentDelinquencyTagRepository 
loanInstallmentDelinquencyTagRepository;
+    private final LoanDelinquencyActionRepository 
loanDelinquencyActionRepository;
+    private final DelinquencyActionParseAndValidator 
delinquencyActionParseAndValidator;
 
     @Override
     public CommandProcessingResult createDelinquencyRange(JsonCommand command) 
{
@@ -199,6 +204,28 @@ public class DelinquencyWritePlatformServiceImpl 
implements DelinquencyWritePlat
         }
     }
 
+    @Override
+    public CommandProcessingResult createDelinquencyAction(Long loanId, 
JsonCommand command) {
+        final Loan loan = 
this.loanRepository.findOneWithNotFoundDetection(loanId);
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        final List<LoanDelinquencyAction> savedDelinquencyList = 
loanDelinquencyActionRepository.findByLoanOrderById(loan);
+
+        LoanDelinquencyAction parsedDelinquencyAction = 
delinquencyActionParseAndValidator.validateAndParseUpdate(command, loan,
+                savedDelinquencyList, businessDate);
+
+        parsedDelinquencyAction.setLoan(loan);
+
+        LoanDelinquencyAction saved = 
loanDelinquencyActionRepository.saveAndFlush(parsedDelinquencyAction);
+
+        return new 
CommandProcessingResultBuilder().withCommandId(command.commandId()) //
+                .withEntityId(saved.getId()) //
+                .withOfficeId(loan.getOfficeId()) //
+                .withClientId(loan.getClientId()) //
+                .withGroupId(loan.getGroupId()) //
+                .withLoanId(loanId) //
+                .build();
+    }
+
     @Override
     public void removeDelinquencyTagToLoan(final Loan loan) {
         setLoanDelinquencyTag(loan, null);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
index 88c27afa8..1b78a48c2 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
@@ -22,6 +22,7 @@ import 
org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
 import 
org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
@@ -33,6 +34,7 @@ import 
org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatfor
 import 
org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformServiceImpl;
 import 
org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService;
 import 
org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainServiceImpl;
+import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParseAndValidator;
 import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
 import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
@@ -52,10 +54,11 @@ public class DelinquencyConfiguration {
             DelinquencyRangeMapper mapperRange, DelinquencyBucketMapper 
mapperBucket,
             LoanDelinquencyTagMapper mapperLoanDelinquencyTagHistory, 
LoanRepository loanRepository,
             LoanDelinquencyDomainService loanDelinquencyDomainService,
-            LoanInstallmentDelinquencyTagRepository 
repositoryLoanInstallmentDelinquencyTag) {
+            LoanInstallmentDelinquencyTagRepository 
repositoryLoanInstallmentDelinquencyTag,
+            LoanDelinquencyActionRepository loanDelinquencyActionRepository) {
         return new DelinquencyReadPlatformServiceImpl(repositoryRange, 
repositoryBucket, repositoryLoanDelinquencyTagHistory, mapperRange,
                 mapperBucket, mapperLoanDelinquencyTagHistory, loanRepository, 
loanDelinquencyDomainService,
-                repositoryLoanInstallmentDelinquencyTag);
+                repositoryLoanInstallmentDelinquencyTag, 
loanDelinquencyActionRepository);
     }
 
     @Bean
@@ -66,10 +69,13 @@ public class DelinquencyConfiguration {
             LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository, 
LoanRepositoryWrapper loanRepository,
             LoanProductRepository loanProductRepository, 
BusinessEventNotifierService businessEventNotifierService,
             LoanDelinquencyDomainService loanDelinquencyDomainService,
-            LoanInstallmentDelinquencyTagRepository 
loanInstallmentDelinquencyTagRepository) {
+            LoanInstallmentDelinquencyTagRepository 
loanInstallmentDelinquencyTagRepository,
+            LoanDelinquencyActionRepository loanDelinquencyActionRepository,
+            DelinquencyActionParseAndValidator 
delinquencyActionParseAndValidator) {
         return new DelinquencyWritePlatformServiceImpl(dataValidatorBucket, 
dataValidatorRange, repositoryRange, repositoryBucket,
                 repositoryBucketMappings, loanDelinquencyTagRepository, 
loanRepository, loanProductRepository, businessEventNotifierService,
-                loanDelinquencyDomainService, 
loanInstallmentDelinquencyTagRepository);
+                loanDelinquencyDomainService, 
loanInstallmentDelinquencyTagRepository, loanDelinquencyActionRepository,
+                delinquencyActionParseAndValidator);
     }
 
     @Bean
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParameters.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParameters.java
new file mode 100644
index 000000000..95d484c5a
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParameters.java
@@ -0,0 +1,31 @@
+/**
+ * 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.portfolio.delinquency.validator;
+
+public final class DelinquencyActionParameters {
+
+    private DelinquencyActionParameters() {}
+
+    public static final String ACTION = "action";
+    public static final String START_DATE = "startDate";
+    public static final String END_DATE = "endDate";
+    public static final String DATE_FORMAT = "dateFormat";
+
+    public static final String LOCALE = "locale";
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidator.java
new file mode 100644
index 000000000..eac6670e5
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidator.java
@@ -0,0 +1,199 @@
+/**
+ * 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.portfolio.delinquency.validator;
+
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
+
+import com.google.gson.JsonElement;
+import jakarta.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.validator.ParseAndValidator;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class DelinquencyActionParseAndValidator extends ParseAndValidator {
+
+    private final FromJsonHelper jsonHelper;
+
+    public LoanDelinquencyAction validateAndParseUpdate(@NotNull final 
JsonCommand command, Loan loan,
+            List<LoanDelinquencyAction> savedDelinquencyActions, LocalDate 
businessDate) {
+        List<LoanDelinquencyActionData> effectiveDelinquencyList = 
calculateEffectiveDelinquencyList(savedDelinquencyActions);
+        LoanDelinquencyAction parsedDelinquencyAction = parseCommand(command);
+        validateLoanIsActive(loan);
+        if 
(DelinquencyAction.PAUSE.equals(parsedDelinquencyAction.getAction())) {
+            validatePauseStartAndEndDate(parsedDelinquencyAction, 
businessDate);
+            validatePauseShallNotOverlap(parsedDelinquencyAction, 
effectiveDelinquencyList);
+        } else if 
(DelinquencyAction.RESUME.equals(parsedDelinquencyAction.getAction())) {
+            validateResumeStartDate(parsedDelinquencyAction, businessDate);
+            validateResumeNoEndDate(parsedDelinquencyAction);
+            validateResumeShouldBeOnActivePause(parsedDelinquencyAction, 
effectiveDelinquencyList);
+        }
+        return parsedDelinquencyAction;
+    }
+
+    private List<LoanDelinquencyActionData> 
calculateEffectiveDelinquencyList(List<LoanDelinquencyAction> 
savedDelinquencyActions) {
+        // partition them based on type
+        Map<DelinquencyAction, List<LoanDelinquencyAction>> partitioned = 
savedDelinquencyActions.stream()
+                
.collect(Collectors.groupingBy(LoanDelinquencyAction::getAction));
+        List<LoanDelinquencyActionData> effective = new ArrayList<>();
+        List<LoanDelinquencyAction> pauses = 
partitioned.get(DelinquencyAction.PAUSE);
+        if (pauses != null && pauses.size() > 0) {
+            for (LoanDelinquencyAction loanDelinquencyAction : pauses) {
+                Optional<LoanDelinquencyAction> resume = 
findMatchingResume(loanDelinquencyAction, partitioned.get(RESUME));
+                LoanDelinquencyActionData loanDelinquencyActionData = new 
LoanDelinquencyActionData(loanDelinquencyAction);
+                resume.ifPresent(r -> 
loanDelinquencyActionData.setEndDate(r.getStartDate()));
+                effective.add(loanDelinquencyActionData);
+            }
+        }
+        return effective;
+    }
+
+    private Optional<LoanDelinquencyAction> 
findMatchingResume(LoanDelinquencyAction pause, List<LoanDelinquencyAction> 
resumes) {
+        if (resumes != null && resumes.size() > 0) {
+            for (LoanDelinquencyAction resume : resumes) {
+                if (!pause.getStartDate().isAfter(resume.getStartDate()) && 
!resume.getStartDate().isAfter(pause.getEndDate())) {
+                    return Optional.of(resume);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    private void validateResumeShouldBeOnActivePause(LoanDelinquencyAction 
parsedDelinquencyAction,
+            List<LoanDelinquencyActionData> savedDelinquencyActions) {
+        boolean match = savedDelinquencyActions.stream()
+                .anyMatch(lda -> 
!DateUtils.isBefore(parsedDelinquencyAction.getStartDate(), lda.getStartDate())
+                        && 
!DateUtils.isAfter(parsedDelinquencyAction.getStartDate(), lda.getEndDate()));
+        if (!match) {
+            
raiseValidationError("loan-delinquency-action-resume-should-be-on-pause",
+                    "Resume Delinquency Action can only be created during an 
active pause");
+        }
+    }
+
+    private void validateResumeNoEndDate(LoanDelinquencyAction 
parsedDelinquencyAction) {
+        if (parsedDelinquencyAction.getEndDate() != null) {
+            
raiseValidationError("loan-delinquency-action-resume-should-have-no-end-date",
+                    "Resume Delinquency action can not have end date");
+        }
+    }
+
+    private void validateResumeStartDate(LoanDelinquencyAction 
parsedDelinquencyAction, LocalDate businessDate) {
+        if (!parsedDelinquencyAction.getStartDate().equals(businessDate)) {
+            raiseValidationError("loan-delinquency-action-invalid-start-date",
+                    "Start date of the Resume Delinquency action must be the 
current business date");
+        }
+    }
+
+    private void validatePauseStartAndEndDate(LoanDelinquencyAction 
parsedDelinquencyAction, LocalDate businessDate) {
+        if 
(parsedDelinquencyAction.getStartDate().equals(parsedDelinquencyAction.getEndDate()))
 {
+            
raiseValidationError("loan-delinquency-action-invalid-start-date-and-end-date",
+                    "Delinquency pause period must be at least one day");
+        }
+
+        if (businessDate.isAfter(parsedDelinquencyAction.getStartDate())) {
+            raiseValidationError("loan-delinquency-action-invalid-start-date", 
"Start date of pause period must be in the future");
+        }
+    }
+
+    private void validateLoanIsActive(Loan loan) {
+        if (!loan.getStatus().isActive()) {
+            raiseValidationError("loan-delinquency-action-invalid-loan-state", 
"Delinquency actions can be created only for active loans.");
+        }
+    }
+
+    private void validatePauseShallNotOverlap(LoanDelinquencyAction 
parsedDelinquencyAction,
+            List<LoanDelinquencyActionData> delinquencyActions) {
+        if (delinquencyActions.stream().filter(lda -> 
lda.getAction().equals(DelinquencyAction.PAUSE))
+                .anyMatch(lda -> isOverlapping(parsedDelinquencyAction, lda))) 
{
+            raiseValidationError("loan-delinquency-action-overlapping",
+                    "Delinquency pause period cannot overlap with another 
pause period");
+        }
+    }
+
+    private boolean isOverlapping(LoanDelinquencyAction 
parsedDelinquencyAction, LoanDelinquencyActionData ldad) {
+        return 
((!parsedDelinquencyAction.getStartDate().isAfter(ldad.getStartDate())
+                && 
!ldad.getStartDate().isAfter(parsedDelinquencyAction.getEndDate()))
+                || 
(!parsedDelinquencyAction.getStartDate().isAfter(ldad.getEndDate())
+                        && 
!ldad.getEndDate().isAfter(parsedDelinquencyAction.getEndDate())));
+    }
+
+    @org.jetbrains.annotations.NotNull
+    private LoanDelinquencyAction 
parseCommand(@org.jetbrains.annotations.NotNull JsonCommand command) {
+        LoanDelinquencyAction parsedDelinquencyAction = new 
LoanDelinquencyAction();
+        parsedDelinquencyAction.setAction(extractAction(command.parsedJson()));
+        
parsedDelinquencyAction.setStartDate(extractStartDate(command.parsedJson()));
+        
parsedDelinquencyAction.setEndDate(extractEndDate(command.parsedJson()));
+        return parsedDelinquencyAction;
+    }
+
+    private DelinquencyAction extractAction(JsonElement json) {
+        String actionString = 
jsonHelper.extractStringNamed(DelinquencyActionParameters.ACTION, json);
+        validateActionString(actionString);
+        if ("pause".equalsIgnoreCase(actionString)) {
+            return DelinquencyAction.PAUSE;
+        } else if ("resume".equalsIgnoreCase(actionString)) {
+            return DelinquencyAction.RESUME;
+        } else {
+            throw new 
PlatformApiDataValidationException(List.of(ApiParameterError.generalError("loan-delinquency-action-invalid-action",
+                    "Invalid Delinquency Action: " + actionString)));
+        }
+    }
+
+    private void validateActionString(String actionString) {
+        if (StringUtils.isEmpty(actionString)) {
+            raiseValidationError("loan-delinquency-action-missing-action", 
"Delinquency Action must not be null or empty");
+        }
+    }
+
+    private LocalDate extractStartDate(JsonElement json) {
+        String dateFormat = 
jsonHelper.extractStringNamed(DelinquencyActionParameters.DATE_FORMAT, json);
+        String locale = 
jsonHelper.extractStringNamed(DelinquencyActionParameters.LOCALE, json);
+        return 
jsonHelper.extractLocalDateNamed(DelinquencyActionParameters.START_DATE, json, 
dateFormat,
+                JsonParserHelper.localeFromString(locale));
+    }
+
+    private LocalDate extractEndDate(JsonElement json) {
+        String dateFormat = 
jsonHelper.extractStringNamed(DelinquencyActionParameters.DATE_FORMAT, json);
+        String locale = 
jsonHelper.extractStringNamed(DelinquencyActionParameters.LOCALE, json);
+        return 
jsonHelper.extractLocalDateNamed(DelinquencyActionParameters.END_DATE, json, 
dateFormat,
+                JsonParserHelper.localeFromString(locale));
+    }
+
+    private void raiseValidationError(String globalisationMessageCode, String 
msg) throws PlatformApiDataValidationException {
+        throw new 
PlatformApiDataValidationException(List.of(ApiParameterError.generalError(globalisationMessageCode,
 msg)));
+    }
+
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/LoanDelinquencyActionData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/LoanDelinquencyActionData.java
new file mode 100644
index 000000000..8f192165e
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/LoanDelinquencyActionData.java
@@ -0,0 +1,51 @@
+/**
+ * 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.portfolio.delinquency.validator;
+
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import lombok.Data;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
+
+@Data
+public class LoanDelinquencyActionData {
+
+    public Long id;
+    public DelinquencyAction action;
+    public LocalDate startDate;
+    public LocalDate endDate;
+    public Long createdById;
+    public OffsetDateTime createdOn;
+    public Long updatedById;
+    public OffsetDateTime lastModifiedOn;
+
+    public LoanDelinquencyActionData(LoanDelinquencyAction 
loanDelinquencyAction) {
+        this.id = loanDelinquencyAction.getId();
+        this.action = loanDelinquencyAction.getAction();
+        this.startDate = loanDelinquencyAction.getStartDate();
+        this.endDate = loanDelinquencyAction.getEndDate();
+
+        loanDelinquencyAction.getCreatedBy().ifPresent(this::setCreatedById);
+        
loanDelinquencyAction.getLastModifiedBy().ifPresent(this::setUpdatedById);
+        this.createdOn = loanDelinquencyAction.getCreatedDateTime();
+        this.lastModifiedOn = loanDelinquencyAction.getLastModifiedDateTime();
+    }
+
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index 0cfd44249..e379e841b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -109,7 +109,9 @@ import 
org.apache.fineract.portfolio.collateralmanagement.service.LoanCollateral
 import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
 import 
org.apache.fineract.portfolio.delinquency.api.DelinquencyApiResourceSwagger;
 import 
org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
 import 
org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
+import 
org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
 import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
 import org.apache.fineract.portfolio.fund.data.FundData;
 import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
@@ -257,6 +259,7 @@ public class LoansApiResource {
     private final DefaultToApiJsonSerializer<LoanAccountData> 
toApiJsonSerializer;
     private final DefaultToApiJsonSerializer<LoanApprovalData> 
loanApprovalDataToApiJsonSerializer;
     private final DefaultToApiJsonSerializer<LoanScheduleData> 
loanScheduleToApiJsonSerializer;
+    private final DefaultToApiJsonSerializer<LoanDelinquencyActionData> 
delinquencyActionSerializer;
     private final ApiRequestParameterHelper apiRequestParameterHelper;
     private final FromJsonHelper fromJsonHelper;
     private final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService;
@@ -778,6 +781,58 @@ public class LoansApiResource {
         return getDelinquencyTagHistory(null, loanExternalId, uriInfo);
     }
 
+    @GET
+    @Path("{loanId}/delinquency-actions")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve delinquency actions related to the loan", 
description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(array = @ArraySchema(schema = @Schema(implementation = 
DelinquencyApiResourceSwagger.GetDelinquencyActionsResponse.class)))) })
+    public String getLoanDelinquencyActions(@PathParam("loanId") 
@Parameter(description = "loanId", required = true) final Long loanId,
+            @Context final UriInfo uriInfo) {
+        return getLoanDelinquencyActions(loanId, null, uriInfo);
+    }
+
+    @GET
+    @Path("external-id/{loanExternalId}/delinquency-actions")
+    @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Retrieve delinquency actions related to the loan", 
description = "")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(array = @ArraySchema(schema = @Schema(implementation = 
DelinquencyApiResourceSwagger.GetDelinquencyActionsResponse.class)))) })
+    public String getLoanDelinquencyActions(
+            @PathParam("loanExternalId") @Parameter(description = 
"loanExternalId", required = true) final String loanExternalId,
+            @Context final UriInfo uriInfo) {
+        return getLoanDelinquencyActions(null, loanExternalId, uriInfo);
+    }
+
+    @POST
+    @Path("{loanId}/delinquency-actions")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Adds a new delinquency action for a loan", 
description = "")
+    @RequestBody(required = true, content = @Content(schema = 
@Schema(implementation = 
DelinquencyApiResourceSwagger.PostLoansDelinquencyActionRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
DelinquencyApiResourceSwagger.PostLoansDelinquencyActionResponse.class))) })
+    public String createLoanDelinquencyAction(@PathParam("loanId") 
@Parameter(description = "loanId", required = true) final Long loanId,
+            @Context final UriInfo uriInfo, @Parameter(hidden = true) final 
String apiRequestBodyAsJson) {
+        return createLoanDelinquencyAction(loanId, ExternalId.empty(), 
apiRequestBodyAsJson);
+    }
+
+    @POST
+    @Path("external-id/{loanExternalId}/delinquency-actions")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Adds a new delinquency action for a loan", 
description = "")
+    @RequestBody(required = true, content = @Content(schema = 
@Schema(implementation = 
DelinquencyApiResourceSwagger.PostLoansDelinquencyActionRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
DelinquencyApiResourceSwagger.PostLoansDelinquencyActionResponse.class))) })
+    public String createLoanDelinquencyAction(
+            @PathParam("loanExternalId") @Parameter(description = 
"loanExternalId", required = true) final String loanExternalId,
+            @Context final UriInfo uriInfo, @Parameter(hidden = true) final 
String apiRequestBodyAsJson) {
+        return createLoanDelinquencyAction(null, 
ExternalIdFactory.produce(loanExternalId), apiRequestBodyAsJson);
+    }
+
     private String retrieveApprovalTemplate(final Long loanId, final String 
loanExternalIdStr, final String templateType,
             final UriInfo uriInfo) {
         
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
@@ -1154,4 +1209,26 @@ public class LoansApiResource {
         }
         return resolvedLoanId;
     }
+
+    private String getLoanDelinquencyActions(final Long loanId, final String 
loanExternalIdStr, final UriInfo uriInfo) {
+        
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+        ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
+        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+
+        final Collection<LoanDelinquencyAction> delinquencyActions = 
this.delinquencyReadPlatformService
+                .retrieveLoanDelinquencyActions(resolvedLoanId);
+        List<LoanDelinquencyActionData> result = 
delinquencyActions.stream().map(LoanDelinquencyActionData::new).toList();
+        return this.jsonSerializerTagHistory.serialize(result);
+    }
+
+    private String createLoanDelinquencyAction(Long loanId, ExternalId 
loanExternalId, String apiRequestBodyAsJson) {
+        
context.authenticatedUser().validateHasUpdatePermission(RESOURCE_NAME_FOR_PERMISSIONS);
+        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+
+        CommandWrapperBuilder builder = new 
CommandWrapperBuilder().createDelinquencyAction(resolvedLoanId);
+        builder.withJson(apiRequestBodyAsJson);
+        CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(builder.build());
+        return delinquencyActionSerializer.serialize(result);
+    }
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index 80a9a8b12..df128b1e1 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -964,9 +964,9 @@ final class LoansApiResourceSwagger {
             private String externalId;
         }
 
-        static final class GetLoansLoanIdCollectionData {
+        static final class GetLoansLoanIdDelinquencySummary {
 
-            private GetLoansLoanIdCollectionData() {}
+            private GetLoansLoanIdDelinquencySummary() {}
 
             @Schema(example = "100.000000")
             public Double availableDisbursementAmount;
@@ -989,6 +989,16 @@ final class LoansApiResourceSwagger {
             public LocalDate lastRepaymentDate;
             @Schema(example = "100.000000")
             public Double lastRepaymentAmount;
+
+            @Schema(example = "true")
+            public Boolean delinquencyCalculationPaused;
+
+            @Schema(example = "[2022, 07, 05]")
+            public LocalDate delinquencyPausePeriodStartDate;
+
+            @Schema(example = "[2022, 07, 10]")
+            public LocalDate delinquencyPausePeriodEndDate;
+
         }
 
         @Schema(example = "1")
@@ -1062,7 +1072,7 @@ final class LoansApiResourceSwagger {
         @Schema(description = "Set of GetLoansLoanIdDisbursementDetails")
         public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
         @Schema(description = "Delinquent data")
-        public GetLoansLoanIdCollectionData delinquent;
+        public GetLoansLoanIdDelinquencySummary delinquent;
         @Schema(description = "Set of charges")
         public List<GetLoansLoanIdLoanChargeData> charges;
         public GetDelinquencyRangesResponse delinquencyRange;
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
index 75d5cad38..6087f19af 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
@@ -43,9 +43,13 @@ public final class CollectionData {
     private LocalDate lastRepaymentDate;
     private BigDecimal lastRepaymentAmount;
 
+    public boolean delinquencyCalculationPaused;
+    public LocalDate delinquencyPausePeriodStartDate;
+    public LocalDate delinquencyPausePeriodEndDate;
+
     public static CollectionData template() {
         final BigDecimal zero = BigDecimal.ZERO;
-        return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, 
null, zero);
+        return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, 
null, zero, false, null, null);
     }
 
 }
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java
new file mode 100644
index 000000000..544ec9498
--- /dev/null
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java
@@ -0,0 +1,204 @@
+/**
+ * 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.portfolio.delinquency.service;
+
+import static java.time.Month.JANUARY;
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.List;
+import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
+import 
org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
+import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
+import 
org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper;
+import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.junit.jupiter.api.Assertions;
+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;
+
+@ExtendWith(MockitoExtension.class)
+class DelinquencyReadPlatformServiceImplTest {
+
+    @Mock
+    private DelinquencyRangeRepository repositoryRange;
+
+    @Mock
+    private DelinquencyBucketRepository repositoryBucket;
+    @Mock
+    private LoanDelinquencyTagHistoryRepository 
repositoryLoanDelinquencyTagHistory;
+    @Mock
+    private DelinquencyRangeMapper mapperRange;
+    @Mock
+    private DelinquencyBucketMapper mapperBucket;
+
+    @Mock
+    private LoanDelinquencyTagMapper mapperLoanDelinquencyTagHistory;
+
+    @Mock
+    private LoanRepository loanRepository;
+
+    @Mock
+    private LoanDelinquencyDomainService loanDelinquencyDomainService;
+
+    @Mock
+    private LoanInstallmentDelinquencyTagRepository 
repositoryLoanInstallmentDelinquencyTag;
+
+    @Mock
+    private LoanDelinquencyActionRepository loanDelinquencyActionRepository;
+
+    @InjectMocks
+    private DelinquencyReadPlatformServiceImpl underTest;
+
+    @Test
+    public void testNoEnrichmentWhenThereIsNoDelinquencyAction() {
+        // given
+        CollectionData collectionData = CollectionData.template();
+        Collection<LoanDelinquencyAction> delinquencyActions = List.of();
+
+        // when
+        underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, 
delinquencyActions, LocalDate.of(2023, JANUARY, 12));
+
+        // then
+        
Assertions.assertFalse(collectionData.isDelinquencyCalculationPaused());
+        
Assertions.assertNull(collectionData.getDelinquencyPausePeriodStartDate());
+        
Assertions.assertNull(collectionData.getDelinquencyPausePeriodEndDate());
+    }
+
+    @Test
+    public void 
testMultiplePausesWithoutResumeActionCurrentlyInPauseFirstDay() {
+        // given
+        CollectionData collectionData = CollectionData.template();
+        Collection<LoanDelinquencyAction> delinquencyActions = List.of(
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
+
+        // when
+        underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, 
delinquencyActions, LocalDate.of(2023, JANUARY, 12));
+
+        // then
+        Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 12), 
collectionData.getDelinquencyPausePeriodStartDate());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 13), 
collectionData.getDelinquencyPausePeriodEndDate());
+    }
+
+    @Test
+    public void testMultiplePausesWithoutResumeActionCurrentlyInPauseLastDay() 
{
+        // given
+        CollectionData collectionData = CollectionData.template();
+        Collection<LoanDelinquencyAction> delinquencyActions = List.of(
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
+
+        // when
+        underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, 
delinquencyActions, LocalDate.of(2023, JANUARY, 13));
+
+        // then
+        Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 12), 
collectionData.getDelinquencyPausePeriodStartDate());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 13), 
collectionData.getDelinquencyPausePeriodEndDate());
+    }
+
+    @Test
+    public void 
testMultiplePausesWithoutResumeActionCurrentBusinessDateBetweenStartAndEndDate()
 {
+        // given
+        CollectionData collectionData = CollectionData.template();
+        Collection<LoanDelinquencyAction> delinquencyActions = List.of(
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 12), LocalDate.of(2023, JANUARY, 14)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
+
+        // when
+        underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, 
delinquencyActions, LocalDate.of(2023, JANUARY, 13));
+
+        // then
+        Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 12), 
collectionData.getDelinquencyPausePeriodStartDate());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 14), 
collectionData.getDelinquencyPausePeriodEndDate());
+    }
+
+    @Test
+    public void 
testMultiplePausesWithoutResumeCurrentBusinessDateIsNotOverlappingWithAnyOfThePauses()
 {
+        // given
+        CollectionData collectionData = CollectionData.template();
+        Collection<LoanDelinquencyAction> delinquencyActions = List.of(
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
+
+        // when
+        underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, 
delinquencyActions, LocalDate.of(2023, JANUARY, 12));
+
+        // then
+        
Assertions.assertFalse(collectionData.isDelinquencyCalculationPaused());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 10), 
collectionData.getDelinquencyPausePeriodStartDate());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 11), 
collectionData.getDelinquencyPausePeriodEndDate());
+    }
+
+    @Test
+    public void testResumeIsAppliedToOneOfThePauseNotActive() {
+        // given
+        CollectionData collectionData = CollectionData.template();
+        Collection<LoanDelinquencyAction> delinquencyActions = List.of(
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
+                new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, 
JANUARY, 11), null),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
+
+        // when
+        underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, 
delinquencyActions, LocalDate.of(2023, JANUARY, 12));
+
+        // then
+        
Assertions.assertFalse(collectionData.isDelinquencyCalculationPaused());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 10), 
collectionData.getDelinquencyPausePeriodStartDate());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 11), 
collectionData.getDelinquencyPausePeriodEndDate());
+    }
+
+    @Test
+    public void testResumeIsAppliedToOneOfThePauseActive() {
+        // given
+        CollectionData collectionData = CollectionData.template();
+        Collection<LoanDelinquencyAction> delinquencyActions = List.of(
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
+                new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, 
JANUARY, 11), null),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
+                new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, 
JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
+
+        // when
+        underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, 
delinquencyActions, LocalDate.of(2023, JANUARY, 11));
+
+        // then
+        Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 10), 
collectionData.getDelinquencyPausePeriodStartDate());
+        Assertions.assertEquals(LocalDate.of(2023, JANUARY, 11), 
collectionData.getDelinquencyPausePeriodEndDate());
+    }
+
+}
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidatorTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidatorTest.java
new file mode 100644
index 000000000..daa161095
--- /dev/null
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidatorTest.java
@@ -0,0 +1,293 @@
+/**
+ * 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.portfolio.delinquency.validator;
+
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
+import static 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.ACTION;
+import static 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.DATE_FORMAT;
+import static 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.END_DATE;
+import static 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.LOCALE;
+import static 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.START_DATE;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.JsonParser;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.mockito.Mockito;
+
+class DelinquencyActionParseAndValidatorTest {
+
+    private final FromJsonHelper fromJsonHelper = new FromJsonHelper();
+    private final DelinquencyActionParseAndValidator underTest = new 
DelinquencyActionParseAndValidator(fromJsonHelper);
+    private static final DateTimeFormatter DATE_TIME_FORMATTER = 
DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.US);
+
+    @Test
+    public void testParseAndValidationIsOKForPause() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        JsonCommand command = delinquencyAction("pause", "09 September 2022", 
"19 September 2022");
+
+        LoanDelinquencyAction parsedDelinquencyAction = 
underTest.validateAndParseUpdate(command, loan, List.of(),
+                localDate("09 September 2022"));
+        Assertions.assertEquals(PAUSE, parsedDelinquencyAction.getAction());
+        Assertions.assertEquals(localDate("09 September 2022"), 
parsedDelinquencyAction.getStartDate());
+        Assertions.assertEquals(localDate("19 September 2022"), 
parsedDelinquencyAction.getEndDate());
+    }
+
+    @Test
+    public void testParseAndValidationIsOKForResume() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        JsonCommand command = delinquencyAction("resume", "09 September 2022", 
null);
+
+        LoanDelinquencyAction parsedDelinquencyAction = 
underTest.validateAndParseUpdate(command, loan,
+                List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 
September 2022")), localDate("09 September 2022"));
+        Assertions.assertEquals(DelinquencyAction.RESUME, 
parsedDelinquencyAction.getAction());
+        Assertions.assertEquals(localDate("09 September 2022"), 
parsedDelinquencyAction.getStartDate());
+        Assertions.assertNull(parsedDelinquencyAction.getEndDate());
+    }
+
+    @Test
+    public void testPauseBothStartAndEndDateIsOverlappingWithAnActivePause() 
throws JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        List<LoanDelinquencyAction> existing = 
List.of(loanDelinquencyAction(PAUSE, "14 September 2022", "22 September 2022"));
+        JsonCommand command = delinquencyAction("pause", "09 September 2022", 
"15 September 2022");
+
+        assertPlatformValidationException("Delinquency pause period cannot 
overlap with another pause period",
+                "loan-delinquency-action-overlapping",
+                () -> underTest.validateAndParseUpdate(command, loan, 
existing, localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testPauseStartIsOverlappingWithAnActivePause() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        List<LoanDelinquencyAction> existing = 
List.of(loanDelinquencyAction(PAUSE, "14 September 2022", "22 September 2022"));
+        JsonCommand command = delinquencyAction("pause", "15 September 2022", 
"23 September 2022");
+
+        assertPlatformValidationException("Delinquency pause period cannot 
overlap with another pause period",
+                "loan-delinquency-action-overlapping",
+                () -> underTest.validateAndParseUpdate(command, loan, 
existing, localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testNewPauseEndIsTheSameAsExistingPauseStart() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        List<LoanDelinquencyAction> existing = 
List.of(loanDelinquencyAction(PAUSE, "15 September 2022", "22 September 2022"));
+        JsonCommand command = delinquencyAction("pause", "09 September 2022", 
"15 September 2022");
+
+        assertPlatformValidationException("Delinquency pause period cannot 
overlap with another pause period",
+                "loan-delinquency-action-overlapping",
+                () -> underTest.validateAndParseUpdate(command, loan, 
existing, localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testNewPauseEndIsTheSameAsExistingPauseEnd() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        List<LoanDelinquencyAction> existing = 
List.of(loanDelinquencyAction(PAUSE, "15 September 2022", "22 September 2022"));
+        JsonCommand command = delinquencyAction("pause", "22 September 2022", 
"25 September 2022");
+
+        assertPlatformValidationException("Delinquency pause period cannot 
overlap with another pause period",
+                "loan-delinquency-action-overlapping",
+                () -> underTest.validateAndParseUpdate(command, loan, 
existing, localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testNewPauseEndIsOverlappingWithExistingPause() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        List<LoanDelinquencyAction> existing = 
List.of(loanDelinquencyAction(PAUSE, "15 September 2022", "22 September 2022"));
+        JsonCommand command = delinquencyAction("pause", "13 September 2022", 
"20 September 2022");
+
+        assertPlatformValidationException("Delinquency pause period cannot 
overlap with another pause period",
+                "loan-delinquency-action-overlapping",
+                () -> underTest.validateAndParseUpdate(command, loan, 
existing, localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testNewPauseIsNotOverlappingBecauseThereWasAResume() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        JsonCommand command = delinquencyAction("pause", "18 September 2022", 
"20 September 2022");
+
+        List<LoanDelinquencyAction> existing = 
List.of(loanDelinquencyAction(PAUSE, "15 September 2022", "22 September 2022"), 
//
+                loanDelinquencyAction(RESUME, "17 September 2022") //
+        );
+
+        LoanDelinquencyAction parsedDelinquencyAction = 
underTest.validateAndParseUpdate(command, loan, existing,
+                localDate("18 September 2022"));
+        Assertions.assertEquals(PAUSE, parsedDelinquencyAction.getAction());
+        Assertions.assertEquals(localDate("18 September 2022"), 
parsedDelinquencyAction.getStartDate());
+        Assertions.assertEquals(localDate("20 September 2022"), 
parsedDelinquencyAction.getEndDate());
+    }
+
+    @Test
+    public void testResumeIsNotOverlappingWithAnActivePause() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        List<LoanDelinquencyAction> existing = 
List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "08 September 2022"));
+        JsonCommand command = delinquencyAction("resume", "09 September 2022", 
null);
+
+        assertPlatformValidationException("Resume Delinquency Action can only 
be created during an active pause",
+                "loan-delinquency-action-resume-should-be-on-pause",
+                () -> underTest.validateAndParseUpdate(command, loan, 
existing, localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testValidationErrorWhenDelinquencyActionIsMissing() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.APPROVED);
+
+        JsonCommand command = delinquencyAction(null, "09 September 2022", "19 
September 2022");
+
+        assertPlatformValidationException("Delinquency Action must not be null 
or empty", "loan-delinquency-action-missing-action",
+                () -> underTest.validateAndParseUpdate(command, loan, 
List.of(), localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testValidationErrorWhenLoanIsNotActive() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.APPROVED);
+
+        JsonCommand command = delinquencyAction("pause", "09 September 2022", 
"19 September 2022");
+
+        assertPlatformValidationException("Delinquency actions can be created 
only for active loans.",
+                "loan-delinquency-action-invalid-loan-state",
+                () -> underTest.validateAndParseUpdate(command, loan, 
List.of(), localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testValidationErrorResumeShouldHaveNoEndDate() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        JsonCommand command = delinquencyAction("resume", "09 September 2022", 
"19 September 2022");
+
+        assertPlatformValidationException("Resume Delinquency action can not 
have end date",
+                "loan-delinquency-action-resume-should-have-no-end-date",
+                () -> underTest.validateAndParseUpdate(command, loan, 
List.of(), localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testValidationErrorResumeInvalidStartDate() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        JsonCommand command = delinquencyAction("resume", "09 September 2022", 
"19 September 2022");
+
+        assertPlatformValidationException("Start date of the Resume 
Delinquency action must be the current business date",
+                "loan-delinquency-action-invalid-start-date",
+                () -> underTest.validateAndParseUpdate(command, loan, 
List.of(), localDate("10 September 2022")));
+    }
+
+    @Test
+    public void testValidationErrorPausePeriodShouldBeAtLeastOneDay() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        JsonCommand command = delinquencyAction("pause", "10 September 2022", 
"10 September 2022");
+
+        assertPlatformValidationException("Delinquency pause period must be at 
least one day",
+                "loan-delinquency-action-invalid-start-date-and-end-date",
+                () -> underTest.validateAndParseUpdate(command, loan, 
List.of(), localDate("09 September 2022")));
+    }
+
+    @Test
+    public void testValidationErrorPausePeriodMustBeInFuture() throws 
JsonProcessingException {
+        Loan loan = Mockito.mock(Loan.class);
+        Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
+
+        JsonCommand command = delinquencyAction("pause", "08 September 2022", 
"09 September 2022");
+
+        assertPlatformValidationException("Start date of pause period must be 
in the future", "loan-delinquency-action-invalid-start-date",
+                () -> underTest.validateAndParseUpdate(command, loan, 
List.of(), localDate("09 September 2022")));
+    }
+
+    @NotNull
+    private JsonCommand delinquencyAction(String action, String startDate, 
String endDate) throws JsonProcessingException {
+        Map<String, Object> map = new HashMap<>();
+        map.put(ACTION, action);
+        map.put(DATE_FORMAT, "dd MMMM yyyy");
+        map.put(LOCALE, "en");
+        map.put(START_DATE, startDate);
+        map.put(END_DATE, endDate);
+        return createJsonCommand(map);
+    }
+
+    private LocalDate localDate(String date) {
+        return LocalDate.parse(date, DATE_TIME_FORMATTER);
+    }
+
+    @NotNull
+    private JsonCommand createJsonCommand(Map<String, Object> jsonMap) throws 
JsonProcessingException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        String json = 
objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonMap);
+        return new JsonCommand(null, JsonParser.parseString(json));
+    }
+
+    private void assertPlatformValidationException(String message, String 
code, Executable executable) {
+        PlatformApiDataValidationException validationException = 
assertThrows(PlatformApiDataValidationException.class, executable);
+        assertPlatformException(message, code, validationException);
+    }
+
+    private void assertPlatformException(String expectedMessage, String 
expectedCode,
+            PlatformApiDataValidationException 
platformApiDataValidationException) {
+        Assertions.assertEquals(expectedMessage, 
platformApiDataValidationException.getErrors().get(0).getDefaultUserMessage());
+        Assertions.assertEquals(expectedCode, 
platformApiDataValidationException.getErrors().get(0).getUserMessageGlobalisationCode());
+    }
+
+    private LoanDelinquencyAction loanDelinquencyAction(DelinquencyAction 
action, String startTime, String endTime) {
+        return new LoanDelinquencyAction(null, action, localDate(startTime), 
localDate(endTime));
+    }
+
+    private LoanDelinquencyAction loanDelinquencyAction(DelinquencyAction 
action, String startTime) {
+        return new LoanDelinquencyAction(null, action, localDate(startTime), 
null);
+    }
+
+}
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
index ab85206e2..d821b5f77 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
@@ -127,7 +127,7 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
         CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null);
+                null, null, false, null, null);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
 
@@ -239,10 +239,10 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
         CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null);
+                null, null, false, null, null);
 
         CollectionData installmentCollectionData = new 
CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate,
-                installmentPrincipalAmount, null, null, null, null);
+                installmentPrincipalAmount, null, null, null, null, false, 
null, null);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
         installmentsCollection.put(1L, installmentCollectionData);
@@ -305,10 +305,10 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
         CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
29L, null, 29L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null);
+                null, null, false, null, null);
 
         CollectionData installmentCollectionData = new 
CollectionData(BigDecimal.ZERO, 29L, null, 29L, overDueSinceDate,
-                installmentPrincipalAmount, null, null, null, null);
+                installmentPrincipalAmount, null, null, null, null, false, 
null, null);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
         installmentsCollection.put(1L, installmentCollectionData);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index 5a8bfd240..1c8168cad 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -79,6 +79,7 @@ public abstract class BaseLoanIntegrationTest {
     protected final LoanProductHelper loanProductHelper = new 
LoanProductHelper();
     protected JournalEntryHelper journalEntryHelper = new 
JournalEntryHelper(requestSpec, responseSpec);
     protected BusinessDateHelper businessDateHelper = new BusinessDateHelper();
+    protected DateTimeFormatter dateTimeFormatter = 
DateTimeFormatter.ofPattern(DATETIME_PATTERN);
 
     // asset
     protected final Account loansReceivableAccount = 
accountHelper.createAssetAccount();
@@ -229,7 +230,6 @@ public abstract class BaseLoanIntegrationTest {
             assertNull(loanDetails.getTransactions(), "No transaction is 
expected");
         } else {
             Assertions.assertEquals(transactions.length, 
loanDetails.getTransactions().size());
-            DateTimeFormatter dateTimeFormatter = 
DateTimeFormatter.ofPattern(DATETIME_PATTERN);
             Arrays.stream(transactions).forEach(tr -> {
                 boolean found = loanDetails.getTransactions().stream()
                         .anyMatch(item -> Objects.equals(item.getAmount(), 
tr.amount) && Objects.equals(item.getType().getValue(), tr.type)
@@ -329,8 +329,13 @@ public abstract class BaseLoanIntegrationTest {
 
     protected Long applyAndApproveLoan(Long clientId, Long loanProductId, 
String loanDisbursementDate, Double amount,
             int numberOfRepayments) {
-        PostLoansResponse postLoansResponse = loanTransactionHelper
-                .applyLoan(applyLoanRequest(clientId, loanProductId, 
loanDisbursementDate, amount, numberOfRepayments));
+        return applyAndApproveLoan(clientId, loanProductId, 
loanDisbursementDate, amount, numberOfRepayments, null);
+    }
+
+    protected Long applyAndApproveLoan(Long clientId, Long loanProductId, 
String loanDisbursementDate, Double amount,
+            int numberOfRepayments, String externalId) {
+        PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(
+                applyLoanRequest(clientId, loanProductId, 
loanDisbursementDate, amount, numberOfRepayments).externalId(externalId));
 
         PostLoansLoanIdResponse approvedLoanResult = 
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
                 approveLoanRequest(amount));
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java
new file mode 100644
index 000000000..262f106c1
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java
@@ -0,0 +1,259 @@
+/**
+ * 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.integrationtests;
+
+import static java.lang.Boolean.TRUE;
+import static 
org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
+import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.UUID;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.BusinessDateRequest;
+import org.apache.fineract.client.models.GetDelinquencyActionsResponse;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
+import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
+import org.apache.fineract.client.util.CallFailedRuntimeException;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@Slf4j
+@ExtendWith(LoanTestLifecycleExtension.class)
+public class DelinquencyActionIntegrationTests extends BaseLoanIntegrationTest 
{
+
+    public static final BigDecimal DOWN_PAYMENT_PERCENTAGE = new 
BigDecimal(25);
+    private final ClientHelper clientHelper = new 
ClientHelper(this.requestSpec, this.responseSpec);
+
+    @Test
+    public void testCreateAndReadPauseDelinquencyAction() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0, 2);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Create Delinquency Pause for the Loan
+            PostLoansDelinquencyActionResponse response = 
loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE,
+                    "10 January 2023", "15 January 2023");
+
+            List<GetDelinquencyActionsResponse> loanDelinquencyActions = 
loanTransactionHelper.getLoanDelinquencyActions(loanId);
+            Assertions.assertNotNull(loanDelinquencyActions);
+            Assertions.assertEquals(1, loanDelinquencyActions.size());
+            Assertions.assertEquals("PAUSE", 
loanDelinquencyActions.get(0).getAction());
+            Assertions.assertEquals(LocalDate.parse("10 January 2023", 
dateTimeFormatter), loanDelinquencyActions.get(0).getStartDate());
+            Assertions.assertEquals(LocalDate.parse("15 January 2023", 
dateTimeFormatter), loanDelinquencyActions.get(0).getEndDate());
+        });
+    }
+
+    @Test
+    public void testCreateAndReadPauseDelinquencyActionUsingExternalId() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Create external ID
+            String externalId = UUID.randomUUID().toString();
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0, 2, externalId);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Create Delinquency Pause for the Loan
+            PostLoansDelinquencyActionResponse response = 
loanTransactionHelper.createLoanDelinquencyAction(externalId, PAUSE,
+                    "10 January 2023", "15 January 2023");
+
+            List<GetDelinquencyActionsResponse> loanDelinquencyActions = 
loanTransactionHelper.getLoanDelinquencyActions(externalId);
+            Assertions.assertNotNull(loanDelinquencyActions);
+            Assertions.assertEquals(1, loanDelinquencyActions.size());
+            Assertions.assertEquals("PAUSE", 
loanDelinquencyActions.get(0).getAction());
+            Assertions.assertEquals(LocalDate.parse("10 January 2023", 
dateTimeFormatter), loanDelinquencyActions.get(0).getStartDate());
+            Assertions.assertEquals(LocalDate.parse("15 January 2023", 
dateTimeFormatter), loanDelinquencyActions.get(0).getEndDate());
+        });
+    }
+
+    @Test
+    public void testCreatePauseAndResumeDelinquencyAction() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0, 2);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Create Delinquency Pause for the Loan
+            loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, 
"10 January 2023", "15 January 2023");
+
+            // Update business date
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("14 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // Create 2nd Delinquency Resume for the Loan
+            loanTransactionHelper.createLoanDelinquencyAction(loanId, RESUME, 
"14 January 2023");
+
+            List<GetDelinquencyActionsResponse> loanDelinquencyActions = 
loanTransactionHelper.getLoanDelinquencyActions(loanId);
+            Assertions.assertNotNull(loanDelinquencyActions);
+            Assertions.assertEquals(2, loanDelinquencyActions.size());
+
+            Assertions.assertEquals("PAUSE", 
loanDelinquencyActions.get(0).getAction());
+            Assertions.assertEquals(LocalDate.parse("10 January 2023", 
dateTimeFormatter), loanDelinquencyActions.get(0).getStartDate());
+            Assertions.assertEquals(LocalDate.parse("15 January 2023", 
dateTimeFormatter), loanDelinquencyActions.get(0).getEndDate());
+
+            Assertions.assertEquals("RESUME", 
loanDelinquencyActions.get(1).getAction());
+            Assertions.assertEquals(LocalDate.parse("14 January 2023", 
dateTimeFormatter), loanDelinquencyActions.get(1).getStartDate());
+        });
+    }
+
+    @Test
+    public void testCreatePauseAndResumeDelinquencyActionWithStatusFlag() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0, 2);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Create Delinquency Pause for the Loan
+            loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, 
"10 January 2023", "15 January 2023");
+
+            // Update business date
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("14 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // Validate Loan Delinquency Pause Period on Loan
+            validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "15 
January 2023", true);
+
+            // Create a Resume for the Loan for the current business date, it 
is still expected to be in pause
+            loanTransactionHelper.createLoanDelinquencyAction(loanId, RESUME, 
"14 January 2023");
+
+            // Validate Loan Delinquency Pause Period on Loan
+            validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "14 
January 2023", true);
+
+            // Update business date to 15 January 2023
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // Validate Loan Delinquency Pause Period on Loan
+            validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "14 
January 2023", false);
+
+            // Create a new pause action for the future
+            loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, 
"20 January 2023", "25 January 2023");
+
+            // Validate Loan Delinquency Pause Period on Loan
+            validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "14 
January 2023", false);
+        });
+    }
+
+    @Test
+    public void testValidationErrorIsThrownWhenCreatingActionInThePast() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0, 2);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Create Delinquency Pause for the Loan in the past
+            CallFailedRuntimeException exception = 
assertThrows(CallFailedRuntimeException.class,
+                    () -> 
loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, "05 December 
2022", "15 January 2023"));
+            assertTrue(exception.getMessage().contains("Start date of pause 
period must be in the future"));
+        });
+    }
+
+    private void validateLoanDelinquencyPausePerios(Long loanId, String 
expectedStart, String expectedEnd, Boolean expectedPauseState) {
+        GetLoansLoanIdResponse loan = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
+        Assertions.assertNotNull(loan.getDelinquent());
+        Assertions.assertEquals(expectedPauseState, 
loan.getDelinquent().getDelinquencyCalculationPaused());
+        Assertions.assertEquals(LocalDate.parse(expectedStart, 
dateTimeFormatter),
+                loan.getDelinquent().getDelinquencyPausePeriodStartDate());
+        Assertions.assertEquals(LocalDate.parse(expectedEnd, 
dateTimeFormatter), loan.getDelinquent().getDelinquencyPausePeriodEndDate());
+    }
+
+    private Long createLoanProductWith25PctDownPayment(boolean 
autoDownPaymentEnabled, boolean multiDisburseEnabled) {
+        PostLoanProductsRequest product = 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
+        product.setMultiDisburseLoan(multiDisburseEnabled);
+
+        if (!multiDisburseEnabled) {
+            product.disallowExpectedDisbursements(null);
+            product.setAllowApprovedDisbursedAmountsOverApplied(null);
+            product.overAppliedCalculationType(null);
+            product.overAppliedNumber(null);
+        }
+
+        product.setEnableDownPayment(true);
+        
product.setDisbursedAmountPercentageForDownPayment(DOWN_PAYMENT_PERCENTAGE);
+        product.setEnableAutoRepaymentForDownPayment(autoDownPaymentEnabled);
+
+        PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(product);
+        GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = 
loanProductHelper
+                .retrieveLoanProductById(loanProductResponse.getResourceId());
+
+        Long loanProductId = loanProductResponse.getResourceId();
+
+        assertEquals(TRUE, 
getLoanProductsProductIdResponse.getEnableDownPayment());
+        
assertNotNull(getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment());
+        assertEquals(0, 
getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment().compareTo(DOWN_PAYMENT_PERCENTAGE));
+        assertEquals(autoDownPaymentEnabled, 
getLoanProductsProductIdResponse.getEnableAutoRepaymentForDownPayment());
+        assertEquals(multiDisburseEnabled, 
getLoanProductsProductIdResponse.getMultiDisburseLoan());
+        return loanProductId;
+    }
+
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
index 928ea3e60..eb5395d2f 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
@@ -41,7 +41,7 @@ import 
org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
 import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
 import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
-import org.apache.fineract.client.models.GetLoansLoanIdCollectionData;
+import org.apache.fineract.client.models.GetLoansLoanIdDelinquencySummary;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
@@ -903,7 +903,7 @@ public class DelinquencyBucketsIntegrationTest {
             getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
             loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
 
-            GetLoansLoanIdCollectionData getLoansLoanIdCollectionData = 
getLoansLoanIdResponse.getDelinquent();
+            GetLoansLoanIdDelinquencySummary getLoansLoanIdCollectionData = 
getLoansLoanIdResponse.getDelinquent();
             assertNotNull(getLoansLoanIdCollectionData);
             assertEquals(0, getLoansLoanIdCollectionData.getDelinquentDays());
             assertEquals(0, getLoansLoanIdCollectionData.getPastDueDays());
@@ -992,7 +992,7 @@ public class DelinquencyBucketsIntegrationTest {
             getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
             loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
 
-            GetLoansLoanIdCollectionData getLoansLoanIdCollectionData = 
getLoansLoanIdResponse.getDelinquent();
+            GetLoansLoanIdDelinquencySummary getLoansLoanIdCollectionData = 
getLoansLoanIdResponse.getDelinquent();
             assertNotNull(getLoansLoanIdCollectionData);
             assertEquals(0, getLoansLoanIdCollectionData.getDelinquentDays());
             assertEquals(0, getLoansLoanIdCollectionData.getPastDueDays());
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 5b6f78e5f..4794baf70 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -45,13 +45,14 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.AdvancedPaymentData;
 import 
org.apache.fineract.client.models.DeleteLoansLoanIdChargesChargeIdResponse;
 import org.apache.fineract.client.models.DeleteLoansLoanIdResponse;
+import org.apache.fineract.client.models.GetDelinquencyActionsResponse;
 import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
 import org.apache.fineract.client.models.GetLoanProductsResponse;
 import org.apache.fineract.client.models.GetLoansApprovalTemplateResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdChargesTemplateResponse;
-import org.apache.fineract.client.models.GetLoansLoanIdCollectionData;
+import org.apache.fineract.client.models.GetLoansLoanIdDelinquencySummary;
 import org.apache.fineract.client.models.GetLoansLoanIdDisbursementDetails;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
@@ -63,6 +64,8 @@ import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionId
 import org.apache.fineract.client.models.GetPaymentTypesResponse;
 import org.apache.fineract.client.models.PostLoanProductsRequest;
 import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PostLoansDelinquencyActionRequest;
+import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
 import 
org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest;
@@ -88,6 +91,7 @@ import 
org.apache.fineract.integrationtests.client.IntegrationTest;
 import org.apache.fineract.integrationtests.common.CommonConstants;
 import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
 import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ss.usermodel.Workbook;
 
@@ -279,6 +283,32 @@ public class LoanTransactionHelper extends IntegrationTest 
{
         return GSON.fromJson(response, delinquencyTagsListType);
     }
 
+    public List<GetDelinquencyActionsResponse> getLoanDelinquencyActions(final 
Long loanID) {
+        return ok(fineract().loans.getLoanDelinquencyActions(loanID));
+    }
+
+    public List<GetDelinquencyActionsResponse> 
getLoanDelinquencyActions(String externalId) {
+        return ok(fineract().loans.getLoanDelinquencyActions1(externalId));
+    }
+
+    public PostLoansDelinquencyActionResponse 
createLoanDelinquencyAction(final Long loanid, DelinquencyAction action, String 
startDate,
+            String endDate) {
+        PostLoansDelinquencyActionRequest postLoansDelinquencyAction = new 
PostLoansDelinquencyActionRequest().action(action.name())
+                
.startDate(startDate).endDate(endDate).locale("en").dateFormat("dd MMMM yyyy");
+        return ok(fineract().loans.createLoanDelinquencyAction(loanid, 
postLoansDelinquencyAction));
+    }
+
+    public PostLoansDelinquencyActionResponse 
createLoanDelinquencyAction(String externalId, DelinquencyAction action, String 
startDate,
+            String endDate) {
+        PostLoansDelinquencyActionRequest postLoansDelinquencyAction = new 
PostLoansDelinquencyActionRequest().action(action.name())
+                
.startDate(startDate).endDate(endDate).locale("en").dateFormat("dd MMMM yyyy");
+        return ok(fineract().loans.createLoanDelinquencyAction1(externalId, 
postLoansDelinquencyAction));
+    }
+
+    public PostLoansDelinquencyActionResponse 
createLoanDelinquencyAction(final Long loanid, DelinquencyAction action, String 
startDate) {
+        return createLoanDelinquencyAction(loanid, action, startDate, null);
+    }
+
     public Object getLoanProductDetail(final RequestSpecification requestSpec, 
final ResponseSpecification responseSpec,
             final Integer loanProductId, final String jsonAttributeToGetBack) {
         final String URL = "/fineract-provider/api/v1/loanproducts/" + 
loanProductId + "?associations=all&" + Utils.TENANT_IDENTIFIER;
@@ -1638,7 +1668,7 @@ public class LoanTransactionHelper extends 
IntegrationTest {
     }
 
     public void printDelinquencyData(GetLoansLoanIdResponse 
getLoansLoanIdResponse) {
-        GetLoansLoanIdCollectionData getLoansLoanIdCollectionData = 
getLoansLoanIdResponse.getDelinquent();
+        GetLoansLoanIdDelinquencySummary getLoansLoanIdCollectionData = 
getLoansLoanIdResponse.getDelinquent();
         if (getLoansLoanIdCollectionData != null) {
             log.info("Loan Delinquency {}", 
getLoansLoanIdCollectionData.toString());
         }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java
index e034d6c6d..295322beb 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java
@@ -33,7 +33,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.DeleteDelinquencyBucketResponse;
 import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
 import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
-import org.apache.fineract.client.models.GetLoansLoanIdCollectionData;
+import org.apache.fineract.client.models.GetLoansLoanIdDelinquencySummary;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
 import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
@@ -121,7 +121,7 @@ public class DelinquencyBucketsHelper {
 
     public static void evaluateLoanCollectionData(GetLoansLoanIdResponse 
getLoansLoanIdResponse, Integer pastDueDays,
             Double amountExpected) {
-        GetLoansLoanIdCollectionData getCollectionData = 
getLoansLoanIdResponse.getDelinquent();
+        GetLoansLoanIdDelinquencySummary getCollectionData = 
getLoansLoanIdResponse.getDelinquent();
         if (getCollectionData != null) {
             log.info("Loan Delinquency Data in Days {} and Amount {}", 
getCollectionData.getPastDueDays(),
                     getCollectionData.getDelinquentAmount());

Reply via email to