"""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, }