Initial commit: Odoo 18.0-20251222 extra-addons
Some checks failed
pre-commit / pre-commit (push) Has been cancelled
tests / Detect unreleased dependencies (push) Has been cancelled
tests / test with OCB (push) Has been cancelled
tests / test with Odoo (push) Has been cancelled

This commit is contained in:
tocmo0nlord
2026-03-13 20:43:25 +00:00
parent 36e847a7df
commit adbe430761
9472 changed files with 1265727 additions and 0 deletions

137
sale_invoice_policy/README.rst Executable file
View File

@@ -0,0 +1,137 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
===================
Sale invoice Policy
===================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:92833c38d476bc1f03727290a81ccb0609e0955b1a905f3f68ce3a96a910858f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/license-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%2Fsale--workflow-lightgray.png?logo=github
:target: https://github.com/OCA/sale-workflow/tree/18.0/sale_invoice_policy
:alt: OCA/sale-workflow
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_invoice_policy
: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/sale-workflow&target_branch=18.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module adds an invoicing policy on sale order level in order to
apply that invoicing policy on the whole sale order.
That invoicing policy can take three values:
- Products Invoicing Policy: The sale order will follow the standard
behavior and apply the policy depending on products configurations.
- Ordered Quantities: The sale order will invoice the ordered
quantities.
- Delivered Quantities: The sale order will invoice the delivered
quantities.
Following the chosen policy, the quantity to invoice and the amount to
invoice on each line will be computed accordingly.
You will be able also to define a default invoicing policy (globally per
company) that can be different than the default invoicing policy for new
products.
**Table of contents**
.. contents::
:local:
Use Cases / Context
===================
In Odoo, products have their own invoicing policy that can be:
- Invoicing on ordered quantities
- Invoicing on ordered quantities
Following that configuration, when trying to create invoices from sale
orders, each line of product will apply its invoicing policy.
In some cases, user needs to apply an invoicing policy on a whole sale
order.
The solution proposed here is to add an invoicing policy on sale order
level.
Configuration
=============
- Go to Sale > Configuration > Settings > Sale Invoice Policy
- Choose the one that fits your needs.
Usage
=====
- Create Sale Order
- Select Invoicing Policy on Sale Order or let it on Products Invoicing
Policy
- The created invoices will use the configuration on sale order.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/sale-workflow/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/sale-workflow/issues/new?body=module:%20sale_invoice_policy%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
-------
* ACSONE SA/NV
Contributors
------------
- Cédric Pigeon <cedric.pigeon@acsone.eu>
- François Honoré <francois.honore@acsone.eu>
- Denis Roussel <denis.roussel@acsone.eu>
- Alexei Rivera <arivera@archeti.com>
- Luis J. Salvatierra <luis.salvatierra@factorlibre.com>
- Alejandro Ji Cheung <alejandro.jicheung@factorlibre.com>
- Ioan Galan <ioan@studio73.es>
- Laurent Mignon <laurent.mignon@acsone.eu>
- Marie Lejeune <marie.lejeune@acsone.eu>
- Jacques-Etienne Baudoux (BCIM) <<je.bcim.be>>
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/sale-workflow <https://github.com/OCA/sale-workflow/tree/18.0/sale_invoice_policy>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,2 @@
from .hooks import pre_init_hook
from . import models

View File

@@ -0,0 +1,20 @@
# Copyright 2017 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Sale invoice Policy",
"summary": """
Sales Management: let the user choose the invoice policy on the
order""",
"author": "ACSONE SA/NV, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/sale-workflow",
"category": "Sales Management",
"version": "18.0.1.1.0",
"license": "AGPL-3",
"depends": ["sale_stock", "base_partition"],
"external_dependencies": {"python": ["openupgradelib"]},
"data": [
"views/res_config_settings_view.xml",
"views/sale_view.xml",
],
"pre_init_hook": "pre_init_hook",
}

22
sale_invoice_policy/hooks.py Executable file
View File

@@ -0,0 +1,22 @@
# Copyright 2024 ACSONE SA/NV (<https://acsone.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from openupgradelib import openupgrade
def pre_init_hook(env):
"""
Create the sale order invoice policy with the "product" policy (standard)
but with a postgres query to avoid an update on all sale order records
"""
field_spec = [
(
"invoice_policy",
"sale.order",
False,
"selection",
False,
"sale_invoice_policy",
"product",
)
]
openupgrade.add_fields(env, field_spec=field_spec)

109
sale_invoice_policy/i18n/ca.po Executable file
View File

