diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java index 2db50c55f..2e44ad9e0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java @@ -362,8 +362,7 @@ public class ProductToGLAccountMappingHelper { private void savePaymentChannelToFundSourceMapping(final Long productId, final Long paymentTypeId, final Long paymentTypeSpecificFundAccountId, final PortfolioProductType portfolioProductType) { final PaymentType paymentType = this.paymentTypeRepositoryWrapper.findOneWithNotFoundDetection(paymentTypeId); - final GLAccount glAccount = getAccountByIdAndType(LoanProductAccountingParams.FUND_SOURCE.getValue(), GLAccountType.ASSET, - paymentTypeSpecificFundAccountId); + final GLAccount glAccount = getAccountById(LoanProductAccountingParams.FUND_SOURCE.getValue(), paymentTypeSpecificFundAccountId); final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping(glAccount, productId, portfolioProductType.getValue(), CashAccountsForLoan.FUND_SOURCE.getValue(), paymentType); this.accountMappingRepository.save(accountMapping); @@ -427,6 +426,11 @@ public class ProductToGLAccountMappingHelper { return glAccount; } + public GLAccount getAccountById(final String paramName, final Long accountId) { + final GLAccount glAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountId); + return glAccount; + } + public GLAccount getAccountByIdAndType(final String paramName, final List expectedAccountTypes, final Long accountId) { final GLAccount glAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountId); // validate account is of the expected Type diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiConstant.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiConstant.java index 3174b455a..b8e86d532 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiConstant.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiConstant.java @@ -31,5 +31,6 @@ public final class GlobalConfigurationApiConstant { public static final String CONFIGURATION_RESOURCE_NAME = "globalConfiguration"; public static final String localeParamName = "locale"; public static final String dateFormatParamName = "dateFormat"; + public static final String STRING_VALUE = "stringValue"; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationDataValidator.java index 40676ed2e..668d6196a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationDataValidator.java @@ -43,9 +43,10 @@ import org.springframework.stereotype.Component; public class GlobalConfigurationDataValidator { private final FromJsonHelper fromApiJsonHelper; - private static final Set UPDATE_CONFIGURATION_DATA_PARAMETERS = new HashSet<>(Arrays.asList( - GlobalConfigurationApiConstant.localeParamName, GlobalConfigurationApiConstant.dateFormatParamName, - GlobalConfigurationApiConstant.ENABLED, GlobalConfigurationApiConstant.VALUE, GlobalConfigurationApiConstant.DATE_VALUE)); + private static final Set UPDATE_CONFIGURATION_DATA_PARAMETERS = new HashSet<>( + Arrays.asList(GlobalConfigurationApiConstant.localeParamName, GlobalConfigurationApiConstant.dateFormatParamName, + GlobalConfigurationApiConstant.ENABLED, GlobalConfigurationApiConstant.VALUE, GlobalConfigurationApiConstant.DATE_VALUE, + GlobalConfigurationApiConstant.STRING_VALUE)); @Autowired public GlobalConfigurationDataValidator(final FromJsonHelper fromApiJsonHelper) { @@ -81,6 +82,11 @@ public class GlobalConfigurationDataValidator { baseDataValidator.reset().parameter(GlobalConfigurationApiConstant.DATE_VALUE).value(dateValue).notNull(); } + if (this.fromApiJsonHelper.parameterExists(GlobalConfigurationApiConstant.STRING_VALUE, element)) { + final String stringValue = this.fromApiJsonHelper.extractStringNamed(GlobalConfigurationApiConstant.STRING_VALUE, element); + baseDataValidator.reset().parameter(GlobalConfigurationApiConstant.STRING_VALUE).value(stringValue).notNull(); + } + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationPropertyData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationPropertyData.java index a893b9373..7e54b01b2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationPropertyData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/data/GlobalConfigurationPropertyData.java @@ -33,6 +33,7 @@ public class GlobalConfigurationPropertyData { private final Long value; @SuppressWarnings("unused") private final Date dateValue; + private String stringValue; @SuppressWarnings("unused") private final Long id; @SuppressWarnings("unused") @@ -41,22 +42,24 @@ public class GlobalConfigurationPropertyData { private final boolean trapDoor; public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, final Date dateValue, - final String description, final boolean trapDoor) { + final String stringValue, final String description, final boolean trapDoor) { this.name = name; this.enabled = enabled; this.value = value; this.dateValue = dateValue; + this.stringValue = stringValue; this.id = null; this.description = description; this.trapDoor = trapDoor; } - public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, Date dateValue, final Long id, - final String description, final boolean isTrapDoor) { + public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, Date dateValue, + final String stringValue, final Long id, final String description, final boolean isTrapDoor) { this.name = name; this.enabled = enabled; this.value = value; this.dateValue = dateValue; + this.stringValue = stringValue; this.id = id; this.description = description; this.trapDoor = isTrapDoor; @@ -74,6 +77,10 @@ public class GlobalConfigurationPropertyData { return this.value; } + public String getStringValue() { + return this.stringValue; + } + public Date getDateValue() { return this.dateValue; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java index 4b791c92b..b35611c1c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -98,4 +98,8 @@ public interface ConfigurationDomainService { boolean isFirstRepaymentDateAfterRescheduleAllowedOnHoliday(); boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI(); + + String getAccountMappingForPaymentType(); + + String getAccountMappingForCharge(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index 321d640d6..b03c2a9d4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -363,4 +363,34 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService } } + @Override + public String getAccountMappingForPaymentType() { + final String propertyName = "account-mapping-for-payment-type"; + String defaultValue = "Asset"; // 1 Stands for Account mapped from asset only + final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName); + if (property.isEnabled()) { + String value = property.getStringValue(); + if (value == null) { + return defaultValue; + } + return value; + } + return defaultValue; + } + + @Override + public String getAccountMappingForCharge() { + final String propertyName = "account-mapping-for-charge"; + String defaultValue = "Income"; // 1 Stands for Account mapped from income only + final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName); + if (property.isEnabled()) { + String value = property.getValue().toString(); + if (value == null) { + return defaultValue; + } + return value; + } + return defaultValue; + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java index e6a3a2e37..84e0283a4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java @@ -46,6 +46,9 @@ public class GlobalConfigurationProperty extends AbstractPersistableCustom { @Column(name = "date_value", nullable = true) private Date dateValue; + @Column(name = "string_value", nullable = true) + private String stringValue; + @Column(name = "description", nullable = true) private String description; @@ -57,16 +60,18 @@ public class GlobalConfigurationProperty extends AbstractPersistableCustom { this.enabled = false; this.value = null; this.dateValue = null; + this.stringValue = null; this.description = null; this.isTrapDoor = false; } public GlobalConfigurationProperty(final String name, final boolean enabled, final Long value, final Date dateValue, - final String description, final boolean isTrapDoor) { + final String stringValue, final String description, final boolean isTrapDoor) { this.name = name; this.enabled = enabled; this.value = value; this.dateValue = dateValue; + this.stringValue = stringValue; this.description = description; this.isTrapDoor = isTrapDoor; } @@ -83,6 +88,10 @@ public class GlobalConfigurationProperty extends AbstractPersistableCustom { return this.dateValue; } + public String getStringValue() { + return this.stringValue; + } + public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap<>(7); @@ -113,6 +122,13 @@ public class GlobalConfigurationProperty extends AbstractPersistableCustom { this.dateValue = newDateValue; } + final String stringValueParamName = "stringValue"; + if (command.isChangeInStringParameterNamed(stringValueParamName, this.stringValue)) { + final String newStringValue = command.stringValueOfParameterNamed(stringValueParamName); + actualChanges.put(stringValueParamName, newStringValue); + this.stringValue = newStringValue; + } + final String passwordPropertyName = "force-password-reset-days"; if (this.name.equalsIgnoreCase(passwordPropertyName)) { if ((this.enabled == true && command.hasParameter(valueParamName) && (this.value == 0)) @@ -126,12 +142,12 @@ public class GlobalConfigurationProperty extends AbstractPersistableCustom { } public static GlobalConfigurationProperty newSurveyConfiguration(final String name) { - return new GlobalConfigurationProperty(name, false, null, null, null, false); + return new GlobalConfigurationProperty(name, false, null, null, null, null, false); } public GlobalConfigurationPropertyData toData() { - return new GlobalConfigurationPropertyData(getName(), isEnabled(), getValue(), getDateValue(), this.getId(), this.description, - this.isTrapDoor); + return new GlobalConfigurationPropertyData(getName(), isEnabled(), getValue(), getDateValue(), getStringValue(), this.getId(), + this.description, this.isTrapDoor); } public String getName() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java index 65eaec1f7..dd7326ad5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java @@ -52,7 +52,7 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl this.context.authenticatedUser(); - String sql = "SELECT c.id, c.name, c.enabled, c.value, c.date_value, c.description, c.is_trap_door FROM c_configuration c "; + String sql = "SELECT c.id, c.name, c.enabled, c.value, c.date_value, c.description,c.string_value, c.is_trap_door FROM c_configuration c "; if (survey) { sql += " JOIN x_registered_table on x_registered_table.registered_table_name = c.name "; @@ -68,11 +68,11 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl } @Override - public GlobalConfigurationPropertyData retrieveGlobalConfiguration(String name) { + public GlobalConfigurationPropertyData retrieveGlobalConfiguration(final String name) { this.context.authenticatedUser(); - final String sql = "SELECT c.id, c.name, c.enabled, c.value, c.date_value, c.description, c.is_trap_door FROM " + final String sql = "SELECT c.id, c.name, c.enabled, c.value, c.date_value, c.string_value, c.description, c.is_trap_door FROM " + "c_configuration c where c.name=? order by c.id"; final GlobalConfigurationPropertyData globalConfiguration = this.jdbcTemplate.queryForObject(sql, this.rm, new Object[] { name }); @@ -84,7 +84,7 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl this.context.authenticatedUser(); - final String sql = "SELECT c.id, c.name, c.enabled, c.value, c.date_value, c.description, c.is_trap_door FROM " + final String sql = "SELECT c.id, c.name, c.enabled, c.value, c.date_value, c.string_value ,c.description, c.is_trap_door FROM " + "c_configuration c where c.id=? order by c.id"; final GlobalConfigurationPropertyData globalConfiguration = this.jdbcTemplate.queryForObject(sql, this.rm, new Object[] { configId }); @@ -102,10 +102,11 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl final boolean enabled = rs.getBoolean("enabled"); final Long value = rs.getLong("value"); final Date dateValue = rs.getDate("date_value"); + final String stringValue = rs.getString("string_value"); final String description = rs.getString("description"); final Long id = rs.getLong("id"); final boolean isTrapDoor = rs.getBoolean("is_trap_door"); - return new GlobalConfigurationPropertyData(name, enabled, value, dateValue, id, description, isTrapDoor); + return new GlobalConfigurationPropertyData(name, enabled, value, dateValue, stringValue, id, description, isTrapDoor); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java index 8f2e884ea..1113571d8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java @@ -191,4 +191,6 @@ public class SavingsApiConstants { public static final String gsimApplicationId = "applicationId"; public static final String gsimLastApplication = "lastApplication"; public static final String ERROR_MSG_SAVINGS_ACCOUNT_NOT_ACTIVE = "not.in.active.state"; + + public static final String accountMappingForPaymentParamName = "accountMappingForPayment"; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsApiSetConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsApiSetConstants.java index 3ec70e700..d27816704 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsApiSetConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsApiSetConstants.java @@ -41,13 +41,12 @@ public class SavingsApiSetConstants extends SavingsApiConstants { "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName, isDormancyTrackingActiveParamName, daysToInactiveParamName, - daysToDormancyParamName, daysToInactiveParamName)); + daysToDormancyParamName, daysToInactiveParamName, accountMappingForPaymentParamName)); /** * These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to * get response parameters to match those of request parameters. */ - protected static final Set SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName, accountNoParamName, externalIdParamName, statusParamName, activatedOnDateParamName, staffIdParamName, clientIdParamName, "clientName", groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency", @@ -58,7 +57,7 @@ public class SavingsApiSetConstants extends SavingsApiConstants { "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee", "annualFee", onHoldFundsParamName, nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, - datatables, savingsAmountOnHold)); + datatables, savingsAmountOnHold, accountMappingForPaymentParamName)); protected static final Set SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>( Arrays.asList(idParamName, "accountId", accountNoParamName, "currency", "amount", dateParamName, paymentDetailDataParamName, 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 ab8d4c25c..d01f4dc0a 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 @@ -52,6 +52,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToG import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.EnumOptionData; @@ -100,6 +101,7 @@ public class SavingsProductsApiResource { private final ChargeReadPlatformService chargeReadPlatformService; private final PaymentTypeReadPlatformService paymentTypeReadPlatformService; private final TaxReadPlatformService taxReadPlatformService; + private final ConfigurationDomainService configurationDomainService; @Autowired public SavingsProductsApiResource(final SavingsProductReadPlatformService savingProductReadPlatformService, @@ -111,7 +113,7 @@ public class SavingsProductsApiResource { final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService, final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService, PaymentTypeReadPlatformService paymentTypeReadPlatformService, - final TaxReadPlatformService taxReadPlatformService) { + final TaxReadPlatformService taxReadPlatformService, final ConfigurationDomainService configurationDomainService) { this.savingProductReadPlatformService = savingProductReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService; this.currencyReadPlatformService = currencyReadPlatformService; @@ -124,6 +126,7 @@ public class SavingsProductsApiResource { this.chargeReadPlatformService = chargeReadPlatformService; this.paymentTypeReadPlatformService = paymentTypeReadPlatformService; this.taxReadPlatformService = taxReadPlatformService; + this.configurationDomainService = configurationDomainService; } @POST @@ -132,7 +135,7 @@ public class SavingsProductsApiResource { @Operation(summary = "Create a Savings Product", description = "Creates a Savings Product\n\n" + "Mandatory Fields: name, shortName, description, currencyCode, digitsAfterDecimal,inMultiplesOf, nominalAnnualInterestRate, interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType,accountingRule\n\n" + "Mandatory Fields for Cash based accounting (accountingRule = 2): savingsReferenceAccountId, savingsControlAccountId, interestOnSavingsAccountId, incomeFromFeeAccountId, transfersInSuspenseAccountId, incomeFromPenaltyAccountId\n\n" - + "Optional Fields: minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, charges, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation,withHoldTax,taxGroupId") + + "Optional Fields: minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, charges, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation,withHoldTax,taxGroupId,accountMapping") @RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.PostSavingsProductsRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.PostSavingsProductsResponse.class))) }) @@ -229,7 +232,8 @@ public class SavingsProductsApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Retrieve Savings Product Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:\n" - + "\n" + "Field Defaults\n" + "Allowed description Lists\n" + "Example Request:\n" + "\n" + "savingsproducts/template") + + "\n" + "Field Defaults\n" + "Allowed description Lists\n" + "Example Request:\n" + "Account Mapping:\n" + "\n" + + "savingsproducts/template") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.GetSavingsProductsTemplateResponse.class))) }) public String retrieveTemplate(@Context final UriInfo uriInfo) { @@ -288,6 +292,8 @@ public class SavingsProductsApiResource { final Map> accountingMappingOptions = this.accountingDropdownReadPlatformService .retrieveAccountMappingOptionsForSavingsProducts(); + final String accountMappingForPayment = configurationDomainService.getAccountMappingForPaymentType(); + // charges final boolean feeChargesOnly = false; Collection chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly); @@ -301,13 +307,14 @@ public class SavingsProductsApiResource { savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, - accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions); + accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, accountMappingForPayment); } else { savingsProductToReturn = SavingsProductData.template(currency, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, - paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions); + paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions, + accountMappingForPayment); } return savingsProductToReturn; 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 20fdd3e9f..5aaeabbc0 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 @@ -70,6 +70,8 @@ final class SavingsProductsApiResourceSwagger { @Schema(example = "1") public Integer accountingRule; public Set charges; + @Schema(example = "accountMappingForPayment") + public String accountMappingForPayment; } @Schema(description = "PostSavingsProductsResponse") @@ -725,6 +727,8 @@ final class SavingsProductsApiResourceSwagger { public Set accountingRuleOptions; public GetSavingsProductsAccountingMappingOptions accountingMappingOptions; public Set chargeOptions; + public GetSavingsProductsResponse.GetSavingsCurrency accountMapping; + } @Schema(description = "DeleteSavingsProductsProductIdResponse") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java index 856adf407..95505c8e3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java @@ -62,6 +62,7 @@ public final class SavingsProductData implements Serializable { private final boolean withHoldTax; private final TaxGroupData taxGroup; private String depositAccountType = null; + private final String accountMappingForPayment; // accounting private final EnumOptionData accountingRule; @@ -102,7 +103,8 @@ public final class SavingsProductData implements Serializable { final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, final Collection paymentTypeOptions, final Collection accountingRuleOptions, final Map> accountingMappingOptions, final Collection chargeOptions, - final Collection penaltyOptions, final Collection taxGroupOptions) { + final Collection penaltyOptions, final Collection taxGroupOptions, + final String accountMappingForPayment) { final Long id = null; final String name = null; @@ -141,7 +143,7 @@ public final class SavingsProductData implements Serializable { penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, - daysToDormancy, daysToEscheat); + daysToDormancy, daysToEscheat, accountMappingForPayment); } public static SavingsProductData withCharges(final SavingsProductData product, final Collection charges) { @@ -158,7 +160,7 @@ public final class SavingsProductData implements Serializable { product.minRequiredBalance, product.enforceMinRequiredBalance, product.minBalanceForInterestCalculation, product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation, product.withHoldTax, product.taxGroup, product.taxGroupOptions, product.isDormancyTrackingActive, product.daysToInactive, product.daysToDormancy, - product.daysToEscheat); + product.daysToEscheat, product.accountMappingForPayment); } /** @@ -167,6 +169,7 @@ public final class SavingsProductData implements Serializable { * * @param taxGroupOptions * TODO + * @param accountMapping */ public static SavingsProductData withTemplate(final SavingsProductData existingProduct, final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, @@ -176,7 +179,7 @@ public final class SavingsProductData implements Serializable { final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, final Collection paymentTypeOptions, final Collection accountingRuleOptions, final Map> accountingMappingOptions, final Collection chargeOptions, - final Collection penaltyOptions, Collection taxGroupOptions) { + final Collection penaltyOptions, Collection taxGroupOptions, final String accountMappingForPayment) { return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, @@ -192,7 +195,7 @@ public final class SavingsProductData implements Serializable { existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions, existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, existingProduct.daysToDormancy, - existingProduct.daysToEscheat); + existingProduct.daysToEscheat, existingProduct.accountMappingForPayment); } public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct, @@ -212,6 +215,7 @@ public final class SavingsProductData implements Serializable { final Map> accountingMappingOptions = null; final Collection chargeOptions = null; final Collection penaltyOptions = null; + final String accountMappingForPayment = null; return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, @@ -227,7 +231,7 @@ public final class SavingsProductData implements Serializable { existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions, existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, - existingProduct.daysToDormancy, existingProduct.daysToEscheat); + existingProduct.daysToDormancy, existingProduct.daysToEscheat, existingProduct.accountMappingForPayment); } public static SavingsProductData instance(final Long id, final String name, final String shortName, final String description, @@ -260,6 +264,7 @@ public final class SavingsProductData implements Serializable { final Collection feeToIncomeAccountMappings = null; final Collection penaltyToIncomeAccountMappings = null; final Collection taxGroupOptions = null; + final String accountMappingForPayment = null; return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, @@ -270,7 +275,7 @@ public final class SavingsProductData implements Serializable { penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, - daysToDormancy, daysToEscheat); + daysToDormancy, daysToEscheat, accountMappingForPayment); } public static SavingsProductData lookup(final Long id, final String name) { @@ -320,6 +325,7 @@ public final class SavingsProductData implements Serializable { final Long daysToInactive = null; final Long daysToDormancy = null; final Long daysToEscheat = null; + final String accountMappingForPayment = null; return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, @@ -330,7 +336,7 @@ public final class SavingsProductData implements Serializable { penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, - daysToDormancy, daysToEscheat); + daysToDormancy, daysToEscheat, accountMappingForPayment); } private SavingsProductData(final Long id, final String name, final String shortName, final String description, @@ -354,7 +360,7 @@ public final class SavingsProductData implements Serializable { final BigDecimal minBalanceForInterestCalculation, 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 Long daysToDormancy, final Long daysToEscheat, final String accountMappingForPayment) { this.id = id; this.name = name; this.shortName = shortName; @@ -411,6 +417,7 @@ public final class SavingsProductData implements Serializable { this.daysToInactive = daysToInactive; this.daysToDormancy = daysToDormancy; this.daysToEscheat = daysToEscheat; + this.accountMappingForPayment = accountMappingForPayment; } public boolean hasAccountingEnabled() { diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V374__adding_expenseliablity_to_paymentorfeetype.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V374__adding_expenseliablity_to_paymentorfeetype.sql new file mode 100644 index 000000000..fa090bee2 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V374__adding_expenseliablity_to_paymentorfeetype.sql @@ -0,0 +1,25 @@ +-- +-- 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. +-- + +ALTER TABLE `c_configuration` + ADD COLUMN `string_value` VARCHAR(100) NULL DEFAULT NULL AFTER `date_value`; + +insert into c_configuration(name, string_value, enabled, description) values('account-mapping-for-payment-type', 'Asset', '1', 'Asset: default for asset, Use comma seperated values for Liability, Asset and Expense accounts'); + +insert into c_configuration(name, string_value, enabled, description) values('account-mapping-for-charge', 'Income', '1', 'Income: default for Income, Use comma seperated values for Income, Liability and Expense accounts'); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java index 39657e281..672185792 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java @@ -88,9 +88,9 @@ public class GlobalConfigurationHelper { ArrayList expectedGlobalConfigurations = getAllDefaultGlobalConfigurations(); ArrayList actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec); - // There are currently 30 global configurations. - Assertions.assertEquals(30, expectedGlobalConfigurations.size()); - Assertions.assertEquals(30, actualGlobalConfigurations.size()); + // There are currently 32 global configurations. + Assertions.assertEquals(32, expectedGlobalConfigurations.size()); + Assertions.assertEquals(32, actualGlobalConfigurations.size()); for (int i = 0; i < expectedGlobalConfigurations.size(); i++) { @@ -362,6 +362,24 @@ public class GlobalConfigurationHelper { isInterestAppropriationEnabled.put("trapDoor", false); defaults.add(isInterestAppropriationEnabled); + HashMap isAccountMappedForPayment = new HashMap<>(); + isAccountMappedForPayment.put("id", 35); + isAccountMappedForPayment.put("name", "account-mapping-for-payment-type"); + isAccountMappedForPayment.put("value", 0); + isAccountMappedForPayment.put("enabled", false); + isAccountMappedForPayment.put("trapDoor", false); + isAccountMappedForPayment.put("string_value", "Asset"); + defaults.add(isAccountMappedForPayment); + + HashMap isAccountMappedForCharge = new HashMap<>(); + isAccountMappedForCharge.put("id", 36); + isAccountMappedForCharge.put("name", "account-mapping-for-charge"); + isAccountMappedForCharge.put("value", 0); + isAccountMappedForCharge.put("enabled", false); + isAccountMappedForCharge.put("trapDoor", false); + isAccountMappedForCharge.put("string_value", "Income"); + defaults.add(isAccountMappedForCharge); + return defaults; }