"""Wizard to create a vendor invoice from a Documents document.
The wizard creates an empty draft ``account.move`` of the chosen
move type, copies the document's binary attachment onto the new
move, posts a chatter note linking back to the source document,
and finally stores the move on ``documents.document.move_id`` so
the source no longer appears as an invoice candidate.
"""
from odoo import _, api, fields, models
from odoo.exceptions import UserError
MOVE_TYPE_LABELS = {
'in_invoice': _('Vendor Bill'),
'in_refund': _('Vendor Credit Note'),
}
class CreateInvoiceFromDocumentWizard(models.TransientModel):
_name = 'fusion.create.invoice.from.document.wizard'
_description = 'Create Vendor Invoice from Document'
document_id = fields.Many2one(
'documents.document',
string='Source Document',
required=True,
readonly=True,
ondelete='cascade',
)
document_name = fields.Char(related='document_id.name', readonly=True)
document_mimetype = fields.Char(related='document_id.mimetype', readonly=True)
partner_id = fields.Many2one(
'res.partner',
string='Vendor',
domain="[('supplier_rank', '>', 0)]",
)
move_type = fields.Selection(
[
('in_invoice', 'Vendor Bill'),
('in_refund', 'Vendor Credit Note'),
],
string='Type',
default='in_invoice',
required=True,
)
company_id = fields.Many2one(
'res.company',
string='Company',
default=lambda self: self.env.company,
required=True,
)
journal_id = fields.Many2one(
'account.journal',
string='Journal',
domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]",
default=lambda self: self._default_journal(),
)
@api.model
def _default_journal(self):
return self.env['account.journal'].search(
[('type', '=', 'purchase'),
('company_id', '=', self.env.company.id)],
limit=1,
)
@api.onchange('company_id')
def _onchange_company_id(self):
if self.journal_id and self.journal_id.company_id != self.company_id:
self.journal_id = self.env['account.journal'].search(
[('type', '=', 'purchase'),
('company_id', '=', self.company_id.id)],
limit=1,
)
def action_create_invoice(self):
self.ensure_one()
if not self.document_id:
raise UserError(_("No document selected."))
if self.document_id.move_id:
raise UserError(_(
"Document %(doc)s is already linked to invoice %(inv)s.",
doc=self.document_id.display_name,
inv=self.document_id.move_id.display_name,
))
if not self.journal_id:
raise UserError(_(
"No purchase journal configured for company %s.",
self.company_id.display_name,
))
move_vals = {
'move_type': self.move_type,
'journal_id': self.journal_id.id,
'company_id': self.company_id.id,
}
if self.partner_id:
move_vals['partner_id'] = self.partner_id.id
move = self.env['account.move'].create(move_vals)
attachment = self.document_id.attachment_id
if attachment:
attachment_copy = attachment.copy({
'res_model': 'account.move',
'res_id': move.id,
})
move.message_post(
body=_(
"Created from Documents source: %s",
self.document_id.name,
),
attachment_ids=[attachment_copy.id],
)
else:
move.message_post(body=_(
"Created from Documents source: %s "
"(no attachment to copy).",
self.document_id.name,
))
self.document_id.move_id = move.id
return {
'type': 'ir.actions.act_window',
'name': MOVE_TYPE_LABELS.get(self.move_type, _('Invoice')),
'res_model': 'account.move',
'view_mode': 'form',
'res_id': move.id,
}