|
3 | 3 |
|
4 | 4 | from pydantic import GetCoreSchemaHandler |
5 | 5 | from pydantic.json_schema import JsonSchemaValue |
6 | | -from pydantic_core import core_schema |
| 6 | +from pydantic_core import PydanticCustomError, core_schema |
7 | 7 |
|
8 | 8 | # An OIN is always 20 characters: |
9 | 9 | # prefix (8 digits) + mainnumber (8 or 9 alphanumeric) + suffix (4 or 3 zeros) |
10 | 10 | OIN_PATTERN = re.compile(r"^\d{8}(?:[A-Za-z0-9]{8}0{4}|[A-Za-z0-9]{9}0{3})$") |
| 11 | +RECIPIENT_ORGANIZATION_PREFIX = "oin:" |
| 12 | +_RECIPIENT_ORGANIZATION_SUFFIX_PATTERN = ( |
| 13 | + r"\d{8}(?:[A-Za-z0-9]{8}0{4}|[A-Za-z0-9]{9}0{3})" |
| 14 | +) |
| 15 | +RECIPIENT_ORGANIZATION_PATTERN = re.compile( |
| 16 | + rf"^{RECIPIENT_ORGANIZATION_PREFIX}{_RECIPIENT_ORGANIZATION_SUFFIX_PATTERN}$" |
| 17 | +) |
11 | 18 |
|
12 | 19 |
|
13 | 20 | class Oin: |
@@ -92,6 +99,8 @@ def __repr__(self) -> str: |
92 | 99 | def __eq__(self, other: Any) -> bool: |
93 | 100 | if isinstance(other, Oin): |
94 | 101 | return self.value == other.value |
| 102 | + if isinstance(other, str): |
| 103 | + return self.value == other |
95 | 104 | return False |
96 | 105 |
|
97 | 106 | def __hash__(self) -> int: |
@@ -128,3 +137,75 @@ def __get_pydantic_json_schema__( |
128 | 137 | "8-digit prefix + 8/9-character alphanumeric mainnumber + 4/3 trailing zeros (suffix)." |
129 | 138 | ), |
130 | 139 | } |
| 140 | + |
| 141 | + |
| 142 | +class RecipientOrganizationOin(Oin): |
| 143 | + """An OIN supplied with recipient organization prefix, e.g. ``oin:<oin>``. |
| 144 | +
|
| 145 | + Internally the stored value remains a plain OIN without the ``oin:`` prefix. |
| 146 | + """ |
| 147 | + |
| 148 | + PREFIX = RECIPIENT_ORGANIZATION_PREFIX |
| 149 | + _INVALID_ERROR = "Invalid recipient organization. Format: oin:<oin_number>" |
| 150 | + |
| 151 | + def __init__(self, value: Any) -> None: |
| 152 | + if isinstance(value, str) and value.startswith(self.PREFIX): |
| 153 | + value = value[len(self.PREFIX) :] |
| 154 | + super().__init__(value) |
| 155 | + |
| 156 | + @classmethod |
| 157 | + def _pydantic_validate(cls, value: Any) -> "RecipientOrganizationOin": |
| 158 | + if isinstance(value, cls): |
| 159 | + return value |
| 160 | + |
| 161 | + if isinstance(value, Oin): |
| 162 | + return cls(value.value) |
| 163 | + |
| 164 | + if not isinstance(value, str): |
| 165 | + raise PydanticCustomError( |
| 166 | + "invalid_recipient_organization", cls._INVALID_ERROR |
| 167 | + ) |
| 168 | + |
| 169 | + if not value.startswith(cls.PREFIX): |
| 170 | + raise PydanticCustomError( |
| 171 | + "invalid_recipient_organization", cls._INVALID_ERROR |
| 172 | + ) |
| 173 | + |
| 174 | + try: |
| 175 | + return cls(value[len(cls.PREFIX) :]) |
| 176 | + except ValueError as e: |
| 177 | + raise PydanticCustomError( |
| 178 | + "invalid_recipient_organization", "{error}", {"error": str(e)} |
| 179 | + ) from e |
| 180 | + |
| 181 | + @classmethod |
| 182 | + def __get_pydantic_core_schema__( |
| 183 | + cls, _source_type: Any, _handler: GetCoreSchemaHandler |
| 184 | + ) -> core_schema.CoreSchema: |
| 185 | + return core_schema.no_info_plain_validator_function( |
| 186 | + cls._pydantic_validate, |
| 187 | + serialization=core_schema.to_string_ser_schema(), |
| 188 | + ) |
| 189 | + |
| 190 | + @classmethod |
| 191 | + def __get_pydantic_json_schema__( |
| 192 | + cls, _schema: core_schema.CoreSchema, _handler: Any |
| 193 | + ) -> JsonSchemaValue: |
| 194 | + return { |
| 195 | + "type": "string", |
| 196 | + "pattern": RECIPIENT_ORGANIZATION_PATTERN.pattern, |
| 197 | + "examples": [f"{RECIPIENT_ORGANIZATION_PREFIX}00000099000000001000"], |
| 198 | + "description": ( |
| 199 | + "Recipient organization string: prefix followed by an OIN (20-char " |
| 200 | + "identifier)." |
| 201 | + ), |
| 202 | + } |
| 203 | + |
| 204 | + def __str__(self) -> str: |
| 205 | + """Return the recipient organization OIN in external format, including ``oin:``.""" |
| 206 | + return f"{self.PREFIX}{self.value}" |
| 207 | + |
| 208 | + @property |
| 209 | + def value(self) -> str: |
| 210 | + """Return the normalized OIN value without the ``oin:`` prefix.""" |
| 211 | + return super().value |
0 commit comments