@@ -0,0 +1,109 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_invoice_policy
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2020-02-26 18:13+0000\n"
"Last-Translator: georginaf <georgina.fornes@qubiq.es>\n"
"Language-Team: none\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.10\n"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_config_settings
msgid "Config Settings"
msgstr "Configuració"
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__delivery
msgid "Delivered quantities"
msgstr "Quantitats entregades"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy
msgid "Invoice Policy"
msgstr "Política de Facturació"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy_required
msgid "Invoice Policy Required"
msgstr "Política de Facturació"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Invoice Policy required in Sale Orders"
msgstr "Política de Facturació"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_sale_order__invoice_policy
msgid ""
"Ordered Quantity: Invoice based on the quantity the customer ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor delivered "
"(time or deliveries)."
msgstr ""
"Quantitat demanada: Factura basada en la quantitat demanada pel client.\n"
"Quantitat entregada: Factura basada en funció de la quantitat entregada pel "
"proveïdor ( temps o entregues)."
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__order
msgid "Ordered quantities"
msgstr "Quantitats Demanades"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Sale Invoice Policy"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "Sale Invoice Policy Required"
msgstr "Política de Facturació"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order_line
msgid "Sales Order Line"
msgstr "Línia de comanda de venda"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "This makes Invoice Policy required on Sale Orders"
msgstr "La política de facturació és necessària a les comandes de venda"
#~ msgid "Default Invoicing Policy"
#~ msgstr "Política de Facturació"
#~ msgid "Default Sale Invoice Policy"
#~ msgstr "Política de Facturació"
#~ msgid "Default Sale Order Invoice Policy"
#~ msgstr "Política de Facturació"
#~ msgid "Invoicing Policy"
#~ msgstr "Política de Facturació"
#~ msgid ""
#~ "Ordered Quantity: Invoice quantities ordered by the customer.\n"
#~ "Delivered Quantity: Invoice quantities delivered to the customer."
#~ msgstr ""
#~ "Quantitat demanada: Factura basada en la quantitat demanada pel client.\n"
#~ "Quantitat entregada: Facturada en funció de la quantitat entregada pel "
#~ "proveïdor ( temps o entregues)."
#~ msgid "Product Template"
#~ msgstr "Plantilla del Producte"
#~ msgid "Sale Order"
#~ msgstr "Comanda de Venda"

105
sale_invoice_policy/i18n/es.po Executable file
View File

@@ -0,0 +1,105 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_invoice_policy
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-03-22 14:12+0000\n"
"PO-Revision-Date: 2023-03-22 14:12+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_config_settings
msgid "Config Settings"
msgstr "Opciones de configuración"
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__delivery
msgid "Delivered quantities"
msgstr "Cantidades entregadas"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy
msgid "Invoice Policy"
msgstr "Política de facturación"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy_required
msgid "Invoice Policy Required"
msgstr "Política de facturación requerida"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Invoice Policy required in Sale Orders"
msgstr "Política de facturación requerida en pedidos de venta"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_sale_order__invoice_policy
msgid ""
"Ordered Quantity: Invoice based on the quantity the customer ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor delivered "
"(time or deliveries)."
msgstr ""
"Cantidad pedida: facturada en función de la cantidad pedida por el cliente.\n"
"Cantidad entregada: facturada en función de la cantidad entregada por el "
"proveedor (tiempo o entregas)."
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__order
msgid "Ordered quantities"
msgstr "Cantidades pedidas"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Sale Invoice Policy"
msgstr "Política de facturación para ventas"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "Sale Invoice Policy Required"
msgstr "Política de facturación requerida para ventas"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order
msgid "Sales Order"
msgstr "Pedido de venta"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order_line
msgid "Sales Order Line"
msgstr "Línea de pedido de venta"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "This makes Invoice Policy required on Sale Orders"
msgstr "Hace que la política de facturación sea requerida en pedidos de venta"
#~ msgid "Default Invoicing Policy"
#~ msgstr "Política de facturación por defecto"
#~ msgid "Default Sale Invoice Policy"
#~ msgstr "Política de facturación por defecto para ventas"
#~ msgid "Default Sale Order Invoice Policy"
#~ msgstr "Política de facturación por defecto en pedidos de venta"
#~ msgid "Invoicing Policy"
#~ msgstr "Política de facturación"
#~ msgid ""
#~ "Ordered Quantity: Invoice quantities ordered by the customer.\n"
#~ "Delivered Quantity: Invoice quantities delivered to the customer."
#~ msgstr ""
#~ "Cantidad pedida: facturar cantidades pedidas por el cliente.\n"
#~ "Cantidad entregada: facturar cantidades entregadas al cliente."
#~ msgid "Product"
#~ msgstr "Producto"

106
sale_invoice_policy/i18n/es_MX.po Executable file
View File

