Foo Developer created FINERACT-2868:
---------------------------------------

             Summary: Duplicate product name / account number returns "Unknown 
data integrity issue" on PostgreSQL instead of the "already exists" error
                 Key: FINERACT-2868
                 URL: https://issues.apache.org/jira/browse/FINERACT-2868
             Project: Apache Fineract
          Issue Type: Bug
          Components: Savings
            Reporter: Foo Developer
            Assignee: Foo Developer


h3. Summary

On PostgreSQL, creating a savings product, fixed deposit product, loan product, 
savings/deposit account or loan whose unique field (name, short name, external 
ID, account number) is already taken does not return the specific "already 
exists" error. The request fails with the generic "Unknown data integrity 
issue" message instead, so the user cannot tell which field is the problem.

h3. Steps to reproduce (stock Fineract on PostgreSQL)
# {{POST /fineract-provider/api/v1/savingsproducts}} with a valid body, e.g.

{code:json}{"name":"Dup 
SP","shortName":"DSP","currencyCode":"USD","digitsAfterDecimal":2,"inMultiplesOf":0,"nominalAnnualInterestRate":0,"interestCompoundingPeriodType":1,"interestPostingPeriodType":4,"interestCalculationType":1,"interestCalculationDaysInYearType":365,"accountingRule":1,"locale":"en"}
{code}
# Send the same request again (or the same name with a different short name).
# Likewise {{POST /v1/loanproducts}} with the name of an existing loan product.

h3. Expected

{{error.msg.savingsproduct.duplicate.name}} ("Savings product with name {{Dup 
SP}} already exists", parameter {{name}}), and 
{{error.msg.product.loan.duplicate.name}} for the loan product.

h3. Actual

Both the second savings product request and the loan product request answer 
HTTP 403 with the generic fallback:

{code:json}{"httpStatusCode":"403","defaultUserMessage":"Unknown data integrity 
issue with savings 
product.","userMessageGlobalisationCode":"error.msg.savingsproduct.unknown.data.integrity.issue"}
{code}

{code:json}{"httpStatusCode":"403","defaultUserMessage":"Unknown data integrity 
issue with 
resource.","userMessageGlobalisationCode":"error.msg.product.loan.unknown.data.integrity.issue"}
{code}

The same happens for a duplicate account number or external ID on 
savings/deposit accounts and loans.

h3. Root cause

The {{handleDataIntegrityIssues}} methods match the violated constraint by 
name, using the explicit names from the old MySQL schema ({{sp_unq_name}}, 
{{sp_unq_short_name}}, {{sa_account_no_UNIQUE}}, {{sa_external_id_UNIQUE}} / 
{{sa_externalid_UNIQUE}}, {{unq_name}}, {{external_id}}, 
{{loan_account_no_UNIQUE}}, {{loan_externalid_UNIQUE}}). The Liquibase 
changelog declares these columns with a plain {{unique="true"}} and no 
constraint name, so on PostgreSQL the constraints get PostgreSQL's generated 
names:

||table||constraints on PostgreSQL||
|{{m_savings_product}}|{{m_savings_product_name_key}}, 
{{m_savings_product_short_name_key}}|
|{{m_savings_account}}|{{m_savings_account_account_no_key}}, 
{{m_savings_account_external_id_key}}|
|{{m_product_loan}}|{{m_product_loan_name_key}}, 
{{m_product_loan_external_id_key}}, {{m_product_loan_short_name_key}}|
|{{m_loan}}|{{m_loan_account_no_key}}, {{m_loan_external_id_key}}|

None of the handlers' strings occur in PostgreSQL's {{duplicate key value 
violates unique constraint "..."}} message, so every branch is skipped and the 
generic fallback is thrown. 
{{LoanProductWritePlatformServiceJpaRepositoryImpl}} already works around this 
for the short name only ({{containsDuplicateShortnameErrorForPostgreSQL}}, 
matching {{m_product_loan_short_name_key}}); name and external ID never got the 
same treatment.

h3. Proposed fix

In each handler, also match the PostgreSQL-generated constraint name next to 
the existing one, without changing the error codes or messages:
* {{SavingsProductWritePlatformServiceJpaRepositoryImpl}}, 
{{FixedDepositProductWritePlatformServiceJpaRepositoryImpl}} (same 
{{m_savings_product}} table)
* {{SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl}}, 
{{DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl}}
* {{LoanProductWritePlatformServiceJpaRepositoryImpl}} (name, external ID)
* {{LoanApplicationWritePlatformServiceJpaRepositoryImpl}} (account number, 
external ID)

Renaming the constraints in a migration would also work but touches existing 
databases; the matching change is local and keeps MySQL/MariaDB behaviour as it 
is.

h3. Test

A unit test for {{SavingsProductWritePlatformServiceJpaRepositoryImpl}} that 
feeds a PostgreSQL unique-violation ({{m_savings_product_name_key}} / 
{{m_savings_product_short_name_key}}) through {{create}} and asserts the 
{{duplicate.name}} / {{duplicate.short.name}} codes, plus the existing 
MySQL-name case.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to