1+ from typing import NotRequired , TypedDict
2+
13import sentry_sdk
24from django .core .cache import cache
35from django .core .exceptions import ValidationError
79from zeep .exceptions import Error
810
911
10- def cache_vies_data (value ):
11- if isinstance (value , str ):
12- value = VATIN .from_str (value )
13- key = f"VAT-{ value } "
14- data = cache .get (key )
12+ class VatinValidation (TypedDict ):
13+ valid : bool
14+ fault_message : NotRequired [str ]
15+ fault_code : NotRequired [str ]
16+
17+
18+ def cache_vies_data (value : str | VATIN ) -> tuple [VATIN , VatinValidation ]:
19+ result = value if isinstance (value , VATIN ) else VATIN .from_str (value )
20+ key = f"VAT-{ result } "
21+ data : VatinValidation | None = cache .get (key )
1522 if data is None :
23+ # Offline validation
1624 try :
17- value .verify_country_code ()
18- value .verify_regex ()
25+ result .verify_country_code ()
26+ result .verify_regex ()
1927 except ValidationError :
20- return value
28+ return result , {"valid" : False }
29+
30+ # Online validation
2131 try :
22- data = {}
23- for item in value .data :
24- data [item ] = value .data [item ]
25- cache .set (key , data , 3600 * 24 * 7 )
32+ vies_data = result .data
2633 except Error as error :
2734 data = {
2835 "valid" : False ,
2936 "fault_code" : getattr (error , "code" , "other:Error" ),
3037 "fault_message" : str (error ),
3138 }
3239 sentry_sdk .capture_exception ()
33- value .__dict__ ["vies_data" ] = data
40+ else :
41+ data = {
42+ "valid" : vies_data .valid ,
43+ "fault_code" : vies_data .get ("fault_code" , "" ),
44+ "fault_message" : vies_data .get ("fault_message" , "" ),
45+ }
46+ cache .set (key , data , 3600 * 24 * 7 )
3447
35- return value
48+ return result , data
3649
3750
38- def validate_vatin (value ) -> None :
39- value = cache_vies_data (value )
51+ def validate_vatin (value : str | VATIN ) -> None :
52+ vatin , vies_data = cache_vies_data (value )
4053 try :
41- value .verify_country_code ()
54+ vatin .verify_country_code ()
4255 except ValidationError as error :
4356 msg = _ ("{} is not a valid country code for any European Union member." )
44- raise ValidationError (msg .format (value .country_code )) from error
57+ raise ValidationError (msg .format (vatin .country_code )) from error
4558 try :
46- value .verify_regex ()
59+ vatin .verify_regex ()
4760 except ValidationError as error :
4861 msg = _ ("{} does not match the country's VAT ID specifications." )
49- raise ValidationError (msg .format (value )) from error
62+ raise ValidationError (msg .format (vatin )) from error
5063
51- if not value . vies_data ["valid" ]:
64+ if not vies_data ["valid" ]:
5265 retry_errors = {"MS_UNAVAILABLE" , "MS_MAX_CONCURRENT_REQ" , "TIMEOUT" }
5366 retry_codes = {"soap:Server" , "other:Error" , "env:Server" }
5467 if (
55- value . vies_data .get ("fault_message" ) in retry_errors
56- or value . vies_data .get ("fault_code" ) in retry_codes
68+ vies_data .get ("fault_message" ) in retry_errors
69+ or vies_data .get ("fault_code" ) in retry_codes
5770 ):
5871 msg = format_html (
5972 '{} <a href="{}" target="_blank">{}</a>' ,
@@ -64,5 +77,5 @@ def validate_vatin(value) -> None:
6477 _ ("View service status." ),
6578 )
6679 else :
67- msg = _ ("{} is not a valid VAT ID." ).format (value )
80+ msg = _ ("{} is not a valid VAT ID." ).format (vatin )
6881 raise ValidationError (msg )
0 commit comments