@@ -0,0 +1,106 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_invoice_policy
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2021-06-01 21:48+0000\n"
"Last-Translator: Jesús Alan Ramos Rodríguez <alan.ramos@jarsa.com.mx>\n"
"Language-Team: none\n"
"Language: es_MX\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.3.2\n"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_config_settings
msgid "Config Settings"
msgstr "Configuración"
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__delivery
msgid "Delivered quantities"
msgstr "Cantidades entregadas"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy
msgid "Invoice Policy"
msgstr "Política de Facturación"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy_required
msgid "Invoice Policy Required"
msgstr "Política de facturación requerida"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Invoice Policy required in Sale Orders"
msgstr "Política de facturas requerida en órdenes de venta"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_sale_order__invoice_policy
msgid ""
"Ordered Quantity: Invoice based on the quantity the customer ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor delivered "
"(time or deliveries)."
msgstr ""
"Cantidad pedida: Factura basada en la cantidad solicitada por el cliente.\n"
"Cantidad entregada: Facturada en base a la cantidad entregada por el "
"proveedor (tiempo o entregas)."
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__order
msgid "Ordered quantities"
msgstr "Cantidades ordenadas"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Sale Invoice Policy"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "Sale Invoice Policy Required"
msgstr "Política de facturación en ventas requerido"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order
msgid "Sales Order"
msgstr "Pedido de Ventas"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order_line
msgid "Sales Order Line"
msgstr "Línea de Pedido de Venta"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "This makes Invoice Policy required on Sale Orders"
msgstr ""
"Esto hace que la política de facturas sea obligatoria en los pedidos de venta"
#~ msgid "Default Invoicing Policy"
#~ msgstr "Política de Facturación por Defecto"
#~ msgid "Default Sale Invoice Policy"
#~ msgstr "Política de Facturación de Ventas por Defecto"
#~ msgid "Default Sale Order Invoice Policy"
#~ msgstr "Política de factura de pedido de venta predeterminada"
#~ msgid "Invoicing Policy"
#~ msgstr "Política de facturación"
#~ msgid ""
#~ "Ordered Quantity: Invoice quantities ordered by the customer.\n"
#~ "Delivered Quantity: Invoice quantities delivered to the customer."
#~ msgstr ""
#~ "Cantidad ordenada: cantidades de factura ordenadas por el cliente.\n"
#~ "Cantidad entregada: cantidades de facturas entregadas al cliente."
#~ msgid "Product Template"
#~ msgstr "Plantilla de producto"

112
sale_invoice_policy/i18n/fr.po Executable file
View File

@@ -0,0 +1,112 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_invoice_policy
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-11 13:12+0000\n"
"PO-Revision-Date: 2020-10-20 11:08+0000\n"
"Last-Translator: Quentin Dupont <perso@quentindupont.fr>\n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 3.10\n"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_config_settings
msgid "Config Settings"
msgstr "Paramètres de configuration"
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__delivery
msgid "Delivered quantities"
msgstr "Quantités Livrées"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy
msgid "Invoice Policy"
msgstr "Politique de facturation"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy_required
msgid "Invoice Policy Required"
msgstr "Politique de facturation requise"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Invoice Policy required in Sale Orders"
msgstr "Politique de facturation requise dans les commandes de vente"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_sale_order__invoice_policy
msgid ""
"Ordered Quantity: Invoice based on the quantity the customer ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor delivered "
"(time or deliveries)."
msgstr ""
"Quantités commandées : facturation basée sur les quantités commandées par le "
"client.\n"
"Quantités livrées : facturation basée sur les quantités que le fournisseur a "
"livré (temps passé dans le cas de prestations)."
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__order
msgid "Ordered quantities"
msgstr "Quantités Commandées"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Sale Invoice Policy"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "Sale Invoice Policy Required"
msgstr "Politique de facturation des ventes requise"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order_line
msgid "Sales Order Line"
msgstr "Ligne de bon de commande"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "This makes Invoice Policy required on Sale Orders"
msgstr "La politique de facturation est requise sur les bons de commandes"
#~ msgid "Default Invoicing Policy"
#~ msgstr "Politique de facturation par défaut"
#~ msgid "Default Sale Invoice Policy"
#~ msgstr "Politique de facturation des ventes par défaut"
#~ msgid "Default Sale Order Invoice Policy"
#~ msgstr "Politique de facturation par défaut des ventes"
#~ msgid "Invoicing Policy"
#~ msgstr "Politique de facturation"
#~ msgid ""
#~ "Ordered Quantity: Invoice quantities ordered by the customer.\n"
#~ "Delivered Quantity: Invoice quantities delivered to the customer."
#~ msgstr ""
#~ "Quantités commandées : facturation basée sur les quantités commandées par "
#~ "le client.\n"
#~ "Quantités livrées : facturation basée sur les quantités que le "
#~ "fournisseur a livré (temps passé dans le cas de prestations)."
#~ msgid "Product Template"
#~ msgstr "Modèle de produit"
#~ msgid "Sale Order"
#~ msgstr "Bon de commande"

106
sale_invoice_policy/i18n/it.po Executable file
View File

