[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) <xmo@odoo.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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': '',
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."))
|
||||
|
||||
@@ -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'})
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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': '',
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 =======
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 <b>%s</b>, <br/> a manufacturing order is created to fulfill the need.') % (destination)
|
||||
source, destination, __ = self._get_message_values()
|
||||
manufacture_message = _('When products are needed in <b>%s</b>, <br/> a manufacturing order is created to fulfill the need.', destination)
|
||||
if self.location_src_id:
|
||||
manufacture_message += _(' <br/><br/> The components will be taken from <b>%s</b>.') % (source)
|
||||
message_dict.update({
|
||||
'manufacture': manufacture_message
|
||||
})
|
||||
manufacture_message += _(' <br/><br/> The components will be taken from <b>%s</b>.', source)
|
||||
message_dict['manufacture'] = manufacture_message
|
||||
return message_dict
|
||||
|
||||
@api.depends('action')
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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')],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)),
|
||||
}}
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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),
|
||||
}}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 <b>%s</b>, <br/> '
|
||||
'a request for quotation is created to fulfill the need.<br/>'
|
||||
'Note: This rule will be used in combination with the rules<br/>'
|
||||
'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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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<br/>
|
||||
From <strong>"%s"</strong> To <strong>"%s"</strong>,
|
||||
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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user