Files
encoach_backend_new_v2/odoo/addons/phone_validation/models/models.py
Yamen Ahmad 3e83d8d7d5 feat: add complete EnCoach backend — Odoo 19 + all addons
Includes:
- Odoo 19 framework (odoo/)
- 27 custom EnCoach addons (new_project/custom_addons/)
  - encoach_core, encoach_api, encoach_lms_api, encoach_adaptive_api
  - encoach_exam, encoach_taxonomy, encoach_adaptive, encoach_assignment
  - encoach_ai, encoach_ai_grading, encoach_ai_generation, encoach_ai_media
  - encoach_courseware, encoach_communication, encoach_subscription
  - encoach_notification, encoach_approval, encoach_branding
  - encoach_classroom, encoach_registration, encoach_stats
  - encoach_faq, encoach_ticket, encoach_training, encoach_resources
  - encoach_adaptive_ai, encoach_sis
- 21 OpenEduCat Enterprise modules (new_project/enterprise-19/)
- 14 OpenEduCat Community modules (new_project/openeducat_erp-19.0/)
- Configuration: odoo.conf, requirements.txt, scripts
- 200+ REST API endpoints with JWT authentication
- SRS and test documentation

Made-with: Cursor
2026-04-01 17:10:04 +04:00

117 lines
4.8 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, exceptions, models
from odoo.addons.phone_validation.tools import phone_validation
class Base(models.AbstractModel):
_inherit = 'base'
# ------------------------------------------------------------
# FIELDS HELPERS
# ------------------------------------------------------------
@api.model
def _phone_get_number_fields(self):
""" This method returns the fields to use to find the number to use to
send an SMS on a record. """
return [
number_fname for number_fname in ('mobile', 'phone') if number_fname in self
]
def _phone_get_country(self):
"""Get a country likely to match the phone of the record.
By default we get it from:
- The country field of the target record (self) based on
:meth:`_phone_get_country_field`;
- The country of any mail partner (e.g. ``self.partner_ids[2].phone``),
considering we are going to contact the customer(s) of the record.
Done using generic :meth:`_mail_get_partner_fields` method allowing
to find record customers;
"""
country_by_record = {}
record_country_fname = self._phone_get_country_field()
for record in self:
if record_country_fname and (record_country := record[record_country_fname]):
country_by_record[record.id] = record_country
continue
for partner_field in self.env[self._name]._mail_get_partner_fields():
partner_records = record[partner_field]
if countries := partner_records.country_id:
country_by_record[record.id] = countries[0]
return country_by_record
@api.model
def _phone_get_country_field(self):
if 'country_id' in self:
return 'country_id'
return False
def _phone_format(self, fname=False, number=False, country=False, force_format='E164', raise_exception=False):
""" Format and return number. This number can be found using a field
(in which case self should be a singleton recordet), or directly given
if the formatting itself is what matter. Field name can be found
automatically using :meth:`_phone_get_number_fields`.
:param str fname: if number is not given, fname indicates the field to
use to find the number; otherwise use :meth:`_phone_get_number_fields`.;
:param str number: number to format (in which case fields-based computation
is skipped);
:param <res.country> country: country used for formatting number; otherwise
it is fetched based on record, using :meth:`_phone_get_number_fields`.;
:param str force_format: stringified version of format globals; should be
one of ``'E164'``, ``'INTERNATIONAL'``, ``'NATIONAL'`` or ``'RFC3966'``;
:param bool raise_exception: raise if formatting is not possible (notably
wrong formatting, invalid country information, ...). Otherwise ``False``
is returned;
:return: formatted number. If formatting is not possible ``False`` is
returned.
:rtype: str | Literal[False]
"""
if not number:
# if no number is given, having a singletong recordset is mandatory to
# always have a number as input
self.ensure_one()
fnames = self._phone_get_number_fields() if not fname else [fname]
number = next((self[fname] for fname in fnames if fname in self and self[fname]), False)
if not number:
return False
# fetch country info only if self is a singleton recordset allowing to
# effectively try to find a country
if not country and self:
self.ensure_one()
country = self._phone_get_country().get(self.id)
if not country:
country = self.env.company.country_id
return self._phone_format_number(
number,
country=country, force_format=force_format,
raise_exception=raise_exception,
)
def _phone_format_number(self, number, country, force_format='E164', raise_exception=False):
""" Format and return number according to the asked format. This is
mainly a small helper around :func:`phone_validation.phone_format`."""
if not number:
return False
try:
number = phone_validation.phone_format(
number,
country.code,
country.phone_code,
force_format=force_format,
raise_exception=True, # do not get original number returned
)
except exceptions.UserError:
if raise_exception:
raise
number = False
return number