@@ -0,0 +1,106 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_invoice_policy
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-12-13 12:34+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_config_settings
msgid "Config Settings"
msgstr "Impostazioni configurazione"
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__delivery
msgid "Delivered quantities"
msgstr "Quantità consegnate"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy
msgid "Invoice Policy"
msgstr "Politica fatturazione"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy_required
msgid "Invoice Policy Required"
msgstr "Richiesta politica fatturazione"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Invoice Policy required in Sale Orders"
msgstr "Politica fatturazione richiesta negli ordini di vendita"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_sale_order__invoice_policy
msgid ""
"Ordered Quantity: Invoice based on the quantity the customer ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor delivered "
"(time or deliveries)."
msgstr ""
"Quantità ordinate: fattura basata sulle quantità ordinate dal cliente.\n"
"Quantità consegnate: fattura basata sulle quantità consegnate da fornitore "
"(tempo o consegne)."
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__order
msgid "Ordered quantities"
msgstr "Quantità ordinate"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Sale Invoice Policy"
msgstr "Plitica fatturazione vendite"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "Sale Invoice Policy Required"
msgstr "Richiesta politica fatturazione vendite"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order
msgid "Sales Order"
msgstr "Ordine di vendita"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order_line
msgid "Sales Order Line"
msgstr "Riga ordine di vendita"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "This makes Invoice Policy required on Sale Orders"
msgstr ""
"Questo rende obbligatoria la politica di fatturazione negli ordini di vendita"
#~ msgid "Default Invoicing Policy"
#~ msgstr "Politica fatturazione predefinita"
#~ msgid "Default Sale Invoice Policy"
#~ msgstr "Politica fatturazione vendite predefinita"
#~ msgid "Default Sale Order Invoice Policy"
#~ msgstr "Politica fatturazione ordini di vendita predefinita"
#~ msgid "Invoicing Policy"
#~ msgstr "Politica fatturazione"
#~ msgid ""
#~ "Ordered Quantity: Invoice quantities ordered by the customer.\n"
#~ "Delivered Quantity: Invoice quantities delivered to the customer."
#~ msgstr ""
#~ "Quantità ordinata: fattura quantità ordinate dal cliente.\n"
#~ "Quantità consegnata: fattura quantità consegnate al cliente."
#~ msgid "Product"
#~ msgstr "Prodotto"

111
sale_invoice_policy/i18n/pt.po Executable file
View File

@@ -0,0 +1,111 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_invoice_policy
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2020-09-04 01:00+0000\n"
"Last-Translator: Pedro Castro Silva <pedrocs@exo.pt>\n"
"Language-Team: none\n"
"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 3.10\n"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_config_settings
msgid "Config Settings"
msgstr "Parâmetros de Configuração"
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__delivery
msgid "Delivered quantities"
msgstr "Quantidades entregues"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy
msgid "Invoice Policy"
msgstr "Política de Faturação"
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy_required
msgid "Invoice Policy Required"
msgstr "Política de Faturação Requerida"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Invoice Policy required in Sale Orders"
msgstr "Política de Faturação requerida em Encomendas de Venda"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_sale_order__invoice_policy
msgid ""
"Ordered Quantity: Invoice based on the quantity the customer ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor delivered "
"(time or deliveries)."
msgstr ""
"Quantidade Encomendada: Faturar com base na quantidade que o cliente "
"encomendou.\n"
"Quantidade Entregue: Faturar tendo em conta a quantidade que o cliente "
"recebeu (tempo ou materiais)."
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__order
msgid "Ordered quantities"
msgstr "Quantidades encomendadas"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid "Sale Invoice Policy"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "Sale Invoice Policy Required"
msgstr "Política de Faturação de Vendas Requerida"
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order_line
msgid "Sales Order Line"
msgstr "Linha de Encomenda de Venda"
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_config_settings__sale_invoice_policy_required
msgid "This makes Invoice Policy required on Sale Orders"
msgstr ""
"Isto faz com que a Política de Faturação seja requerida em Encomendas de "
"Venda"
#~ msgid "Default Invoicing Policy"
#~ msgstr "Política de Faturação Predefinida"
#~ msgid "Default Sale Invoice Policy"
#~ msgstr "Política de Faturação de Vendas Predefinida"
#~ msgid "Default Sale Order Invoice Policy"
#~ msgstr "Política Predefinida de Faturação de Encomendas de Vendas"
#~ msgid "Invoicing Policy"
#~ msgstr "Política de Faturação"
#~ msgid ""
#~ "Ordered Quantity: Invoice quantities ordered by the customer.\n"
#~ "Delivered Quantity: Invoice quantities delivered to the customer."
#~ msgstr ""
#~ "Quantidade Encomendada: Faturar quantidades encomendadas pelo cliente.\n"
#~ "Quantidade Entregue: Faturar quantidades entregues ao cliente."
#~ msgid "Product Template"
#~ msgstr "Modelo de Produto"
#~ msgid "Sale Order"
#~ msgstr "Encomenda de Venda"

View File

