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

View File

@@ -0,0 +1,6 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from . import exception_rule
from . import sale_order
from . import sale_order_line
from . import res_company
from . import res_config_settings

View File

@@ -0,0 +1,22 @@
# Copyright 2011 Akretion, Sodexis
# Copyright 2018 Akretion
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class ExceptionRule(models.Model):
_inherit = "exception.rule"
model = fields.Selection(
selection_add=[
("sale.order", "Sale order"),
("sale.order.line", "Sale order line"),
],
ondelete={
"sale.order": "cascade",
"sale.order.line": "cascade",
},
)
sale_ids = fields.Many2many("sale.order", string="Sales")

View File

@@ -0,0 +1,12 @@
# Copyright 2025 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models
class ResCompany(models.Model):
_inherit = "res.company"
sale_exception_show_popup = fields.Boolean(
string="Sale Exception Popup",
default=True,
)

View File

@@ -0,0 +1,13 @@
# Copyright 2025 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
sale_exception_show_popup = fields.Boolean(
related="company_id.sale_exception_show_popup",
readonly=False,
)

View File

@@ -0,0 +1,69 @@
# Copyright 2011 Akretion, Sodexis
# Copyright 2018 Akretion
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, models
class SaleOrder(models.Model):
_inherit = ["sale.order", "base.exception"]
_name = "sale.order"
_order = "main_exception_id asc, date_order desc, name desc"
@api.model
def _reverse_field(self):
return "sale_ids"
def detect_exceptions(self):
all_exceptions = super().detect_exceptions()
lines = self.mapped("order_line")
all_exceptions += lines.detect_exceptions()
return all_exceptions
@api.model
def test_all_draft_orders(self):
order_set = self.search([("state", "=", "draft")])
order_set.detect_exceptions()
return True
def _fields_trigger_check_exception(self):
return ["ignore_exception", "order_line", "state"]
def _check_sale_check_exception(self, vals):
check_exceptions = any(
field in vals for field in self._fields_trigger_check_exception()
)
if check_exceptions:
self.sale_check_exception()
def write(self, vals):
result = super().write(vals)
self._check_sale_check_exception(vals)
return result
def sale_check_exception(self):
orders = self.filtered(lambda s: s.state == "sale")
if orders:
orders._check_exception()
def action_confirm(self):
if self.detect_exceptions():
if not self.env.company.sale_exception_show_popup:
return
return self._popup_exceptions()
return super().action_confirm()
def action_draft(self):
res = super().action_draft()
orders = self.filtered("ignore_exception")
orders.write({"ignore_exception": False})
return res
def _sale_get_lines(self):
self.ensure_one()
return self.order_line
@api.model
def _get_popup_action(self):
return self.env.ref("sale_exception.action_sale_exception_confirm")

View File

@@ -0,0 +1,61 @@
# © 2019 Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import html
from odoo import api, fields, models
from odoo.fields import Command
class SaleOrderLine(models.Model):
_inherit = ["sale.order.line", "base.exception.method"]
_name = "sale.order.line"
exception_ids = fields.Many2many(
"exception.rule", string="Exceptions", copy=False, readonly=True
)
exceptions_summary = fields.Html(
readonly=True, compute="_compute_exceptions_summary"
)
ignore_exception = fields.Boolean(
related="order_id.ignore_exception", store=True, string="Ignore Exceptions"
)
is_exception_danger = fields.Boolean(compute="_compute_is_exception_danger")
@api.depends("exception_ids", "ignore_exception")
def _compute_is_exception_danger(self):
for rec in self:
rec.is_exception_danger = (
len(rec.exception_ids) > 0 and not rec.ignore_exception
)
@api.depends("exception_ids", "ignore_exception")
def _compute_exceptions_summary(self):
for rec in self:
if rec.exception_ids and not rec.ignore_exception:
rec.exceptions_summary = rec._get_exception_summary()
else:
rec.exceptions_summary = False
def _get_exception_summary(self):
items = "".join(
f"<li>{html.escape(e.name)}: <i>{html.escape(e.description)}</i></li>"
for e in self.exception_ids
)
return f"<ul>{items}</ul>"
def _get_main_records(self):
return self.mapped("order_id")
@api.model
def _reverse_field(self):
return "sale_ids"
def _detect_exceptions(self, rule):
records = super()._detect_exceptions(rule)
# Thanks to the new flush of odoo 13.0, queries will be optimized
# together at the end even if we update the exception_ids many times.
# On previous versions, this could be unoptimized.
(self - records).exception_ids = [Command.unlink(rule.id)]
records.exception_ids = [Command.link(rule.id)]
return records.mapped("order_id")