From ef1cfd0aeba95054056af125ced8c1f4bce38567 Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Wed, 23 Aug 2023 08:23:29 -0600 Subject: [PATCH] FINERACT-1960: Selected fees to be considered for accrual --- .../savings/SavingsApiConstants.java | 1 + .../savings/data/SavingsProductData.java | 24 ++++-- .../service/ChargeReadPlatformService.java | 2 + .../ChargeReadPlatformServiceImpl.java | 15 ++++ .../api/SavingsProductsApiResource.java | 3 +- .../SavingsProductsApiResourceSwagger.java | 20 ++++- .../data/SavingsProductDataValidator.java | 3 +- .../domain/DepositProductAssembler.java | 60 ++----------- .../savings/domain/FixedDepositProduct.java | 2 +- .../savings/domain/SavingsProduct.java | 64 +++++++++++--- .../domain/SavingsProductAssembler.java | 67 ++------------- .../domain/SavingsProductBaseAssembler.java | 86 +++++++++++++++++++ .../CreateSavingsProductCommandHandler.java | 8 +- ...WritePlatformServiceJpaRepositoryImpl.java | 9 +- ...WritePlatformServiceJpaRepositoryImpl.java | 14 ++- ...WritePlatformServiceJpaRepositoryImpl.java | 39 +++------ .../db/changelog/tenant/changelog-tenant.xml | 1 + ...add_savings_product_charge_for_accrual.xml | 46 ++++++++++ 18 files changed, 285 insertions(+), 179 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductBaseAssembler.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0125_add_savings_product_charge_for_accrual.xml diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java index 4fe8d64b151..c6607c9d136 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java @@ -156,6 +156,7 @@ public class SavingsApiConstants { // charges parameters public static final String chargeIdParamName = "chargeId"; public static final String chargesParamName = "charges"; + public static final String accrualChargesParamName = "accrualCharges"; public static final String savingsAccountChargeIdParamName = "savingsAccountChargeId"; public static final String chargeNameParamName = "name"; public static final String penaltyParamName = "penalty"; diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java index bed16288e09..7534e5a718b 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java @@ -75,6 +75,7 @@ public final class SavingsProductData implements Serializable { // charges private final Collection charges; + private final Collection accrualCharges; // template private final Collection currencyOptions; @@ -120,6 +121,7 @@ public static SavingsProductData template(final CurrencyData currency, final Enu final Map accountingMappings = null; final Collection paymentChannelToFundSourceMappings = null; final Collection charges = null; + final Collection accrualCharges = null; final Collection feeToIncomeAccountMappings = null; final Collection penaltyToIncomeAccountMappings = null; final boolean allowOverdraft = false; @@ -147,10 +149,11 @@ public static SavingsProductData template(final CurrencyData currency, final Enu penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, - isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment); + isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment, accrualCharges); } - public static SavingsProductData withCharges(final SavingsProductData product, final Collection charges) { + public static SavingsProductData withCharges(final SavingsProductData product, final Collection charges, + final Collection accrualCharges) { return new SavingsProductData(product.id, product.name, product.shortName, product.description, product.currency, product.nominalAnnualInterestRate, product.interestCompoundingPeriodType, product.interestPostingPeriodType, product.interestCalculationType, product.interestCalculationDaysInYearType, product.minRequiredOpeningBalance, @@ -165,7 +168,7 @@ public static SavingsProductData withCharges(final SavingsProductData product, f product.minBalanceForInterestCalculation, product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation, product.withHoldTax, product.taxGroup, product.taxGroupOptions, product.isDormancyTrackingActive, product.daysToInactive, product.daysToDormancy, product.daysToEscheat, - product.accountMappingForPayment); + product.accountMappingForPayment, accrualCharges); } /** @@ -200,7 +203,8 @@ public static SavingsProductData withTemplate(final SavingsProductData existingP existingProduct.maxAllowedLienLimit, existingProduct.lienAllowed, existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions, existingProduct.isDormancyTrackingActive, - existingProduct.daysToInactive, existingProduct.daysToDormancy, existingProduct.daysToEscheat, accountMappingForPayment); + existingProduct.daysToInactive, existingProduct.daysToDormancy, existingProduct.daysToEscheat, accountMappingForPayment, + existingProduct.accrualCharges); } public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct, @@ -237,7 +241,7 @@ public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions, existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, existingProduct.daysToDormancy, - existingProduct.daysToEscheat, existingProduct.accountMappingForPayment); + existingProduct.daysToEscheat, existingProduct.accountMappingForPayment, existingProduct.accrualCharges); } public static SavingsProductData instance(final Long id, final String name, final String shortName, final String description, @@ -268,6 +272,7 @@ public static SavingsProductData instance(final Long id, final String name, fina final Collection chargeOptions = null; final Collection penaltyOptions = null; final Collection charges = null; + final Collection accrualCharges = null; final Collection feeToIncomeAccountMappings = null; final Collection penaltyToIncomeAccountMappings = null; final Collection taxGroupOptions = null; @@ -282,7 +287,7 @@ public static SavingsProductData instance(final Long id, final String name, fina penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, - isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment); + isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment, accrualCharges); } public static SavingsProductData lookup(final Long id, final String name) { @@ -325,6 +330,7 @@ public static SavingsProductData lookup(final Long id, final String name) { final Collection accountingRuleOptions = null; final Map> accountingMappingOptions = null; final Collection charges = null; + final Collection accrualCharges = null; final Collection chargeOptions = null; final Collection penaltyOptions = null; final Collection feeToIncomeAccountMappings = null; @@ -345,7 +351,7 @@ public static SavingsProductData lookup(final Long id, final String name) { penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, - isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment); + isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment, accrualCharges); } public static SavingsProductData createForInterestPosting(final Long id, final EnumOptionData accountingRule) { @@ -386,6 +392,7 @@ private SavingsProductData(final Long id, final EnumOptionData accountingRule) { this.charges = null;// charges associated with Savings product this.chargeOptions = null;// charges available for adding to + this.accrualCharges = null; // Savings product this.penaltyOptions = null;// penalties available for adding // to Savings product @@ -433,7 +440,7 @@ private SavingsProductData(final Long id, final String name, final String shortN final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup, final Collection taxGroupOptions, final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat, - final String accountMappingForPayment) { + final String accountMappingForPayment, final Collection accrualCharges) { this.id = id; this.name = name; this.shortName = shortName; @@ -469,6 +476,7 @@ private SavingsProductData(final Long id, final String name, final String shortN this.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings; this.charges = charges;// charges associated with Savings product + this.accrualCharges = accrualCharges; this.chargeOptions = chargeOptions;// charges available for adding to // Savings product this.penaltyOptions = penaltyOptions;// penalties available for adding diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java index fbec2a84fc3..126a15993be 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformService.java @@ -112,6 +112,8 @@ public interface ChargeReadPlatformService { */ Collection retrieveSavingsProductCharges(Long savingsProductId); + Collection retrieveSavingsProductAccrualCharges(Long savingsProductId); + /** Retrieve savings account charges **/ Collection retrieveSavingsAccountApplicableCharges(Long savingsId); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java index 1aa993e9144..b99b4cd54e0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java @@ -307,6 +307,10 @@ public String savingsProductChargeSchema() { return chargeSchema() + " join m_savings_product_charge spc on spc.charge_id = c.id"; } + public String savingsProductAccrualChargeSchema() { + return chargeSchema() + " join m_savings_product_accrual_charge spc on spc.charge_id = c.id"; + } + public String shareProductChargeSchema() { return chargeSchema() + " join m_share_product_charge mspc on mspc.charge_id = c.id"; } @@ -431,6 +435,17 @@ public Collection retrieveSavingsProductCharges(final Long savingsPr return this.jdbcTemplate.query(sql, rm, new Object[] { savingsProductId }); // NOSONAR } + @Override + public Collection retrieveSavingsProductAccrualCharges(final Long savingsProductId) { + final ChargeMapper rm = new ChargeMapper(); + + String sql = "select " + rm.savingsProductAccrualChargeSchema() + + " where c.is_deleted=false and c.is_active=true and spc.savings_product_id=? "; + sql += addInClauseToSQL_toLimitChargesMappedToOffice_ifOfficeSpecificProductsEnabled(); + + return this.jdbcTemplate.query(sql, rm, new Object[] { savingsProductId }); // NOSONAR + } + @Override public Collection retrieveShareProductCharges(final Long shareProductId) { final ChargeMapper rm = new ChargeMapper(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResource.java index b814180e67e..b44fb8ce43a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResource.java @@ -173,8 +173,9 @@ public String retrieveOne(@PathParam("productId") @Parameter(description = "prod SavingsProductData savingProductData = this.savingProductReadPlatformService.retrieveOne(productId); final Collection charges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId); + final Collection accrualCharges = this.chargeReadPlatformService.retrieveSavingsProductAccrualCharges(productId); - savingProductData = SavingsProductData.withCharges(savingProductData, charges); + savingProductData = SavingsProductData.withCharges(savingProductData, charges, accrualCharges); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResourceSwagger.java index 5aaeabbc0da..32ab313b670 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsProductsApiResourceSwagger.java @@ -20,7 +20,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import java.math.BigDecimal; -import java.util.List; import java.util.Set; /** @@ -70,6 +69,7 @@ private PostSavingsCharges() {} @Schema(example = "1") public Integer accountingRule; public Set charges; + public Set accrualCharges; @Schema(example = "accountMappingForPayment") public String accountMappingForPayment; } @@ -396,6 +396,20 @@ private GetSavingsProductsPenaltyToIncomeAccountMappingsCharge() {} public GetSavingsProductsAccountingMappings.GetSavingsProductsIncomeFromPenaltyAccount incomeAccount; } + static final class GetSavingsProductsCharge { + + private GetSavingsProductsCharge() {} + + @Schema(example = "12") + public Integer id; + @Schema(example = "12.34") + public BigDecimal amount; + @Schema(example = "Annual Fee") + public String name; + @Schema(example = "false") + public Boolean active; + } + @Schema(example = "1") public Integer id; @Schema(example = "savings product") @@ -419,7 +433,9 @@ private GetSavingsProductsPenaltyToIncomeAccountMappingsCharge() {} public Set feeToIncomeAccountMappings; public Set penaltyToIncomeAccountMappings; @Schema(example = "[]") - public List charges; + public Set charges; + @Schema(example = "[]") + public Set accrualCharges; } @Schema(description = "GetSavingsProductsTemplateResponse") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductDataValidator.java index 368c1a87a97..5b4438f3e6d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductDataValidator.java @@ -107,7 +107,8 @@ public class SavingsProductDataValidator { nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, SavingsApiConstants.minRequiredBalanceParamName, SavingsApiConstants.enforceMinRequiredBalanceParamName, SavingsApiConstants.maxAllowedLienLimitParamName, SavingsApiConstants.lienAllowedParamName, - minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName)); + minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName, + SavingsApiConstants.accrualChargesParamName)); @Autowired public SavingsProductDataValidator(final FromJsonHelper fromApiJsonHelper) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java index 3cef7419570..c1957cfc4f9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositProductAssembler.java @@ -40,7 +40,6 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; -import static org.apache.fineract.portfolio.savings.SavingsApiConstants.idParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; @@ -70,10 +69,8 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; -import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException; import org.apache.fineract.portfolio.interestratechart.domain.InterestRateChart; import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartAssembler; -import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException; import org.apache.fineract.portfolio.savings.PreClosurePenalInterestOnType; import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType; @@ -86,18 +83,15 @@ import org.springframework.stereotype.Service; @Service -public class DepositProductAssembler { +public class DepositProductAssembler extends SavingsProductBaseAssembler { - private final ChargeRepositoryWrapper chargeRepository; private final InterestRateChartAssembler chartAssembler; - private final TaxGroupRepositoryWrapper taxGroupRepository; @Autowired - public DepositProductAssembler(final ChargeRepositoryWrapper chargeRepository, final InterestRateChartAssembler chartAssembler, - final TaxGroupRepositoryWrapper taxGroupRepository) { - this.chargeRepository = chargeRepository; + public DepositProductAssembler(ChargeRepositoryWrapper chargeRepository, TaxGroupRepositoryWrapper taxGroupRepository, + InterestRateChartAssembler chartAssembler) { + super(chargeRepository, taxGroupRepository); this.chartAssembler = chartAssembler; - this.taxGroupRepository = taxGroupRepository; } public FixedDepositProduct assembleFixedDepositProduct(final JsonCommand command) { @@ -157,7 +151,7 @@ public FixedDepositProduct assembleFixedDepositProduct(final JsonCommand command depositTermDetail, depositProductAmountDetails, null); // Savings product charges - final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); + final Set charges = assembleListOfSavingsProductCharges(command, currencyCode, chargesParamName); // Interest rate charts final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -253,7 +247,7 @@ public RecurringDepositProduct assembleRecurringDepositProduct(final JsonCommand final DepositProductRecurringDetail productRecurringDetail = DepositProductRecurringDetail.createNew(recurringDetail, null); // Savings product charges - final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); + final Set charges = assembleListOfSavingsProductCharges(command, currencyCode, chargesParamName); // Interest rate charts final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -444,40 +438,6 @@ public DepositRecurringDetail assembleRecurringDetail(final JsonCommand command) return depositRecurringDetail; } - public Set assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) { - - final Set charges = new HashSet<>(); - - if (command.parameterExists(chargesParamName)) { - final JsonArray chargesArray = command.arrayOfParameterNamed(chargesParamName); - if (chargesArray != null) { - for (int i = 0; i < chargesArray.size(); i++) { - - final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject(); - if (jsonObject.has(idParamName)) { - final Long id = jsonObject.get(idParamName).getAsLong(); - - final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id); - - if (!charge.isSavingsCharge()) { - final String errorMessage = "Charge with identifier " + charge.getId() - + " cannot be applied to Savings product."; - throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, charge.getId()); - } - - if (!savingsProductCurrencyCode.equals(charge.getCurrencyCode())) { - final String errorMessage = "Charge and Savings Product must have the same currency."; - throw new InvalidCurrencyException("charge", "attach.to.savings.product", errorMessage); - } - charges.add(charge); - } - } - } - } - - return charges; - } - private Set assembleListOfCharts(JsonCommand command, String currencyCode, DataValidatorBuilder baseDataValidator) { final Set charts = new HashSet<>(); if (command.parameterExists(chartsParamName)) { @@ -516,12 +476,4 @@ public DepositProductAmountDetails assembleDepositAmountDetails(final JsonComman return depositRecurringDetail; } - public TaxGroup assembleTaxGroup(final JsonCommand command) { - final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName); - TaxGroup taxGroup = null; - if (taxGroupId != null) { - taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId); - } - return taxGroup; - } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java index f87d6140a9b..ee918cb571d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositProduct.java @@ -113,7 +113,7 @@ protected FixedDepositProduct(final String name, final String shortName, final S super(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, - minBalanceForInterestCalculation, withHoldTax, taxGroup); + minBalanceForInterestCalculation, withHoldTax, taxGroup, null); if (charts != null) { this.charts = charts; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java index f47f6214967..06d61eddf39 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProduct.java @@ -20,6 +20,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accrualChargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName; @@ -163,6 +164,10 @@ public class SavingsProduct extends AbstractPersistableCustom { @JoinTable(name = "m_savings_product_charge", joinColumns = @JoinColumn(name = "savings_product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) protected Set charges; + @ManyToMany + @JoinTable(name = "m_savings_product_accrual_charge", joinColumns = @JoinColumn(name = "savings_product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) + protected Set accrualCharges; + @Column(name = "allow_overdraft") private boolean allowOverdraft; @@ -220,14 +225,15 @@ public static SavingsProduct createNew(final String name, final String shortName final BigDecimal minRequiredBalance, final boolean lienAllowed, final BigDecimal maxAllowedLienLimit, final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax, TaxGroup taxGroup, - final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) { + final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat, + final Set accrualCharges) { return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, lienAllowed, maxAllowedLienLimit, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, - taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); + taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accrualCharges); } protected SavingsProduct() { @@ -242,11 +248,12 @@ protected SavingsProduct(final String name, final String shortName, final String final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges, final boolean allowOverdraft, final BigDecimal overdraftLimit, BigDecimal minBalanceForInterestCalculation, boolean withHoldTax, - TaxGroup taxGroup) { + TaxGroup taxGroup, final Set accrualCharges) { this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, - false, null, false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup, null, null, null, null); + false, null, false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup, null, null, null, null, + accrualCharges); } protected SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, @@ -259,7 +266,8 @@ protected SavingsProduct(final String name, final String shortName, final String final BigDecimal minRequiredBalance, final boolean lienAllowed, final BigDecimal maxAllowedLienLimit, BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroup taxGroup, - final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) { + final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat, + final Set accrualCharges) { this.name = name; this.shortName = shortName; @@ -291,6 +299,10 @@ protected SavingsProduct(final String name, final String shortName, final String this.charges = charges; } + if (accrualCharges != null) { + this.accrualCharges = accrualCharges; + } + validateLockinDetails(); this.allowOverdraft = allowOverdraft; this.overdraftLimit = overdraftLimit; @@ -494,10 +506,18 @@ public Map update(final JsonCommand command) { // charges if (command.hasParameter(chargesParamName)) { - final JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName); + JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName); if (jsonArray != null) { actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName)); } + + // accrual charges + if (command.hasParameter(accrualChargesParamName)) { + jsonArray = command.arrayOfParameterNamed(accrualChargesParamName); + if (jsonArray != null) { + actualChanges.put(accrualChargesParamName, command.jsonFragment(accrualChargesParamName)); + } + } } if (command.isChangeInBooleanParameterNamed(allowOverdraftParamName, this.allowOverdraft)) { @@ -671,23 +691,27 @@ public Integer getAccountingType() { return this.accountingRule; } - public boolean update(final Set newSavingsProductCharges) { - if (newSavingsProductCharges == null) { - return false; - } + public boolean update(final Set newSavingsProductCharges, final Set newSavingsProductAccrualCharges) { + final boolean updatedCharges = (newSavingsProductCharges == null) ? false : validateCharges(this.charges, newSavingsProductCharges); + final boolean updatedAccrualCharges = (newSavingsProductAccrualCharges == null) ? false + : validateCharges(this.accrualCharges, newSavingsProductAccrualCharges); + return updatedCharges || updatedAccrualCharges; + } + + private boolean validateCharges(Set currentCharges, final Set newSavingsProductCharges) { boolean updated = false; - if (this.charges != null) { - final Set currentSetOfCharges = new HashSet<>(this.charges); + if (currentCharges != null) { + final Set currentSetOfCharges = new HashSet<>(currentCharges); final Set newSetOfCharges = new HashSet<>(newSavingsProductCharges); if (!currentSetOfCharges.equals(newSetOfCharges)) { updated = true; - this.charges = newSavingsProductCharges; + currentCharges = newSavingsProductCharges; } } else { updated = true; - this.charges = newSavingsProductCharges; + currentCharges = newSavingsProductCharges; } return updated; } @@ -724,6 +748,10 @@ public Set charges() { return this.charges; } + public Set accrualCharges() { + return this.accrualCharges; + } + public InterestRateChart applicableChart(@SuppressWarnings("unused") final LocalDate target) { return null; } @@ -780,4 +808,12 @@ public Long getDaysToEscheat() { return this.daysToEscheat; } + public void setCharges(Set charges) { + this.charges = charges; + } + + public void setAccrualCharges(Set accrualCharges) { + this.accrualCharges = accrualCharges; + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java index a9f3862ae0b..b8660e4cfb5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductAssembler.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.savings.domain; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accrualChargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName; @@ -27,7 +28,6 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName; -import static org.apache.fineract.portfolio.savings.SavingsApiConstants.idParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.inMultiplesOfParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName; @@ -47,22 +47,16 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName; -import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; import java.math.BigDecimal; -import java.util.HashSet; import java.util.Set; import org.apache.fineract.accounting.common.AccountingRuleType; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; -import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException; -import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException; import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType; import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType; @@ -74,15 +68,11 @@ import org.springframework.stereotype.Component; @Component -public class SavingsProductAssembler { - - private final ChargeRepositoryWrapper chargeRepository; - private final TaxGroupRepositoryWrapper taxGroupRepository; +public class SavingsProductAssembler extends SavingsProductBaseAssembler { @Autowired - public SavingsProductAssembler(final ChargeRepositoryWrapper chargeRepository, final TaxGroupRepositoryWrapper taxGroupRepository) { - this.chargeRepository = chargeRepository; - this.taxGroupRepository = taxGroupRepository; + public SavingsProductAssembler(ChargeRepositoryWrapper chargeRepository, TaxGroupRepositoryWrapper taxGroupRepository) { + super(chargeRepository, taxGroupRepository); } public SavingsProduct assemble(final JsonCommand command) { @@ -141,7 +131,10 @@ public SavingsProduct assemble(final JsonCommand command) { final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule")); // Savings product charges - final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); + final Set charges = assembleListOfSavingsProductCharges(command, currencyCode, chargesParamName); + + // Savings product charges to be accrued + final Set accrualCharges = assembleListOfSavingsProductCharges(command, currencyCode, accrualChargesParamName); boolean allowOverdraft = false; if (command.parameterExists(allowOverdraftParamName)) { @@ -198,49 +191,7 @@ public SavingsProduct assemble(final JsonCommand command) { lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, lienAllowed, maxAllowedLienLimit, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, - taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); - } - - public Set assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) { - - final Set charges = new HashSet<>(); - - if (command.parameterExists(chargesParamName)) { - final JsonArray chargesArray = command.arrayOfParameterNamed(chargesParamName); - if (chargesArray != null) { - for (int i = 0; i < chargesArray.size(); i++) { - - final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject(); - if (jsonObject.has(idParamName)) { - final Long id = jsonObject.get(idParamName).getAsLong(); - - final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id); - - if (!charge.isSavingsCharge()) { - final String errorMessage = "Charge with identifier " + charge.getId() - + " cannot be applied to Savings product."; - throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, charge.getId()); - } - - if (!savingsProductCurrencyCode.equals(charge.getCurrencyCode())) { - final String errorMessage = "Charge and Savings Product must have the same currency."; - throw new InvalidCurrencyException("charge", "attach.to.savings.product", errorMessage); - } - charges.add(charge); - } - } - } - } - - return charges; + taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accrualCharges); } - public TaxGroup assembleTaxGroup(final JsonCommand command) { - final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName); - TaxGroup taxGroup = null; - if (taxGroupId != null) { - taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId); - } - return taxGroup; - } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductBaseAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductBaseAssembler.java new file mode 100644 index 00000000000..f2587822f97 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsProductBaseAssembler.java @@ -0,0 +1,86 @@ +/** + * 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.savings.domain; + +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.idParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.HashSet; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.portfolio.charge.domain.Charge; +import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; +import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException; +import org.apache.fineract.portfolio.loanproduct.exception.InvalidCurrencyException; +import org.apache.fineract.portfolio.tax.domain.TaxGroup; +import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper; + +@RequiredArgsConstructor +public class SavingsProductBaseAssembler { + + protected final ChargeRepositoryWrapper chargeRepository; + protected final TaxGroupRepositoryWrapper taxGroupRepository; + + public Set assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode, + final String chargesParameterName) { + + final Set charges = new HashSet<>(); + + if (command.parameterExists(chargesParameterName)) { + final JsonArray chargesArray = command.arrayOfParameterNamed(chargesParameterName); + if (chargesArray != null) { + for (int i = 0; i < chargesArray.size(); i++) { + + final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject(); + if (jsonObject.has(idParamName)) { + final Long id = jsonObject.get(idParamName).getAsLong(); + + final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(id); + + if (!charge.isSavingsCharge()) { + final String errorMessage = "Charge with identifier " + charge.getId() + + " cannot be applied to Savings product."; + throw new ChargeCannotBeAppliedToException("savings.product", errorMessage, charge.getId()); + } + + if (!savingsProductCurrencyCode.equals(charge.getCurrencyCode())) { + final String errorMessage = "Charge and Savings Product must have the same currency."; + throw new InvalidCurrencyException("charge", "attach.to.savings.product", errorMessage); + } + charges.add(charge); + } + } + } + } + + return charges; + } + + public TaxGroup assembleTaxGroup(final JsonCommand command) { + final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName); + TaxGroup taxGroup = null; + if (taxGroupId != null) { + taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId); + } + return taxGroup; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/CreateSavingsProductCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/CreateSavingsProductCommandHandler.java index 8ba4dfe3b7a..b2789ee6820 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/CreateSavingsProductCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/CreateSavingsProductCommandHandler.java @@ -18,25 +18,21 @@ */ package org.apache.fineract.portfolio.savings.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.savings.service.SavingsProductWritePlatformService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @CommandType(entity = "SAVINGSPRODUCT", action = "CREATE") +@RequiredArgsConstructor public class CreateSavingsProductCommandHandler implements NewCommandSourceHandler { private final SavingsProductWritePlatformService writePlatformService; - @Autowired - public CreateSavingsProductCommandHandler(final SavingsProductWritePlatformService writePlatformService) { - this.writePlatformService = writePlatformService; - } - @Override public CommandProcessingResult processCommand(final JsonCommand command) { return this.writePlatformService.create(command); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java index bc7fd7e97fa..17a32b97ac5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java @@ -20,6 +20,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accrualChargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName; @@ -124,10 +125,14 @@ public CommandProcessingResult update(final Long productId, final JsonCommand co if (changes.containsKey(chargesParamName)) { final Set savingsProductCharges = this.depositProductAssembler.assembleListOfSavingsProductCharges(command, - product.currency().getCode()); - final boolean updated = product.update(savingsProductCharges); + product.currency().getCode(), chargesParamName); + final Set savingsProductAccrualCharges = this.depositProductAssembler.assembleListOfSavingsProductCharges(command, + product.currency().getCode(), accrualChargesParamName); + + final boolean updated = product.update(savingsProductCharges, savingsProductAccrualCharges); if (!updated) { changes.remove(chargesParamName); + changes.remove(accrualChargesParamName); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java index 798b20263c2..70f06230a3b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java @@ -20,6 +20,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accrualChargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName; @@ -122,12 +123,17 @@ public CommandProcessingResult update(final Long productId, final JsonCommand co final Map changes = product.update(command); - if (changes.containsKey(chargesParamName)) { - final Set savingsProductCharges = this.depositProductAssembler.assembleListOfSavingsProductCharges(command, - product.currency().getCode()); - final boolean updated = product.update(savingsProductCharges); + if (changes.containsKey(chargesParamName) || changes.containsKey(accrualChargesParamName)) { + final Set savingsProductCharges = depositProductAssembler.assembleListOfSavingsProductCharges(command, + product.currency().getCode(), chargesParamName); + + final Set savingsProductAccrualCharges = depositProductAssembler.assembleListOfSavingsProductCharges(command, + product.currency().getCode(), accrualChargesParamName); + + final boolean updated = product.update(savingsProductCharges, savingsProductAccrualCharges); if (!updated) { changes.remove(chargesParamName); + changes.remove(accrualChargesParamName); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java index 56b429e403b..53e9da0b02d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java @@ -20,6 +20,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accrualChargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName; @@ -27,7 +28,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; import org.apache.fineract.infrastructure.core.api.JsonCommand; @@ -40,7 +42,6 @@ import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessType; import org.apache.fineract.infrastructure.entityaccess.service.FineractEntityAccessUtil; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; -import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.data.SavingsProductDataValidator; import org.apache.fineract.portfolio.savings.domain.SavingsProduct; @@ -48,17 +49,15 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; import org.apache.fineract.portfolio.tax.domain.TaxGroup; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +@Slf4j @Service +@RequiredArgsConstructor public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements SavingsProductWritePlatformService { - private static final Logger LOG = LoggerFactory.getLogger(SavingsProductWritePlatformServiceJpaRepositoryImpl.class); private final PlatformSecurityContext context; private final SavingsProductRepository savingProductRepository; private final SavingsProductDataValidator fromApiJsonDataValidator; @@ -66,20 +65,6 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; private final FineractEntityAccessUtil fineractEntityAccessUtil; - @Autowired - public SavingsProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, - final SavingsProductRepository savingProductRepository, final SavingsProductDataValidator fromApiJsonDataValidator, - final SavingsProductAssembler savingsProductAssembler, - final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, - final FineractEntityAccessUtil fineractEntityAccessUtil) { - this.context = context; - this.savingProductRepository = savingProductRepository; - this.fromApiJsonDataValidator = fromApiJsonDataValidator; - this.savingsProductAssembler = savingsProductAssembler; - this.accountMappingWritePlatformService = accountMappingWritePlatformService; - this.fineractEntityAccessUtil = fineractEntityAccessUtil; - } - /* * Guaranteed to throw an exception no matter what the data integrity issue is. */ @@ -103,7 +88,7 @@ private void handleDataIntegrityIssues(final JsonCommand command, final Throwabl } private void logAsErrorUnexpectedDataIntegrityException(final Exception dae) { - LOG.error("Error occured.", dae); + log.error("Error occured.", dae); } @Transactional @@ -153,13 +138,11 @@ public CommandProcessingResult update(final Long productId, final JsonCommand co final Map changes = product.update(command); - if (changes.containsKey(chargesParamName)) { - final Set savingsProductCharges = this.savingsProductAssembler.assembleListOfSavingsProductCharges(command, - product.currency().getCode()); - final boolean updated = product.update(savingsProductCharges); - if (!updated) { - changes.remove(chargesParamName); - } + if (changes.containsKey(chargesParamName) || changes.containsKey(accrualChargesParamName)) { + product.setCharges(savingsProductAssembler.assembleListOfSavingsProductCharges(command, product.currency().getCode(), + chargesParamName)); + product.setAccrualCharges(savingsProductAssembler.assembleListOfSavingsProductCharges(command, product.currency().getCode(), + accrualChargesParamName)); } if (changes.containsKey(taxGroupIdParamName)) { diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index cd410caa888..0c82d146ebc 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -144,4 +144,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0125_add_savings_product_charge_for_accrual.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0125_add_savings_product_charge_for_accrual.xml new file mode 100644 index 00000000000..751c22f6653 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0125_add_savings_product_charge_for_accrual.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + +