Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions account_edi_ubl_cii_payment_unece/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
=================================================
Electronic invoices with UBL/CII - UNECE payments
=================================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:2700b6157b3804b70aebf0ab3ce069449a78393795880a288a0178e0a9dff400
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github
:target: https://github.com/OCA/edi/tree/18.0/account_edi_ubl_cii_payment_unece
:alt: OCA/edi
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/edi-18-0/edi-18-0-account_edi_ubl_cii_payment_unece
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/edi&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Integrate UNECE Payment Means (module ``account_payment_unece`` from
`OCA/community-data-files
project <https://github.com/OCA/community-data-files/>`__) with Odoo
standard UBL/CII electronic invoices (module ``account_edi_ubl_cii``).

When using for example SEPA direct debit, your can configure the
corresponding UNECE code on the payment method and this will be declared
properly in the electronic invoice. Also, when receiving an invoice
declared with a payment means SEPA direct debit, you can configure an
outbound payment means with that UNECE code and the created invoice will
have that payment means set so that you know you don't have to pay it.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/edi/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/edi/issues/new?body=module:%20account_edi_ubl_cii_payment_unece%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* BCIM
* Akretion

Contributors
------------

- Jacques-Etienne Baudoux <je@bcim.be>
- Sébastien Alix <sebastien.alix@akretion.com>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/edi <https://github.com/OCA/edi/tree/18.0/account_edi_ubl_cii_payment_unece>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions account_edi_ubl_cii_payment_unece/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
23 changes: 23 additions & 0 deletions account_edi_ubl_cii_payment_unece/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2026 Akretion (https://www.akretion.com).
# @author Sébastien Alix <sebastien.alix@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
{
"name": "Electronic invoices with UBL/CII - UNECE payments",
"version": "18.0.1.0.0",
"category": "Accounting & Finance",
"license": "AGPL-3",
"summary": "Import/Export UNECE payment codes in UBL and CII XML documents.",
"author": "BCIM, Akretion, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/edi",
"depends": [
# Odoo
"account_edi_ubl_cii",
# OCA/community-data-files
"account_payment_unece",
],
"data": [
"data/account_payment_method.xml",
],
"installable": True,
"auto_install": True,
}
18 changes: 18 additions & 0 deletions account_edi_ubl_cii_payment_unece/data/account_payment_method.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2026 Akretion (https://www.akretion.com).
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">
<record id="direct_debit" model="account.payment.method">
<field name="name">Direct Debit to suppliers</field>
<field name="code">direct_debit</field>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the code you use here, you need to extend the method in AccountPaymentMethod to have the AccountPaymentMethodLine created

class AccountPaymentMethod(models.Model):
    _inherit = "account.payment.method"

    @api.model
    def _get_payment_method_information(self):
        res = super()._get_payment_method_information()
        res["direct_debit"] = {"mode": "multi", "type": ("bank",)}
        res["direct_debit_sepa"] = {"mode": "multi", "type": ("bank",)}
        return res

like here:
https://github.com/OCA/bank-payment-alternative/blob/18.0/account_payment_sepa_direct_debit/models/account_payment_method.py#L41

<field name="payment_type">outbound</field>
<field name="unece_id" ref="account_payment_unece.payment_means_49" />
</record>

<record id="direct_debit_sepa_" model="account.payment.method">
<field name="name">SEPA Direct Debit to suppliers</field>
<field name="code">direct_debit_sepa</field>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexis-via As we need different code for sepa direct debit inbound and outbound (because inbound requires a pain version and outbound doesn't - also it is in different modules) and you already used
sepa_direct_debit for inbound and sepa_credit_transfer for outbound, we've put direct_debit_sepa for outbound.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, for SEPA direct debit outbound, the code is "manual", using the native account.payment.method declared in the account module here: https://github.com/odoo/odoo/blob/18.0/addons/account/data/account_data.xml#L135
When there is no bank file to generated, using the account.payment.method with code "manual" is the good implementation for me. You can have several account.payment.method.line that use the same account.payment.method with code "manual".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexis-via The issue is that the unece code is on account.payment.method and there is a unicity constrain per code, payment_type (in/out). So we have to put a different code for each account.payment.method having a different unece code.

<field name="payment_type">outbound</field>
<field name="unece_id" ref="account_payment_unece.payment_means_59" />
</record>
</odoo>
3 changes: 3 additions & 0 deletions account_edi_ubl_cii_payment_unece/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import account_edi_ubl
from . import account_edi_xml_cii
from . import account_edi_xml_ubl_bis3
40 changes: 40 additions & 0 deletions account_edi_ubl_cii_payment_unece/models/account_edi_ubl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2026 Akretion (https://www.akretion.com).
# @author Sébastien Alix <sebastien.alix@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import models


class AccountEdiUBL(models.AbstractModel):
_inherit = "account.edi.ubl"

def _import_ubl_invoice_add_payment_reference(self, collected_values):
# NOTE: we override this method to access `collected_values`. It could
# have been any method called in '<account.edi.ubl>._ubl_import_invoice()'.
res = super()._import_ubl_invoice_add_payment_reference(collected_values)
self._import_ubl_invoice_add_unece_payment_mean(collected_values)
return res

def _import_ubl_invoice_add_unece_payment_mean(self, collected_values):
tree = collected_values["tree"]
payment_mean_code = None
for node in tree.findall("./{*}PaymentMeans/{*}PaymentMeansCode"):
if note := node.text:
payment_mean_code = note
break
if not payment_mean_code:
return
# Look for a matching payment method line
payment_method_line = self.env["account.payment.method.line"].search(
[
Comment thread
sebalix marked this conversation as resolved.
("journal_id.type", "in", ("cash", "bank", "credit")),
("payment_type", "=", "outbound"),
("payment_method_id.unece_code", "=", payment_mean_code),
],
limit=1,
)
if not payment_method_line:
return
collected_values["to_write"]["preferred_payment_method_line_id"] = (
payment_method_line.id
)
47 changes: 47 additions & 0 deletions account_edi_ubl_cii_payment_unece/models/account_edi_xml_cii.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2026 Akretion (https://www.akretion.com).
# @author Sébastien Alix <sebastien.alix@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import models


class AccountEdiXmlCII(models.AbstractModel):
_inherit = "account.edi.xml.cii"

def _export_invoice_vals(self, invoice):
template_values = super()._export_invoice_vals(invoice)
payment_method_line = invoice.preferred_payment_method_line_id
payment_method = payment_method_line.payment_method_id
if payment_method.unece_id:
# Integrate UNECE payment means
template_values["payment_means_code"] = payment_method.unece_id.code
return template_values

def _import_fill_invoice(self, invoice, tree, qty_factor):
res = super()._import_fill_invoice(invoice, tree, qty_factor)
invoice_values = self._extract_invoice_unece_payment_mean(invoice, tree)
if invoice_values:
invoice.write(invoice_values)
return res

def _extract_invoice_unece_payment_mean(self, invoice, tree):
payment_mean_code = None
for node in tree.findall(
".//{*}SpecifiedTradeSettlementPaymentMeans/{*}TypeCode"
):
if payment_mean_code := node.text:
break
if not payment_mean_code:
return {}
# Look for a matching payment method line
payment_method_line = self.env["account.payment.method.line"].search(
[
("journal_id.type", "in", ("cash", "bank", "credit")),
("payment_type", "=", "outbound"),
("payment_method_id.unece_code", "=", payment_mean_code),
],
limit=1,
)
if not payment_method_line:
return
return {"preferred_payment_method_line_id": payment_method_line.id}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2026 Akretion (https://www.akretion.com).
# @author Sébastien Alix <sebastien.alix@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import models


class AccountEdiXmlUBLBIS3(models.AbstractModel):
_inherit = "account.edi.xml.ubl_bis3"

def _ubl_add_payment_means_nodes(self, vals):
super()._ubl_add_payment_means_nodes(vals)
nodes = vals["document_node"]["cac:PaymentMeans"]
invoice = vals.get("invoice")
if not invoice:
return
payment_method_line = invoice.preferred_payment_method_line_id
payment_method = payment_method_line.payment_method_id
if payment_method.unece_id:
# Integrate UNECE payment means
for node in nodes:
node["cbc:PaymentMeansCode"] = {
"_text": payment_method.unece_id.code,
"name": payment_method.unece_id.name,
}
3 changes: 3 additions & 0 deletions account_edi_ubl_cii_payment_unece/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions account_edi_ubl_cii_payment_unece/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Jacques-Etienne Baudoux \<<je@bcim.be>\>
- Sébastien Alix \<<sebastien.alix@akretion.com>\>
10 changes: 10 additions & 0 deletions account_edi_ubl_cii_payment_unece/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Integrate UNECE Payment Means (module `account_payment_unece` from
[OCA/community-data-files project](https://github.com/OCA/community-data-files/))
with Odoo standard UBL/CII electronic invoices (module `account_edi_ubl_cii`).

When using for example SEPA direct debit, your can configure the corresponding
UNECE code on the payment method and this will be declared properly in the
electronic invoice.
Also, when receiving an invoice declared with a payment means SEPA direct debit,
you can configure an outbound payment means with that UNECE code and the created
invoice will have that payment means set so that you know you don't have to pay it.
Loading
Loading