@@ -0,0 +1,83 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_invoice_policy
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sale_invoice_policy
#: model_terms:ir.ui.view,arch_db:sale_invoice_policy.view_sales_config
msgid ""
"Choose the default policy that will be applied for new sale orders (the "
"'Invoicing Policy' configuration is the policy for new products)."
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_company
msgid "Companies"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__res_company__sale_default_invoice_policy__delivery
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__delivery
msgid "Delivered quantities"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_sale_order__invoice_policy
msgid "Invoice Policy"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_sale_order__invoice_policy
msgid ""
"Ordered Quantity: Invoice based on the quantity the customer ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor delivered (time or deliveries)."
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__res_company__sale_default_invoice_policy__order
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__order
msgid "Ordered quantities"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__res_company__sale_default_invoice_policy__product
#: model:ir.model.fields.selection,name:sale_invoice_policy.selection__sale_order__invoice_policy__product
msgid "Products Invoice Policy"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_company__sale_default_invoice_policy
#: model:ir.model.fields,field_description:sale_invoice_policy.field_res_config_settings__sale_default_invoice_policy
msgid "Sale Default Invoice Policy"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model,name:sale_invoice_policy.model_sale_order_line
msgid "Sales Order Line"
msgstr ""
#. module: sale_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_company__sale_default_invoice_policy
#: model:ir.model.fields,help:sale_invoice_policy.field_res_config_settings__sale_default_invoice_policy
msgid "This will be the default invoice policy for sale orders."
msgstr ""

View File

@@ -0,0 +1,4 @@
from . import res_config_settings
from . import sale_order
from . import sale_order_line
from . import res_company

View File

@@ -0,0 +1,19 @@
# Copyright 2024 ACSONE SA/NV (<https://acsone.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class ResCompany(models.Model):
_inherit = "res.company"
sale_default_invoice_policy = fields.Selection(
[
("product", "Products Invoice Policy"),
("order", "Ordered quantities"),
("delivery", "Delivered quantities"),
],
default="product",
required=True,
help="This will be the default invoice policy for sale orders.",
)

View File

@@ -0,0 +1,13 @@
# Copyright 2018 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
sale_default_invoice_policy = fields.Selection(
related="company_id.sale_default_invoice_policy",
readonly=False,
)

View File

@@ -0,0 +1,41 @@
# Copyright 2017 ACSONE SA/NV (<http://acsone.eu>)
# Copyright 2025 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class SaleOrder(models.Model):
_inherit = "sale.order"
invoice_policy = fields.Selection(
[
("product", "Products Invoice Policy"),
("order", "Ordered quantities"),
("delivery", "Delivered quantities"),
],
compute="_compute_invoice_policy",
store=True,
readonly=False,
required=True,
precompute=True,
help="Ordered Quantity: Invoice based on the quantity the customer "
"ordered.\n"
"Delivered Quantity: Invoiced based on the quantity the vendor "
"delivered (time or deliveries).",
)
@api.depends("company_id")
def _compute_invoice_policy(self) -> None:
"""
Get default sale order invoice policy
"""
for company, sale_orders in self.partition("company_id").items():
sale_orders.invoice_policy = company.sale_default_invoice_policy
def _force_lines_to_invoice_policy_order(self):
# When a SO is fully paid by a payment transaction and the automatic
# invoicing is enabled, the lines are forced to policy on order.
# Reflect this on the SO policy.
self.invoice_policy = "order"
return super()._force_lines_to_invoice_policy_order()

View File

@@ -0,0 +1,65 @@
# Copyright 2017 ACSONE SA/NV (<http://acsone.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from contextlib import contextmanager
from odoo import api, models
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
@contextmanager
def _sale_invoice_policy(self, lines):
"""Apply the sale invoice policy to the products
This method must be called with lines sharing the same invoice policy
"""
invoice_policy = set(lines.mapped("order_id.invoice_policy"))
if len(invoice_policy) > 1:
raise Exception(
"The method _sale_invoice_policy() must be called with lines "
"sharing the same invoice policy"
)
invoice_policy = next(iter(invoice_policy))
invoice_policy_field = self.env["product.product"]._fields["invoice_policy"]
products = lines.product_id
with self.env.protecting([invoice_policy_field], products):
old_values = {}
for product in products:
old_values[product] = product.invoice_policy
product.invoice_policy = invoice_policy
yield
for product, invoice_policy in old_values.items():
product.invoice_policy = invoice_policy
@api.depends("order_id.invoice_policy")
def _compute_qty_to_invoice(self):
"""
Exclude lines that have their order invoice policy filled in
"""
other_lines = self.filtered(
lambda line: line.product_id.type == "service"
or line.order_id.invoice_policy == "product"
)
super(SaleOrderLine, other_lines)._compute_qty_to_invoice()
for line in self - other_lines:
invoice_policy = line.order_id.invoice_policy
if invoice_policy == "order":
line.qty_to_invoice = line.product_uom_qty - line.qty_invoiced
else:
line.qty_to_invoice = line.qty_delivered - line.qty_invoiced
return True
@api.depends("order_id.invoice_policy")
def _compute_untaxed_amount_to_invoice(self) -> None:
other_lines = self.filtered(
lambda line: line.product_id.type == "service"
or line.order_id.invoice_policy == "product"
or line.order_id.invoice_policy == line.product_id.invoice_policy
or line.state not in ["sale", "done"]
)
super(SaleOrderLine, other_lines)._compute_untaxed_amount_to_invoice()
for lines in (self - other_lines).partition("order_id.invoice_policy").values():
with self._sale_invoice_policy(lines):
super(SaleOrderLine, lines)._compute_untaxed_amount_to_invoice()
return

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@@ -0,0 +1,2 @@
- Go to Sale \> Configuration \> Settings \> Sale Invoice Policy
- Choose the one that fits your needs.

