Skip to content

Conversation

@alexis-via
Copy link
Contributor

res.partner.title was dropped in 19.0.
This module restores title on partners.
I added "active" and "sequence" on res.partner.title, so no more need for OCA modules partner_title_active (cf 17.0) and partner_title_order (cf 18.0)

partner_title

@alexis-via
Copy link
Contributor Author

Feel free to propose a better icon than the "right hand" that I have used !

@alexis-via
Copy link
Contributor Author

I changed to use the "user" icon, it's better

new_icon

@alexis-via alexis-via force-pushed the 19-add-partner_title branch 2 times, most recently from a7994a6 to d0fe8a5 Compare October 28, 2025 22:31
Comment on lines +12 to +18
title_id = fields.Many2one(
"res.partner.title",
ondelete="restrict",
compute="_compute_title_id",
store=True,
precompute=True,
readonly=False,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you created a compute field just to clear the title when the partner type is set to company.
I’d prefer to avoid adding multiple attributes and instead use a standard Many2one field, clearing the value through an onchange.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now try to avoid onchange as much as we can, especially in new code. That's what I'm doing here.

precompute=True,
readonly=False,
)
name_with_title = fields.Char(compute="_compute_name_with_title")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any specific purpose for using it somewhere?
Right now, it’s not even displayed in the view.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's very useful in reports.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment about that to explain.

Comment on lines +65 to +77
<menuitem
id="res_partner_title_parent_menu"
parent="base.menu_custom"
name="Contact Titles"
sequence="500"
/>

<menuitem
id="res_partner_title_menu"
action="res_partner_title_action"
parent="res_partner_title_parent_menu"
sequence="10"
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t we add this menu under the Configuration section of the Contacts app, like in the previous versions, by adding contacts as a dependency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep. See my PR #2189 that provides a glue module with contacts.

Comment on lines +8 to +27
<record id="res_partner_title_form" model="ir.ui.view">
<field name="model">res.partner.title</field>
<field name="arch" type="xml">
<form>
<sheet>
<widget
name="web_ribbon"
title="Archived"
bg_color="bg-danger"
invisible="active"
/>
<group name="main">
<field name="name" />
<field name="shortcut" />
<field name="active" invisible="1" />
</group>
</sheet>
</form>
</field>
</record>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t it enough to just have an editable tree view?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally prefer modules that provide both a tree view and a form view.

Comment on lines 37 to 47
@api.constrains("title_id", "is_company")
def _check_title(self):
for partner in self:
if partner.is_company and partner.title_id:
raise ValidationError(
self.env._(
"Partner '%(name)s' is a company, so it cannot have a title.",
name=partner.display_name,
)
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found an issue when creating a new record — if I change the partner type to Person, fill in the title, and save, it causes a problem.

Screenshot 2025-10-29 102041

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arf !!! Looks like a 19.0 framework issue: when we enter the constraint code, neither is_company nor company_type has the proper value.
As this constraint is really not important, I removed it.

if partner.is_company:
partner.title_id = False

@api.depends("is_company", "title_id")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will do nothing for a non-stored computed field.

Suggested change
@api.depends("is_company", "title_id")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you really sure about this? I don't think so.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course I’m sure. Why don’t you check and see?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some examples in odoo code where they use @api.depends on non-stored fields:
https://github.com/odoo/odoo/blob/19.0/odoo/addons/base/models/res_partner.py#L403
https://github.com/odoo/odoo/blob/19.0/addons/account/models/partner.py#L1009
https://github.com/odoo/odoo/blob/19.0/addons/account/models/partner.py#L998
But, of course, the code of Odoo may be wrong.
I thought that @api.depends on non-stored field was useful to invalidate the cache.
Of course, it is not as important as on stored fields where it is vital...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the behavior in detail and found that without the @api.depends decorator, the compute method is triggered when changing the name field, but the name value inside the method still shows the old value before the change, resulting in an incorrect name_with_title. It also does not trigger when other fields are changed. (Seems like strange)

With the @api.depends decorator, the name_with_title field updates instantly before saving when title is changed.

Since the field isn’t displayed in the view, having or not having the decorator doesn’t affect the final result — the correct name_with_title appears after saving in both cases. Therefore, we can remove the decorator. However, if you prefer to keep it, the name field should also be included in the @api.depends.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dropped

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of @api.depends on _compute_name_with_title() broke the tests ! That's the best proof that @api.depends is useful on non-stored fields.
@AungKoKoLin1997 Please, don't say "Of course I’m sure. Why don’t you check and see?" when you're completely wrong. You made me loose my time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I restored @api.depends => tests are green again.

Comment on lines +29 to +35
def _compute_name_with_title(self):
for partner in self:
name = partner.name
if not partner.is_company and partner.title_id:
title = partner.title_id.shortcut or partner.title_id.name
name = " ".join([title, name])
partner.name_with_title = name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _compute_name_with_title(self):
for partner in self:
name = partner.name
if not partner.is_company and partner.title_id:
title = partner.title_id.shortcut or partner.title_id.name
name = " ".join([title, name])
partner.name_with_title = name
def _compute_name_with_title(self):
for partner in self:
partner.name_with_title = partner.name
if not partner.is_company and partner.title_id:
title = partner.title_id.shortcut or partner.title_id.name
partner.name_with_title = " ".join([title, partner.name_with_title])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer my implementation...

@alexis-via alexis-via force-pushed the 19-add-partner_title branch 3 times, most recently from 69a7f47 to 38f8aa8 Compare October 30, 2025 11:04
@alexis-via alexis-via force-pushed the 19-add-partner_title branch from 38f8aa8 to fe006a1 Compare October 30, 2025 14:18
class ResPartner(models.Model):
_inherit = "res.partner"

title_id = fields.Many2one(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the field name should be changed to title following 18.0, or a migration script needs to be added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A migration script will be needed anyway (for data), so let's put proper field names.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants