Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
4
sale_invoice_policy/models/__init__.py
Executable file
4
sale_invoice_policy/models/__init__.py
Executable file
@@ -0,0 +1,4 @@
|
||||
from . import res_config_settings
|
||||
from . import sale_order
|
||||
from . import sale_order_line
|
||||
from . import res_company
|
||||
19
sale_invoice_policy/models/res_company.py
Executable file
19
sale_invoice_policy/models/res_company.py
Executable 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.",
|
||||
)
|
||||
13
sale_invoice_policy/models/res_config_settings.py
Executable file
13
sale_invoice_policy/models/res_config_settings.py
Executable 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,
|
||||
)
|
||||
41
sale_invoice_policy/models/sale_order.py
Executable file
41
sale_invoice_policy/models/sale_order.py
Executable 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()
|
||||
65
sale_invoice_policy/models/sale_order_line.py
Executable file
65
sale_invoice_policy/models/sale_order_line.py
Executable 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
|
||||
Reference in New Issue
Block a user