View File

@@ -0,0 +1,13 @@
In Odoo, products have their own invoicing policy that can be:
- Invoicing on ordered quantities
- Invoicing on ordered quantities
Following that configuration, when trying to create invoices from sale
orders, each line of product will apply its invoicing policy.
In some cases, user needs to apply an invoicing policy on a whole sale
order.
The solution proposed here is to add an invoicing policy on sale order
level.

View File

@@ -0,0 +1,10 @@
- Cédric Pigeon \<<cedric.pigeon@acsone.eu>\>
- François Honoré \<<francois.honore@acsone.eu>\>
- Denis Roussel \<<denis.roussel@acsone.eu>\>
- Alexei Rivera \<<arivera@archeti.com>\>
- Luis J. Salvatierra \<<luis.salvatierra@factorlibre.com>\>
- Alejandro Ji Cheung \<<alejandro.jicheung@factorlibre.com>\>
- Ioan Galan \<<ioan@studio73.es>\>
- Laurent Mignon \<<laurent.mignon@acsone.eu>\>
- Marie Lejeune \<<marie.lejeune@acsone.eu>\>
- Jacques-Etienne Baudoux (BCIM) \<<je.bcim.be>\>

View File

@@ -0,0 +1,18 @@
This module adds an invoicing policy on sale order level in order to
apply that invoicing policy on the whole sale order.
That invoicing policy can take three values:
- Products Invoicing Policy: The sale order will follow the standard
behavior and apply the policy depending on products configurations.
- Ordered Quantities: The sale order will invoice the ordered
quantities.
- Delivered Quantities: The sale order will invoice the delivered
quantities.
Following the chosen policy, the quantity to invoice and the amount to
invoice on each line will be computed accordingly.
You will be able also to define a default invoicing policy (globally per
company) that can be different than the default invoicing policy for new
products.

View File

