factpulse 2.0.37__py3-none-any.whl → 3.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of factpulse might be problematic. Click here for more details.
- factpulse/__init__.py +265 -197
- factpulse/api/__init__.py +5 -4
- factpulse/api/afnorpdppa_api.py +34 -34
- factpulse/api/afnorpdppa_directory_service_api.py +59 -59
- factpulse/api/afnorpdppa_flow_service_api.py +23 -23
- factpulse/api/chorus_pro_api.py +211 -211
- factpulse/api/document_conversion_api.py +1506 -0
- factpulse/api/{sant_api.py → health_api.py} +22 -22
- factpulse/api/invoice_processing_api.py +3437 -0
- factpulse/api/{vrification_pdfxml_api.py → pdfxml_verification_api.py} +240 -240
- factpulse/api/{utilisateur_api.py → user_api.py} +17 -17
- factpulse/api_client.py +3 -3
- factpulse/configuration.py +3 -3
- factpulse/exceptions.py +2 -2
- factpulse/models/__init__.py +128 -95
- factpulse/models/acknowledgment_status.py +38 -0
- factpulse/models/additional_document.py +115 -0
- factpulse/models/afnor_credentials.py +106 -0
- factpulse/models/afnor_destination.py +127 -0
- factpulse/models/afnor_health_check_response.py +91 -0
- factpulse/models/afnor_result.py +105 -0
- factpulse/models/allowance_charge.py +147 -0
- factpulse/models/allowance_reason_code.py +42 -0
- factpulse/models/allowance_total_amount.py +145 -0
- factpulse/models/amount.py +139 -0
- factpulse/models/amount_due.py +139 -0
- factpulse/models/api_error.py +5 -5
- factpulse/models/async_task_status.py +97 -0
- factpulse/models/base_amount.py +145 -0
- factpulse/models/bounding_box_schema.py +10 -10
- factpulse/models/celery_status.py +40 -0
- factpulse/models/certificate_info_response.py +24 -24
- factpulse/models/charge_total_amount.py +145 -0
- factpulse/models/chorus_pro_destination.py +108 -0
- factpulse/models/chorus_pro_result.py +101 -0
- factpulse/models/contact.py +113 -0
- factpulse/models/convert_error_response.py +105 -0
- factpulse/models/convert_pending_input_response.py +114 -0
- factpulse/models/convert_resume_request.py +87 -0
- factpulse/models/convert_success_response.py +126 -0
- factpulse/models/convert_validation_failed_response.py +120 -0
- factpulse/models/delivery_party.py +121 -0
- factpulse/models/destination.py +27 -27
- factpulse/models/document_type_info.py +91 -0
- factpulse/models/electronic_address.py +90 -0
- factpulse/models/enriched_invoice_info.py +133 -0
- factpulse/models/error_level.py +2 -2
- factpulse/models/error_source.py +2 -2
- factpulse/models/extraction_info.py +93 -0
- factpulse/models/factur_x_invoice.py +320 -0
- factpulse/models/factur_x_profile.py +39 -0
- factpulse/models/factur_xpdf_info.py +91 -0
- factpulse/models/facture_electronique_rest_api_schemas_chorus_pro_chorus_pro_credentials.py +95 -0
- factpulse/models/facture_electronique_rest_api_schemas_processing_chorus_pro_credentials.py +115 -0
- factpulse/models/field_status.py +40 -0
- factpulse/models/file_info.py +94 -0
- factpulse/models/files_info.py +106 -0
- factpulse/models/flow_direction.py +37 -0
- factpulse/models/flow_profile.py +38 -0
- factpulse/models/flow_summary.py +131 -0
- factpulse/models/flow_syntax.py +40 -0
- factpulse/models/flow_type.py +40 -0
- factpulse/models/generate_certificate_request.py +26 -26
- factpulse/models/generate_certificate_response.py +15 -15
- factpulse/models/get_chorus_pro_id_request.py +100 -0
- factpulse/models/get_chorus_pro_id_response.py +98 -0
- factpulse/models/get_invoice_request.py +98 -0
- factpulse/models/get_invoice_response.py +142 -0
- factpulse/models/get_structure_request.py +100 -0
- factpulse/models/get_structure_response.py +142 -0
- factpulse/models/global_allowance_amount.py +139 -0
- factpulse/models/gross_unit_price.py +145 -0
- factpulse/models/http_validation_error.py +2 -2
- factpulse/models/incoming_invoice.py +196 -0
- factpulse/models/incoming_supplier.py +144 -0
- factpulse/models/invoice_format.py +38 -0
- factpulse/models/invoice_line.py +354 -0
- factpulse/models/invoice_line_allowance_amount.py +145 -0
- factpulse/models/invoice_note.py +94 -0
- factpulse/models/invoice_references.py +194 -0
- factpulse/models/invoice_status.py +96 -0
- factpulse/models/invoice_totals.py +177 -0
- factpulse/models/invoice_totals_prepayment.py +145 -0
- factpulse/models/invoice_type_code.py +51 -0
- factpulse/models/invoicing_framework.py +110 -0
- factpulse/models/invoicing_framework_code.py +39 -0
- factpulse/models/line_net_amount.py +145 -0
- factpulse/models/line_total_amount.py +145 -0
- factpulse/models/mandatory_note_schema.py +124 -0
- factpulse/models/manual_rate.py +139 -0
- factpulse/models/manual_vat_rate.py +139 -0
- factpulse/models/missing_field.py +107 -0
- factpulse/models/operation_nature.py +49 -0
- factpulse/models/output_format.py +37 -0
- factpulse/models/page_dimensions_schema.py +89 -0
- factpulse/models/payee.py +168 -0
- factpulse/models/payment_card.py +99 -0
- factpulse/models/payment_means.py +41 -0
- factpulse/models/pdf_validation_result_api.py +169 -0
- factpulse/models/pdp_credentials.py +15 -15
- factpulse/models/percentage.py +145 -0
- factpulse/models/postal_address.py +134 -0
- factpulse/models/price_allowance_amount.py +145 -0
- factpulse/models/price_basis_quantity.py +145 -0
- factpulse/models/processing_options.py +94 -0
- factpulse/models/product_characteristic.py +89 -0
- factpulse/models/product_classification.py +101 -0
- factpulse/models/quantity.py +139 -0
- factpulse/models/recipient.py +167 -0
- factpulse/models/rounding_amount.py +145 -0
- factpulse/models/scheme_id.py +10 -4
- factpulse/models/search_flow_request.py +143 -0
- factpulse/models/search_flow_response.py +101 -0
- factpulse/models/search_services_response.py +101 -0
- factpulse/models/search_structure_request.py +119 -0
- factpulse/models/search_structure_response.py +101 -0
- factpulse/models/signature_info.py +6 -6
- factpulse/models/signature_info_api.py +122 -0
- factpulse/models/signature_parameters.py +133 -0
- factpulse/models/simplified_invoice_data.py +124 -0
- factpulse/models/structure_info.py +14 -14
- factpulse/models/structure_parameters.py +91 -0
- factpulse/models/structure_service.py +93 -0
- factpulse/models/submission_mode.py +38 -0
- factpulse/models/submit_complete_invoice_request.py +116 -0
- factpulse/models/submit_complete_invoice_response.py +145 -0
- factpulse/models/submit_flow_request.py +123 -0
- factpulse/models/submit_flow_response.py +109 -0
- factpulse/models/submit_gross_amount.py +139 -0
- factpulse/models/submit_invoice_request.py +176 -0
- factpulse/models/submit_invoice_response.py +103 -0
- factpulse/models/submit_net_amount.py +139 -0
- factpulse/models/submit_vat_amount.py +139 -0
- factpulse/models/supplementary_attachment.py +95 -0
- factpulse/models/supplier.py +225 -0
- factpulse/models/task_response.py +87 -0
- factpulse/models/tax_representative.py +95 -0
- factpulse/models/taxable_amount.py +139 -0
- factpulse/models/total_gross_amount.py +139 -0
- factpulse/models/total_net_amount.py +139 -0
- factpulse/models/total_vat_amount.py +139 -0
- factpulse/models/unit_net_price.py +139 -0
- factpulse/models/unit_of_measure.py +41 -0
- factpulse/models/validation_error.py +2 -2
- factpulse/models/validation_error_detail.py +6 -6
- factpulse/models/validation_error_loc_inner.py +2 -2
- factpulse/models/validation_error_response.py +87 -0
- factpulse/models/validation_info.py +105 -0
- factpulse/models/validation_success_response.py +87 -0
- factpulse/models/vat_accounting_code.py +39 -0
- factpulse/models/vat_amount.py +139 -0
- factpulse/models/vat_category.py +44 -0
- factpulse/models/vat_line.py +140 -0
- factpulse/models/vat_point_date_code.py +38 -0
- factpulse/models/vat_rate.py +145 -0
- factpulse/models/verification_success_response.py +135 -0
- factpulse/models/verified_field_schema.py +129 -0
- factpulse/rest.py +2 -2
- factpulse-3.0.7.dist-info/METADATA +292 -0
- factpulse-3.0.7.dist-info/RECORD +168 -0
- factpulse_helpers/__init__.py +34 -34
- factpulse_helpers/client.py +1019 -795
- factpulse_helpers/exceptions.py +68 -68
- factpulse/api/traitement_facture_api.py +0 -3437
- factpulse/models/adresse_electronique.py +0 -90
- factpulse/models/adresse_postale.py +0 -120
- factpulse/models/cadre_de_facturation.py +0 -110
- factpulse/models/categorie_tva.py +0 -44
- factpulse/models/champ_verifie_schema.py +0 -129
- factpulse/models/chorus_pro_credentials.py +0 -95
- factpulse/models/code_cadre_facturation.py +0 -39
- factpulse/models/code_raison_reduction.py +0 -42
- factpulse/models/consulter_facture_request.py +0 -98
- factpulse/models/consulter_facture_response.py +0 -142
- factpulse/models/consulter_structure_request.py +0 -100
- factpulse/models/consulter_structure_response.py +0 -142
- factpulse/models/credentials_afnor.py +0 -106
- factpulse/models/credentials_chorus_pro.py +0 -115
- factpulse/models/destinataire.py +0 -130
- factpulse/models/destination_afnor.py +0 -127
- factpulse/models/destination_chorus_pro.py +0 -108
- factpulse/models/dimension_page_schema.py +0 -89
- factpulse/models/direction_flux.py +0 -37
- factpulse/models/donnees_facture_simplifiees.py +0 -124
- factpulse/models/facture_enrichie_info.py +0 -133
- factpulse/models/facture_entrante.py +0 -196
- factpulse/models/facture_factur_x.py +0 -183
- factpulse/models/flux_resume.py +0 -131
- factpulse/models/format_facture.py +0 -38
- factpulse/models/format_sortie.py +0 -37
- factpulse/models/fournisseur.py +0 -153
- factpulse/models/fournisseur_entrant.py +0 -144
- factpulse/models/information_signature_api.py +0 -122
- factpulse/models/ligne_de_poste.py +0 -183
- factpulse/models/ligne_de_poste_montant_remise_ht.py +0 -145
- factpulse/models/ligne_de_poste_taux_tva_manuel.py +0 -145
- factpulse/models/ligne_de_tva.py +0 -132
- factpulse/models/mode_depot.py +0 -38
- factpulse/models/mode_paiement.py +0 -41
- factpulse/models/montant_a_payer.py +0 -139
- factpulse/models/montant_base_ht.py +0 -139
- factpulse/models/montant_ht_total.py +0 -139
- factpulse/models/montant_remise_globale_ttc.py +0 -139
- factpulse/models/montant_total.py +0 -133
- factpulse/models/montant_total_acompte.py +0 -145
- factpulse/models/montant_total_ligne_ht.py +0 -139
- factpulse/models/montant_ttc_total.py +0 -139
- factpulse/models/montant_tva.py +0 -139
- factpulse/models/montant_tva_ligne.py +0 -139
- factpulse/models/montant_tva_total.py +0 -139
- factpulse/models/montant_unitaire_ht.py +0 -139
- factpulse/models/nature_operation.py +0 -49
- factpulse/models/note.py +0 -94
- factpulse/models/note_obligatoire_schema.py +0 -124
- factpulse/models/obtenir_id_chorus_pro_request.py +0 -100
- factpulse/models/obtenir_id_chorus_pro_response.py +0 -98
- factpulse/models/options_processing.py +0 -94
- factpulse/models/parametres_signature.py +0 -133
- factpulse/models/parametres_structure.py +0 -91
- factpulse/models/pdf_factur_x_info.py +0 -91
- factpulse/models/piece_jointe_complementaire.py +0 -95
- factpulse/models/profil_api.py +0 -39
- factpulse/models/profil_flux.py +0 -38
- factpulse/models/quantite.py +0 -139
- factpulse/models/rechercher_services_response.py +0 -101
- factpulse/models/rechercher_structure_request.py +0 -119
- factpulse/models/rechercher_structure_response.py +0 -101
- factpulse/models/references.py +0 -124
- factpulse/models/reponse_healthcheck_afnor.py +0 -91
- factpulse/models/reponse_recherche_flux.py +0 -101
- factpulse/models/reponse_soumission_flux.py +0 -109
- factpulse/models/reponse_tache.py +0 -87
- factpulse/models/reponse_validation_erreur.py +0 -87
- factpulse/models/reponse_validation_succes.py +0 -87
- factpulse/models/reponse_verification_succes.py +0 -135
- factpulse/models/requete_recherche_flux.py +0 -143
- factpulse/models/requete_soumission_flux.py +0 -123
- factpulse/models/resultat_afnor.py +0 -105
- factpulse/models/resultat_chorus_pro.py +0 -101
- factpulse/models/resultat_validation_pdfapi.py +0 -169
- factpulse/models/service_structure.py +0 -93
- factpulse/models/soumettre_facture_complete_request.py +0 -116
- factpulse/models/soumettre_facture_complete_response.py +0 -145
- factpulse/models/soumettre_facture_request.py +0 -176
- factpulse/models/soumettre_facture_response.py +0 -103
- factpulse/models/statut_acquittement.py +0 -38
- factpulse/models/statut_celery.py +0 -40
- factpulse/models/statut_champ_api.py +0 -40
- factpulse/models/statut_facture.py +0 -96
- factpulse/models/statut_tache.py +0 -97
- factpulse/models/syntaxe_flux.py +0 -40
- factpulse/models/tauxmanuel.py +0 -139
- factpulse/models/type_document.py +0 -40
- factpulse/models/type_facture.py +0 -37
- factpulse/models/type_flux.py +0 -40
- factpulse/models/type_tva.py +0 -39
- factpulse/models/unite.py +0 -41
- factpulse-2.0.37.dist-info/METADATA +0 -292
- factpulse-2.0.37.dist-info/RECORD +0 -134
- {factpulse-2.0.37.dist-info → factpulse-3.0.7.dist-info}/WHEEL +0 -0
- {factpulse-2.0.37.dist-info → factpulse-3.0.7.dist-info}/licenses/LICENSE +0 -0
- {factpulse-2.0.37.dist-info → factpulse-3.0.7.dist-info}/top_level.txt +0 -0
factpulse_helpers/exceptions.py
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Custom exceptions for the FactPulse client.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
This module defines an exception hierarchy aligned with the FactPulse API error format
|
|
4
|
+
(APIError, ValidationErrorDetail) compliant with AFNOR XP Z12-013 standard.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Exception hierarchy:
|
|
7
7
|
- FactPulseError (base)
|
|
8
8
|
├── FactPulseAuthError (401)
|
|
9
|
-
├── FactPulseValidationError (400, 422) -
|
|
10
|
-
├── FactPulsePollingTimeout (timeout
|
|
9
|
+
├── FactPulseValidationError (400, 422) - with structured details
|
|
10
|
+
├── FactPulsePollingTimeout (polling timeout)
|
|
11
11
|
├── FactPulseNotFoundError (404)
|
|
12
12
|
├── FactPulseServiceUnavailableError (503)
|
|
13
|
-
└── FactPulseAPIError (
|
|
13
|
+
└── FactPulseAPIError (generic with error_code)
|
|
14
14
|
"""
|
|
15
15
|
from dataclasses import dataclass
|
|
16
16
|
from typing import Any, Dict, List, Optional
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class FactPulseError(Exception):
|
|
20
|
-
"""
|
|
20
|
+
"""Base class for all FactPulse errors."""
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class FactPulseAuthError(FactPulseError):
|
|
25
|
-
"""
|
|
25
|
+
"""FactPulse authentication error (401).
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
- client_uid
|
|
27
|
+
Raised when:
|
|
28
|
+
- Invalid email/password
|
|
29
|
+
- Expired or invalid JWT token
|
|
30
|
+
- client_uid not found
|
|
31
31
|
"""
|
|
32
|
-
def __init__(self, message: str = "
|
|
32
|
+
def __init__(self, message: str = "Authentication required"):
|
|
33
33
|
self.message = message
|
|
34
34
|
super().__init__(message)
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class FactPulsePollingTimeout(FactPulseError):
|
|
38
|
-
"""Timeout
|
|
38
|
+
"""Timeout while polling an asynchronous task."""
|
|
39
39
|
def __init__(self, task_id: str, timeout: int):
|
|
40
40
|
self.task_id = task_id
|
|
41
41
|
self.timeout = timeout
|
|
42
|
-
super().__init__(f"Timeout ({timeout}ms)
|
|
42
|
+
super().__init__(f"Timeout ({timeout}ms) reached for task {task_id}")
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
@dataclass
|
|
46
46
|
class ValidationErrorDetail:
|
|
47
|
-
"""
|
|
47
|
+
"""Validation error detail in AFNOR format.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Aligned with the AcknowledgementDetail schema from AFNOR XP Z12-013 standard.
|
|
50
50
|
|
|
51
51
|
Attributes:
|
|
52
|
-
level:
|
|
53
|
-
item:
|
|
54
|
-
reason:
|
|
55
|
-
source:
|
|
56
|
-
code:
|
|
52
|
+
level: Severity level ('Error' or 'Warning')
|
|
53
|
+
item: Identifier of the affected element (BR-FR rule, field, XPath)
|
|
54
|
+
reason: Error description
|
|
55
|
+
source: Error source (schematron, pydantic, pdfa, afnor, chorus_pro)
|
|
56
|
+
code: Unique error code (e.g., SCHEMATRON_BR_FR_01)
|
|
57
57
|
"""
|
|
58
58
|
level: str = ""
|
|
59
59
|
item: str = ""
|
|
@@ -69,13 +69,13 @@ class ValidationErrorDetail:
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
class FactPulseValidationError(FactPulseError):
|
|
72
|
-
"""
|
|
72
|
+
"""Validation error with structured details (400, 422).
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
Contains a list of ValidationErrorDetail for diagnostics.
|
|
75
75
|
|
|
76
76
|
Attributes:
|
|
77
|
-
errors:
|
|
78
|
-
error_code:
|
|
77
|
+
errors: List of detailed errors
|
|
78
|
+
error_code: API error code (e.g., VALIDATION_FAILED, SCHEMATRON_VALIDATION_FAILED)
|
|
79
79
|
"""
|
|
80
80
|
def __init__(
|
|
81
81
|
self,
|
|
@@ -87,52 +87,52 @@ class FactPulseValidationError(FactPulseError):
|
|
|
87
87
|
self.error_code = error_code
|
|
88
88
|
if self.errors:
|
|
89
89
|
details = "\n".join(f" - {e}" for e in self.errors)
|
|
90
|
-
message = f"{message}\n\
|
|
90
|
+
message = f"{message}\n\nDetails:\n{details}"
|
|
91
91
|
super().__init__(message)
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
class FactPulseNotFoundError(FactPulseError):
|
|
95
|
-
"""
|
|
95
|
+
"""Resource not found (404).
|
|
96
96
|
|
|
97
97
|
Attributes:
|
|
98
|
-
resource:
|
|
99
|
-
identifier:
|
|
98
|
+
resource: Resource type (invoice, structure, flow, client)
|
|
99
|
+
identifier: Resource identifier
|
|
100
100
|
"""
|
|
101
101
|
def __init__(self, resource: str, identifier: str = ""):
|
|
102
102
|
self.resource = resource
|
|
103
103
|
self.identifier = identifier
|
|
104
|
-
message = f"{resource.capitalize()}
|
|
104
|
+
message = f"{resource.capitalize()} not found"
|
|
105
105
|
if identifier:
|
|
106
|
-
message = f"{resource.capitalize()} '{identifier}'
|
|
106
|
+
message = f"{resource.capitalize()} '{identifier}' not found"
|
|
107
107
|
super().__init__(message)
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
class FactPulseServiceUnavailableError(FactPulseError):
|
|
111
|
-
"""
|
|
111
|
+
"""External service unavailable (503).
|
|
112
112
|
|
|
113
113
|
Attributes:
|
|
114
|
-
service_name:
|
|
115
|
-
original_error:
|
|
114
|
+
service_name: Service name (AFNOR PDP, Chorus Pro, Django)
|
|
115
|
+
original_error: Original exception (optional)
|
|
116
116
|
"""
|
|
117
117
|
def __init__(self, service_name: str, original_error: Optional[Exception] = None):
|
|
118
118
|
self.service_name = service_name
|
|
119
119
|
self.original_error = original_error
|
|
120
|
-
message = f"
|
|
120
|
+
message = f"Service {service_name} is unavailable"
|
|
121
121
|
if original_error:
|
|
122
122
|
message = f"{message}: {str(original_error)}"
|
|
123
123
|
super().__init__(message)
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
class FactPulseAPIError(FactPulseError):
|
|
127
|
-
"""
|
|
127
|
+
"""Generic API error with structured error code.
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
Used for errors not covered by specific exceptions.
|
|
130
130
|
|
|
131
131
|
Attributes:
|
|
132
|
-
status_code:
|
|
133
|
-
error_code:
|
|
134
|
-
error_message:
|
|
135
|
-
details:
|
|
132
|
+
status_code: HTTP response status code
|
|
133
|
+
error_code: API error code (e.g., INTERNAL_ERROR)
|
|
134
|
+
error_message: API error message
|
|
135
|
+
details: Optional details (ValidationErrorDetail)
|
|
136
136
|
"""
|
|
137
137
|
def __init__(
|
|
138
138
|
self,
|
|
@@ -149,18 +149,18 @@ class FactPulseAPIError(FactPulseError):
|
|
|
149
149
|
|
|
150
150
|
|
|
151
151
|
def parse_api_error(response_json: Dict[str, Any], status_code: int = 400) -> FactPulseError:
|
|
152
|
-
"""Parse
|
|
152
|
+
"""Parse an API error response and return the appropriate exception.
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
(APIError
|
|
156
|
-
|
|
154
|
+
This function parses the unified FactPulse API error format
|
|
155
|
+
(APIError with errorCode, errorMessage, details) and returns
|
|
156
|
+
the appropriate Python exception.
|
|
157
157
|
|
|
158
158
|
Args:
|
|
159
|
-
response_json:
|
|
160
|
-
status_code:
|
|
159
|
+
response_json: Error response JSON (dict)
|
|
160
|
+
status_code: HTTP response status code
|
|
161
161
|
|
|
162
162
|
Returns:
|
|
163
|
-
|
|
163
|
+
Appropriate exception based on status_code and error_code
|
|
164
164
|
|
|
165
165
|
Example:
|
|
166
166
|
>>> response = requests.post(url, json=data)
|
|
@@ -168,20 +168,20 @@ def parse_api_error(response_json: Dict[str, Any], status_code: int = 400) -> Fa
|
|
|
168
168
|
... error = parse_api_error(response.json(), response.status_code)
|
|
169
169
|
... raise error
|
|
170
170
|
"""
|
|
171
|
-
#
|
|
172
|
-
# Support
|
|
171
|
+
# Extract API error fields
|
|
172
|
+
# Support both formats: camelCase (API) and snake_case
|
|
173
173
|
error_code = response_json.get("errorCode") or response_json.get("error_code") or "UNKNOWN_ERROR"
|
|
174
|
-
error_message = response_json.get("errorMessage") or response_json.get("error_message") or "
|
|
174
|
+
error_message = response_json.get("errorMessage") or response_json.get("error_message") or "Unknown error"
|
|
175
175
|
details_raw = response_json.get("details") or []
|
|
176
176
|
|
|
177
|
-
#
|
|
177
|
+
# Sometimes the error is wrapped in a "detail" field
|
|
178
178
|
if "detail" in response_json and isinstance(response_json["detail"], dict):
|
|
179
179
|
detail = response_json["detail"]
|
|
180
180
|
error_code = detail.get("error") or detail.get("errorCode") or error_code
|
|
181
181
|
error_message = detail.get("message") or detail.get("errorMessage") or error_message
|
|
182
182
|
details_raw = detail.get("details") or details_raw
|
|
183
183
|
|
|
184
|
-
#
|
|
184
|
+
# Parse details into ValidationErrorDetail
|
|
185
185
|
details = []
|
|
186
186
|
for d in details_raw:
|
|
187
187
|
if isinstance(d, dict):
|
|
@@ -193,18 +193,18 @@ def parse_api_error(response_json: Dict[str, Any], status_code: int = 400) -> Fa
|
|
|
193
193
|
code=d.get("code"),
|
|
194
194
|
))
|
|
195
195
|
|
|
196
|
-
#
|
|
196
|
+
# Return appropriate exception based on status_code
|
|
197
197
|
if status_code == 401:
|
|
198
198
|
return FactPulseAuthError(error_message)
|
|
199
199
|
elif status_code == 404:
|
|
200
|
-
#
|
|
201
|
-
resource = "
|
|
200
|
+
# Try to extract resource from message
|
|
201
|
+
resource = "resource"
|
|
202
202
|
if "client" in error_message.lower():
|
|
203
203
|
resource = "client"
|
|
204
204
|
elif "flux" in error_message.lower() or "flow" in error_message.lower():
|
|
205
|
-
resource = "
|
|
206
|
-
elif "facture" in error_message.lower():
|
|
207
|
-
resource = "
|
|
205
|
+
resource = "flow"
|
|
206
|
+
elif "facture" in error_message.lower() or "invoice" in error_message.lower():
|
|
207
|
+
resource = "invoice"
|
|
208
208
|
elif "structure" in error_message.lower():
|
|
209
209
|
resource = "structure"
|
|
210
210
|
return FactPulseNotFoundError(resource)
|
|
@@ -222,17 +222,17 @@ def parse_api_error(response_json: Dict[str, Any], status_code: int = 400) -> Fa
|
|
|
222
222
|
|
|
223
223
|
|
|
224
224
|
def api_exception_to_validation_error(api_exception) -> FactPulseValidationError:
|
|
225
|
-
"""
|
|
225
|
+
"""Convert an SDK-generated ApiException to FactPulseValidationError.
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
The openapi-generator SDK generates ApiException objects that are not
|
|
228
|
+
very user-friendly. This function converts them to FactPulse exceptions
|
|
229
|
+
with intelligent error parsing.
|
|
230
230
|
|
|
231
231
|
Args:
|
|
232
|
-
api_exception:
|
|
232
|
+
api_exception: ApiException from the generated SDK
|
|
233
233
|
|
|
234
234
|
Returns:
|
|
235
|
-
FactPulseValidationError
|
|
235
|
+
FactPulseValidationError with structured details
|
|
236
236
|
"""
|
|
237
237
|
import json
|
|
238
238
|
|
|
@@ -246,7 +246,7 @@ def api_exception_to_validation_error(api_exception) -> FactPulseValidationError
|
|
|
246
246
|
|
|
247
247
|
error = parse_api_error(response_json, status_code)
|
|
248
248
|
|
|
249
|
-
#
|
|
249
|
+
# Convert to FactPulseValidationError if not already
|
|
250
250
|
if isinstance(error, FactPulseValidationError):
|
|
251
251
|
return error
|
|
252
252
|
else:
|