From 9ebbfdac7378ac0e16eac9e08175a4143fb50061 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 20 Oct 2023 11:36:46 +0200 Subject: [PATCH] [FIX] *: incorrect translations markings Fixes a large number of cases where strings are translated then formatted, instead of letting `_()` do the formatting internally, which allows it to recover from incorrect translations (missing, broken, or extra placeholders). Also - removes translation markers entirely when there's nothing to translate e.g. `_("%s - %s")` is not useful - fixes a few messes which lead to only partial translatability (DRY is generally a bad idea when translations are involved, even more so when you don't make the variable part translatable) - fixes a few nearby issues noticed at the same time - replaces a few `"%s"` by `%r`, which should automatically quote strings relatively appropriately - fixes translated strings which use `\` to escape a newline (in order to fill-paragraph): `\` escapes only the newline, if the continuation string is indented this results in a bunch of spaces ending in the string to translate, which is pretty garbage for the translator, using implicit concatenation works much better Note: some of the updates revert f-string parameters to %, because babel (2.9) apparently has trouble with f-strings and blows up trying to extract them. Not in scope: Helping translators fix translatable strings e.g. any translation string with more than one placeholder probably should use keyword placeholders - Provides more context / data to the translator to make sense of the sentence. - Allows reordering the translated terms, which can be necessary depending on the sentence and language. closes odoo/odoo#139314 Related: odoo/enterprise#49311 Signed-off-by: Xavier Morel (xmo) --- addons/account/models/account_account.py | 6 +-- .../models/account_bank_statement_line.py | 4 +- .../account/models/account_full_reconcile.py | 2 +- addons/account/models/account_journal.py | 4 +- addons/account/models/account_move.py | 4 +- addons/account/models/account_move_line.py | 8 ++-- .../models/account_partial_reconcile.py | 5 ++- .../account/models/account_reconcile_model.py | 2 +- addons/account/models/account_tax.py | 17 +++---- addons/account/models/company.py | 4 +- addons/account/wizard/account_move_send.py | 4 +- addons/account/wizard/setup_wizards.py | 4 +- addons/account_edi/models/account_move.py | 6 +-- .../account_tax_python/models/account_tax.py | 4 +- .../auth_password_policy/models/res_users.py | 2 +- .../base_automation/models/base_automation.py | 10 ++--- .../base_geolocalize/models/base_geocoder.py | 6 +-- addons/base_iban/models/res_partner_bank.py | 2 +- addons/base_import_module/models/ir_module.py | 4 +- addons/calendar/models/calendar_attendee.py | 4 +- addons/calendar/models/calendar_recurrence.py | 2 +- .../models/crm_iap_lead_mining_request.py | 6 +-- addons/delivery/models/delivery_carrier.py | 2 +- addons/event/models/event_event.py | 4 +- addons/event/models/event_registration.py | 2 +- addons/fleet/models/fleet_vehicle.py | 2 +- .../models/gamification_goal_definition.py | 2 +- .../google_account/models/google_service.py | 2 +- addons/hr/models/hr_employee.py | 2 +- addons/hr/models/hr_job.py | 2 +- addons/hr_attendance/models/hr_attendance.py | 21 ++++----- addons/hr_attendance/models/hr_employee.py | 6 ++- .../hr_holidays/models/hr_leave_allocation.py | 2 +- addons/hr_recruitment_survey/models/hr_job.py | 2 +- addons/hr_timesheet/models/hr_timesheet.py | 4 +- addons/hr_timesheet/models/project_project.py | 4 +- addons/hr_timesheet/models/project_task.py | 2 +- .../models/hr_work_entry.py | 6 +-- .../iot_handlers/drivers/SerialBaseDriver.py | 2 +- addons/hw_drivers/tools/helpers.py | 4 +- addons/l10n_ar/models/account_journal.py | 7 +-- addons/l10n_ar/models/account_move.py | 8 ++-- addons/l10n_ar/models/res_partner.py | 2 +- addons/l10n_br/models/res_partner.py | 2 +- addons/l10n_ch/models/res_bank.py | 2 +- .../models/l10n_latam_document_type.py | 8 ++-- addons/l10n_ec/models/res_partner.py | 12 ++--- addons/l10n_fr/models/res_company.py | 2 +- addons/l10n_fr_pos_cert/models/res_company.py | 2 +- addons/l10n_in/models/account_invoice.py | 2 +- .../models/account_move.py | 4 +- .../wizards/account_move_reversal.py | 2 +- .../models/account_edi_xml_ubl_ciusro.py | 16 +++---- .../l10n_sa_edi/models/account_edi_format.py | 14 +++--- addons/l10n_sa_edi/models/account_journal.py | 4 +- .../l10n_sa_edi/models/res_config_settings.py | 7 ++- addons/l10n_se/models/account_move.py | 2 +- .../models/mail_tracking_duration_mixin.py | 10 ++--- addons/mass_mailing_sms/controllers/main.py | 4 +- addons/mrp/models/mrp_bom.py | 7 +-- addons/mrp/models/mrp_production.py | 4 +- addons/mrp/models/mrp_routing.py | 2 +- addons/mrp/models/stock_rule.py | 11 ++--- .../mrp/wizard/stock_assign_serial_numbers.py | 6 +-- .../models/mrp_production.py | 4 +- .../mrp_subcontracting/models/stock_move.py | 2 +- addons/point_of_sale/models/pos_order.py | 2 +- addons/point_of_sale/models/pos_payment.py | 2 +- addons/point_of_sale/models/pos_session.py | 39 ++++++++-------- addons/point_of_sale/models/product.py | 2 +- addons/point_of_sale/models/res_company.py | 2 +- addons/point_of_sale/report/pos_invoice.py | 2 +- addons/pos_adyen/models/pos_payment_method.py | 10 ++--- addons/pos_hr/models/hr_employee.py | 2 +- .../pos_online_payment/models/pos_session.py | 2 +- .../pos_restaurant/models/pos_restaurant.py | 7 ++- addons/product/models/decimal_precision.py | 4 +- .../product/models/product_pricelist_item.py | 6 +-- addons/product/models/uom_uom.py | 4 +- addons/project/models/project_project.py | 6 +-- .../project/models/project_project_stage.py | 2 +- addons/project/models/project_task.py | 2 +- addons/project/models/project_task_type.py | 2 +- .../models/hr_holidays.py | 2 +- addons/purchase/models/purchase.py | 4 +- addons/purchase_mrp/models/stock_move.py | 2 +- addons/purchase_stock/models/purchase.py | 2 +- addons/purchase_stock/models/stock_move.py | 3 +- addons/purchase_stock/models/stock_rule.py | 6 +-- addons/resource/models/resource_calendar.py | 2 +- addons/resource/models/resource_resource.py | 2 +- addons/sale/models/product_template.py | 2 +- addons/sale/models/sale_order_line.py | 4 +- addons/sale_project/models/product.py | 6 +-- .../sale_purchase/models/sale_order_line.py | 2 +- addons/sale_stock/models/sale_order.py | 8 ++-- addons/sale_timesheet/models/product.py | 8 ++-- .../wizard/project_create_sale_order.py | 2 +- .../wizard/account_move_send.py | 5 ++- addons/stock/models/stock_location.py | 12 ++--- addons/stock/models/stock_move.py | 16 ++++--- addons/stock/models/stock_move_line.py | 3 +- addons/stock/models/stock_package_type.py | 2 +- addons/stock/models/stock_picking.py | 10 ++--- addons/stock/models/stock_quant.py | 6 +-- addons/stock/models/stock_rule.py | 8 ++-- addons/stock/models/stock_storage_category.py | 2 +- addons/stock/models/stock_warehouse.py | 10 ++--- addons/stock/wizard/stock_picking_return.py | 2 +- .../stock/wizard/stock_replenishment_info.py | 11 ++--- addons/stock_account/models/product.py | 23 ++++++---- addons/stock_account/models/stock_move.py | 4 +- .../stock_delivery/models/delivery_carrier.py | 11 ++--- .../models/stock_landed_cost.py | 2 +- addons/survey/models/survey_survey.py | 2 +- addons/survey/wizard/survey_invite.py | 2 +- addons/uom/models/uom_uom.py | 8 ++-- addons/website/models/website_rewrite.py | 6 +-- .../website/models/website_snippet_filter.py | 2 +- .../models/crm_lead.py | 2 +- .../wizard/crm_forward_to_partner.py | 2 +- .../controllers/website_event_main.py | 4 +- .../controllers/website_event_main.py | 4 +- addons/website_forum/models/forum_post.py | 2 +- addons/website_livechat/controllers/main.py | 4 +- .../models/discuss_channel.py | 7 +-- .../models/website_visitor.py | 2 +- addons/website_sale/models/sale_order.py | 2 +- addons/website_slides/models/slide_channel.py | 4 +- addons/website_twitter/controllers/main.py | 4 +- odoo/addons/base/models/assetsbundle.py | 2 +- odoo/addons/base/models/ir_actions_report.py | 15 +++++-- odoo/addons/base/models/ir_default.py | 4 +- odoo/addons/base/models/ir_fields.py | 13 +++--- odoo/addons/base/models/ir_filters.py | 2 +- odoo/addons/base/models/ir_mail_server.py | 9 ++-- odoo/addons/base/models/ir_model.py | 44 ++++++++++--------- odoo/addons/base/models/ir_module.py | 28 ++++++------ odoo/addons/base/models/ir_qweb_fields.py | 2 +- odoo/addons/base/models/ir_sequence.py | 2 +- .../base/wizard/base_import_language.py | 4 +- .../addons/base/wizard/base_module_upgrade.py | 6 +-- 142 files changed, 408 insertions(+), 385 deletions(-) diff --git a/addons/account/models/account_account.py b/addons/account/models/account_account.py index 1bbf1fac25b..a3210cccfec 100644 --- a/addons/account/models/account_account.py +++ b/addons/account/models/account_account.py @@ -629,15 +629,15 @@ class AccountAccount(models.Model): return super(AccountAccount, self).copy(default) try: default['code'] = (str(int(self.code) + 10) or '').zfill(len(self.code)) - default.setdefault('name', _("%s (copy)") % (self.name or '')) + default.setdefault('name', _("%s (copy)", self.name or '')) while self.env['account.account'].search([ *self.env['account.account']._check_company_domain(default.get('company_id', False) or self.company_id), ('code', '=', default['code']), ], limit=1): default['code'] = (str(int(default['code']) + 10) or '') - default['name'] = _("%s (copy)") % (self.name or '') + default['name'] = _("%s (copy)", self.name or '') except ValueError: - default['code'] = _("%s.copy") % (self.code or '') + default['code'] = _("%s.copy", self.code or '') default['name'] = self.name return super(AccountAccount, self).copy(default) diff --git a/addons/account/models/account_bank_statement_line.py b/addons/account/models/account_bank_statement_line.py index 6f63dbbbf63..27636de023b 100644 --- a/addons/account/models/account_bank_statement_line.py +++ b/addons/account/models/account_bank_statement_line.py @@ -680,8 +680,8 @@ class AccountBankStatementLine(models.Model): raise UserError(_( "The journal entry %s reached an invalid state regarding its related statement line.\n" "To be consistent, the journal entry must always have exactly one journal item involving the " - "bank/cash account." - ) % st_line.move_id.display_name) + "bank/cash account.", + st_line.move_id.display_name)) st_line_vals_to_write.update({ 'payment_ref': liquidity_lines.name, diff --git a/addons/account/models/account_full_reconcile.py b/addons/account/models/account_full_reconcile.py index 4bfa8e38a05..d701816f756 100644 --- a/addons/account/models/account_full_reconcile.py +++ b/addons/account/models/account_full_reconcile.py @@ -30,7 +30,7 @@ class AccountFullReconcile(models.Model): if moves_to_reverse: default_values_list = [{ 'date': move._get_accounting_date(move.date, move._affect_tax_report()), - 'ref': _('Reversal of: %s') % move.name, + 'ref': _('Reversal of: %s', move.name), } for move in moves_to_reverse] moves_to_reverse._reverse_moves(default_values_list, cancel=True) diff --git a/addons/account/models/account_journal.py b/addons/account/models/account_journal.py index caf15423fd2..6f81b59b05a 100644 --- a/addons/account/models/account_journal.py +++ b/addons/account/models/account_journal.py @@ -502,7 +502,7 @@ class AccountJournal(models.Model): default.update( code=copy_code, - name=_("%s (copy)") % (self.name or '')) + name=_("%s (copy)", self.name or '')) return super(AccountJournal, self).copy(default) @@ -816,7 +816,7 @@ class AccountJournal(models.Model): for seq_field in sequence_fields: if not journal[seq_field]: vals = { - 'name': _('Securisation of %s - %s') % (seq_field, journal.name), + 'name': _('Securisation of %s - %s', seq_field, journal.name), 'code': 'SECUR%s-%s' % (journal.id, seq_field), 'implementation': 'no_gap', 'prefix': '', diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index d0ac5700c06..f90c9b13066 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -2427,7 +2427,7 @@ class AccountMove(models.Model): self._sanitize_vals(vals) for move in self: if (move.restrict_mode_hash_table and move.state == "posted" and set(vals).intersection(move._get_integrity_hash_fields())): - raise UserError(_("You cannot edit the following fields due to restrict mode being activated on the journal: %s.") % ', '.join(move._get_integrity_hash_fields())) + raise UserError(_("You cannot edit the following fields due to restrict mode being activated on the journal: %s.", ', '.join(move._get_integrity_hash_fields()))) if (move.restrict_mode_hash_table and move.inalterable_hash and 'inalterable_hash' in vals) or (move.secure_sequence_number and 'secure_sequence_number' in vals): raise UserError(_('You cannot overwrite the values ensuring the inalterability of the accounting.')) if (move.posted_before and 'journal_id' in vals and move.journal_id.id != vals['journal_id']): @@ -3660,7 +3660,7 @@ class AccountMove(models.Model): for move in self: if move.state in ['posted', 'cancel']: - raise UserError(_('The entry %s (id %s) must be in draft.') % (move.name, move.id)) + raise UserError(_('The entry %s (id %s) must be in draft.', move.name, move.id)) if not move.line_ids.filtered(lambda line: line.display_type not in ('line_section', 'line_note')): raise UserError(_('You need to add a line before posting.')) if not soft and move.auto_post != 'no' and move.date > fields.Date.context_today(self): diff --git a/addons/account/models/account_move_line.py b/addons/account/models/account_move_line.py index d3373bcf715..7283d3697e0 100644 --- a/addons/account/models/account_move_line.py +++ b/addons/account/models/account_move_line.py @@ -1196,7 +1196,7 @@ class AccountMoveLine(models.Model): journal = line.move_id.journal_id if account.deprecated: - raise UserError(_('The account %s (%s) is deprecated.') % (account.name, account.code)) + raise UserError(_('The account %s (%s) is deprecated.', account.name, account.code)) account_currency = account.currency_id if account_currency and account_currency != line.company_currency_id and account_currency != line.currency_id: @@ -1257,15 +1257,15 @@ class AccountMoveLine(models.Model): move = line.move_id if move.company_id.max_tax_lock_date and move.date <= move.company_id.max_tax_lock_date and line._affect_tax_report(): raise UserError(_("The operation is refused as it would impact an already issued tax statement. " - "Please change the journal entry date or the tax lock date set in the settings (%s) to proceed.") - % format_date(self.env, move.company_id.max_tax_lock_date)) + "Please change the journal entry date or the tax lock date set in the settings (%s) to proceed.", + format_date(self.env, move.company_id.max_tax_lock_date))) def _check_reconciliation(self): for line in self: if line.matched_debit_ids or line.matched_credit_ids: raise UserError(_("You cannot do this modification on a reconciled journal entry. " "You can just change some non legal fields or you must unreconcile first.\n" - "Journal Entry (id): %s (%s)") % (line.move_id.name, line.move_id.id)) + "Journal Entry (id): %s (%s)", line.move_id.name, line.move_id.id)) @api.constrains('tax_ids', 'tax_repartition_line_id') def _check_caba_non_caba_shared_tags(self): diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index 7697491422f..26098971bff 100644 --- a/addons/account/models/account_partial_reconcile.py +++ b/addons/account/models/account_partial_reconcile.py @@ -122,7 +122,7 @@ class AccountPartialReconcile(models.Model): if moves_to_reverse: default_values_list = [{ 'date': move._get_accounting_date(move.date, move._affect_tax_report()), - 'ref': _('Reversal of: %s') % move.name, + 'ref': _('Reversal of: %s', move.name), } for move in moves_to_reverse] moves_to_reverse._reverse_moves(default_values_list, cancel=True) @@ -163,7 +163,8 @@ class AccountPartialReconcile(models.Model): if not journal: raise UserError(_("There is no tax cash basis journal defined for the '%s' company.\n" - "Configure it in Accounting/Configuration/Settings") % partial.company_id.display_name) + "Configure it in Accounting/Configuration/Settings", + partial.company_id.display_name)) partial_amount = 0.0 partial_amount_currency = 0.0 diff --git a/addons/account/models/account_reconcile_model.py b/addons/account/models/account_reconcile_model.py index fda2a6bf00a..5e29285c26a 100644 --- a/addons/account/models/account_reconcile_model.py +++ b/addons/account/models/account_reconcile_model.py @@ -33,7 +33,7 @@ class AccountReconcileModelPartnerMapping(models.Model): current_regex = record.narration_regex re.compile(record.narration_regex) except re.error: - raise ValidationError(_("The following regular expression is invalid to create a partner mapping: %s") % current_regex) + raise ValidationError(_("The following regular expression is invalid to create a partner mapping: %s", current_regex)) class AccountReconcileModelLine(models.Model): diff --git a/addons/account/models/account_tax.py b/addons/account/models/account_tax.py index 905de9d56ce..9154260b351 100644 --- a/addons/account/models/account_tax.py +++ b/addons/account/models/account_tax.py @@ -418,7 +418,7 @@ class AccountTax(models.Model): def _check_children_scope(self): for tax in self: if not tax._check_m2m_recursion('children_tax_ids'): - raise ValidationError(_("Recursion found for tax '%s'.") % (tax.name,)) + raise ValidationError(_("Recursion found for tax %r.", tax.name)) if any(child.type_tax_use not in ('none', tax.type_tax_use) or child.tax_scope != tax.tax_scope for child in tax.children_tax_ids): raise ValidationError(_('The application scope of taxes in a group must be either the same as the group or left empty.')) @@ -475,7 +475,7 @@ class AccountTax(models.Model): def copy(self, default=None): default = dict(default or {}) if 'name' not in default: - default['name'] = _("%s (Copy)") % self.name + default['name'] = _("%s (Copy)", self.name) return super(AccountTax, self).copy(default=default) @api.depends('type_tax_use', 'tax_scope') @@ -1491,19 +1491,16 @@ class AccountTaxRepartitionLine(models.Model): @api.model_create_multi def create(self, vals): - tax_ids = list(set([line.get('tax_id') for line in vals])) # Sorted - taxes = self.env['account.tax'].search_fetch([('id', 'in', tax_ids)], ['name'], order='id ASC') - tax_dict = dict(zip(tax_ids, taxes)) - for line in vals: - tax = tax_dict.get(line.get('tax_id')) - if tax and tax.is_used: - raise ValidationError(_("The tax named {} has already been used, you cannot add nor delete its tax repartition lines.").format(tax.name)) + tax_ids = {tax_id for line in vals if (tax_id := line.get('tax_id'))} + taxes = self.env['account.tax'].browse(tax_ids) + for tax in taxes.filtered('is_used'): + raise ValidationError(_("The tax named %s has already been used, you cannot add nor delete its tax repartition lines.", tax.name)) return super().create(vals) def unlink(self): for repartition_line in self: if repartition_line.tax_id.is_used: - raise ValidationError(_("The tax named {} has already been used, you cannot add nor delete its tax repartition lines.").format(repartition_line.tax_id.name)) + raise ValidationError(_("The tax named %s has already been used, you cannot add nor delete its tax repartition lines.", repartition_line.tax_id.name)) return super().unlink() @api.depends('company_id.multi_vat_foreign_country_ids', 'company_id.account_fiscal_country_id') diff --git a/addons/account/models/company.py b/addons/account/models/company.py index 26c6d9d1951..e82f838d682 100644 --- a/addons/account/models/company.py +++ b/addons/account/models/company.py @@ -622,9 +622,9 @@ class ResCompany(models.Model): 'last_move_date': format_date(self.env, end_move_info[2]), }) if len(moves) == all_moves_count: - rslt.update({'msg_cover': _('All entries are hashed.')}) + rslt['msg_cover'] = _('All entries are hashed.') else: - rslt.update({'msg_cover': _('Entries are hashed from %s (%s)') % (start_move_info[0], format_date(self.env, start_move_info[2]))}) + rslt['msg_cover'] = _('Entries are hashed from %s (%s)', start_move_info[0], format_date(self.env, start_move_info[2])) results_by_journal['results'].append(rslt) return results_by_journal diff --git a/addons/account/wizard/account_move_send.py b/addons/account/wizard/account_move_send.py index 2bc17e6e20e..9ecf39a443e 100644 --- a/addons/account/wizard/account_move_send.py +++ b/addons/account/wizard/account_move_send.py @@ -205,8 +205,8 @@ class AccountMoveSend(models.Model): if wizard.mode == 'invoice_multi' and wizard.checkbox_send_mail and invoices_without_mail_data: wizard.send_mail_warning_message = _( "The partners on the following invoices have no email address, " - "so those invoices will not be sent: %s" - ) % ", ".join(invoices_without_mail_data.mapped('name')) + "so those invoices will not be sent: %s", + ", ".join(invoices_without_mail_data.mapped('name'))) @api.depends('move_ids') def _compute_checkbox_send_mail(self): diff --git a/addons/account/wizard/setup_wizards.py b/addons/account/wizard/setup_wizards.py index df4d3c02f31..f419cf99cf7 100644 --- a/addons/account/wizard/setup_wizards.py +++ b/addons/account/wizard/setup_wizards.py @@ -35,8 +35,8 @@ class FinancialYearOpeningWizard(models.TransientModel): date(2020, int(wiz.fiscalyear_last_month), wiz.fiscalyear_last_day) except ValueError: raise ValidationError( - _('Incorrect fiscal year date: day is out of range for month. Month: %s; Day: %s') % - (wiz.fiscalyear_last_month, wiz.fiscalyear_last_day) + _('Incorrect fiscal year date: day is out of range for month. Month: %s; Day: %s', + wiz.fiscalyear_last_month, wiz.fiscalyear_last_day) ) def write(self, vals): diff --git a/addons/account_edi/models/account_move.py b/addons/account_edi/models/account_move.py index 28b8337ae06..cf610b2d23b 100644 --- a/addons/account_edi/models/account_move.py +++ b/addons/account_edi/models/account_move.py @@ -247,7 +247,7 @@ class AccountMove(models.Model): if move_applicability: errors = edi_format._check_move_configuration(move) if errors: - raise UserError(_("Invalid invoice configuration:\n\n%s") % '\n'.join(errors)) + raise UserError(_("Invalid invoice configuration:\n\n%s", '\n'.join(errors))) existing_edi_document = move.edi_document_ids.filtered(lambda x: x.edi_format_id == edi_format) if existing_edi_document: @@ -285,8 +285,8 @@ class AccountMove(models.Model): if move.edi_show_cancel_button: raise UserError(_( "You can't edit the following journal entry %s because an electronic document has already been " - "sent. Please use the 'Request EDI Cancellation' button instead." - ) % move.display_name) + "sent. Please use the 'Request EDI Cancellation' button instead.", + move.display_name)) res = super().button_draft() diff --git a/addons/account_tax_python/models/account_tax.py b/addons/account_tax_python/models/account_tax.py index 4322264f6dc..2bddf09e7d6 100644 --- a/addons/account_tax_python/models/account_tax.py +++ b/addons/account_tax_python/models/account_tax.py @@ -39,7 +39,7 @@ class AccountTaxPython(models.Model): try: safe_eval(self.python_compute, localdict, mode="exec", nocopy=True) except Exception as e: - raise UserError(_("You entered invalid code %r in %r taxes\n\nError : %s") % (self.python_compute, self.name, e)) from e + raise UserError(_("You entered invalid code %r in %r taxes\n\nError : %s", self.python_compute, self.name, e)) from e return localdict['result'] return super(AccountTaxPython, self)._compute_amount(base_amount, price_unit, quantity, product, partner, fixed_multiplicator) @@ -53,7 +53,7 @@ class AccountTaxPython(models.Model): try: safe_eval(tax.python_applicable, localdict, mode="exec", nocopy=True) except Exception as e: - raise UserError(_("You entered invalid code %r in %r taxes\n\nError : %s") % (tax.python_applicable, tax.name, e)) from e + raise UserError(_("You entered invalid code %r in %r taxes\n\nError : %s", tax.python_applicable, tax.name, e)) from e return localdict.get('result', False) return True diff --git a/addons/auth_password_policy/models/res_users.py b/addons/auth_password_policy/models/res_users.py index b938acd5ee9..a0fa1d02be2 100644 --- a/addons/auth_password_policy/models/res_users.py +++ b/addons/auth_password_policy/models/res_users.py @@ -27,7 +27,7 @@ class ResUsers(models.Model): if not password: continue if len(password) < minlength: - failures.append(_(u"Passwords must have at least %d characters, got %d.") % (minlength, len(password))) + failures.append(_("Passwords must have at least %d characters, got %d.", minlength, len(password))) if failures: raise UserError(u'\n\n '.join(failures)) diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index f0f26ac6e80..8d6feeae538 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -384,12 +384,10 @@ class BaseAutomation(models.Model): 'title': _("Warning"), 'message': _( "The \"%(trigger_value)s\" %(trigger_label)s can only be " - "used with the \"%(state_value)s\" action type" - ) % { - 'trigger_value': dict(trigger_field._description_selection(self.env))['on_change'], - 'trigger_label': trigger_field._description_string(self.env), - 'state_value': action_states['code'], - } + "used with the \"%(state_value)s\" action type", + trigger_value=dict(trigger_field._description_selection(self.env))['on_change'], + trigger_label=trigger_field._description_string(self.env), + state_value=action_states['code']) }} MAIL_STATES = ('mail_post', 'followers', 'next_activity') diff --git a/addons/base_geolocalize/models/base_geocoder.py b/addons/base_geolocalize/models/base_geocoder.py index 069536a1415..0a130c75fde 100644 --- a/addons/base_geolocalize/models/base_geocoder.py +++ b/addons/base_geolocalize/models/base_geocoder.py @@ -67,8 +67,8 @@ class GeoCoder(models.AbstractModel): result = service(addr, **kw) except AttributeError: raise UserError(_( - 'Provider %s is not implemented for geolocation service.' - ) % provider) + 'Provider %s is not implemented for geolocation service.', + provider)) except UserError: raise except Exception: @@ -128,7 +128,7 @@ class GeoCoder(models.AbstractModel): '\n\nGoogle made this a paid feature.\n' 'You should first enable billing on your Google account.\n' 'Then, go to Developer Console, and enable the APIs:\n' - 'Geocoding, Maps Static, Maps Javascript.\n') % result.get('error_message') + 'Geocoding, Maps Static, Maps Javascript.\n', result.get('error_message')) raise UserError(error_msg) geo = result['results'][0]['geometry']['location'] return float(geo['lat']), float(geo['lng']) diff --git a/addons/base_iban/models/res_partner_bank.py b/addons/base_iban/models/res_partner_bank.py index eabd77fcee1..0decb52dded 100644 --- a/addons/base_iban/models/res_partner_bank.py +++ b/addons/base_iban/models/res_partner_bank.py @@ -37,7 +37,7 @@ def validate_iban(iban): iban_template = _map_iban_template[country_code] if len(iban) != len(iban_template.replace(' ', '')) or not re.fullmatch("[a-zA-Z0-9]+", iban): raise ValidationError(_("The IBAN does not seem to be correct. You should have entered something like this %s\n" - "Where B = National bank code, S = Branch code, C = Account No, k = Check digit") % iban_template) + "Where B = National bank code, S = Branch code, C = Account No, k = Check digit", iban_template)) check_chars = iban[4:] + iban[:4] digits = int(''.join(str(int(char, 36)) for char in check_chars)) # BASE 36: 0..9,A..Z -> 0..35 diff --git a/addons/base_import_module/models/ir_module.py b/addons/base_import_module/models/ir_module.py index 8bf6a6590c1..5e819168aea 100644 --- a/addons/base_import_module/models/ir_module.py +++ b/addons/base_import_module/models/ir_module.py @@ -64,9 +64,9 @@ class IrModule(models.Model): _is_studio_custom(path)): err = _("Studio customizations require Studio") else: - err = _("Unmet module dependencies: \n\n - %s") % '\n - '.join( + err = _("Unmet module dependencies: \n\n - %s", '\n - '.join( known_mods.filtered(lambda mod: mod.name in unmet_dependencies).mapped('shortdesc') - ) + )) raise UserError(err) elif 'web_studio' not in installed_mods and _is_studio_custom(path): raise UserError(_("Studio customizations require the Odoo Studio app.")) diff --git a/addons/calendar/models/calendar_attendee.py b/addons/calendar/models/calendar_attendee.py index 699ac45bf4d..986e8c01ce4 100644 --- a/addons/calendar/models/calendar_attendee.py +++ b/addons/calendar/models/calendar_attendee.py @@ -157,7 +157,7 @@ class Attendee(models.Model): for attendee in self: attendee.event_id.message_post( author_id=attendee.partner_id.id, - body=_("%s has accepted the invitation") % (attendee.common_name), + body=_("%s has accepted the invitation", attendee.common_name), subtype_xmlid="calendar.subtype_invitation", ) return self.write({'state': 'accepted'}) @@ -167,7 +167,7 @@ class Attendee(models.Model): for attendee in self: attendee.event_id.message_post( author_id=attendee.partner_id.id, - body=_("%s has declined the invitation") % (attendee.common_name), + body=_("%s has declined the invitation", attendee.common_name), subtype_xmlid="calendar.subtype_invitation", ) return self.write({'state': 'declined'}) diff --git a/addons/calendar/models/calendar_recurrence.py b/addons/calendar/models/calendar_recurrence.py index 06fb142e2f8..d99fb242134 100644 --- a/addons/calendar/models/calendar_recurrence.py +++ b/addons/calendar/models/calendar_recurrence.py @@ -152,7 +152,7 @@ class RecurrenceRule(models.Model): week_map = {v: k for k, v in RRULE_WEEKDAYS.items()} weekday_short = [week_map[w] for w in weekdays] day_strings = [d[1] for d in WEEKDAY_SELECTION if d[0] in weekday_short] - on = _("on %s") % ", ".join([day_name for day_name in day_strings]) + on = _("on %s", ", ".join(day_strings)) elif recurrence.rrule_type == 'monthly': if recurrence.month_by == 'day': position_label = dict(BYDAY_SELECTION)[recurrence.byday] diff --git a/addons/crm_iap_mine/models/crm_iap_lead_mining_request.py b/addons/crm_iap_mine/models/crm_iap_lead_mining_request.py index 5b640977eda..cd48f8dc6f7 100644 --- a/addons/crm_iap_mine/models/crm_iap_lead_mining_request.py +++ b/addons/crm_iap_mine/models/crm_iap_lead_mining_request.py @@ -92,9 +92,9 @@ class CRMLeadMiningRequest(models.Model): company_credits = CREDIT_PER_COMPANY * record.lead_number contact_credits = CREDIT_PER_CONTACT * record.contact_number total_contact_credits = contact_credits * record.lead_number - record.lead_contacts_credits = _("Up to %d additional credits will be consumed to identify %d contacts per company.") % (contact_credits*company_credits, record.contact_number) - record.lead_credits = _('%d credits will be consumed to find %d companies.') % (company_credits, record.lead_number) - record.lead_total_credits = _("This makes a total of %d credits for this request.") % (total_contact_credits + company_credits) + record.lead_contacts_credits = _("Up to %d additional credits will be consumed to identify %d contacts per company.", contact_credits*company_credits, record.contact_number) + record.lead_credits = _('%d credits will be consumed to find %d companies.', company_credits, record.lead_number) + record.lead_total_credits = _("This makes a total of %d credits for this request.", total_contact_credits + company_credits) @api.depends('lead_ids.lead_mining_request_id') def _compute_lead_count(self): diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py index 9e6884db010..f62720c4ff6 100644 --- a/addons/delivery/models/delivery_carrier.py +++ b/addons/delivery/models/delivery_carrier.py @@ -210,7 +210,7 @@ class DeliveryCarrier(models.Model): # free when order is large enough amount_without_delivery = order._compute_amount_total_without_delivery() if res['success'] and self.free_over and self._compute_currency(order, amount_without_delivery, 'pricelist_to_company') >= self.amount: - res['warning_message'] = _('The shipping is free since the order amount exceeds %.2f.') % (self.amount) + res['warning_message'] = _('The shipping is free since the order amount exceeds %.2f.', self.amount) res['price'] = 0.0 return res diff --git a/addons/event/models/event_event.py b/addons/event/models/event_event.py index 89d9942f4f8..757039ed884 100644 --- a/addons/event/models/event_event.py +++ b/addons/event/models/event_event.py @@ -357,7 +357,7 @@ class EventEvent(models.Model): if operator not in ['=', '!=']: raise UserError(_('This operator is not supported')) if not isinstance(value, bool): - raise UserError(_('Value should be True or False (not %s)') % value) + raise UserError(_('Value should be True or False (not %s)', value)) now = fields.Datetime.now() if (operator == '=' and value) or (operator == '!=' and not value): domain = [('date_begin', '<=', now), ('date_end', '>', now)] @@ -615,7 +615,7 @@ class EventEvent(models.Model): @api.returns('self', lambda value: value.id) def copy(self, default=None): self.ensure_one() - default = dict(default or {}, name=_("%s (copy)") % (self.name)) + default = dict(default or {}, name=_("%s (copy)", self.name)) return super(EventEvent, self).copy(default) @api.model diff --git a/addons/event/models/event_registration.py b/addons/event/models/event_registration.py index f431617d363..73e229a1265 100644 --- a/addons/event/models/event_registration.py +++ b/addons/event/models/event_registration.py @@ -379,7 +379,7 @@ class EventRegistration(models.Model): elif diff.days == 1: return _('tomorrow') elif (diff.days < 7): - return _('in %d days') % (diff.days, ) + return _('in %d days', diff.days) elif (diff.days < 14): return _('next week') elif event_date.month == (today + relativedelta(months=+1)).month: diff --git a/addons/fleet/models/fleet_vehicle.py b/addons/fleet/models/fleet_vehicle.py index 56c99a5fdb8..b51ebcc4fb1 100644 --- a/addons/fleet/models/fleet_vehicle.py +++ b/addons/fleet/models/fleet_vehicle.py @@ -306,7 +306,7 @@ class FleetVehicle(models.Model): vehicle.activity_schedule( 'mail.mail_activity_data_todo', user_id=vehicle.manager_id.id or self.env.user.id, - note=_('Specify the End date of %s') % vehicle.driver_id.name) + note=_('Specify the End date of %s', vehicle.driver_id.name)) if 'future_driver_id' in vals and vals['future_driver_id']: state_waiting_list = self.env.ref('fleet.fleet_vehicle_state_waiting_list', raise_if_not_found=False) diff --git a/addons/gamification/models/gamification_goal_definition.py b/addons/gamification/models/gamification_goal_definition.py index 358efd2404d..93ff1cc0a81 100644 --- a/addons/gamification/models/gamification_goal_definition.py +++ b/addons/gamification/models/gamification_goal_definition.py @@ -91,7 +91,7 @@ class GoalDefinition(models.Model): msg = e if isinstance(e, SyntaxError): msg = (e.msg + '\n' + e.text) - raise exceptions.UserError(_("The domain for the definition %s seems incorrect, please check it.\n\n%s") % (definition.name, msg)) + raise exceptions.UserError(_("The domain for the definition %s seems incorrect, please check it.\n\n%s", definition.name, msg)) return True def _check_model_validity(self): diff --git a/addons/google_account/models/google_service.py b/addons/google_account/models/google_service.py index 40766bba5a0..fba27a498fc 100644 --- a/addons/google_account/models/google_service.py +++ b/addons/google_account/models/google_service.py @@ -125,7 +125,7 @@ class GoogleService(models.AbstractModel): elif method.upper() in ('POST', 'PATCH', 'PUT'): res = requests.request(method.lower(), preuri + uri, data=params, headers=headers, timeout=timeout) else: - raise Exception(_('Method not supported [%s] not in [GET, POST, PUT, PATCH or DELETE]!') % (method)) + raise Exception(_('Method not supported [%s] not in [GET, POST, PUT, PATCH or DELETE]!', method)) res.raise_for_status() status = res.status_code diff --git a/addons/hr/models/hr_employee.py b/addons/hr/models/hr_employee.py index 8b0d4dd3417..a5a175357d3 100644 --- a/addons/hr/models/hr_employee.py +++ b/addons/hr/models/hr_employee.py @@ -245,7 +245,7 @@ class HrEmployeePrivate(models.Model): public_fields = self.env['hr.employee.public']._fields private_fields = [fname for fname in field_names if fname not in public_fields] if private_fields: - raise AccessError(_('The fields "%s" you try to read is not available on the public employee profile.') % (','.join(private_fields))) + raise AccessError(_('The fields %r you try to read is not available on the public employee profile.', ','.join(private_fields))) def _copy_cache_from(self, public, field_names): # HACK: retrieve publicly available values from hr.employee.public and diff --git a/addons/hr/models/hr_job.py b/addons/hr/models/hr_job.py index 8488087c489..abd242095d4 100644 --- a/addons/hr/models/hr_job.py +++ b/addons/hr/models/hr_job.py @@ -54,7 +54,7 @@ class Job(models.Model): self.ensure_one() default = dict(default or {}) if 'name' not in default: - default['name'] = _("%s (copy)") % (self.name) + default['name'] = _("%s (copy)", self.name) return super(Job, self).copy(default=default) def write(self, vals): diff --git a/addons/hr_attendance/models/hr_attendance.py b/addons/hr_attendance/models/hr_attendance.py index bdd9f5ebf83..69b881ec89f 100644 --- a/addons/hr_attendance/models/hr_attendance.py +++ b/addons/hr_attendance/models/hr_attendance.py @@ -173,10 +173,9 @@ class HrAttendance(models.Model): ('id', '!=', attendance.id), ], order='check_in desc', limit=1) if last_attendance_before_check_in and last_attendance_before_check_in.check_out and last_attendance_before_check_in.check_out > attendance.check_in: - raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s") % { - 'empl_name': attendance.employee_id.name, - 'datetime': format_datetime(self.env, attendance.check_in, dt_format=False), - }) + raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s", + empl_name=attendance.employee_id.name, + datetime=format_datetime(self.env, attendance.check_in, dt_format=False))) if not attendance.check_out: # if our attendance is "open" (no check_out), we verify there is no other "open" attendance @@ -186,10 +185,9 @@ class HrAttendance(models.Model): ('id', '!=', attendance.id), ], order='check_in desc', limit=1) if no_check_out_attendances: - raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee hasn't checked out since %(datetime)s") % { - 'empl_name': attendance.employee_id.name, - 'datetime': format_datetime(self.env, no_check_out_attendances.check_in, dt_format=False), - }) + raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee hasn't checked out since %(datetime)s", + empl_name=attendance.employee_id.name, + datetime=format_datetime(self.env, no_check_out_attendances.check_in, dt_format=False))) else: # we verify that the latest attendance with check_in time before our check_out time # is the same as the one before our check_in time computed before, otherwise it overlaps @@ -199,10 +197,9 @@ class HrAttendance(models.Model): ('id', '!=', attendance.id), ], order='check_in desc', limit=1) if last_attendance_before_check_out and last_attendance_before_check_in != last_attendance_before_check_out: - raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s") % { - 'empl_name': attendance.employee_id.name, - 'datetime': format_datetime(self.env, last_attendance_before_check_out.check_in, dt_format=False), - }) + raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s", + empl_name=attendance.employee_id.name, + datetime=format_datetime(self.env, last_attendance_before_check_out.check_in, dt_format=False))) @api.model def _get_day_start_and_day(self, employee, dt): diff --git a/addons/hr_attendance/models/hr_employee.py b/addons/hr_attendance/models/hr_employee.py index 4ea2d709f06..38918bad8a2 100644 --- a/addons/hr_attendance/models/hr_employee.py +++ b/addons/hr_attendance/models/hr_employee.py @@ -181,8 +181,10 @@ class HrEmployee(models.Model): 'check_out': action_date }) else: - raise exceptions.UserError(_('Cannot perform check out on %(empl_name)s, could not find corresponding check in. ' - 'Your attendances have probably been modified manually by human resources.') % {'empl_name': self.sudo().name, }) + raise exceptions.UserError(_( + 'Cannot perform check out on %(empl_name)s, could not find corresponding check in. ' + 'Your attendances have probably been modified manually by human resources.', + empl_name=self.sudo().name)) return attendance def action_open_last_month_attendances(self): diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index e4ccd2c1806..b3e8e03f64c 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -666,7 +666,7 @@ class HolidaysAllocation(models.Model): def _unlink_if_correct_states(self): state_description_values = {elem[0]: elem[1] for elem in self._fields['state']._description_selection(self.env)} for allocation in self.filtered(lambda allocation: allocation.state not in ['confirm', 'refuse']): - raise UserError(_('You cannot delete an allocation request which is in %s state.') % (state_description_values.get(allocation.state),)) + raise UserError(_('You cannot delete an allocation request which is in %s state.', state_description_values.get(allocation.state))) @api.ondelete(at_uninstall=False) def _unlink_if_no_leaves(self): diff --git a/addons/hr_recruitment_survey/models/hr_job.py b/addons/hr_recruitment_survey/models/hr_job.py index abd7132fef8..734a0bcec62 100644 --- a/addons/hr_recruitment_survey/models/hr_job.py +++ b/addons/hr_recruitment_survey/models/hr_job.py @@ -18,7 +18,7 @@ class Job(models.Model): def action_new_survey(self): self.ensure_one() survey = self.env['survey.survey'].create({ - 'title': _("Interview Form: %s") % self.name, + 'title': _("Interview Form: %s", self.name), }) self.write({'survey_id': survey.id}) diff --git a/addons/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py index 6801c31ddd7..36cc647c536 100644 --- a/addons/hr_timesheet/models/hr_timesheet.py +++ b/addons/hr_timesheet/models/hr_timesheet.py @@ -264,7 +264,7 @@ class AccountAnalyticLine(models.Model): # custom inheretied view stored in database. Even if normally, no xpath can be done on # 'string' attribute. for node in doc.xpath("//field[@name='unit_amount'][@widget='timesheet_uom'][not(@string)]"): - node.set('string', _('%s Spent') % (re.sub(r'[\(\)]', '', encoding_uom.name or ''))) + node.set('string', _('%s Spent', re.sub(r'[\(\)]', '', encoding_uom.name or ''))) return doc @api.model @@ -274,7 +274,7 @@ class AccountAnalyticLine(models.Model): # Just fetch the name of the uom in `timesheet_encode_uom_id` of the current company encoding_uom_name = self.env.company.timesheet_encode_uom_id.with_context(prefetch_fields=False).sudo().name for node in doc.xpath("//field[@widget='timesheet_uom'][not(@string)] | //field[@widget='timesheet_uom_no_toggle'][not(@string)]"): - name_with_uom = re.sub(_('Hours') + "|Hours", encoding_uom_name or '', Model._fields[node.get('name')]._description_string(self.env), flags=re.IGNORECASE) + name_with_uom = re.sub(re.escape(_('Hours')) + "|Hours", encoding_uom_name or '', Model._fields[node.get('name')]._description_string(self.env), flags=re.IGNORECASE) node.set('string', name_with_uom) return doc diff --git a/addons/hr_timesheet/models/project_project.py b/addons/hr_timesheet/models/project_project.py index 648d48e5384..364bc5d9d28 100644 --- a/addons/hr_timesheet/models/project_project.py +++ b/addons/hr_timesheet/models/project_project.py @@ -96,9 +96,9 @@ class Project(models.Model): @api.model def _search_is_project_overtime(self, operator, value): if not isinstance(value, bool): - raise ValueError(_('Invalid value: %s') % value) + raise ValueError(_('Invalid value: %s', value)) if operator not in ['=', '!=']: - raise ValueError(_('Invalid operator: %s') % operator) + raise ValueError(_('Invalid operator: %s', operator)) query = """ SELECT Project.id diff --git a/addons/hr_timesheet/models/project_task.py b/addons/hr_timesheet/models/project_task.py index 02244fb8f2c..b8d6fee61fa 100644 --- a/addons/hr_timesheet/models/project_task.py +++ b/addons/hr_timesheet/models/project_task.py @@ -173,7 +173,7 @@ class Task(models.Model): if self.env.context.get('hr_timesheet_display_remaining_hours'): for task in self: if task.allow_timesheets and task.allocated_hours > 0 and task.encode_uom_in_days: - days_left = _("(%s days remaining)") % task._convert_hours_to_days(task.remaining_hours) + days_left = _("(%s days remaining)", task._convert_hours_to_days(task.remaining_hours)) task.display_name = task.display_name + "\u00A0" + days_left elif task.allow_timesheets and task.allocated_hours > 0: hours, mins = (str(int(duration)).rjust(2, '0') for duration in divmod(abs(task.remaining_hours) * 60, 60)) diff --git a/addons/hr_work_entry_contract/models/hr_work_entry.py b/addons/hr_work_entry_contract/models/hr_work_entry.py index 4dc9e8f2e99..d972f9fdd74 100644 --- a/addons/hr_work_entry_contract/models/hr_work_entry.py +++ b/addons/hr_work_entry_contract/models/hr_work_entry.py @@ -127,10 +127,10 @@ class HrWorkEntry(models.Model): employee = self.env['hr.employee'].browse(vals.get('employee_id')) contracts = employee._get_contracts(contract_start, contract_end, states=['open', 'pending', 'close']) if not contracts: - raise ValidationError(_("%s does not have a contract from %s to %s.") % (employee.name, contract_start, contract_end)) + raise ValidationError(_("%s does not have a contract from %s to %s.", employee.name, contract_start, contract_end)) elif len(contracts) > 1: - raise ValidationError(_("%s has multiple contracts from %s to %s. A work entry cannot overlap multiple contracts.") - % (employee.name, contract_start, contract_end)) + raise ValidationError(_("%s has multiple contracts from %s to %s. A work entry cannot overlap multiple contracts.", + employee.name, contract_start, contract_end)) return dict(vals, contract_id=contracts[0].id) return vals diff --git a/addons/hw_drivers/iot_handlers/drivers/SerialBaseDriver.py b/addons/hw_drivers/iot_handlers/drivers/SerialBaseDriver.py index 0de32a24a11..47da576677a 100644 --- a/addons/hw_drivers/iot_handlers/drivers/SerialBaseDriver.py +++ b/addons/hw_drivers/iot_handlers/drivers/SerialBaseDriver.py @@ -107,7 +107,7 @@ class SerialDriver(Driver): self._actions[data['action']](data) time.sleep(self._protocol.commandDelay) except Exception: - msg = _('An error occurred while performing action %s on %s') % (data, self.device_name) + msg = _('An error occurred while performing action %s on %s', data, self.device_name) _logger.exception(msg) self._status = {'status': self.STATUS_ERROR, 'message_title': msg, 'message_body': traceback.format_exc()} self._push_status() diff --git a/addons/hw_drivers/tools/helpers.py b/addons/hw_drivers/tools/helpers.py index bc20cb19c42..2433b08c96a 100644 --- a/addons/hw_drivers/tools/helpers.py +++ b/addons/hw_drivers/tools/helpers.py @@ -116,11 +116,11 @@ def check_certificate(): if key[0] == b'CN': cn = key[1].decode('utf-8') if cn == 'OdooTempIoTBoxCertificate' or datetime.datetime.now() > cert_end_date: - message = _('Your certificate %s must be updated') % (cn) + message = _('Your certificate %s must be updated', cn) _logger.info(message) return {"status": CertificateStatus.NEED_REFRESH} else: - message = _('Your certificate %s is valid until %s') % (cn, cert_end_date) + message = _('Your certificate %s is valid until %s', cn, cert_end_date) _logger.info(message) return {"status": CertificateStatus.OK, "message": message} diff --git a/addons/l10n_ar/models/account_journal.py b/addons/l10n_ar/models/account_journal.py index bb7e8f0dd66..1388e7fbdc1 100644 --- a/addons/l10n_ar/models/account_journal.py +++ b/addons/l10n_ar/models/account_journal.py @@ -146,9 +146,10 @@ class AccountJournal(models.Model): lambda j: j.l10n_ar_is_pos and j.type == 'purchase' and j.l10n_ar_afip_pos_system not in ['II_IM', 'RLI_RLM', 'RAW_MAW']) if journals: - raise ValidationError("\n".join([_( - "The pos system %s can not be used on a purchase journal (id %s)" - ) % (x.l10n_ar_afip_pos_system, x.id) for x in journals])) + raise ValidationError("\n".join( + _("The pos system %s can not be used on a purchase journal (id %s)", x.l10n_ar_afip_pos_system, x.id) + for x in journals + )) @api.constrains('l10n_ar_afip_pos_number') def _check_afip_pos_number(self): diff --git a/addons/l10n_ar/models/account_move.py b/addons/l10n_ar/models/account_move.py index 1f1eca4b49b..b1b2c357f76 100644 --- a/addons/l10n_ar/models/account_move.py +++ b/addons/l10n_ar/models/account_move.py @@ -136,12 +136,12 @@ class AccountMove(models.Model): for line in inv.mapped('invoice_line_ids').filtered(lambda x: x.display_type not in ('line_section', 'line_note')): vat_taxes = line.tax_ids.filtered(lambda x: x.tax_group_id.l10n_ar_vat_afip_code) if len(vat_taxes) != 1: - raise UserError(_('There should be a single tax from the "VAT" tax group per line, add it to "%s". If you already have it, please check the tax configuration, in advanced options, in the corresponding field "Tax Group".') % line.name) + raise UserError(_('There should be a single tax from the "VAT" tax group per line, add it to %r. If you already have it, please check the tax configuration, in advanced options, in the corresponding field "Tax Group".', line.name)) elif purchase_aliquots == 'zero' and vat_taxes.tax_group_id.l10n_ar_vat_afip_code != '0': - raise UserError(_('On invoice id "%s" you must use VAT Not Applicable on every line.') % inv.id) + raise UserError(_('On invoice id %r you must use VAT Not Applicable on every line.', inv.id)) elif purchase_aliquots == 'not_zero' and vat_taxes.tax_group_id.l10n_ar_vat_afip_code == '0': - raise UserError(_('On invoice id "%s" you must use VAT taxes different than VAT Not Applicable.') % inv.id) + raise UserError(_('On invoice id %r you must use VAT taxes different than VAT Not Applicable.', inv.id)) def _set_afip_service_dates(self): for rec in self.filtered(lambda m: m.invoice_date and m.l10n_ar_afip_concept in ['2', '3', '4']): @@ -176,7 +176,7 @@ class AccountMove(models.Model): and not self.partner_id.l10n_ar_afip_responsibility_type_id: return {'warning': { 'title': _('Missing Partner Configuration'), - 'message': _('Please configure the AFIP Responsibility for "%s" in order to continue') % ( + 'message': _('Please configure the AFIP Responsibility for "%s" in order to continue', self.partner_id.name)}} @api.onchange('partner_id') diff --git a/addons/l10n_ar/models/res_partner.py b/addons/l10n_ar/models/res_partner.py index b734d77c761..b5b6e44957f 100644 --- a/addons/l10n_ar/models/res_partner.py +++ b/addons/l10n_ar/models/res_partner.py @@ -81,7 +81,7 @@ class ResPartner(models.Model): This method can be used to validate is the VAT is proper defined in the partner """ self.ensure_one() if not self.l10n_ar_vat: - raise UserError(_('No VAT configured for partner [%i] %s') % (self.id, self.name)) + raise UserError(_('No VAT configured for partner [%i] %s', self.id, self.name)) return self.l10n_ar_vat def _get_validation_module(self): diff --git a/addons/l10n_br/models/res_partner.py b/addons/l10n_br/models/res_partner.py index 8731d8f1a01..f378820a751 100644 --- a/addons/l10n_br/models/res_partner.py +++ b/addons/l10n_br/models/res_partner.py @@ -17,4 +17,4 @@ class ResPartner(models.Model): def check_cpf(self): for partner in self.filtered(lambda partner: partner.l10n_latam_identification_type_id == self.env.ref('l10n_br.cpf')): if partner.vat and not stdnum.br.cpf.is_valid(partner.vat) and not stdnum.br.cnpj.is_valid(partner.vat): - raise ValidationError(_('CPF/CNPJ number %s for %s is not valid.') % (partner.vat, partner.display_name)) + raise ValidationError(_('CPF/CNPJ number %s for %s is not valid.', partner.vat, partner.display_name)) diff --git a/addons/l10n_ch/models/res_bank.py b/addons/l10n_ch/models/res_bank.py index c6cf26965e4..da2c9bc6398 100644 --- a/addons/l10n_ch/models/res_bank.py +++ b/addons/l10n_ch/models/res_bank.py @@ -23,7 +23,7 @@ def validate_qr_iban(qr_iban): # Now, check if it's valid QR-IBAN (based on its IID). if not check_qr_iban_range(sanitized_qr_iban): - raise ValidationError(_("QR-IBAN '%s' is invalid.") % qr_iban) + raise ValidationError(_("QR-IBAN %r is invalid.", qr_iban)) return True diff --git a/addons/l10n_ec/models/l10n_latam_document_type.py b/addons/l10n_ec/models/l10n_latam_document_type.py index 8ff2f6eae8c..ac4ced60a29 100644 --- a/addons/l10n_ec/models/l10n_latam_document_type.py +++ b/addons/l10n_ec/models/l10n_latam_document_type.py @@ -32,9 +32,9 @@ class L10nLatamDocumentType(models.Model): # Fill each number group with zeroes (3, 3 and 9 respectively) document_number = "-".join([n.zfill(3 if i < 2 else 9) for i, n in enumerate(num_match.groups())]) else: - raise UserError( - _(u"Ecuadorian Document %s must be like 001-001-123456789") - % (self.display_name) - ) + raise UserError(_( + "Ecuadorian Document %s must be like 001-001-123456789", + self.display_name + )) return document_number diff --git a/addons/l10n_ec/models/res_partner.py b/addons/l10n_ec/models/res_partner.py index 2bad55eb33a..5441fad26f8 100644 --- a/addons/l10n_ec/models/res_partner.py +++ b/addons/l10n_ec/models/res_partner.py @@ -70,11 +70,11 @@ class ResPartner(models.Model): it_dni.id, ): if partner.l10n_latam_identification_type_id.id == it_dni.id and len(partner.vat) != 10: - raise ValidationError(_('If your identification type is %s, it must be 10 digits') - % it_dni.display_name) + raise ValidationError(_('If your identification type is %s, it must be 10 digits', + it_dni.display_name)) if partner.l10n_latam_identification_type_id.id == it_ruc.id and len(partner.vat) != 13: - raise ValidationError(_('If your identification type is %s, it must be 13 digits') - % it_ruc.display_name) + raise ValidationError(_('If your identification type is %s, it must be 13 digits', + it_ruc.display_name)) return super(ResPartner, self - ecuadorian_partners).check_vat() @api.depends("vat", "country_id", "l10n_latam_identification_type_id") @@ -90,10 +90,10 @@ class ResPartner(models.Model): if not final_consumer: if partner.l10n_latam_identification_type_id.id == it_dni.id and not ci.is_valid(partner.vat): partner.l10n_ec_vat_validation = _("The VAT %s seems to be invalid as the tenth digit doesn't comply with the validation algorithm " - "(could be an old VAT number)") % partner.vat + "(could be an old VAT number)", partner.vat) if partner.l10n_latam_identification_type_id.id == it_ruc.id and not ruc.is_valid(partner.vat): partner.l10n_ec_vat_validation = _("The VAT %s seems to be invalid as the tenth digit doesn't comply with the validation algorithm " - "(SRI has stated that this validation is not required anymore for some VAT numbers)") % partner.vat + "(SRI has stated that this validation is not required anymore for some VAT numbers)", partner.vat) def _l10n_ec_get_identification_type(self): """Maps Odoo identification types to Ecuadorian ones. diff --git a/addons/l10n_fr/models/res_company.py b/addons/l10n_fr/models/res_company.py index 39dd96d722c..688edcf9207 100644 --- a/addons/l10n_fr/models/res_company.py +++ b/addons/l10n_fr/models/res_company.py @@ -52,7 +52,7 @@ class ResCompany(models.Model): for seq_field in sequence_fields: if not company[seq_field]: vals = { - 'name': _('Securisation of %s - %s') % (seq_field, company.name), + 'name': _('Securisation of %s - %s', seq_field, company.name), 'code': 'FRSECURE%s-%s' % (company.id, seq_field), 'implementation': 'no_gap', 'prefix': '', diff --git a/addons/l10n_fr_pos_cert/models/res_company.py b/addons/l10n_fr_pos_cert/models/res_company.py index a6cd8c793c6..9a0afa64431 100644 --- a/addons/l10n_fr_pos_cert/models/res_company.py +++ b/addons/l10n_fr_pos_cert/models/res_company.py @@ -96,4 +96,4 @@ class ResCompany(models.Model): 'corrupted_orders': corrupted_orders or 'None' } else: - raise UserError(_('Accounting is not unalterable for the company %s. This mechanism is designed for companies where accounting is unalterable.') % self.env.company.name) + raise UserError(_('Accounting is not unalterable for the company %s. This mechanism is designed for companies where accounting is unalterable.', self.env.company.name)) diff --git a/addons/l10n_in/models/account_invoice.py b/addons/l10n_in/models/account_invoice.py index 84fc13e9631..e77180df039 100644 --- a/addons/l10n_in/models/account_invoice.py +++ b/addons/l10n_in/models/account_invoice.py @@ -70,7 +70,7 @@ class AccountMove(models.Model): for move in posted.filtered(lambda m: m.country_code == 'IN'): if not move.company_id.state_id: msg = _("Your company %s needs to have a correct address in order to validate this invoice.\n" - "Set the address of your company (Don't forget the State field)") % (move.company_id.name) + "Set the address of your company (Don't forget the State field)", move.company_id.name) action = { "view_mode": "form", "res_model": "res.company", diff --git a/addons/l10n_in_edi_ewaybill/models/account_move.py b/addons/l10n_in_edi_ewaybill/models/account_move.py index 4917d1ec11c..39dd0a3e33a 100755 --- a/addons/l10n_in_edi_ewaybill/models/account_move.py +++ b/addons/l10n_in_edi_ewaybill/models/account_move.py @@ -107,11 +107,11 @@ class AccountMove(models.Model): raise UserError(_("You can only create E-waybill from posted invoice")) errors = edi_format._check_move_configuration(move) if errors: - raise UserError(_("Invalid invoice configuration:\n\n%s") % '\n'.join(errors)) + raise UserError(_("Invalid invoice configuration:\n\n%s", '\n'.join(errors))) existing_edi_document = move.edi_document_ids.filtered(lambda x: x.edi_format_id == edi_format) if existing_edi_document: if existing_edi_document.state in ('sent', 'to_cancel'): - raise UserError(_("E-waybill is already created") % '\n'.join(errors)) + raise UserError(_("E-waybill is already created")) existing_edi_document.sudo().write({ 'state': 'to_send', 'attachment_id': False, diff --git a/addons/l10n_latam_invoice_document/wizards/account_move_reversal.py b/addons/l10n_latam_invoice_document/wizards/account_move_reversal.py index 88efd3c0799..2045b600a8d 100644 --- a/addons/l10n_latam_invoice_document/wizards/account_move_reversal.py +++ b/addons/l10n_latam_invoice_document/wizards/account_move_reversal.py @@ -48,7 +48,7 @@ class AccountMoveReversal(models.TransientModel): if len(record.move_ids) > 1: move_ids_use_document = record.move_ids._origin.filtered(lambda move: move.l10n_latam_use_documents) if move_ids_use_document: - raise UserError(_('You can only reverse documents with legal invoicing documents from Latin America one at a time.\nProblematic documents: %s') % ", ".join(move_ids_use_document.mapped('name'))) + raise UserError(_('You can only reverse documents with legal invoicing documents from Latin America one at a time.\nProblematic documents: %s', ", ".join(move_ids_use_document.mapped('name')))) else: record.l10n_latam_use_documents = record.move_ids.journal_id.l10n_latam_use_documents diff --git a/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py b/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py index a45b62c2be9..097b441df79 100644 --- a/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py +++ b/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py @@ -88,19 +88,19 @@ class AccountEdiXmlUBLRO(models.AbstractModel): if not partner.vat and not partner.company_registry: constraints[f"ciusro_{partner_type}_tax_identifier_required"] = _( "The following partner doesn't have a VAT nor Company ID: %s. " - "At least one of them is required. " - ) % partner.name + "At least one of them is required. ", + partner.name) if partner.vat and not partner.vat.startswith(partner.country_code): constraints[f"ciusro_{partner_type}_country_code_vat_required"] = _( - "The following partner's doesn't have a country code prefix in their VAT: %s. " - ) % partner.name + "The following partner's doesn't have a country code prefix in their VAT: %s.", + partner.name) if (not partner.vat and partner.company_registry and not partner.company_registry.startswith(partner.country_code)): constraints[f"ciusro_{partner_type}_country_code_company_registry_required"] = _( - "The following partner's doesn't have a country code prefix in their Company ID: %s. " - ) % partner.name + "The following partner's doesn't have a country code prefix in their Company ID: %s.", + partner.name) if (partner.country_code == 'RO' and partner.state_id @@ -109,7 +109,7 @@ class AccountEdiXmlUBLRO(models.AbstractModel): constraints[f"ciusro_{partner_type}_invalid_city_name"] = _( "The following partner's city name is invalid: %s. " "If partner's state is București, the city name must be 'SECTORX', " - "where X is a number between 1-6. " - ) % partner.name + "where X is a number between 1-6.", + partner.name) return constraints diff --git a/addons/l10n_sa_edi/models/account_edi_format.py b/addons/l10n_sa_edi/models/account_edi_format.py index faf90ed5919..239ae968790 100644 --- a/addons/l10n_sa_edi/models/account_edi_format.py +++ b/addons/l10n_sa_edi/models/account_edi_format.py @@ -116,9 +116,9 @@ class AccountEdiFormat(models.Model): """ mode = 'reporting' if invoice._l10n_sa_is_simplified() else 'clearance' if mode == 'clearance' and clearance_data.get('clearanceStatus', '') != 'CLEARED': - return {'error': _("Invoice could not be cleared: \r\n %s ") % clearance_data, 'blocking_level': 'error'} + return {'error': _("Invoice could not be cleared:\n%s", clearance_data), 'blocking_level': 'error'} elif mode == 'reporting' and clearance_data.get('reportingStatus', '') != 'REPORTED': - return {'error': _("Invoice could not be reported: \r\n %s ") % clearance_data, 'blocking_level': 'error'} + return {'error': _("Invoice could not be reported:\n%s", clearance_data), 'blocking_level': 'error'} return clearance_data # ====== UBL Document Rendering & Submission ======= @@ -149,7 +149,7 @@ class AccountEdiFormat(models.Model): xml_content, errors = self.env['account.edi.xml.ubl_21.zatca']._export_invoice(invoice) if errors: return { - 'error': _("Could not generate Invoice UBL content: %s") % ", \n".join(errors), + 'error': _("Could not generate Invoice UBL content: %s", ", \n".join(errors)), 'blocking_level': 'error' } return self._l10n_sa_postprocess_zatca_template(xml_content) @@ -391,7 +391,7 @@ class AccountEdiFormat(models.Model): """ def _set_missing_partner_fields(missing_fields, name): - return _("- Please, set the following fields on the %s: %s") % (name, ', '.join(missing_fields)) + return _("- Please, set the following fields on the %s: %s", name, ', '.join(missing_fields)) journal = invoice.journal_id company = invoice.company_id @@ -408,17 +408,17 @@ class AccountEdiFormat(models.Model): if not journal._l10n_sa_ready_to_submit_einvoices(): errors.append( - _("- Finish the Onboarding procees for journal %s by requesting the CSIDs and completing the checks.") % journal.name) + _("- Finish the Onboarding procees for journal %s by requesting the CSIDs and completing the checks.", journal.name)) if not company._l10n_sa_check_organization_unit(): errors.append( _("- The company VAT identification must contain 15 digits, with the first and last digits being '3' as per the BR-KSA-39 and BR-KSA-40 of ZATCA KSA business rule.")) if not company.sudo().l10n_sa_private_key: errors.append( - _("- No Private Key was generated for company %s. A Private Key is mandatory in order to generate Certificate Signing Requests (CSR).") % company.name) + _("- No Private Key was generated for company %s. A Private Key is mandatory in order to generate Certificate Signing Requests (CSR).", company.name)) if not journal.l10n_sa_serial_number: errors.append( - _("- No Serial Number was assigned for journal %s. A Serial Number is mandatory in order to generate Certificate Signing Requests (CSR).") % journal.name) + _("- No Serial Number was assigned for journal %s. A Serial Number is mandatory in order to generate Certificate Signing Requests (CSR).", journal.name)) supplier_missing_info = self._l10n_sa_check_seller_missing_info(invoice) customer_missing_info = self._l10n_sa_check_buyer_missing_info(invoice) diff --git a/addons/l10n_sa_edi/models/account_journal.py b/addons/l10n_sa_edi/models/account_journal.py index 569341c9438..aeac7ae31f4 100644 --- a/addons/l10n_sa_edi/models/account_journal.py +++ b/addons/l10n_sa_edi/models/account_journal.py @@ -242,7 +242,7 @@ class AccountJournal(models.Model): """ CCSID_data = self._l10n_sa_api_get_compliance_CSID(otp) if CCSID_data.get('error'): - raise UserError(_("Could not obtain Compliance CSID: %s") % CCSID_data['error']) + raise UserError(_("Could not obtain Compliance CSID: %s", CCSID_data['error'])) self.sudo().write({ 'l10n_sa_compliance_csid_json': json.dumps(CCSID_data), 'l10n_sa_production_csid_json': False, @@ -274,7 +274,7 @@ class AccountJournal(models.Model): CCSID_data = json.loads(self_sudo.l10n_sa_compliance_csid_json) PCSID_data = self_sudo._l10n_sa_request_production_csid(CCSID_data, renew, OTP) if PCSID_data.get('error'): - raise UserError(_("Could not obtain Production CSID: %s") % PCSID_data['error']) + raise UserError(_("Could not obtain Production CSID: %s", PCSID_data['error'])) self_sudo.l10n_sa_production_csid_json = json.dumps(PCSID_data) # ====== Compliance Checks ======= diff --git a/addons/l10n_sa_edi/models/res_config_settings.py b/addons/l10n_sa_edi/models/res_config_settings.py index 64ed9e43959..a3684e0ba65 100644 --- a/addons/l10n_sa_edi/models/res_config_settings.py +++ b/addons/l10n_sa_edi/models/res_config_settings.py @@ -11,4 +11,9 @@ class ResConfigSettings(models.TransientModel): super()._compute_company_informations() for record in self: if self.company_id.country_code == 'SA': - record.company_informations += _('\nBuilding Number: %s, Plot Identification: %s \nNeighborhood: %s') % (self.company_id.l10n_sa_edi_building_number, self.company_id.l10n_sa_edi_plot_identification, self.company_id.street2) + record.company_informations += _( + '\nBuilding Number: %s, Plot Identification: %s\nNeighborhood: %s', + self.company_id.l10n_sa_edi_building_number, + self.company_id.l10n_sa_edi_plot_identification, + self.company_id.street2, + ) diff --git a/addons/l10n_se/models/account_move.py b/addons/l10n_se/models/account_move.py index f0fbb6c987c..ae7c2e687aa 100644 --- a/addons/l10n_se/models/account_move.py +++ b/addons/l10n_se/models/account_move.py @@ -24,7 +24,7 @@ class AccountMove(models.Model): ocr_length = self.journal_id.l10n_se_invoice_ocr_length if len(reference) + 1 > ocr_length: - raise UserError(_("OCR Reference Number length is greater than allowed. Allowed length in invoice journal setting is %s.") % str(ocr_length)) + raise UserError(_("OCR Reference Number length is greater than allowed. Allowed length in invoice journal setting is %s.", ocr_length)) reference = reference.rjust(ocr_length - 1, '0') return reference + luhn.calc_check_digit(reference) diff --git a/addons/mail/models/mail_tracking_duration_mixin.py b/addons/mail/models/mail_tracking_duration_mixin.py index 43a1461eaf1..735f9870ff9 100644 --- a/addons/mail/models/mail_tracking_duration_mixin.py +++ b/addons/mail/models/mail_tracking_duration_mixin.py @@ -37,12 +37,10 @@ class MailTrackingDurationMixin(models.AbstractModel): or self._fields[self._track_duration_field].type != 'many2one' ): self.duration_tracking = False - raise ValueError( - _('Field "%(field)s" on model "%(model)s" must be of type Many2one ' - 'and have tracking=True for the computation of duration.') % { - 'field': self._track_duration_field, 'model': self._name - } - ) + raise ValueError(_( + 'Field "(field)r on model %(model)r must be of type Many2one and have tracking=True for the computation of duration.', + field=self._track_duration_field, model=self._name + )) self.env['mail.tracking.value'].flush_model() self.env['mail.message'].flush_model() diff --git a/addons/mass_mailing_sms/controllers/main.py b/addons/mass_mailing_sms/controllers/main.py index 59e00008b85..35aee22dbd1 100644 --- a/addons/mass_mailing_sms/controllers/main.py +++ b/addons/mass_mailing_sms/controllers/main.py @@ -68,8 +68,8 @@ class MailingSMSController(http.Controller): else: blacklist_rec = request.env['phone.blacklist'].sudo().add(tocheck_number) blacklist_rec._message_log( - body=_('Blacklist through SMS Marketing unsubscribe (mailing ID: %s - model: %s)') % - (trace.mass_mailing_id.id, trace.mass_mailing_id.mailing_model_id.display_name)) + body=_('Blacklist through SMS Marketing unsubscribe (mailing ID: %s - model: %s)', + trace.mass_mailing_id.id, trace.mass_mailing_id.mailing_model_id.display_name)) lists_optin = request.env['mailing.subscription'].sudo().search([ ('contact_id.phone_sanitized', '=', tocheck_number), ('list_id', 'not in', mailing_list_ids.ids), diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 7c15be50981..2b8ee43d5af 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -126,8 +126,9 @@ class MrpBom(models.Model): for component in components: if component in finished_products: names = finished_products.mapped('display_name') - raise ValidationError(_("The current configuration is incorrect because it would create a cycle " - "between these products: %s.") % ', '.join(names)) + raise ValidationError(_( + "The current configuration is incorrect because it would create a cycle between these products: %s.", + ', '.join(names))) if component not in subcomponents_dict: products_to_find |= component @@ -182,7 +183,7 @@ class MrpBom(models.Model): else: same_product = bom.product_tmpl_id == byproduct.product_id.product_tmpl_id if same_product: - raise ValidationError(_("By-product %s should not be the same as BoM product.") % bom.display_name) + raise ValidationError(_("By-product %s should not be the same as BoM product.", bom.display_name)) if byproduct.cost_share < 0: raise ValidationError(_("By-products cost shares must be positive.")) if sum(bom.byproduct_ids.mapped('cost_share')) > 100: diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 0ea0f82a2b2..d07739c7374 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -1312,7 +1312,7 @@ class MrpProduction(models.Model): }) else: action.update({ - 'name': _("%s Child MO's") % self.name, + 'name': _("%s Child MO's", self.name), 'domain': [('id', 'in', mrp_production_ids)], 'view_mode': 'tree,form', }) @@ -1332,7 +1332,7 @@ class MrpProduction(models.Model): }) else: action.update({ - 'name': _("MO Generated by %s") % self.name, + 'name': _("MO Generated by %s", self.name), 'domain': [('id', 'in', mrp_production_ids)], 'view_mode': 'tree,form', }) diff --git a/addons/mrp/models/mrp_routing.py b/addons/mrp/models/mrp_routing.py index a68cc26f3d4..fade265a2a6 100644 --- a/addons/mrp/models/mrp_routing.py +++ b/addons/mrp/models/mrp_routing.py @@ -62,7 +62,7 @@ class MrpRoutingWorkcenter(models.Model): @api.depends('time_mode', 'time_mode_batch') def _compute_time_computed_on(self): for operation in self: - operation.time_computed_on = _('%i work orders') % operation.time_mode_batch if operation.time_mode != 'manual' else False + operation.time_computed_on = _('%i work orders', operation.time_mode_batch) if operation.time_mode != 'manual' else False @api.depends('time_cycle_manual', 'time_mode', 'workorder_ids') def _compute_time_cycle(self): diff --git a/addons/mrp/models/stock_rule.py b/addons/mrp/models/stock_rule.py index a0a4221e62f..7624d4b7804 100644 --- a/addons/mrp/models/stock_rule.py +++ b/addons/mrp/models/stock_rule.py @@ -7,7 +7,6 @@ from dateutil.relativedelta import relativedelta from odoo import api, fields, models, SUPERUSER_ID, _ from odoo.osv import expression -from odoo.addons.stock.models.stock_rule import ProcurementException from odoo.tools import float_compare, OrderedSet @@ -19,13 +18,11 @@ class StockRule(models.Model): def _get_message_dict(self): message_dict = super(StockRule, self)._get_message_dict() - source, destination, operation = self._get_message_values() - manufacture_message = _('When products are needed in %s,
a manufacturing order is created to fulfill the need.') % (destination) + source, destination, __ = self._get_message_values() + manufacture_message = _('When products are needed in %s,
a manufacturing order is created to fulfill the need.', destination) if self.location_src_id: - manufacture_message += _('

The components will be taken from %s.') % (source) - message_dict.update({ - 'manufacture': manufacture_message - }) + manufacture_message += _('

The components will be taken from %s.', source) + message_dict['manufacture'] = manufacture_message return message_dict @api.depends('action') diff --git a/addons/mrp/wizard/stock_assign_serial_numbers.py b/addons/mrp/wizard/stock_assign_serial_numbers.py index 84b0fe6c837..2c778e3c25a 100644 --- a/addons/mrp/wizard/stock_assign_serial_numbers.py +++ b/addons/mrp/wizard/stock_assign_serial_numbers.py @@ -42,7 +42,7 @@ class StockAssignSerialNumbers(models.TransientModel): if duplicate_serial_numbers: self.serial_numbers = "" self.produced_qty = 0 - raise UserError(_('Duplicate Serial Numbers (%s)') % ','.join(duplicate_serial_numbers)) + raise UserError(_('Duplicate Serial Numbers (%s)', ','.join(duplicate_serial_numbers))) existing_serial_numbers = self.env['stock.lot'].search([ ('company_id', '=', self.production_id.company_id.id), ('product_id', '=', self.production_id.product_id.id), @@ -51,14 +51,14 @@ class StockAssignSerialNumbers(models.TransientModel): if existing_serial_numbers: self.serial_numbers = "" self.produced_qty = 0 - raise UserError(_('Existing Serial Numbers (%s)') % ','.join(existing_serial_numbers.mapped('display_name'))) + raise UserError(_('Existing Serial Numbers (%s)', ','.join(existing_serial_numbers.mapped('display_name')))) if len(serial_numbers) > self.expected_qty: self.serial_numbers = "" self.produced_qty = 0 raise UserError(_('There are more Serial Numbers than the Quantity to Produce')) self.produced_qty = len(serial_numbers) self.show_apply = self.produced_qty == self.expected_qty - self.show_backorders = self.produced_qty > 0 and self.produced_qty < self.expected_qty + self.show_backorders = 0 < self.produced_qty < self.expected_qty def _assign_serial_numbers(self, cancel_remaining_quantity=False): serial_numbers = self._get_serial_numbers() diff --git a/addons/mrp_subcontracting/models/mrp_production.py b/addons/mrp_subcontracting/models/mrp_production.py index 8fc7a28d55a..d0c847f7327 100644 --- a/addons/mrp_subcontracting/models/mrp_production.py +++ b/addons/mrp_subcontracting/models/mrp_production.py @@ -63,10 +63,10 @@ class MrpProduction(models.Model): if float_is_zero(self.qty_producing, precision_rounding=self.product_uom_id.rounding): return {'type': 'ir.actions.act_window_close'} if self.product_tracking != 'none' and not self.lot_producing_id: - raise UserError(_('You must enter a serial number for %s') % self.product_id.name) + raise UserError(_('You must enter a serial number for %s', self.product_id.name)) for sml in self.move_raw_ids.move_line_ids: if sml.tracking != 'none' and not sml.lot_id: - raise UserError(_('You must enter a serial number for each line of %s') % sml.product_id.display_name) + raise UserError(_('You must enter a serial number for each line of %s', sml.product_id.display_name)) if self.move_raw_ids and not any(self.move_raw_ids.mapped('quantity_done')): raise UserError(_("You must indicate a non-zero amount consumed for at least one of your components")) consumption_issues = self._get_consumption_issues() diff --git a/addons/mrp_subcontracting/models/stock_move.py b/addons/mrp_subcontracting/models/stock_move.py index 4dddcd35f19..ad723e07b46 100644 --- a/addons/mrp_subcontracting/models/stock_move.py +++ b/addons/mrp_subcontracting/models/stock_move.py @@ -126,7 +126,7 @@ class StockMove(models.Model): form_view = self.env.ref('mrp_subcontracting.mrp_subcontracting_portal_move_form_view') ctx.update(no_breadcrumbs=False) return { - 'name': _('Raw Materials for %s') % (self.product_id.display_name), + 'name': _('Raw Materials for %s', self.product_id.display_name), 'type': 'ir.actions.act_window', 'res_model': 'stock.move', 'views': [(tree_view.id, 'list'), (form_view.id, 'form')], diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 7b91e4a732b..16b6fc7ee29 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -102,7 +102,7 @@ class PosOrder(models.Model): _logger.warning('attempting to create recovery session for saving order %s', order['name']) new_session = PosSession.create({ 'config_id': closed_session.config_id.id, - 'name': _('(RESCUE FOR %(session)s)') % {'session': closed_session.name}, + 'name': _('(RESCUE FOR %(session)s)', session=closed_session.name), 'rescue': True, # avoid conflict with live sessions }) # bypass opening_control (necessary when using cash control) diff --git a/addons/point_of_sale/models/pos_payment.py b/addons/point_of_sale/models/pos_payment.py index bd5d2d843e3..bd0ff002a10 100644 --- a/addons/point_of_sale/models/pos_payment.py +++ b/addons/point_of_sale/models/pos_payment.py @@ -76,7 +76,7 @@ class PosPayment(models.Model): payment_move = self.env['account.move'].with_context(default_journal_id=journal.id).create({ 'journal_id': journal.id, 'date': fields.Date.context_today(payment), - 'ref': _('Invoice payment for %s (%s) using %s') % (order.name, order.account_move.name, payment_method.name), + 'ref': _('Invoice payment for %s (%s) using %s', order.name, order.account_move.name, payment_method.name), 'pos_payment_ids': payment.ids, }) result |= payment_move diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index b3559a296c4..dfd12be4940 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -191,10 +191,10 @@ class PosSession(models.Model): def _check_invoices_are_posted(self): unposted_invoices = self.order_ids.sudo().with_company(self.company_id).account_move.filtered(lambda x: x.state != 'posted') if unposted_invoices: - raise UserError(_('You cannot close the POS when invoices are not posted.\n' - 'Invoices: %s') % str.join('\n', - ['%s - %s' % (invoice.name, invoice.state) for invoice in - unposted_invoices])) + raise UserError(_( + 'You cannot close the POS when invoices are not posted.\nInvoices: %s', + '\n'.join(f'{invoice.name} - {invoice.state}' for invoice in unposted_invoices) + )) @api.model_create_multi def create(self, vals_list): @@ -877,13 +877,13 @@ class PosSession(models.Model): tax_vals = [self._get_tax_vals(key, amounts['amount'], amounts['amount_converted'], amounts['base_amount_converted']) for key, amounts in taxes.items()] # Check if all taxes lines have account_id assigned. If not, there are repartition lines of the tax that have no account_id. - tax_names_no_account = [line['name'] for line in tax_vals if line['account_id'] == False] - if len(tax_names_no_account) > 0: - error_message = _( + tax_names_no_account = [line['name'] for line in tax_vals if not line['account_id']] + if tax_names_no_account: + raise UserError(_( 'Unable to close and validate the session.\n' - 'Please set corresponding tax account in each repartition line of the following taxes: \n%s' - ) % ', '.join(tax_names_no_account) - raise UserError(error_message) + 'Please set corresponding tax account in each repartition line of the following taxes: \n%s', + ', '.join(tax_names_no_account) + )) rounding_vals = [] if not float_is_zero(rounding_difference['amount'], precision_rounding=self.currency_id.rounding) or not float_is_zero(rounding_difference['amount_converted'], precision_rounding=self.currency_id.rounding): @@ -949,7 +949,7 @@ class PosSession(models.Model): 'journal_id': payment_method.journal_id.id, 'force_outstanding_account_id': outstanding_account.id, 'destination_account_id': destination_account.id, - 'ref': _('Combine %s POS payments from %s') % (payment_method.name, self.name), + 'ref': _('Combine %s POS payments from %s', payment_method.name, self.name), 'pos_payment_method_id': payment_method.id, 'pos_session_id': self.id, }) @@ -994,7 +994,7 @@ class PosSession(models.Model): 'journal_id': payment_method.journal_id.id, 'force_outstanding_account_id': outstanding_account.id, 'destination_account_id': destination_account.id, - 'ref': _('%s POS payment of %s in %s') % (payment_method.name, payment.partner_id.display_name, self.name), + 'ref': _('%s POS payment of %s in %s', payment_method.name, payment.partner_id.display_name, self.name), 'pos_payment_method_id': payment_method.id, 'pos_session_id': self.id, }) @@ -1185,8 +1185,8 @@ class PosSession(models.Model): product = order_line.product_id income_account = product.with_company(order_line.company_id)._get_product_accounts()['income'] or self.config_id.journal_id.default_account_id if not income_account: - raise UserError(_('Please define income account for this product: "%s" (id:%d).') - % (product.name, product.id)) + raise UserError(_('Please define income account for this product: "%s" (id:%d).', + product.name, product.id)) return order_line.order_id.fiscal_position_id.map_account(income_account) company_domain = self.env['account.tax']._check_company_domain(order_line.order_id.company_id) @@ -1232,8 +1232,9 @@ class PosSession(models.Model): accounting_partner = self.env["res.partner"]._find_accounting_partner(payment.partner_id) if not accounting_partner: raise UserError(_("You have enabled the \"Identify Customer\" option for %s payment method," - "but the order %s does not contain a customer.") % (payment.payment_method_id.name, - payment.pos_order_id.name)) + "but the order %s does not contain a customer.", + payment.payment_method_id.name, + payment.pos_order_id.name)) partial_vals = { 'account_id': accounting_partner.property_account_receivable_id.id, 'move_id': self.move_id.id, @@ -1573,9 +1574,9 @@ class PosSession(models.Model): if draft_orders: raise UserError(_( 'There are still orders in draft state in the session. ' - 'Pay or cancel the following orders to validate the session:\n%s' - ) % ', '.join(draft_orders.mapped('name')) - ) + 'Pay or cancel the following orders to validate the session:\n%s', + ', '.join(draft_orders.mapped('name')) + )) return True def try_cash_in_out(self, _type, amount, reason, extras): diff --git a/addons/point_of_sale/models/product.py b/addons/point_of_sale/models/product.py index de971654832..99287159a5b 100644 --- a/addons/point_of_sale/models/product.py +++ b/addons/point_of_sale/models/product.py @@ -43,7 +43,7 @@ class ProductTemplate(models.Model): if not product.available_in_pos: combo_name = self.env['pos.combo.line'].search([('product_id', 'in', product.product_variant_ids.ids)], limit=1).combo_id.name if combo_name: - raise UserError(_('You must first remove this product from the %s combo') % combo_name) + raise UserError(_('You must first remove this product from the %s combo', combo_name)) class ProductProduct(models.Model): _inherit = 'product.product' diff --git a/addons/point_of_sale/models/res_company.py b/addons/point_of_sale/models/res_company.py index 277903632bb..aef68828e5d 100644 --- a/addons/point_of_sale/models/res_company.py +++ b/addons/point_of_sale/models/res_company.py @@ -36,4 +36,4 @@ class ResCompany(models.Model): ) if sessions_in_period: sessions_str = ', '.join(sessions_in_period.mapped('name')) - raise ValidationError(_("Please close all the point of sale sessions in this period before closing it. Open sessions are: %s ") % (sessions_str)) + raise ValidationError(_("Please close all the point of sale sessions in this period before closing it. Open sessions are: %s ", sessions_str)) diff --git a/addons/point_of_sale/report/pos_invoice.py b/addons/point_of_sale/report/pos_invoice.py index bc85f273578..fe4216808ec 100644 --- a/addons/point_of_sale/report/pos_invoice.py +++ b/addons/point_of_sale/report/pos_invoice.py @@ -22,7 +22,7 @@ class PosInvoiceReport(models.AbstractModel): if not_invoiced_orders_ids: not_invoiced_posorders = PosOrder.browse(not_invoiced_orders_ids) not_invoiced_orders_names = [a.name for a in not_invoiced_posorders] - raise UserError(_('No link to an invoice for %s.') % ', '.join(not_invoiced_orders_names)) + raise UserError(_('No link to an invoice for %s.', ', '.join(not_invoiced_orders_names))) return { 'docs': self.env['account.move'].sudo().browse(ids_to_print), diff --git a/addons/pos_adyen/models/pos_payment_method.py b/addons/pos_adyen/models/pos_payment_method.py index acb31e5f264..9746fb6fefe 100644 --- a/addons/pos_adyen/models/pos_payment_method.py +++ b/addons/pos_adyen/models/pos_payment_method.py @@ -38,11 +38,11 @@ class PosPaymentMethod(models.Model): limit=1) if existing_payment_method: if existing_payment_method.company_id == payment_method.company_id: - raise ValidationError(_('Terminal %s is already used on payment method %s.') - % (payment_method.adyen_terminal_identifier, existing_payment_method.display_name)) + raise ValidationError(_('Terminal %s is already used on payment method %s.', + payment_method.adyen_terminal_identifier, existing_payment_method.display_name)) else: - raise ValidationError(_('Terminal %s is already used in company %s on payment method %s.') - % (payment_method.adyen_terminal_identifier, + raise ValidationError(_('Terminal %s is already used in company %s on payment method %s.', + payment_method.adyen_terminal_identifier, existing_payment_method.company_id.name, existing_payment_method.display_name)) @@ -52,7 +52,7 @@ class PosPaymentMethod(models.Model): } def _is_write_forbidden(self, fields): - whitelisted_fields = set(('adyen_latest_response', 'adyen_latest_diagnosis')) + whitelisted_fields = {'adyen_latest_response', 'adyen_latest_diagnosis'} return super(PosPaymentMethod, self)._is_write_forbidden(fields - whitelisted_fields) def _adyen_diagnosis_request_data(self, pos_config_name): diff --git a/addons/pos_hr/models/hr_employee.py b/addons/pos_hr/models/hr_employee.py index 50304185d55..fbde5d2d45e 100644 --- a/addons/pos_hr/models/hr_employee.py +++ b/addons/pos_hr/models/hr_employee.py @@ -32,6 +32,6 @@ class HrEmployee(models.Model): for employee in self: config_ids = configs_with_all_employees | configs_with_specific_employees.filtered(lambda c: employee in c.basic_employee_ids) if config_ids: - error_msg += _("Employee: %s - PoS Config(s): %s \n") % (employee.name, ', '.join(config.name for config in config_ids)) + error_msg += _("Employee: %s - PoS Config(s): %s \n", employee.name, ', '.join(config.name for config in config_ids)) raise UserError(error_msg) diff --git a/addons/pos_online_payment/models/pos_session.py b/addons/pos_online_payment/models/pos_session.py index 2c8d015566f..9aa88474912 100644 --- a/addons/pos_online_payment/models/pos_session.py +++ b/addons/pos_online_payment/models/pos_session.py @@ -56,7 +56,7 @@ class PosSession(models.Model): partner = payment.online_account_payment_id.partner_id accounting_partner = self.env["res.partner"]._find_accounting_partner(partner) if not accounting_partner: - raise UserError(_("The partner of the POS online payment (id=%d) could not be found") % (payment.id)) + raise UserError(_("The partner of the POS online payment (id=%d) could not be found", payment.id)) partial_vals = { 'account_id': accounting_partner.property_account_receivable_id.id, 'move_id': self.move_id.id, diff --git a/addons/pos_restaurant/models/pos_restaurant.py b/addons/pos_restaurant/models/pos_restaurant.py index 78b57508393..df741f18ef0 100644 --- a/addons/pos_restaurant/models/pos_restaurant.py +++ b/addons/pos_restaurant/models/pos_restaurant.py @@ -23,14 +23,13 @@ class RestaurantFloor(models.Model): def _unlink_except_active_pos_session(self): confs = self.mapped('pos_config_ids').filtered(lambda c: c.module_pos_restaurant) opened_session = self.env['pos.session'].search([('config_id', 'in', confs.ids), ('state', '!=', 'closed')]) - if opened_session: + if opened_session and confs: error_msg = _("You cannot remove a floor that is used in a PoS session, close the session(s) first: \n") for floor in self: for session in opened_session: if floor in session.config_id.floor_ids: - error_msg += _("Floor: %s - PoS Config: %s \n") % (floor.name, session.config_id.name) - if confs: - raise UserError(error_msg) + error_msg += _("Floor: %s - PoS Config: %s \n", floor.name, session.config_id.name) + raise UserError(error_msg) def write(self, vals): for floor in self: diff --git a/addons/product/models/decimal_precision.py b/addons/product/models/decimal_precision.py index 0539fbb32dc..11797835d52 100644 --- a/addons/product/models/decimal_precision.py +++ b/addons/product/models/decimal_precision.py @@ -35,6 +35,6 @@ class DecimalPrecision(models.Model): "You are setting a Decimal Accuracy less precise than the UOMs:\n" "%s\n" "This may cause inconsistencies in computations.\n" - "Please increase the rounding of those units of measure, or the digits of this Decimal Accuracy." - ) % ('\n'.join(uom_descriptions)), + "Please increase the rounding of those units of measure, or the digits of this Decimal Accuracy.", + '\n'.join(uom_descriptions)), }} diff --git a/addons/product/models/product_pricelist_item.py b/addons/product/models/product_pricelist_item.py index 30f665a8f34..790f3f39d73 100644 --- a/addons/product/models/product_pricelist_item.py +++ b/addons/product/models/product_pricelist_item.py @@ -143,11 +143,11 @@ class PricelistItem(models.Model): def _compute_name_and_price(self): for item in self: if item.categ_id and item.applied_on == '2_product_category': - item.name = _("Category: %s") % (item.categ_id.display_name) + item.name = _("Category: %s", item.categ_id.display_name) elif item.product_tmpl_id and item.applied_on == '1_product': - item.name = _("Product: %s") % (item.product_tmpl_id.display_name) + item.name = _("Product: %s", item.product_tmpl_id.display_name) elif item.product_id and item.applied_on == '0_product_variant': - item.name = _("Variant: %s") % (item.product_id.display_name) + item.name = _("Variant: %s", item.product_id.display_name) else: item.name = _("All Products") diff --git a/addons/product/models/uom_uom.py b/addons/product/models/uom_uom.py index cae84ba1ba0..63b7072346f 100644 --- a/addons/product/models/uom_uom.py +++ b/addons/product/models/uom_uom.py @@ -16,6 +16,6 @@ class UoM(models.Model): 'message': _( "This rounding precision is higher than the Decimal Accuracy" " (%s digits).\nThis may cause inconsistencies in computations.\n" - "Please set a precision between %s and 1." - ) % (precision, 1.0 / 10.0**precision), + "Please set a precision between %s and 1.", + precision, 1.0 / 10.0**precision), }} diff --git a/addons/project/models/project_project.py b/addons/project/models/project_project.py index 24f728700d4..170e7de20f8 100644 --- a/addons/project/models/project_project.py +++ b/addons/project/models/project_project.py @@ -334,9 +334,9 @@ class Project(models.Model): @api.model def _search_is_milestone_exceeded(self, operator, value): if not isinstance(value, bool): - raise ValueError(_('Invalid value: %s') % value) + raise ValueError(_('Invalid value: %s', value)) if operator not in ['=', '!=']: - raise ValueError(_('Invalid operator: %s') % operator) + raise ValueError(_('Invalid operator: %s', operator)) query = """ SELECT P.id @@ -431,7 +431,7 @@ class Project(models.Model): if default is None: default = {} if not default.get('name'): - default['name'] = _("%s (copy)") % (self.name) + default['name'] = _("%s (copy)", self.name) self_with_mail_context = self.with_context(mail_auto_subscribe_no_notify=True, mail_create_nosubscribe=True) project = super(Project, self_with_mail_context).copy(default) for follower in self.message_follower_ids: diff --git a/addons/project/models/project_project_stage.py b/addons/project/models/project_project_stage.py index 84712cb8c6a..2e97e15ed13 100644 --- a/addons/project/models/project_project_stage.py +++ b/addons/project/models/project_project_stage.py @@ -21,7 +21,7 @@ class ProjectProjectStage(models.Model): def copy(self, default=None): default = dict(default or {}) if not default.get('name'): - default['name'] = _("%s (copy)") % (self.name) + default['name'] = _("%s (copy)", self.name) return super().copy(default) def unlink_wizard(self, stage_view=False): diff --git a/addons/project/models/project_task.py b/addons/project/models/project_task.py index d4509a423a4..4a7d6a87787 100644 --- a/addons/project/models/project_task.py +++ b/addons/project/models/project_task.py @@ -828,7 +828,7 @@ class Task(models.Model): def _ensure_portal_user_can_write(self, fields): for field in fields: if field not in self.SELF_WRITABLE_FIELDS: - raise AccessError(_('You have not write access of %s field.') % field) + raise AccessError(_('You have not write access of %s field.', field)) def _load_records_create(self, vals_list): for vals in vals_list: diff --git a/addons/project/models/project_task_type.py b/addons/project/models/project_task_type.py index d82a1b08abd..d27a9dfce58 100644 --- a/addons/project/models/project_task_type.py +++ b/addons/project/models/project_task_type.py @@ -76,7 +76,7 @@ class ProjectTaskType(models.Model): def copy(self, default=None): default = dict(default or {}) if not default.get('name'): - default['name'] = _("%s (copy)") % (self.name) + default['name'] = _("%s (copy)", self.name) return super().copy(default) def toggle_active(self): diff --git a/addons/project_timesheet_holidays/models/hr_holidays.py b/addons/project_timesheet_holidays/models/hr_holidays.py index 0d0ac1e9210..03a833c9902 100644 --- a/addons/project_timesheet_holidays/models/hr_holidays.py +++ b/addons/project_timesheet_holidays/models/hr_holidays.py @@ -47,7 +47,7 @@ class HolidaysType(models.Model): if not holiday_status.timesheet_project_id or not holiday_status.timesheet_task_id: raise ValidationError(_("Both the internal project and task are required to " "generate a timesheet for the time off %s. If you don't want a timesheet, you should " - "leave the internal project and task empty.") % (holiday_status.name)) + "leave the internal project and task empty.", holiday_status.name)) class Holidays(models.Model): diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index d36ed65c686..8e5a4787eca 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -1098,7 +1098,7 @@ class PurchaseOrderLine(models.Model): lines = super().create(vals_list) for line in lines: if line.product_id and line.order_id.state == 'purchase': - msg = _("Extra line with %s ") % (line.product_id.display_name,) + msg = _("Extra line with %s ", line.product_id.display_name) line.order_id.message_post(body=msg) return lines @@ -1129,7 +1129,7 @@ class PurchaseOrderLine(models.Model): for line in self: if line.order_id.state in ['purchase', 'done']: state_description = {state_desc[0]: state_desc[1] for state_desc in self._fields['state']._description_selection(self.env)} - raise UserError(_('Cannot delete a purchase order line which is in state \'%s\'.') % (state_description.get(line.state),)) + raise UserError(_('Cannot delete a purchase order line which is in state %r.', state_description.get(line.state))) @api.model def _get_date_planned(self, seller, po=False): diff --git a/addons/purchase_mrp/models/stock_move.py b/addons/purchase_mrp/models/stock_move.py index c54f0cbb3a0..8f2efcfdb3e 100644 --- a/addons/purchase_mrp/models/stock_move.py +++ b/addons/purchase_mrp/models/stock_move.py @@ -39,5 +39,5 @@ class StockMove(models.Model): valuation_total_qty = self._compute_kit_quantities(related_aml.product_id, order_qty, kit_bom, filters) valuation_total_qty = kit_bom.product_uom_id._compute_quantity(valuation_total_qty, related_aml.product_id.uom_id) if float_is_zero(valuation_total_qty, precision_rounding=related_aml.product_uom_id.rounding or related_aml.product_id.uom_id.rounding): - raise UserError(_('Odoo is not able to generate the anglo saxon entries. The total valuation of %s is zero.') % related_aml.product_id.display_name) + raise UserError(_('Odoo is not able to generate the anglo saxon entries. The total valuation of %s is zero.', related_aml.product_id.display_name)) return valuation_price_unit_total, valuation_total_qty diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index c5e18ab9983..c49d0e1eb75 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -111,7 +111,7 @@ class PurchaseOrder(models.Model): for order in self: for move in order.order_line.mapped('move_ids'): if move.state == 'done': - raise UserError(_('Unable to cancel purchase order %s as some receptions have already been done.') % (order.name)) + raise UserError(_('Unable to cancel purchase order %s as some receptions have already been done.', order.name)) # If the product is MTO, change the procure_method of the closest move to purchase to MTS. # The purpose is to link the po that the user will manually generate to the existing moves's chain. if order.state in ('draft', 'sent', 'to approve', 'purchase'): diff --git a/addons/purchase_stock/models/stock_move.py b/addons/purchase_stock/models/stock_move.py index 2dade39cbbc..c25eefc6275 100644 --- a/addons/purchase_stock/models/stock_move.py +++ b/addons/purchase_stock/models/stock_move.py @@ -198,7 +198,8 @@ class StockMove(models.Model): valuation_total_qty += layers_qty if float_is_zero(valuation_total_qty, precision_rounding=related_aml.product_uom_id.rounding or related_aml.product_id.uom_id.rounding): raise UserError( - _('Odoo is not able to generate the anglo saxon entries. The total valuation of %s is zero.') % related_aml.product_id.display_name) + _('Odoo is not able to generate the anglo saxon entries. The total valuation of %s is zero.', + related_aml.product_id.display_name)) return valuation_price_unit_total, valuation_total_qty def _is_purchase_return(self): diff --git a/addons/purchase_stock/models/stock_rule.py b/addons/purchase_stock/models/stock_rule.py index b43576c3f14..e2d4bba9238 100644 --- a/addons/purchase_stock/models/stock_rule.py +++ b/addons/purchase_stock/models/stock_rule.py @@ -20,12 +20,12 @@ class StockRule(models.Model): def _get_message_dict(self): message_dict = super(StockRule, self)._get_message_dict() - dummy, destination, dummy = self._get_message_values() + __, destination, __ = self._get_message_values() message_dict.update({ 'buy': _('When products are needed in %s,
' 'a request for quotation is created to fulfill the need.
' 'Note: This rule will be used in combination with the rules
' - 'of the reception route(s)') % (destination) + 'of the reception route(s)', destination) }) return message_dict @@ -72,7 +72,7 @@ class StockRule(models.Model): )[:1] if not supplier: - msg = _('There is no matching vendor price to generate the purchase order for product %s (no vendor defined, minimum quantity not reached, dates not valid, ...). Go on the product form and complete the list of vendors.') % (procurement.product_id.display_name) + msg = _('There is no matching vendor price to generate the purchase order for product %s (no vendor defined, minimum quantity not reached, dates not valid, ...). Go on the product form and complete the list of vendors.', procurement.product_id.display_name) errors.append((procurement, msg)) partner = supplier.partner_id diff --git a/addons/resource/models/resource_calendar.py b/addons/resource/models/resource_calendar.py index d0775f553f4..9855616d792 100644 --- a/addons/resource/models/resource_calendar.py +++ b/addons/resource/models/resource_calendar.py @@ -138,7 +138,7 @@ class ResourceCalendar(models.Model): if default is None: default = {} if not default.get('name'): - default.update(name=_('%s (copy)') % (self.name)) + default['name'] = _('%s (copy)', self.name) return super().copy(default) @api.constrains('attendance_ids') diff --git a/addons/resource/models/resource_resource.py b/addons/resource/models/resource_resource.py index bb4d547c3c4..2b12d08c47c 100644 --- a/addons/resource/models/resource_resource.py +++ b/addons/resource/models/resource_resource.py @@ -68,7 +68,7 @@ class ResourceResource(models.Model): if default is None: default = {} if not default.get('name'): - default.update(name=_('%s (copy)') % (self.name)) + default['name'] = _('%s (copy)', self.name) return super().copy(default) def write(self, values): diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py index cb0ecd11c26..74a0379257b 100644 --- a/addons/sale/models/product_template.py +++ b/addons/sale/models/product_template.py @@ -76,7 +76,7 @@ class ProductTemplate(models.Model): 'sales orders in another company:\n%s\n' 'You can archive these products and recreate them ' 'with your company restriction instead, or leave them as ' - 'shared product.') % (target_company.name, ', '.join(used_products))) + 'shared product.', target_company.name, ', '.join(used_products))) def action_view_sales(self): action = self.env['ir.actions.actions']._for_xml_id('sale.report_all_channels_sales_action') diff --git a/addons/sale/models/sale_order_line.py b/addons/sale/models/sale_order_line.py index 31d6ce2387f..532739d24ad 100644 --- a/addons/sale/models/sale_order_line.py +++ b/addons/sale/models/sale_order_line.py @@ -1001,8 +1001,8 @@ class SaleOrderLine(models.Model): ]) if fields: raise UserError( - _('It is forbidden to modify the following fields in a locked order:\n%s') - % '\n'.join(fields.mapped('field_description')) + _('It is forbidden to modify the following fields in a locked order:\n%s', + '\n'.join(fields.mapped('field_description'))) ) result = super().write(values) diff --git a/addons/sale_project/models/product.py b/addons/sale_project/models/product.py index 7cfbaf95443..2414eba24ec 100644 --- a/addons/sale_project/models/product.py +++ b/addons/sale_project/models/product.py @@ -149,11 +149,11 @@ class ProductTemplate(models.Model): """ for product in self: if product.service_tracking == 'no' and (product.project_id or product.project_template_id): - raise ValidationError(_('The product %s should not have a project nor a project template since it will not generate project.') % (product.name,)) + raise ValidationError(_('The product %s should not have a project nor a project template since it will not generate project.', product.name)) elif product.service_tracking == 'task_global_project' and product.project_template_id: - raise ValidationError(_('The product %s should not have a project template since it will generate a task in a global project.') % (product.name,)) + raise ValidationError(_('The product %s should not have a project template since it will generate a task in a global project.', product.name)) elif product.service_tracking in ['task_in_project', 'project_only'] and product.project_id: - raise ValidationError(_('The product %s should not have a global project since it will generate a project.') % (product.name,)) + raise ValidationError(_('The product %s should not have a global project since it will generate a project.', product.name)) @api.onchange('service_tracking') def _onchange_service_tracking(self): diff --git a/addons/sale_purchase/models/sale_order_line.py b/addons/sale_purchase/models/sale_order_line.py index 10a51d6e0d9..161cea9f333 100644 --- a/addons/sale_purchase/models/sale_order_line.py +++ b/addons/sale_purchase/models/sale_order_line.py @@ -217,7 +217,7 @@ class SaleOrderLine(models.Model): # determine vendor of the order (take the first matching company and product) suppliers = self.product_id._select_seller(partner_id=self._retrieve_purchase_partner(), quantity=self.product_uom_qty, uom_id=self.product_uom) if warning and not suppliers: - raise UserError(_("There is no vendor associated to the product %s. Please define a vendor for this product.") % (self.product_id.display_name,)) + raise UserError(_("There is no vendor associated to the product %s. Please define a vendor for this product.", self.product_id.display_name)) return suppliers[0] def _purchase_service_match_purchase_order(self, partner, company=False): diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 8979822cb3c..ca774c444e7 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -103,10 +103,10 @@ class SaleOrder(models.Model): new_partner = self.env['res.partner'].browse(values.get('partner_shipping_id')) for record in self: picking = record.mapped('picking_ids').filtered(lambda x: x.state not in ('done', 'cancel')) - addresses = (record.partner_shipping_id.display_name, new_partner.display_name) message = _("""The delivery address has been changed on the Sales Order
From "%s" To "%s", - You should probably update the partner on this document.""") % addresses + You should probably update the partner on this document.""", + record.partner_shipping_id.display_name, new_partner.display_name) picking.activity_schedule('mail.mail_activity_data_warning', note=message, user_id=self.env.user.id) if 'commitment_date' in values: @@ -177,8 +177,8 @@ class SaleOrder(models.Model): res['warning'] = { 'title': _('Warning!'), 'message': _( - 'Do not forget to change the partner on the following delivery orders: %s' - ) % (','.join(pickings.mapped('name'))) + 'Do not forget to change the partner on the following delivery orders: %s', + ','.join(pickings.mapped('name'))) } return res diff --git a/addons/sale_timesheet/models/product.py b/addons/sale_timesheet/models/product.py index 2871736bb43..0e22a3d9631 100644 --- a/addons/sale_timesheet/models/product.py +++ b/addons/sale_timesheet/models/product.py @@ -110,7 +110,7 @@ class ProductTemplate(models.Model): def _unlink_except_master_data(self): time_product = self.env.ref('sale_timesheet.time_product') if time_product.product_tmpl_id in self: - raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.') % time_product.name) + raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.', time_product.name)) def write(self, vals): # timesheet product can't be archived @@ -118,7 +118,7 @@ class ProductTemplate(models.Model): if not test_mode and 'active' in vals and not vals['active']: time_product = self.env.ref('sale_timesheet.time_product') if time_product.product_tmpl_id in self: - raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.') % time_product.name) + raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.', time_product.name)) return super(ProductTemplate, self).write(vals) @@ -159,7 +159,7 @@ class ProductProduct(models.Model): def _unlink_except_master_data(self): time_product = self.env.ref('sale_timesheet.time_product') if time_product in self: - raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.') % time_product.name) + raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.', time_product.name)) def write(self, vals): # timesheet product can't be archived @@ -167,5 +167,5 @@ class ProductProduct(models.Model): if not test_mode and 'active' in vals and not vals['active']: time_product = self.env.ref('sale_timesheet.time_product') if time_product in self: - raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.') % time_product.name) + raise ValidationError(_('The %s product is required by the Timesheets app and cannot be archived nor deleted.', time_product.name)) return super(ProductProduct, self).write(vals) diff --git a/addons/sale_timesheet/wizard/project_create_sale_order.py b/addons/sale_timesheet/wizard/project_create_sale_order.py index 14622f81fa7..9f9e6fb7bf1 100644 --- a/addons/sale_timesheet/wizard/project_create_sale_order.py +++ b/addons/sale_timesheet/wizard/project_create_sale_order.py @@ -88,7 +88,7 @@ class ProjectCreateSalesOrder(models.TransientModel): map_employees = self.line_ids.mapped('employee_id') missing_meployees = timesheet_employees - map_employees if missing_meployees: - raise UserError(_('The Sales Order cannot be created because you did not enter some employees that entered timesheets on this project. Please list all the relevant employees before creating the Sales Order.\nMissing employee(s): %s') % (', '.join(missing_meployees.mapped('name')))) + raise UserError(_('The Sales Order cannot be created because you did not enter some employees that entered timesheets on this project. Please list all the relevant employees before creating the Sales Order.\nMissing employee(s): %s', ', '.join(missing_meployees.mapped('name')))) # check here if timesheet already linked to SO line timesheet_with_so_line = self.env['account.analytic.line'].search_count([('task_id', 'in', self.project_id.tasks.ids), ('so_line', '!=', False)]) diff --git a/addons/snailmail_account/wizard/account_move_send.py b/addons/snailmail_account/wizard/account_move_send.py index feaada9bc45..40b7fa57af1 100644 --- a/addons/snailmail_account/wizard/account_move_send.py +++ b/addons/snailmail_account/wizard/account_move_send.py @@ -53,8 +53,9 @@ class AccountMoveSend(models.Model): if invoice_without_valid_address: wizard.send_by_post_warning_message = _( "The partners on the following invoices have no valid address, " - "so those invoices will not be sent: %s" - ) % ", ".join(invoice_without_valid_address.mapped('name')) + "so those invoices will not be sent: %s", + ", ".join(invoice_without_valid_address.mapped('name')) + ) # ------------------------------------------------------------------------- # BUSINESS ACTIONS diff --git a/addons/stock/models/stock_location.py b/addons/stock/models/stock_location.py index 6c920860adf..f5d1d2fc2ad 100644 --- a/addons/stock/models/stock_location.py +++ b/addons/stock/models/stock_location.py @@ -220,16 +220,18 @@ class Location(models.Model): for location in self: warehouses = self.env['stock.warehouse'].search([('active', '=', True), '|', ('lot_stock_id', '=', location.id), ('view_location_id', '=', location.id)]) if warehouses: - raise UserError(_("You cannot archive the location %s as it is" - " used by your warehouse %s") % (location.display_name, warehouses[0].display_name)) + raise UserError(_( + "You cannot archive the location %s as it is used by your warehouse %s", + location.display_name, warehouses[0].display_name)) if not self.env.context.get('do_not_check_quant'): children_location = self.env['stock.location'].with_context(active_test=False).search([('id', 'child_of', self.ids)]) internal_children_locations = children_location.filtered(lambda l: l.usage == 'internal') children_quants = self.env['stock.quant'].search(['&', '|', ('quantity', '!=', 0), ('reserved_quantity', '!=', 0), ('location_id', 'in', internal_children_locations.ids)]) if children_quants and values['active'] == False: - raise UserError(_('You still have some product in locations %s') % - (', '.join(children_quants.mapped('location_id.display_name')))) + raise UserError(_( + 'You still have some product in locations %s' + ', '.join(children_quants.mapped('location_id.display_name')))) else: super(Location, children_location - self).with_context(do_not_check_quant=True).write({ 'active': values['active'], @@ -249,7 +251,7 @@ class Location(models.Model): def copy(self, default=None): default = dict(default or {}) if 'name' not in default: - default['name'] = _("%s (copy)") % self.name + default['name'] = _("%s (copy)", self.name) return super().copy(default=default) def _get_putaway_strategy(self, product, quantity=0, package=None, packaging=None, additional_qty=None): diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 8d6338f82cb..2ad4576c1ee 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -632,11 +632,15 @@ Please change the quantity done or the rounding precision of your unit of measur def _check_uom(self): moves_error = self.filtered(lambda move: move.product_id.uom_id.category_id != move.product_uom.category_id) if moves_error: - user_warning = _('You cannot perform the move because the unit of measure has a different category as the product unit of measure.') - for move in moves_error: - user_warning += _('\n\n%s --> Product UoM is %s (%s) - Move UoM is %s (%s)') % (move.product_id.display_name, move.product_id.uom_id.name, move.product_id.uom_id.category_id.name, move.product_uom.name, move.product_uom.category_id.name) - user_warning += _('\n\nBlocking: %s') % ' ,'.join(moves_error.mapped('name')) - raise UserError(user_warning) + user_warnings = [ + _('You cannot perform the move because the unit of measure has a different category as the product unit of measure.'), + *( + _('%s --> Product UoM is %s (%s) - Move UoM is %s (%s)', move.product_id.display_name, move.product_id.uom_id.name, move.product_id.uom_id.category_id.name, move.product_uom.name, move.product_uom.category_id.name) + for move in moves_error + ), + _('Blocking: %s', ' ,'.join(moves_error.mapped('name'))) + ] + raise UserError('\n\n'.join(user_warnings)) def init(self): self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('stock_move_product_location_index',)) @@ -764,7 +768,7 @@ Please change the quantity done or the rounding precision of your unit of measur if not documents or not doc_orig: return - msg = escape(_("The deadline has been automatically updated due to a delay on %s.") % doc_orig[0]._get_html_link()) + msg = escape(_("The deadline has been automatically updated due to a delay on %s.")) % doc_orig[0]._get_html_link() msg_subject = _("Deadline updated due to delay on %s", doc_orig[0].name) # write the message on each document for doc in documents: diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 7f26043447a..27eaf75027a 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -604,7 +604,8 @@ class StockMoveLine(models.Model): if float_compare(uom_qty, qty_done, precision_digits=precision_digits) != 0: raise UserError(_('The quantity done for the product "%s" doesn\'t respect the rounding precision ' 'defined on the unit of measure "%s". Please change the quantity done or the ' - 'rounding precision of your unit of measure.') % (ml.product_id.display_name, ml.product_uom_id.name)) + 'rounding precision of your unit of measure.', + ml.product_id.display_name, ml.product_uom_id.name)) qty_done_float_compared = float_compare(ml.qty_done, 0, precision_rounding=ml.product_uom_id.rounding) if qty_done_float_compared > 0: diff --git a/addons/stock/models/stock_package_type.py b/addons/stock/models/stock_package_type.py index 4acbf4ffa02..b36d02ac6dc 100644 --- a/addons/stock/models/stock_package_type.py +++ b/addons/stock/models/stock_package_type.py @@ -45,5 +45,5 @@ class PackageType(models.Model): def copy(self, default=None): default = dict(default or {}) - default.update(name=_("%s (copy)") % self.name) + default['name'] = _("%s (copy)", self.name) return super().copy(default) diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 7e64da1b6b1..751cdd1acc8 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -1104,15 +1104,15 @@ class Picking(models.Model): if pickings_without_quantities: raise UserError(self._get_without_quantities_error_message()) if pickings_without_lots: - raise UserError(_('You need to supply a Lot/Serial number for products %s.') % ', '.join(products_without_lots.mapped('display_name'))) + raise UserError(_('You need to supply a Lot/Serial number for products %s.', ', '.join(products_without_lots.mapped('display_name')))) else: message = "" if pickings_without_moves: - message += _('Transfers %s: Please add some items to move.') % ', '.join(pickings_without_moves.mapped('name')) + message += _('Transfers %s: Please add some items to move.', ', '.join(pickings_without_moves.mapped('name'))) if pickings_without_quantities: - message += _('\n\nTransfers %s: You cannot validate these transfers if no quantities are reserved nor done. To force these transfers, switch in edit more and encode the done quantities.') % ', '.join(pickings_without_quantities.mapped('name')) + message += _('\n\nTransfers %s: You cannot validate these transfers if no quantities are reserved nor done. To force these transfers, switch in edit more and encode the done quantities.', ', '.join(pickings_without_quantities.mapped('name'))) if pickings_without_lots: - message += _('\n\nTransfers %s: You need to supply a Lot/Serial number for products %s.') % (', '.join(pickings_without_lots.mapped('name')), ', '.join(products_without_lots.mapped('display_name'))) + message += _('\n\nTransfers %s: You need to supply a Lot/Serial number for products %s.', ', '.join(pickings_without_lots.mapped('name')), ', '.join(products_without_lots.mapped('display_name'))) if message: raise UserError(message.lstrip()) @@ -1662,7 +1662,7 @@ class Picking(models.Model): report = self.env['ir.actions.report']._render_qweb_pdf("stock.action_report_delivery", self.id) filename = "%s_signed_delivery_slip" % self.name if self.partner_id: - message = _('Order signed by %s') % (self.partner_id.name) + message = _('Order signed by %s', self.partner_id.name) else: message = _('Order signed') self.message_post( diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index aa62e6b1e74..516ae683b0e 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -588,13 +588,13 @@ class StockQuant(models.Model): ) for product, _location, lot, qty in groups: if float_compare(abs(qty), 1, precision_rounding=product.uom_id.rounding) > 0: - raise ValidationError(_('The serial number has already been assigned: \n Product: %s, Serial Number: %s') % (product.display_name, lot.name)) + raise ValidationError(_('The serial number has already been assigned: \n Product: %s, Serial Number: %s', product.display_name, lot.name)) @api.constrains('location_id') def check_location_id(self): for quant in self: if quant.location_id.usage == 'view': - raise ValidationError(_('You cannot take products from or deliver products to a location of type "view" (%s).') % quant.location_id.name) + raise ValidationError(_('You cannot take products from or deliver products to a location of type "view" (%s).', quant.location_id.name)) @api.model def _get_removal_strategy(self, product_id, location_id): @@ -723,7 +723,7 @@ class StockQuant(models.Model): if qty > 0: return self._run_least_packages_removal_strategy_astar(domain, qty), 'in_date ASC, id' return domain, 'in_date ASC, id' - raise UserError(_('Removal strategy %s not implemented.') % (removal_strategy,)) + raise UserError(_('Removal strategy %s not implemented.', removal_strategy)) def _get_removal_strategy_sort_key(self, removal_strategy): key = lambda q: (q.in_date, q.id) diff --git a/addons/stock/models/stock_rule.py b/addons/stock/models/stock_rule.py index bda142fe23b..9b9e7a593ef 100644 --- a/addons/stock/models/stock_rule.py +++ b/addons/stock/models/stock_rule.py @@ -233,7 +233,7 @@ class StockRule(models.Model): # `location_src_id` field. for procurement, rule in procurements: if not rule.location_src_id: - msg = _('No source location defined on stock rule: %s!') % (rule.name, ) + msg = _('No source location defined on stock rule: %s!', rule.name) raise ProcurementException([(procurement, msg)]) if rule.procure_method == 'mts_else_mto': @@ -374,7 +374,7 @@ class StockRule(models.Model): if rule.action in ['pull', 'pull_push'] and rule.delay ] if global_visibility_days: - delay_description.append((_('Global Visibility Days'), _('+ %d day(s)') % int(global_visibility_days))) + delay_description.append((_('Global Visibility Days'), _('+ %d day(s)', int(global_visibility_days)))) return delay, delay_description @@ -458,8 +458,8 @@ class ProcurementGroup(models.Model): continue rule = self._get_rule(procurement.product_id, procurement.location_id, procurement.values) if not rule: - error = _('No rule has been found to replenish "%s" in "%s".\nVerify the routes configuration on the product.') %\ - (procurement.product_id.display_name, procurement.location_id.display_name) + error = _('No rule has been found to replenish %r in %r.\nVerify the routes configuration on the product.', + procurement.product_id.display_name, procurement.location_id.display_name) procurement_errors.append((procurement, error)) else: action = 'pull' if rule.action == 'pull_push' else rule.action diff --git a/addons/stock/models/stock_storage_category.py b/addons/stock/models/stock_storage_category.py index 242abeb59cc..57c10f4eb99 100644 --- a/addons/stock/models/stock_storage_category.py +++ b/addons/stock/models/stock_storage_category.py @@ -41,7 +41,7 @@ class StorageCategory(models.Model): def copy(self, default=None): default = dict(default or {}) - default.update(name=_("%s (copy)") % self.name) + default['name'] = _("%s (copy)", self.name) return super().copy(default) diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index 5d73c443813..9208e8a4c3a 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -224,8 +224,8 @@ class Warehouse(models.Model): ('state', 'not in', ('done', 'cancel')), ]) if move_ids: - raise UserError(_('You still have ongoing operations for picking types %s in warehouse %s') % - (', '.join(move_ids.mapped('picking_type_id.name')), warehouse.name)) + raise UserError(_('You still have ongoing operations for picking types %s in warehouse %s', + ', '.join(move_ids.mapped('picking_type_id.name')), warehouse.name)) else: picking_type_ids.write({'active': vals['active']}) location_ids = self.env['stock.location'].with_context(active_test=False).search([('location_id', 'child_of', warehouse.view_location_id.id)]) @@ -235,8 +235,8 @@ class Warehouse(models.Model): ('id', 'not in', picking_type_ids.ids), ]) if picking_type_using_locations: - raise UserError(_('%s use default source or destination locations from warehouse %s that will be archived.') % - (', '.join(picking_type_using_locations.mapped('name')), warehouse.name)) + raise UserError(_('%s use default source or destination locations from warehouse %s that will be archived.', + ', '.join(picking_type_using_locations.mapped('name')), warehouse.name)) warehouse.view_location_id.write({'active': vals['active']}) rule_ids = self.env['stock.rule'].with_context(active_test=False).search([('warehouse_id', '=', warehouse.id)]) @@ -385,7 +385,7 @@ class Warehouse(models.Model): if not route: route = self.env['stock.route'].search([('name', 'like', route_name)], limit=1) if not route and raise_if_not_found: - raise UserError(_('Can\'t find any generic route %s.') % (route_name)) + raise UserError(_('Can\'t find any generic route %s.', route_name)) return route def _get_global_route_rules_values(self): diff --git a/addons/stock/wizard/stock_picking_return.py b/addons/stock/wizard/stock_picking_return.py index 882c7ec720e..d6a7b0e52aa 100644 --- a/addons/stock/wizard/stock_picking_return.py +++ b/addons/stock/wizard/stock_picking_return.py @@ -117,7 +117,7 @@ class ReturnPicking(models.TransientModel): 'picking_type_id': self.picking_id.picking_type_id.return_picking_type_id.id or self.picking_id.picking_type_id.id, 'state': 'draft', 'return_id': self.picking_id.id, - 'origin': _("Return of %s") % self.picking_id.name, + 'origin': _("Return of %s", self.picking_id.name), } # TestPickShip.test_mto_moves_return, TestPickShip.test_mto_moves_return_extra, # TestPickShip.test_pick_pack_ship_return, TestPickShip.test_pick_ship_return, TestPickShip.test_return_lot diff --git a/addons/stock/wizard/stock_replenishment_info.py b/addons/stock/wizard/stock_replenishment_info.py index 7c272e3211b..f711c606fdf 100644 --- a/addons/stock/wizard/stock_replenishment_info.py +++ b/addons/stock/wizard/stock_replenishment_info.py @@ -126,11 +126,12 @@ class StockReplenishmentOption(models.TransientModel): self.warning_message = '' for record in self: if record.free_qty < record.qty_to_order: - record.warning_message = _('{0} can only provide {1} {2}, while the quantity to order is {3} {2}.').format( - record.warehouse_id.name, - record.free_qty, - record.uom, - record.qty_to_order + record.warning_message = _( + '%(warehouse)s can only provide %(free_qty)s %(uom)s, while the quantity to order is %(qty_to_order)s %(uom)s.', + warehouse=record.warehouse_id.name, + free_qty=record.free_qty, + uom=record.uom, + qty_to_order=record.qty_to_order ) def select_route(self): diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index 43dd2a4556d..b146e5eda2c 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -37,10 +37,13 @@ class ProductTemplate(models.Model): continue # Empty out the stock with the current cost method. - description = _("Due to a change of product category (from %s to %s), the costing method\ - has changed for product template %s: from %s to %s.") %\ - (product_template.categ_id.display_name, new_product_category.display_name, - product_template.display_name, product_template.cost_method, new_product_category.property_cost_method) + description = _( + "Due to a change of product category (from %s to %s), the costing method has changed for product template %s: from %s to %s.", + product_template.categ_id.display_name, + new_product_category.display_name, + product_template.display_name, + product_template.cost_method, + new_product_category.property_cost_method) out_svl_vals_list, products_orig_quantity_svl, products = Product\ ._svl_empty_stock(description, product_template=product_template) out_stock_valuation_layers = SVL.create(out_svl_vals_list) @@ -249,7 +252,7 @@ class ProductProduct(models.Model): svl_vals = { 'company_id': company_id.id, 'product_id': product.id, - 'description': _('Product value manually modified (from %s to %s)') % (product.standard_price, rounded_new_price), + 'description': _('Product value manually modified (from %s to %s)', product.standard_price, rounded_new_price), 'value': value, 'quantity': 0, } @@ -816,11 +819,13 @@ class ProductCategory(models.Model): # Empty out the stock with the current cost method. if new_cost_method: - description = _("Costing method change for product category %s: from %s to %s.") \ - % (product_category.display_name, product_category.property_cost_method, new_cost_method) + description = _( + "Costing method change for product category %s: from %s to %s.", + product_category.display_name, product_category.property_cost_method, new_cost_method) else: - description = _("Valuation method change for product category %s: from %s to %s.") \ - % (product_category.display_name, product_category.property_valuation, new_valuation) + description = _( + "Valuation method change for product category %s: from %s to %s.", + product_category.display_name, product_category.property_valuation, new_valuation) out_svl_vals_list, products_orig_quantity_svl, products = Product\ ._svl_empty_stock(description, product_category=product_category) out_stock_valuation_layers = SVL.sudo().create(out_svl_vals_list) diff --git a/addons/stock_account/models/stock_move.py b/addons/stock_account/models/stock_move.py index decd7f00e5c..a5f6b98a7bf 100644 --- a/addons/stock_account/models/stock_move.py +++ b/addons/stock_account/models/stock_move.py @@ -352,9 +352,9 @@ class StockMove(models.Model): if not accounts_data.get('stock_journal', False): raise UserError(_('You don\'t have any stock journal defined on your product category, check if you have installed a chart of accounts.')) if not acc_src: - raise UserError(_('Cannot find a stock input account for the product %s. You must define one on the product category, or on the location, before processing this operation.') % (self.product_id.display_name)) + raise UserError(_('Cannot find a stock input account for the product %s. You must define one on the product category, or on the location, before processing this operation.', self.product_id.display_name)) if not acc_dest: - raise UserError(_('Cannot find a stock output account for the product %s. You must define one on the product category, or on the location, before processing this operation.') % (self.product_id.display_name)) + raise UserError(_('Cannot find a stock output account for the product %s. You must define one on the product category, or on the location, before processing this operation.', self.product_id.display_name)) if not acc_valuation: raise UserError(_('You don\'t have any stock valuation account defined on your product category. You must define one before processing this operation.')) journal_id = accounts_data['stock_journal'].id diff --git a/addons/stock_delivery/models/delivery_carrier.py b/addons/stock_delivery/models/delivery_carrier.py index 28192495a24..9fc9933d191 100644 --- a/addons/stock_delivery/models/delivery_carrier.py +++ b/addons/stock_delivery/models/delivery_carrier.py @@ -100,7 +100,7 @@ class DeliveryCarrier(models.Model): total_weight = order._get_estimated_weight() + default_package_type.base_weight if total_weight == 0.0: weight_uom_name = self.env['product.template']._get_weight_uom_name_from_ir_config_parameter() - raise UserError(_("The package cannot be created because the total weight of the products in the picking is 0.0 %s") % (weight_uom_name)) + raise UserError(_("The package cannot be created because the total weight of the products in the picking is 0.0 %s", weight_uom_name)) # If max weight == 0 => division by 0. If this happens, we want to have # more in the max weight than in the total weight, so that it only # creates ONE package with everything. @@ -180,10 +180,11 @@ class DeliveryCarrier(models.Model): picking=picking, )) elif not packages: - raise UserError( - _("The package cannot be created because the total weight of the products in the " - "picking is 0.0 %s") % (picking.weight_uom_name) - ) + raise UserError(_( + "The package cannot be created because the total weight of the " + "products in the picking is 0.0 %s", + picking.weight_uom_name + )) return packages def _get_commodities_from_order(self, order): diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py index f3e4e53c15c..d067453842f 100644 --- a/addons/stock_landed_costs/models/stock_landed_cost.py +++ b/addons/stock_landed_costs/models/stock_landed_cost.py @@ -396,7 +396,7 @@ class AdjustmentLines(models.Model): credit_account_id = self.cost_line_id.account_id.id or cost_product.categ_id.property_stock_account_input_categ_id.id if not credit_account_id: - raise UserError(_('Please configure Stock Expense Account for product: %s.') % (cost_product.name)) + raise UserError(_('Please configure Stock Expense Account for product: %s.', cost_product.name)) return self._create_account_move_line(move, credit_account_id, debit_account_id, qty_out, already_out_account_id) diff --git a/addons/survey/models/survey_survey.py b/addons/survey/models/survey_survey.py index 96b96a558bf..5031f2df7b8 100644 --- a/addons/survey/models/survey_survey.py +++ b/addons/survey/models/survey_survey.py @@ -438,7 +438,7 @@ class Survey(models.Model): return clone def copy_data(self, default=None): - new_defaults = {'title': _("%s (copy)") % (self.title)} + new_defaults = {'title': _("%s (copy)", self.title)} default = dict(new_defaults, **(default or {})) return super(Survey, self).copy_data(default) diff --git a/addons/survey/wizard/survey_invite.py b/addons/survey/wizard/survey_invite.py index c9e6443730d..f98c894dae7 100644 --- a/addons/survey/wizard/survey_invite.py +++ b/addons/survey/wizard/survey_invite.py @@ -124,7 +124,7 @@ class SurveyInvite(models.TransientModel): else: valid.extend(email_check) if error: - raise UserError(_("Some emails you just entered are incorrect: %s") % (', '.join(error))) + raise UserError(_("Some emails you just entered are incorrect: %s", ', '.join(error))) self.emails = '\n'.join(valid) @api.onchange('partner_ids') diff --git a/addons/uom/models/uom_uom.py b/addons/uom/models/uom_uom.py index bf9796579aa..865c94bb24c 100644 --- a/addons/uom/models/uom_uom.py +++ b/addons/uom/models/uom_uom.py @@ -88,9 +88,9 @@ class UoM(models.Model): reference_count = sum( uom.uom_type == 'reference' for uom in category.uom_ids) if reference_count > 1: - raise ValidationError(_("UoM category %s should only have one reference unit of measure.") % category.name) + raise ValidationError(_("UoM category %s should only have one reference unit of measure.", category.name)) elif reference_count == 0: - raise ValidationError(_("UoM category %s should have a reference unit of measure.") % category.name) + raise ValidationError(_("UoM category %s should have a reference unit of measure.", category.name)) @api.depends('factor') def _compute_factor_inv(self): @@ -211,7 +211,9 @@ class UoM(models.Model): if self != to_unit and self.category_id.id != to_unit.category_id.id: if raise_if_failure: - raise UserError(_('The unit of measure %s defined on the order line doesn\'t belong to the same category as the unit of measure %s defined on the product. Please correct the unit of measure defined on the order line or on the product, they should belong to the same category.') % (self.name, to_unit.name)) + raise UserError(_( + 'The unit of measure %s defined on the order line doesn\'t belong to the same category as the unit of measure %s defined on the product. Please correct the unit of measure defined on the order line or on the product, they should belong to the same category.', + self.name, to_unit.name)) else: return qty diff --git a/addons/website/models/website_rewrite.py b/addons/website/models/website_rewrite.py index fc22fd9c454..6b45971648d 100644 --- a/addons/website/models/website_rewrite.py +++ b/addons/website/models/website_rewrite.py @@ -93,17 +93,17 @@ class WebsiteRewrite(models.Model): raise ValidationError(_('"URL to" must start with a leading slash.')) for param in re.findall('/<.*?>', rewrite.url_from): if param not in rewrite.url_to: - raise ValidationError(_('"URL to" must contain parameter %s used in "URL from".') % param) + raise ValidationError(_('"URL to" must contain parameter %s used in "URL from".', param)) for param in re.findall('/<.*?>', rewrite.url_to): if param not in rewrite.url_from: - raise ValidationError(_('"URL to" cannot contain parameter %s which is not used in "URL from".') % param) + raise ValidationError(_('"URL to" cannot contain parameter %s which is not used in "URL from".', param)) try: converters = self.env['ir.http']._get_converters() routing_map = werkzeug.routing.Map(strict_slashes=False, converters=converters) rule = werkzeug.routing.Rule(rewrite.url_to) routing_map.add(rule) except ValueError as e: - raise ValidationError(_('"URL to" is invalid: %s') % e) + raise ValidationError(_('"URL to" is invalid: %s', e)) from e @api.depends('redirect_type') def _compute_display_name(self): diff --git a/addons/website/models/website_snippet_filter.py b/addons/website/models/website_snippet_filter.py index 614369b0ff4..e37a2c96132 100644 --- a/addons/website/models/website_snippet_filter.py +++ b/addons/website/models/website_snippet_filter.py @@ -52,7 +52,7 @@ class WebsiteSnippetFilter(models.Model): for record in self: for field_name in record.field_names.split(","): if not field_name.strip(): - raise ValidationError(_("Empty field name in %r") % (record.field_names)) + raise ValidationError(_("Empty field name in %r", record.field_names)) def _render(self, template_key, limit, search_domain=None, with_sample=False): """Renders the website dynamic snippet items""" diff --git a/addons/website_crm_partner_assign/models/crm_lead.py b/addons/website_crm_partner_assign/models/crm_lead.py index a62352ea1f0..9a9a1ec0719 100644 --- a/addons/website_crm_partner_assign/models/crm_lead.py +++ b/addons/website_crm_partner_assign/models/crm_lead.py @@ -258,7 +258,7 @@ class CrmLead(models.Model): fields = ['partner_name', 'phone', 'mobile', 'email_from', 'street', 'street2', 'city', 'zip', 'state_id', 'country_id'] if any([key not in fields for key in values]): - raise UserError(_("Not allowed to update the following field(s): %s.") % ", ".join([key for key in values if not key in fields])) + raise UserError(_("Not allowed to update the following field(s): %s.", ", ".join([key for key in values if not key in fields]))) return self.sudo().write(values) @api.model diff --git a/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py index 864191e7f11..39ca7a6c6ee 100644 --- a/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/website_crm_partner_assign/wizard/crm_forward_to_partner.py @@ -67,7 +67,7 @@ class CrmLeadForwardToPartner(models.TransientModel): if lead.partner_assigned_id and not lead.partner_assigned_id.email: no_email.add(lead.partner_assigned_id.name) if no_email: - raise UserError(_('Set an email address for the partner(s): %s') % ", ".join(no_email)) + raise UserError(_('Set an email address for the partner(s): %s', ", ".join(no_email))) if self.forward_type == 'single' and not self.partner_id.email: raise UserError(_('Set an email address for the partner %s', self.partner_id.name)) diff --git a/addons/website_event_exhibitor/controllers/website_event_main.py b/addons/website_event_exhibitor/controllers/website_event_main.py index e182089dde2..f8e055f539f 100644 --- a/addons/website_event_exhibitor/controllers/website_event_main.py +++ b/addons/website_event_exhibitor/controllers/website_event_main.py @@ -18,8 +18,8 @@ class WebsiteEventController(main.WebsiteEventController): date_begin = format_datetime(event.date_begin, format="medium", tzinfo=event.date_tz) values["toast_message"] = ( - _('The event %s starts on %s (%s). \nJoin us there to meet %s!') - % (event.name, date_begin, event.date_tz, sponsor.partner_name) + _('The event %s starts on %s (%s). \nJoin us there to meet %s!', + event.name, date_begin, event.date_tz, sponsor.partner_name) ) return values diff --git a/addons/website_event_meet/controllers/website_event_main.py b/addons/website_event_meet/controllers/website_event_main.py index e48844048c9..4ab30ba9484 100644 --- a/addons/website_event_meet/controllers/website_event_main.py +++ b/addons/website_event_meet/controllers/website_event_main.py @@ -18,8 +18,8 @@ class WebsiteEventController(main.WebsiteEventController): date_begin = format_datetime(event.date_begin, format="medium", tzinfo=event.date_tz) values["toast_message"] = ( - _('The event %s starts on %s (%s). \nJoin us there to chat about "%s"!') - % (event.name, date_begin, event.date_tz, meeting_room.name) + _('The event %s starts on %s (%s). \nJoin us there to chat about "%s"!', + event.name, date_begin, event.date_tz, meeting_room.name) ) return values diff --git a/addons/website_forum/models/forum_post.py b/addons/website_forum/models/forum_post.py index 871471d24f5..280c1bd422e 100644 --- a/addons/website_forum/models/forum_post.py +++ b/addons/website_forum/models/forum_post.py @@ -682,7 +682,7 @@ class Post(models.Model): 'forum_id': question.forum_id.id, 'content': comment_sudo.body, 'parent_id': question.id, - 'name': _('Re: %s') % (question.name or ''), + 'name': _('Re: %s', question.name or ''), } # done with the author user to have create_uid correctly set new_post = self.with_user(post_create_uid).sudo().create(post_values).sudo(False) diff --git a/addons/website_livechat/controllers/main.py b/addons/website_livechat/controllers/main.py index 05062e35e64..9b47f884602 100644 --- a/addons/website_livechat/controllers/main.py +++ b/addons/website_livechat/controllers/main.py @@ -62,14 +62,14 @@ class WebsiteLivechat(LivechatController): def _get_guest_name(self): visitor_sudo = request.env["website.visitor"]._get_visitor_from_request() - return _('Visitor #%d') % visitor_sudo.id if visitor_sudo else super()._get_guest_name() + return _('Visitor #%d', visitor_sudo.id) if visitor_sudo else super()._get_guest_name() @http.route() def get_session(self, channel_id, anonymous_name, previous_operator_id=None, chatbot_script_id=None, persisted=True, **kwargs): """ Override to use visitor name instead of 'Visitor' whenever a visitor start a livechat session. """ visitor_sudo = request.env['website.visitor']._get_visitor_from_request() if visitor_sudo: - anonymous_name = _('Visitor #%s') % visitor_sudo.id + anonymous_name = _('Visitor #%s', visitor_sudo.id) return super(WebsiteLivechat, self).get_session(channel_id, anonymous_name, previous_operator_id=previous_operator_id, chatbot_script_id=chatbot_script_id, persisted=persisted, **kwargs) def _livechat_templates_get(self): diff --git a/addons/website_livechat/models/discuss_channel.py b/addons/website_livechat/models/discuss_channel.py index 6f778dc7505..93a7b6d8e13 100644 --- a/addons/website_livechat/models/discuss_channel.py +++ b/addons/website_livechat/models/discuss_channel.py @@ -60,12 +60,13 @@ class DiscussChannel(models.Model): return ' → '.join(visit.page_id.name + ' (' + visit.visit_datetime.strftime('%H:%M') + ')' for visit in reversed(recent_history)) def _get_visitor_leave_message(self, operator=False, cancel=False): - name = _('The visitor') if not self.livechat_visitor_id else self.livechat_visitor_id.display_name if cancel: + name = self.livechat_visitor_id.display_name or _('The visitor') message = _("""%s started a conversation with %s. - The chat request has been canceled.""") % (name, operator or _('an operator')) + The chat request has been canceled.""", + name, operator or _('an operator')) else: - message = _('Visitor %s left the conversation.') % (f"#{self.livechat_visitor_id.id}" if self.livechat_visitor_id else '') + message = _('Visitor %s left the conversation.', ("#%d" % self.livechat_visitor_id.id) if self.livechat_visitor_id else '') return message diff --git a/addons/website_livechat/models/website_visitor.py b/addons/website_livechat/models/website_visitor.py index fc697efa08c..62bd03c82a9 100644 --- a/addons/website_livechat/models/website_visitor.py +++ b/addons/website_livechat/models/website_visitor.py @@ -85,7 +85,7 @@ class WebsiteVisitor(models.Model): self.env['mail.guest']._find_or_create_for_channel( channel, country_id=country.id, - name=_("Visitor #%d") % channel.livechat_visitor_id.id, + name=_("Visitor #%d", channel.livechat_visitor_id.id), post_joined_message=False, timezone=visitor.timezone ) diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 0292afdb2d0..7b543af7ec9 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -42,7 +42,7 @@ class SaleOrder(models.Model): if 'company_id' in vals: company = self.env['res.company'].browse(vals['company_id']) if website.company_id.id != company.id: - raise ValueError(_("The company of the website you are trying to sale from (%s) is different than the one you want to use (%s)") % (website.company_id.name, company.name)) + raise ValueError(_("The company of the website you are trying to sale from (%s) is different than the one you want to use (%s)", website.company_id.name, company.name)) else: vals['company_id'] = website.company_id.id return super().create(vals_list) diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 64d50c1edb3..2fd3c465f61 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -1070,7 +1070,7 @@ class Channel(models.Model): def action_view_ratings(self): action = self.env["ir.actions.actions"]._for_xml_id("website_slides.rating_rating_action_slide_channel") - action['name'] = _('Rating of %s') % (self.name) + action['name'] = _('Rating of %s', self.name) action['domain'] = expression.AND([ast.literal_eval(action.get('domain', '[]')), [('res_id', 'in', self.ids)]]) return action @@ -1126,7 +1126,7 @@ class Channel(models.Model): if channel.id not in requested_cids: activities += channel.activity_schedule( 'website_slides.mail_activity_data_access_request', - note=_('%s is requesting access to this course.') % partner.name, + note=_('%s is requesting access to this course.', partner.name), user_id=channel.user_id.id, request_partner_id=partner.id ) diff --git a/addons/website_twitter/controllers/main.py b/addons/website_twitter/controllers/main.py index 36861775138..92c11a1d4ec 100644 --- a/addons/website_twitter/controllers/main.py +++ b/addons/website_twitter/controllers/main.py @@ -35,8 +35,8 @@ class Twitter(http.Controller): if len(tweets) < 12: if debug: return {"error": _("Twitter user @%(username)s has less than 12 favorite tweets. " - "Please add more or choose a different screen name.") % \ - {'username': screen_name}} + "Please add more or choose a different screen name.", + username=screen_name)} else: return [] return tweets.mapped(lambda t: json.loads(t.tweet)) diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py index 2226261c7ce..82a626acb6b 100644 --- a/odoo/addons/base/models/assetsbundle.py +++ b/odoo/addons/base/models/assetsbundle.py @@ -497,7 +497,7 @@ class AssetsBundle(object): else: raise ValueError(_("Module %r not loaded or inexistent (try to inherit %r), or templates of addon being loaded %r are misordered (template %r)", parent_addon, parent_name, addon, template_name)) if parent_name not in template_dict[parent_addon]: - raise ValueError(_("Cannot create %r because the template to inherit %r is not found.") % (f'{addon}.{template_name}', f'{parent_addon}.{parent_name}')) + raise ValueError(_("Cannot create %r because the template to inherit %r is not found.", '%s.%s' % (addon, template_name), '%s.%s' % (parent_addon, parent_name))) # After several performance tests, we found out that deepcopy is the most efficient # solution in this case (compared with copy, xpath with '.' and stringifying). diff --git a/odoo/addons/base/models/ir_actions_report.py b/odoo/addons/base/models/ir_actions_report.py index 0f89cb31082..258b0ada3fe 100644 --- a/odoo/addons/base/models/ir_actions_report.py +++ b/odoo/addons/base/models/ir_actions_report.py @@ -490,11 +490,18 @@ class IrActionsReport(models.Model): if process.returncode not in [0, 1]: if process.returncode == -11: message = _( - 'Wkhtmltopdf failed (error code: %s). Memory limit too low or maximum file number of subprocess reached. Message : %s') + 'Wkhtmltopdf failed (error code: %s). Memory limit too low or maximum file number of subprocess reached. Message : %s', + process.returncode, + err[-1000:], + ) else: - message = _('Wkhtmltopdf failed (error code: %s). Message: %s') - _logger.warning(message, process.returncode, err[-1000:]) - raise UserError(message % (str(process.returncode), err[-1000:])) + message = _( + 'Wkhtmltopdf failed (error code: %s). Message: %s', + process.returncode, + err[-1000:], + ) + _logger.warning(message) + raise UserError(message) else: if err: _logger.warning('wkhtmltopdf: %s' % err) diff --git a/odoo/addons/base/models/ir_default.py b/odoo/addons/base/models/ir_default.py index b28c5b62a60..561e0dde56c 100644 --- a/odoo/addons/base/models/ir_default.py +++ b/odoo/addons/base/models/ir_default.py @@ -75,9 +75,9 @@ class IrDefault(models.Model): field.convert_to_cache(value, model) json_value = json.dumps(value, ensure_ascii=False) except KeyError: - raise ValidationError(_("Invalid field %s.%s") % (model_name, field_name)) + raise ValidationError(_("Invalid field %s.%s", model_name, field_name)) except Exception: - raise ValidationError(_("Invalid value for %s.%s: %s") % (model_name, field_name, value)) + raise ValidationError(_("Invalid value for %s.%s: %s", model_name, field_name, value)) # update existing default for the same scope, or create one field = self.env['ir.model.fields']._get(model_name, field_name) diff --git a/odoo/addons/base/models/ir_fields.py b/odoo/addons/base/models/ir_fields.py index 89f174058f8..b258d553bf2 100644 --- a/odoo/addons/base/models/ir_fields.py +++ b/odoo/addons/base/models/ir_fields.py @@ -462,9 +462,11 @@ class IrFieldsConverter(models.AbstractModel): ids = RelatedModel.name_search(name=value, operator='=') if ids: if len(ids) > 1: - warnings.append(ImportWarning( - _(u"Found multiple matches for value '%s' in field '%%(field)s' (%d matches)") - %(str(value).replace('%', '%%'), len(ids)))) + warnings.append(ImportWarning(_( + "Found multiple matches for value %r in field %%(field)r (%d matches)", + str(value).replace('%', '%%'), + len(ids), + ))) id, _name = ids[0] else: name_create_enabled_fields = self.env.context.get('name_create_enabled_fields') or {} @@ -473,12 +475,11 @@ class IrFieldsConverter(models.AbstractModel): with self.env.cr.savepoint(): id, _name = RelatedModel.name_create(name=value) except (Exception, psycopg2.IntegrityError): - error_msg = _(u"Cannot create new '%s' records from their name alone. Please create those records manually and try importing again.", RelatedModel._description) + error_msg = _("Cannot create new '%s' records from their name alone. Please create those records manually and try importing again.", RelatedModel._description) else: raise self._format_import_error( Exception, - _(u"Unknown sub-field '%s'"), - subfield + _("Unknown sub-field %r", subfield) ) set_empty = False diff --git a/odoo/addons/base/models/ir_filters.py b/odoo/addons/base/models/ir_filters.py index 023a3a4b709..7528ed4f7d0 100644 --- a/odoo/addons/base/models/ir_filters.py +++ b/odoo/addons/base/models/ir_filters.py @@ -108,7 +108,7 @@ class IrFilters(models.Model): if matching_filters and (matching_filters[0]['id'] == defaults.id): return - raise UserError(_("There is already a shared filter set as default for %(model)s, delete or change it before setting a new default") % {'model': vals.get('model_id')}) + raise UserError(_("There is already a shared filter set as default for %(model)s, delete or change it before setting a new default", model=vals.get('model_id'))) @api.model @api.returns('self', lambda value: value.id) diff --git a/odoo/addons/base/models/ir_mail_server.py b/odoo/addons/base/models/ir_mail_server.py index fd088a21bb7..72389b038f4 100644 --- a/odoo/addons/base/models/ir_mail_server.py +++ b/odoo/addons/base/models/ir_mail_server.py @@ -236,20 +236,17 @@ class IrMailServer(models.Model): # Testing the MAIL FROM step should detect sender filter problems (code, repl) = smtp.mail(email_from) if code != 250: - raise UserError(_('The server refused the sender address (%(email_from)s) ' - 'with error %(repl)s') % locals()) + raise UserError(_('The server refused the sender address (%(email_from)s) with error %(repl)s', email_from=email_from, repl=repl)) # Testing the RCPT TO step should detect most relaying problems (code, repl) = smtp.rcpt(email_to) if code not in (250, 251): - raise UserError(_('The server refused the test recipient (%(email_to)s) ' - 'with error %(repl)s') % locals()) + raise UserError(_('The server refused the test recipient (%(email_to)s) with error %(repl)s', email_from=email_from, repl=repl)) # Beginning the DATA step should detect some deferred rejections # Can't use self.data() as it would actually send the mail! smtp.putcmd("data") (code, repl) = smtp.getreply() if code != 354: - raise UserError(_('The server refused the test connection ' - 'with error %(repl)s') % locals()) + raise UserError(_('The server refused the test connection with error %(repl)s', repl=repl)) except UserError as e: # let UserErrors (messages) bubble up raise e diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index 0775c32fe51..55595fe8405 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -310,7 +310,7 @@ class IrModel(models.Model): # Prevent manual deletion of module tables for model in self: if model.state != 'manual': - raise UserError(_("Model '%s' contains module data and cannot be removed.", model.name)) + raise UserError(_("Model %r contains module data and cannot be removed.", model.name)) def unlink(self): # prevent screwing up fields that depend on these models' fields @@ -626,10 +626,10 @@ class IrModelFields(models.Model): for index, name in enumerate(names): field = self._get(model_name, name) if not field: - raise UserError(_("Unknown field name '%s' in related field '%s'") % (name, self.related)) + raise UserError(_("Unknown field name %r in related field %r", name, self.related)) model_name = field.relation if index < last and not field.relation: - raise UserError(_("Non-relational field name '%s' in related field '%s'") % (name, self.related)) + raise UserError(_("Non-relational field name %r in related field %r", name, self.related)) return field @api.constrains('related') @@ -638,9 +638,9 @@ class IrModelFields(models.Model): if rec.state == 'manual' and rec.related: field = rec._related_field() if field.ttype != rec.ttype: - raise ValidationError(_("Related field '%s' does not have type '%s'") % (rec.related, rec.ttype)) + raise ValidationError(_("Related field %r does not have type %r", rec.related, rec.ttype)) if field.relation != rec.relation: - raise ValidationError(_("Related field '%s' does not have comodel '%s'") % (rec.related, rec.relation)) + raise ValidationError(_("Related field %r does not have comodel %r", rec.related, rec.relation)) @api.onchange('related') def _onchange_related(self): @@ -674,7 +674,7 @@ class IrModelFields(models.Model): continue for seq in record.depends.split(","): if not seq.strip(): - raise UserError(_("Empty dependency in %r") % (record.depends)) + raise UserError(_("Empty dependency in %r", record.depends)) model = self.env[record.model] names = seq.strip().split(".") last = len(names) - 1 @@ -683,9 +683,9 @@ class IrModelFields(models.Model): raise UserError(_("Compute method cannot depend on field 'id'")) field = model._fields.get(name) if field is None: - raise UserError(_("Unknown field %r in dependency %r") % (name, seq.strip())) + raise UserError(_("Unknown field %r in dependency %r", name, seq.strip())) if index < last and not field.relational: - raise UserError(_("Non-relational field %r in dependency %r") % (name, seq.strip())) + raise UserError(_("Non-relational field %r in dependency %r", name, seq.strip())) model = model[name] @api.onchange('compute') @@ -710,7 +710,7 @@ class IrModelFields(models.Model): else: currency_field = self._get(rec.model, rec.currency_field) if not currency_field: - raise ValidationError(_("Unknown field name '%s' in currency_field") % (rec.currency_field)) + raise ValidationError(_("Unknown field name %r in currency_field", rec.currency_field)) if currency_field.ttype != 'many2one': raise ValidationError(_("Currency field does not have type many2one")) @@ -882,16 +882,17 @@ class IrModelFields(models.Model): view._check_xml() except Exception: if not uninstalling: - raise UserError("\n".join([ - _("Cannot rename/delete fields that are still present in views:"), - _("Fields: %s") % ", ".join(str(f) for f in fields), - _("View: %s", view.name), - ])) + raise UserError(_( + "Cannot rename/delete fields that are still present in views:\nFields: %s\nView: %s", + ", ".join(str(f) for f in fields), + view.name, + )) else: # uninstall mode - _logger.warning("The following fields were force-deleted to prevent a registry crash " - + ", ".join(str(f) for f in fields) - + " the following view might be broken %s" % view.name) + _logger.warning( + "The following fields were force-deleted to prevent a registry crash %s the following view might be broken %s", + ", ".join(str(f) for f in fields), + view.name) finally: if not uninstalling: # the registry has been modified, restore it @@ -964,7 +965,7 @@ class IrModelFields(models.Model): ('model', '=', vals['relation']), ('name', '=', vals['relation_field']), ]): - raise UserError(_("Many2one %s on model %s does not exist!") % (vals['relation_field'], vals['relation'])) + raise UserError(_("Many2one %s on model %s does not exist!", vals['relation_field'], vals['relation'])) if any(model in self.pool for model in models): # setup models; this re-initializes model in registry @@ -1575,8 +1576,9 @@ class IrModelSelection(models.Model): else: # this shouldn't happen... simply a sanity check raise ValueError(_( - "The ondelete policy %r is not valid for field %r" - ) % (ondelete, selection)) + "The ondelete policy %r is not valid for field %r", + ondelete, selection + )) def _get_records(self): """ Return the records having 'self' as a value. """ @@ -2067,7 +2069,7 @@ class IrModelData(models.Model): if self.env[model].search([('id', '=', res_id)]): return model, res_id if raise_on_access_error: - raise AccessError(_('Not enough access rights on the external ID:') + ' %s.%s' % (module, xml_id)) + raise AccessError(_('Not enough access rights on the external ID %r', '%s.%s', (module, xml_id))) return model, False @api.returns('self', lambda value: value.id) diff --git a/odoo/addons/base/models/ir_module.py b/odoo/addons/base/models/ir_module.py index fa98426536d..a5fd4f295c7 100644 --- a/odoo/addons/base/models/ir_module.py +++ b/odoo/addons/base/models/ir_module.py @@ -338,12 +338,12 @@ class Module(models.Model): modules.check_manifest_dependencies(terp) except Exception as e: if newstate == 'to install': - msg = _('Unable to install module "%s" because an external dependency is not met: %s') + msg = _('Unable to install module "%s" because an external dependency is not met: %s', module_name, e.args[0]) elif newstate == 'to upgrade': - msg = _('Unable to upgrade module "%s" because an external dependency is not met: %s') + msg = _('Unable to upgrade module "%s" because an external dependency is not met: %s', module_name, e.args[0]) else: - msg = _('Unable to process module "%s" because an external dependency is not met: %s') - raise UserError(msg % (module_name, e.args[0])) + msg = _('Unable to process module "%s" because an external dependency is not met: %s', module_name, e.args[0]) + raise UserError(msg) def _state_update(self, newstate, states_to_update, level=100): if level < 1: @@ -361,7 +361,7 @@ class Module(models.Model): update_mods, ready_mods = self.browse(), self.browse() for dep in module.dependencies_id: if dep.state == 'unknown': - raise UserError(_("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system.") % (module.name, dep.name,)) + raise UserError(_("You try to install module %r that depends on module %r.\nBut the latter module is not available in your system.", module.name, dep.name)) if dep.depend_id.state == newstate: ready_mods += dep.depend_id else: @@ -408,8 +408,7 @@ class Module(models.Model): for module in install_mods: for exclusion in module.exclusion_ids: if exclusion.name in install_names: - msg = _('Modules "%s" and "%s" are incompatible.') - raise UserError(msg % (module.shortdesc, exclusion.exclusion_id.shortdesc)) + raise UserError(_('Modules %r and %r are incompatible.', module.shortdesc, exclusion.exclusion_id.shortdesc)) # check category exclusions def closure(module): @@ -427,12 +426,13 @@ class Module(models.Model): # the installation is valid if all installed modules in categories # belong to the transitive dependencies of one of them if modules and not any(modules <= closure(module) for module in modules): - msg = _('You are trying to install incompatible modules in category "%s":') labels = dict(self.fields_get(['state'])['state']['selection']) - raise UserError("\n".join([msg % category.name] + [ - "- %s (%s)" % (module.shortdesc, labels[module.state]) - for module in modules - ])) + raise UserError( + _('You are trying to install incompatible modules in category %r:%s', category.name, ''.join( + f"\n- {module.shortdesc} ({labels[module.state]})" + for module in modules + )) + ) return dict(ACTION_DICT, name=_('Install')) @@ -669,7 +669,7 @@ class Module(models.Model): module = todo[i] i += 1 if module.state not in ('installed', 'to upgrade'): - raise UserError(_("Can not upgrade module '%s'. It is not installed.") % (module.name,)) + raise UserError(_("Can not upgrade module %r. It is not installed.", module.name)) if self.get_module_info(module.name).get("installable", True): self.check_external_dependencies(module.name, 'to upgrade') for dep in Dependency.search([('name', '=', module.name)]): @@ -688,7 +688,7 @@ class Module(models.Model): continue for dep in module.dependencies_id: if dep.state == 'unknown': - raise UserError(_('You try to upgrade the module %s that depends on the module: %s.\nBut this module is not available in your system.') % (module.name, dep.name,)) + raise UserError(_('You try to upgrade the module %s that depends on the module: %s.\nBut this module is not available in your system.', module.name, dep.name)) if dep.state == 'uninstalled': to_install += self.search([('name', '=', dep.name)]).ids diff --git a/odoo/addons/base/models/ir_qweb_fields.py b/odoo/addons/base/models/ir_qweb_fields.py index c1b68aaeef6..47b2a412cb3 100644 --- a/odoo/addons/base/models/ir_qweb_fields.py +++ b/odoo/addons/base/models/ir_qweb_fields.py @@ -719,7 +719,7 @@ class BarcodeConverter(models.AbstractModel): if k.startswith('img_') and k[4:] in safe_attrs: img_element.set(k[4:], v) if not img_element.get('alt'): - img_element.set('alt', _('Barcode %s') % value) + img_element.set('alt', _('Barcode %s', value)) img_element.set('src', 'data:image/png;base64,%s' % base64.b64encode(barcode).decode()) return Markup(html.tostring(img_element, encoding='unicode')) diff --git a/odoo/addons/base/models/ir_sequence.py b/odoo/addons/base/models/ir_sequence.py index 9e39103af83..da090538a65 100644 --- a/odoo/addons/base/models/ir_sequence.py +++ b/odoo/addons/base/models/ir_sequence.py @@ -234,7 +234,7 @@ class IrSequence(models.Model): interpolated_prefix = _interpolate(self.prefix, d) interpolated_suffix = _interpolate(self.suffix, d) except ValueError: - raise UserError(_('Invalid prefix or suffix for sequence \'%s\'') % self.name) + raise UserError(_('Invalid prefix or suffix for sequence %r', self.name)) return interpolated_prefix, interpolated_suffix def get_next_char(self, number_next): diff --git a/odoo/addons/base/wizard/base_import_language.py b/odoo/addons/base/wizard/base_import_language.py index 643d89e0034..8026382f72a 100644 --- a/odoo/addons/base/wizard/base_import_language.py +++ b/odoo/addons/base/wizard/base_import_language.py @@ -44,8 +44,8 @@ class BaseLanguageImport(models.TransientModel): _logger.warning('Could not import the file due to a format mismatch or it being malformed.') raise UserError( _('File %r not imported due to format mismatch or a malformed file.' - ' (Valid formats are .csv, .po)\n\nTechnical Details:\n%s') % \ - (base_lang_import.filename, tools.ustr(e)) + ' (Valid formats are .csv, .po)\n\nTechnical Details:\n%s', + base_lang_import.filename, tools.ustr(e)) ) translation_importer.save(overwrite=overwrite) return True diff --git a/odoo/addons/base/wizard/base_module_upgrade.py b/odoo/addons/base/wizard/base_module_upgrade.py index a4da1855379..ddbd4c8ade7 100644 --- a/odoo/addons/base/wizard/base_module_upgrade.py +++ b/odoo/addons/base/wizard/base_module_upgrade.py @@ -57,11 +57,11 @@ class BaseModuleUpgrade(models.TransientModel): FROM ir_module_module m JOIN ir_module_module_dependency d ON (m.id = d.module_id) LEFT JOIN ir_module_module m2 ON (d.name = m2.name) - WHERE m.id in %s and (m2.state IS NULL or m2.state IN %s) """ - self._cr.execute(query, (tuple(mods.ids), ('uninstalled',))) + WHERE m.id = any(%s) and (m2.state IS NULL or m2.state = %s) """ + self._cr.execute(query, (mods.ids, 'uninstalled')) unmet_packages = [row[0] for row in self._cr.fetchall()] if unmet_packages: - raise UserError(_('The following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages))) + raise UserError(_('The following modules are not installed or unknown: %s', '\n\n' + '\n'.join(unmet_packages))) # terminate transaction before re-creating cursor below self._cr.commit()