@@ -0,0 +1,4 @@
- Create Sale Order
- Select Invoicing Policy on Sale Order or let it on Products Invoicing
Policy
- The created invoices will use the configuration on sale order.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,486 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="sale-invoice-policy">
<h1>Sale invoice Policy</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:92833c38d476bc1f03727290a81ccb0609e0955b1a905f3f68ce3a96a910858f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/sale-workflow/tree/18.0/sale_invoice_policy"><img alt="OCA/sale-workflow" src="https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_invoice_policy"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module adds an invoicing policy on sale order level in order to
apply that invoicing policy on the whole sale order.</p>
<p>That invoicing policy can take three values:</p>
<ul class="simple">
<li>Products Invoicing Policy: The sale order will follow the standard
behavior and apply the policy depending on products configurations.</li>
<li>Ordered Quantities: The sale order will invoice the ordered
quantities.</li>
<li>Delivered Quantities: The sale order will invoice the delivered
quantities.</li>
</ul>
<p>Following the chosen policy, the quantity to invoice and the amount to
invoice on each line will be computed accordingly.</p>
<p>You will be able also to define a default invoicing policy (globally per
company) that can be different than the default invoicing policy for new
products.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#use-cases-context" id="toc-entry-1">Use Cases / Context</a></li>
<li><a class="reference internal" href="#configuration" id="toc-entry-2">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-3">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-4">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-5">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-6">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-7">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-8">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="use-cases-context">
<h2><a class="toc-backref" href="#toc-entry-1">Use Cases / Context</a></h2>
<p>In Odoo, products have their own invoicing policy that can be:</p>
<ul class="simple">
<li>Invoicing on ordered quantities</li>
<li>Invoicing on ordered quantities</li>
</ul>
<p>Following that configuration, when trying to create invoices from sale
orders, each line of product will apply its invoicing policy.</p>
<p>In some cases, user needs to apply an invoicing policy on a whole sale
order.</p>
<p>The solution proposed here is to add an invoicing policy on sale order
level.</p>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-2">Configuration</a></h2>
<ul class="simple">
<li>Go to Sale &gt; Configuration &gt; Settings &gt; Sale Invoice Policy</li>
<li>Choose the one that fits your needs.</li>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-3">Usage</a></h2>
<ul class="simple">
<li>Create Sale Order</li>
<li>Select Invoicing Policy on Sale Order or let it on Products Invoicing
Policy</li>
<li>The created invoices will use the configuration on sale order.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/sale-workflow/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/sale-workflow/issues/new?body=module:%20sale_invoice_policy%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-5">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-6">Authors</a></h3>
<ul class="simple">
<li>ACSONE SA/NV</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-7">Contributors</a></h3>
<ul class="simple">
<li>Cédric Pigeon &lt;<a class="reference external" href="mailto:cedric.pigeon&#64;acsone.eu">cedric.pigeon&#64;acsone.eu</a>&gt;</li>
<li>François Honoré &lt;<a class="reference external" href="mailto:francois.honore&#64;acsone.eu">francois.honore&#64;acsone.eu</a>&gt;</li>
<li>Denis Roussel &lt;<a class="reference external" href="mailto:denis.roussel&#64;acsone.eu">denis.roussel&#64;acsone.eu</a>&gt;</li>
<li>Alexei Rivera &lt;<a class="reference external" href="mailto:arivera&#64;archeti.com">arivera&#64;archeti.com</a>&gt;</li>
<li>Luis J. Salvatierra &lt;<a class="reference external" href="mailto:luis.salvatierra&#64;factorlibre.com">luis.salvatierra&#64;factorlibre.com</a>&gt;</li>
<li>Alejandro Ji Cheung &lt;<a class="reference external" href="mailto:alejandro.jicheung&#64;factorlibre.com">alejandro.jicheung&#64;factorlibre.com</a>&gt;</li>
<li>Ioan Galan &lt;<a class="reference external" href="mailto:ioan&#64;studio73.es">ioan&#64;studio73.es</a>&gt;</li>
<li>Laurent Mignon &lt;<a class="reference external" href="mailto:laurent.mignon&#64;acsone.eu">laurent.mignon&#64;acsone.eu</a>&gt;</li>
<li>Marie Lejeune &lt;<a class="reference external" href="mailto:marie.lejeune&#64;acsone.eu">marie.lejeune&#64;acsone.eu</a>&gt;</li>
<li>Jacques-Etienne Baudoux (BCIM) &lt;&lt;je.bcim.be&gt;&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/sale-workflow/tree/18.0/sale_invoice_policy">OCA/sale-workflow</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
from . import test_sale_invoice_policy

View File

