FINERACT-1971: Fix Delinquency bucket error upon Loan product editing
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
index 8bf064a..6db51d8 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
@@ -209,7 +209,7 @@
     private Integer overAppliedNumber;
 
     @ManyToOne(fetch = FetchType.LAZY)
-    @JoinColumn(name = "delinquency_bucket_id")
+    @JoinColumn(name = "delinquency_bucket_id", nullable = true)
     private DelinquencyBucket delinquencyBucket;
 
     @Column(name = "enable_installment_level_delinquency", nullable = false)
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index a672db5..91b10f4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -501,7 +501,7 @@
             final boolean isRatesEnabled = false;
 
             // Delinquency Buckets
-            final Long delinquencyBucketId = rs.getLong("delinquencyBucketId");
+            final Long delinquencyBucketId = JdbcSupport.getLong(rs, "delinquencyBucketId");
             final String delinquencyBucketName = rs.getString("delinquencyBucketName");
             final DelinquencyBucketData delinquencyBucket = new DelinquencyBucketData(delinquencyBucketId, delinquencyBucketName,
                     new ArrayList<>());
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
index 6f94cbd..f06014a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
@@ -127,9 +127,8 @@
                     loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(loanTransactionProcessingStrategyCode).getName());
 
             if (command.parameterExists("delinquencyBucketId")) {
-                DelinquencyBucket delinquencyBucket = this.delinquencyBucketRepository
-                        .getReferenceById(command.longValueOfParameterNamed("delinquencyBucketId"));
-                loanProduct.setDelinquencyBucket(delinquencyBucket);
+                loanProduct
+                        .setDelinquencyBucket(findDelinquencyBucketIdIfProvided(command.longValueOfParameterNamed("delinquencyBucketId")));
             }
 
             this.loanProductRepository.saveAndFlush(loanProduct);
@@ -168,6 +167,15 @@
         return fund;
     }
 
+    private DelinquencyBucket findDelinquencyBucketIdIfProvided(final Long delinquencyBucketId) {
+        DelinquencyBucket delinquencyBucket = null;
+        if (delinquencyBucketId != null) {
+            delinquencyBucket = delinquencyBucketRepository.findById(delinquencyBucketId)
+                    .orElseThrow(() -> new FundNotFoundException(delinquencyBucketId));
+        }
+        return delinquencyBucket;
+    }
+
     @Transactional
     @Override
     public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) {
@@ -201,9 +209,7 @@
             }
 
             if (changes.containsKey("delinquencyBucketId")) {
-                final Long delinquencyBucketId = (Long) changes.get("delinquencyBucketId");
-                final DelinquencyBucket delinquencyBucket = this.delinquencyBucketRepository.getReferenceById(delinquencyBucketId);
-                product.setDelinquencyBucket(delinquencyBucket);
+                product.setDelinquencyBucket(findDelinquencyBucketIdIfProvided((Long) changes.get("delinquencyBucketId")));
             }
 
             if (changes.containsKey("transactionProcessingStrategyCode")) {
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 80b8e96..9b3953a 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
@@ -285,12 +285,20 @@
         // Loan product creation without Delinquency bucket
         GetLoanProductsProductIdResponse getLoanProductResponse = createLoanProduct(loanTransactionHelper, null, null);
         assertNotNull(getLoanProductResponse);
+        assertNull(getLoanProductResponse.getDelinquencyBucket().getId());
 
         // Loan product creation with Delinquency bucket
         getLoanProductResponse = createLoanProduct(loanTransactionHelper, Math.toIntExact(delinquencyBucket.getId()), null);
         assertNotNull(getLoanProductResponse);
         log.info("Loan Product Bucket Name: {}", getLoanProductResponse.getDelinquencyBucket().getName());
         assertEquals(getLoanProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
+
+        // Update Loan product to remove the Delinquency bucket
+        final Long loanProductId = getLoanProductResponse.getId();
+        loanTransactionHelper.updateLoanProduct(loanProductId, "{delinquencyBucketId: null}");
+        getLoanProductResponse = loanTransactionHelper.getLoanProduct(loanProductId.intValue());
+        assertNotNull(getLoanProductResponse);
+        assertNull(getLoanProductResponse.getDelinquencyBucket().getId());
     }
 
     @Test
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 c00dcac..3d3de06 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
@@ -100,6 +100,7 @@
 public class LoanTransactionHelper extends IntegrationTest {
 
     public static final String DATE_TIME_FORMAT = "dd MMMM yyyy HH:mm";
+    private static final String LOAN_PRODUCTS_URL = "/fineract-provider/api/v1/loanproducts";
     private static final String CREATE_LOAN_PRODUCT_URL = "/fineract-provider/api/v1/loanproducts?" + Utils.TENANT_IDENTIFIER;
     private static final String APPLY_LOAN_URL = "/fineract-provider/api/v1/loans?" + Utils.TENANT_IDENTIFIER;
     private static final String LOAN_ACCOUNT_URL = "/fineract-provider/api/v1/loans";
@@ -1975,4 +1976,10 @@
         final String ADD_CHARGES_URL = LOAN_ACCOUNT_URL + "/" + loanId + "/charges?" + Utils.TENANT_IDENTIFIER;
         return Utils.performServerPost(requestSpec, responseSpec, ADD_CHARGES_URL, request, jsonAttributeToGetBack);
     }
+
+    public Object updateLoanProduct(final Long loanProductId, final String request) {
+        final String UPDATE_LOAN_PRODUCT_URL = LOAN_PRODUCTS_URL + "/" + loanProductId + "?" + Utils.TENANT_IDENTIFIER;
+        return Utils.performServerPut(requestSpec, responseSpec, UPDATE_LOAN_PRODUCT_URL, request, null);
+    }
+
 }