feat(fusion_accounting_documents): Documents app <-> invoice bridge
Replaces Enterprise's documents_account with a Fusion-native bridge. When a PDF/image lands in the Documents app, users can convert it into a draft vendor bill via a wizard that copies the document's binary onto the new account.move and posts a chatter note linking back to the source document. Adds: - documents.document.move_id (Many2one to the linked invoice) - documents.document.is_invoice_candidate (computed; True for unlinked PDF/image binaries) - documents.document.action_create_invoice() opens the wizard - account.move.source_document_ids reverse linkage + statinfo button - fusion.create.invoice.from.document.wizard (TransientModel + form) - ir.actions.server bound to documents.document so the workflow appears in the kanban/list Actions menu (the Documents app has no regular form view to inherit from in v19) The wizard: - defaults to the company's first purchase journal - supports vendor bill or vendor credit note - copies the source attachment onto the new move - posts a chatter note linking back - marks the document linked so it stops appearing as a candidate Auto-installs when documents + fusion_accounting_core are both present. 8 unit tests cover the candidate flag, wizard happy path, attachment copy, reverse linkage, already-linked guard, non-PDF guard, and credit-note creation. Made-with: Cursor
This commit is contained in:
71
fusion_accounting_documents/models/documents_document.py
Normal file
71
fusion_accounting_documents/models/documents_document.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""Bridge documents.document to accounting moves.
|
||||
|
||||
Adds a Many2one link to the created invoice/move, a computed
|
||||
``is_invoice_candidate`` flag for PDFs/images that have not yet been
|
||||
turned into a vendor bill, and the ``action_create_invoice`` entry
|
||||
point used by both the form button and the server action.
|
||||
"""
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
INVOICE_CANDIDATE_MIMETYPES = (
|
||||
'application/pdf',
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
)
|
||||
|
||||
|
||||
class DocumentsDocument(models.Model):
|
||||
_inherit = 'documents.document'
|
||||
|
||||
move_id = fields.Many2one(
|
||||
'account.move',
|
||||
string='Linked Invoice/Move',
|
||||
copy=False,
|
||||
ondelete='set null',
|
||||
help="The accounting move this document was used to create.",
|
||||
)
|
||||
is_invoice_candidate = fields.Boolean(
|
||||
string='Is Invoice Candidate',
|
||||
compute='_compute_is_invoice_candidate',
|
||||
store=True,
|
||||
help="True when this document looks like a vendor bill "
|
||||
"(PDF/image binary) and has not yet been linked to a move.",
|
||||
)
|
||||
|
||||
@api.depends('mimetype', 'type', 'move_id')
|
||||
def _compute_is_invoice_candidate(self):
|
||||
for d in self:
|
||||
d.is_invoice_candidate = (
|
||||
d.type == 'binary'
|
||||
and (d.mimetype or '') in INVOICE_CANDIDATE_MIMETYPES
|
||||
and not d.move_id
|
||||
)
|
||||
|
||||
def action_create_invoice(self):
|
||||
"""Open the wizard to create a vendor invoice from this document."""
|
||||
self.ensure_one()
|
||||
if self.move_id:
|
||||
raise UserError(_(
|
||||
"This document is already linked to invoice %s.",
|
||||
self.move_id.display_name,
|
||||
))
|
||||
if self.type == 'folder':
|
||||
raise UserError(_(
|
||||
"Folders cannot be turned into invoices."
|
||||
))
|
||||
if (self.mimetype or '') not in INVOICE_CANDIDATE_MIMETYPES:
|
||||
raise UserError(_(
|
||||
"Only PDF or image documents can be turned into invoices."
|
||||
))
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Create Invoice from Document'),
|
||||
'res_model': 'fusion.create.invoice.from.document.wizard',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {'default_document_id': self.id},
|
||||
}
|
||||
Reference in New Issue
Block a user