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,7 @@
# Copyright 2025 Binhex <https://www.binhex.cloud>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import product_template_id_category
from . import product_template
from . import res_partner_id_number
from . import sale_order

View File

@@ -0,0 +1,42 @@
# Copyright 2025 Binhex <https://www.binhex.cloud>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class ProductTemplate(models.Model):
_inherit = "product.template"
required_identification = fields.Boolean(default=False)
product_tmpl_category_ids = fields.One2many(
"product.template.id_category", "product_tmpl_id"
)
@api.constrains("product_tmpl_category_ids")
def _check_product_tmpl_category_ids(self):
ProductTemplateIdCategory = self.env["product.template.id_category"]
for product_templ in self:
category_ids = ProductTemplateIdCategory.sudo()._read_group(
[
("product_tmpl_id", "=", product_templ.id),
],
["category_id"],
["category_id:count"],
order="category_id:count DESC",
)
if category_ids and category_ids[0][1] > 1:
category_ids = list(filter(lambda x: x[1] > 1, category_ids))
raise ValidationError(
_(
"There are repeated categories in the identifications "
"configuration, the quantities are shown below.\n%(categories)s"
)
% {
"categories": "\n".join(
f"{category[0].name}:\u2009{category[1]}"
for category in category_ids
)
}
)

View File

@@ -0,0 +1,20 @@
# Copyright 2025 Binhex <https://www.binhex.cloud>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductTemplateIdcategory(models.Model):
_name = "product.template.id_category"
_description = "Product Template Identification Category"
product_tmpl_id = fields.Many2one("product.template")
category_id = fields.Many2one("res.partner.id_category")
is_mandatory = fields.Boolean(
default=True, help="Defines whether identification is mandatory."
)
message = fields.Text(
help="Allows you to define a description of why "
"this identification is being added.\n"
"Example: Asking the customer for identification"
)

View File

@@ -0,0 +1,109 @@
# Copyright 2025 Binhex <https://www.binhex.cloud>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from datetime import datetime
import pytz
from odoo import _, api, models
from odoo.exceptions import ValidationError
class ResPartnerIdNumber(models.Model):
_inherit = "res.partner.id_number"
@api.model
def message_error_identifications(
self, product_tmpl_ids, diff_identification_ids, required=False
):
"""
Define a message identifications by product
:param product_tmpl_ids: Products that have at least one identification
category defined.
:param diff_identification_ids: Required or optional identifications.
:param required: Defines whether what is being validated is required or not.
:return: A message with the identification categories per product.
"""
message = ""
for product in product_tmpl_ids:
identifications = product.mapped("product_tmpl_category_ids").filtered(
lambda x: x.is_mandatory == required
and x.category_id.id in diff_identification_ids
)
if identifications:
message += _("\n%(product)s\n%(categories)s") % {
"product": product.name,
"categories": "\n".join(
identifications.mapped(
lambda x: f"\u2003\u2022\u2009"
f"{x.category_id.name}\u2009"
f"{f'({x.message})' if x.message else ''}"
)
),
}
return message
def _identification_domain(self, **params):
"""
Build a domain to filter valid identifications
:param params: Dictionary of expected parameters
:return: list: List of tuples representing the search domain
"""
user_tz = self.env.user.tz or self.env.context.get("tz")
user_pytz = pytz.timezone(user_tz) if user_tz else pytz.utc
now_dt = datetime.now().astimezone(user_pytz).date()
if not params.get("partner_id", False):
return []
return [
("partner_id", "=", params.get("partner_id")),
"|",
"|",
"|",
"&",
("valid_from", "=", False),
("valid_until", "=", False),
"&",
"&",
("valid_from", "!=", False),
("valid_until", "=", False),
("valid_from", "<=", now_dt),
"&",
"&",
"&",
("valid_from", "!=", False),
("valid_until", "!=", False),
("valid_from", "<=", now_dt),
("valid_until", ">=", now_dt),
"&",
"&",
("valid_from", "=", False),
("valid_until", "!=", False),
("valid_until", ">=", now_dt),
]
@api.model
def validate_identification(self, **params):
"""
Allows you to obtain the difference between 2 recordset IDs
:param params: A dictionary of values where at least
the value 'compare_identification_ids' and 'partner_id'
must be present to validate the identifications
:return: A set of records with the difference between submitted
identifications and those of the partner
"""
identification_ids = params.get("compare_identification_ids", set())
partner_id = params.get("partner_id", False)
if not partner_id:
raise ValidationError(_("A client is required to verify identifications."))
domain = self._identification_domain(**{"partner_id": partner_id})
partner_identification_ids = self.env["res.partner.id_category"]
for id_number in self.env["res.partner.id_number"].search(domain):
id_number.category_id.validate_id_number(id_number)
partner_identification_ids |= id_number.category_id
return (
identification_ids - partner_identification_ids
if identification_ids
else partner_identification_ids
)

