From 92016724bde95e88c0ebd0b55ca4ae8dacd4877c Mon Sep 17 00:00:00 2001 From: "Rym.Ghosn" Date: Wed, 19 Aug 2026 13:04:36 +0300 Subject: [PATCH] FINERACT-2772: Fix TaxGroup mapping lookup NPE and expose businessDate on TaxGroupData TaxGroup.findOneBy() threw TaxMappingNotFoundException as soon as the first existing mapping didn't match instead of checking the rest, and could NPE via getId().equals(...) when an existing mapping's id was null. Both surfaced as intermittent Internal Server Errors when adding a new component to a tax group that already had multiple mappings. Also exposes the read-side businessDate on TaxGroupData so clients can validate a new component's start date against it before submitting. --- .../portfolio/tax/data/TaxGroupData.java | 14 +++++++++++--- .../tax/service/TaxReadPlatformServiceImpl.java | 4 +++- .../fineract/portfolio/tax/domain/TaxGroup.java | 17 ++++++++++------- .../portfolio/tax/mapper/TaxGroupMapper.java | 1 + 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java index 5f9804536f6..d99926f644c 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java @@ -19,6 +19,7 @@ package org.apache.fineract.portfolio.tax.data; import java.io.Serializable; +import java.time.LocalDate; import java.util.Collection; import lombok.AllArgsConstructor; import lombok.Getter; @@ -35,21 +36,28 @@ public final class TaxGroupData implements Serializable { @SuppressWarnings("unused") private final Collection taxComponents; + // System business date, exposed so clients can validate a new tax component's start date client-side + private final LocalDate businessDate; + public static TaxGroupData lookup(final Long id, final String name) { final Collection taxComponents = null; final Collection taxAssociations = null; - return new TaxGroupData(id, name, taxAssociations, taxComponents); + return new TaxGroupData(id, name, taxAssociations, taxComponents, null); } public static TaxGroupData template(final Collection taxComponents) { final Long id = null; final String name = null; final Collection taxAssociations = null; - return new TaxGroupData(id, name, taxAssociations, taxComponents); + return new TaxGroupData(id, name, taxAssociations, taxComponents, null); } public static TaxGroupData template(final TaxGroupData taxGroupData, final Collection taxComponents) { - return new TaxGroupData(taxGroupData.id, taxGroupData.name, taxGroupData.taxAssociations, taxComponents); + return new TaxGroupData(taxGroupData.id, taxGroupData.name, taxGroupData.taxAssociations, taxComponents, taxGroupData.businessDate); + } + + public static TaxGroupData withBusinessDate(final TaxGroupData taxGroupData, final LocalDate businessDate) { + return new TaxGroupData(taxGroupData.id, taxGroupData.name, taxGroupData.taxAssociations, taxGroupData.taxComponents, businessDate); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformServiceImpl.java index 9c4bc698a06..c93ac8e5200 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxReadPlatformServiceImpl.java @@ -22,6 +22,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService; +import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.portfolio.tax.data.TaxComponentData; import org.apache.fineract.portfolio.tax.data.TaxGroupData; import org.apache.fineract.portfolio.tax.domain.TaxComponentRepository; @@ -65,7 +66,8 @@ public List retrieveAllTaxGroups() { @Override public TaxGroupData retrieveTaxGroupData(final Long id) { - return taxGroupMapper.map(taxGroupRepositoryWrapper.findOneWithNotFoundDetection(id)); + final TaxGroupData taxGroupData = taxGroupMapper.map(taxGroupRepositoryWrapper.findOneWithNotFoundDetection(id)); + return TaxGroupData.withBusinessDate(taxGroupData, DateUtils.getBusinessLocalDate()); } @Override diff --git a/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java b/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java index b03ceae25ad..5e0c22a2129 100644 --- a/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java +++ b/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/domain/TaxGroup.java @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.fineract.infrastructure.core.api.JsonCommand; @@ -93,15 +94,17 @@ public Map update(final JsonCommand command, final Set getTaxGroupMappings() { diff --git a/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/mapper/TaxGroupMapper.java b/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/mapper/TaxGroupMapper.java index 3edadeef55b..3ed8965f242 100644 --- a/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/mapper/TaxGroupMapper.java +++ b/fineract-tax/src/main/java/org/apache/fineract/portfolio/tax/mapper/TaxGroupMapper.java @@ -30,6 +30,7 @@ public interface TaxGroupMapper { @Mapping(target = "taxAssociations", source = "taxGroup.taxGroupMappings") @Mapping(target = "taxComponents", ignore = true) + @Mapping(target = "businessDate", ignore = true) TaxGroupData map(TaxGroup taxGroup); List map(List taxGroups);