-
Notifications
You must be signed in to change notification settings - Fork 23k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FIX] account_journal: avoid duplicate mail alias #159037
base: 17.0
Are you sure you want to change the base?
[FIX] account_journal: avoid duplicate mail alias #159037
Conversation
48c881a
to
f81e395
Compare
f81e395
to
050b332
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, added some preliminary remarks / questions :)
Would say that functional side should be checked with accounting team to be sure this is the behavior they want for journal aliases, which is not standard compared to other models. There is quite a lot of automatic computation that is not present in other modules and we should be sure it works as intended.
Please also note that you should provide some unit tests in order to highlight the issue, otherwise it is a bit hard to see the final result :) .
sanitized_alias_name = self.env['mail.alias']._sanitize_alias_name(base_alias_name) | ||
existing_alias = self.env['mail.alias'].search([('alias_name', '=', sanitized_alias_name)]) | ||
if len(existing_alias) > 0 and existing_alias.id != self.alias_id.id: | ||
alias_name = f'{sanitized_alias_name}-{code}-{company.id}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But code is already taken into account in base_alias_name ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the failing upgrades (around 17 since last week), when I tested in a fresh DB, and from reading the code (AFAICT) the mail alias generated from the journal doesn't have a code, journal 'Customer Invoices' would generate 'customer-invoices' as the alias name for example, and the full alias in most of the failing cases is -------@localhost
Please also note that you should provide some unit tests in order to highlight the issue, otherwise it is a bit hard to see the final result :) .
Ok! Indeed. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"-------@localhost" ? Encoding issue ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you tagged the user localhost xD
I used "----" to ignore the less important part, i meant to say that the domain is most of the time localhost
, and when it's not, it's still not unique, this is in the context of same company, the error is easy to reproduce..
But I didn't know about the uniqueness in a given alias domain, I will fix things and add a unit test!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But you talk about upgrade. However alias names should be unique already in 16, did something change ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But you talk about upgrade. However alias names should be unique already in 16, did something change ?
They are, but the way they are created isn't always unique.
As said in the commit message, the issue is mainly when the name of a journal is changed, because that doesn't change the name of the alias that was created with it:
- I create a journal named
A
, automatically an alias namedA
will be created. Afterwards if the journal name is changed, the alias will remainA
. (changing the journal name doesn't affect the alias name)
Now a new journal namedA
can be created because that name is not taken. This will fail because the creation of that journal will trigger the creation of an aliasA
which is already exists.
This also happens when users changed or manually translated the name of journals that where created by odoo because it doesn't suit their needs. As during the upgrade, Odoo will try to recreate the journals since the name is not the same anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see two different issues then
- create a journal, rename it, create a new journal with old name -> that's effectively a standard issue; would say we probably don't care in standard addons (there is few automatic alias name generation), but we can effectively improve accounting (but still not sure for the heuristic here, to check how accounting see it; shouldn't we add a number as suffix ?)
- upgrade trying to create journals again -> isn't it an upgrade issue ? Should it do it ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we add a number as suffix ?
That was the first version of the code, but the combination of code
and company_id
is unique, so we can have a unique alias without having numbering like 1,2,3...
upgrade trying to create journals again -> isn't it an upgrade issue ? Should it do it ?
During the upgrade, as in the creation of a new odoo DB, data is added from data/demo directories to the DB, that's normal
The issue with the account journals made by the chart template is that they don't have an xmlid, see here how they are created:
https://github.com/odoo/odoo/blob/17.0/addons/account/models/chart_template.py#L913
(Currently looking into why they are made this way)
So almost any change to these template journals will make them unrecognisable, when the chart is loaded again or during an upgrade, it will seem like data created by the user.
# Check for duplicate alias, if exists append a suffix to ensure uniqueness | ||
# It's not a duplicate if it's the same journal | ||
sanitized_alias_name = self.env['mail.alias']._sanitize_alias_name(base_alias_name) | ||
existing_alias = self.env['mail.alias'].search([('alias_name', '=', sanitized_alias_name)]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not true anymore with introduction of alias domains, alias names are not unique anymore, but unique in a given alias domain.
Hello I discussed the issue with a PO and he prefers the solution with adding a suffix on the new alias. We don't want to change the alias when changing the name of a journal as the domain might have already been transmitted to external sources (suppliers, ...). |
df643e6
to
7ecbcea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job for the PR. I've made a few comments. Also, when testing on the web interface, I see that setting the name, then the journal type, then the code makes the alias name without the code, it is not recomputed when writing the code, and it therefore write something like vendor-bills-false@mycompany.com
. Is it something expected ?
|
||
def test_alias_journal_after_rename(self): | ||
""" Test alias when journal is created with name matching existing alias """ | ||
company_name = 'company_1_data' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
company_name = 'company_1_data' | |
company_name = self.company_data['company'].name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the thorough review, it seems I misunderstood some things while reading and testing how the aliases are generated and I will start working on a better version.
However I'm unable to recreate the case where the code is False since the journal cannot be created without a code, in my case the alias is successfully created with the code. Could you tell me what you tried?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an example video where the code is False: https://app.screencastify.com/v3/watch/bymZIwT2wVgmPntjqBxV
maybe I misunderstood something, don't hesitate to tell me if so
base_alias_name = f'{base_alias_name}-{company_identifier}' | ||
|
||
# Check for duplicate alias, if exists append a suffix to ensure uniqueness | ||
# It's not a duplicate if it's the same journal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# It's not a duplicate if it's the same journal |
No need to specify this in my opinion
existing_alias = self.env['mail.alias'].search([('alias_name', '=', sanitized_alias_name)]) | ||
if len(existing_alias) > 0 and existing_alias.id != self.alias_id.id: | ||
alias_name = f'{sanitized_alias_name}-{code}' | ||
else: | ||
alias_name = sanitized_alias_name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
existing_alias = self.env['mail.alias'].search([('alias_name', '=', sanitized_alias_name)]) | |
if len(existing_alias) > 0 and existing_alias.id != self.alias_id.id: | |
alias_name = f'{sanitized_alias_name}-{code}' | |
else: | |
alias_name = sanitized_alias_name | |
existing_alias = self.env['mail.alias'].search([('alias_name', '=', sanitized_alias_name), ('id', '!=', self.alias_id.id)]) | |
alias_name = f'{sanitized_alias_name}-{code}' if existing_alias else sanitized_alias_name | |
return self.env['mail.alias']._sanitize_alias_name(alias_name) |
Something like this is a bit cleaner IMO, but you seem to have forgotten about TDE's comment on the uniqueness of alias name inside an alias domain.
Also, I see the function you try to improve is defined with a api.model
, meaning that the content of the self should not be relevant. However, you now use self.alias_id.id.
}) | ||
# check that the alias name is correctly set, name + company name + journal code | ||
self.assertEqual(journal_2.alias_name, | ||
self.env['mail.alias']._sanitize_alias_name(f'test-journal-{company_name}-{journal_2.code}')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.env['mail.alias']._sanitize_alias_name(f'test-journal-{company_name}-{journal_2.code}')) | |
self.env['mail.alias']._sanitize_alias_name(f'test-journal-{company_name}-{journal_2.code}')) |
Suggestion to align with the comparison line above
self.env['mail.alias']._sanitize_alias_name(f'test-journal-{company_name}-{journal_2.code}')) | ||
self.assertTrue(journal_2.alias_id) | ||
self.assertEqual(journal_2.alias_id.alias_parent_thread_id, journal_2.id, | ||
'Journal alias owned by journal itself') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'Journal alias owned by journal itself') | |
'Journal alias owned by journal itself') |
self.assertTrue(journal_2.alias_id) | ||
self.assertEqual(journal_2.alias_id.alias_parent_thread_id, journal_2.id, | ||
'Journal alias owned by journal itself') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly does this test ? I don't really understand, as your code seems to only improve the naming of the alias names
7ecbcea
to
88b8522
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how you ended up doing this, but this is the wrong approach...
b8da169
to
1239ae2
Compare
1239ae2
to
baf3651
Compare
baf3651
to
f2628e8
Compare
10be5b8
to
5de9d4a
Compare
5de9d4a
to
96afa34
Compare
Steps to reproduce: Create an account journal. Rename the created journal (let's say from A to B). Create a new account journal named A. Changing the name of a journal doesn't change the name of the alias, An duplicate error will occur. Additionally the error can happen if the user has manually added aliases with the same name before upgrading to 17.0. Adding the code of the journal to the alias name on creation when needed.
96afa34
to
1bc1365
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made a few comments 😃
('alias_name', '=', alias_name), | ||
]) | ||
|
||
if existing_alias > 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if existing_alias > 0: | |
if existing_alias: |
existing_alias = None | ||
if alias_domain_name: | ||
existing_alias = self.env['mail.alias'].search_count([ | ||
('alias_name', '=', alias_name), | ||
('alias_domain', '=', alias_domain_name), | ||
]) | ||
else: | ||
existing_alias = self.env['mail.alias'].search_count([ | ||
('alias_name', '=', alias_name), | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
existing_alias = None | |
if alias_domain_name: | |
existing_alias = self.env['mail.alias'].search_count([ | |
('alias_name', '=', alias_name), | |
('alias_domain', '=', alias_domain_name), | |
]) | |
else: | |
existing_alias = self.env['mail.alias'].search_count([ | |
('alias_name', '=', alias_name), | |
]) | |
domain = [('alias_name', '=', alias_name)] | |
if alias_domain_name: | |
domain.append(('alias_domain', '=', alias_domain_name)) | |
existing_alias = self.env['mail.alias'].search_count(domain, limit=1) |
Can be simplified a bit, also adding limit=1 is better for performances
@@ -604,6 +604,30 @@ def _alias_prepare_alias_name(self, alias_name, name, code, jtype, company): | |||
alias_name = f"{alias_name}-{company_identifier}" | |||
return self.env['mail.alias']._sanitize_alias_name(alias_name) | |||
|
|||
def _check_unique_alias(self, vals, company): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def _check_unique_alias(self, vals, company): | |
api.model | |
def _check_unique_alias(self, vals, company): |
self is irrelevant in your function so you can put api.model
vals['alias_name'] = self._alias_prepare_alias_name( | ||
False, vals.get('name'), vals.get('code'), journal_type, company | ||
) | ||
vals['alias_name'] = self._check_unique_alias(vals, company) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not trying to put the logic of the function _check_unique_alias
inside the function _alias_prepare_alias_name
? This way the function _alias_prepare_alias_name
always returns a unique name. It's just an idea so you might have arguments against. Also, this way, if set properly, the function _sanitize_alias_name
would be called after, making sure that no special character are used
Steps to reproduce:
Changing the name of a journal doesn't change the name of the alias, An error caused by duplicate mail alias names will occur. Additionally the error can happen if the user has manually added aliases with the same name before upgrading to 17.0.
Adding the code and company id of the journal to the alias to ensure uniqueness when an alias with the same name is already in the DB (as the combo
company_id
,code
is unique).Other possible fixes: (I'm not sure what's better here)
Description of the issue/feature this PR addresses:
Current behavior before PR:
Desired behavior after PR is merged:
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr