Skip to Content

Odoo 19 Community: combinar facturas en borrador – o noso módulo de código aberto Merge Draft Invoices

18 de setembro de 2026 by
Odoo 19 Community: combinar facturas en borrador – o noso módulo de código aberto Merge Draft Invoices
Alexander Biringer | Biricon IT Services GmbH

Quen factura en Odoo a partir de pedidos, tickets de helpdesk e rexistro de tempos coñece o resultado: un cliente recibe ao final do mes non unha factura, senón cinco. Cada unha procede dun pedido ou ticket distinto, cada unha é correcta no seu contido – e, aínda así, o cliente pregunta se non cabería todo nun só documento.

Precisamente para isto non hai en Odoo 19 Community unha solución limpa. O coñecido módulo OCA account_invoice_merge aínda non está liberado para Odoo 19 e resolve o problema dun xeito que supón un risco serio para os prestadores de servizos: as posicións iguais agrúpanse e, con iso, perdese o vínculo de volta co pedido, coa tarefa e co tempo rexistrado.

Para a nosa propia instalación de Odoo 19 Community desenvolvemos por iso o módulo Merge Draft Invoices. Como este problema non nos afecta só a nós, poñémolo de balde á disposición en GitHub baixo a licenza libre AGPL-3.

Ao proxecto en GitHub:

Odoo 19 Merge Draft Invoices

Por que un módulo propio en vez da solución OCA?

A OCA mantén desde hai moitos anos account_invoice_merge no repositorio OCA/account-invoicing. No momento desta publicación, a migración á 19.0 aínda é alí un pull request aberto, non un módulo publicado.

O máis importante, con todo, é que os dous módulos fan cousas distintas:


OCA account_invoice_mergeMerge Draft Invoices
Resultadocombínase nunha das facturas seleccionadassempre unha factura nova
Posiciónsas posicións iguais agrúpanse, as cantidades sumansecada posición tómase un a un
Orixeinvoice_origin / payment_reference concatenados na cabeceiracampos de orixe por posición copiados a través do hook propio de Odoo _copy_data_extend_business_fields
Rexistro de temposnon se ten en contatimesheet_invoice_id pasa á nova factura
Lexible para o clientenadaun título sobre as posicións de cada orixe co pedido e o ticket
Facturas de orixeanúlanseanular (por defecto), eliminar ou conservar – con consecuencias claramente nomeadas

Agrupar posicións iguais é a diferenza decisiva. Unha posición agrupada só pode ter unha orixe. En canto dúas liñas de dous pedidos distintos teñen o mesmo aspecto, o vínculo da factura de volta co pedido, coa tarefa e co rexistro de tempo desaparece. Nunha empresa de servizos na que a facturación segue a cadea ticket → rexistro de tempos → factura, isto significa: as mesmas horas factúranse dúas veces ou desaparecen.

O noso módulo, por iso, en principio non agrupa nada. A factura combinada ten exactamente tantas posicións como as súas orixes xuntas.

Non se tomou ningún código do módulo OCA. Ambos módulos están baixo AGPL-3.

O que fai o módulo

Na vista de lista de facturas, marcar dous ou máis borradores, escoller no menú de accións Combinar facturas en borrador – un asistente mostra a selección, as comprobacións e as opcións para as facturas de orixe. Despois de confirmar, chégase directamente á nova factura.

Antes de combinar, o módulo comproba, e fai todos os puntos dunha vez, para non ver o seguinte erro despois de cada corrección:

  • seleccionáronse polo menos dúas facturas
  • todas no estado Borrador
  • ningunha foi contabilizada nunca (un documento restablecido a borrador conserva o seu historial de contabilización e rexéitase)
  • ningunha foi xa combinada
  • todas son factura, factura de provedor, nota de abono ou documento – non simples asentos contables
  • mesmo socio, tipo de documento, empresa, moeda e diario

As diferenzas en condición de pagamento, posición fiscal, enderezo de entrega, conta bancaria ou vendedor non bloquean. O asistente avisa e adopta o valor da factura seleccionada máis antiga.

O que se conserva

O núcleo do módulo é que todo vínculo que unha posición de factura ten coa súa orixe tamén exista na nova factura:

VínculoTratamento
Liñas de pedido de venda (sale_line_ids)copiadas por posición a través de _copy_data_extend_business_fields de Odoo
Liñas de pedido de compra (purchase_line_id)igualmente
Distribución por centros de custo (analytic_distribution)copiada por posición
outros campos de orixe por posiciónautomaticamente, se o módulo provedor implementa o hook documentado de Odoo
Orixe e referencia na cabeceira da facturacombinadas, sen duplicados, editables no asistente
Pedido de venda: invoice_ids, invoice_status, qty_invoicedcalcúlanse a partir das posicións – o pedido recoñece a nova factura por si mesmo
Rexistro de tempos (timesheet_invoice_id)pasa á nova factura antes de que as orixes queden invalidadas
Ticket de helpdeskdepende do rexistro de tempos e, así, apunta automaticamente á nova factura
ChatterMensaxe na nova factura con todas as orixes, mensaxe en cada orixe cunha ligazón á nova
merged_into_move_id / merged_from_move_idsvínculo almacenado nas dúas direccións

As liñas de impostos, as liñas de condicións de pagamento e as liñas de redondeo e de desconto por pronto pagamento non se copian, senón que Odoo as recalcula a partir do contido. A factura combinada ten, polo tanto, unha liña de imposto por tipo impositivo, non unha por orixe. Créase sen numerar como borrador; a propia combinación non consome ningún número de documento.

Títulos: o cliente ve de onde procede cada posición

Esta é a función que marca a diferenza no día a día. Sobre as posicións de cada factura de orixe, o módulo insire unha liña de sección – o mesmo elemento que Odoo mesmo utiliza para a sección “Anticipos”:

BE10181                                     <- Título
  22,500 x Hora de técnico        3.577,50
                    Subtotal      3.577,50
BE10182 · Tícket: HT00219                   <- Título
   3,000 x Hora de técnico          477,00
                    Subtotal        477,00
                    IMPORTE NETO  4.054,50

O título imprímese no documento que recibe o cliente, e o informe de factura estándar de Odoo engade por si mesmo un subtotal por sección. Así o cliente ve tamén canto custou cada parte.

A etiqueta compónse co que a orixe realmente ten:

  1. os pedidos, directamente a partir dos vínculos das posicións – os nomes son, así, exactos; se faltan vínculos con pedidos, utilízase o texto libre de invoice_origin
  2. os tickets, determinados a través do rexistro de tempos – mostrados como Tícket: HT00219 ou Tickets: …, limitados a cinco máis “e N máis”
  3. se non hai ningún dos dous: o número da factura de orixe, despois a súa referencia
  4. se non hai absolutamente nada: un neutro “Outras posicións”, para que as liñas dunha orixe nunca desaparezan en silencio no bloque de arriba

O que falta omítese, non se inventa. O ticket imprímese co seu propio número – número de helpdesk_mgmt, ticket_ref de Enterprise ou o nome para mostrar do modelo – nunca co ID da base de datos.

Os títulos escríbense no idioma do cliente, tal como Odoo o fai ao xerar facturas a partir de pedidos. O chatter permanece no idioma do usuario.

Un título non leva produto, cantidade, prezo, imposto, conta nin distribución por centros de custo. Non pode influír nas sumas, na base impositiva nin na contabilidade analítica. Quen desactive os títulos no asistente obtén exactamente a factura que se obtería sen esta función – unha proba compara ambos resultados ata o último céntimo.

O que pasa coas facturas de orixe

O asistente ofrece tres opcións:

  • Anular (por defecto, recomendado). Anular un borrador non ten consecuencias: sen número de documento, sen contabilización. As facturas permanecen na base de datos como documentos anulados con chatter e ligazón á nova factura. qty_invoiced no pedido de venda omite os documentos anulados, nada se conta dúas veces. Esta é a opción que aínda se pode seguir de forma comprensible dentro de seis meses.
  • Eliminar. Irrevogable. O chatter e a referencia de volta desaparecen co rexistro; só queda a mensaxe na nova factura. Útil para borradores desbotables que ninguén debe volver ver.
  • Deixar como borrador. As orixes quedan abertas. Os seus pedidos de venda contan entón a orixe e a nova factura como facturadas – o traballo parece facturado por duplicado ata que alguén limpa os borradores a man. Pensado para o caso raro de que se queira comparar antes de decidir. Unha proba recolle precisamente esta dobre contaxe para que non sorprenda a ninguén.

Límites – o que o módulo deliberadamente non fai

Léase antes do uso produtivo:

  • Só borradores. As facturas contabilizadas non se combinan. Iso é unha tarefa de notas de abono, non un proceso de copia.
  • Rexéitanse os borradores contabilizados unha vez e restablecidos. Teñen un número e un historial de contabilización; ocultar iso noutro documento non é algo que unha acción masiva deba facer en silencio.
  • Non se combina entre socios, moedas, empresas ou diarios distintos. Non hai para iso ningún valor único con sentido, así que o módulo rexeita en vez de adiviñar.
  • payment_reference non se adopta. As referencias de pagamento estruturadas identifican un documento; dúas concatenadas non encaixan con nada no banco. A nova factura recibe a súa propia ao contabilizarse.
  • As posicións de anticipo cópianse como calquera outra posición. Se as orixes conteñen liñas de anticipo de sale, hai que revisar o resultado antes de contabilizar.
  • Só timesheet_invoice_id pasa automaticamente á nova factura. Calquera outro modelo cun Many2one a account.move (procesos de facturación propios, vínculos de subscrición, envíos EDI) segue apuntando á orixe. Para iso existe o hook _merge_draft_relink.
  • Sen agrupación de posicións iguais – de forma intencionada. Quen queira unha factura curta e ordenada está aquí no lugar equivocado e pagaría por iso cos vínculos de orixe.
  • Os títulos créanse unha soa vez ao combinar. Despois son liñas de sección normais: editables, eliminables e non se actualizan se máis tarde se renomea o pedido ou o ticket.
  • Un título nomea os tickets de toda a factura de orixe, non da posición individual.