View File

@@ -0,0 +1,115 @@
# Copyright 2025 Binhex <https://www.binhex.cloud>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import Command, _, models
from odoo.exceptions import ValidationError
class SaleOrder(models.Model):
_inherit = "sale.order"
def _diff_identification(self, compare_identification_ids):
diff_identification = self.env["res.partner.id_number"].validate_identification(
**{
"compare_identification_ids": compare_identification_ids,
"partner_id": self.partner_id.id,
}
)
return diff_identification
def _message_error_identifications(self, diff_identification=False, required=False):
"""
Display messages created using the message_error_identifications
method in order of sale.
:param diff_identification: Required or optional identifications.
:return: Message by order
"""
message_error = ""
for order in self:
message_error = _("%(order)s%(message)s") % {
"order": f"\n{order.name}" if len(self) > 1 else "",
"message": self.env[
"res.partner.id_number"
].message_error_identifications(
order.mapped("order_line.product_template_id").filtered(
lambda product: product.required_identification
and product.product_tmpl_category_ids
),
diff_identification,
required,
),
}
return message_error
def _action_generate_confirm_identification(self, message):
view = self.env.ref("sale_product_identification.confirm_identification_view")
return {
"name": _("Confirm identification"),
"type": "ir.actions.act_window",
"view_mode": "form",
"res_model": "confirm.identification",
"views": [(view.id, "form")],
"view_id": view.id,
"target": "new",
"context": {
"default_order_ids": [Command.set(self.ids)],
"default_message": message,
},
}
def _get_domain_identifications(self, is_mandatory=False):
return [
("product_tmpl_id", "in", self.order_line.product_template_id.ids),
("product_tmpl_id.required_identification", "=", True),
("is_mandatory", "=", is_mandatory),
]
def _validate_opt_identification(self):
self.ensure_one()
products_opt_identification_ids = (
self.env["product.template.id_category"]
.search(self._get_domain_identifications())
.mapped("category_id")
)
if products_opt_identification_ids:
message = _(
"The following identifications require verification, "
"please validate before continuing:\n %(identifications)s"
) % {
"identifications": self._message_error_identifications(
products_opt_identification_ids.ids
)
}
return self._action_generate_confirm_identification(message)
return True
def _validate_identification(self):
self.ensure_one()
products_identification_ids = (
self.env["product.template.id_category"]
.search(self._get_domain_identifications(True))
.mapped("category_id")
)
if products_identification_ids:
diff_identification = self._diff_identification(products_identification_ids)
if diff_identification:
message = _(
"The following identifications are required for "
"partner, please verify.\n %(identifications)s"
) % {
"identifications": self._message_error_identifications(
diff_identification.ids, True
)
}
raise ValidationError(message)
def action_confirm(self):
for order in self:
order._validate_identification()
if not self.env.context.get("not_verify_optional_identification", False):
res = self[:1]._validate_opt_identification()
if res is not True:
return res
return super().action_confirm()