Files
odoo_source/addons/account_debit_note/wizard/account_debit_note.py
T
Martin Trigaux 69f911d994 [IMP] *: enforce usage of Markup in mail
When using message_post, the body format must be explicitly specified.
If html is expected, a Markup object should be used.
If text is given, the content will be escaped.

Before this PR:
message_post was unaware if the content of a message was HTML or
text. This lead to multiple situation where the content was
incorrectly considered as HTML and led to display errors.
In
  self.message_post(body="Hello %s!" % self.name)
if the name contained HTML, it would be evaluated.

In
  self.message_post(body="Contact Raoul <raoul@caramail.be>")
the email would not be displayed as considered as unknown HTML and
discarded by the sanitizer

Now each call must explict the type of content.
Use the escape() helper to properly combine Markup and translations.
It would also be acceptable to use Markup() to wrap a static
translation but escape is better as one can not guarantee the content
of a translation.

closes odoo/odoo#111850

Related: odoo/documentation#3612
Related: odoo/enterprise#36728
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2023-04-13 16:39:48 +02:00

95 lines
4.7 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.tools.translate import _
from odoo.exceptions import UserError
from markupsafe import escape
class AccountDebitNote(models.TransientModel):
"""
Add Debit Note wizard: when you want to correct an invoice with a positive amount.
Opposite of a Credit Note, but different from a regular invoice as you need the link to the original invoice.
In some cases, also used to cancel Credit Notes
"""
_name = 'account.debit.note'
_description = 'Add Debit Note wizard'
move_ids = fields.Many2many('account.move', 'account_move_debit_move', 'debit_id', 'move_id',
domain=[('state', '=', 'posted')])
date = fields.Date(string='Debit Note Date', default=fields.Date.context_today, required=True)
reason = fields.Char(string='Reason')
journal_id = fields.Many2one('account.journal', string='Use Specific Journal',
help='If empty, uses the journal of the journal entry to be debited.')
copy_lines = fields.Boolean("Copy Lines",
help="In case you need to do corrections for every line, it can be in handy to copy them. "
"We won't copy them for debit notes from credit notes. ")
# computed fields
move_type = fields.Char(compute="_compute_from_moves")
journal_type = fields.Char(compute="_compute_from_moves")
country_code = fields.Char(related='move_ids.company_id.country_id.code')
@api.model
def default_get(self, fields):
res = super(AccountDebitNote, self).default_get(fields)
move_ids = self.env['account.move'].browse(self.env.context['active_ids']) if self.env.context.get('active_model') == 'account.move' else self.env['account.move']
if any(move.state != "posted" for move in move_ids):
raise UserError(_('You can only debit posted moves.'))
elif any(move.debit_origin_id for move in move_ids):
raise UserError(_("You can't make a debit note for an invoice that is already linked to a debit note."))
elif any(move.move_type not in ['out_invoice', 'in_invoice', 'out_refund', 'in_refund'] for move in move_ids):
raise UserError(_("You can make a debit note only for a Customer Invoice, a Customer Credit Note, a Vendor Bill or a Vendor Credit Note."))
res['move_ids'] = [(6, 0, move_ids.ids)]
return res
@api.depends('move_ids')
def _compute_from_moves(self):
for record in self:
move_ids = record.move_ids
record.move_type = move_ids[0].move_type if len(move_ids) == 1 or not any(m.move_type != move_ids[0].move_type for m in move_ids) else False
record.journal_type = record.move_type in ['in_refund', 'in_invoice'] and 'purchase' or 'sale'
def _prepare_default_values(self, move):
if move.move_type in ('in_refund', 'out_refund'):
type = 'in_invoice' if move.move_type == 'in_refund' else 'out_invoice'
else:
type = move.move_type
default_values = {
'ref': '%s, %s' % (move.name, self.reason) if self.reason else move.name,
'date': self.date or move.date,
'invoice_date': move.is_invoice(include_receipts=True) and (self.date or move.date) or False,
'journal_id': self.journal_id and self.journal_id.id or move.journal_id.id,
'invoice_payment_term_id': None,
'debit_origin_id': move.id,
'move_type': type,
}
if not self.copy_lines or move.move_type in [('in_refund', 'out_refund')]:
default_values['line_ids'] = [(5, 0, 0)]
return default_values
def create_debit(self):
self.ensure_one()
new_moves = self.env['account.move']
for move in self.move_ids.with_context(include_business_fields=True): #copy sale/purchase links
default_values = self._prepare_default_values(move)
new_move = move.copy(default=default_values)
move_msg = escape(_("This debit note was created from: %s")) % move._get_html_link()
new_move.message_post(body=move_msg)
new_moves |= new_move
action = {
'name': _('Debit Notes'),
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'context': {'default_move_type': default_values['move_type']},
}
if len(new_moves) == 1:
action.update({
'view_mode': 'form',
'res_id': new_moves.id,
})
else:
action.update({
'view_mode': 'tree,form',
'domain': [('id', 'in', new_moves.ids)],
})
return action