Instalación en Odoo 19 Community

A instalación realízase como nun módulo adicional de Odoo habitual:

  1. Copiar o directorio do módulo nunha ruta de addons configurada.
  2. Actualizar a lista de aplicacións en Odoo.
  3. Instalar o módulo account_invoice_merge_draft.

O módulo depende só de account. Todo o que sabe sobre sale, purchase, project, hr_timesheet, sale_timesheet ou un módulo de helpdesk determínase en tempo de execución – instálase e funciona tamén nunha base de datos de facturación pura.

Despois: Contabilidade → Clientes → Facturas (ou Provedores → Facturas), marcar dous ou máis borradores, menú da roda dentada → Combinar facturas en borrador. A acción está dispoñible para todos os usuarios co grupo Facturación.

Quen actualice desde unha versión anterior do módulo debería facer unha soa vez a actualización con -u account_invoice_merge_draft --i18n-overwrite, para que os nomes de campo cambiados cheguen á tradución ao alemán. Unha nova instalación non se ve afectada.

Para desenvolvedores: API e hooks

A combinación tamén se pode chamar desde código propio:

python

merged = draft_moves.merge_draft_moves(
    extra_vals={"invoice_date": date(2026, 3, 31), "ref": "Q1"},
    source_handling="cancel",   # "delete" ou "keep"
    add_source_sections=True,   # Títulos; por defecto True
)

merge_draft_moves lanza un UserError con todos os motivos de rexeitamento se a selección non se pode combinar. _merge_draft_get_errors() devolve os mesmos motivos como lista sen excepción.

Quen teña referencias de volta propias a account.move sobrescribe _merge_draft_relink:

python

class AccountMove(models.Model):
    _inherit = "account.move"

    def _merge_draft_relink(self, new_move):
        super()._merge_draft_relink(new_move)
        if "my_move_id" not in self.env["my.model"]._fields:
            return
        self.env["my.model"].search([("my_move_id", "in", self.ids)]).write(
            {"my_move_id": new_move.id}
        )

Quen teña unha referencia de volta na posición da factura non necesita este módulo en absoluto: abonda co hook propio de Odoo account.move.line._copy_data_extend_business_fields, e o campo adóptase igualmente ao combinar, ao anular e ao duplicar. Para outros módulos de helpdesk ou outra formulación dos títulos existen _merge_draft_get_source_tickets() e _merge_draft_section_name().

Probado antes de funcionar con datos produtivos

48 probas cobren estados mixtos, socios, moedas e tipos de documento distintos, posicións con e sen pedido de venda ou de compra, impostos, seccións e notas, distribucións por centros de custo, o traspaso do rexistro de tempos, as tres opcións para as facturas de orixe, o asistente e os títulos – que están activos por defecto, que desactivalos reproduce o resultado antigo ata o céntimo, que un título non leva importes nin contas, que as posicións dunha orixe permanecen xuntas, que o ticket aparece co seu número e nunca co ID da base de datos.

Os datos de proba baséanse no plan de contas existente da empresa activa en lugar de AccountTestInvoicingCommon – este helper adoita non arrancar en bases de datos produtivas migradas, e precisamente nelas debe funcionar o módulo.

odoo-bin -d <db> -u account_invoice_merge_draft --test-enable \
         --test-tags /account_invoice_merge_draft --stop-after-init

Como con toda extensión no ámbito da contabilidade, recomendamos revisar a instalación primeiro nun contorno de proba cunha copia da propia base de datos.

Gratuíto e de código aberto para a Odoo Community

En Biricon IT Services GmbH desenvolvemos solucións primeiro para requisitos concretos da nosa propia actividade. Cando disto xorde un módulo de uso xeral, non queremos ocultalo nunha única instalación.

Merge Draft Invoices está por iso dispoñible de balde como proxecto de código aberto. O código fonte pódese consultar, revisar, adaptar e seguir desenvolvendo no marco da licenza AGPL-3.

O noso obxectivo non era a factura máis curta posible, senón unha factura na que, tamén dentro dun ano, cada posición leve de volta ao seu pedido, ao seu ticket e ao seu rexistro de tempo.

Proxecto, código fonte e colaboración

O código fonte completo, as indicacións de instalación e a documentación técnica están dispoñibles en GitHub:

Abrir Odoo 19 Merge Draft Invoices en GitHub

Agradecemos as experiencias prácticas, os informes de erros, as propostas de mellora e as achegas da Odoo Community.

Gratuíto. Comprensible. Código aberto. Para Odoo 19 Community.