From 3c62ca1eb96d571b2b686b5caee370324c589ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Voet=20=28ryv=29?= Date: Thu, 27 Apr 2023 14:43:43 +0000 Subject: [PATCH] [REM] core: remove `name_get` API Rationale ========= Since v8, the `display_name` field is present on all models. By default, `display_name` uses `name_get` which has pretty much the same purpose (return record name used by the web client). Gradually, many (backend) developers (and the ORM: https://github.com/odoo/odoo/commit/6da1c3ac4c036eac289597602976538e243cb939) started using `display_name` (more convenient than `record.name_get()[0][1]`) but it still had the `name_get` override. It becomes more complex than necessary and poeple start to misunderstand the two (and sometimes override both, leading to inconstiencies between `display_name`/`name_get`). To simplify the ORM and the API, we decided to keep only one of them, the `display_name` field: - It is much more convenient from a backend point of view (`record.name_get()[0][1]` vs `record.display_name`) - It is cached during the same transaction (and invalidated if its dependencies change) - It can be overridden like any other compute field (override `_compute_display_name` with any extra dependencies) - `name_get` is replaced by `read(['display_name'])` (API perceptive), which can actually be more efficient (if `display_name`'s depends are correct, the ORM will only fetch the fields it needs instead of every prefetchable field) Changes ======= - Deprecates `name_get` for the v17 and based the method on `display_name` (the opposite of before) - Converts all usage of `name_get` - Overrides of `name_get` are now overrides of `_compute_display_name` - For `res.partner`, rename the field store `display_name` into `complete_name` because `display_name` context-dependent and it makes no sense to have a compute store that is context-dependent. - Previously, it was possible to return multiple names for the same record with `name_get`, but it was tricky and most of the usage of this `name_get` didn't take this into account. The only example of this is the `name_get` of `product.product` (now use `", ".join()`). Part-of: odoo/odoo#122085 --- addons/account/models/account_account.py | 17 +-- addons/account/models/account_account_tag.py | 9 +- addons/account/models/account_incoterms.py | 6 +- addons/account/models/account_journal.py | 8 +- addons/account/models/account_move.py | 6 +- addons/account/models/account_move_line.py | 17 +-- addons/account/models/account_payment.py | 5 +- .../account/models/account_payment_method.py | 9 +- addons/account/models/account_report.py | 12 +- addons/account/models/account_tax.py | 6 +- addons/account/models/res_partner_bank.py | 12 +- addons/analytic/models/analytic_account.py | 6 +- .../base_address_extended/models/res_city.py | 8 +- addons/base_sparse_field/models/fields.py | 2 +- addons/calendar/models/calendar_event.py | 9 +- .../crm_iap_mine/models/crm_iap_lead_role.py | 10 +- .../models/crm_iap_lead_seniority.py | 5 +- addons/event/models/event_event.py | 8 +- addons/event/models/event_registration.py | 7 +- addons/event/models/event_ticket.py | 8 +- .../models/fleet_vehicle_assignation_log.py | 5 +- addons/fleet/models/fleet_vehicle_model.py | 8 +- .../models/gamification_challenge.py | 3 +- addons/hr/models/hr_department.py | 12 +- addons/hr/models/hr_employee.py | 7 +- addons/hr/models/res_partner.py | 6 +- addons/hr_attendance/models/hr_attendance.py | 24 ++-- .../hr_contract/report/hr_contract_history.py | 1 - addons/hr_holidays/models/hr_department.py | 2 +- addons/hr_holidays/models/hr_leave.py | 123 +++++++----------- .../hr_holidays/models/hr_leave_allocation.py | 21 ++- addons/hr_holidays/models/hr_leave_type.py | 26 ++-- addons/hr_holidays/views/hr_leave_views.xml | 4 +- .../models/hr_leave_type.py | 37 +++--- addons/hr_recruitment/models/hr_applicant.py | 12 +- addons/hr_skills/models/hr_skill.py | 7 +- addons/hr_skills/models/hr_skill_level.py | 7 +- addons/hr_timesheet/models/hr_employee.py | 10 +- addons/hr_timesheet/models/hr_timesheet.py | 25 ++-- addons/hr_timesheet/models/project_project.py | 11 +- addons/hr_timesheet/models/project_task.py | 10 +- addons/http_routing/models/ir_http.py | 1 - .../models/chatbot_script_answer.py | 12 +- .../models/ewaybill_type.py | 8 +- addons/l10n_it_edi/models/ddt.py | 6 +- .../models/l10n_latam_identification_type.py | 6 +- .../models/account_payment.py | 9 +- .../models/l10n_latam_document_type.py | 8 +- addons/loyalty/models/loyalty_card.py | 5 +- addons/loyalty/models/loyalty_reward.py | 5 +- addons/lunch/models/lunch_cashmove.py | 5 +- addons/lunch/models/lunch_supplier.py | 8 +- addons/lunch/models/lunch_topping.py | 6 +- addons/lunch/report/lunch_cashmove_report.py | 5 +- .../models/discuss/discuss_channel_member.py | 5 +- addons/mail/models/mail_activity.py | 6 +- addons/mail/models/mail_alias.py | 10 +- addons/mail/models/mail_message.py | 6 +- addons/mail/models/mail_thread.py | 2 +- addons/mail/models/mail_tracking_value.py | 4 +- .../mail/models/res_users_settings_volumes.py | 5 +- .../mail/static/src/core/web/message_patch.js | 2 +- addons/mail_plugin/controllers/mail_plugin.py | 2 +- addons/maintenance/models/maintenance.py | 8 +- addons/mass_mailing/models/mailing_contact.py | 4 +- addons/mass_mailing/models/mailing_list.py | 5 +- addons/mass_mailing/models/mailing_trace.py | 3 +- addons/mrp/models/mrp_bom.py | 5 +- addons/mrp/models/mrp_workcenter.py | 8 +- addons/mrp/models/mrp_workorder.py | 8 +- .../models/mrp_production.py | 7 - .../models/res_company.py | 2 +- addons/payment/models/payment_token.py | 9 +- addons/payment/wizards/payment_link_wizard.py | 5 +- addons/point_of_sale/models/pos_bill.py | 4 +- addons/point_of_sale/models/pos_category.py | 5 +- addons/point_of_sale/models/pos_config.py | 8 +- addons/point_of_sale/models/pos_payment.py | 8 +- .../screens/ticket_screen/ticket_screen.js | 2 +- .../SaleOrderManagementControlPanel.js | 10 +- .../wizard/privacy_lookup_wizard.py | 8 +- .../product/models/product_attribute_value.py | 7 +- addons/product/models/product_category.py | 12 +- addons/product/models/product_pricelist.py | 5 +- addons/product/models/product_product.py | 48 +++---- addons/product/models/product_template.py | 10 +- .../product_template_attribute_value.py | 5 +- addons/product/tests/test_seller.py | 24 ++-- addons/project/models/project_collaborator.py | 7 +- addons/project/models/project_tags.py | 2 +- addons/purchase/models/purchase.py | 6 +- addons/purchase/report/purchase_bill.py | 6 +- addons/rating/models/rating.py | 8 +- .../models/resource_calendar_attendance.py | 2 +- addons/sale/models/sale_order.py | 23 ++-- addons/sale/models/sale_order_line.py | 20 ++- addons/sale/tests/test_sale_order.py | 4 +- addons/sale_expense/models/sale_order.py | 2 +- .../security/sale_expense_security.xml | 2 +- .../sale_timesheet/models/project_update.py | 3 +- addons/sale_timesheet/models/sale_order.py | 13 +- addons/snailmail/models/snailmail_letter.py | 7 +- .../src/data_sources/metadata_repository.js | 4 +- .../plugins/global_filters_ui_plugin.js | 4 +- .../static/src/pivot/pivot_model.js | 2 +- .../global_filters_model_test.js | 10 +- .../static/tests/lists/list_plugin_test.js | 2 +- .../tests/pivots/model/pivot_plugin_test.js | 2 +- addons/stock/models/stock_move.py | 8 +- addons/stock/models/stock_picking.py | 11 +- addons/stock/models/stock_quant.py | 8 +- addons/stock/tests/test_move_lines.py | 4 +- .../stock/wizard/stock_track_confirmation.py | 4 +- addons/survey/models/survey_user_input.py | 4 +- .../tests/test_base_import.py | 2 +- addons/test_xlsx_export/models.py | 5 +- addons/uom/models/uom_uom.py | 2 +- addons/web/static/src/core/orm_service.js | 4 +- .../src/legacy/js/views/basic/basic_model.js | 70 +++++----- .../static/src/search/search_arch_parser.js | 4 +- .../views/fields/properties/property_value.js | 2 +- .../src/views/fields/radio/radio_field.js | 2 +- .../web/static/src/views/relational_model.js | 4 +- addons/web/static/src/views/sample_server.js | 1 + .../static/tests/core/orm_service_tests.js | 6 +- .../web/static/tests/helpers/mock_server.js | 29 +---- .../tests/legacy/helpers/mock_server.js | 30 +---- .../static/tests/legacy/mockserver_tests.js | 104 --------------- .../tests/legacy/views/basic_model_tests.js | 16 +-- addons/web/static/tests/mock_server_tests.js | 99 -------------- .../static/tests/search/search_bar_tests.js | 2 +- .../views/fields/many2one_field_tests.js | 76 +++++------ .../views/fields/one2many_field_tests.js | 12 +- .../views/fields/reference_field_tests.js | 18 +-- .../tests/views/form/form_view_tests.js | 6 +- .../tests/views/kanban/kanban_view_tests.js | 4 +- .../web/static/tests/views/list_view_tests.js | 20 +-- addons/web/static/tests/views/record_tests.js | 2 +- addons/website/models/ir_ui_view.py | 8 +- addons/website/models/res_partner.py | 18 ++- addons/website/models/website_menu.py | 10 +- addons/website/models/website_rewrite.py | 7 +- addons/website/models/website_visitor.py | 9 +- .../models/event_registration_answer.py | 10 +- .../website_event/models/website_visitor.py | 9 +- addons/website_google_map/controllers/main.py | 2 +- .../models/hr_department.py | 9 +- .../models/product_public_category.py | 6 +- addons/website_sale/models/product_ribbon.py | 5 +- odoo/addons/base/models/ir_fields.py | 4 +- odoo/addons/base/models/ir_model.py | 36 ++--- odoo/addons/base/models/ir_qweb_fields.py | 9 +- odoo/addons/base/models/ir_ui_menu.py | 5 +- odoo/addons/base/models/res_bank.py | 12 +- odoo/addons/base/models/res_config.py | 7 +- odoo/addons/base/models/res_country.py | 6 +- odoo/addons/base/models/res_currency.py | 3 - odoo/addons/base/models/res_partner.py | 89 ++++++------- odoo/addons/base/tests/test_deprecation.py | 9 ++ odoo/addons/base/tests/test_expression.py | 54 ++++---- odoo/addons/base/tests/test_res_config.py | 2 +- odoo/addons/base/tests/test_res_partner.py | 25 ++-- odoo/addons/base/tests/test_translate.py | 2 +- odoo/addons/base/views/res_partner_views.xml | 2 +- .../tests/test_check_access.py | 4 - odoo/addons/test_impex/models.py | 26 ++-- odoo/addons/test_impex/tests/test_export.py | 11 +- odoo/addons/test_impex/tests/test_load.py | 31 ++--- .../test_new_api/models/test_unity_read.py | 11 +- .../test_new_api/tests/test_onchange.py | 15 +-- .../test_new_api/tests/test_onchange2.py | 6 +- .../test_new_api/tests/test_properties.py | 18 +-- .../tests/test_performance.py | 2 +- .../tests/test_private_read_group.py | 8 +- odoo/fields.py | 48 +++---- odoo/models.py | 82 +++++------- 176 files changed, 860 insertions(+), 1325 deletions(-) diff --git a/addons/account/models/account_account.py b/addons/account/models/account_account.py index 4638b9da74d..7344eb90193 100644 --- a/addons/account/models/account_account.py +++ b/addons/account/models/account_account.py @@ -597,12 +597,9 @@ class AccountAccount(models.Model): self.name = name self.code = code - def name_get(self): - result = [] + def _compute_display_name(self): for account in self: - name = account.code + ' ' + account.name - result.append((account.id, name)) - return result + account.display_name = f"{account.code} {account.name}" @api.returns('self', lambda value: value.id) def copy(self, default=None): @@ -701,7 +698,8 @@ class AccountAccount(models.Model): """ if 'import_file' in self.env.context: code, name = self._split_code_name(name) - return self.create({'code': code, 'name': name}).name_get()[0] + record = self.create({'code': code, 'name': name}) + return record.id, record.display_name raise ValidationError(_("Please create new accounts from the Chart of Accounts menu.")) @api.model_create_multi @@ -828,15 +826,14 @@ class AccountGroup(models.Model): if not group.code_prefix_start or group.code_prefix_start > group.code_prefix_end: group.code_prefix_start = group.code_prefix_end - def name_get(self): - result = [] + def _compute_display_name(self): for group in self: prefix = group.code_prefix_start and str(group.code_prefix_start) if prefix and group.code_prefix_end != group.code_prefix_start: prefix += '-' + str(group.code_prefix_end) name = (prefix and (prefix + ' ') or '') + group.name - result.append((group.id, name)) - return result + group.display_name = name + @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): diff --git a/addons/account/models/account_account_tag.py b/addons/account/models/account_account_tag.py index 394ab1f255f..d8c5ed6e648 100644 --- a/addons/account/models/account_account_tag.py +++ b/addons/account/models/account_account_tag.py @@ -14,18 +14,15 @@ class AccountAccountTag(models.Model): tax_negate = fields.Boolean(string="Negate Tax Balance", help="Check this box to negate the absolute value of the balance of the lines associated with this tag in tax report computation.") country_id = fields.Many2one(string="Country", comodel_name='res.country', help="Country for which this tag is available, when applied on taxes.") - def name_get(self): + def _compute_display_name(self): if not self.env.company.multi_vat_foreign_country_ids: - return super().name_get() + return super()._compute_display_name() - res = [] for tag in self: name = tag.name if tag.applicability == "taxes" and tag.country_id and tag.country_id != self.env.company.account_fiscal_country_id: name = _("%s (%s)", tag.name, tag.country_id.code) - res.append((tag.id, name,)) - - return res + tag.display_name = name @api.model def _get_tax_tags(self, tag_name, country_id): diff --git a/addons/account/models/account_incoterms.py b/addons/account/models/account_incoterms.py index 905ae7377e7..78b7de7ff36 100644 --- a/addons/account/models/account_incoterms.py +++ b/addons/account/models/account_incoterms.py @@ -18,6 +18,6 @@ class AccountIncoterms(models.Model): 'Active', default=True, help="By unchecking the active field, you may hide an INCOTERM you will not use.") - def name_get(self): - return [(incoterm.id, '%s%s' % (incoterm.code and '[%s] ' % incoterm.code or '', incoterm.name)) - for incoterm in self] + def _compute_display_name(self): + for incoterm in self: + incoterm.display_name = '%s%s' % (incoterm.code and '[%s] ' % incoterm.code or '', incoterm.name) diff --git a/addons/account/models/account_journal.py b/addons/account/models/account_journal.py index 8f5f68c8775..58e3816aaf9 100644 --- a/addons/account/models/account_journal.py +++ b/addons/account/models/account_journal.py @@ -696,14 +696,12 @@ class AccountJournal(models.Model): 'journal_id': self, }).id - def name_get(self): - res = [] + def _compute_display_name(self): for journal in self: name = journal.name if journal.currency_id and journal.currency_id != journal.company_id.currency_id: - name = "%s (%s)" % (name, journal.currency_id.name) - res += [(journal.id, name)] - return res + name = f"{name} ({journal.currency_id.name})" + journal.display_name = name def action_configure_bank_journal(self): """ This function is called by the "configure" button of bank journals, diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index b405c724aba..7d99ed30dc6 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -2387,11 +2387,9 @@ class AccountMove(models.Model): self.line_ids.unlink() return super().unlink() - def name_get(self): - result = [] + def _compute_display_name(self): for move in self: - result.append((move.id, move._get_move_display_name(show_ref=True))) - return result + move.display_name = move._get_move_display_name(show_ref=True) def onchange(self, values, field_name, field_onchange): if field_name in ('line_ids', 'invoice_line_ids'): diff --git a/addons/account/models/account_move_line.py b/addons/account/models/account_move_line.py index 29db1d10ffc..1c4ae08fa4c 100644 --- a/addons/account/models/account_move_line.py +++ b/addons/account/models/account_move_line.py @@ -1525,14 +1525,15 @@ class AccountMoveLine(models.Model): return res - def name_get(self): - return [(line.id, " ".join( - element for element in ( - line.move_id.name, - line.ref and f"({line.ref})", - line.name or line.product_id.display_name, - ) if element - )) for line in self] + def _compute_display_name(self): + for line in self: + line.display_name = " ".join( + element for element in ( + line.move_id.name, + line.ref and f"({line.ref})", + line.name or line.product_id.display_name, + ) if element + ) def copy_data(self, default=None): data_list = super().copy_data(default=default) diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index dbb78fc8967..436cdf26724 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -740,8 +740,9 @@ class AccountPayment(models.Model): return res @api.depends('move_id.name') - def name_get(self): - return [(payment.id, payment.move_id.name != '/' and payment.move_id.name or _('Draft Payment')) for payment in self] + def _compute_display_name(self): + for payment in self: + payment.display_name = payment.move_id.name if payment.move_id.name != '/' else _('Draft Payment') # ------------------------------------------------------------------------- # SYNCHRONIZATION account.payment <-> account.move diff --git a/addons/account/models/account_payment_method.py b/addons/account/models/account_payment_method.py index eb99b76bc61..7ada71ce051 100644 --- a/addons/account/models/account_payment_method.py +++ b/addons/account/models/account_payment_method.py @@ -119,10 +119,11 @@ class AccountPaymentMethodLine(models.Model): company_id = fields.Many2one(related='journal_id.company_id') available_payment_method_ids = fields.Many2many(related='journal_id.available_payment_method_ids') - def name_get(self): - if self.env.context.get('show_payment_journal_id'): - return [(method.id, "%s (%s)" % (method.name, method.journal_id.name)) for method in self] - return super().name_get() + def _compute_display_name(self): + if not self.env.context.get('show_payment_journal_id'): + return super()._compute_display_name() + for method in self: + method.display_name = f"{method.name} ({method.journal_id.name})" @api.depends('payment_method_id.name') def _compute_name(self): diff --git a/addons/account/models/account_report.py b/addons/account/models/account_report.py index 5e920647afb..ab1fcc3f1ad 100644 --- a/addons/account/models/account_report.py +++ b/addons/account/models/account_report.py @@ -203,11 +203,9 @@ class AccountReport(models.Model): return name @api.depends('name', 'country_id') - def name_get(self): - result = [] + def _compute_display_name(self): for report in self: - result.append((report.id, report.name + (f' ({report.country_id.code})' if report.country_id else ''))) - return result + report.display_name = report.name + (f' ({report.country_id.code})' if report.country_id else '') class AccountReportLine(models.Model): @@ -561,8 +559,10 @@ class AccountReportExpression(models.Model): tags_to_archive.active = False tags_to_unlink.unlink() - def name_get(self): - return [(expr.id, f'{expr.report_line_name} [{expr.label}]') for expr in self] + def _compute_display_name(self): + for expr in self: + expr.display_name = f'{expr.report_line_name} [{expr.label}]' + def _expand_aggregations(self): """Return self and its full aggregation expression dependency""" diff --git a/addons/account/models/account_tax.py b/addons/account/models/account_tax.py index 487ff06810a..7baf3ac44fa 100644 --- a/addons/account/models/account_tax.py +++ b/addons/account/models/account_tax.py @@ -362,8 +362,7 @@ class AccountTax(models.Model): default['name'] = _("%s (Copy)") % self.name return super(AccountTax, self).copy(default=default) - def name_get(self): - name_list = [] + def _compute_display_name(self): type_tax_use = dict(self._fields['type_tax_use']._description_selection(self.env)) tax_scope = dict(self._fields['tax_scope']._description_selection(self.env)) for record in self: @@ -372,8 +371,7 @@ class AccountTax(models.Model): name += ' (%s)' % type_tax_use.get(record.type_tax_use) if record.tax_scope: name += ' (%s)' % tax_scope.get(record.tax_scope) - name_list += [(record.id, name)] - return name_list + record.display_name = name @api.onchange('amount') def onchange_amount(self): diff --git a/addons/account/models/res_partner_bank.py b/addons/account/models/res_partner_bank.py index e20b9d1dccf..81f608d9490 100644 --- a/addons/account/models/res_partner_bank.py +++ b/addons/account/models/res_partner_bank.py @@ -307,15 +307,13 @@ class ResPartnerBank(models.Model): account.partner_id._message_log(body=msg) return super().unlink() - def name_get(self): - res = super().name_get() + def _compute_display_name(self): + super()._compute_display_name() if self.env.context.get('display_account_trust'): - res = [] for acc in self: trusted_label = _('trusted') if acc.allow_out_payment else _('untrusted') if acc.bank_id: - name = '{} - {} ({})'.format(acc.acc_number, acc.bank_id.name, trusted_label) + name = f'{acc.acc_number} - {acc.bank_id.name} ({trusted_label})' else: - name = '{} ({})'.format(acc.acc_number, trusted_label) - res.append((acc.id, name)) - return res + name = f'{acc.acc_number} ({trusted_label})' + acc.display_name = name diff --git a/addons/analytic/models/analytic_account.py b/addons/analytic/models/analytic_account.py index b18bb9a1f2a..566b5372fe2 100644 --- a/addons/analytic/models/analytic_account.py +++ b/addons/analytic/models/analytic_account.py @@ -115,16 +115,14 @@ class AccountAnalyticAccount(models.Model): if self._cr.fetchone(): raise UserError(_("You can't set a different company on your analytic account since there are some analytic items linked to it.")) - def name_get(self): - res = [] + def _compute_display_name(self): for analytic in self: name = analytic.name if analytic.code: name = f'[{analytic.code}] {name}' if analytic.partner_id.commercial_partner_id.name: name = f'{name} - {analytic.partner_id.commercial_partner_id.name}' - res.append((analytic.id, name)) - return res + analytic.display_name = name def copy_data(self, default=None): default = dict(default or {}) diff --git a/addons/base_address_extended/models/res_city.py b/addons/base_address_extended/models/res_city.py index b45c4584de4..18bcc6ccbfa 100644 --- a/addons/base_address_extended/models/res_city.py +++ b/addons/base_address_extended/models/res_city.py @@ -15,9 +15,7 @@ class City(models.Model): country_id = fields.Many2one(comodel_name='res.country', string='Country', required=True) state_id = fields.Many2one(comodel_name='res.country.state', string='State', domain="[('country_id', '=', country_id)]") - def name_get(self): - res = [] + def _compute_display_name(self): for city in self: - name = city.name if not city.zipcode else '%s (%s)' % (city.name, city.zipcode) - res.append((city.id, name)) - return res + name = city.name if not city.zipcode else f'{city.name} ({city.zipcode})' + city.display_name = name diff --git a/addons/base_sparse_field/models/fields.py b/addons/base_sparse_field/models/fields.py index 4da062703d7..466982e734d 100644 --- a/addons/base_sparse_field/models/fields.py +++ b/addons/base_sparse_field/models/fields.py @@ -59,7 +59,7 @@ def _compute_sparse(self, records): def _inverse_sparse(self, records): for record in records: values = record[self.sparse] - value = self.convert_to_read(record[self.name], record, use_name_get=False) + value = self.convert_to_read(record[self.name], record, use_display_name=False) if value: if values.get(self.name) != value: values[self.name] = value diff --git a/addons/calendar/models/calendar_event.py b/addons/calendar/models/calendar_event.py index c597f068264..b3c7be02f7e 100644 --- a/addons/calendar/models/calendar_event.py +++ b/addons/calendar/models/calendar_event.py @@ -645,7 +645,7 @@ class Meeting(models.Model): return True - def name_get(self): + def _compute_display_name(self): """ Hide private events' name for events which don't belong to the current user """ hidden = self.filtered( @@ -654,11 +654,8 @@ class Meeting(models.Model): evt.user_id.id != self.env.uid and self.env.user.partner_id not in evt.partner_ids ) - - shown = self - hidden - shown_names = super(Meeting, shown).name_get() - obfuscated_names = [(eid, _('Busy')) for eid in hidden.ids] - return shown_names + obfuscated_names + hidden.display_name = _('Busy') + super(Meeting, self - hidden)._compute_display_name() @api.model def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): diff --git a/addons/crm_iap_mine/models/crm_iap_lead_role.py b/addons/crm_iap_mine/models/crm_iap_lead_role.py index 6ab177b30fc..98e869eb4e0 100644 --- a/addons/crm_iap_mine/models/crm_iap_lead_role.py +++ b/addons/crm_iap_mine/models/crm_iap_lead_role.py @@ -18,8 +18,9 @@ class PeopleRole(models.Model): ] @api.depends('name') - def name_get(self): - return [(role.id, role.name.replace('_', ' ').title()) for role in self] + def _compute_display_name(self): + for role in self: + role.display_name = role.name.replace('_', ' ').title() class PeopleSeniority(models.Model): @@ -35,5 +36,6 @@ class PeopleSeniority(models.Model): ] @api.depends('name') - def name_get(self): - return [(seniority.id, seniority.name.replace('_', ' ').title()) for seniority in self] + def _compute_display_name(self): + for seniority in self: + seniority.display_name = seniority.name.replace('_', ' ').title() diff --git a/addons/crm_iap_mine/models/crm_iap_lead_seniority.py b/addons/crm_iap_mine/models/crm_iap_lead_seniority.py index f110c4f69ca..50211413bf8 100644 --- a/addons/crm_iap_mine/models/crm_iap_lead_seniority.py +++ b/addons/crm_iap_mine/models/crm_iap_lead_seniority.py @@ -17,5 +17,6 @@ class PeopleSeniority(models.Model): ] @api.depends('name') - def name_get(self): - return [(seniority.id, seniority.name.replace('_', ' ').title()) for seniority in self] + def _compute_display_name(self): + for seniority in self: + seniority.display_name = seniority.name.replace('_', ' ').title() diff --git a/addons/event/models/event_event.py b/addons/event/models/event_event.py index 4aacad1a68f..0793dc6ea7d 100644 --- a/addons/event/models/event_event.py +++ b/addons/event/models/event_event.py @@ -589,11 +589,10 @@ class EventEvent(models.Model): self.message_subscribe([vals['organizer_id']]) return res - def name_get(self): + def _compute_display_name(self): """Adds ticket seats availability if requested by context.""" if not self.env.context.get('name_with_seats_availability'): - return super().name_get() - res = [] + return super()._compute_display_name() for event in self: # event or its tickets are sold out if event.event_registrations_sold_out: @@ -606,8 +605,7 @@ class EventEvent(models.Model): ) else: name = event.name - res.append((event.id, name)) - return res + event.display_name = name @api.returns('self', lambda value: value.id) def copy(self, default=None): diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py index 15456493a58..cc3cac72253 100644 --- a/addons/event/models/event_registration.py +++ b/addons/event/models/event_registration.py @@ -162,10 +162,11 @@ class EventRegistration(models.Model): return ret - def name_get(self): - """ Custom name_get in case a registration is nott linked to an attendee + def _compute_display_name(self): + """ Custom display_name in case a registration is nott linked to an attendee """ - return [(registration.id, registration.name or f"#{registration.id}") for registration in self] + for registration in self: + registration.display_name = registration.name or f"#{registration.id}" def toggle_active(self): pre_inactive = self - self.filtered(self._active_name) diff --git a/addons/event/models/event_ticket.py b/addons/event/models/event_ticket.py index 711610301ce..f8f5263fc82 100644 --- a/addons/event/models/event_ticket.py +++ b/addons/event/models/event_ticket.py @@ -163,11 +163,10 @@ class EventTicket(models.Model): raise ValidationError(_('There are not enough seats available for:') + '\n%s\n' % '\n'.join(sold_out_tickets)) - def name_get(self): + def _compute_display_name(self): """Adds ticket seats availability if requested by context.""" if not self.env.context.get('name_with_seats_availability'): - return super().name_get() - res = [] + return super()._compute_display_name() for ticket in self: if not ticket.seats_max: name = ticket.name @@ -179,8 +178,7 @@ class EventTicket(models.Model): ticket_name=ticket.name, count=formatLang(self.env, ticket.seats_available, digits=0), ) - res.append((ticket.id, name)) - return res + ticket.display_name = name def _get_ticket_multiline_description(self): """ Compute a multiline description of this ticket. It is used when ticket diff --git a/addons/fleet/models/fleet_vehicle_assignation_log.py b/addons/fleet/models/fleet_vehicle_assignation_log.py index 29092569802..ff55dde66c0 100644 --- a/addons/fleet/models/fleet_vehicle_assignation_log.py +++ b/addons/fleet/models/fleet_vehicle_assignation_log.py @@ -14,5 +14,6 @@ class FleetVehicleAssignationLog(models.Model): date_start = fields.Date(string="Start Date") date_end = fields.Date(string="End Date") - def name_get(self): - return [(rec.id, f'{rec.vehicle_id.name} - {rec.driver_id.name}') for rec in self] + def _compute_display_name(self): + for rec in self: + rec.display_name = f'{rec.vehicle_id.name} - {rec.driver_id.name}' diff --git a/addons/fleet/models/fleet_vehicle_model.py b/addons/fleet/models/fleet_vehicle_model.py index 2d409f720cb..50294964c1d 100644 --- a/addons/fleet/models/fleet_vehicle_model.py +++ b/addons/fleet/models/fleet_vehicle_model.py @@ -53,14 +53,12 @@ class FleetVehicleModel(models.Model): domain = expression.AND([name_domain, domain]) return self._search(domain, limit=limit, order=order) - def name_get(self): - res = [] + def _compute_display_name(self): for record in self: name = record.name if record.brand_id.name: - name = record.brand_id.name + '/' + name - res.append((record.id, name)) - return res + name = f"{record.brand_id.name}/{name}" + record.display_name = name def _compute_vehicle_count(self): group = self.env['fleet.vehicle']._read_group( diff --git a/addons/gamification/models/gamification_challenge.py b/addons/gamification/models/gamification_challenge.py index f0efb43283d..938cf513c6f 100644 --- a/addons/gamification/models/gamification_challenge.py +++ b/addons/gamification/models/gamification_challenge.py @@ -698,11 +698,10 @@ class Challenge(models.Model): message_body = _("The challenge %s is finished.", challenge.name) if rewarded_users: - user_names = rewarded_users.name_get() message_body += Markup("
") + _( "Reward (badge %(badge_name)s) for every succeeding user was sent to %(users)s.", badge_name=challenge.reward_id.name, - users=", ".join(name for (user_id, name) in user_names) + users=", ".join(rewarded_users.mapped('display_name')) ) else: message_body += Markup("
") + _("Nobody has succeeded to reach every goal, no badge is rewarded for this challenge.") diff --git a/addons/hr/models/hr_department.py b/addons/hr/models/hr_department.py index 70abae88506..b8c61d73f26 100644 --- a/addons/hr/models/hr_department.py +++ b/addons/hr/models/hr_department.py @@ -31,14 +31,16 @@ class Department(models.Model): master_department_id = fields.Many2one( 'hr.department', 'Master Department', compute='_compute_master_department_id', store=True) - def name_get(self): - if not self.env.context.get('hierarchical_naming', True): - return [(record.id, record.name) for record in self] - return super(Department, self).name_get() + def _compute_display_name(self): + if self.env.context.get('hierarchical_naming', True): + return super()._compute_display_name() + for record in self: + record.display_name = record.name @api.model def name_create(self, name): - return self.create({'name': name}).name_get()[0] + record = self.create({'name': name}) + return record.id, record.display_name @api.depends('name', 'parent_id.complete_name') def _compute_complete_name(self): diff --git a/addons/hr/models/hr_employee.py b/addons/hr/models/hr_employee.py index 93eb7b58e63..f4955f84e04 100644 --- a/addons/hr/models/hr_employee.py +++ b/addons/hr/models/hr_employee.py @@ -191,10 +191,11 @@ class HrEmployeePrivate(models.Model): }) } - def name_get(self): + def _compute_display_name(self): if self.check_access_rights('read', raise_exception=False): - return super(HrEmployeePrivate, self).name_get() - return self.env['hr.employee.public'].browse(self.ids).name_get() + return super()._compute_display_name() + for employee_private, employee_public in zip(self, self.env['hr.employee.public'].browse(self.ids)): + employee_private.display_name = employee_public.display_name def search_fetch(self, domain, field_names, offset=0, limit=None, order=None): if self.check_access_rights('read', raise_exception=False): diff --git a/addons/hr/models/res_partner.py b/addons/hr/models/res_partner.py index 6b3ef53787d..5c8722744ff 100644 --- a/addons/hr/models/res_partner.py +++ b/addons/hr/models/res_partner.py @@ -13,17 +13,17 @@ class Partner(models.Model): help="Related employees based on their private address") employees_count = fields.Integer(compute='_compute_employees_count', groups="hr.group_hr_user") - def name_get(self): + def _compute_display_name(self): """ Override to allow an employee to see its private address in his profile. This avoids to relax access rules on `res.parter` and to add an `ir.rule`. (advantage in both security and performance). Use a try/except instead of systematically checking to minimize the impact on performance. """ try: - return super(Partner, self).name_get() + super()._compute_display_name() except AccessError as e: if len(self) == 1 and self in self.env.user.employee_ids.mapped('address_home_id'): - return super(Partner, self.sudo()).name_get() + super(Partner, self.sudo())._compute_display_name() raise e def _compute_employees_count(self): diff --git a/addons/hr_attendance/models/hr_attendance.py b/addons/hr_attendance/models/hr_attendance.py index c0dcfd3df98..80a6640cded 100644 --- a/addons/hr_attendance/models/hr_attendance.py +++ b/addons/hr_attendance/models/hr_attendance.py @@ -31,21 +31,21 @@ class HrAttendance(models.Model): check_out = fields.Datetime(string="Check Out") worked_hours = fields.Float(string='Worked Hours', compute='_compute_worked_hours', store=True, readonly=True) - def name_get(self): - result = [] + def _compute_display_name(self): for attendance in self: if not attendance.check_out: - result.append((attendance.id, _("%(empl_name)s from %(check_in)s") % { - 'empl_name': attendance.employee_id.name, - 'check_in': format_datetime(self.env, attendance.check_in, dt_format=False), - })) + attendance.display_name = _( + "%s from %s", + attendance.employee_id.name, + format_datetime(self.env, attendance.check_in, dt_format=False) + ) else: - result.append((attendance.id, _("%(empl_name)s from %(check_in)s to %(check_out)s") % { - 'empl_name': attendance.employee_id.name, - 'check_in': format_datetime(self.env, attendance.check_in, dt_format=False), - 'check_out': format_datetime(self.env, attendance.check_out, dt_format=False), - })) - return result + attendance.display_name = _( + "%s from %s to %s", + attendance.employee_id.name, + format_datetime(self.env, attendance.check_in, dt_format=False), + format_datetime(self.env, attendance.check_out, dt_format=False), + ) def _get_employee_calendar(self): self.ensure_one() diff --git a/addons/hr_contract/report/hr_contract_history.py b/addons/hr_contract/report/hr_contract_history.py index 26ea29198bf..af3bca153a6 100644 --- a/addons/hr_contract/report/hr_contract_history.py +++ b/addons/hr_contract/report/hr_contract_history.py @@ -17,7 +17,6 @@ class ContractHistory(models.Model): # to the employee. That's why we will use this id (employee_id) as the id of the hr.contract.history. contract_id = fields.Many2one('hr.contract', readonly=True) - display_name = fields.Char(compute='_compute_display_name') name = fields.Char('Contract Name', readonly=True) date_hired = fields.Date('Hire Date', readonly=True) date_start = fields.Date('Start Date', readonly=True) diff --git a/addons/hr_holidays/models/hr_department.py b/addons/hr_holidays/models/hr_department.py index 725641f7802..8893eb4520b 100644 --- a/addons/hr_holidays/models/hr_department.py +++ b/addons/hr_holidays/models/hr_department.py @@ -61,7 +61,7 @@ class Department(models.Model): **self._get_action_context(), 'search_default_active_time_off': 3, 'hide_employee_name': 1, - 'holiday_status_name_get': False + 'holiday_status_display_name': False } return action diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index 9c8a8177930..0c5db995f06 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -82,7 +82,7 @@ class HolidaysRequest(models.Model): defaults = super(HolidaysRequest, self).default_get(fields_list) defaults = self._default_get_request_parameters(defaults) - if self.env.context.get('holiday_status_name_get', True) and 'holiday_status_id' in fields_list and not defaults.get('holiday_status_id'): + if self.env.context.get('holiday_status_display_name', True) and 'holiday_status_id' in fields_list and not defaults.get('holiday_status_id'): lt = self.env['hr.leave.type'].search(['|', ('requires_allocation', '=', 'no'), ('has_valid_allocation', '=', True)], limit=1, order='sequence') if lt: @@ -774,19 +774,6 @@ class HolidaysRequest(models.Model): @api.depends('employee_id', 'holiday_status_id') def _compute_display_name(self): - super()._compute_display_name() - - def onchange(self, values, field_name, field_onchange): - # Try to force the leave_type name_get when creating new records - # This is called right after pressing create and returns the name_get for - # most fields in the view. - if field_onchange.get('employee_id') and 'employee_id' not in self._context and values: - employee_id = get_employee_from_context(values, self._context, self.env.user.employee_id.id) - self = self.with_context(employee_id=employee_id) - return super().onchange(values, field_name, field_onchange) - - def name_get(self): - res = [] for leave in self: user_tz = timezone(leave.tz) date_from_utc = leave.date_from and leave.date_from.astimezone(user_tz).date() @@ -795,13 +782,9 @@ class HolidaysRequest(models.Model): if self.env.context.get('short_name'): short_leave_name = leave.name or time_off_type_display or _('Time Off') if leave.leave_type_request_unit == 'hour': - res.append(( - leave.id, - _("%s: %.2f hours") % (short_leave_name, leave.number_of_hours_display))) + leave.display_name = _("%s: %.2f hours", short_leave_name, leave.number_of_hours_display) else: - res.append(( - leave.id, - _("%s: %.2f days") % (short_leave_name, leave.number_of_days))) + leave.display_name = _("%s: %.2f days", short_leave_name, leave.number_of_days) else: if leave.holiday_type == 'company': target = leave.mode_company_id.name @@ -816,65 +799,55 @@ class HolidaysRequest(models.Model): display_date = format_date(self.env, date_from_utc) or "" if leave.leave_type_request_unit == 'hour': if self.env.context.get('hide_employee_name') and 'employee_id' in self.env.context.get('group_by', []): - res.append(( - leave.id, - _("%(leave_type)s: %(duration).2f hours on %(date)s", - leave_type=time_off_type_display, - duration=leave.number_of_hours_display, - date=fields.Date.to_string(date_from_utc) or "", - ) - )) + leave.display_name = _("%(leave_type)s: %(duration).2f hours on %(date)s", + leave_type=time_off_type_display, + duration=leave.number_of_hours_display, + date=fields.Date.to_string(date_from_utc) or "", + ) elif not time_off_type_display: - res.append(( - leave.id, - _("%(person)s: %(duration).2f hours on %(date)s", - person=target, - duration=leave.number_of_hours_display, - date=display_date, - ) - )) + leave.display_name = _("%(person)s: %(duration).2f hours on %(date)s", + person=target, + duration=leave.number_of_hours_display, + date=display_date, + ) else: - res.append(( - leave.id, - _("%(person)s on %(leave_type)s: %(duration).2f hours on %(date)s", - person=target, - leave_type=time_off_type_display, - duration=leave.number_of_hours_display, - date=display_date, - ) - )) + leave.display_name = _("%(person)s on %(leave_type)s: %(duration).2f hours on %(date)s", + person=target, + leave_type=time_off_type_display, + duration=leave.number_of_hours_display, + date=display_date, + ) else: if leave.number_of_days > 1 and date_from_utc and date_to_utc: display_date += ' / %s' % format_date(self.env, date_to_utc) or "" if not target or self.env.context.get('hide_employee_name') and 'employee_id' in self.env.context.get('group_by', []): - res.append(( - leave.id, - _("%(leave_type)s: %(duration).2f days (%(start)s)", - leave_type=time_off_type_display, - duration=leave.number_of_days, - start=display_date, - ) - )) + leave.display_name = _("%(leave_type)s: %(duration).2f days (%(start)s)", + leave_type=time_off_type_display, + duration=leave.number_of_days, + start=display_date, + ) elif not time_off_type_display: - res.append(( - leave.id, - _("%(person)s: %(duration).2f days (%(start)s)", - person=target, - duration=leave.number_of_days, - start=display_date, - ) - )) + leave.display_name = _("%(person)s: %(duration).2f days (%(start)s)", + person=target, + duration=leave.number_of_days, + start=display_date, + ) else: - res.append(( - leave.id, - _("%(person)s on %(leave_type)s: %(duration).2f days (%(start)s)", - person=target, - leave_type=time_off_type_display, - duration=leave.number_of_days, - start=display_date, - ) - )) - return res + leave.display_name = _("%(person)s on %(leave_type)s: %(duration).2f days (%(start)s)", + person=target, + leave_type=time_off_type_display, + duration=leave.number_of_days, + start=display_date, + ) + + def onchange(self, values, field_name, field_onchange): + # Try to force the leave_type display_name when creating new records + # This is called right after pressing create and returns the display_name for + # most fields in the view. + if field_onchange.get('employee_id') and 'employee_id' not in self._context and values: + employee_id = get_employee_from_context(values, self._context, self.env.user.employee_id.id) + self = self.with_context(employee_id=employee_id) + return super().onchange(values, field_name, field_onchange) def add_follower(self, employee_id): employee = self.env['hr.employee'].browse(employee_id) @@ -1373,18 +1346,16 @@ class HolidaysRequest(models.Model): def _notify_manager(self): leaves = self.filtered(lambda hol: (hol.validation_type == 'both' and hol.state in ['validate1', 'validate']) or (hol.validation_type == 'manager' and hol.state == 'validate')) - holiday_names = self.name_get() for holiday in leaves: responsible = holiday.employee_id.leave_manager_id.partner_id.ids if responsible: - holiday_name = list(filter(lambda h: holiday.id in h, holiday_names))[0][1] self.env['mail.thread'].sudo().message_notify( partner_ids=responsible, model_description='Time Off', subject=_('Refused Time Off'), body=_( '%(holiday_name)s has been refused.', - holiday_name=holiday_name, + holiday_name=holiday.display_name, ), email_layout_xmlid='mail.mail_notification_light', ) @@ -1397,7 +1368,6 @@ class HolidaysRequest(models.Model): self._force_cancel(reason, 'mail.mt_note') def _force_cancel(self, reason, msg_subtype='mail.mt_comment'): - leave_names = self.name_get() recs = self.browse() if self.env.context.get(MODULE_UNINSTALL_FLAG) else self for leave in recs: leave.message_post( @@ -1418,14 +1388,13 @@ class HolidaysRequest(models.Model): responsibles |= leave.holiday_status_id.responsible_ids.partner_id if responsibles: - leave_name = list(filter(lambda h: leave.id in h, leave_names))[0][1] self.env['mail.thread'].sudo().message_notify( partner_ids=responsibles.ids, model_description='Time Off', subject=_('Canceled Time Off'), body=_( "%(leave_name)s has been cancelled with the justification:
%(reason)s.", - leave_name=leave_name, + leave_name=leave.display_name, reason=reason ), email_layout_xmlid='mail.mail_notification_light', diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index 693582195c4..75d7aa6f4cc 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -520,16 +520,15 @@ class HolidaysAllocation(models.Model): #################################################### def onchange(self, values, field_name, field_onchange): - # Try to force the leave_type name_get when creating new records - # This is called right after pressing create and returns the name_get for + # Try to force the leave_type display_name when creating new records + # This is called right after pressing create and returns the display_name for # most fields in the view. if field_onchange.get('employee_id') and 'employee_id' not in self._context and values: employee_id = get_employee_from_context(values, self._context, self.env.user.employee_id.id) self = self.with_context(employee_id=employee_id) return super().onchange(values, field_name, field_onchange) - def name_get(self): - res = [] + def _compute_display_name(self): for allocation in self: if allocation.holiday_type == 'company': target = allocation.mode_company_id.name @@ -542,16 +541,12 @@ class HolidaysAllocation(models.Model): else: target = ', '.join(allocation.employee_ids.sudo().mapped('name')) - res.append( - (allocation.id, - _("Allocation of %(allocation_name)s: %(duration).2f %(duration_type)s to %(person)s", - allocation_name=allocation.holiday_status_id.sudo().name, - duration=allocation.number_of_hours_display if allocation.type_request_unit == 'hour' else allocation.number_of_days, - duration_type=_('hours') if allocation.type_request_unit == 'hour' else _('days'), - person=target - )) + allocation.display_name = _("Allocation of %s: %.2f %s to %s", + allocation.holiday_status_id.sudo().name, + allocation.number_of_hours_display if allocation.type_request_unit == 'hour' else allocation.number_of_days, + _('hours') if allocation.type_request_unit == 'hour' else _('days'), + target, ) - return res def add_follower(self, employee_id): employee = self.env['hr.employee'].browse(employee_id) diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index 7148eb5a3f2..fda87bdc930 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -537,26 +537,26 @@ class HolidaysType(models.Model): if leave_type.employee_requests == 'no': leave_type.allocation_validation_type = 'officer' - def requested_name_get(self): - return self._context.get('holiday_status_name_get', True) and self._context.get('employee_id') + def requested_display_name(self): + return self._context.get('holiday_status_display_name', True) and self._context.get('employee_id') - def name_get(self): - if not self.requested_name_get(): + @api.depends('requires_allocation', 'virtual_remaining_leaves', 'max_leaves', 'request_unit') + @api.depends_context('holiday_status_display_name', 'employee_id', 'from_manager_leave_form') + def _compute_display_name(self): + if not self.requested_display_name(): # leave counts is based on employee_id, would be inaccurate if not based on correct employee - return super(HolidaysType, self).name_get() - res = [] + return super()._compute_display_name() for record in self: name = record.name if record.requires_allocation == "yes" and not self._context.get('from_manager_leave_form'): - name = "%(name)s (%(count)s)" % { - 'name': name, - 'count': _('%g remaining out of %g') % ( + name = "{name} ({count})".format( + name=name, + count=_('%g remaining out of %g') % ( float_round(record.virtual_remaining_leaves, precision_digits=2) or 0.0, float_round(record.max_leaves, precision_digits=2) or 0.0, - ) + (_(' hours') if record.request_unit == 'hour' else _(' days')) - } - res.append((record.id, name)) - return res + ) + (_(' hours') if record.request_unit == 'hour' else _(' days')), + ) + record.display_name = name @api.model def _search(self, domain, offset=0, limit=None, order=None, access_rights_uid=None): diff --git a/addons/hr_holidays/views/hr_leave_views.xml b/addons/hr_holidays/views/hr_leave_views.xml index a8a7799bca3..5686201fb59 100644 --- a/addons/hr_holidays/views/hr_leave_views.xml +++ b/addons/hr_holidays/views/hr_leave_views.xml @@ -679,7 +679,7 @@ 'search_default_active_employee': 3, 'search_default_active_time_off': 4, 'hide_employee_name': 1, - 'holiday_status_name_get': False} + 'holiday_status_display_name': False} ['|', ('employee_id.company_id', 'in', allowed_company_ids), '&', ('multi_employee', '=', True), @@ -701,7 +701,7 @@ { 'hide_employee_name': 1, - 'holiday_status_name_get': False} + 'holiday_status_display_name': False} [('holiday_allocation_id', '=', active_id)] diff --git a/addons/hr_holidays_attendance/models/hr_leave_type.py b/addons/hr_holidays_attendance/models/hr_leave_type.py index 1995dc4d2d3..3119d0682e0 100644 --- a/addons/hr_holidays_attendance/models/hr_leave_type.py +++ b/addons/hr_holidays_attendance/models/hr_leave_type.py @@ -13,27 +13,24 @@ class HRLeaveType(models.Model): "Deduct Extra Hours", default=False, help="Once a time off of this type is approved, extra hours in attendances will be deducted.") - def name_get(self): + @api.depends_context('request_type', 'leave', 'holiday_status_display_name', 'employee_id') + def _compute_display_name(self): # Exclude hours available in allocation contexts, it might be confusing otherwise - if not self.requested_name_get() or self._context.get('request_type', 'leave') == 'allocation': - return super().name_get() - employee_id = False - overtime_leaves = self.env['hr.leave.type'] - res = [] - for leave_type in self: - if leave_type.overtime_deductible and leave_type.requires_allocation == 'no': - if not employee_id: - employee_id = self.env['hr.employee'].browse(self._context.get('employee_id')).sudo() - if employee_id.total_overtime > 0: - overtime_leaves |= leave_type - name = "%(name)s (%(count)s)" % { - 'name': leave_type.name, - 'count': _('%s hours available', - format_duration(employee_id.total_overtime)), - } - res.append((leave_type.id, name)) - res += super(HRLeaveType, self - overtime_leaves).name_get() - return res + if not self.requested_display_name() or self._context.get('request_type', 'leave') == 'allocation': + return super()._compute_display_name() + + employee = self.env['hr.employee'].browse(self._context.get('employee_id')).sudo() + if employee.total_overtime <= 0: + return super()._compute_display_name() + + overtime_leaves = self.filtered(lambda l_type: l_type.overtime_deductible and l_type.requires_allocation == 'no') + for leave_type in overtime_leaves: + leave_type.display_name = "%(name)s (%(count)s)" % { + 'name': leave_type.name, + 'count': _('%s hours available', + format_duration(employee.total_overtime)), + } + super(HRLeaveType, self - overtime_leaves)._compute_display_name() def get_employees_days(self, employee_ids, date=None): res = super().get_employees_days(employee_ids, date) diff --git a/addons/hr_recruitment/models/hr_applicant.py b/addons/hr_recruitment/models/hr_applicant.py index 4ea632695be..c0251ec7258 100644 --- a/addons/hr_recruitment/models/hr_applicant.py +++ b/addons/hr_recruitment/models/hr_applicant.py @@ -505,13 +505,11 @@ class Applicant(models.Model): applicant._message_add_suggested_recipient(recipients, email=email_from, reason=_('Contact Email')) return recipients - def name_get(self): - if self.env.context.get('show_partner_name'): - return [ - (applicant.id, applicant.partner_name or applicant.name) - for applicant in self - ] - return super().name_get() + def _compute_display_name(self): + if not self.env.context.get('show_partner_name'): + return super()._compute_display_name() + for applicant in self: + applicant.display_name = applicant.partner_name or applicant.name @api.model def message_new(self, msg, custom_values=None): diff --git a/addons/hr_skills/models/hr_skill.py b/addons/hr_skills/models/hr_skill.py index 1244cc69065..2a37e66b47d 100644 --- a/addons/hr_skills/models/hr_skill.py +++ b/addons/hr_skills/models/hr_skill.py @@ -13,7 +13,8 @@ class Skill(models.Model): sequence = fields.Integer(default=10) skill_type_id = fields.Many2one('hr.skill.type', required=True, ondelete='cascade') - def name_get(self): + def _compute_display_name(self): if not self._context.get('from_skill_dropdown'): - return super().name_get() - return [(record.id, f"{record.name} ({record.skill_type_id.name})") for record in self] + return super()._compute_display_name() + for record in self: + record.display_name = f"{record.name} ({record.skill_type_id.name})" diff --git a/addons/hr_skills/models/hr_skill_level.py b/addons/hr_skills/models/hr_skill_level.py index c879c501a3f..4edd74c6d47 100644 --- a/addons/hr_skills/models/hr_skill_level.py +++ b/addons/hr_skills/models/hr_skill_level.py @@ -19,10 +19,11 @@ class SkillLevel(models.Model): ('check_level_progress', 'CHECK(level_progress BETWEEN 0 AND 100)', "Progress should be a number between 0 and 100."), ] - def name_get(self): + def _compute_display_name(self): if not self._context.get('from_skill_level_dropdown'): - return super().name_get() - return [(record.id, f"{record.name} ({record.level_progress}%)") for record in self] + return super()._compute_display_name() + for record in self: + record.display_name = f"{record.name} ({record.level_progress}%)" @api.model_create_multi def create(self, vals_list): diff --git a/addons/hr_timesheet/models/hr_employee.py b/addons/hr_timesheet/models/hr_employee.py index 5cb1b687af4..38e721eb99d 100644 --- a/addons/hr_timesheet/models/hr_employee.py +++ b/addons/hr_timesheet/models/hr_employee.py @@ -23,13 +23,12 @@ class HrEmployee(models.Model): for employee in self: employee.has_timesheet = result.get(employee.id, False) - def name_get(self): - res = super().name_get() + def _compute_display_name(self): + super()._compute_display_name() allowed_company_ids = self.env.context.get('allowed_company_ids', []) if len(allowed_company_ids) <= 1: - return res + return - name_mapping = dict(res) employees_count_per_user = { user.id: count for user, count in self.env['hr.employee'].sudo()._read_group( @@ -40,8 +39,7 @@ class HrEmployee(models.Model): } for employee in self: if employees_count_per_user.get(employee.user_id.id, 0) > 1: - name_mapping[employee.id] = f'{name_mapping[employee.id]} - {employee.company_id.name}' - return list(name_mapping.items()) + employee.display_name = f'{employee.display_name} - {employee.company_id.name}' def action_unlink_wizard(self): wizard = self.env['hr.employee.delete.wizard'].create({ diff --git a/addons/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py index e3d7df5cdf6..1aaa8baabbc 100644 --- a/addons/hr_timesheet/models/hr_timesheet.py +++ b/addons/hr_timesheet/models/hr_timesheet.py @@ -65,23 +65,14 @@ class AccountAnalyticLine(models.Model): partner_id = fields.Many2one(compute='_compute_partner_id', store=True, readonly=False) readonly_timesheet = fields.Boolean(string="Readonly Timesheet", compute="_compute_readonly_timesheet") - def name_get(self): - result = super().name_get() - timesheets_read = self.env[self._name].search_read([('project_id', '!=', False), ('id', 'in', self.ids)], ['id', 'project_id', 'task_id']) - if not timesheets_read: - return result - def _get_display_name(project_id, task_id): - """ Get the display name of the timesheet based on the project and task - :param project_id: tuple containing the id and the display name of the project - :param task_id: tuple containing the id and the display name of the task if a task exists in the timesheet - otherwise False. - :returns: the display name of the timesheet - """ - if task_id: - return '%s - %s' % (project_id[1], task_id[1]) - return project_id[1] - timesheet_dict = {res['id']: _get_display_name(res['project_id'], res['task_id']) for res in timesheets_read} - return list({**dict(result), **timesheet_dict}.items()) + def _compute_display_name(self): + analytic_line_with_project = self.filtered('project_id') + super(AccountAnalyticLine, self - analytic_line_with_project)._compute_display_name() + for analytic_line in analytic_line_with_project: + if analytic_line.task_id: + analytic_line.display_name = f"{analytic_line.project_id.display_name} - {analytic_line.task_id.display_name}" + else: + analytic_line.display_name = analytic_line.project_id.display_name def _is_readonly(self): self.ensure_one() diff --git a/addons/hr_timesheet/models/project_project.py b/addons/hr_timesheet/models/project_project.py index 6cd07e1c059..f6a7d3d9692 100644 --- a/addons/hr_timesheet/models/project_project.py +++ b/addons/hr_timesheet/models/project_project.py @@ -164,15 +164,14 @@ class Project(models.Model): project._create_analytic_account() return super(Project, self).write(values) - def name_get(self): - res = super().name_get() + def _compute_display_name(self): + super()._compute_display_name() if len(self.env.context.get('allowed_company_ids', [])) <= 1: - return res - name_mapping = dict(res) + return + for project in self: if project.is_internal_project: - name_mapping[project.id] = f'{name_mapping[project.id]} - {project.company_id.name}' - return list(name_mapping.items()) + project.display_name = f'{project.display_name} - {project.company_id.name}' @api.model def _init_data_analytic_account(self): diff --git a/addons/hr_timesheet/models/project_task.py b/addons/hr_timesheet/models/project_task.py index 3cbb10f1a40..6c66e8db0e9 100644 --- a/addons/hr_timesheet/models/project_task.py +++ b/addons/hr_timesheet/models/project_task.py @@ -181,13 +181,13 @@ class Task(models.Model): return res - def name_get(self): + def _compute_display_name(self): + super()._compute_display_name() if self.env.context.get('hr_timesheet_display_remaining_hours'): - name_mapping = dict(super().name_get()) for task in self: if task.allow_timesheets and task.planned_hours > 0 and task.encode_uom_in_days: days_left = _("(%s days remaining)") % task._convert_hours_to_days(task.remaining_hours) - name_mapping[task.id] = name_mapping.get(task.id, '') + u"\u00A0" + days_left + task.display_name = task.display_name + "\u00A0" + days_left elif task.allow_timesheets and task.planned_hours > 0: hours, mins = (str(int(duration)).rjust(2, '0') for duration in divmod(abs(task.remaining_hours) * 60, 60)) hours_left = _( @@ -196,9 +196,7 @@ class Task(models.Model): hours=hours, minutes=mins, ) - name_mapping[task.id] = name_mapping.get(task.id, '') + u"\u00A0" + hours_left - return list(name_mapping.items()) - return super().name_get() + task.display_name = task.display_name + "\u00A0" + hours_left @api.model def _get_view_cache_key(self, view_id=None, view_type='form', **options): diff --git a/addons/http_routing/models/ir_http.py b/addons/http_routing/models/ir_http.py index 3be942d5d18..4b8f9a8c62a 100644 --- a/addons/http_routing/models/ir_http.py +++ b/addons/http_routing/models/ir_http.py @@ -88,7 +88,6 @@ def slug(value): try: if not value.id: raise ValueError("Cannot slug non-existent record %s" % value) - # [(id, name)] = value.name_get() identifier, name = value.id, getattr(value, 'seo_name', False) or value.display_name except AttributeError: # assume name_search result tuple diff --git a/addons/im_livechat/models/chatbot_script_answer.py b/addons/im_livechat/models/chatbot_script_answer.py index 7f12d4f04a2..34a9f70ec86 100644 --- a/addons/im_livechat/models/chatbot_script_answer.py +++ b/addons/im_livechat/models/chatbot_script_answer.py @@ -21,21 +21,15 @@ class ChatbotScriptAnswer(models.Model): 'chatbot.script.step', string='Script Step', required=True, ondelete='cascade') chatbot_script_id = fields.Many2one(related='script_step_id.chatbot_script_id') - def name_get(self): + def _compute_display_name(self): if self._context.get('chatbot_script_answer_display_short_name'): - return super().name_get() + return super()._compute_display_name() - result = [] for answer in self: answer_message = answer.script_step_id.message.replace('\n', ' ') shortened_message = textwrap.shorten(answer_message, width=26, placeholder=" [...]") - result.append(( - answer.id, - "%s: %s" % (shortened_message, answer.name) - )) - - return result + answer.display_name = f"{shortened_message}: {answer.name}" @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): diff --git a/addons/l10n_in_edi_ewaybill/models/ewaybill_type.py b/addons/l10n_in_edi_ewaybill/models/ewaybill_type.py index bf9e6920c2a..a656d33a148 100755 --- a/addons/l10n_in_edi_ewaybill/models/ewaybill_type.py +++ b/addons/l10n_in_edi_ewaybill/models/ewaybill_type.py @@ -22,11 +22,7 @@ class EWayBillType(models.Model): ) active = fields.Boolean("Active", default=True) - def name_get(self): + def _compute_display_name(self): """Show name and sub_type in name""" - result_dict = dict() for ewaybill_type in self: - name = ewaybill_type.name - name += _(" (Sub-Type: %s)", ewaybill_type.sub_type) - result_dict[ewaybill_type.id] = name - return list(result_dict.items()) + ewaybill_type.display_name = _("%s (Sub-Type: %s)", ewaybill_type.name, ewaybill_type.sub_type) diff --git a/addons/l10n_it_edi/models/ddt.py b/addons/l10n_it_edi/models/ddt.py index 1047e82303a..58ac4d2ee35 100644 --- a/addons/l10n_it_edi/models/ddt.py +++ b/addons/l10n_it_edi/models/ddt.py @@ -11,8 +11,6 @@ class L10nItDdt(models.Model): name = fields.Char(string="Numero DDT", size=20, help="Transport document number", required=True) date = fields.Date(string="Data DDT", help="Transport document date", required=True) - def name_get(self): - res = [] + def _compute_display_name(self): for ddt in self: - res.append((ddt.id, ("%s (%s)") % (ddt.name, ddt.date))) - return res + ddt.display_name = f"{ddt.name} ({ddt.date})" diff --git a/addons/l10n_latam_base/models/l10n_latam_identification_type.py b/addons/l10n_latam_base/models/l10n_latam_identification_type.py index c893e61eacb..c6f873168c3 100644 --- a/addons/l10n_latam_base/models/l10n_latam_identification_type.py +++ b/addons/l10n_latam_base/models/l10n_latam_identification_type.py @@ -16,7 +16,7 @@ class L10nLatamIdentificationType(models.Model): is_vat = fields.Boolean() country_id = fields.Many2one('res.country') - def name_get(self): + def _compute_display_name(self): multi_localization = len(self.search([]).mapped('country_id')) > 1 - return [(rec.id, '%s%s' % ( - rec.name, multi_localization and rec.country_id and ' (%s)' % rec.country_id.code or '')) for rec in self] + for rec in self: + rec.display_name = '{}{}'.format(rec.name, multi_localization and rec.country_id and ' (%s)' % rec.country_id.code or '') diff --git a/addons/l10n_latam_check/models/account_payment.py b/addons/l10n_latam_check/models/account_payment.py index e5d76a5377c..5db65694895 100644 --- a/addons/l10n_latam_check/models/account_payment.py +++ b/addons/l10n_latam_check/models/account_payment.py @@ -283,13 +283,12 @@ class AccountPayment(models.Model): }) return res - def name_get(self): + def _compute_display_name(self): """ Add check number to display_name on check_id m2o field """ - res_names = super().name_get() - for i, (res_name, rec) in enumerate(zip(res_names, self)): + super()._compute_display_name() + for rec in self: if rec.check_number and rec.payment_method_line_id.code == 'new_third_party_checks': - res_names[i] = (res_name[0], "%s %s" % (res_name[1], _("(Check %s)", rec.check_number))) - return res_names + rec.display_name = "{} {}".format(rec.display_name, _("(Check %s)", rec.check_number)) def button_open_check_operations(self): ''' Redirect the user to the invoice(s) paid by this payment. diff --git a/addons/l10n_latam_invoice_document/models/l10n_latam_document_type.py b/addons/l10n_latam_invoice_document/models/l10n_latam_document_type.py index d4aaf288afd..d208db5a6af 100644 --- a/addons/l10n_latam_invoice_document/models/l10n_latam_document_type.py +++ b/addons/l10n_latam_invoice_document/models/l10n_latam_document_type.py @@ -35,11 +35,9 @@ class L10nLatamDocumentType(models.Model): self.ensure_one() return document_number - def name_get(self): - result = [] + def _compute_display_name(self): for rec in self: name = rec.name if rec.code: - name = '(%s) %s' % (rec.code, name) - result.append((rec.id, name)) - return result + name = f'({rec.code}) {name}' + rec.display_name = name diff --git a/addons/loyalty/models/loyalty_card.py b/addons/loyalty/models/loyalty_card.py index 893980bfc4e..9fe01f30bc4 100644 --- a/addons/loyalty/models/loyalty_card.py +++ b/addons/loyalty/models/loyalty_card.py @@ -19,8 +19,9 @@ class LoyaltyCard(models.Model): """ return '044' + str(uuid4())[7:-18] - def name_get(self): - return [(card.id, f'{card.program_id.name}: {card.code}') for card in self] + def _compute_display_name(self): + for card in self: + card.display_name = f'{card.program_id.name}: {card.code}' program_id = fields.Many2one('loyalty.program', ondelete='restrict', default=lambda self: self.env.context.get('active_id', None)) program_type = fields.Selection(related='program_id.program_type') diff --git a/addons/loyalty/models/loyalty_reward.py b/addons/loyalty/models/loyalty_reward.py index b19a4f62c14..b77c502be31 100644 --- a/addons/loyalty/models/loyalty_reward.py +++ b/addons/loyalty/models/loyalty_reward.py @@ -38,8 +38,9 @@ class LoyaltyReward(models.Model): ('per_order', _('%s per order', symbol)) ] - def name_get(self): - return [(reward.id, '%s - %s' % (reward.program_id.name, reward.description)) for reward in self] + def _compute_display_name(self): + for reward in self: + reward.display_name = f'{reward.program_id.name} - {reward.description}' active = fields.Boolean(default=True) program_id = fields.Many2one('loyalty.program', required=True, ondelete='cascade') diff --git a/addons/lunch/models/lunch_cashmove.py b/addons/lunch/models/lunch_cashmove.py index a0125ac4073..83761121f9e 100644 --- a/addons/lunch/models/lunch_cashmove.py +++ b/addons/lunch/models/lunch_cashmove.py @@ -18,8 +18,9 @@ class LunchCashMove(models.Model): amount = fields.Float('Amount', required=True) description = fields.Text('Description') - def name_get(self): - return [(cashmove.id, '%s %s' % (_('Lunch Cashmove'), '#%d' % cashmove.id)) for cashmove in self] + def _compute_display_name(self): + for cashmove in self: + cashmove.display_name = '{} {}'.format(_('Lunch Cashmove'), '#%d' % cashmove.id) @api.model def get_wallet_balance(self, user, include_config=True): diff --git a/addons/lunch/models/lunch_supplier.py b/addons/lunch/models/lunch_supplier.py index a0330eba77a..589c5d6a568 100644 --- a/addons/lunch/models/lunch_supplier.py +++ b/addons/lunch/models/lunch_supplier.py @@ -117,14 +117,12 @@ class LunchSupplier(models.Model): 'Automatic Email Sending Time should be between 0 and 12'), ] - def name_get(self): - res = [] + def _compute_display_name(self): for supplier in self: if supplier.phone: - res.append((supplier.id, '%s %s' % (supplier.name, supplier.phone))) + supplier.display_name = f'{supplier.name} {supplier.phone}' else: - res.append((supplier.id, supplier.name)) - return res + supplier.display_name = supplier.name def _sync_cron(self): for supplier in self: diff --git a/addons/lunch/models/lunch_topping.py b/addons/lunch/models/lunch_topping.py index c7d6741672d..518f46661c0 100644 --- a/addons/lunch/models/lunch_topping.py +++ b/addons/lunch/models/lunch_topping.py @@ -17,10 +17,8 @@ class LunchTopping(models.Model): supplier_id = fields.Many2one('lunch.supplier', ondelete='cascade') topping_category = fields.Integer('Topping Category', required=True, default=1) - def name_get(self): + def _compute_display_name(self): currency_id = self.env.company.currency_id - res = dict(super(LunchTopping, self).name_get()) for topping in self: price = formatLang(self.env, topping.price, currency_obj=currency_id) - res[topping.id] = '%s %s' % (topping.name, price) - return list(res.items()) + topping.display_name = f'{topping.name} {price}' diff --git a/addons/lunch/report/lunch_cashmove_report.py b/addons/lunch/report/lunch_cashmove_report.py index a45d66da916..1f7d7728c1d 100644 --- a/addons/lunch/report/lunch_cashmove_report.py +++ b/addons/lunch/report/lunch_cashmove_report.py @@ -17,8 +17,9 @@ class CashmoveReport(models.Model): user_id = fields.Many2one('res.users', string='User') description = fields.Text('Description') - def name_get(self): - return [(cashmove.id, '%s %s' % (_('Lunch Cashmove'), '#%d' % cashmove.id)) for cashmove in self] + def _compute_display_name(self): + for cashmove in self: + cashmove.display_name = '{} {}'.format(_('Lunch Cashmove'), '#%d' % cashmove.id) def init(self): tools.drop_view_if_exists(self._cr, self._table) diff --git a/addons/mail/models/discuss/discuss_channel_member.py b/addons/mail/models/discuss/discuss_channel_member.py index 93312e7f459..8d5714963da 100644 --- a/addons/mail/models/discuss/discuss_channel_member.py +++ b/addons/mail/models/discuss/discuss_channel_member.py @@ -59,8 +59,9 @@ class ChannelMember(models.Model): else: self.message_unread_counter = 0 - def name_get(self): - return [(record.id, record.partner_id.name or record.guest_id.name) for record in self] + def _compute_display_name(self): + for record in self: + record.display_name = record.partner_id.name or record.guest_id.name def init(self): self.env.cr.execute("CREATE UNIQUE INDEX IF NOT EXISTS discuss_channel_member_partner_unique ON %s (channel_id, partner_id) WHERE partner_id IS NOT NULL" % self._table) diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py index c82b0bec848..dcd4352a09f 100644 --- a/addons/mail/models/mail_activity.py +++ b/addons/mail/models/mail_activity.py @@ -404,12 +404,10 @@ class MailActivity(models.Model): ) return activities._as_query(order) - def name_get(self): - res = [] + def _compute_display_name(self): for record in self: name = record.summary or record.activity_type_id.display_name - res.append((record.id, name)) - return res + record.display_name = name # ------------------------------------------------------ # Business Methods diff --git a/addons/mail/models/mail_alias.py b/addons/mail/models/mail_alias.py index 1152f85a70d..e81d64a31db 100644 --- a/addons/mail/models/mail_alias.py +++ b/addons/mail/models/mail_alias.py @@ -147,20 +147,18 @@ class Alias(models.Model): vals['alias_name'] = self._clean_and_check_unique([vals.get('alias_name')])[0] return super(Alias, self).write(vals) - def name_get(self): + def _compute_display_name(self): """Return the mail alias display alias_name, including the implicit mail catchall domain if exists from config otherwise "New Alias". e.g. `jobs@mail.odoo.com` or `jobs` or 'New Alias' """ - res = [] for record in self: if record.alias_name and record.alias_domain: - res.append((record['id'], "%s@%s" % (record.alias_name, record.alias_domain))) + record.display_name = f"{record.alias_name}@{record.alias_domain}" elif record.alias_name: - res.append((record['id'], "%s" % (record.alias_name))) + record.display_name = record.alias_name else: - res.append((record['id'], _("Inactive Alias"))) - return res + record.display_name = _("Inactive Alias") def _clean_and_check_unique(self, names): """When an alias name appears to already be an email, we keep the local diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 584c432a6cb..ba37a2c7186 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -103,7 +103,7 @@ class Message(models.Model): # related document model = fields.Char('Related Document Model') res_id = fields.Many2oneReference('Related Document ID', model_field='model') - record_name = fields.Char('Message Record Name') # name_get() of the related document + record_name = fields.Char('Message Record Name') # display_name of the related document link_preview_ids = fields.One2many('mail.link.preview', 'message_id', string='Link Previews', groups="base.group_erp_manager") # characteristics message_type = fields.Selection([ @@ -993,7 +993,7 @@ class Message(models.Model): 'email_from': 'sacha@pokemon.com' # email address or False 'subtype_id': (1, u'Discussions'), 'date': '2015-06-30 08:22:33', - 'partner_ids': [[7, "Sacha Du Bourg-Palette"]], # list of partner name_get + 'partner_ids': [[7, "Sacha Du Bourg-Palette"]], # list of partner convert_to_read 'message_type': u'comment', 'id': 59, 'subject': False @@ -1194,7 +1194,7 @@ class Message(models.Model): @api.model def _get_record_name(self, values): - """ Return the related document name, using name_get. It is done using + """ Return the related document name, using display_name. It is done using SUPERUSER_ID, to be sure to have the record name correctly stored. """ model = values.get('model', self.env.context.get('default_model')) res_id = values.get('res_id', self.env.context.get('default_res_id')) diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 629fcded8dd..a72af6e9a36 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -2712,7 +2712,7 @@ class MailThread(models.AbstractModel): :return str: default subject """ self.ensure_one() - return self.name_get()[0][1] + return self.display_name def _message_create(self, values_list): """ Low-level helper to create mail.message records. It is mainly used diff --git a/addons/mail/models/mail_tracking_value.py b/addons/mail/models/mail_tracking_value.py index 89945a5a803..2c3ccf2e524 100644 --- a/addons/mail/models/mail_tracking_value.py +++ b/addons/mail/models/mail_tracking_value.py @@ -78,8 +78,8 @@ class MailTracking(models.Model): values.update({ 'old_value_integer': initial_value and initial_value.id or 0, 'new_value_integer': new_value and new_value.id or 0, - 'old_value_char': initial_value and initial_value.sudo().name_get()[0][1] or '', - 'new_value_char': new_value and new_value.sudo().name_get()[0][1] or '' + 'old_value_char': initial_value and initial_value.sudo().display_name or '', + 'new_value_char': new_value and new_value.sudo().display_name or '' }) elif col_info['type'] in ['many2many', 'one2many']: values.update({ diff --git a/addons/mail/models/res_users_settings_volumes.py b/addons/mail/models/res_users_settings_volumes.py index 7d9bde64556..c2c4f0f0ef3 100644 --- a/addons/mail/models/res_users_settings_volumes.py +++ b/addons/mail/models/res_users_settings_volumes.py @@ -22,8 +22,9 @@ class ResUsersSettingsVolumes(models.Model): ("partner_or_guest_exists", "CHECK((partner_id IS NOT NULL AND guest_id IS NULL) OR (partner_id IS NULL AND guest_id IS NOT NULL))", "A volume setting must have a partner or a guest."), ] - def name_get(self): - return [(rec.id, f'{rec.user_setting_id.user_id.name} - {rec.partner_id.name or rec.guest_id.name}') for rec in self] + def _compute_display_name(self): + for rec in self: + rec.display_name = f'{rec.user_setting_id.user_id.name} - {rec.partner_id.name or rec.guest_id.name}' def _discuss_users_settings_volume_format(self): return [{ diff --git a/addons/mail/static/src/core/web/message_patch.js b/addons/mail/static/src/core/web/message_patch.js index e68488c9bbf..62b9074d11b 100644 --- a/addons/mail/static/src/core/web/message_patch.js +++ b/addons/mail/static/src/core/web/message_patch.js @@ -60,7 +60,7 @@ patch(Message.prototype, "mail/core/web", { case "boolean": return trackingValue.value ? _t("Yes") : _t("No"); /** - * many2one formatter exists but is expecting id/name_get or data + * many2one formatter exists but is expecting id/display_name or data * object but only the target record name is known in this context. * * Selection formatter exists but requires knowing all diff --git a/addons/mail_plugin/controllers/mail_plugin.py b/addons/mail_plugin/controllers/mail_plugin.py index 4ca6c7e426f..a163a4a4edc 100644 --- a/addons/mail_plugin/controllers/mail_plugin.py +++ b/addons/mail_plugin/controllers/mail_plugin.py @@ -193,7 +193,7 @@ class MailPluginController(http.Controller): if normalized_email: filter_domain = [('email_normalized', 'ilike', search_term)] else: - filter_domain = ['|', '|', ('display_name', 'ilike', search_term), ('ref', '=', search_term), + filter_domain = ['|', '|', ('complete_name', 'ilike', search_term), ('ref', '=', search_term), ('email', 'ilike', search_term)] # Search for the partner based on the email. diff --git a/addons/maintenance/models/maintenance.py b/addons/maintenance/models/maintenance.py index a6441d64aaa..d90f846f7ae 100644 --- a/addons/maintenance/models/maintenance.py +++ b/addons/maintenance/models/maintenance.py @@ -92,14 +92,12 @@ class MaintenanceEquipment(models.Model): return self.env.ref('maintenance.mt_mat_assign') return super(MaintenanceEquipment, self)._track_subtype(init_values) - def name_get(self): - result = [] + def _compute_display_name(self): for record in self: if record.name and record.serial_no: - result.append((record.id, record.name + '/' + record.serial_no)) + record.display_name = record.name + '/' + record.serial_no if record.name and not record.serial_no: - result.append((record.id, record.name)) - return result + record.display_name = record.name @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): diff --git a/addons/mass_mailing/models/mailing_contact.py b/addons/mass_mailing/models/mailing_contact.py index 21933b40f36..d94b8bf9fe7 100644 --- a/addons/mass_mailing/models/mailing_contact.py +++ b/addons/mass_mailing/models/mailing_contact.py @@ -129,13 +129,13 @@ class MassMailingContact(models.Model): def name_create(self, name): name, email = self.get_name_email(name) contact = self.create({'name': name, 'email': email}) - return contact.name_get()[0] + return contact.id, contact.display_name @api.model def add_to_list(self, name, list_id): name, email = self.get_name_email(name) contact = self.create({'name': name, 'email': email, 'list_ids': [(4, list_id)]}) - return contact.name_get()[0] + return contact.id, contact.display_name def _message_get_default_recipients(self): return {r.id: { diff --git a/addons/mass_mailing/models/mailing_list.py b/addons/mass_mailing/models/mailing_list.py index e6f3de9f8f9..e2585c5bddf 100644 --- a/addons/mass_mailing/models/mailing_list.py +++ b/addons/mass_mailing/models/mailing_list.py @@ -120,8 +120,9 @@ class MassMailingList(models.Model): return super(MassMailingList, self).write(vals) - def name_get(self): - return [(list.id, "%s (%s)" % (list.name, list.contact_count)) for list in self] + def _compute_display_name(self): + for mailing_list in self: + mailing_list.display_name = f"{mailing_list.name} ({mailing_list.contact_count})" def copy(self, default=None): self.ensure_one() diff --git a/addons/mass_mailing/models/mailing_trace.py b/addons/mass_mailing/models/mailing_trace.py index 9fa467177bc..cf0f2fd1380 100644 --- a/addons/mass_mailing/models/mailing_trace.py +++ b/addons/mass_mailing/models/mailing_trace.py @@ -52,7 +52,6 @@ class MailingTrace(models.Model): _order = 'create_date DESC' trace_type = fields.Selection([('mail', 'Email')], string='Type', default='mail', required=True) - display_name = fields.Char(compute='_compute_display_name') # mail data mail_mail_id = fields.Many2one('mail.mail', string='Mail', index='btree_not_null') mail_mail_id_int = fields.Integer( @@ -117,7 +116,7 @@ class MailingTrace(models.Model): @api.depends('trace_type', 'mass_mailing_id') def _compute_display_name(self): for trace in self: - trace.display_name = '%s: %s (%s)' % (trace.trace_type, trace.mass_mailing_id.name, trace.id) + trace.display_name = f'{trace.trace_type}: {trace.mass_mailing_id.name} ({trace.id})' @api.model_create_multi def create(self, values_list): diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 1f7a51d4fa1..878183f36c6 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -253,8 +253,9 @@ class MrpBom(models.Model): self.with_context({'active_test': False}).operation_ids.toggle_active() return super().toggle_active() - def name_get(self): - return [(bom.id, '%s%s' % (bom.code and '%s: ' % bom.code or '', bom.product_tmpl_id.display_name)) for bom in self] + def _compute_display_name(self): + for bom in self: + bom.display_name = f"{bom.code + ': ' if bom.code else ''}{bom.product_tmpl_id.display_name}" def action_compute_bom_days(self): company_id = self.env.context.get('default_company_id', self.env.company.id) diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py index 58a97df5578..a6cb798f5fd 100644 --- a/addons/mrp/models/mrp_workcenter.py +++ b/addons/mrp/models/mrp_workcenter.py @@ -328,15 +328,13 @@ class MrpWorkcenterProductivityLossType(models.Model): _rec_name = 'loss_type' @api.depends('loss_type') - def name_get(self): + def _compute_display_name(self): """ As 'category' field in form view is a Many2one, its value will be in - lower case. In order to display its value capitalized 'name_get' is + lower case. In order to display its value capitalized 'display_name' is overrided. """ - result = [] for rec in self: - result.append((rec.id, rec.loss_type.title())) - return result + rec.display_name = rec.loss_type.title() loss_type = fields.Selection([ ('availability', 'Availability'), diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index 32561266444..26fad8d089b 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -272,14 +272,12 @@ class MrpWorkorder(models.Model): if not self._check_m2m_recursion('blocked_by_workorder_ids'): raise ValidationError(_("You cannot create cyclic dependency.")) - def name_get(self): - res = [] + def _compute_display_name(self): for wo in self: if len(wo.production_id.workorder_ids) == 1: - res.append((wo.id, "%s - %s - %s" % (wo.production_id.name, wo.product_id.name, wo.name))) + wo.display_name = f"{wo.production_id.name} - {wo.product_id.name} - {wo.name}" else: - res.append((wo.id, "%s - %s - %s - %s" % (wo.production_id.workorder_ids.ids.index(wo._origin.id) + 1, wo.production_id.name, wo.product_id.name, wo.name))) - return res + wo.display_name = f"{wo.production_id.workorder_ids.ids.index(wo._origin.id) + 1} - {wo.production_id.name} - {wo.product_id.name} - {wo.name}" def unlink(self): # Removes references to workorder to avoid Validation Error diff --git a/addons/mrp_subcontracting/models/mrp_production.py b/addons/mrp_subcontracting/models/mrp_production.py index 4bd5940f77b..a9ab62ef40c 100644 --- a/addons/mrp_subcontracting/models/mrp_production.py +++ b/addons/mrp_subcontracting/models/mrp_production.py @@ -21,13 +21,6 @@ class MrpProduction(models.Model): incoming_picking = fields.Many2one(related='move_finished_ids.move_dest_ids.picking_id') - @api.depends('name') - def name_get(self): - return [ - (record.id, "%s (%s)" % (record.incoming_picking.name, record.name)) if record.bom_id.type == 'subcontract' - else (record.id, record.name) for record in self - ] - @api.depends('move_raw_ids.move_line_ids') def _compute_move_line_raw_ids(self): for production in self: diff --git a/addons/partner_autocomplete/models/res_company.py b/addons/partner_autocomplete/models/res_company.py index b2c52162548..742cbdf6d66 100644 --- a/addons/partner_autocomplete/models/res_company.py +++ b/addons/partner_autocomplete/models/res_company.py @@ -65,7 +65,7 @@ class ResCompany(models.Model): company_data = {field: value for field, value in company_data.items() if field in self.partner_id._fields and value and (field == 'image_1920' or not self.partner_id[field])} - # for company and childs: from state_id / country_id name_get like to IDs + # for company and childs: from state_id / country_id display_name like to IDs company_data.update(self._enrich_extract_m2o_id(company_data, ['state_id', 'country_id'])) if company_data.get('child_ids'): company_data['child_ids'] = [ diff --git a/addons/payment/models/payment_token.py b/addons/payment/models/payment_token.py index 1450e89b907..4acad1c76b7 100644 --- a/addons/payment/models/payment_token.py +++ b/addons/payment/models/payment_token.py @@ -27,6 +27,12 @@ class PaymentToken(models.Model): verified = fields.Boolean(string="Verified") active = fields.Boolean(string="Active", default=True) + #=== COMPUTE METHODS ===# + + def _compute_display_name(self): + for token in self: + token.display_name = token._build_display_name() + #=== CRUD METHODS ===# @api.model_create_multi @@ -91,9 +97,6 @@ class PaymentToken(models.Model): """ return - def name_get(self): - return [(token.id, token._build_display_name()) for token in self] - #=== BUSINESS METHODS ===# def _get_available_tokens(self, providers_ids, partner_id, is_validation=False, **kwargs): diff --git a/addons/payment/wizards/payment_link_wizard.py b/addons/payment/wizards/payment_link_wizard.py index 11ef8e6d4c6..69581b73f98 100644 --- a/addons/payment/wizards/payment_link_wizard.py +++ b/addons/payment/wizards/payment_link_wizard.py @@ -100,14 +100,15 @@ class PaymentLinkWizard(models.TransientModel): partner_id = related_document.partner_id currency_id = related_document.currency_id selection.extend( - self._get_payment_provider_available( + (payment_provider.id, payment_provider.display_name) + for payment_provider in self._get_payment_provider_available( res_model=res_model, res_id=res_id, company_id=company_id.id, partner_id=partner_id.id, amount=related_document.amount_total, currency_id=currency_id.id, - ).name_get() + ) ) return selection diff --git a/addons/point_of_sale/models/pos_bill.py b/addons/point_of_sale/models/pos_bill.py index 87d17c784a8..36caa48ad98 100644 --- a/addons/point_of_sale/models/pos_bill.py +++ b/addons/point_of_sale/models/pos_bill.py @@ -15,7 +15,7 @@ class Bill(models.Model): def name_create(self, name): try: value = float(name) - except: + except ValueError: raise UserError(_("The name of the Coins/Bills must be a number.")) result = super().create({"name": name, "value": value}) - return result.name_get()[0] + return result.id, result.display_name diff --git a/addons/point_of_sale/models/pos_category.py b/addons/point_of_sale/models/pos_category.py index 31bf309ed4d..ab79e2ac36a 100644 --- a/addons/point_of_sale/models/pos_category.py +++ b/addons/point_of_sale/models/pos_category.py @@ -32,8 +32,9 @@ class PosCategory(models.Model): self.ensure_one() return (self.parent_id._get_hierarchy() if self.parent_id else []) + [self.name] - def name_get(self) -> List[Tuple[int, str]]: - return [(category.id, " / ".join(category._get_hierarchy())) for category in self if category.name] + def _compute_display_name(self): + for cat in self: + cat.display_name = " / ".join(cat._get_hierarchy()) @api.ondelete(at_uninstall=False) def _unlink_except_session_open(self): diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py index a5d6f18c91c..51530d93cb3 100644 --- a/addons/point_of_sale/models/pos_config.py +++ b/addons/point_of_sale/models/pos_config.py @@ -366,15 +366,13 @@ class PosConfig(models.Model): if trusted_config.currency_id != config.currency_id: raise ValidationError(_("You cannot share open orders with configuration that does not use the same currency.")) - def name_get(self): - result = [] + def _compute_display_name(self): for config in self: last_session = self.env['pos.session'].search([('config_id', '=', config.id)], limit=1) if (not last_session) or (last_session.state == 'closed'): - result.append((config.id, _("%(pos_name)s (not used)", pos_name=config.name))) + config.display_name = _("%(pos_name)s (not used)", pos_name=config.name) else: - result.append((config.id, "%s (%s)" % (config.name, last_session.user_id.name))) - return result + config.display_name = f"{config.name} ({last_session.user_id.name})" @api.model_create_multi def create(self, vals_list): diff --git a/addons/point_of_sale/models/pos_payment.py b/addons/point_of_sale/models/pos_payment.py index b3e9b6bf305..ad2bf1648b7 100644 --- a/addons/point_of_sale/models/pos_payment.py +++ b/addons/point_of_sale/models/pos_payment.py @@ -33,14 +33,12 @@ class PosPayment(models.Model): is_change = fields.Boolean(string='Is this payment change?', default=False) account_move_id = fields.Many2one('account.move') - def name_get(self): - res = [] + def _compute_display_name(self): for payment in self: if payment.name: - res.append((payment.id, '%s %s' % (payment.name, formatLang(self.env, payment.amount, currency_obj=payment.currency_id)))) + payment.display_name = f'{payment.name} {formatLang(self.env, payment.amount, currency_obj=payment.currency_id)}' else: - res.append((payment.id, formatLang(self.env, payment.amount, currency_obj=payment.currency_id))) - return res + payment.display_name = formatLang(self.env, payment.amount, currency_obj=payment.currency_id) @api.constrains('payment_method_id') def _check_payment_method_id(self): diff --git a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js index 4a395b38f61..fdc322a8b28 100644 --- a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js +++ b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js @@ -587,7 +587,7 @@ export class TicketScreen extends Component { PARTNER: { repr: (order) => order.get_partner_name(), displayName: this.env._t("Customer"), - modelField: "partner_id.display_name", + modelField: "partner_id.complete_name", }, }; diff --git a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementControlPanel.js b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementControlPanel.js index 0d8f51bc186..89f4b1acd7f 100644 --- a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementControlPanel.js +++ b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementControlPanel.js @@ -9,12 +9,12 @@ import { usePos } from "@point_of_sale/app/store/pos_hook"; const VALID_SEARCH_TAGS = new Set(["date", "customer", "client", "name", "order"]); const FIELD_MAP = { date: "date_order", - customer: "partner_id.display_name", - client: "partner_id.display_name", + customer: "partner_id.complete_name", + client: "partner_id.complete_name", name: "name", order: "name", }; -const SEARCH_FIELDS = ["name", "partner_id.display_name", "date_order"]; +const SEARCH_FIELDS = ["name", "partner_id.complete_name", "date_order"]; /** * @emits search @@ -64,7 +64,7 @@ export class SaleOrderManagementControlPanel extends Component { * '|', * '|', * ['pos_reference', 'ilike', '%Customer 1%'], - * ['partner_id.display_name', 'ilike', '%Customer 1%'], + * ['partner_id.complete_name', 'ilike', '%Customer 1%'], * ['date_order', 'ilike', '%Customer 1%'] * ] * ``` @@ -81,7 +81,7 @@ export class SaleOrderManagementControlPanel extends Component { * ``` * searchString = 'customer: Steward, date: 2020-05-01' * result = [ - * ['partner_id.display_name', 'ilike', '%Steward%'], + * ['partner_id.complete_name', 'ilike', '%Steward%'], * ['date_order', 'ilike', '%2020-05-01%'] * ] * ``` diff --git a/addons/privacy_lookup/wizard/privacy_lookup_wizard.py b/addons/privacy_lookup/wizard/privacy_lookup_wizard.py index 6a63e6608f8..cc5ffbbd769 100644 --- a/addons/privacy_lookup/wizard/privacy_lookup_wizard.py +++ b/addons/privacy_lookup/wizard/privacy_lookup_wizard.py @@ -27,8 +27,8 @@ class PrivacyLookupWizard(models.TransientModel): for wizard in self: wizard.line_count = len(wizard.line_ids) - def name_get(self): - return [(w.id, _('Privacy Lookup')) for w in self] + def _compute_display_name(self): + self.display_name = _('Privacy Lookup') def _get_query_models_blacklist(self): return [ @@ -286,8 +286,8 @@ class PrivacyLookupWizardLine(models.TransientModel): record = self.env[line.res_model].sudo().browse(line.res_id) if not record.exists(): continue - name = record.name_get() - line.res_name = name[0][1] if name else ('%s/%s') % (line.res_model_id.name, line.res_id) + name = record.display_name + line.res_name = name if name else f'{line.res_model_id.name}/{line.res_id}' @api.onchange('is_active') def _onchange_is_active(self): diff --git a/addons/product/models/product_attribute_value.py b/addons/product/models/product_attribute_value.py index b0a050a7ce9..eed67e0d9e8 100644 --- a/addons/product/models/product_attribute_value.py +++ b/addons/product/models/product_attribute_value.py @@ -55,7 +55,7 @@ class ProductAttributeValue(models.Model): for pav in self: pav.is_used_on_products = bool(pav.pav_attribute_line_ids) - def name_get(self): + def _compute_display_name(self): """Override because in general the name of the value is confusing if it is displayed without the name of the corresponding attribute. Eg. on product list & kanban views, on BOM form view @@ -65,8 +65,9 @@ class ProductAttributeValue(models.Model): on every value. """ if not self.env.context.get('show_attribute', True): - return super().name_get() - return [(value.id, "%s: %s" % (value.attribute_id.name, value.name)) for value in self] + return super()._compute_display_name() + for value in self: + value.display_name = f"{value.attribute_id.name}: {value.name}" def write(self, values): if 'attribute_id' in values: diff --git a/addons/product/models/product_category.py b/addons/product/models/product_category.py index 1c16e963a5b..59e976b9d4e 100644 --- a/addons/product/models/product_category.py +++ b/addons/product/models/product_category.py @@ -48,12 +48,14 @@ class ProductCategory(models.Model): @api.model def name_create(self, name): - return self.create({'name': name}).name_get()[0] + category = self.create({'name': name}) + return category.id, category.display_name - def name_get(self): - if not self.env.context.get('hierarchical_naming', True): - return [(record.id, record.name) for record in self] - return super().name_get() + def _compute_display_name(self): + if self.env.context.get('hierarchical_naming', True): + return super()._compute_display_name() + for record in self: + record.display_name = record.name @api.ondelete(at_uninstall=False) def _unlink_except_default_category(self): diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py index 9acad80871d..87123044943 100644 --- a/addons/product/models/product_pricelist.py +++ b/addons/product/models/product_pricelist.py @@ -64,8 +64,9 @@ class Pricelist(models.Model): ], copy=True) - def name_get(self): - return [(pricelist.id, '%s (%s)' % (pricelist.name, pricelist.currency_id.name)) for pricelist in self] + def _compute_display_name(self): + for pricelist in self: + pricelist.display_name = f'{pricelist.name} ({pricelist.currency_id.name})' def _get_products_price(self, products, *args, **kwargs): """Compute the pricelist prices for the specified products, quantity & uom. diff --git a/addons/product/models/product_product.py b/addons/product/models/product_product.py index d2fb084846b..dee7e5a62e8 100644 --- a/addons/product/models/product_product.py +++ b/addons/product/models/product_product.py @@ -8,6 +8,7 @@ from odoo import api, fields, models, tools, _ from odoo.exceptions import ValidationError from odoo.osv import expression from odoo.tools import float_compare +from odoo.tools.misc import unique class ProductProduct(models.Model): @@ -415,21 +416,13 @@ class ProductProduct(models.Model): domain.append((('categ_id', 'child_of', self._context['search_default_categ_id']))) return super()._search(domain, offset, limit, order, access_rights_uid) - @api.depends_context('display_default_code', 'seller_id') + @api.depends_context('display_default_code', 'seller_id', 'company_id') def _compute_display_name(self): - # `display_name` is calling `name_get()`` which is overidden on product - # to depend on `display_default_code` and `seller_id` - return super()._compute_display_name() - def name_get(self): - # TDE: this could be cleaned a bit I think - - def _name_get(d): - name = d.get('name', '') - code = self._context.get('display_default_code', True) and d.get('default_code', False) or False - if code: - name = '[%s] %s' % (code,name) - return (d['id'], name) + def get_display_name(name, code): + if self._context.get('display_default_code', True) and code: + return f'[{code}] {name}' + return name partner_id = self._context.get('partner_id') if partner_id: @@ -443,15 +436,13 @@ class ProductProduct(models.Model): self.check_access_rights("read") self.check_access_rule("read") - result = [] - - # prefetch the fields used by the `name_get` + # prefetch the fields used by the `display_name` self.sudo().fetch(['name', 'default_code', 'product_tmpl_id']) product_template_ids = self.sudo().product_tmpl_id.ids if partner_ids: - # prefetch the fields used by the `name_get` + # prefetch the fields used by the `display_name` supplier_info = self.env['product.supplierinfo'].sudo().search_fetch( [('product_tmpl_id', 'in', product_template_ids), ('partner_id', 'in', partner_ids)], ['product_tmpl_id', 'product_id', 'company_id', 'product_name', 'product_code'], @@ -476,27 +467,18 @@ class ProductProduct(models.Model): if company_id: sellers = [x for x in sellers if x.company_id.id in [company_id, False]] if sellers: + temp = [] for s in sellers: seller_variant = s.product_name and ( variant and "%s (%s)" % (s.product_name, variant) or s.product_name ) or False - mydict = { - 'id': product.id, - 'name': seller_variant or name, - 'default_code': s.product_code or product.default_code, - } - temp = _name_get(mydict) - if temp not in result: - result.append(temp) - else: - mydict = { - 'id': product.id, - 'name': name, - 'default_code': product.default_code, - } - result.append(_name_get(mydict)) + temp.append(get_display_name(seller_variant or name, s.product_code or product.default_code)) - return result + # => Feature drop here, one record can only have one display_name now, instead separate with `,` + # Remove this comment + product.display_name = ", ".join(unique(temp)) + else: + product.display_name = get_display_name(name, product.default_code) @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index 029d47379cd..f05f58d8ee1 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -485,11 +485,11 @@ class ProductTemplate(models.Model): default['name'] = _("%s (copy)", self.name) return super(ProductTemplate, self).copy(default=default) - def name_get(self): - # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields + def _compute_display_name(self): + # Prefetch the fields used by the `display_name`, so `browse` doesn't fetch other fields self.fetch(['name', 'default_code']) - return [(template.id, '%s%s' % (template.default_code and '[%s] ' % template.default_code or '', template.name)) - for template in self] + for template in self: + template.display_name = '{}{}'.format(template.default_code and '[%s] ' % template.default_code or '', template.name) @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): @@ -529,7 +529,7 @@ class ProductTemplate(models.Model): domain2 = expression.AND([domain, [('id', 'in', tmpl_without_variant.ids)]]) searched_ids |= set(super()._name_search(name, domain2, operator, limit, order)) - # re-apply product.template order + name_get + # re-apply product.template order + display_name domain = [('id', 'in', list(searched_ids))] return super()._name_search('', domain, 'ilike', limit, order) diff --git a/addons/product/models/product_template_attribute_value.py b/addons/product/models/product_template_attribute_value.py index eea2dd5bab7..ebd6b3924ca 100644 --- a/addons/product/models/product_template_attribute_value.py +++ b/addons/product/models/product_template_attribute_value.py @@ -147,12 +147,13 @@ class ProductTemplateAttributeValue(models.Model): ptav_to_archive.write({'ptav_active': False}) return True - def name_get(self): + def _compute_display_name(self): """Override because in general the name of the value is confusing if it is displayed without the name of the corresponding attribute. Eg. on exclusion rules form """ - return [(value.id, "%s: %s" % (value.attribute_id.name, value.name)) for value in self] + for value in self: + value.display_name = f"{value.attribute_id.name}: {value.name}" def _only_active(self): return self.filtered(lambda ptav: ptav.ptav_active) diff --git a/addons/product/tests/test_seller.py b/addons/product/tests/test_seller.py index da3c495331f..41eb23c8e00 100644 --- a/addons/product/tests/test_seller.py +++ b/addons/product/tests/test_seller.py @@ -48,26 +48,20 @@ class TestSeller(TransactionCase): (0, 0, {'partner_id': self.asustec.id, 'product_code': 'NO', 'company_id': False}), ]}) - names = self.product_consu.with_context( + name = self.product_consu.with_context( partner_id=self.asustec.id, - ).name_get() - ref = set([x[1] for x in names]) - self.assertEqual(len(names), 3, "3 vendor references should have been found") - self.assertEqual(ref, {'[A] Boudin', '[B] Boudin', '[NO] Boudin'}, "Incorrect vendor reference list") - names = self.product_consu.with_context( + ).display_name + self.assertEqual(name, '[A] Boudin, [B] Boudin, [NO] Boudin', "Incorrect vendor reference list") + name = self.product_consu.with_context( partner_id=self.asustec.id, company_id=company_a.id, - ).name_get() - ref = set([x[1] for x in names]) - self.assertEqual(len(names), 2, "2 vendor references should have been found") - self.assertEqual(ref, {'[A] Boudin', '[NO] Boudin'}, "Incorrect vendor reference list") - names = self.product_consu.with_context( + ).display_name + self.assertEqual(name, '[A] Boudin, [NO] Boudin', "Incorrect vendor reference list") + name = self.product_consu.with_context( partner_id=self.asustec.id, company_id=company_b.id, - ).name_get() - ref = set([x[1] for x in names]) - self.assertEqual(len(names), 2, "2 vendor references should have been found") - self.assertEqual(ref, {'[B] Boudin', '[NO] Boudin'}, "Incorrect vendor reference list") + ).display_name + self.assertEqual(name, '[B] Boudin, [NO] Boudin', "Incorrect vendor reference list") def test_30_select_seller(self): self.res_partner_1 = self.asustec diff --git a/addons/project/models/project_collaborator.py b/addons/project/models/project_collaborator.py index 5919f33da72..f39dd93e431 100644 --- a/addons/project/models/project_collaborator.py +++ b/addons/project/models/project_collaborator.py @@ -16,9 +16,10 @@ class ProjectCollaborator(models.Model): ('unique_collaborator', 'UNIQUE(project_id, partner_id)', 'A collaborator cannot be selected more than once in the project sharing access. Please remove duplicate(s) and try again.'), ] - def name_get(self): - collaborator_search_read = self.search_read([('id', 'in', self.ids)], ['id', 'project_id', 'partner_id']) - return [(collaborator['id'], '%s - %s' % (collaborator['project_id'][1], collaborator['partner_id'][1])) for collaborator in collaborator_search_read] + def _compute_display_name(self): + self.fetch(['project_id', 'partner_id']) + for collaborator in self: + collaborator.display_name = f'{collaborator.project_id.display_name} - {collaborator.partner_id.display_name}' @api.model_create_multi def create(self, vals_list): diff --git a/addons/project/models/project_tags.py b/addons/project/models/project_tags.py index 1ab86ea6dc2..ea0a54c3cff 100644 --- a/addons/project/models/project_tags.py +++ b/addons/project/models/project_tags.py @@ -121,5 +121,5 @@ class ProjectTags(models.Model): def name_create(self, name): existing_tag = self.search([('name', '=ilike', name.strip())], limit=1) if existing_tag: - return existing_tag.name_get()[0] + return existing_tag.id, existing_tag.display_name return super().name_create(name) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 49924ebd1f6..78e04d69b1a 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -193,16 +193,14 @@ class PurchaseOrder(models.Model): order.date_planned = False @api.depends('name', 'partner_ref') - def name_get(self): - result = [] + def _compute_display_name(self): for po in self: name = po.name if po.partner_ref: name += ' (' + po.partner_ref + ')' if self.env.context.get('show_total_amount') and po.amount_total: name += ': ' + formatLang(self.env, po.amount_total, currency_obj=po.currency_id) - result.append((po.id, name)) - return result + po.display_name = name @api.depends('order_line.taxes_id', 'order_line.price_subtotal', 'amount_total', 'amount_untaxed') def _compute_tax_totals(self): diff --git a/addons/purchase/report/purchase_bill.py b/addons/purchase/report/purchase_bill.py index 8ca87dfdaa7..4cb82f845e0 100644 --- a/addons/purchase/report/purchase_bill.py +++ b/addons/purchase/report/purchase_bill.py @@ -40,8 +40,7 @@ class PurchaseBillUnion(models.Model): invoice_status in ('to invoice', 'no') )""") - def name_get(self): - result = [] + def _compute_display_name(self): for doc in self: name = doc.name or '' if doc.reference: @@ -50,5 +49,4 @@ class PurchaseBillUnion(models.Model): if doc.purchase_order_id and doc.purchase_order_id.invoice_status == 'no': amount = 0.0 name += ': ' + formatLang(self.env, amount, monetary=True, currency_obj=doc.currency_id) - result.append((doc.id, name)) - return result + doc.display_name = name diff --git a/addons/rating/models/rating.py b/addons/rating/models/rating.py index b47e04d751d..7b005fb929c 100644 --- a/addons/rating/models/rating.py +++ b/addons/rating/models/rating.py @@ -59,8 +59,8 @@ class Rating(models.Model): @api.depends('res_model', 'res_id') def _compute_res_name(self): for rating in self: - name = self.env[rating.res_model].sudo().browse(rating.res_id).name_get() - rating.res_name = name and name[0][1] or ('%s/%s') % (rating.res_model, rating.res_id) + name = self.env[rating.res_model].sudo().browse(rating.res_id).display_name + rating.res_name = name or f'{rating.res_model}/{rating.res_id}' @api.depends('res_model', 'res_id') def _compute_resource_ref(self): @@ -83,8 +83,8 @@ class Rating(models.Model): for rating in self: name = False if rating.parent_res_model and rating.parent_res_id: - name = self.env[rating.parent_res_model].sudo().browse(rating.parent_res_id).name_get() - name = name and name[0][1] or ('%s/%s') % (rating.parent_res_model, rating.parent_res_id) + name = self.env[rating.parent_res_model].sudo().browse(rating.parent_res_id).display_name + name = name or f'{rating.parent_res_model}/{rating.parent_res_id}' rating.parent_res_name = name def _get_rating_image_filename(self): diff --git a/addons/resource/models/resource_calendar_attendance.py b/addons/resource/models/resource_calendar_attendance.py index 859673f5f8d..e8aa2552464 100644 --- a/addons/resource/models/resource_calendar_attendance.py +++ b/addons/resource/models/resource_calendar_attendance.py @@ -70,7 +70,7 @@ class ResourceCalendarAttendance(models.Model): section_names = {'0': _('First week'), '1': _('Second week')} section_info = {True: _('this week'), False: _('other week')} for record in self.filtered(lambda l: l.display_type == 'line_section'): - section_name = "%s (%s)" % (section_names[record.week_type], section_info[this_week_type == record.week_type]) + section_name = f"{section_names[record.week_type]} ({section_info[this_week_type == record.week_type]})" record.display_name = section_name def _copy_attendance_vals(self): diff --git a/addons/sale/models/sale_order.py b/addons/sale/models/sale_order.py index 72803e6cfee..51aead66ee6 100644 --- a/addons/sale/models/sale_order.py +++ b/addons/sale/models/sale_order.py @@ -295,6 +295,18 @@ class SaleOrder(models.Model): #=== COMPUTE METHODS ===# + + + @api.depends_context('sale_show_partner_name') + def _compute_display_name(self): + if not self._context.get('sale_show_partner_name'): + return super()._compute_display_name() + for order in self: + name = order.name + if order.partner_id.name: + name = f'{name} - {order.partner_id.name}' + order.display_name = name + @api.depends('company_id') def _compute_require_signature(self): for order in self: @@ -1557,17 +1569,6 @@ class SaleOrder(models.Model): upselling_orders = filtered_self.filtered(lambda so: so.invoice_status == 'upselling') upselling_orders._create_upsell_activity() - def name_get(self): - if self._context.get('sale_show_partner_name'): - res = [] - for order in self: - name = order.name - if order.partner_id.name: - name = f'{name} - {order.partner_id.name}' - res.append((order.id, name)) - return res - return super().name_get() - #=== BUSINESS METHODS ===# def _create_upsell_activity(self): diff --git a/addons/sale/models/sale_order_line.py b/addons/sale/models/sale_order_line.py index 2132330a960..0d0a9db29b4 100644 --- a/addons/sale/models/sale_order_line.py +++ b/addons/sale/models/sale_order_line.py @@ -263,6 +263,15 @@ class SaleOrderLine(models.Model): #=== COMPUTE METHODS ===# + def _compute_display_name(self): + name_per_id = self._additional_name_per_id() + for so_line in self.sudo(): + name = '{} - {}'.format(so_line.order_id.name, so_line.name and so_line.name.split('\n')[0] or so_line.product_id.name) + additional_name = name_per_id.get(so_line.id) + if additional_name: + name = f'{name} {additional_name}' + so_line.display_name = name + @api.depends('product_id') def _compute_product_template_id(self): for line in self: @@ -1157,17 +1166,6 @@ class SaleOrderLine(models.Model): if so_line.order_partner_id.ref or so_line.order_partner_id.name } - def name_get(self): - result = [] - name_per_id = self._additional_name_per_id() - for so_line in self.sudo(): - name = '%s - %s' % (so_line.order_id.name, so_line.name and so_line.name.split('\n')[0] or so_line.product_id.name) - additional_name = name_per_id.get(so_line.id) - if additional_name: - name = '%s %s' % (name, additional_name) - result.append((so_line.id, name)) - return result - #=== HOOKS ===# def _is_delivery(self): diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py index 8fd2591eecb..dceb246abb1 100644 --- a/addons/sale/tests/test_sale_order.py +++ b/addons/sale/tests/test_sale_order.py @@ -290,7 +290,7 @@ class TestSaleOrder(SaleCommon): "No validity date must be specified if the company validity duration is 0") def test_so_names(self): - """Test custom context key for name_get & name_search. + """Test custom context key for display_name & name_search. Note: this key is used in sale_expense & sale_timesheet modules. """ @@ -302,7 +302,7 @@ class TestSaleOrder(SaleCommon): self.assertNotIn(self.sale_order.partner_id.name, self.sale_order.display_name) self.assertIn( self.sale_order.partner_id.name, - self.sale_order.with_context(sale_show_partner_name=True).name_get()[0][1]) + self.sale_order.with_context(sale_show_partner_name=True).display_name) def test_state_changes(self): """Test some untested state changes methods & logic.""" diff --git a/addons/sale_expense/models/sale_order.py b/addons/sale_expense/models/sale_order.py index a8b34056416..258ff1349a5 100644 --- a/addons/sale_expense/models/sale_order.py +++ b/addons/sale_expense/models/sale_order.py @@ -13,7 +13,7 @@ class SaleOrder(models.Model): @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): - """ For expense, we want to show all sales order but only their name_get (no ir.rule applied), this is the only way to do it. """ + """ For expense, we want to show all sales order but only their display_name (no ir.rule applied), this is the only way to do it. """ if ( self._context.get('sale_expense_all_order') and self.user_has_groups('sales_team.group_sale_salesman') diff --git a/addons/sale_expense/security/sale_expense_security.xml b/addons/sale_expense/security/sale_expense_security.xml index 48edc9cf1db..e9080467417 100644 --- a/addons/sale_expense/security/sale_expense_security.xml +++ b/addons/sale_expense/security/sale_expense_security.xml @@ -1,7 +1,7 @@ - + Expense Employee can read confirmed SO diff --git a/addons/sale_timesheet/models/project_update.py b/addons/sale_timesheet/models/project_update.py index f1aa7b212fc..53a2ee57b12 100644 --- a/addons/sale_timesheet/models/project_update.py +++ b/addons/sale_timesheet/models/project_update.py @@ -37,7 +37,6 @@ class ProjectUpdate(models.Model): ('is_downpayment', '=', False), ]), ) - name_by_sol = dict(sols.with_context(with_price_unit=True).name_get()) product_uom_unit = self.env.ref('uom.product_uom_unit') product_uom_hour = self.env.ref('uom.product_uom_hour') company_uom = self.env.company.timesheet_encode_uom_id @@ -50,7 +49,7 @@ class ProjectUpdate(models.Model): qty_invoiced = sol.product_uom._compute_quantity(sol.qty_invoiced, company_uom, raise_if_failure=False) unit = sol.product_uom if is_unit else company_uom services.append({ - 'name': name_by_sol[sol.id], + 'name': sol.with_context(with_price_unit=True).display_name, 'sold_value': product_uom_qty, 'effective_value': qty_delivered, 'remaining_value': product_uom_qty - qty_delivered, diff --git a/addons/sale_timesheet/models/sale_order.py b/addons/sale_timesheet/models/sale_order.py index 9cd71b24b3b..f03f64ead6a 100644 --- a/addons/sale_timesheet/models/sale_order.py +++ b/addons/sale_timesheet/models/sale_order.py @@ -157,12 +157,10 @@ class SaleOrderLine(models.Model): has_displayed_warning_upsell = fields.Boolean('Has Displayed Warning Upsell') timesheet_ids = fields.One2many('account.analytic.line', 'so_line', 'Timesheets') - def name_get(self): - res = super(SaleOrderLine, self).name_get() + def _compute_display_name(self): + super()._compute_display_name() with_remaining_hours = self.env.context.get('with_remaining_hours') if with_remaining_hours and any(line.remaining_hours_available for line in self): - names = dict(res) - result = [] company = self.env.company encoding_uom = company.timesheet_encode_uom_id is_hour = is_day = False @@ -174,7 +172,6 @@ class SaleOrderLine(models.Model): is_day = True unit_label = _('days remaining') for line in self: - name = names.get(line.id) if line.remaining_hours_available: remaining_time = '' if is_hour: @@ -198,12 +195,10 @@ class SaleOrderLine(models.Model): unit=unit_label ) name = '{name}{remaining_time}'.format( - name=name, + name=line.display_name, remaining_time=remaining_time ) - result.append((line.id, name)) - return result - return res + line.display_name = name @api.depends('product_id.service_policy') def _compute_remaining_hours_available(self): diff --git a/addons/snailmail/models/snailmail_letter.py b/addons/snailmail/models/snailmail_letter.py index 42c48ddcc28..22c569d8819 100644 --- a/addons/snailmail/models/snailmail_letter.py +++ b/addons/snailmail/models/snailmail_letter.py @@ -60,7 +60,6 @@ class SnailmailLetter(models.Model): "If not, it will got in state 'Error' and the error message will be displayed in the field 'Error Message'.") error_code = fields.Selection([(err_code, err_code) for err_code in ERROR_CODES], string="Error") info_msg = fields.Char('Information') - display_name = fields.Char('Display Name', compute="_compute_display_name") reference = fields.Char(string='Related Record', compute='_compute_reference', readonly=True, store=False) @@ -78,7 +77,7 @@ class SnailmailLetter(models.Model): def _compute_display_name(self): for letter in self: if letter.attachment_id: - letter.display_name = "%s - %s" % (letter.attachment_id.name, letter.partner_id.name) + letter.display_name = f"{letter.attachment_id.name} - {letter.partner_id.name}" else: letter.display_name = letter.partner_id.name @@ -237,7 +236,7 @@ class SnailmailLetter(models.Model): 'letter_id': letter.id, 'res_model': letter.model, 'res_id': letter.res_id, - 'contact_address': letter.partner_id.with_context(snailmail_layout=True, show_address=True).name_get()[0][1], + 'contact_address': letter.partner_id.with_context(snailmail_layout=True, show_address=True).display_name, 'address': { 'name': recipient_name, 'street': letter.partner_id.street, @@ -459,7 +458,7 @@ class SnailmailLetter(models.Model): return all(record[key] for key in required_keys) def _append_cover_page(self, invoice_bin: bytes): - address_split = self.partner_id.with_context(show_address=True, lang='en_US')._get_name().split('\n') + address_split = self.partner_id.with_context(show_address=True, lang='en_US').display_name.split('\n') address_split[0] = self.partner_id.name or self.partner_id.parent_id and self.partner_id.parent_id.name or address_split[0] address = '
'.join(address_split) address_x = 118 * mm diff --git a/addons/spreadsheet/static/src/data_sources/metadata_repository.js b/addons/spreadsheet/static/src/data_sources/metadata_repository.js index e8cf870951c..f90500ba873 100644 --- a/addons/spreadsheet/static/src/data_sources/metadata_repository.js +++ b/addons/spreadsheet/static/src/data_sources/metadata_repository.js @@ -31,7 +31,7 @@ import { EventBus } from "@odoo/owl"; * * Implementation note: * For the labels, when someone is asking for a display name which is not loaded yet, - * the proxy returns directly (undefined) and a request for a name_get will + * the proxy returns directly (undefined) and a request to read display_name will * be triggered. All the requests created are batched and send, with only one * request per model, after a clock cycle. * At the end of this process, an event is triggered (labels-fetched) @@ -102,7 +102,7 @@ export class MetadataRepository extends EventBus { } /** - * Save the result of a name_get request in the cache + * Save the result of display_name read request in the cache */ setDisplayName(model, id, result) { this.displayNameRepository.setDisplayName(model, id, result); diff --git a/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js b/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js index d5129286080..7e0a39dc3d4 100644 --- a/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js +++ b/addons/spreadsheet/static/src/global_filters/plugins/global_filters_ui_plugin.js @@ -237,8 +237,8 @@ export default class GlobalFiltersUIPlugin extends spreadsheet.UIPlugin { return ""; } if (!this.recordsDisplayName[filter.id]) { - this.orm.call(filter.modelName, "name_get", [value]).then((result) => { - const names = result.map(([, name]) => name); + this.orm.call(filter.modelName, "read", [value, ["display_name"]]).then((result) => { + const names = result.map(({display_name}) => display_name); this.recordsDisplayName[filter.id] = names; this.dispatch("EVALUATE_CELLS", { sheetId: this.getters.getActiveSheetId(), diff --git a/addons/spreadsheet/static/src/pivot/pivot_model.js b/addons/spreadsheet/static/src/pivot/pivot_model.js index cec6a1ebac3..75c89f29204 100644 --- a/addons/spreadsheet/static/src/pivot/pivot_model.js +++ b/addons/spreadsheet/static/src/pivot/pivot_model.js @@ -117,7 +117,7 @@ export function parsePivotFormulaFieldValue(field, groupValue) { /** * This class is an extension of PivotModel with some additional information - * that we need in spreadsheet (name_get, isUsedInSheet, ...) + * that we need in spreadsheet (display_name, isUsedInSheet, ...) */ export class SpreadsheetPivotModel extends PivotModel { /** diff --git a/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js b/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js index ef4ff9d2b73..8a54f1b07ae 100644 --- a/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js +++ b/addons/spreadsheet/static/tests/global_filters/global_filters_model_test.js @@ -637,14 +637,14 @@ QUnit.module("spreadsheet > Global filters model", {}, () => { assert.expect(6); const model = await createModelWithDataSource({ mockRPC: function (route, { method, args }) { - if (method === "name_get") { - const [resId] = args[0]; + if (method === "read") { + const resId = args[0][0]; const names = { 1: "Jean-Jacques", 2: "Raoul Grosbedon", }; - assert.step(`name_get_${resId}`); - return [[resId, names[resId]]]; + assert.step(`read_${resId}`); + return [{id: resId, display_name: names[resId]}] } }, }); @@ -685,7 +685,7 @@ QUnit.module("spreadsheet > Global filters model", {}, () => { }); await nextTick(); assert.equal(getCellValue(model, "A10"), "Raoul Grosbedon"); - assert.verifySteps(["name_get_1", "name_get_2"]); + assert.verifySteps(["read_1", "read_2"]); }); QUnit.test( diff --git a/addons/spreadsheet/static/tests/lists/list_plugin_test.js b/addons/spreadsheet/static/tests/lists/list_plugin_test.js index c1afd79160f..4e5c22140fe 100644 --- a/addons/spreadsheet/static/tests/lists/list_plugin_test.js +++ b/addons/spreadsheet/static/tests/lists/list_plugin_test.js @@ -46,7 +46,7 @@ QUnit.module("spreadsheet > list plugin", {}, () => { assert.strictEqual(getCellValue(model, "A2"), "Spreadsheet"); }); - QUnit.test("Return name_get of many2one field", async (assert) => { + QUnit.test("Return display_name of many2one field", async (assert) => { const { model } = await createSpreadsheetWithList({ columns: ["product_id"] }); assert.strictEqual(getCellValue(model, "A2"), "xphone"); }); diff --git a/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js b/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js index 4a816bf27b6..763eeaf6419 100644 --- a/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js +++ b/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js @@ -427,7 +427,7 @@ QUnit.module("spreadsheet > pivot plugin", {}, () => { assert.step(`${model}/${method}`); await dataPromise; } - if (model === "product" && method === "name_get") { + if (model === "product" && method === "read") { assert.ok(false, "should not be called because data is put in cache"); } return result; diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 7fab3b6c7f3..5a95a647308 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -641,14 +641,12 @@ Please change the quantity done or the rounding precision of your unit of measur defaults['additional'] = True # to trigger `_autoconfirm_picking` return defaults - def name_get(self): - res = [] + def _compute_display_name(self): for move in self: - res.append((move.id, '%s%s%s>%s' % ( + move.display_name = '%s%s%s>%s' % ( move.picking_id.origin and '%s/' % move.picking_id.origin or '', move.product_id.code and '%s: ' % move.product_id.code or '', - move.location_id.name, move.location_dest_id.name))) - return res + move.location_id.name, move.location_dest_id.name) @api.model_create_multi def create(self, vals_list): diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index ecc74970a3a..76691b2ecea 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -164,20 +164,17 @@ class PickingType(models.Model): for record in self: record[field_name] = count.get(record.id, 0) - def name_get(self): + def _compute_display_name(self): """ Display 'Warehouse_name: PickingType_name' """ - res = [] for picking_type in self: if picking_type.warehouse_id: - name = picking_type.warehouse_id.name + ': ' + picking_type.name + picking_type.display_name = f"{picking_type.warehouse_id.name}: {picking_type.name}" else: - name = picking_type.name - res.append((picking_type.id, name)) - return res + picking_type.display_name = picking_type.name @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): - # Try to reverse the `name_get` structure + # Try to reverse the `display_name` structure parts = name.split(': ') if len(parts) == 2: name_domain = [('warehouse_id.name', operator, parts[0]), ('name', operator, parts[1])] diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 785777dfc8c..4f164aa5d3d 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -543,9 +543,8 @@ class StockQuant(models.Model): 'target': 'new', } - def name_get(self): + def _compute_display_name(self): """name that will be displayed in the detailed operation""" - name_parts = [] for record in self: name = [] if self.env.user.has_group('stock.group_stock_multi_locations'): @@ -556,10 +555,7 @@ class StockQuant(models.Model): name.append(record.package_id.name) if self.env.user.has_group('stock.group_tracking_owner') and record.owner_id: name.append(record.owner_id.name) - name_parts.append(name) - if name_parts: - return [(quant.id, ' - '.join(name)) if name else (quant.id, "- no data -") for quant, name in zip(self, name_parts)] - return [] + self.display_name = ' - '.join(name) if name else "- no data -" @api.constrains('product_id') def check_product_id(self): diff --git a/addons/stock/tests/test_move_lines.py b/addons/stock/tests/test_move_lines.py index 7a618ed872b..8f3581946cc 100644 --- a/addons/stock/tests/test_move_lines.py +++ b/addons/stock/tests/test_move_lines.py @@ -47,8 +47,8 @@ class StockMoveLine(TestStockCommon): }) def test_pick_from_1(self): - """ test quant name_get """ - self.assertEqual(self.quant.name_get()[0][1], 'WH/Stock/Shelf 1 - Lot 1 - Pack A - The Owner') + """ test quant display_name """ + self.assertEqual(self.quant.display_name, 'WH/Stock/Shelf 1 - Lot 1 - Pack A - The Owner') def test_pick_from_2(self): """ Create a move line from a quant""" diff --git a/addons/stock/wizard/stock_track_confirmation.py b/addons/stock/wizard/stock_track_confirmation.py index cd93c999781..27ea52adcac 100644 --- a/addons/stock/wizard/stock_track_confirmation.py +++ b/addons/stock/wizard/stock_track_confirmation.py @@ -25,12 +25,12 @@ class StockTrackingLines(models.TransientModel): _name = 'stock.track.line' _description = 'Stock Track Line' - product_display_name = fields.Char('Name', compute='_compute_display_name', readonly=True) + product_display_name = fields.Char('Name', compute='_compute_product_display_name', readonly=True) product_id = fields.Many2one('product.product', 'Product', readonly=True) tracking = fields.Selection(related='product_id.tracking') wizard_id = fields.Many2one('stock.track.confirmation', readonly=True) - def _compute_display_name(self): + def _compute_product_display_name(self): """ Onchange results in product.display_name not being directly accessible """ for line in self: line.product_display_name = line.product_id._origin.display_name diff --git a/addons/survey/models/survey_user_input.py b/addons/survey/models/survey_user_input.py index 2c1313e9d3b..f38ecdf0701 100644 --- a/addons/survey/models/survey_user_input.py +++ b/addons/survey/models/survey_user_input.py @@ -671,9 +671,7 @@ class SurveyUserInputLine(models.Model): line.display_name = fields.Datetime.to_string(line.value_datetime) elif line.answer_type == 'suggestion': if line.matrix_row_id: - line.display_name = '%s: %s' % ( - line.suggested_answer_id.value, - line.matrix_row_id.value) + line.display_name = f'{line.suggested_answer_id.value}: {line.matrix_row_id.value}' else: line.display_name = line.suggested_answer_id.value diff --git a/addons/test_base_import/tests/test_base_import.py b/addons/test_base_import/tests/test_base_import.py index c7b0ac0ef10..de585651e88 100644 --- a/addons/test_base_import/tests/test_base_import.py +++ b/addons/test_base_import/tests/test_base_import.py @@ -86,7 +86,7 @@ class TestBasicFields(BaseImportCase): self.assertEqualFields(self.get_fields('char.stillreadonly'), [ID_FIELD]) def test_m2o(self): - """ M2O fields should allow import of themselves (name_get), + """ M2O fields should allow import of themselves (display_name), their id and their xid""" self.assertEqualFields(self.get_fields('m2o'), make_field( field_type='many2one', comodel_name=base_import_model('m2o.related'), model_name=base_import_model('m2o'), diff --git a/addons/test_xlsx_export/models.py b/addons/test_xlsx_export/models.py index 6e4d6d9fa0a..885fcea9631 100644 --- a/addons/test_xlsx_export/models.py +++ b/addons/test_xlsx_export/models.py @@ -9,8 +9,9 @@ class NewModel(models.Model): value = fields.Integer(default=4) - def name_get(self): - return [(record.id, "%s:%s" % (self._name, record.value)) for record in self] + def _compute_display_name(self): + for record in self: + record.display_name = f"{self._name}:{record.value}" class GroupOperator(models.Model): _name = 'export.group_operator' diff --git a/addons/uom/models/uom_uom.py b/addons/uom/models/uom_uom.py index 7d84ae13d33..bf9796579aa 100644 --- a/addons/uom/models/uom_uom.py +++ b/addons/uom/models/uom_uom.py @@ -195,7 +195,7 @@ class UoM(models.Model): else: values['category_id'] = EnglishUoMCateg.name_create('Unsorted/Imported Units')[0] new_uom = self.create(values) - return new_uom.name_get()[0] + return new_uom.id, new_uom.display_name def _compute_quantity(self, qty, to_unit, round=True, rounding_method='UP', raise_if_failure=True): """ Convert the given quantity from the current UoM `self` into a given one diff --git a/addons/web/static/src/core/orm_service.js b/addons/web/static/src/core/orm_service.js index abb5365c841..0c656b5d0fa 100644 --- a/addons/web/static/src/core/orm_service.js +++ b/addons/web/static/src/core/orm_service.js @@ -147,7 +147,9 @@ export class ORM { if (!ids.length) { return Promise.resolve([]); } - return this.call(model, "name_get", [ids], kwargs); + return this.call(model, "read", [ids, ["display_name"]], kwargs).then( + (data) => data && data.map(({id, display_name}) => [id, display_name]) + ); } /** diff --git a/addons/web/static/src/legacy/js/views/basic/basic_model.js b/addons/web/static/src/legacy/js/views/basic/basic_model.js index a67f7dc7b0d..803d7591bf6 100644 --- a/addons/web/static/src/legacy/js/views/basic/basic_model.js +++ b/addons/web/static/src/legacy/js/views/basic/basic_model.js @@ -1603,7 +1603,7 @@ var BasicModel = AbstractModel.extend({ * Apply an x2one (either a many2one or a reference field) change. There is * a need for this function because the server only gives an id when a * onchange modifies a many2one field. For this reason, we need (sometimes) - * to do a /name_get to fetch a display_name. + * to do a read of display_name. * * Moreover, for the many2one case, a new value can sometimes be set (i.e. * a display_name is given, but no id). When this happens, we first do a @@ -1673,12 +1673,12 @@ var BasicModel = AbstractModel.extend({ // TODO: refactor this to use _fetchNameGet def = this._rpc({ model: coModel, - method: 'name_get', - args: [data.id], + method: 'read', + args: [data.id, ['display_name']], context: this._getContext(record, { fieldName, viewType }), }) .then(function (result) { - rel_data.display_name = result[0][1]; + rel_data.display_name = result[0]['display_name']; }); } return Promise.resolve(def).then(function () { @@ -2479,7 +2479,7 @@ var BasicModel = AbstractModel.extend({ } }, /** - * Fetch name_get for a record datapoint. + * Fetch display_name for a record datapoint. * * @param {Object} dataPoint * @returns {Promise} @@ -2487,15 +2487,15 @@ var BasicModel = AbstractModel.extend({ _fetchNameGet: function (dataPoint) { return this._rpc({ model: dataPoint.model, - method: 'name_get', - args: [dataPoint.res_id], + method: 'read', + args: [dataPoint.res_id, ['display_name']], context: dataPoint.getContext(), }).then(function (result) { - dataPoint.data.display_name = result[0][1]; + dataPoint.data.display_name = result[0]['display_name']; }); }, /** - * Fetch name_get for a field of type Many2one or Reference + * Fetch display_name for a field of type Many2one or Reference * * @private * @params {Object} list: must be a datapoint of type list @@ -2530,14 +2530,14 @@ var BasicModel = AbstractModel.extend({ } return this._rpc({ model: model, - method: 'name_get', - args: [unique(ids)], + method: 'read', + args: [unique(ids), ['display_name']], context: list.context, }) - .then(function (name_gets) { + .then(function (readRecords) { records.forEach((record) => { - var nameGet = name_gets.find(nameGet => nameGet[0] === record.data.id); - record.data.display_name = nameGet[1]; + const readRecord = readRecords.find(readRecord => readRecord['id'] === record.data.id); + record.data.display_name = readRecord['display_name']; }); }); }, @@ -2585,7 +2585,7 @@ var BasicModel = AbstractModel.extend({ }); }, /** - * Fetch the `name_get` for a reference field. + * Fetch the `display_name` for a reference field. * * @private * @param {Object} record @@ -2601,15 +2601,12 @@ var BasicModel = AbstractModel.extend({ if (model && model !== 'False' && resID) { def = self._rpc({ model: model, - method: 'name_get', - args: [resID], + method: 'read', + args: [resID, ['display_name']], context: record.getContext({fieldName: fieldName}), }).then(function (result) { return self._makeDataPoint({ - data: { - id: result[0][0], - display_name: result[0][1], - }, + data: result[0], modelName: model, parentID: record.id, }); @@ -2635,19 +2632,16 @@ var BasicModel = AbstractModel.extend({ var parent = datapoints[ids[0]][0]; var def = self._rpc({ model: model, - method: 'name_get', - args: [ids], + method: 'read', + args: [ids, ['display_name']], context: self.localData[parent].getContext({fieldName: fieldName, withoutRecordData: true}), }).then(function (result) { result.forEach((el) => { - var parentIDs = datapoints[el[0]]; + var parentIDs = datapoints[el['id']]; parentIDs.forEach((parentID) => { var parent = self.localData[parentID]; var referenceDp = self._makeDataPoint({ - data: { - id: el[0], - display_name: el[1], - }, + data: el, modelName: model, parentID: parent.id, }); @@ -2658,7 +2652,7 @@ var BasicModel = AbstractModel.extend({ return def; }, /** - * Fetch the extra data (`name_get`) for the reference fields of the record + * Fetch the extra data (`display_name`) for the reference fields of the record * model. * * @private @@ -2698,7 +2692,7 @@ var BasicModel = AbstractModel.extend({ var toFetch = this._getDataToFetchByModel(list, fieldName); var defs = []; - // one name_get by model + // one display_name read by model Object.entries(toFetch).forEach(([model, datapoints]) => { defs.push(self._fetchReferenceData(datapoints, model, fieldName)); }); @@ -2741,7 +2735,7 @@ var BasicModel = AbstractModel.extend({ }); var defs = []; - // one name_get by model + // one display_name read by model Object.entries(toFetch).forEach(([model, datapoints]) => { defs.push(self._fetchReferenceData(datapoints, model, fieldName)); }); @@ -2750,7 +2744,7 @@ var BasicModel = AbstractModel.extend({ }, /** * Batch requests for all reference field in list's children. - * Called by _readGroup to make only one 'name_get' rpc by fieldName. + * Called by _readGroup to make only one display_name 'read' rpc by fieldName. * * @param {Object} list a valid resource object * @returns {Promise} @@ -2939,7 +2933,7 @@ var BasicModel = AbstractModel.extend({ }); }, /** - * Fetches the `name_get` associated to the reference widget if the field is + * Fetches the `display_name` associated to the reference widget if the field is * a `char` (which is a supported case). * * @private @@ -2951,7 +2945,7 @@ var BasicModel = AbstractModel.extend({ _fetchSpecialReference: function (record, fieldName, fieldInfo) { var field = record.fields[fieldName]; if (field.type === 'char') { - // if the widget reference is set on a char field, the name_get + // if the widget reference is set on a char field, the display_name // needs to be fetched a posteriori return Promise.resolve(this._fetchReference(record, fieldName)); } else if (fieldInfo.options.model_field) { @@ -4504,17 +4498,17 @@ var BasicModel = AbstractModel.extend({ const _changes = record._changes || {}; const relRecordId = _changes[name] || record.data[name]; if (!relRecordId) { - return; // field is unset, no need to do the name_get + return; // field is unset, no need to do the display_name read } var relRecord = self.localData[relRecordId]; defs.push(self._rpc({ model: field.relation, - method: 'name_get', - args: [relRecord.data.id], + method: 'read', + args: [relRecord.data.id, ['display_name']], context: self._getContext(record, {fieldName: name, viewType: viewType, withoutRecordData: true}), }) .then(function (result) { - relRecord.data.display_name = result[0][1]; + relRecord.data.display_name = result[0]['display_name']; })); } } diff --git a/addons/web/static/src/search/search_arch_parser.js b/addons/web/static/src/search/search_arch_parser.js index 61911f02a0d..534cf945afe 100644 --- a/addons/web/static/src/search/search_arch_parser.js +++ b/addons/web/static/src/search/search_arch_parser.js @@ -160,9 +160,9 @@ export class SearchArchParser extends XMLParser { } else if (fieldType === "many2one") { this.labels.push((orm) => { return orm - .call(relation, "name_get", [value], { context }) + .call(relation, "read", [value, ['display_name']], { context }) .then((results) => { - preField.defaultAutocompleteValue.label = results[0][1]; + preField.defaultAutocompleteValue.label = results[0]['display_name']; }); }); } diff --git a/addons/web/static/src/views/fields/properties/property_value.js b/addons/web/static/src/views/fields/properties/property_value.js index a15b6ba2fad..603cfae3947 100644 --- a/addons/web/static/src/views/fields/properties/property_value.js +++ b/addons/web/static/src/views/fields/properties/property_value.js @@ -328,7 +328,7 @@ export class PropertyValue extends Component { * @returns {array} [record id, record name] */ async _nameGet(recordId) { - const result = await this.orm.call(this.props.comodel, "name_get", [[recordId]], { + const result = await this.orm.nameGet(this.props.comodel, [recordId], { context: this.props.context, }); return result[0]; diff --git a/addons/web/static/src/views/fields/radio/radio_field.js b/addons/web/static/src/views/fields/radio/radio_field.js index 79383b199fc..aa4acb2b2df 100644 --- a/addons/web/static/src/views/fields/radio/radio_field.js +++ b/addons/web/static/src/views/fields/radio/radio_field.js @@ -91,7 +91,7 @@ registry.category("fields").add("radio", radioField); export async function preloadRadio(orm, record, fieldName, { domain }) { const field = record.fields[fieldName]; const records = await orm.searchRead(field.relation, domain, ["id"]); - return await orm.call(field.relation, "name_get", [records.map((record) => record.id)]); + return await orm.nameGet(field.relation, [records.map((record) => record.id)]); } registry.category("preloadedData").add("radio", { diff --git a/addons/web/static/src/views/relational_model.js b/addons/web/static/src/views/relational_model.js index 4bdfbc4c224..31e5ba6886a 100644 --- a/addons/web/static/src/views/relational_model.js +++ b/addons/web/static/src/views/relational_model.js @@ -146,9 +146,9 @@ class RequestBatcherORM extends ORM { } /** - * Entry point to batch "name_get" calls. If the `resModel` argument has + * Entry point to batch display_name "read" calls. If the `resModel` argument has * already been called, the given ids are added to the previous list of ids - * to perform a single name_get call. + * to perform a single read of display_name call. * * @param {string} resModel * @param {number[]} resIds diff --git a/addons/web/static/src/views/sample_server.js b/addons/web/static/src/views/sample_server.js index 7ea837ff08a..847bc37bc64 100644 --- a/addons/web/static/src/views/sample_server.js +++ b/addons/web/static/src/views/sample_server.js @@ -370,6 +370,7 @@ export class SampleServer { * @returns {Array[]} a list of [id, display_name] */ _mockNameGet(params) { + throw new Error("name_get is deprecated, it shouldn't be called"); const { model, args } = params; let ids = args[0]; if (!args.length) { diff --git a/addons/web/static/tests/core/orm_service_tests.js b/addons/web/static/tests/core/orm_service_tests.js index fbd481c76c0..9de164cfa6d 100644 --- a/addons/web/static/tests/core/orm_service_tests.js +++ b/addons/web/static/tests/core/orm_service_tests.js @@ -152,9 +152,9 @@ QUnit.test("nameGet method", async (assert) => { const env = await makeTestEnv(); const context = { complete: true }; await env.services.orm.nameGet("sale.order", [2, 5], { context }); - assert.strictEqual(query.route, "/web/dataset/call_kw/sale.order/name_get"); + assert.strictEqual(query.route, "/web/dataset/call_kw/sale.order/read"); assert.deepEqual(query.params, { - args: [[2, 5]], + args: [[2, 5], ['display_name']], kwargs: { context: { complete: true, @@ -163,7 +163,7 @@ QUnit.test("nameGet method", async (assert) => { uid: 7, }, }, - method: "name_get", + method: "read", model: "sale.order", }); }); diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index 2b59cb3aa9a..53cd22c3031 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -590,7 +590,7 @@ export class MockServer { /** * Converts an Object representing a record to actual return Object of the * python `onchange` method. - * Specifically, it applies `name_get` on many2one's and transforms raw id + * Specifically, it reads `display_name` on many2one's and transforms raw id * list in orm command lists for x2many's. * For x2m fields that add or update records (ORM commands 0 and 1), it is * recursive. @@ -603,7 +603,6 @@ export class MockServer { Object.entries(values).forEach(([fname, val]) => { const field = this.models[modelName].fields[fname]; if (field.type === "many2one" && typeof val === "number") { - // implicit name_get const m2oRecord = this.models[field.relation].records.find((r) => r.id === val); values[fname] = [val, m2oRecord.display_name]; } else if (field.type === "one2many" || field.type === "many2many") { @@ -671,8 +670,6 @@ export class MockServer { return this.mockGetViews(args.model, args.kwargs); case "name_create": return this.mockNameCreate(args.model, args.args[0], args.kwargs); - case "name_get": - return this.mockNameGet(args.model, args.args); case "name_search": return this.mockNameSearch(args.model, args.args, args.kwargs); case "onchange": @@ -874,30 +871,6 @@ export class MockServer { return [id, name]; } - /** - * Simulate a 'name_get' operation - * - * @private - * @param {string} model - * @param {Array} args - * @returns {Array[]} a list of [id, display_name] - */ - mockNameGet(model, args) { - var ids = args[0]; - if (!args.length) { - throw new Error("name_get: expected one argument"); - } else if (!ids) { - return []; - } - if (!Array.isArray(ids)) { - ids = [ids]; - } - var records = this.models[model].records; - var names = ids.map((id) => - id ? [id, records.find((r) => r.id === id).display_name] : [null, ""] - ); - return names; - } /** * Simulate a 'name_search' operation. diff --git a/addons/web/static/tests/legacy/helpers/mock_server.js b/addons/web/static/tests/legacy/helpers/mock_server.js index c8ab53fc903..93fb6ce52d4 100644 --- a/addons/web/static/tests/legacy/helpers/mock_server.js +++ b/addons/web/static/tests/legacy/helpers/mock_server.js @@ -229,7 +229,7 @@ var MockServer = Class.extend({ /** * Converts an Object representing a record to actual return Object of the * python `onchange` method. - * Specifically, it applies `name_get` on many2one's and transforms raw id + * Specifically, it reads `display_name` on many2one's and transforms raw id * list in orm command lists for x2many's. * For x2m fields that add or update records (ORM commands 0 and 1), it is * recursive. @@ -243,7 +243,6 @@ var MockServer = Class.extend({ Object.entries(values).forEach(([fname, val]) => { const field = this.data[model].fields[fname]; if (field.type === 'many2one' && typeof val === 'number') { - // implicit name_get const m2oRecord = this.data[field.relation].records.find(r => r.id === val); values[fname] = [val, m2oRecord.display_name]; } else if (field.type === 'one2many' || field.type === 'many2many') { @@ -1234,31 +1233,6 @@ var MockServer = Class.extend({ }); return views; }, - /** - * Simulate a 'name_get' operation - * - * @private - * @param {string} model - * @param {Array} args - * @returns {Array[]} a list of [id, display_name] - */ - _mockNameGet: function (model, args) { - var ids = args[0]; - if (!args.length) { - throw new Error("name_get: expected one argument"); - } - else if (!ids) { - return [] - } - if (!Array.isArray(ids)) { - ids = [ids]; - } - var records = this.data[model].records; - var names = ids.map((id) => { - return id ? [id, records.find((r) => r.id === id).display_name] : [null, ""]; - }); - return names; - }, /** * Simulate a 'name_create' operation * @@ -2029,8 +2003,6 @@ var MockServer = Class.extend({ case 'get_views': return this._mockGetViews(args.model, args.kwargs); - case 'name_get': - return this._mockNameGet(args.model, args.args); case 'name_create': return this._mockNameCreate(args.model, args.args); diff --git a/addons/web/static/tests/legacy/mockserver_tests.js b/addons/web/static/tests/legacy/mockserver_tests.js index 9db8e827c2e..4f2a06be9ec 100644 --- a/addons/web/static/tests/legacy/mockserver_tests.js +++ b/addons/web/static/tests/legacy/mockserver_tests.js @@ -109,110 +109,6 @@ QUnit.module("Legacy MockServer", { ); }); - QUnit.test("performRpc: name_get with no args", async function (assert) { - assert.expect(2); - const server = new MockServer(this.data, {}); - try { - await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [], - kwargs: {}, - }); - } catch { - assert.step("name_get failed") - } - assert.verifySteps(["name_get failed"]) - }); - - QUnit.test("performRpc: name_get with undefined arg", async function (assert) { - assert.expect(1); - const server = new MockServer(this.data, {}); - const result = await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [undefined], - kwargs: {}, - }); - assert.deepEqual(result, []) - }); - - QUnit.test("performRpc: name_get with a single id", async function (assert) { - assert.expect(1); - const server = new MockServer(this.data, {}); - const result = await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [1], - kwargs: {}, - }); - assert.deepEqual(result, [[1, "Jean-Michel"]]); - }); - - QUnit.test("performRpc: name_get with array of ids", async function (assert) { - assert.expect(1); - const server = new MockServer(this.data, {}); - const result = await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [[1]], - kwargs: {}, - }); - assert.deepEqual(result, [[1, "Jean-Michel"]]); - }); - - QUnit.test("performRpc: name_get with invalid id", async function (assert) { - assert.expect(2); - const server = new MockServer(this.data, {}); - try { - await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [11111], - kwargs: {}, - }); - } catch { - assert.step("name_get failed") - } - assert.verifySteps(["name_get failed"]) - }); - - QUnit.test("performRpc: name_get with id and undefined id", async function (assert) { - assert.expect(1); - const server = new MockServer(this.data, {}); - const result = await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [[undefined, 1]], - kwargs: {}, - }); - assert.deepEqual(result, [[null, ""], [1, "Jean-Michel"]]); - }); - - QUnit.test("performRpc: name_get with single id 0", async function (assert) { - assert.expect(1); - const server = new MockServer(this.data, {}); - const result = await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [0], - kwargs: {}, - }); - assert.deepEqual(result, []); - }); - - QUnit.test("performRpc: name_get with array of id 0", async function (assert) { - assert.expect(1); - const server = new MockServer(this.data, {}); - const result = await server.performRpc("", { - model: "res.partner", - method: "name_get", - args: [[0]], - kwargs: {}, - }); - assert.deepEqual(result, [[null, ""]]); - }); - QUnit.test("performRpc: search with active_test=false", async function (assert) { assert.expect(1); const server = new MockServer(this.data, {}); diff --git a/addons/web/static/tests/legacy/views/basic_model_tests.js b/addons/web/static/tests/legacy/views/basic_model_tests.js index 11b5aed7571..bcd22b44ffe 100644 --- a/addons/web/static/tests/legacy/views/basic_model_tests.js +++ b/addons/web/static/tests/legacy/views/basic_model_tests.js @@ -368,7 +368,7 @@ Model: BasicModel, data: this.data, mockRPC: function (route, args) { - if (args.method === 'name_get') { + if (args.method === 'read' && args.args[1].length === 1 && args.args[1][0] === 'display_name') { assert.strictEqual(args.model, 'product'); } return this._super(route, args); @@ -448,7 +448,7 @@ Model: BasicModel, data: this.data, mockRPC: function (route, args) { - if (args.method === 'name_get') { + if (args.method === 'read' && args.args[1].length === 1 && args.args[1][0] === 'display_name') { assert.strictEqual(args.model, 'product'); } return this._super(route, args); @@ -2083,9 +2083,9 @@ data: this.data, mockRPC: function (route, args) { assert.step(route); - if (route === "/web/dataset/call_kw/product/name_get") { - assert.deepEqual(args.args, [[37, 41]], - "the name_get should contain the product ids"); + if (route === "/web/dataset/call_kw/product/read") { + assert.deepEqual(args.args, [[37, 41], ['display_name']], + "the read display_name should contain the product ids"); } return this._super(route, args); }, @@ -2095,9 +2095,9 @@ var record = model.get(resultID); assert.strictEqual(record.data[0].data.reference.data.display_name, "xphone", - "name_get should have been correctly fetched"); - assert.verifySteps(["/web/dataset/search_read", "/web/dataset/call_kw/product/name_get"], - "should have done 2 rpc (searchread and name_get for product)"); + "display_name should have been correctly fetched"); + assert.verifySteps(["/web/dataset/search_read", "/web/dataset/call_kw/product/read"], + "should have done 2 rpc (searchread and read display_name for product)"); model.destroy(); }); diff --git a/addons/web/static/tests/mock_server_tests.js b/addons/web/static/tests/mock_server_tests.js index 4d471c2ba27..d2e6d850668 100644 --- a/addons/web/static/tests/mock_server_tests.js +++ b/addons/web/static/tests/mock_server_tests.js @@ -195,105 +195,6 @@ QUnit.module("MockServer", (hooks) => { assert.deepEqual(result, [{ id: 1, name: "Jean-Michel" }]); }); - QUnit.test("performRPC: name_get with no args", async function (assert) { - const server = new MockServer(data, {}); - try { - await server.performRPC("", { - model: "partner", - method: "name_get", - args: [], - kwargs: {}, - }); - } catch { - assert.step("name_get failed"); - } - assert.verifySteps(["name_get failed"]); - }); - - QUnit.test("performRPC: name_get with undefined arg", async function (assert) { - const server = new MockServer(data, {}); - const result = await server.performRPC("", { - model: "partner", - method: "name_get", - args: [undefined], - kwargs: {}, - }); - assert.deepEqual(result, []); - }); - - QUnit.test("performRPC: name_get with a single id", async function (assert) { - const server = new MockServer(data, {}); - const result = await server.performRPC("", { - model: "partner", - method: "name_get", - args: [1], - kwargs: {}, - }); - assert.deepEqual(result, [[1, "Jean-Michel"]]); - }); - - QUnit.test("performRPC: name_get with array of ids", async function (assert) { - const server = new MockServer(data, {}); - const result = await server.performRPC("", { - model: "partner", - method: "name_get", - args: [[1]], - kwargs: {}, - }); - assert.deepEqual(result, [[1, "Jean-Michel"]]); - }); - - QUnit.test("performRPC: name_get with invalid id", async function (assert) { - const server = new MockServer(data, {}); - try { - await server.performRPC("", { - model: "partner", - method: "name_get", - args: [11111], - kwargs: {}, - }); - } catch { - assert.step("name_get failed"); - } - assert.verifySteps(["name_get failed"]); - }); - - QUnit.test("performRPC: name_get with id and undefined id", async function (assert) { - const server = new MockServer(data, {}); - const result = await server.performRPC("", { - model: "partner", - method: "name_get", - args: [[undefined, 1]], - kwargs: {}, - }); - assert.deepEqual(result, [ - [null, ""], - [1, "Jean-Michel"], - ]); - }); - - QUnit.test("performRPC: name_get with single id 0", async function (assert) { - const server = new MockServer(data, {}); - const result = await server.performRPC("", { - model: "partner", - method: "name_get", - args: [0], - kwargs: {}, - }); - assert.deepEqual(result, []); - }); - - QUnit.test("performRPC: name_get with array of id 0", async function (assert) { - const server = new MockServer(data, {}); - const result = await server.performRPC("", { - model: "partner", - method: "name_get", - args: [[0]], - kwargs: {}, - }); - assert.deepEqual(result, [[null, ""]]); - }); - QUnit.test("performRPC: search_count", async function (assert) { const server = new MockServer(data, {}); const result = await server.performRPC("", { diff --git a/addons/web/static/tests/search/search_bar_tests.js b/addons/web/static/tests/search/search_bar_tests.js index 3433730b8d3..b6017fb7456 100644 --- a/addons/web/static/tests/search/search_bar_tests.js +++ b/addons/web/static/tests/search/search_bar_tests.js @@ -1020,7 +1020,7 @@ QUnit.module("Search", (hooks) => { const def = makeDeferred(); const mockRPC = async (_, args) => { - if (args.method === "name_get") { + if (args.method === "read") { await def; } }; diff --git a/addons/web/static/tests/views/fields/many2one_field_tests.js b/addons/web/static/tests/views/fields/many2one_field_tests.js index d05e4b6f4be..97a13280dd3 100644 --- a/addons/web/static/tests/views/fields/many2one_field_tests.js +++ b/addons/web/static/tests/views/fields/many2one_field_tests.js @@ -347,7 +347,7 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, { method, kwargs }) { - if (method === "name_get") { + if (method === "read") { assert.strictEqual( kwargs.context.blip, undefined, @@ -422,9 +422,9 @@ QUnit.module("Fields", (hooks) => { `, - mockRPC(route, { method, kwargs }) { - if (method === "name_get" && kwargs.context.show_address) { - return [[4, "aaa\nStreet\nCity ZIP"]]; + mockRPC(route, { args, method, kwargs }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name" && kwargs.context.show_address) { + return [{id: 4, display_name: "aaa\nStreet\nCity ZIP"}]; } }, }); @@ -462,8 +462,8 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, { args, kwargs, method }) { - if (method === "name_get" && kwargs.context.show_address) { - return args.map((id) => [id, namegets[id]]); + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name" && kwargs.context.show_address) { + return args.map((id) => ({id, display_name: namegets[id]})); } }, }); @@ -521,9 +521,9 @@ QUnit.module("Fields", (hooks) => { `, - mockRPC(route, { kwargs, method, model }) { - if (method === "name_get" && kwargs.context.show_address) { - return [[2, "second record\nrue morgue\nparis 75013"]]; + mockRPC(route, { args, kwargs, method }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name" && kwargs.context.show_address) { + return [{id: 2, display_name: "second record\nrue morgue\nparis 75013"}]; } }, }); @@ -565,9 +565,9 @@ QUnit.module("Fields", (hooks) => { `, - mockRPC(route, { kwargs, method, model }) { - if (method === "name_get" && kwargs.context.show_address) { - return [[2, "second record\nrue morgue\nparis 75013"]]; + mockRPC(route, { kwargs, method, args }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name" && kwargs.context.show_address) { + return [{id: 2, display_name: "second record\nrue morgue\nparis 75013"}]; } }, }); @@ -1429,9 +1429,9 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, { args, kwargs, method }) { - if (method === "name_get") { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name") { assert.step(method); - return args.map((id) => [id, namegets[id]]); + return args.map((id) => ({id, display_name: namegets[id]})); } if (method === "name_search") { assert.step(method); @@ -1439,7 +1439,7 @@ QUnit.module("Fields", (hooks) => { } }, }); - assert.verifySteps(["name_get"]); + assert.verifySteps(["read"]); const input = target.querySelector(".o_field_widget input"); @@ -1463,7 +1463,7 @@ QUnit.module("Fields", (hooks) => { await click(target.querySelector(".dropdown-menu li")); // Check the selection has been taken into account - assert.verifySteps(["name_get"]); + assert.verifySteps(["read"]); assert.strictEqual(input.value, "fizz"); assert.strictEqual( target.querySelector(".o_field_many2one_extra").innerHTML, @@ -1541,15 +1541,15 @@ QUnit.module("Fields", (hooks) => {
`, - mockRPC(route, { method }) { - if (method === "name_get") { + mockRPC(route, { method, args }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name") { count++; - return Promise.resolve([[1, "first record\nand some address"]]); + return Promise.resolve([{id: 1, display_name: "first record\nand some address"}]); } }, }); - assert.strictEqual(count, 1, "an extra name_get should have been done"); + assert.strictEqual(count, 1, "an extra read should have been done"); assert.ok( target.querySelector("a.o_form_uri").textContent.includes("and some address"), "should display additional result" @@ -1568,15 +1568,15 @@ QUnit.module("Fields", (hooks) => {
`, - mockRPC(route, { method }) { - if (method === "name_get") { + mockRPC(route, { method, args }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name") { count++; - return Promise.resolve([[1, "first record\nand some address"]]); + return Promise.resolve([{id: 1, display_name: "first record\nand some address"}]); } }, }); - assert.strictEqual(count, 1, "an extra name_get should have been done"); + assert.strictEqual(count, 1, "an extra read should have been done"); assert.strictEqual( target.querySelector(".o_field_widget[name='trululu'] input").value, "first record", @@ -1986,9 +1986,9 @@ QUnit.module("Fields", (hooks) => {
`, - mockRPC(route, { method }) { - if (method === "name_get") { - throw new Error("Nameget should not be called"); + mockRPC(route, { method, args}) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name") { + throw new Error("read(['display_name']) should not be called"); } }, }); @@ -2025,9 +2025,9 @@ QUnit.module("Fields", (hooks) => {
`, - mockRPC(route, { method }) { - if (method === "name_get") { - throw new Error("Nameget should not be called"); + mockRPC(route, { method, args }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name") { + throw new Error("read(['display_name']) should not be called"); } }, }); @@ -2115,7 +2115,7 @@ QUnit.module("Fields", (hooks) => { } ); - QUnit.test("list in form: name_get with unique ids (default_get)", async function (assert) { + QUnit.test("list in form: read with unique ids (default_get)", async function (assert) { serverData.models.partner.records[0].display_name = "MyTrululu"; serverData.models.partner.fields.p.default = [ [0, 0, { trululu: 1, p: [] }], @@ -2136,9 +2136,9 @@ QUnit.module("Fields", (hooks) => {
`, - mockRPC(route, { method }) { - if (method === "name_get") { - throw new Error("should not call name_get"); + mockRPC(route, { method, args }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name") { + throw new Error("read(['display_name']) should not called"); } }, }); @@ -2838,16 +2838,16 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, { args, method }) { count++; - if (method === "name_get" && args[0] === 2) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name" && args[0] === 2) { // LPE: any call_kw route can take either [ids] or id as the first // argument as model.browse() in python supports both - // With the basic_model, name_get is passed only an id, not an array - return Promise.resolve([[2, "hello world\nso much noise"]]); + // With the basic_model, read is passed only an id, not an array + return Promise.resolve([{id: 2, display_name: "hello world\nso much noise"}]); } }, }); - assert.strictEqual(count, 3, "should have done 3 rpcs (get_views, onchange and name_get)"); + assert.strictEqual(count, 3, "should have done 3 rpcs (get_views, onchange and read)"); assert.strictEqual( target.querySelector(".o_field_widget[name='trululu'] input").value, "hello world", diff --git a/addons/web/static/tests/views/fields/one2many_field_tests.js b/addons/web/static/tests/views/fields/one2many_field_tests.js index 6fa48613b16..71c11f3b1c4 100644 --- a/addons/web/static/tests/views/fields/one2many_field_tests.js +++ b/addons/web/static/tests/views/fields/one2many_field_tests.js @@ -275,7 +275,7 @@ QUnit.module("Fields", (hooks) => { await clickCreate(target); // change the int_field through drag and drop - // that way, we'll trigger the sorting and the name_get + // that way, we'll trigger the sorting and the display_name read // of the lines of "p" await dragAndDrop("tbody tr:nth-child(2) .o_handle_cell", "tbody tr", "top"); @@ -1728,11 +1728,11 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, args) { - if (checkRPC && args.method === "name_get") { + if (checkRPC && args.method === "read" && args.args[1].length === 1 && args.args[1][0] === "display_name") { assert.deepEqual( args.args[0], [37], - "should only fetch the name_get of the unknown record" + "should only fetch the display_name of the unknown record" ); } }, @@ -8794,7 +8794,7 @@ QUnit.module("Fields", (hooks) => { ); QUnit.test( - "one2many with sequence field, fetch name_get from empty list, field text", + "one2many with sequence field, fetch display from empty list, field text", async function (assert) { // There was a bug where a RPC would fail because no route was set. // The scenario is: @@ -8803,7 +8803,7 @@ QUnit.module("Fields", (hooks) => { // - a handle field // - a many2one, which is not required, and we will leave it empty // - reorder the lines with the handle - // -> This will call a resequence, which calls a name_get. + // -> This will call a resequence, which calls a display_name read. // -> With the bug that would fail, if it's ok the test will pass. // This test will also make sure lists with @@ -11124,7 +11124,7 @@ QUnit.module("Fields", (hooks) => { await addRow(target); }); - QUnit.test("do not call name_get if display_name already known", async function (assert) { + QUnit.test("do not call read if display_name already known", async function (assert) { serverData.models.partner.fields.product_id.default = 37; serverData.models.partner.onchanges = { trululu: function (obj) { diff --git a/addons/web/static/tests/views/fields/reference_field_tests.js b/addons/web/static/tests/views/fields/reference_field_tests.js index 52ddceca89f..43e25a10b03 100644 --- a/addons/web/static/tests/views/fields/reference_field_tests.js +++ b/addons/web/static/tests/views/fields/reference_field_tests.js @@ -253,7 +253,7 @@ QUnit.module("Fields", (hooks) => { "name_create", "create", "read", - "name_get", + "read", ], "The name_create method should have been called" ); @@ -612,14 +612,14 @@ QUnit.module("Fields", (hooks) => { `, - mockRPC(route, { method, model }) { - if (method === "name_get") { + mockRPC(route, { method, model, args }) { + if (method === "read" && args[1].length === 1 && args[1][0] === "display_name") { assert.step(model); } }, }); - assert.verifySteps(["product"], "the first name_get should have been done"); + assert.verifySteps(["product"], "the first display_name read should have been done"); assert.strictEqual( target.querySelector(".o_field_widget[name='reference'] select").value, "product", @@ -634,7 +634,7 @@ QUnit.module("Fields", (hooks) => { // trigger onchange await editInput(target, ".o_field_widget[name=int_field] input", 12); - assert.verifySteps(["partner_type"], "the second name_get should have been done"); + assert.verifySteps(["partner_type"], "the second display_name read should have been done"); assert.strictEqual( target.querySelector(".o_field_widget[name='reference'] select").value, "partner_type", @@ -705,14 +705,14 @@ QUnit.module("Fields", (hooks) => { `, - mockRPC(route, { model, method }) { - if (model === "product" && method === "name_get") { + mockRPC(route, { model, method, args }) { + if (model === "product" && method === "read" && args[1].length === 1 && args[1][0] === "display_name") { nbNameGet++; } }, }); - assert.strictEqual(nbNameGet, 1, "the first name_get should have been done"); + assert.strictEqual(nbNameGet, 1, "the first display_name read should have been done"); assert.strictEqual( target.querySelector(".o_field_widget[name=foo]").textContent, "xphone", @@ -722,7 +722,7 @@ QUnit.module("Fields", (hooks) => { // trigger onchange await editInput(target, ".o_field_widget[name=int_field] input", 41); - assert.strictEqual(nbNameGet, 2, "the second name_get should have been done"); + assert.strictEqual(nbNameGet, 2, "the second display_name read should have been done"); assert.strictEqual( target.querySelector(".o_field_widget[name=foo]").textContent, "xpad", diff --git a/addons/web/static/tests/views/form/form_view_tests.js b/addons/web/static/tests/views/form/form_view_tests.js index c6ab1527b24..07643c5340f 100644 --- a/addons/web/static/tests/views/form/form_view_tests.js +++ b/addons/web/static/tests/views/form/form_view_tests.js @@ -3837,7 +3837,7 @@ QUnit.module("Views", (hooks) => {
`, mockRPC(route, args) { - if (args.method === "name_get") { + if (args.method === 'read' && args.args[1].length === 1 && args.args[1][0] === 'display_name') { nameGetCount++; } }, @@ -3868,8 +3868,8 @@ QUnit.module("Views", (hooks) => { serverData, arch: '
', mockRPC(route, args) { - if (args.method === "name_get") { - throw new Error("Should not call name_get"); + if (args.method === 'read' && args.args[1].length === 1 && args.args[1][0] === 'display_name') { + throw new Error("Should not call display_name read"); } }, }); diff --git a/addons/web/static/tests/views/kanban/kanban_view_tests.js b/addons/web/static/tests/views/kanban/kanban_view_tests.js index 00891ffd95e..58a09a3d760 100644 --- a/addons/web/static/tests/views/kanban/kanban_view_tests.js +++ b/addons/web/static/tests/views/kanban/kanban_view_tests.js @@ -4809,11 +4809,11 @@ QUnit.module("Views", (hooks) => { "web_read_group", "web_search_read", "web_search_read", - "name_get", + "read", "web_read_group", "web_search_read", "web_search_read", - "name_get", + "read", ]); }); diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index eeb98f53114..39dcdfef094 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -10750,7 +10750,7 @@ QUnit.module("Views", (hooks) => { serverData, arch: '', mockRPC(route, args) { - if (args.method === "name_get") { + if (args.method === "read") { assert.step(args.model); } }, @@ -10758,7 +10758,7 @@ QUnit.module("Views", (hooks) => { assert.verifySteps( ["bar", "res_currency"], - "should have done 1 name_get by model in reference values" + "should have done 1 read by model in reference values" ); assert.strictEqual( $(target).find("tbody td:not(.o_list_record_selector)").text(), @@ -10791,7 +10791,7 @@ QUnit.module("Views", (hooks) => { groupBy: ["foo"], mockRPC(route, args) { assert.step(args.method || route); - if (args.method === "name_get") { + if (args.method === 'read' && args.args[1].length === 1 && args.args[1][0] === 'display_name') { if (args.model === "bar") { assert.deepEqual(args.args[0], [1, 2, 3]); } @@ -10801,7 +10801,7 @@ QUnit.module("Views", (hooks) => { } }, }); - assert.verifySteps(["get_views", "web_read_group", "name_get", "name_get"]); + assert.verifySteps(["get_views", "web_read_group", "read", "read"]); assert.containsN(target, ".o_group_header", 2); const allNames = Array.from( target.querySelectorAll(".o_data_cell"), @@ -10876,7 +10876,7 @@ QUnit.module("Views", (hooks) => { if (args.method === "write") { assert.deepEqual(args.args, [[1, 2, 3], { bar: true }]); } - if (args.method === "name_get") { + if (args.method === 'read' && args.args[1].length === 1 && args.args[1][0] === 'display_name') { if (nameGetCount === 2) { assert.strictEqual(args.model, "bar"); assert.deepEqual(args.args[0], [1, 2]); @@ -10890,7 +10890,7 @@ QUnit.module("Views", (hooks) => { }, }); - assert.verifySteps(["get_views", "web_read_group", "name_get", "name_get"]); + assert.verifySteps(["get_views", "web_read_group", "read", "read"]); await click(target.querySelectorAll(".o_data_row .o_list_record_selector input")[0]); await click(target.querySelectorAll(".o_data_row .o_list_record_selector input")[1]); await click(target.querySelectorAll(".o_data_row .o_list_record_selector input")[2]); @@ -10899,7 +10899,7 @@ QUnit.module("Views", (hooks) => { await click(target, ".modal .modal-footer .btn-primary"); assert.containsNone(target, ".modal"); - assert.verifySteps(["write", "read", "name_get", "name_get"]); + assert.verifySteps(["write", "read", "read", "read"]); assert.containsN(target, ".o_group_header", 2); const allNames = Array.from(target.querySelectorAll(".o_data_cell")) @@ -17833,8 +17833,8 @@ QUnit.module("Views", (hooks) => { `, mockRPC(_, args) { - if (args.method === "name_get") { - assert.step("name_get"); + if (args.method === 'read' && args.args[1].length === 1 && args.args[1][0] === 'display_name') { + assert.step("readDisplayName"); assert.deepEqual(args.args[0], [3]); } }, @@ -17853,7 +17853,7 @@ QUnit.module("Views", (hooks) => { await click(target, ".modal .o_data_row:nth-child(3) td[name=display_name]"); - assert.verifySteps(["name_get"]); + assert.verifySteps(["readDisplayName"]); await clickSave(target); assert.deepEqual( diff --git a/addons/web/static/tests/views/record_tests.js b/addons/web/static/tests/views/record_tests.js index 1c47aa11ee4..aad8b33ec9d 100644 --- a/addons/web/static/tests/views/record_tests.js +++ b/addons/web/static/tests/views/record_tests.js @@ -326,7 +326,7 @@ QUnit.module("Record Component", (hooks) => { }, }), }); - assert.verifySteps(["/web/dataset/call_kw/bar/name_get"]); + assert.verifySteps(["/web/dataset/call_kw/bar/read"]); assert.strictEqual(target.querySelector(".o_field_many2one_selection input").value, "bar1"); await editInput(target, ".o_field_many2one_selection input", "abc"); assert.verifySteps(["/web/dataset/call_kw/bar/name_search"]); diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index f5e2894fe13..62bcb90296c 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -69,19 +69,17 @@ class View(models.Model): raise ValueError(f"Trying to create a view for website {new_website_id} from a website {website_id} environment") return super().create(vals_list) - def name_get(self): + def _compute_display_name(self): if not (self._context.get('display_key') or self._context.get('display_website')): - return super(View, self).name_get() + return super()._compute_display_name() - res = [] for view in self: view_name = view.name if self._context.get('display_key'): view_name += ' <%s>' % view.key if self._context.get('display_website') and view.website_id: view_name += ' [%s]' % view.website_id.name - res.append((view.id, view_name)) - return res + view.display_name = view_name def write(self, vals): '''COW for ir.ui.view. This way editing websites does not impact other diff --git a/addons/website/models/res_partner.py b/addons/website/models/res_partner.py index d549cad59dd..61b925f8474 100644 --- a/addons/website/models/res_partner.py +++ b/addons/website/models/res_partner.py @@ -3,7 +3,7 @@ import werkzeug.urls -from odoo import models, fields +from odoo import models, fields, api class Partner(models.Model): _name = 'res.partner' @@ -31,13 +31,11 @@ class Partner(models.Model): } return 'https://maps.google.com/maps?' + werkzeug.urls.url_encode(params) - def _get_name(self): - name = super(Partner, self)._get_name() - if self._context.get('display_website') and self.env.user.has_group('website.group_multi_website'): - if self.website_id: - name += ' [%s]' % self.website_id.name - return name - + @api.depends_context('display_website') def _compute_display_name(self): - self2 = self.with_context(display_website=False) - super(Partner, self2)._compute_display_name() + super()._compute_display_name() + if not self._context.get('display_website') or not self.env.user.has_group('website.group_multi_website'): + return + for partner in self: + if partner.website_id: + partner.display_name += f' [{self.website_id.name}]' diff --git a/addons/website/models/website_menu.py b/addons/website/models/website_menu.py index 83bedf697a0..5c668cd034e 100644 --- a/addons/website/models/website_menu.py +++ b/addons/website/models/website_menu.py @@ -52,17 +52,15 @@ class Menu(models.Model): mega_menu_content = fields.Html(translate=html_translate, sanitize=False, prefetch=True) mega_menu_classes = fields.Char() - def name_get(self): + def _compute_display_name(self): if not self._context.get('display_website') and not self.env.user.has_group('website.group_multi_website'): - return super(Menu, self).name_get() + return super()._compute_display_name() - res = [] for menu in self: menu_name = menu.name if menu.website_id: - menu_name += ' [%s]' % menu.website_id.name - res.append((menu.id, menu_name)) - return res + menu_name += f' [{menu.website_id.name}]' + menu.display_name = menu_name @api.model_create_multi def create(self, vals_list): diff --git a/addons/website/models/website_rewrite.py b/addons/website/models/website_rewrite.py index 2e393904203..bbb926e0025 100644 --- a/addons/website/models/website_rewrite.py +++ b/addons/website/models/website_rewrite.py @@ -103,12 +103,9 @@ class WebsiteRewrite(models.Model): except ValueError as e: raise ValidationError(_('"URL to" is invalid: %s') % e) - def name_get(self): - result = [] + def _compute_display_name(self): for rewrite in self: - name = "%s - %s" % (rewrite.redirect_type, rewrite.name) - result.append((rewrite.id, name)) - return result + rewrite.display_name = f"{rewrite.redirect_type} - {rewrite.name}" @api.model_create_multi def create(self, vals_list): diff --git a/addons/website/models/website_visitor.py b/addons/website/models/website_visitor.py index e8c2b9fdb93..81fdcfee277 100644 --- a/addons/website/models/website_visitor.py +++ b/addons/website/models/website_visitor.py @@ -84,14 +84,9 @@ class WebsiteVisitor(models.Model): ] @api.depends('partner_id') - def name_get(self): - res = [] + def _compute_display_name(self): for record in self: - res.append(( - record.id, - record.partner_id.name or _('Website Visitor #%s', record.id) - )) - return res + record.display_name = record.partner_id.name or _('Website Visitor #%s', record.id) @api.depends('access_token') def _compute_partner_id(self): diff --git a/addons/website_event/models/event_registration_answer.py b/addons/website_event/models/event_registration_answer.py index b78590cd1ca..2d5817928fb 100644 --- a/addons/website_event/models/event_registration_answer.py +++ b/addons/website_event/models/event_registration_answer.py @@ -25,10 +25,6 @@ class EventRegistrationAnswer(models.Model): ] # for displaying selected answers by attendees in attendees list view - def name_get(self): - return [ - (reg.id, - reg.value_answer_id.name if reg.question_type == "simple_choice" else reg.value_text_box - ) - for reg in self - ] + def _compute_display_name(self): + for reg in self: + reg.display_name = reg.value_answer_id.name if reg.question_type == "simple_choice" else reg.value_text_box diff --git a/addons/website_event/models/website_visitor.py b/addons/website_event/models/website_visitor.py index 9b2a9d5c685..70d009bc5bf 100644 --- a/addons/website_event/models/website_visitor.py +++ b/addons/website_event/models/website_visitor.py @@ -21,15 +21,14 @@ class WebsiteVisitor(models.Model): search="_search_event_registered_ids", groups="event.group_event_registration_desk") - @api.depends('partner_id, event_registration_ids.name') - def name_get(self): + @api.depends('partner_id', 'event_registration_ids.name') + def _compute_display_name(self): """ If there is an event registration for an anonymous visitor, use that registered attendee name as visitor name. """ - res_dict = dict(super().name_get()) + super()._compute_display_name() # sudo is needed for `event_registration_ids` for visitor in self.sudo().filtered(lambda v: not v.partner_id and v.event_registration_ids): - res_dict[visitor.id] = visitor.event_registration_ids[-1].name - return list(res_dict.items()) + visitor.display_name = visitor.event_registration_ids[-1].name @api.depends('event_registration_ids') def _compute_event_registration_count(self): diff --git a/addons/website_google_map/controllers/main.py b/addons/website_google_map/controllers/main.py index fd5528fb6d1..1d3d9fe7dcf 100644 --- a/addons/website_google_map/controllers/main.py +++ b/addons/website_google_map/controllers/main.py @@ -40,7 +40,7 @@ class GoogleMap(http.Controller): partner_data["partners"].append({ 'id': partner.id, 'name': partner.name, - 'address': '\n'.join(partner.name_get()[0][1].split('\n')[1:]), + 'address': '\n'.join(partner.display_name.split('\n')[1:]), 'latitude': str(partner.partner_latitude) if partner.partner_latitude else False, 'longitude': str(partner.partner_longitude) if partner.partner_longitude else False, }) diff --git a/addons/website_hr_recruitment/models/hr_department.py b/addons/website_hr_recruitment/models/hr_department.py index 1a983340246..4e5befdc28d 100644 --- a/addons/website_hr_recruitment/models/hr_department.py +++ b/addons/website_hr_recruitment/models/hr_department.py @@ -1,14 +1,11 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models +from odoo import models, fields class Department(models.Model): _inherit = 'hr.department' - def name_get(self): - # Get department name using superuser, because model is not accessible - # for portal users - self_sudo = self.sudo() - return super(Department, self_sudo).name_get() + # Get department name using superuser, because model is not accessible for portal users + display_name = fields.Char(compute_sudo=True) diff --git a/addons/website_sale/models/product_public_category.py b/addons/website_sale/models/product_public_category.py index b32eef093d1..618c721370f 100644 --- a/addons/website_sale/models/product_public_category.py +++ b/addons/website_sale/models/product_public_category.py @@ -37,11 +37,9 @@ class ProductPublicCategory(models.Model): if not self._check_recursion(): raise ValueError(_('Error! You cannot create recursive categories.')) - def name_get(self): - res = [] + def _compute_display_name(self): for category in self: - res.append((category.id, " / ".join(category.parents_and_self.mapped('name')))) - return res + category.display_name = " / ".join(category.parents_and_self.mapped('name')) def _compute_parents_and_self(self): for category in self: diff --git a/addons/website_sale/models/product_ribbon.py b/addons/website_sale/models/product_ribbon.py index 9d23f9e054f..a8c36699322 100644 --- a/addons/website_sale/models/product_ribbon.py +++ b/addons/website_sale/models/product_ribbon.py @@ -8,8 +8,9 @@ class ProductRibbon(models.Model): _name = "product.ribbon" _description = 'Product ribbon' - def name_get(self): - return [(ribbon.id, '%s (#%d)' % (tools.html2plaintext(ribbon.html), ribbon.id)) for ribbon in self] + def _compute_display_name(self): + for ribbon in self: + ribbon.display_name = f'{tools.html2plaintext(ribbon.html)} (#{ribbon.id})' html = fields.Html(string='Ribbon html', required=True, translate=True, sanitize=False) bg_color = fields.Char(string='Ribbon background color', required=False) diff --git a/odoo/addons/base/models/ir_fields.py b/odoo/addons/base/models/ir_fields.py index ba7345a09f2..38c37e7e104 100644 --- a/odoo/addons/base/models/ir_fields.py +++ b/odoo/addons/base/models/ir_fields.py @@ -398,7 +398,7 @@ class IrFieldsConverter(models.AbstractModel): :param model: model to which the field belongs :param field: relational field for which references are provided :param subfield: a relational subfield allowing building of refs to - existing records: ``None`` for a name_get/name_search, + existing records: ``None`` for a name_search, ``id`` for an external id and ``.id`` for a database id :param value: value of the reference to match to an actual record @@ -542,7 +542,7 @@ class IrFieldsConverter(models.AbstractModel): :return: the record subfield to use for referencing and a list of warnings :rtype: str, list """ - # Can import by name_get, external id or database id + # Can import by display_name, external id or database id fieldset = set(record) if fieldset - REFERENCING_FIELDS: raise ValueError( diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index 9ebb847bdc1..6bafbf7d646 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -371,11 +371,11 @@ class IrModel(models.Model): @api.model def name_create(self, name): """ Infer the model from the name. E.g.: 'My New Model' should become 'x_my_new_model'. """ - vals = { + ir_model = self.create({ 'name': name, 'model': 'x_' + '_'.join(name.lower().split(' ')), - } - return self.create(vals).name_get()[0] + }) + return ir_model.id, ir_model.display_name def _reflect_model_params(self, model): """ Return the values to write to the database for the given model. """ @@ -1042,11 +1042,9 @@ class IrModelFields(models.Model): self.clear_caches() return res - def name_get(self): - res = [] + def _compute_display_name(self): for field in self: - res.append((field.id, '%s (%s)' % (field.field_description, field.model))) - return res + field.display_name = f'{field.field_description} ({field.model})' def _reflect_field_params(self, field, model_id): """ Return the values to write to the database for the given field. """ @@ -1998,22 +1996,14 @@ class IrModelData(models.Model): self._table, ['model', 'res_id']) return res - def name_get(self): - model_id_name = defaultdict(dict) # {res_model: {res_id: name}} - for xid in self: - model_id_name[xid.model][xid.res_id] = None - - # fill in model_id_name with name_get() of corresponding records - for model, id_name in model_id_name.items(): - try: - ng = self.env[model].browse(id_name).name_get() - id_name.update(ng) - except Exception: - pass - - # return results, falling back on complete_name - return [(xid.id, model_id_name[xid.model][xid.res_id] or xid.complete_name) - for xid in self] + def _compute_display_name(self): + for model, model_data_records in self.grouped('model').items(): + records = self.env[model].browse(model_data_records.mapped('res_id')) + for xid, target_record in zip(model_data_records, records): + try: + xid.display_name = target_record.display_name or xid.complete_name + except Exception: # pylint: disable=broad-except + xid.display_name = xid.complete_name # NEW V8 API @api.model diff --git a/odoo/addons/base/models/ir_qweb_fields.py b/odoo/addons/base/models/ir_qweb_fields.py index dbb2f47520b..c1b68aaeef6 100644 --- a/odoo/addons/base/models/ir_qweb_fields.py +++ b/odoo/addons/base/models/ir_qweb_fields.py @@ -777,16 +777,15 @@ class Contact(models.AbstractModel): opsep = Markup('
') value = value.sudo().with_context(show_address=True) - name_get = value.name_get()[0][1] # Avoid having something like: - # name_get = 'Foo\n \n' -> This is a res.partner with a name and no address + # display_name = 'Foo\n \n' -> This is a res.partner with a name and no address # That would return markup('
') as address. But there is no address set. - if any(elem.strip() for elem in name_get.split("\n")[1:]): - address = opsep.join(name_get.split("\n")[1:]).strip() + if any(elem.strip() for elem in value.display_name.split("\n")[1:]): + address = opsep.join(value.display_name.split("\n")[1:]).strip() else: address = '' val = { - 'name': name_get.split("\n")[0], + 'name': value.display_name.split("\n")[0], 'address': address, 'phone': value.phone, 'mobile': value.mobile, diff --git a/odoo/addons/base/models/ir_ui_menu.py b/odoo/addons/base/models/ir_ui_menu.py index 132ad93e1d2..038c879ab15 100644 --- a/odoo/addons/base/models/ir_ui_menu.py +++ b/odoo/addons/base/models/ir_ui_menu.py @@ -155,8 +155,9 @@ class IrUiMenu(models.Model): # to be consistent with search() above return len(self.search(domain, limit=limit)) - def name_get(self): - return [(menu.id, menu._get_full_name()) for menu in self] + def _compute_display_name(self): + for menu in self: + menu.display_name = menu._get_full_name() @api.model_create_multi def create(self, vals_list): diff --git a/odoo/addons/base/models/res_bank.py b/odoo/addons/base/models/res_bank.py index 3f43eaef8ae..73d40b189d9 100644 --- a/odoo/addons/base/models/res_bank.py +++ b/odoo/addons/base/models/res_bank.py @@ -30,12 +30,10 @@ class Bank(models.Model): active = fields.Boolean(default=True) bic = fields.Char('Bank Identifier Code', index=True, help="Sometimes called BIC or Swift.") - def name_get(self): - result = [] + def _compute_display_name(self): for bank in self: name = (bank.name or '') + (bank.bic and (' - ' + bank.bic) or '') - result.append((bank.id, name)) - return result + bank.display_name = name @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): @@ -113,9 +111,9 @@ class ResPartnerBank(models.Model): """ return 'bank' - def name_get(self): - return [(acc.id, '{} - {}'.format(acc.acc_number, acc.bank_id.name) if acc.bank_id else acc.acc_number) - for acc in self] + def _compute_display_name(self): + for acc in self: + acc.display_name = f'{acc.acc_number} - {acc.bank_id.name}' if acc.bank_id else acc.acc_number @api.model def _search(self, domain, offset=0, limit=None, order=None, access_rights_uid=None): diff --git a/odoo/addons/base/models/res_config.py b/odoo/addons/base/models/res_config.py index b23eda027e5..3dd883f15c1 100644 --- a/odoo/addons/base/models/res_config.py +++ b/odoo/addons/base/models/res_config.py @@ -639,12 +639,11 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin) return actions.read()[0] return {} - def name_get(self): - """ Override name_get method to return an appropriate configuration wizard + def _compute_display_name(self): + """ Override display_name method to return an appropriate configuration wizard name, and not the generated name.""" action = self.env['ir.actions.act_window'].search([('res_model', '=', self._name)], limit=1) - name = action.name or self._name - return [(record.id, name) for record in self] + self.display_name = action.name or self._name @api.model def get_option_path(self, menu_xml_id): diff --git a/odoo/addons/base/models/res_country.py b/odoo/addons/base/models/res_country.py index 94c44036116..fd4654e7250 100644 --- a/odoo/addons/base/models/res_country.py +++ b/odoo/addons/base/models/res_country.py @@ -193,8 +193,6 @@ class CountryState(models.Model): if state_id not in first_state_ids ] - def name_get(self): - result = [] + def _compute_display_name(self): for record in self: - result.append((record.id, "{} ({})".format(record.name, record.country_id.code))) - return result + record.display_name = f"{record.name} ({record.country_id.code})" diff --git a/odoo/addons/base/models/res_currency.py b/odoo/addons/base/models/res_currency.py index 3a1a553ee3d..a28d66a1a1a 100644 --- a/odoo/addons/base/models/res_currency.py +++ b/odoo/addons/base/models/res_currency.py @@ -156,9 +156,6 @@ class Currency(models.Model): for currency in self: currency.date = currency.rate_ids[:1].name - def name_get(self): - return [(currency.id, tools.ustr(currency.name)) for currency in self] - def amount_to_text(self, amount): self.ensure_one() def _num2words(number, lang): diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index 380068174eb..da51c8c358a 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -128,25 +128,23 @@ class PartnerCategory(models.Model): if not self._check_recursion(): raise ValidationError(_('You can not create recursive tags.')) - def name_get(self): + def _compute_display_name(self): """ Return the categories' display name, including their direct parent by default. """ - res = [] for category in self: names = [] current = category while current: names.append(current.name) current = current.parent_id - res.append((category.id, ' / '.join(reversed(names)))) - return res + category.display_name = ' / '.join(reversed(names)) @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): domain = domain or [] if name: - # Be sure name_search is symetric to name_get + # Be sure name_search is symetric to display_name name = name.split(' / ')[-1] domain = [('name', operator, name)] + domain return self._search(domain, limit=limit, order=order) @@ -165,8 +163,11 @@ class Partner(models.Model): _description = 'Contact' _inherit = ['format.address.mixin', 'avatar.mixin'] _name = "res.partner" - _order = "display_name, id" - _rec_names_search = ['display_name', 'email', 'ref', 'vat', 'company_registry'] # TODO vat must be sanitized the same way for storing/searching + _order = "complete_name, id" + _rec_names_search = ['complete_name', 'email', 'ref', 'vat', 'company_registry'] # TODO vat must be sanitized the same way for storing/searching + + # the partner types that must be added to a partner's complete name, like "Delivery" + _complete_name_displayed_types = ('invoice', 'delivery', 'other') def _default_category(self): return self.env['res.partner.category'].browse(self._context.get('category_id')) @@ -189,7 +190,7 @@ class Partner(models.Model): return values name = fields.Char(index=True, default_export_compatible=True) - display_name = fields.Char(compute='_compute_display_name', recursive=True, store=True, index=True) + complete_name = fields.Char(compute='_compute_complete_name', store=True, index=True) date = fields.Date(index=True) title = fields.Many2one('res.partner.title') parent_id = fields.Many2one('res.partner', string='Related Company', index=True) @@ -332,12 +333,22 @@ class Partner(models.Model): return "base/static/img/money.png" return super()._avatar_get_placeholder_path() - @api.depends('is_company', 'name', 'parent_id.display_name', 'type', 'company_name', 'commercial_company_name') - def _compute_display_name(self): - # retrieve name_get() without any fancy feature - names = dict(self.with_context({}).name_get()) + @api.depends('is_company', 'name', 'parent_id.name', 'type', 'company_name', 'commercial_company_name') + def _compute_complete_name(self): + displayed_types = self._complete_name_displayed_types + # determine the labels of partner types to be included + # as 'displayed_types' (without user lang to avoid context dependency) + type_description = dict(self._fields['type']._description_selection(self.with_context({}).env)) + for partner in self: - partner.display_name = names.get(partner.id) + name = partner.name or '' + if partner.company_name or partner.parent_id: + if not name and partner.type in displayed_types: + name = type_description[partner.type] + if not partner.is_company: + name = f"{partner.commercial_company_name or partner.sudo().parent_id.name}, {name}" + + partner.complete_name = name.strip() @api.depends('lang') def _compute_active_lang_count(self): @@ -803,39 +814,25 @@ class Partner(models.Model): 'target': 'new', } - def _get_contact_name(self, partner, name): - return "%s, %s" % (partner.commercial_company_name or partner.sudo().parent_id.name, name) - - def _get_name(self): - """ Utility method to allow name_get to be overrided without re-browse the partner """ - partner = self - name = partner.name or '' - - if partner.company_name or partner.parent_id: - if not name and partner.type in ['invoice', 'delivery', 'other']: - name = dict(self.fields_get(['type'])['type']['selection'])[partner.type] - if not partner.is_company: - name = self._get_contact_name(partner, name) - if self._context.get('show_address'): - name = name + "\n" + partner._display_address(without_company=True) - name = re.sub(r'\s+\n', '\n', name) - if self._context.get('partner_show_db_id'): - name = "%s (%s)" % (name, partner.id) - if self._context.get('address_inline'): - splitted_names = name.split("\n") - name = ", ".join([n for n in splitted_names if n.strip()]) - if self._context.get('show_email') and partner.email: - name = "%s <%s>" % (name, partner.email) - if self._context.get('show_vat') and partner.vat: - name = "%s ‒ %s" % (name, partner.vat) - return name.strip() - - def name_get(self): - res = [] + @api.depends('complete_name') + @api.depends_context('show_address', 'partner_show_db_id', 'address_inline', 'show_email', 'show_vat') + def _compute_display_name(self): for partner in self: - name = partner._get_name() - res.append((partner.id, name)) - return res + name = partner.complete_name + if partner._context.get('show_address'): + name = name + "\n" + partner._display_address(without_company=True) + name = re.sub(r'\s+\n', '\n', name) + if partner._context.get('partner_show_db_id'): + name = f"{name} ({partner.id})" + if partner._context.get('address_inline'): + splitted_names = name.split("\n") + name = ", ".join([n for n in splitted_names if n.strip()]) + if partner._context.get('show_email') and partner.email: + name = f"{name} <{partner.email}>" + if partner._context.get('show_vat') and partner.vat: + name = f"{name} ‒ {partner.vat}" + + partner.display_name = name.strip() def _parse_partner_name(self, text): """ Parse partner name (given by text) in order to find a name and an @@ -891,7 +888,7 @@ class Partner(models.Model): if email: # keep default_email in context create_values['email'] = email partner = self.create(create_values) - return partner.name_get()[0] + return partner.id, partner.display_name @api.model def _search(self, domain, offset=0, limit=None, order=None, access_rights_uid=None): diff --git a/odoo/addons/base/tests/test_deprecation.py b/odoo/addons/base/tests/test_deprecation.py index bb68d980c68..f1d7442827e 100644 --- a/odoo/addons/base/tests/test_deprecation.py +++ b/odoo/addons/base/tests/test_deprecation.py @@ -27,3 +27,12 @@ class TestModelDeprecations(TransactionCase): if module: msg += f" in {module}" self.fail(msg) + + def test_name_get(self): + for model_name, Model in self.registry.items(): + with self.subTest(model=model_name): + # name_get should exist but define by BaseModel + module = inspect.getmodule(Model.name_get) + if module.__name__ == 'odoo.models': + continue + self.fail(f"Deprecated name_get method found on {model_name} in {module.__name__}, you should override `_compute_display_name` instead") diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py index e4f651f0677..067674c0648 100644 --- a/odoo/addons/base/tests/test_expression.py +++ b/odoo/addons/base/tests/test_expression.py @@ -1127,7 +1127,7 @@ class TestQueries(TransactionCase): ) ) ) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): Model.search(domain) @@ -1139,7 +1139,7 @@ class TestQueries(TransactionCase): SELECT "res_partner"."id" FROM "res_partner" WHERE (("res_partner"."active" = %s) AND ("res_partner"."name"::text LIKE %s)) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): Model.search([('name', 'like', 'foo')]) @@ -1296,7 +1296,7 @@ class TestMany2one(TransactionCase): SELECT "res_partner"."id" FROM "res_partner" WHERE ("res_partner"."company_id" = %s) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id', '=', self.company.id)]) @@ -1308,7 +1308,7 @@ class TestMany2one(TransactionCase): FROM "res_company" WHERE ("res_company"."name"::text LIKE %s) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id.name', 'like', self.company.name)]) @@ -1324,7 +1324,7 @@ class TestMany2one(TransactionCase): WHERE ("res_partner"."name"::text LIKE %s) )) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)]) @@ -1340,7 +1340,7 @@ class TestMany2one(TransactionCase): FROM "res_country" WHERE ("res_country"."code"::text LIKE %s) ))) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([ '|', @@ -1359,7 +1359,7 @@ class TestMany2one(TransactionCase): FROM "res_company" WHERE (("res_company"."active" = %s) AND ("res_company"."name"::text LIKE %s)) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): company_ids = self.company._search([('name', 'like', self.company.name)], order='id') self.Partner.search([('company_id', 'in', company_ids)]) @@ -1375,7 +1375,7 @@ class TestMany2one(TransactionCase): ORDER BY "res_company"."id" LIMIT 1 )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): company_ids = self.company._search([('name', 'like', self.company.name)], order='id', limit=1) self.Partner.search([('company_id', 'in', company_ids)]) @@ -1390,7 +1390,7 @@ class TestMany2one(TransactionCase): SELECT "res_partner"."id" FROM "res_partner" WHERE ("res_partner"."company_id" IN %s) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): company_ids = self.company._search([('name', 'like', self.company.name)], order='id') company_ids = tuple(company_ids) @@ -1406,7 +1406,7 @@ class TestMany2one(TransactionCase): SELECT "res_partner"."id" FROM "res_partner" WHERE ("res_partner"."company_id" IN %s) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): companies = self.company.search([('name', 'like', self.company.name)], order='id') company_ids = companies._as_query(ordered=False) @@ -1424,7 +1424,7 @@ class TestMany2one(TransactionCase): LEFT JOIN "res_company" AS "res_partner__company_id" ON ("res_partner"."company_id" = "res_partner__company_id"."id") WHERE ("res_partner__company_id"."name"::text LIKE %s) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id.name', 'like', self.company.name)]) @@ -1438,7 +1438,7 @@ class TestMany2one(TransactionCase): FROM "res_partner" WHERE ("res_partner"."name"::text LIKE %s) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)]) @@ -1457,7 +1457,7 @@ class TestMany2one(TransactionCase): ("res_company"."partner_id" = "res_company__partner_id"."id") WHERE ("res_company__partner_id"."name"::text LIKE %s) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)]) @@ -1474,7 +1474,7 @@ class TestMany2one(TransactionCase): LEFT JOIN "res_partner" AS "res_partner__company_id__partner_id" ON ("res_partner__company_id"."partner_id" = "res_partner__company_id__partner_id"."id") WHERE ("res_partner__company_id__partner_id"."name"::text LIKE %s) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)]) @@ -1496,7 +1496,7 @@ class TestMany2one(TransactionCase): ("res_partner"."company_id" = "res_partner__company_id"."id") WHERE (("res_partner__company_id"."name"::text LIKE %s) OR ("res_partner__country_id"."code"::text LIKE %s)) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([ '|', @@ -1515,7 +1515,7 @@ class TestMany2one(TransactionCase): FROM "res_company" WHERE ("res_company"."name"::text LIKE %s) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id', 'like', self.company.name)]) @@ -1527,7 +1527,7 @@ class TestMany2one(TransactionCase): FROM "res_company" WHERE (("res_company"."name"::text not like %s) OR "res_company"."name" IS NULL)) ) OR "res_partner"."company_id" IS NULL) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('company_id', 'not like', "blablabla")]) @@ -1556,7 +1556,7 @@ class TestOne2many(TransactionCase): WHERE ("res_partner"."id" IN ( SELECT "partner_id" FROM "res_partner_bank" WHERE "id" IN %s )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)]) @@ -1568,7 +1568,7 @@ class TestOne2many(TransactionCase): FROM "res_partner_bank" WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')]) @@ -1584,7 +1584,7 @@ class TestOne2many(TransactionCase): WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s) )) AND "res_partner"."parent_id" IS NOT NULL )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')]) @@ -1601,7 +1601,7 @@ class TestOne2many(TransactionCase): WHERE ("res_partner"."id" IN ( SELECT "partner_id" FROM "res_partner_bank" WHERE "id" IN %s )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)]) @@ -1613,7 +1613,7 @@ class TestOne2many(TransactionCase): FROM "res_partner_bank" WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')]) @@ -1629,7 +1629,7 @@ class TestOne2many(TransactionCase): FROM "res_partner_bank" WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s) ))) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([ ('bank_ids.sanitized_acc_number', 'like', '12'), @@ -1647,7 +1647,7 @@ class TestOne2many(TransactionCase): FROM "res_partner_bank" WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s))) ))) - ORDER BY "res_partner"."display_name", + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')]) @@ -1678,7 +1678,7 @@ class TestOne2many(TransactionCase): ) )) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('child_ids.bank_ids.id', 'in', self.partner.bank_ids.ids)]) @@ -1704,7 +1704,7 @@ class TestOne2many(TransactionCase): "res_partner__state_id__country_id"."code"::text LIKE %s )) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('child_ids.state_id.country_id.code', 'like', 'US')]) @@ -1719,7 +1719,7 @@ class TestOne2many(TransactionCase): FROM "res_partner_bank" WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s) )) - ORDER BY "res_partner"."display_name", "res_partner"."id" + ORDER BY "res_partner"."complete_name", "res_partner"."id" ''']): self.Partner.search([('bank_ids', 'like', '12')]) diff --git a/odoo/addons/base/tests/test_res_config.py b/odoo/addons/base/tests/test_res_config.py index b0ad82ed070..4813a38cf0b 100644 --- a/odoo/addons/base/tests/test_res_config.py +++ b/odoo/addons/base/tests/test_res_config.py @@ -225,7 +225,7 @@ class TestResConfigExecute(TransactionCase): settings.set_values() # Check user has access to all models of relational fields in view - # because the webclient makes a name_get request for all specified records + # because the webclient makes a read of display_name request for all specified records # even if they are not shown to the user. settings_view_arch = etree.fromstring(settings.get_view(view_id=self.settings_view.id)['arch']) seen_fields = set() diff --git a/odoo/addons/base/tests/test_res_partner.py b/odoo/addons/base/tests/test_res_partner.py index 6ad7c7989f3..74972a2632f 100644 --- a/odoo/addons/base/tests/test_res_partner.py +++ b/odoo/addons/base/tests/test_res_partner.py @@ -27,21 +27,21 @@ class TestPartner(TransactionCase): ns_res = self.env['res.partner'].with_user(public_user).sudo().name_search('Vlad', args=[('user_ids.email', 'ilike', 'vlad')]) self.assertEqual(set(i[0] for i in ns_res), set(test_user.partner_id.ids)) - def test_name_get(self): - """ Check name_get on partner, especially with different context - Check name_get correctly return name with context. """ + def test_display_name(self): + """ Check display_name on partner, especially with different context + Check display_name correctly return name with context. """ test_partner_jetha = self.env['res.partner'].create({'name': 'Jethala', 'street': 'Powder gali', 'street2': 'Gokuldham Society'}) test_partner_bhide = self.env['res.partner'].create({'name': 'Atmaram Bhide'}) - res_jetha = test_partner_jetha.with_context(show_address=1).name_get() - self.assertEqual(res_jetha[0][1], "Jethala\nPowder gali\nGokuldham Society", "name should contain comma separated name and address") - res_bhide = test_partner_bhide.with_context(show_address=1).name_get() - self.assertEqual(res_bhide[0][1], "Atmaram Bhide", "name should contain only name if address is not available, without extra commas") + res_jetha = test_partner_jetha.with_context(show_address=1).display_name + self.assertEqual(res_jetha, "Jethala\nPowder gali\nGokuldham Society", "name should contain comma separated name and address") + res_bhide = test_partner_bhide.with_context(show_address=1).display_name + self.assertEqual(res_bhide, "Atmaram Bhide", "name should contain only name if address is not available, without extra commas") - res_jetha = test_partner_jetha.with_context(show_address=1, address_inline=1).name_get() - self.assertEqual(res_jetha[0][1], "Jethala, Powder gali, Gokuldham Society", "name should contain comma separated name and address") - res_bhide = test_partner_bhide.with_context(show_address=1, address_inline=1).name_get() - self.assertEqual(res_bhide[0][1], "Atmaram Bhide", "name should contain only name if address is not available, without extra commas") + res_jetha = test_partner_jetha.with_context(show_address=1, address_inline=1).display_name + self.assertEqual(res_jetha, "Jethala, Powder gali, Gokuldham Society", "name should contain comma separated name and address") + res_bhide = test_partner_bhide.with_context(show_address=1, address_inline=1).display_name + self.assertEqual(res_bhide, "Atmaram Bhide", "name should contain only name if address is not available, without extra commas") def test_company_change_propagation(self): """ Check propagation of company_id across children """ @@ -182,8 +182,7 @@ class TestPartner(TransactionCase): partner_merge_wizard = self.env['base.partner.merge.automatic.wizard'].with_context( {'partner_show_db_id': True, 'default_dst_partner_id': test_partner}).new() self.assertEqual( - partner_merge_wizard.dst_partner_id.name_get(), - [(test_partner.id, expected_partner_name)], + partner_merge_wizard.dst_partner_id.display_name, expected_partner_name, "'Destination Contact' name should contain db ID in brackets" ) diff --git a/odoo/addons/base/tests/test_translate.py b/odoo/addons/base/tests/test_translate.py index beda9616116..e542a4116e9 100644 --- a/odoo/addons/base/tests/test_translate.py +++ b/odoo/addons/base/tests/test_translate.py @@ -368,7 +368,7 @@ class TestTranslation(TransactionCase): def test_101_create_translated_record(self): category = self.customers.with_context({}) - self.assertEqual(category.name, 'Customers', "Error in basic name_get") + self.assertEqual(category.name, 'Customers', "Error in basic name") category_fr = category.with_context({'lang': 'fr_FR'}) self.assertEqual(category_fr.name, 'Clients', "Translation not found") diff --git a/odoo/addons/base/views/res_partner_views.xml b/odoo/addons/base/views/res_partner_views.xml index 5ede699ccf2..39487be4215 100644 --- a/odoo/addons/base/views/res_partner_views.xml +++ b/odoo/addons/base/views/res_partner_views.xml @@ -430,7 +430,7 @@ + filter_domain="['|', '|', '|', '|', ('complete_name', 'ilike', self), ('ref', '=', self), ('email', 'ilike', self), ('vat', 'ilike', self), ('company_registry', 'ilike', self)]"/> diff --git a/odoo/addons/test_access_rights/tests/test_check_access.py b/odoo/addons/test_access_rights/tests/test_check_access.py index 7cf3d383e30..10b08ff72fa 100644 --- a/odoo/addons/test_access_rights/tests/test_check_access.py +++ b/odoo/addons/test_access_rights/tests/test_check_access.py @@ -51,8 +51,4 @@ class TestAccess(odoo.tests.HttpCase): }) document = self.env['test_access_right.ticket'].with_user(no_access_user) res = document.sudo().name_search('Need help here') - #Invalide cache in case the name is already there - #and will not trigget check_access_rights when - #the name_get will access the name - self.document.invalidate_model(['name']) self.assertEqual(res[0][1], "Need help here") diff --git a/odoo/addons/test_impex/models.py b/odoo/addons/test_impex/models.py index d249460105b..136798c7bea 100644 --- a/odoo/addons/test_impex/models.py +++ b/odoo/addons/test_impex/models.py @@ -46,8 +46,9 @@ for name, field in MODELS: const = fields.Integer(default=4) value = field - def name_get(self): - return [(record.id, "%s:%s" % (self._name, record.value)) for record in self] + def _compute_display_name(self): + for record in self: + record.display_name = f"{self._name}:{record.value}" @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): @@ -59,7 +60,7 @@ for name, field in MODELS: class One2ManyChild(models.Model): _name = 'export.one2many.child' _description = 'Export One to Many Child' - # FIXME: orm.py:1161, fix to name_get on m2o field + # FIXME: orm.py:1161, fix to display_name on m2o field _rec_name = 'value' parent_id = fields.Many2one('export.one2many') @@ -67,8 +68,9 @@ class One2ManyChild(models.Model): m2o = fields.Many2one('export.integer') value = fields.Integer() - def name_get(self): - return [(record.id, "%s:%s" % (self._name, record.value)) for record in self] + def _compute_display_name(self): + for record in self: + record.display_name = f"{self._name}:{record.value}" @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): @@ -91,7 +93,7 @@ class One2ManyMultiple(models.Model): class One2ManyChildMultiple(models.Model): _name = 'export.one2many.multiple.child' - # FIXME: orm.py:1161, fix to name_get on m2o field + # FIXME: orm.py:1161, fix to display_name on m2o field _rec_name = 'value' _description = 'Export One To Many Multiple Child' @@ -99,8 +101,9 @@ class One2ManyChildMultiple(models.Model): str = fields.Char() value = fields.Integer() - def name_get(self): - return [(record.id, "%s:%s" % (self._name, record.value)) for record in self] + def _compute_display_name(self): + for record in self: + record.display_name = f"{self._name}:{record.value}" class One2ManyChild1(models.Model): @@ -118,14 +121,15 @@ class One2ManyChild2(models.Model): class Many2ManyChild(models.Model): _name = 'export.many2many.other' _description = 'Export Many to Many Other' - # FIXME: orm.py:1161, fix to name_get on m2o field + # FIXME: orm.py:1161, fix to display_name on m2o field _rec_name = 'value' str = fields.Char() value = fields.Integer() - def name_get(self): - return [(record.id, "%s:%s" % (self._name, record.value)) for record in self] + def _compute_display_name(self): + for record in self: + record.display_name = f"{self._name}:{record.value}" @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): diff --git a/odoo/addons/test_impex/tests/test_export.py b/odoo/addons/test_impex/tests/test_export.py index cf10c68acca..0e3546b0265 100644 --- a/odoo/addons/test_impex/tests/test_export.py +++ b/odoo/addons/test_impex/tests/test_export.py @@ -257,7 +257,7 @@ class test_datetime(CreatorCase): def test_tz(self): """ Export converts the value in the user's TZ - .. note:: on the other hand, export uses user lang for name_get + .. note:: on the other hand, export uses user lang for display_name """ self.assertEqual( self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}), @@ -326,13 +326,12 @@ class test_m2o(CreatorCase): [['']]) def test_basic(self): - """ Exported value is the name_get of the related object + """ Exported value is the display_name of the related object """ record = self.env['export.integer'].create({'value': 42}) - name = dict(record.name_get())[record.id] self.assertEqual( self.export(record.id), - [[name]]) + [[record.display_name]]) def test_path(self): """ Can recursively export fields of m2o via path @@ -389,7 +388,7 @@ class test_o2m(CreatorCase): def test_single(self): self.assertEqual( self.export([Command.create({'value': 42})]), - # name_get result + # display_name result [[u'export.one2many.child:42']]) def test_single_subfield(self): @@ -600,7 +599,7 @@ class test_m2m(CreatorCase): def test_single(self): self.assertEqual( self.export([Command.create({'value': 42})]), - # name_get result + # display_name result [[u'export.many2many.other:42']]) def test_single_subfield(self): diff --git a/odoo/addons/test_impex/tests/test_load.py b/odoo/addons/test_impex/tests/test_load.py index 2a6b506c47d..69e57929594 100644 --- a/odoo/addons/test_impex/tests/test_load.py +++ b/odoo/addons/test_impex/tests/test_load.py @@ -68,7 +68,7 @@ class ImporterCase(common.TransactionCase): return d['name'] name = record.display_name - # fix dotted name_get results, otherwise xid lookups blow up + # fix dotted display_name results, otherwise xid lookups blow up name = name.replace('.', '-') ModelData.create({ 'name': name, @@ -562,14 +562,11 @@ class test_m2o(ImporterCase): # create integer objects record1 = self.env['export.integer'].create({'value': 42}) record2 = self.env['export.integer'].create({'value': 36}) - # get its name - name1 = dict(record1.name_get())[record1.id] - name2 = dict(record2.name_get())[record2.id] # preheat the oven for _ in range(5): with contextlib.closing(self.env.cr.savepoint(flush=False)): - self.import_(['value'], [[name1], [name1], [name2]]) + self.import_(['value'], [[record1.display_name], [record1.display_name], [record2.display_name]]) # 1 x SAVEPOINT load # 3 x name_search @@ -580,19 +577,19 @@ class test_m2o(ImporterCase): # => 10 with self.assertQueryCount(8): result = self.import_(['value'], [ - # import by name_get - [name1], - [name1], - [name2], + # import by display_name + [record1.display_name], + [record1.display_name], + [record2.display_name], ]) self.assertFalse(result['messages']) self.assertEqual(len(result['ids']), 3) # correct ids assigned to corresponding records self.assertEqual([ - (record1.id, name1), - (record1.id, name1), - (record2.id, name2),], + (record1.id, record1.display_name), + (record1.id, record1.display_name), + (record2.id, record2.display_name),], values(self.read())) def test_by_xid(self): @@ -616,19 +613,17 @@ class test_m2o(ImporterCase): def test_by_names(self): record1 = self.env['export.integer'].create({'value': 42}) record2 = self.env['export.integer'].create({'value': 42}) - name1 = dict(record1.name_get())[record1.id] - name2 = dict(record2.name_get())[record2.id] # names should be the same - self.assertEqual(name1, name2) + self.assertEqual(record1.display_name, record2.display_name) - result = self.import_(['value'], [[name2]]) + result = self.import_(['value'], [[record2.display_name]]) self.assertEqual( result['messages'], [message(u"Found multiple matches for value 'export.integer:42' in field 'Value' (2 matches)", type='warning')]) self.assertEqual(len(result['ids']), 1) self.assertEqual([ - (record1.id, name1) + (record1.id, record1.display_name) ], values(self.read())) def test_fail_by_implicit_id(self): @@ -859,7 +854,7 @@ class test_m2m(ImporterCase): class test_o2m(ImporterCase): model_name = 'export.one2many' - def test_name_get(self): + def test_display_name(self): s = u'Java is a DSL for taking large XML files and converting them ' \ u'to stack traces' result = self.import_( diff --git a/odoo/addons/test_new_api/models/test_unity_read.py b/odoo/addons/test_new_api/models/test_unity_read.py index 8d5939ea5a2..0c827138511 100644 --- a/odoo/addons/test_new_api/models/test_unity_read.py +++ b/odoo/addons/test_new_api/models/test_unity_read.py @@ -43,15 +43,14 @@ class Person(models.Model): lesson_ids = fields.Many2many('test_new_api.lesson', 'course_id') employer_id = fields.Many2one('test_new_api.employer') - def name_get(self): + def _compute_display_name(self): """ use to check that a context has can still have an impact when reading the names of a many2one """ - return [(record.id, - ("particular " if 'particular' in self.env.context else "") + - record.name + - (" special" if 'special' in self.env.context else "")) - for record in self] + particular = "particular " if 'particular' in self.env.context else "" + special = " special" if 'special' in self.env.context else "" + for record in self: + record.display_name = f"{particular}{record.name}{special}" class Employer(models.Model): _name = 'test_new_api.employer' diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 16ee6372441..62321733efd 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -106,7 +106,7 @@ class TestOnChange(SavepointCaseWithUserDemo): self.env.invalidate_all() result = Category.onchange(values, 'parent', field_onchange).get('value', {}) self.assertIn('root_categ', result) - self.assertEqual(result['root_categ'], root.name_get()[0]) + self.assertEqual(result['root_categ'], (root.id, root.display_name)) values.update(result) values['parent'] = False @@ -160,21 +160,21 @@ class TestOnChange(SavepointCaseWithUserDemo): Command.update(message1.id, { 'name': "[%s] %s" % ("Foo", USER.name), 'body': "ABC", - 'author': USER.name_get()[0], + 'author': (USER.id, USER.display_name), 'size': 3, 'important': False, }), Command.update(message2.id, { 'name': "[%s] %s" % ("Foo", USER.name), 'body': "XYZ", # this must be sent back - 'author': USER.name_get()[0], + 'author': (USER.id, USER.display_name), 'size': 3, 'important': False, }), Command.create({ 'name': "[%s] %s" % ("Foo", USER.name), 'body': "ABC", - 'author': USER.name_get()[0], + 'author': (USER.id, USER.display_name), 'size': 3, 'important': False, }), @@ -231,7 +231,7 @@ class TestOnChange(SavepointCaseWithUserDemo): (0, REFERENCE, { 'name': "[%s] %s" % ("Foo", USER.name), 'body': BODY, - 'author': USER.name_get()[0], + 'author': (USER.id, USER.display_name), 'size': len(BODY), 'important': False, }), @@ -479,7 +479,7 @@ class TestOnChange(SavepointCaseWithUserDemo): [Command.clear(), Command.update(email.id, { 'name': u'[Foo Bar] %s' % USER.name, 'body': BODY, - 'author': USER.name_get()[0], + 'author': (USER.id, USER.display_name), 'size': len(BODY), 'important': True, 'email_to': demo.email, @@ -500,7 +500,7 @@ class TestOnChange(SavepointCaseWithUserDemo): onchange_result = { 'message_name': 'Hey dude!', - 'message_currency': self.env.user.name_get()[0], + 'message_currency': (self.env.user.id, self.env.user.display_name), } self.env.invalidate_all() @@ -625,7 +625,6 @@ class TestOnChange(SavepointCaseWithUserDemo): record = form.save() self.assertEqual(record.name, False) self.assertEqual(record.display_name, "") - self.assertEqual(record.name_get(), [(record.id, "")]) class TestComputeOnchange(common.TransactionCase): diff --git a/odoo/addons/test_new_api/tests/test_onchange2.py b/odoo/addons/test_new_api/tests/test_onchange2.py index 9f020a87078..7509823eb7f 100644 --- a/odoo/addons/test_new_api/tests/test_onchange2.py +++ b/odoo/addons/test_new_api/tests/test_onchange2.py @@ -705,7 +705,6 @@ class TestOnchange2(SavepointCaseWithUserDemo): record = form.save() self.assertEqual(record.name, False) self.assertEqual(record.display_name, "") - self.assertEqual(record.name_get(), [(record.id, "")]) def test_reading_one2many_and_inverse_is_not_supported(self): # when reading a relation one2many, sometime the relation itself defines @@ -759,7 +758,6 @@ class TestOnchange2(SavepointCaseWithUserDemo): 'root_categ': {'id': root.id, 'display_name': root.name, 'color': root.color}, }) - @skip("read() on many2one fields currently use name_get() instead of field 'display_name'") def test_one2many_field_with_context_many2one(self): """ test many2one fields with a context on their one2many container field """ multi = self.env['test_new_api.multi'].create({}) @@ -791,8 +789,8 @@ class TestOnchange2(SavepointCaseWithUserDemo): 'name': 'A partner', 'email': 'foo@example.com', }) - display_name = partner.name_get()[0][1] - display_name_email = partner.with_context(show_email=True).name_get()[0][1] + display_name = partner.display_name + display_name_email = partner.with_context(show_email=True).display_name self.assertNotEqual(display_name_email, display_name) # modify 'partner' diff --git a/odoo/addons/test_new_api/tests/test_properties.py b/odoo/addons/test_new_api/tests/test_properties.py index 0b7ccd75a38..525af4240fd 100644 --- a/odoo/addons/test_new_api/tests/test_properties.py +++ b/odoo/addons/test_new_api/tests/test_properties.py @@ -524,12 +524,12 @@ class PropertiesCase(TestPropertiesMixin): msg='When reading many2one default, it should return the display name', ) - # read the default many2one and deactivate the name_get + # read the default many2one and deactivate the display_name read_values = self.discussion_1.read(['attributes_definition'], load=None)[0]['attributes_definition'] self.assertEqual( read_values[0]['default'], self.partner.id, - msg='If the name_get is deactivate, it should not return the display name', + msg='If the display_name is deactivate, it should not return the display name', ) message = self.env['test_new_api.message'].create({ @@ -609,7 +609,7 @@ class PropertiesCase(TestPropertiesMixin): In comparison with a simple "record.properties", the read method should not record a recordset for the many2one, but a tuple with the record id and - the record name_get. + the record display_name. """ properties_values = (self.message_1 | self.message_3).read(['attributes']) @@ -646,13 +646,13 @@ class PropertiesCase(TestPropertiesMixin): msg='Definition must be present when reading child') self.assertEqual(many2one_property['value'], (self.partner.id, self.partner.display_name)) - # disable the name_get + # disable the display_name properties_values = (self.message_1 | self.message_3).read(['attributes'], load=None) many2one_property = properties_values[0]['attributes'][1] self.assertEqual( many2one_property['value'], self.partner.id, - msg='If name_get is disable, should only return the record id') + msg='If display_name is disable, should only return the record id') def test_properties_field_many2one_basic(self): """Test the basic (read, write...) of the many2one property.""" @@ -698,7 +698,7 @@ class PropertiesCase(TestPropertiesMixin): with self.assertQueryCount(4): # 1 query to read the field # 1 query to read the definition - # 2 queries to check if the many2one still exists / name_get + # 2 queries to check if the many2one still exists / display_name self.assertFalse(self.message_2.read(['attributes'])[0]['attributes'][0]['value']) # remove the partner, and use the read method @@ -1072,7 +1072,7 @@ class PropertiesCase(TestPropertiesMixin): "string": "Partners", "type": "many2many", "comodel": "test_new_api.partner", - "value": partners[:10].name_get(), + "value": list(zip(partners[:10]._ids, partners[:10].mapped('display_name'))), } ] attributes = self.message_1.read(['attributes'], load=None)[0]['attributes'] @@ -1086,7 +1086,7 @@ class PropertiesCase(TestPropertiesMixin): partners[5].unlink() with self.assertQueryCount(5): properties = self.message_1.read(['attributes'])[0]['attributes'] - self.assertEqual(properties[0]['value'], partners[6:10].name_get()) + self.assertEqual(properties[0]['value'], list(zip(partners[6:10]._ids, partners[6:10].mapped('display_name')))) # need to wait next write to clean data in database # a single read won't clean the removed many2many @@ -1097,7 +1097,7 @@ class PropertiesCase(TestPropertiesMixin): sql_values = self._get_sql_properties(self.message_1) self.assertEqual(sql_values, {'moderator_partner_ids': partners[6:10].ids}) - # read and disable name_get + # read and disable display_name properties = self.message_1.read(['attributes'], load=None)[0]['attributes'] self.assertEqual( properties[0]['value'], diff --git a/odoo/addons/test_performance/tests/test_performance.py b/odoo/addons/test_performance/tests/test_performance.py index b2bf41ac164..f15ff2bb85b 100644 --- a/odoo/addons/test_performance/tests/test_performance.py +++ b/odoo/addons/test_performance/tests/test_performance.py @@ -614,7 +614,7 @@ class TestPerformance(SavepointCaseWithUserDemo): } for partner in partners] @users('__system__', 'demo') - def test_read_group_with_name_get(self): + def test_read_group_with_display_name(self): model = self.env['test_performance.base'] expected = self.expected_read_group() # use read_group and check the expected result diff --git a/odoo/addons/test_read_group/tests/test_private_read_group.py b/odoo/addons/test_read_group/tests/test_private_read_group.py index 09b223d8e77..1dd73ff40c5 100644 --- a/odoo/addons/test_read_group/tests/test_private_read_group.py +++ b/odoo/addons/test_read_group/tests/test_private_read_group.py @@ -46,10 +46,10 @@ class TestPrivateReadGroup(common.TransactionCase): ON ("test_read_group_aggregate"."partner_id" = "test_read_group_aggregate__partner_id"."id") GROUP BY "test_read_group_aggregate"."key", "test_read_group_aggregate"."partner_id", - "test_read_group_aggregate__partner_id"."display_name", + "test_read_group_aggregate__partner_id"."complete_name", "test_read_group_aggregate__partner_id"."id" ORDER BY "test_read_group_aggregate"."key" ASC, - "test_read_group_aggregate__partner_id"."display_name", + "test_read_group_aggregate__partner_id"."complete_name", "test_read_group_aggregate__partner_id"."id" """]): self.assertEqual( @@ -152,9 +152,9 @@ class TestPrivateReadGroup(common.TransactionCase): ON ("test_read_group_aggregate"."partner_id" = "test_read_group_aggregate__partner_id"."id") GROUP BY "test_read_group_aggregate"."display_name", "test_read_group_aggregate"."partner_id", - "test_read_group_aggregate__partner_id"."display_name", + "test_read_group_aggregate__partner_id"."complete_name", "test_read_group_aggregate__partner_id"."id" - ORDER BY "test_read_group_aggregate__partner_id"."display_name" DESC, + ORDER BY "test_read_group_aggregate__partner_id"."complete_name" DESC, "test_read_group_aggregate__partner_id"."id" DESC """]): result = Model._read_group( diff --git a/odoo/fields.py b/odoo/fields.py index 91f9f828f07..a228ce66f3f 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -941,14 +941,14 @@ class Field(MetaField('DummyField', (object,), {})): convert = self.convert_to_record return [convert(value, record) for value, record in zip(values, records)] - def convert_to_read(self, value, record, use_name_get=True): + def convert_to_read(self, value, record, use_display_name=True): """ Convert ``value`` from the record format to the format returned by method :meth:`BaseModel.read`. :param value: :param record: - :param bool use_name_get: when True, the value's display name will be - computed using :meth:`BaseModel.name_get`, if relevant for the field + :param bool use_display_name: when True, the value's display name will be + computed using `display_name`, if relevant for the field """ return False if value is None else value @@ -1441,7 +1441,7 @@ class Integer(Field): def convert_to_record(self, value, record): return value or 0 - def convert_to_read(self, value, record, use_name_get=True): + def convert_to_read(self, value, record, use_display_name=True): # Integer values greater than 2^31-1 are not supported in pure XMLRPC, # so we have to pass them as floats :-( if value and value > MAXINT: @@ -1642,7 +1642,7 @@ class Monetary(Field): def convert_to_record(self, value, record): return value or 0.0 - def convert_to_read(self, value, record, use_name_get=True): + def convert_to_read(self, value, record, use_display_name=True): return value def convert_to_write(self, value, record): @@ -2028,8 +2028,8 @@ class Html(_String): r = r.decode() return r and Markup(r) - def convert_to_read(self, value, record, use_name_get=True): - r = super().convert_to_read(value, record, use_name_get) + def convert_to_read(self, value, record, use_display_name=True): + r = super().convert_to_read(value, record, use_display_name) if isinstance(r, bytes): r = r.decode() return r and Markup(r) @@ -2778,7 +2778,7 @@ class Reference(Selection): return record.env[res_model].browse(int(res_id)) return None - def convert_to_read(self, value, record, use_name_get=True): + def convert_to_read(self, value, record, use_display_name=True): return "%s,%s" % (value._name, value.id) if value else False def convert_to_export(self, value, record): @@ -2993,9 +2993,9 @@ class Many2one(_Relational): ids = tuple(unique(id_ for id_ in values if id_ is not None)) return records.pool[self.comodel_name](records.env, ids, prefetch_ids) - def convert_to_read(self, value, record, use_name_get=True): - if use_name_get and value: - # evaluate name_get() as superuser, because the visibility of a + def convert_to_read(self, value, record, use_display_name=True): + if use_display_name and value: + # evaluate display_name as superuser, because the visibility of a # many2one field value (id and name) depends on the current record's # access rights, and not the value's access rights. try: @@ -3304,10 +3304,10 @@ class Properties(Field): # 'value': [1337, 'Bob'], # }] # - def convert_to_read(self, value, record, use_name_get=True): - return self.convert_to_read_multi([value], record, use_name_get)[0] + def convert_to_read(self, value, record, use_display_name=True): + return self.convert_to_read_multi([value], record, use_display_name)[0] - def convert_to_read_multi(self, values, records, use_name_get=True): + def convert_to_read_multi(self, values, records, use_display_name=True): if not records: return values assert len(values) == len(records) @@ -3322,13 +3322,13 @@ class Properties(Field): assert isinstance(value, dict), f"Wrong type {value!r}" result.append(self._dict_to_list(value, definition)) - res_ids_per_model = self._get_res_ids_per_model(records, result, use_name_get) + res_ids_per_model = self._get_res_ids_per_model(records, result, use_display_name) # value is in record format for value in result: self._parse_json_types(value, records.env, res_ids_per_model) - if use_name_get: + if use_display_name: for value in result: self._add_display_name(value, records.env) @@ -3353,7 +3353,7 @@ class Properties(Field): record._cache[self.definition_record] = snapshot[self.definition_record].id or None return super().convert_to_onchange(value, record, names) - def _get_res_ids_per_model(self, records, values_list, use_name_get=True): + def _get_res_ids_per_model(self, records, values_list, use_display_name=True): """Read everything needed in batch for the given records. To retrieve relational properties names, or to check their existence, @@ -3390,7 +3390,7 @@ class Properties(Field): recs = records.env[model].browse(ids).exists() res_ids_per_model[model] = set(recs.ids) - if use_name_get: + if use_display_name: for record in recs: # read a field to pre-fetch the recordset try: @@ -3511,7 +3511,7 @@ class Properties(Field): @classmethod def _add_display_name(cls, values_list, env, value_keys=('value', 'default')): - """Add the "name_get" for each many2one / many2many properties. + """Add the "display_name" for each many2one / many2many properties. Modify in place "values_list". @@ -3760,7 +3760,7 @@ class PropertiesDefinition(Field): This method accepts a list properties definition. The relational properties (many2one / many2many) default value - might contain the name_get of those records (and will be removed). + might contain the display_name of those records (and will be removed). [{ 'name': '3adf37f3258cfe40', @@ -3865,12 +3865,12 @@ class PropertiesDefinition(Field): return result - def convert_to_read(self, value, record, use_name_get=True): + def convert_to_read(self, value, record, use_display_name=True): # record format -> read format (list of dicts with display names) if not value: return value - if use_name_get: + if use_display_name: Properties._add_display_name(value, record.env, value_keys=('default',)) return value @@ -4146,7 +4146,7 @@ class _RelationalMulti(_Relational): corecords = corecords.filtered(Comodel._active_name).with_prefetch(prefetch_ids) return corecords - def convert_to_read(self, value, record, use_name_get=True): + def convert_to_read(self, value, record, use_display_name=True): return value.ids def convert_to_write(self, value, record): @@ -4191,7 +4191,7 @@ class _RelationalMulti(_Relational): raise ValueError("Wrong value for %s: %s" % (self, value)) def convert_to_export(self, value, record): - return ','.join(name for id, name in value.name_get()) if value else '' + return ','.join(value.mapped('display_name')) if value else '' def convert_to_display_name(self, value, record): raise NotImplementedError() diff --git a/odoo/models.py b/odoo/models.py index 23d4bbef4dc..71b30d62e92 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -1039,7 +1039,7 @@ class BaseModel(metaclass=MetaModel): break if index is None: # not found anything, assume we just want the - # name_get in the first column + # display_name in the first column name = None index = i @@ -1279,7 +1279,7 @@ class BaseModel(metaclass=MetaModel): The following sub-fields may be set on the record (by key): - * None is the name_get for the record (to use with name_create/name_search) + * None is the display_name for the record (to use with name_create/name_search) * "id" is the External ID for the record * ".id" is the Database ID for the record """ @@ -1323,7 +1323,7 @@ class BaseModel(metaclass=MetaModel): comodel = self.env[fields[relfield].comodel_name] # get only cells for this sub-field, should be strictly - # non-empty, field path [None] is for name_get field + # non-empty, field path [None] is for display_name field indices, subfields = zip(*((index, fnames[1:] or [None]) for index, fnames in enumerate(fields_) if fnames[0] == relfield)) @@ -1604,18 +1604,18 @@ class BaseModel(metaclass=MetaModel): def _compute_display_name(self): """Compute the value of the `display_name` field. - In general `display_name` is equal to calling `name_get()[0][1]`. - - In that case, it is recommended to use `display_name` to uniformize the - code and to potentially take advantage of prefetch when applicable. - - However some models might override this method. For them, the behavior - might differ, and it is important to select which of `display_name` or - `name_get()[0][1]` to call depending on the desired result. + The `display_name` field is a textual representation of the record. + This method can be overridden to change the representation. If needed, + it can be made field-dependent using :attr:`~odoo.api.depends` and + context-dependent using :attr:`~odoo.api.depends_context`. """ - names = dict(self.name_get()) - for record in self: - record.display_name = names.get(record.id) + if self._rec_name: + convert = self._fields[self._rec_name].convert_to_display_name + for record in self: + record.display_name = convert(record[self._rec_name], record) + else: + for record in self: + record.display_name = f"{record._name},{record.id}" def name_get(self): """Returns a textual representation for the records in ``self``, with @@ -1628,20 +1628,14 @@ class BaseModel(metaclass=MetaModel): :attr:`~.display_name` it is important that it resets to the "default" behaviour if the context keys are empty / missing. + .. deprecated:: 17.0 + Deprecated method, read([`display_name`]) instead + :return: list of pairs ``(id, text_repr)`` for each record :rtype: list[(int, str)] """ - result = [] - name = self._rec_name - if name in self._fields: - convert = self._fields[name].convert_to_display_name - for record in self: - result.append((record.id, convert(record[name], record) or "")) - else: - for record in self: - result.append((record.id, "%s,%s" % (record._name, record.id))) - - return result + warnings.warn("Since 17.0, deprecated method, read display_name instead", DeprecationWarning, 2) + return [(record.id, record.display_name) for record in self] @api.model def name_create(self, name): @@ -1656,11 +1650,11 @@ class BaseModel(metaclass=MetaModel): :param name: display name of the record to create :rtype: tuple - :return: the :meth:`~.name_get` pair value of the created record + :return: the (id, display_name) pair value of the created record """ if self._rec_name: record = self.create({self._rec_name: name}) - return record.name_get()[0] + return record.id, record.display_name else: _logger.warning("Cannot execute name_create, no _rec_name defined on %s", self._name) return False @@ -1675,11 +1669,11 @@ class BaseModel(metaclass=MetaModel): This is used for example to provide suggestions based on a partial value for a relational field. Should usually behave as the reverse of - :meth:`~.name_get`, but that is ont guaranteed. + ``display_name``, but that is not guaranteed. This method is equivalent to calling :meth:`~.search` with a search - domain based on ``display_name`` and then :meth:`~.name_get` on the - result of the search. + domain based on ``display_name`` and mapping id and display_name on + the resulting search. :param str name: the name pattern to match :param list args: optional search domain (see :meth:`~.search` for @@ -1691,7 +1685,7 @@ class BaseModel(metaclass=MetaModel): :return: list of pairs ``(id, text_repr)`` for all matching records. """ ids = self._name_search(name, args, operator, limit=limit, order=self._order) - return self.browse(ids).sudo().name_get() + return [(record.id, record.display_name) for record in self.browse(ids).sudo()] @api.model def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None): @@ -2376,12 +2370,7 @@ class BaseModel(metaclass=MetaModel): field_name = group.split(':')[0] field = self._fields[field_name] - if field.type in ('many2one', 'many2many') or field_name == 'id': - ids = [row[group].id for row in rows_dict if row[group] and isinstance(row[group], BaseModel)] - m2x_records = self.env[field.comodel_name].browse(ids) - name_get_dict = dict(m2x_records.sudo().name_get()) - - elif field.type in ('date', 'datetime'): + if field.type in ('date', 'datetime'): locale = get_lang(self.env).code fmt = DEFAULT_SERVER_DATETIME_FORMAT if field.type == 'datetime' else DEFAULT_SERVER_DATE_FORMAT granularity = group.split(':')[1] if ':' in group else 'month' @@ -2391,8 +2380,8 @@ class BaseModel(metaclass=MetaModel): value = row[group] if field.type in ('many2one', 'many2many') and isinstance(value, BaseModel): + row[group] = (value.id, value.sudo().display_name) if value else False value = value.id - row[group] = (value, name_get_dict[value]) if value else value additional_domain = [(field_name, '=', value)] @@ -3161,7 +3150,7 @@ class BaseModel(metaclass=MetaModel): :param list fields: field names to return (default is all fields) :param str load: loading mode, currently the only option is to set to - ``None`` to avoid loading the ``name_get`` of m2o fields + ``None`` to avoid loading the `display_name` of m2o fields :return: a list of dictionaries mapping field names to their values, with one dictionary per record :rtype: list @@ -3337,7 +3326,7 @@ class BaseModel(metaclass=MetaModel): values from the cache without doing a query when it is avoidable. """ data = [(record, {'id': record._ids[0]}) for record in self] - use_name_get = (load == '_classic_read') + use_display_name = (load == '_classic_read') for name in fnames: field = self._fields[name] if field.type == 'properties': @@ -3350,7 +3339,7 @@ class BaseModel(metaclass=MetaModel): except MissingError: vals.clear() - results = field.convert_to_read_multi(values_list, self.browse(records), use_name_get) + results = field.convert_to_read_multi(values_list, self.browse(records), use_display_name) for record_read_vals, convert_result in zip(data, results): record_read_vals[1][name] = convert_result continue @@ -3361,7 +3350,7 @@ class BaseModel(metaclass=MetaModel): if not vals: continue try: - vals[name] = convert(record[name], record, use_name_get) + vals[name] = convert(record[name], record, use_display_name) except MissingError: vals.clear() result = [vals for record, vals in data if vals] @@ -5260,7 +5249,7 @@ class BaseModel(metaclass=MetaModel): Defaults to no sort. :param read_kwargs: All read keywords arguments used to call ``read(..., **read_kwargs)`` method e.g. you can use - ``search_read(..., load='')`` in order to avoid computing name_get + ``search_read(..., load='')`` in order to avoid computing display_name :return: List of dictionaries containing the asked fields. :rtype: list(dict). """ @@ -7018,13 +7007,6 @@ PGERROR_TO_OE = defaultdict( '23514': convert_pgerror_constraint, }) - -def lazy_name_get(self): - """ Evaluate self.name_get() lazily. """ - names = tools.lazy(lambda: dict(self.name_get())) - return [(rid, tools.lazy(operator.getitem, names, rid)) for rid in self.ids] - - # keep those imports here to avoid dependency cycle errors # pylint: disable=wrong-import-position from . import fields