From f32225e2758197c48ffc2242fbdab90c6c1268f9 Mon Sep 17 00:00:00 2001 From: Madhukar Date: Tue, 24 Dec 2013 17:38:24 +0530 Subject: [PATCH] Adding short names for loan and saving products --- api-docs/apiLive.htm | 26 +++++++++++++++++-- ...r_m_product_loan_and_m_savings_product.sql | 18 +++++++++++++ ...ollectionSheetReadPlatformServiceImpl.java | 2 +- .../loanproduct/LoanProductConstants.java | 1 + .../api/LoanProductsApiResource.java | 2 +- .../loanproduct/data/LoanProductData.java | 14 +++++++--- .../loanproduct/domain/LoanProduct.java | 19 +++++++++++--- .../LoanProductDataValidator.java | 10 ++++++- .../LoanProductReadPlatformServiceImpl.java | 5 ++-- ...WritePlatformServiceJpaRepositoryImpl.java | 5 ++++ .../savings/SavingsApiConstants.java | 5 ++-- .../savings/data/SavingsProductData.java | 26 +++++++++++-------- .../data/SavingsProductDataValidator.java | 9 +++++++ .../savings/domain/SavingsProduct.java | 23 ++++++++++++---- .../domain/SavingsProductAssembler.java | 4 ++- ...SavingsProductReadPlatformServiceImpl.java | 8 +++--- ...WritePlatformServiceJpaRepositoryImpl.java | 5 ++++ 17 files changed, 145 insertions(+), 37 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V137_1__add_short_name_for_m_product_loan_and_m_savings_product.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 6526774d7..6e94d6580 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -7873,6 +7873,14 @@ Request Body: Name associated with loan product on system. + + shortName + + + Shortname associated with a loan product.
+ An abbreviated version of the name, used in reports or menus where space is limited, such as Collection Sheets. + + description @@ -8090,6 +8098,7 @@ GET https://DomainName/api/v1/loanproducts { "id": 1, "name": "personal loan product", + "shortName": "pe1", "includeInBorrowerCycle": false, "useBorrowerCycle": false, "startDate": [ @@ -8937,6 +8946,7 @@ GET https://DomainName/api/v1/loanproducts/{productId} { "id": 11, "name": "advanced accounting", + "shortName": "ad11", "includeInBorrowerCycle": true, "useBorrowerCycle": true, "status": "loanProduct.active", @@ -9148,7 +9158,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
Mandatory Fields
- name, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, + name, shortName, currencyCode, digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, transactionProcessingStrategyId, @@ -9222,6 +9232,7 @@ Request Body: ], "accountingRule":"2", "name":"product 5", +"shortName":"prd5", "principal":"5000", "numberOfRepayments":"7", "repaymentEvery":"7", @@ -9253,6 +9264,7 @@ Content-Type: application/json Request Body: { "name": "LP Cash Accounting", + "shortName": "LPCA", "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", @@ -9312,6 +9324,7 @@ Content-Type: application/json Request Body: { "name": "LP Accrual Accounting", + "shortName": "LPAA", "currencyCode": "USD", "locale": "en_GB", "digitsAfterDecimal": "2", @@ -12934,6 +12947,11 @@ Content-Type: application/json name The name of the product offering. + shortName + Shortname associated with a saving product.
+ An abbreviated version of the name, used in reports or menus where space is limited. + + description A description of the product offering. @@ -13537,7 +13555,7 @@ Content-Type: application/json -
Mandatory Fields
name, description, currencyCode, digitsAfterDecimal,inMultiplesOf, nominalAnnualInterestRate, + name, shortName, description, currencyCode, digitsAfterDecimal,inMultiplesOf, nominalAnnualInterestRate, interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType,accountingRule
@@ -13561,6 +13579,7 @@ Content-Type: application/json Request Body: { "name": "Passbook Savings", + "shortName": "PBSV", "description": "Daily compounding using Daily Balance, 5% per year, 365 days in year", "currencyCode": "USD", "digitsAfterDecimal": 2, @@ -13586,6 +13605,7 @@ Request Body: { "locale": "en", "name": "New Passbook Savings ", + "shortName": "NPBS", "description": "A savings product", "currencyCode": "USD", "digitsAfterDecimal": "2", @@ -13652,6 +13672,7 @@ Request Body: { "id": 1, "name": "savings product", + "shortName": "sa1", "description": "gtasga", "currency": { "code": "USD", @@ -13835,6 +13856,7 @@ Content-Type: application/json { "id": 1, "name": "Savings product", + "shortName": "sa1", "description": "gtasga", "currency": { "code": "USD", diff --git a/mifosng-db/migrations/core_db/V137_1__add_short_name_for_m_product_loan_and_m_savings_product.sql b/mifosng-db/migrations/core_db/V137_1__add_short_name_for_m_product_loan_and_m_savings_product.sql new file mode 100644 index 000000000..bab1844ee --- /dev/null +++ b/mifosng-db/migrations/core_db/V137_1__add_short_name_for_m_product_loan_and_m_savings_product.sql @@ -0,0 +1,18 @@ +ALTER TABLE `m_product_loan` + ADD COLUMN `short_name` VARCHAR(4) NULL DEFAULT NULL AFTER `id`; +update m_product_loan set short_name=concat(LEFT(name,2), RIGHT(id,2)) where short_name is null; +ALTER TABLE `m_product_loan` + ALTER `short_name` DROP DEFAULT; +ALTER TABLE `m_product_loan` + CHANGE COLUMN `short_name` `short_name` VARCHAR(4) NOT NULL AFTER `id`; +ALTER TABLE `m_product_loan` + ADD UNIQUE INDEX `unq_short_name` (`short_name`); + +ALTER TABLE `m_savings_product` + ADD COLUMN `short_name` VARCHAR(4) NULL DEFAULT NULL AFTER `name`; +update m_savings_product set short_name=concat(LEFT(name,2), RIGHT(id,2)) where short_name is null; +ALTER TABLE `m_savings_product` + ALTER `short_name` DROP DEFAULT; +ALTER TABLE `m_savings_product` + CHANGE COLUMN `short_name` `short_name` VARCHAR(4) NOT NULL AFTER `name`, + ADD UNIQUE INDEX `sp_unq_short_name` (`short_name`); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index c0afd6b92..95f30c603 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -215,7 +215,7 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe + "ln.id As loanId, " + "ln.account_no As accountId, " + "ln.loan_status_id As accountStatusId, " - + "pl.name As productShortName, " + + "pl.short_name As productShortName, " + "ln.product_id As productId, " + "ln.currency_code as currencyCode, ln.currency_digits as currencyDigits, ln.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + "if(ln.loan_status_id = 200 , ln.principal_amount , null) As disbursementAmount, " diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java index 2452bc8b5..69f819cc9 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java @@ -49,4 +49,5 @@ public interface LoanProductConstants { public static final String VALUE_CONDITION_END_WITH_ERROR = "condition.type.must.end.with.greterthan"; public static final String VALUE_CONDITION_START_WITH_ERROR = "condition.type.must.start.with.equal"; + public static final String shortName = "shortName"; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java index c14e0d423..a55cbe902 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java @@ -63,7 +63,7 @@ import org.springframework.stereotype.Component; @Scope("singleton") public class LoanProductsApiResource { - private final Set LOAN_PRODUCT_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "name", "description", "fundId", + private final Set LOAN_PRODUCT_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "name", "shortName", "description", "fundId", "fundName", "includeInBorrowerCycle", "currency", "principal", "minPrincipal", "maxPrincipal", "numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "graceOnPrincipalPayment", "graceOnInterestPayment", "graceOnInterestCharged", "interestRatePerPeriod", "minInterestRatePerPeriod", diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java index 18b4c36e8..644d9a773 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java @@ -35,6 +35,7 @@ public class LoanProductData { private final Long id; private final String name; + private final String shortName; private final String description; private final Long fundId; private final String fundName; @@ -109,6 +110,7 @@ public class LoanProductData { * Used when returning lookup information about loan product for dropdowns. */ public static LoanProductData lookup(final Long id, final String name) { + final String shortName = null; final String description = null; final CurrencyData currency = null; final BigDecimal principal = null; @@ -146,7 +148,7 @@ public class LoanProductData { final LocalDate closeDate = null; final String status = null; final String externalId = null; - return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, + return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, @@ -156,6 +158,7 @@ public class LoanProductData { } public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) { + final String shortName = null; final String description = null; final BigDecimal principal = null; final BigDecimal minPrincipal = null; @@ -195,7 +198,7 @@ public class LoanProductData { final Collection interestRateVariations = new ArrayList(1); final Collection numberOfRepaymentVariations = new ArrayList(1); - return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, + return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, @@ -207,6 +210,7 @@ public class LoanProductData { public static LoanProductData sensibleDefaultsForNewLoanProductCreation() { final Long id = null; final String name = null; + final String shortName = null; final String description = null; final CurrencyData currency = CurrencyData.blank(); final BigDecimal principal = null; @@ -250,7 +254,7 @@ public class LoanProductData { final String status = null; final String externalId = null; - return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, + return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId, @@ -270,7 +274,7 @@ public class LoanProductData { return productData; } - public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency, + public LoanProductData(final Long id, final String name, final String shortName, final String description, final CurrencyData currency, final BigDecimal principal, final BigDecimal minPrincipal, final BigDecimal maxPrincipal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer minNumberOfRepayments, final Integer maxNumberOfRepayments, final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal minInterestRatePerPeriod, @@ -285,6 +289,7 @@ public class LoanProductData { Collection numberOfRepaymentVariations) { this.id = id; this.name = name; + this.shortName = shortName; this.description = description; this.currency = currency; this.principal = principal; @@ -354,6 +359,7 @@ public class LoanProductData { final List valueConditionTypeOptions) { this.id = productData.id; this.name = productData.name; + this.shortName = productData.shortName; this.description = productData.description; this.fundId = productData.fundId; this.fundName = productData.fundName; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 660da76aa..729785598 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -57,7 +57,8 @@ import com.google.gson.JsonObject; */ @Entity @Table(name = "m_product_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name"), - @UniqueConstraint(columnNames = { "external_id" }, name = "external_id_UNIQUE") }) + @UniqueConstraint(columnNames = { "external_id" }, name = "external_id_UNIQUE"), + @UniqueConstraint(columnNames = { "short_name" }, name = "unq_short_name")}) public class LoanProduct extends AbstractPersistable { @ManyToOne @@ -70,6 +71,9 @@ public class LoanProduct extends AbstractPersistable { @Column(name = "name", nullable = false, unique = true) private String name; + + @Column(name = "short_name", nullable = false, unique = true) + private String shortName; @Column(name = "description") private String description; @@ -112,6 +116,7 @@ public class LoanProduct extends AbstractPersistable { final List productCharges, final JsonCommand command, final AprCalculator aprCalculator) { final String name = command.stringValueOfParameterNamed("name"); + final String shortName = command.stringValueOfParameterNamed(LoanProductConstants.shortName); final String description = command.stringValueOfParameterNamed("description"); final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); final Integer digitsAfterDecimal = command.integerValueOfParameterNamed("digitsAfterDecimal"); @@ -160,7 +165,7 @@ public class LoanProduct extends AbstractPersistable { populateBorrowerCyclevariations(command, loanProductBorrowerCycleVariations); } - return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal, + return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, @@ -362,7 +367,7 @@ public class LoanProduct extends AbstractPersistable { } public LoanProduct(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String name, - final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal, + final String shortName, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal, final BigDecimal defaultMinPrincipal, final BigDecimal defaultMaxPrincipal, final BigDecimal defaultNominalInterestRatePerPeriod, final BigDecimal defaultMinNominalInterestRatePerPeriod, final BigDecimal defaultMaxNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType, @@ -378,6 +383,7 @@ public class LoanProduct extends AbstractPersistable { this.fund = fund; this.transactionProcessingStrategy = transactionProcessingStrategy; this.name = name.trim(); + this.shortName = shortName.trim(); if (StringUtils.isNotBlank(description)) { this.description = description.trim(); } else { @@ -477,6 +483,13 @@ public class LoanProduct extends AbstractPersistable { actualChanges.put(nameParamName, newValue); this.name = newValue; } + + final String shortNameParamName = LoanProductConstants.shortName; + if (command.isChangeInStringParameterNamed(shortNameParamName, this.shortName)) { + final String newValue = command.stringValueOfParameterNamed(shortNameParamName); + actualChanges.put(shortNameParamName, newValue); + this.shortName = newValue; + } final String descriptionParamName = "description"; if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index b0bb1c0bc..937642112 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -56,7 +56,7 @@ public final class LoanProductDataValidator { LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(),LoanProductConstants.useBorrowerCycleParameterName, LoanProductConstants.principalVariationsForBorrowerCycleParameterName,LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName, - LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName)); + LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName, LoanProductConstants.shortName)); private final FromJsonHelper fromApiJsonHelper; @@ -78,6 +78,9 @@ public final class LoanProductDataValidator { final String name = this.fromApiJsonHelper.extractStringNamed("name", element); baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100); + + final String shortName = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.shortName, element); + baseDataValidator.reset().parameter(LoanProductConstants.shortName).value(shortName).notBlank().notExceedingLengthOf(4); final String description = this.fromApiJsonHelper.extractStringNamed("description", element); baseDataValidator.reset().parameter("description").value(description).notExceedingLengthOf(500); @@ -340,6 +343,11 @@ public final class LoanProductDataValidator { final String name = this.fromApiJsonHelper.extractStringNamed("name", element); baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100); } + + if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.shortName, element)) { + final String shortName = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.shortName, element); + baseDataValidator.reset().parameter(LoanProductConstants.shortName).value(shortName).notBlank().notExceedingLengthOf(4); + } if (this.fromApiJsonHelper.parameterExists("description", element)) { final String description = this.fromApiJsonHelper.extractStringNamed("description", element); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index cb5306051..3ec6715a5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -112,7 +112,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo public String loanProductSchema() { return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_id as transactionStrategyId, ltps.name as transactionStrategyName, " - + "lp.name as name, lp.description as description, " + + "lp.name as name, lp.short_name as shortName, lp.description as description, " + "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, lp.currency_multiplesof as inMultiplesOf, " + "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, " + "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod," @@ -132,6 +132,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo final Long id = rs.getLong("id"); final String name = rs.getString("name"); + final String shortName = rs.getString("shortName"); final String description = rs.getString("description"); final Long fundId = JdbcSupport.getLong(rs, "fundId"); final String fundName = rs.getString("fundName"); @@ -213,7 +214,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo } } - return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, + return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java index 7cebc8070..7b4ee21bb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java @@ -237,6 +237,11 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro final String name = command.stringValueOfParameterNamed("name"); throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.name", "Loan product with name `" + name + "` already exists", "name", name); + } else if (realCause.getMessage().contains("unq_short_name")) { + + final String shortName = command.stringValueOfParameterNamed("shortName"); + throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.short.name", "Loan product with short name `" + shortName + + "` already exists", "shortName", shortName); } else if (realCause.getMessage().contains("Duplicate entry")) { final Object[] args = null; throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.charge", diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index 89ba2d3fa..b74eaf285 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -66,6 +66,7 @@ public class SavingsApiConstants { public static final String activeParamName = "active"; public static final String nameParamName = "name"; + public static final String shortNameParamName = "shortName"; public static final String descriptionParamName = "description"; public static final String currencyCodeParamName = "currencyCode"; public static final String digitsAfterDecimalParamName = "digitsAfterDecimal"; @@ -129,7 +130,7 @@ public class SavingsApiConstants { public static final Set SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, - monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, + monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, @@ -150,7 +151,7 @@ public class SavingsApiConstants { * parameters to match those of request parameters. */ public static final Set SAVINGS_PRODUCT_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, - nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, inMultiplesOfParamName, + nameParamName, shortNameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java index c5bbf8e56..3704bf9b8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java @@ -26,6 +26,7 @@ public class SavingsProductData { private final Long id; private final String name; + private final String shortName; private final String description; private final CurrencyData currency; private final BigDecimal nominalAnnualInterestRate; @@ -76,6 +77,7 @@ public class SavingsProductData { final Long id = null; final String name = null; + final String shortName = null; final String description = null; final BigDecimal nominalAnnualInterestRate = null; final BigDecimal minRequiredOpeningBalance = null; @@ -88,7 +90,7 @@ public class SavingsProductData { final Collection feeToIncomeAccountMappings = null; final Collection penaltyToIncomeAccountMappings = null; - return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingRule, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, @@ -99,7 +101,7 @@ public class SavingsProductData { } public static SavingsProductData withCharges(final SavingsProductData product, final Collection charges) { - return new SavingsProductData(product.id, product.name, product.description, product.currency, product.nominalAnnualInterestRate, + 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, product.lockinPeriodFrequency, product.lockinPeriodFrequencyType, product.withdrawalFeeForTransfers, product.accountingRule, product.accountingMappings, @@ -125,8 +127,8 @@ public class SavingsProductData { final Map> accountingMappingOptions, final Collection chargeOptions, final Collection penaltyOptions) { - return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.description, existingProduct.currency, - existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, + return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, + existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance, existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, @@ -157,8 +159,8 @@ public class SavingsProductData { final Collection chargeOptions = null; final Collection penaltyOptions = null; - return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.description, existingProduct.currency, - existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, + return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description, + existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance, existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, @@ -170,8 +172,8 @@ public class SavingsProductData { penaltyToIncomeAccountMappings); } - public static SavingsProductData instance(final Long id, final String name, final String description, final CurrencyData currency, - final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, + public static SavingsProductData instance(final Long id, final String name, final String shortName, final String description, + final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, @@ -196,7 +198,7 @@ public class SavingsProductData { final Collection feeToIncomeAccountMappings = null; final Collection penaltyToIncomeAccountMappings = null; - return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, @@ -208,6 +210,7 @@ public class SavingsProductData { public static SavingsProductData lookup(final Long id, final String name) { + final String shortName = null; final CurrencyData currency = null; final String description = null; final BigDecimal nominalAnnualInterestRate = null; @@ -239,7 +242,7 @@ public class SavingsProductData { final Collection feeToIncomeAccountMappings = null; final Collection penaltyToIncomeAccountMappings = null; - return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, + return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingType, accountingMappings, paymentChannelToFundSourceMappings, @@ -249,7 +252,7 @@ public class SavingsProductData { penaltyToIncomeAccountMappings); } - private SavingsProductData(final Long id, final String name, final String description, final CurrencyData currency, + private SavingsProductData(final Long id, final String name, final String shortName, final String description, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, @@ -268,6 +271,7 @@ public class SavingsProductData { final Collection penaltyToIncomeAccountMappings) { this.id = id; this.name = name; + this.shortName = shortName; this.description = description; this.currency = currency; this.nominalAnnualInterestRate = nominalAnnualInterestRate; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java index ce39ed6e2..9668e94ed 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java @@ -23,6 +23,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -76,6 +77,9 @@ public class SavingsProductDataValidator { final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100); + + final String shortName = this.fromApiJsonHelper.extractStringNamed(shortNameParamName, element); + baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4); final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); baseDataValidator.reset().parameter(descriptionParamName).value(description).notBlank().notExceedingLengthOf(500); @@ -260,6 +264,11 @@ public class SavingsProductDataValidator { final String name = this.fromApiJsonHelper.extractStringNamed(nameParamName, element); baseDataValidator.reset().parameter(nameParamName).value(name).notBlank().notExceedingLengthOf(100); } + + if (this.fromApiJsonHelper.parameterExists(shortNameParamName, element)) { + final String shortName = this.fromApiJsonHelper.extractStringNamed(shortNameParamName, element); + baseDataValidator.reset().parameter(shortNameParamName).value(shortName).notBlank().notExceedingLengthOf(4); + } if (this.fromApiJsonHelper.parameterExists(descriptionParamName, element)) { final String description = this.fromApiJsonHelper.extractStringNamed(descriptionParamName, element); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java index d45cde179..7657ded94 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java @@ -23,6 +23,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; import java.math.BigDecimal; import java.util.ArrayList; @@ -39,6 +40,7 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; +import javax.persistence.UniqueConstraint; import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.infrastructure.core.api.JsonCommand; @@ -58,11 +60,15 @@ import org.springframework.data.jpa.domain.AbstractPersistable; import com.google.gson.JsonArray; @Entity -@Table(name = "m_savings_product") +@Table(name = "m_savings_product", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "sp_unq_name"), + @UniqueConstraint(columnNames = { "short_name" }, name = "sp_unq_short_name")}) public class SavingsProduct extends AbstractPersistable { - @Column(name = "name", nullable = false) + @Column(name = "name", nullable = false, unique = true) private String name; + + @Column(name = "short_name", nullable = false, unique = true) + private String shortName; @Column(name = "description", length = 500, nullable = false) private String description; @@ -124,14 +130,14 @@ public class SavingsProduct extends AbstractPersistable { @JoinTable(name = "m_savings_product_charge", joinColumns = @JoinColumn(name = "savings_product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) private Set charges; - public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency, + public static SavingsProduct createNew(final String name, final String shortName, final String description, final MonetaryCurrency currency, final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges) { - return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, + return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges); } @@ -141,7 +147,7 @@ public class SavingsProduct extends AbstractPersistable { this.description = null; } - private SavingsProduct(final String name, final String description, final MonetaryCurrency currency, final BigDecimal interestRate, + private SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency, final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, @@ -149,6 +155,7 @@ public class SavingsProduct extends AbstractPersistable { final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges) { this.name = name; + this.shortName = shortName; this.description = description; this.currency = currency; @@ -238,6 +245,12 @@ public class SavingsProduct extends AbstractPersistable { actualChanges.put(nameParamName, newValue); this.name = newValue; } + + if (command.isChangeInStringParameterNamed(shortNameParamName, this.name)) { + final String newValue = command.stringValueOfParameterNamed(shortNameParamName); + actualChanges.put(shortNameParamName, newValue); + this.shortName = newValue; + } if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) { final String newValue = command.stringValueOfParameterNamed(descriptionParamName); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java index 6f7f3bb8d..7a724a642 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java @@ -21,6 +21,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.shortNameParamName; import java.math.BigDecimal; import java.util.HashSet; @@ -57,6 +58,7 @@ public class SavingsProductAssembler { public SavingsProduct assemble(final JsonCommand command) { final String name = command.stringValueOfParameterNamed(nameParamName); + final String shortName = command.stringValueOfParameterNamed(shortNameParamName); final String description = command.stringValueOfParameterNamed(descriptionParamName); final String currencyCode = command.stringValueOfParameterNamed(currencyCodeParamName); @@ -111,7 +113,7 @@ public class SavingsProductAssembler { // Savings product charges final Set charges = assembleListOfSavingsProductCharges(command, currencyCode); - return SavingsProduct.createNew(name, description, currency, interestRate, interestCompoundingPeriodType, + return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index c15fc8ce0..d130ac5aa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -73,9 +73,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead public SavingProductMapper() { final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("sp.id as id, sp.name as name, sp.description as description, "); - sqlBuilder - .append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("sp.id as id, sp.name as name, sp.short_name as shortName, sp.description as description, "); + sqlBuilder.append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, "); sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualInterestRate, "); @@ -108,6 +107,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead final Long id = rs.getLong("id"); final String name = rs.getString("name"); + final String shortName = rs.getString("shortName"); final String description = rs.getString("description"); final String currencyCode = rs.getString("currencyCode"); @@ -168,7 +168,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead annualFeeOnMonthDay = new MonthDay(annualFeeOnMonth, annualFeeOnDay); } */ - return SavingsProductData.instance(id, name, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType, + return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingRuleType); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java index 1316ccd2d..bd530effa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java @@ -65,6 +65,11 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi final String name = command.stringValueOfParameterNamed("name"); throw new PlatformDataIntegrityException("error.msg.product.savings.duplicate.name", "Savings product with name `" + name + "` already exists", "name", name); + } else if (realCause.getMessage().contains("sp_unq_short_name")) { + + final String shortName = command.stringValueOfParameterNamed("shortName"); + throw new PlatformDataIntegrityException("error.msg.product.savings.duplicate.short.name", "Savings product with short name `" + + shortName + "` already exists", "shortName", shortName); } logAsErrorUnexpectedDataIntegrityException(dae);