""" Common validators for CRM models. """ import datetime import json import re import uuid as uuid_module from decimal import Decimal from zoneinfo import available_timezones from django.core.exceptions import ValidationError as DjangoValidationError from django.core.validators import RegexValidator from django.utils import timezone from django.utils.dateparse import parse_date, parse_datetime from django.utils.translation import gettext_lazy as _ from rest_framework.exceptions import ValidationError as DRFValidationError # E.164 format: +[country code][number] (max 15 digits total) # Examples: +12025551234, +442071234567 e164_phone_validator = RegexValidator( regex=r"^\+[1-9]\d{1,14}$", message=_("Phone must be in E.164 format (e.g., +12025551234)"), ) # Flexible phone format allowing common separators # Allows: digits, spaces, dashes, parentheses, plus sign # Examples: +1 (202) 555-1234, 202-555-1234, +44 20 7123 4567 flexible_phone_validator = RegexValidator( regex=r"^[\d\s\-\(\)\+\.]{7,25}$", message=_( "Enter a valid phone number (7-25 characters, digits and separators only)" ), ) def validate_uuid(value, field: str) -> str: """Return ``value`` if it is a usable id, otherwise raise a DRF 400. Id-valued filters (``tags``, ``assigned_to``, ``account``, ...) arrive as raw query-string text and go straight into an ``id`` lookup. Django parses them with ``uuid.UUID`` while building the query and raises ``django.core.exceptions.ValidationError`` on anything malformed. DRF's exception handler does not translate that class, so the request answered 500 rather than 400: a stale bookmark or a hand-edited URL took the endpoint down. Parsing here with ``uuid.UUID`` deliberately mirrors what ``UUIDField`` does, so this accepts exactly what the ORM accepts (hyphenated, bare hex, braced and URN forms alike) and cannot drift into rejecting requests that used to work. """ try: uuid_module.UUID(str(value)) except (AttributeError, TypeError, ValueError): raise DRFValidationError({field: [f"'{value}' is not a valid id."]}) from None return value def validate_uuid_list(value, field: str) -> list: """Normalise ``value`` to a list of ids, raising a DRF 400 on any bad one. Accepts a list, a single bare string (a body may send one id unwrapped), or nothing. Empty entries are dropped rather than rejected: a ``