@@ -0,0 +1,188 @@
# Copyright 2017 Acsone SA/NV (http://www.acsone.eu)
# Copyright 2025 Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import odoo.tests.common as common
class TestSaleOrderInvoicePolicy(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.product_obj = cls.env["product.product"]
cls.sale_obj = cls.env["sale.order"]
cls.partner = cls.env.ref("base.res_partner_2")
cls.product = cls.product_obj.create(
{"name": "Test", "type": "consu", "list_price": 20.0}
)
cls.product2 = cls.product_obj.create(
{"name": "Test 2", "type": "consu", "list_price": 45.0}
)
cls.product3 = cls.product_obj.create(
{
"name": "Test 3 (service)",
"type": "service",
"list_price": 850.5,
}
)
def test_sale_order_invoice_order(self):
"""Test invoicing based on ordered quantities"""
so = self.env["sale.order"].create(
{
"partner_id": self.env.ref("base.res_partner_2").id,
"order_line": [
(0, 0, {"product_id": self.product.id, "product_uom_qty": 2.0}),
(0, 0, {"product_id": self.product2.id, "product_uom_qty": 3.0}),
],
"invoice_policy": "order",
}
)
self.assertEqual(so.invoice_policy, "order")
# SO is not confirmed yet
self.assertEqual([0.0, 0.0], so.order_line.mapped("untaxed_amount_to_invoice"))
so.action_confirm()
self.assertEqual(len(so.picking_ids), 1)
picking = so.picking_ids
picking.action_assign()
self.assertEqual(picking.state, "assigned")
so_line = so.order_line[0]
self.assertEqual(so_line.qty_to_invoice, 2)
self.assertEqual(so_line.invoice_status, "to invoice")
self.assertEqual(so_line.product_id.invoice_policy, "order")
so_line = so.order_line[1]
self.assertEqual(so_line.qty_to_invoice, 3)
self.assertEqual(so_line.invoice_status, "to invoice")
self.assertEqual(so_line.product_id.invoice_policy, "order")
def test_sale_order_invoice_deliver(self):
"""Test invoicing based on delivered quantities"""
self.assertEqual("order", self.product.invoice_policy)
so = self.env["sale.order"].create(
{
"partner_id": self.env.ref("base.res_partner_2").id,
"invoice_policy": "delivery",
"order_line": [
(0, 0, {"product_id": self.product.id, "product_uom_qty": 2.0}),
(0, 0, {"product_id": self.product2.id, "product_uom_qty": 3.0}),
],
}
)
self.assertEqual(so.invoice_policy, "delivery")
# SO is not confirmed yet
self.assertEqual([0.0, 0.0], so.order_line.mapped("untaxed_amount_to_invoice"))
so.action_confirm()
# SO is not delivered
self.assertEqual([0.0, 0.0], so.order_line.mapped("untaxed_amount_to_invoice"))
self.assertEqual(len(so.picking_ids), 1)
picking = so.picking_ids
picking.action_assign()
self.assertEqual(picking.state, "assigned")
so_line = so.order_line[0]
self.assertEqual(so_line.qty_to_invoice, 0)
self.assertEqual(so_line.invoice_status, "no")
so_line = so.order_line[1]
self.assertEqual(so_line.qty_to_invoice, 0)
self.assertEqual(so_line.invoice_status, "no")
for mv in picking.move_line_ids:
mv.quantity = mv.quantity_product_uom
picking.button_validate()
self.assertEqual(picking.state, "done")
so_line = so.order_line[0]
self.assertEqual(so_line.qty_to_invoice, 2)
self.assertEqual(so_line.invoice_status, "to invoice")
self.assertEqual(40.0, so_line.untaxed_amount_to_invoice)
# Check that product has still its original invoice policy
self.assertEqual("order", self.product.invoice_policy)
so_line = so.order_line[1]
self.assertEqual(so_line.qty_to_invoice, 3)
self.assertEqual(so_line.invoice_status, "to invoice")
self.assertEqual(135.0, so_line.untaxed_amount_to_invoice)
def test_settings(self):
# delivery policy is the default
settings = self.env["res.config.settings"].create({})
settings.sale_default_invoice_policy = "delivery"
settings.execute()
so = self.env["sale.order"].create(
{"partner_id": self.env.ref("base.res_partner_2").id}
)
self.assertEqual(so.invoice_policy, "delivery")
# order policy is the default
settings.sale_default_invoice_policy = "order"
settings.execute()
so = self.env["sale.order"].create(
{"partner_id": self.env.ref("base.res_partner_2").id}
)
self.assertEqual(so.invoice_policy, "order")
def test_context_manager_exception(self):
"""Check the exception is well managed when called with several
invoice policies"""
self.assertEqual("order", self.product.invoice_policy)
so = self.env["sale.order"].create(
{
"partner_id": self.env.ref("base.res_partner_2").id,
"invoice_policy": "delivery",
"order_line": [
(0, 0, {"product_id": self.product.id, "product_uom_qty": 2.0}),
(0, 0, {"product_id": self.product2.id, "product_uom_qty": 3.0}),
],
}
)
so2 = self.env["sale.order"].create(
{
"partner_id": self.env.ref("base.res_partner_2").id,
"invoice_policy": "order",
"order_line": [
(0, 0, {"product_id": self.product.id, "product_uom_qty": 2.0}),
(0, 0, {"product_id": self.product2.id, "product_uom_qty": 3.0}),
],
}
)
lines = (so | so2).order_line
with self.assertRaises(Exception) as exc:
with self.env["sale.order.line"]._sale_invoice_policy(
lines
): # pragma: no cover
pass
self.assertEqual(
"The method _sale_invoice_policy() must be called with lines sharing "
"the same invoice policy",
exc.exception.args[0],
)
def test_force_lines_to_invoice_policy_order(self):
"""Check when a SO is fully paid by a payment transaction and the automatic
invoicing is enabled, that the policy is changed on order."""
so = self.env["sale.order"].create(
{
"partner_id": self.env.ref("base.res_partner_2").id,
"order_line": [
(0, 0, {"product_id": self.product.id, "product_uom_qty": 2.0}),
(0, 0, {"product_id": self.product2.id, "product_uom_qty": 3.0}),
],
"invoice_policy": "delivery",
}
)
so.action_confirm()
self.assertEqual(so.invoice_policy, "delivery")
so._force_lines_to_invoice_policy_order()
self.assertEqual(so.invoice_policy, "order")

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_sales_config" model="ir.ui.view">
<field name="name">sale settings</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="sale.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath
expr="//setting[@id='sales_settings_invoicing_policy']"
position="after"
>
<setting
id="sale_invoice_policy_setting"
help="Choose the default policy that will be applied for new sale orders (the 'Invoicing Policy' configuration is the policy for new products)."
>
<field name="sale_default_invoice_policy" />
</setting>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="sale_order_view_form_inherit_abi_product" model="ir.ui.view">
<field name="name">sale.order.form (abi_sale_invoice_policy)</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="priority">50</field>
<field name="arch" type="xml">
<field name="payment_term_id" position="after">
<field name="invoice_policy" />
</field>
</field>
</record>
</odoo>