factpulse 1.0.9__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.

Files changed (269) hide show
  1. factpulse/__init__.py +275 -203
  2. factpulse/api/__init__.py +5 -3
  3. factpulse/api/afnorpdppa_api.py +559 -9
  4. factpulse/api/afnorpdppa_directory_service_api.py +4313 -66
  5. factpulse/api/afnorpdppa_flow_service_api.py +23 -23
  6. factpulse/api/chorus_pro_api.py +362 -404
  7. factpulse/api/{signature_lectronique_api.py → document_conversion_api.py} +519 -371
  8. factpulse/api/health_api.py +526 -0
  9. factpulse/api/invoice_processing_api.py +3437 -0
  10. factpulse/api/pdfxml_verification_api.py +1719 -0
  11. factpulse/api/{sant_api.py → user_api.py} +18 -17
  12. factpulse/api_client.py +6 -6
  13. factpulse/configuration.py +6 -4
  14. factpulse/exceptions.py +8 -5
  15. factpulse/models/__init__.py +133 -99
  16. factpulse/models/acknowledgment_status.py +38 -0
  17. factpulse/models/additional_document.py +115 -0
  18. factpulse/models/afnor_credentials.py +106 -0
  19. factpulse/models/afnor_destination.py +127 -0
  20. factpulse/models/afnor_health_check_response.py +91 -0
  21. factpulse/models/afnor_result.py +105 -0
  22. factpulse/models/allowance_charge.py +147 -0
  23. factpulse/models/allowance_reason_code.py +42 -0
  24. factpulse/models/allowance_total_amount.py +145 -0
  25. factpulse/models/amount.py +139 -0
  26. factpulse/models/amount_due.py +139 -0
  27. factpulse/models/api_error.py +104 -0
  28. factpulse/models/async_task_status.py +97 -0
  29. factpulse/models/base_amount.py +145 -0
  30. factpulse/models/bounding_box_schema.py +100 -0
  31. factpulse/models/celery_status.py +40 -0
  32. factpulse/models/certificate_info_response.py +24 -24
  33. factpulse/models/charge_total_amount.py +145 -0
  34. factpulse/models/chorus_pro_destination.py +108 -0
  35. factpulse/models/chorus_pro_result.py +101 -0
  36. factpulse/models/contact.py +113 -0
  37. factpulse/models/convert_error_response.py +105 -0
  38. factpulse/models/convert_pending_input_response.py +114 -0
  39. factpulse/models/convert_resume_request.py +87 -0
  40. factpulse/models/convert_success_response.py +126 -0
  41. factpulse/models/convert_validation_failed_response.py +120 -0
  42. factpulse/models/delivery_party.py +121 -0
  43. factpulse/models/destination.py +27 -27
  44. factpulse/models/document_type_info.py +91 -0
  45. factpulse/models/electronic_address.py +90 -0
  46. factpulse/models/enriched_invoice_info.py +133 -0
  47. factpulse/models/error_level.py +37 -0
  48. factpulse/models/error_source.py +43 -0
  49. factpulse/models/extraction_info.py +93 -0
  50. factpulse/models/factur_x_invoice.py +320 -0
  51. factpulse/models/factur_x_profile.py +39 -0
  52. factpulse/models/factur_xpdf_info.py +91 -0
  53. factpulse/models/facture_electronique_rest_api_schemas_chorus_pro_chorus_pro_credentials.py +95 -0
  54. factpulse/models/facture_electronique_rest_api_schemas_processing_chorus_pro_credentials.py +115 -0
  55. factpulse/models/field_status.py +40 -0
  56. factpulse/models/file_info.py +94 -0
  57. factpulse/models/files_info.py +106 -0
  58. factpulse/models/flow_direction.py +37 -0
  59. factpulse/models/flow_profile.py +38 -0
  60. factpulse/models/flow_summary.py +131 -0
  61. factpulse/models/flow_syntax.py +40 -0
  62. factpulse/models/flow_type.py +40 -0
  63. factpulse/models/generate_certificate_request.py +26 -26
  64. factpulse/models/generate_certificate_response.py +15 -15
  65. factpulse/models/get_chorus_pro_id_request.py +100 -0
  66. factpulse/models/get_chorus_pro_id_response.py +98 -0
  67. factpulse/models/get_invoice_request.py +98 -0
  68. factpulse/models/get_invoice_response.py +142 -0
  69. factpulse/models/get_structure_request.py +100 -0
  70. factpulse/models/get_structure_response.py +142 -0
  71. factpulse/models/global_allowance_amount.py +139 -0
  72. factpulse/models/gross_unit_price.py +145 -0
  73. factpulse/models/http_validation_error.py +2 -2
  74. factpulse/models/incoming_invoice.py +196 -0
  75. factpulse/models/incoming_supplier.py +144 -0
  76. factpulse/models/invoice_format.py +38 -0
  77. factpulse/models/invoice_line.py +354 -0
  78. factpulse/models/invoice_line_allowance_amount.py +145 -0
  79. factpulse/models/invoice_note.py +94 -0
  80. factpulse/models/invoice_references.py +194 -0
  81. factpulse/models/invoice_status.py +96 -0
  82. factpulse/models/invoice_totals.py +177 -0
  83. factpulse/models/invoice_totals_prepayment.py +145 -0
  84. factpulse/models/invoice_type_code.py +51 -0
  85. factpulse/models/invoicing_framework.py +110 -0
  86. factpulse/models/invoicing_framework_code.py +39 -0
  87. factpulse/models/line_net_amount.py +145 -0
  88. factpulse/models/line_total_amount.py +145 -0
  89. factpulse/models/mandatory_note_schema.py +124 -0
  90. factpulse/models/manual_rate.py +139 -0
  91. factpulse/models/manual_vat_rate.py +139 -0
  92. factpulse/models/missing_field.py +107 -0
  93. factpulse/models/operation_nature.py +49 -0
  94. factpulse/models/output_format.py +37 -0
  95. factpulse/models/page_dimensions_schema.py +89 -0
  96. factpulse/models/payee.py +168 -0
  97. factpulse/models/payment_card.py +99 -0
  98. factpulse/models/payment_means.py +41 -0
  99. factpulse/models/pdf_validation_result_api.py +169 -0
  100. factpulse/models/pdp_credentials.py +20 -13
  101. factpulse/models/percentage.py +145 -0
  102. factpulse/models/postal_address.py +134 -0
  103. factpulse/models/price_allowance_amount.py +145 -0
  104. factpulse/models/price_basis_quantity.py +145 -0
  105. factpulse/models/processing_options.py +94 -0
  106. factpulse/models/product_characteristic.py +89 -0
  107. factpulse/models/product_classification.py +101 -0
  108. factpulse/models/quantity.py +139 -0
  109. factpulse/models/recipient.py +167 -0
  110. factpulse/models/rounding_amount.py +145 -0
  111. factpulse/models/scheme_id.py +14 -8
  112. factpulse/models/search_flow_request.py +143 -0
  113. factpulse/models/search_flow_response.py +101 -0
  114. factpulse/models/search_services_response.py +101 -0
  115. factpulse/models/search_structure_request.py +119 -0
  116. factpulse/models/search_structure_response.py +101 -0
  117. factpulse/models/signature_info.py +6 -6
  118. factpulse/models/signature_info_api.py +122 -0
  119. factpulse/models/signature_parameters.py +133 -0
  120. factpulse/models/simplified_invoice_data.py +124 -0
  121. factpulse/models/structure_info.py +14 -14
  122. factpulse/models/structure_parameters.py +91 -0
  123. factpulse/models/structure_service.py +93 -0
  124. factpulse/models/submission_mode.py +38 -0
  125. factpulse/models/submit_complete_invoice_request.py +116 -0
  126. factpulse/models/submit_complete_invoice_response.py +145 -0
  127. factpulse/models/submit_flow_request.py +123 -0
  128. factpulse/models/submit_flow_response.py +109 -0
  129. factpulse/models/submit_gross_amount.py +139 -0
  130. factpulse/models/submit_invoice_request.py +176 -0
  131. factpulse/models/submit_invoice_response.py +103 -0
  132. factpulse/models/submit_net_amount.py +139 -0
  133. factpulse/models/submit_vat_amount.py +139 -0
  134. factpulse/models/supplementary_attachment.py +95 -0
  135. factpulse/models/supplier.py +225 -0
  136. factpulse/models/task_response.py +87 -0
  137. factpulse/models/tax_representative.py +95 -0
  138. factpulse/models/taxable_amount.py +139 -0
  139. factpulse/models/total_gross_amount.py +139 -0
  140. factpulse/models/total_net_amount.py +139 -0
  141. factpulse/models/total_vat_amount.py +139 -0
  142. factpulse/models/unit_net_price.py +139 -0
  143. factpulse/models/unit_of_measure.py +41 -0
  144. factpulse/models/validation_error.py +2 -2
  145. factpulse/models/validation_error_detail.py +107 -0
  146. factpulse/models/validation_error_loc_inner.py +2 -2
  147. factpulse/models/validation_error_response.py +87 -0
  148. factpulse/models/validation_info.py +105 -0
  149. factpulse/models/validation_success_response.py +87 -0
  150. factpulse/models/vat_accounting_code.py +39 -0
  151. factpulse/models/vat_amount.py +139 -0
  152. factpulse/models/vat_category.py +44 -0
  153. factpulse/models/vat_line.py +140 -0
  154. factpulse/models/vat_point_date_code.py +38 -0
  155. factpulse/models/vat_rate.py +145 -0
  156. factpulse/models/verification_success_response.py +135 -0
  157. factpulse/models/verified_field_schema.py +129 -0
  158. factpulse/rest.py +9 -4
  159. factpulse-3.0.7.dist-info/METADATA +292 -0
  160. factpulse-3.0.7.dist-info/RECORD +168 -0
  161. factpulse-3.0.7.dist-info/top_level.txt +2 -0
  162. factpulse_helpers/__init__.py +96 -0
  163. factpulse_helpers/client.py +2111 -0
  164. factpulse_helpers/exceptions.py +253 -0
  165. factpulse/api/processing_endpoints_unifis_api.py +0 -592
  166. factpulse/api/traitement_facture_api.py +0 -3439
  167. factpulse/api/utilisateur_api.py +0 -282
  168. factpulse/models/adresse_electronique.py +0 -90
  169. factpulse/models/adresse_postale.py +0 -120
  170. factpulse/models/body_ajouter_fichier_api_v1_chorus_pro_transverses_ajouter_fichier_post.py +0 -104
  171. factpulse/models/body_completer_facture_api_v1_chorus_pro_factures_completer_post.py +0 -104
  172. factpulse/models/body_lister_services_structure_api_v1_chorus_pro_structures_id_structure_cpp_services_get.py +0 -102
  173. factpulse/models/body_rechercher_factures_destinataire_api_v1_chorus_pro_factures_rechercher_destinataire_post.py +0 -104
  174. factpulse/models/body_rechercher_factures_fournisseur_api_v1_chorus_pro_factures_rechercher_fournisseur_post.py +0 -104
  175. factpulse/models/body_recycler_facture_api_v1_chorus_pro_factures_recycler_post.py +0 -104
  176. factpulse/models/body_telecharger_groupe_factures_api_v1_chorus_pro_factures_telecharger_groupe_post.py +0 -104
  177. factpulse/models/body_traiter_facture_recue_api_v1_chorus_pro_factures_traiter_facture_recue_post.py +0 -104
  178. factpulse/models/body_valideur_consulter_facture_api_v1_chorus_pro_factures_valideur_consulter_post.py +0 -104
  179. factpulse/models/body_valideur_rechercher_factures_api_v1_chorus_pro_factures_valideur_rechercher_post.py +0 -104
  180. factpulse/models/body_valideur_traiter_facture_api_v1_chorus_pro_factures_valideur_traiter_post.py +0 -104
  181. factpulse/models/cadre_de_facturation.py +0 -102
  182. factpulse/models/categorie_tva.py +0 -44
  183. factpulse/models/chorus_pro_credentials.py +0 -95
  184. factpulse/models/code_cadre_facturation.py +0 -39
  185. factpulse/models/code_raison_reduction.py +0 -42
  186. factpulse/models/consulter_facture_request.py +0 -98
  187. factpulse/models/consulter_facture_response.py +0 -142
  188. factpulse/models/consulter_structure_request.py +0 -100
  189. factpulse/models/consulter_structure_response.py +0 -142
  190. factpulse/models/credentials_afnor.py +0 -106
  191. factpulse/models/credentials_chorus_pro.py +0 -115
  192. factpulse/models/destinataire.py +0 -116
  193. factpulse/models/destination_afnor.py +0 -127
  194. factpulse/models/destination_chorus_pro.py +0 -108
  195. factpulse/models/direction_flux.py +0 -37
  196. factpulse/models/donnees_facture_simplifiees.py +0 -124
  197. factpulse/models/facture_enrichie_info_input.py +0 -123
  198. factpulse/models/facture_enrichie_info_output.py +0 -133
  199. factpulse/models/facture_factur_x.py +0 -173
  200. factpulse/models/flux_resume.py +0 -131
  201. factpulse/models/format_sortie.py +0 -37
  202. factpulse/models/fournisseur.py +0 -146
  203. factpulse/models/information_signature_api.py +0 -122
  204. factpulse/models/ligne_de_poste.py +0 -188
  205. factpulse/models/ligne_de_poste_montant_remise_ht.py +0 -145
  206. factpulse/models/ligne_de_poste_montant_total_ligne_ht.py +0 -145
  207. factpulse/models/ligne_de_poste_taux_tva_manuel.py +0 -145
  208. factpulse/models/ligne_de_tva.py +0 -118
  209. factpulse/models/mode_depot.py +0 -38
  210. factpulse/models/mode_paiement.py +0 -41
  211. factpulse/models/montant_ht_total.py +0 -139
  212. factpulse/models/montant_total.py +0 -138
  213. factpulse/models/montant_total_acompte.py +0 -145
  214. factpulse/models/montant_total_montant_remise_globale_ttc.py +0 -145
  215. factpulse/models/montant_ttc_total.py +0 -139
  216. factpulse/models/montant_tva.py +0 -139
  217. factpulse/models/montantapayer.py +0 -139
  218. factpulse/models/montantbaseht.py +0 -139
  219. factpulse/models/montanthttotal.py +0 -139
  220. factpulse/models/montantttctotal.py +0 -139
  221. factpulse/models/montanttva.py +0 -139
  222. factpulse/models/montanttva1.py +0 -139
  223. factpulse/models/montantunitaireht.py +0 -139
  224. factpulse/models/obtenir_id_chorus_pro_request.py +0 -100
  225. factpulse/models/obtenir_id_chorus_pro_response.py +0 -98
  226. factpulse/models/options_processing.py +0 -94
  227. factpulse/models/parametres_signature.py +0 -133
  228. factpulse/models/parametres_structure.py +0 -91
  229. factpulse/models/pdf_factur_x_info.py +0 -91
  230. factpulse/models/piece_jointe_complementaire.py +0 -95
  231. factpulse/models/profil_api.py +0 -39
  232. factpulse/models/profil_flux.py +0 -38
  233. factpulse/models/quantite.py +0 -139
  234. factpulse/models/quota_info.py +0 -95
  235. factpulse/models/rechercher_services_response.py +0 -101
  236. factpulse/models/rechercher_structure_request.py +0 -119
  237. factpulse/models/rechercher_structure_response.py +0 -101
  238. factpulse/models/references.py +0 -124
  239. factpulse/models/reponse_healthcheck_afnor.py +0 -91
  240. factpulse/models/reponse_recherche_flux.py +0 -101
  241. factpulse/models/reponse_soumission_flux.py +0 -109
  242. factpulse/models/reponse_tache.py +0 -87
  243. factpulse/models/reponse_validation_erreur.py +0 -87
  244. factpulse/models/reponse_validation_succes.py +0 -87
  245. factpulse/models/requete_recherche_flux.py +0 -143
  246. factpulse/models/requete_soumission_flux.py +0 -123
  247. factpulse/models/resultat_afnor.py +0 -105
  248. factpulse/models/resultat_chorus_pro.py +0 -101
  249. factpulse/models/resultat_validation_pdfapi.py +0 -169
  250. factpulse/models/service_structure.py +0 -93
  251. factpulse/models/soumettre_facture_complete_request.py +0 -116
  252. factpulse/models/soumettre_facture_complete_response.py +0 -145
  253. factpulse/models/soumettre_facture_request.py +0 -164
  254. factpulse/models/soumettre_facture_response.py +0 -103
  255. factpulse/models/statut_acquittement.py +0 -38
  256. factpulse/models/statut_facture.py +0 -96
  257. factpulse/models/statut_tache.py +0 -99
  258. factpulse/models/syntaxe_flux.py +0 -40
  259. factpulse/models/tauxmanuel.py +0 -139
  260. factpulse/models/type_facture.py +0 -37
  261. factpulse/models/type_flux.py +0 -40
  262. factpulse/models/type_tva.py +0 -39
  263. factpulse/models/unite.py +0 -41
  264. factpulse/models/utilisateur.py +0 -128
  265. factpulse-1.0.9.dist-info/METADATA +0 -182
  266. factpulse-1.0.9.dist-info/RECORD +0 -131
  267. factpulse-1.0.9.dist-info/top_level.txt +0 -1
  268. {factpulse-1.0.9.dist-info → factpulse-3.0.7.dist-info}/WHEEL +0 -0
  269. {factpulse-1.0.9.dist-info → factpulse-3.0.7.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,354 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ FactPulse REST API
5
+
6
+ REST API for electronic invoicing in France: Factur-X, AFNOR PDP/PA, electronic signatures. ## 🎯 Main Features ### 📄 Factur-X Invoice Generation - **Formats**: XML only or PDF/A-3 with embedded XML - **Profiles**: MINIMUM, BASIC, EN16931, EXTENDED - **Standards**: EN 16931 (EU directive 2014/55), ISO 19005-3 (PDF/A-3), CII (UN/CEFACT) - **🆕 Simplified Format**: Generation from SIRET + auto-enrichment (Chorus Pro API + Business Search) ### ✅ Validation and Compliance - **XML Validation**: Schematron (45 to 210+ rules depending on profile) - **PDF Validation**: PDF/A-3, Factur-X XMP metadata, electronic signatures - **VeraPDF**: Strict PDF/A validation (146+ ISO 19005-3 rules) - **Asynchronous Processing**: Celery support for heavy validations (VeraPDF) ### 📡 AFNOR PDP/PA Integration (XP Z12-013) - **Flow Submission**: Send invoices to Partner Dematerialization Platforms - **Flow Search**: View submitted invoices - **Download**: Retrieve PDF/A-3 with XML - **Directory Service**: Company search (SIREN/SIRET) - **Multi-client**: Support for multiple PDP configs per user (stored credentials or zero-storage) ### ✍️ PDF Electronic Signature - **Standards**: PAdES-B-B, PAdES-B-T (RFC 3161 timestamping), PAdES-B-LT (long-term archival) - **eIDAS Levels**: SES (self-signed), AdES (commercial CA), QES (QTSP) - **Validation**: Cryptographic integrity and certificate verification - **Certificate Generation**: Self-signed X.509 certificates for testing ### 🔄 Asynchronous Processing - **Celery**: Asynchronous generation, validation and signing - **Polling**: Status tracking via `/tasks/{task_id}/status` - **No timeout**: Ideal for large files or heavy validations ## 🔒 Authentication All requests require a **JWT token** in the Authorization header: ``` Authorization: Bearer YOUR_JWT_TOKEN ``` ### How to obtain a JWT token? #### 🔑 Method 1: `/api/token/` API (Recommended) **URL:** `https://www.factpulse.fr/api/token/` This method is **recommended** for integration in your applications and CI/CD workflows. **Prerequisites:** Having set a password on your account **For users registered via email/password:** - You already have a password, use it directly **For users registered via OAuth (Google/GitHub):** - You must first set a password at: https://www.factpulse.fr/accounts/password/set/ - Once the password is created, you can use the API **Request example:** ```bash curl -X POST https://www.factpulse.fr/api/token/ \\ -H \"Content-Type: application/json\" \\ -d '{ \"username\": \"your_email@example.com\", \"password\": \"your_password\" }' ``` **Optional `client_uid` parameter:** To select credentials for a specific client (PA/PDP, Chorus Pro, signing certificates), add `client_uid`: ```bash curl -X POST https://www.factpulse.fr/api/token/ \\ -H \"Content-Type: application/json\" \\ -d '{ \"username\": \"your_email@example.com\", \"password\": \"your_password\", \"client_uid\": \"550e8400-e29b-41d4-a716-446655440000\" }' ``` The `client_uid` will be included in the JWT and allow the API to automatically use: - AFNOR/PDP credentials configured for this client - Chorus Pro credentials configured for this client - Electronic signature certificates configured for this client **Response:** ```json { \"access\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\", // Access token (validity: 30 min) \"refresh\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\" // Refresh token (validity: 7 days) } ``` **Advantages:** - ✅ Full automation (CI/CD, scripts) - ✅ Programmatic token management - ✅ Refresh token support for automatic access renewal - ✅ Easy integration in any language/tool #### 🖥️ Method 2: Dashboard Generation (Alternative) **URL:** https://www.factpulse.fr/dashboard/ This method is suitable for quick tests or occasional use via the graphical interface. **How it works:** - Log in to the dashboard - Use the \"Generate Test Token\" or \"Generate Production Token\" buttons - Works for **all** users (OAuth and email/password), without requiring a password **Token types:** - **Test Token**: 24h validity, 1000 calls/day quota (free) - **Production Token**: 7 days validity, quota based on your plan **Advantages:** - ✅ Quick for API testing - ✅ No password required - ✅ Simple visual interface **Disadvantages:** - ❌ Requires manual action - ❌ No refresh token - ❌ Less suited for automation ### 📚 Full Documentation For more information on authentication and API usage: https://www.factpulse.fr/documentation-api/
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ import pprint
17
+ import re # noqa: F401
18
+ import json
19
+
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr
21
+ from typing import Any, ClassVar, Dict, List, Optional
22
+ from factpulse.models.additional_document import AdditionalDocument
23
+ from factpulse.models.allowance_charge import AllowanceCharge
24
+ from factpulse.models.allowance_reason_code import AllowanceReasonCode
25
+ from factpulse.models.gross_unit_price import GrossUnitPrice
26
+ from factpulse.models.invoice_line_allowance_amount import InvoiceLineAllowanceAmount
27
+ from factpulse.models.invoice_note import InvoiceNote
28
+ from factpulse.models.line_net_amount import LineNetAmount
29
+ from factpulse.models.manual_vat_rate import ManualVatRate
30
+ from factpulse.models.price_allowance_amount import PriceAllowanceAmount
31
+ from factpulse.models.price_basis_quantity import PriceBasisQuantity
32
+ from factpulse.models.product_characteristic import ProductCharacteristic
33
+ from factpulse.models.product_classification import ProductClassification
34
+ from factpulse.models.quantity import Quantity
35
+ from factpulse.models.unit_net_price import UnitNetPrice
36
+ from factpulse.models.unit_of_measure import UnitOfMeasure
37
+ from factpulse.models.vat_category import VATCategory
38
+ from typing import Optional, Set
39
+ from typing_extensions import Self
40
+
41
+ class InvoiceLine(BaseModel):
42
+ """
43
+ Represents an invoice line item (BG-25).
44
+ """ # noqa: E501
45
+ line_number: StrictInt = Field(description="Invoice line identifier (BT-126).")
46
+ line_note: Optional[StrictStr] = None
47
+ reference: Optional[StrictStr] = None
48
+ buyer_assigned_id: Optional[StrictStr] = None
49
+ product_global_id: Optional[StrictStr] = None
50
+ product_global_id_scheme: Optional[StrictStr] = None
51
+ item_name: StrictStr = Field(description="Item name (BT-153).")
52
+ item_description: Optional[StrictStr] = None
53
+ origin_country: Optional[StrictStr] = None
54
+ characteristics: Optional[List[ProductCharacteristic]] = None
55
+ classifications: Optional[List[ProductClassification]] = None
56
+ quantity: Quantity
57
+ unit: UnitOfMeasure = Field(description="Invoiced quantity unit of measure code (BT-130).")
58
+ gross_unit_price: Optional[GrossUnitPrice] = None
59
+ unit_net_price: UnitNetPrice
60
+ price_basis_quantity: Optional[PriceBasisQuantity] = None
61
+ price_basis_unit: Optional[StrictStr] = None
62
+ price_allowance_amount: Optional[PriceAllowanceAmount] = None
63
+ line_net_amount: Optional[LineNetAmount] = Field(default=None, alias="lineNetAmount")
64
+ allowance_amount: Optional[InvoiceLineAllowanceAmount] = Field(default=None, alias="allowanceAmount")
65
+ allowance_reason_code: Optional[AllowanceReasonCode] = Field(default=None, alias="allowanceReasonCode")
66
+ allowance_reason: Optional[StrictStr] = Field(default=None, alias="allowanceReason")
67
+ allowances_charges: Optional[List[AllowanceCharge]] = None
68
+ vat_rate: Optional[StrictStr] = None
69
+ manual_vat_rate: Optional[ManualVatRate] = None
70
+ vat_category: Optional[VATCategory] = None
71
+ period_start_date: Optional[StrictStr] = None
72
+ period_end_date: Optional[StrictStr] = None
73
+ purchase_order_line_ref: Optional[StrictStr] = None
74
+ accounting_account: Optional[StrictStr] = None
75
+ additional_documents: Optional[List[AdditionalDocument]] = None
76
+ line_notes: Optional[List[InvoiceNote]] = None
77
+ __properties: ClassVar[List[str]] = ["line_number", "line_note", "reference", "buyer_assigned_id", "product_global_id", "product_global_id_scheme", "item_name", "item_description", "origin_country", "characteristics", "classifications", "quantity", "unit", "gross_unit_price", "unit_net_price", "price_basis_quantity", "price_basis_unit", "price_allowance_amount", "lineNetAmount", "allowanceAmount", "allowanceReasonCode", "allowanceReason", "allowances_charges", "vat_rate", "manual_vat_rate", "vat_category", "period_start_date", "period_end_date", "purchase_order_line_ref", "accounting_account", "additional_documents", "line_notes"]
78
+
79
+ model_config = ConfigDict(
80
+ populate_by_name=True,
81
+ validate_assignment=True,
82
+ protected_namespaces=(),
83
+ )
84
+
85
+
86
+ def to_str(self) -> str:
87
+ """Returns the string representation of the model using alias"""
88
+ return pprint.pformat(self.model_dump(by_alias=True))
89
+
90
+ def to_json(self) -> str:
91
+ """Returns the JSON representation of the model using alias"""
92
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
93
+ return json.dumps(self.to_dict())
94
+
95
+ @classmethod
96
+ def from_json(cls, json_str: str) -> Optional[Self]:
97
+ """Create an instance of InvoiceLine from a JSON string"""
98
+ return cls.from_dict(json.loads(json_str))
99
+
100
+ def to_dict(self) -> Dict[str, Any]:
101
+ """Return the dictionary representation of the model using alias.
102
+
103
+ This has the following differences from calling pydantic's
104
+ `self.model_dump(by_alias=True)`:
105
+
106
+ * `None` is only added to the output dict for nullable fields that
107
+ were set at model initialization. Other fields with value `None`
108
+ are ignored.
109
+ """
110
+ excluded_fields: Set[str] = set([
111
+ ])
112
+
113
+ _dict = self.model_dump(
114
+ by_alias=True,
115
+ exclude=excluded_fields,
116
+ exclude_none=True,
117
+ )
118
+ # override the default output from pydantic by calling `to_dict()` of each item in characteristics (list)
119
+ _items = []
120
+ if self.characteristics:
121
+ for _item_characteristics in self.characteristics:
122
+ if _item_characteristics:
123
+ _items.append(_item_characteristics.to_dict())
124
+ _dict['characteristics'] = _items
125
+ # override the default output from pydantic by calling `to_dict()` of each item in classifications (list)
126
+ _items = []
127
+ if self.classifications:
128
+ for _item_classifications in self.classifications:
129
+ if _item_classifications:
130
+ _items.append(_item_classifications.to_dict())
131
+ _dict['classifications'] = _items
132
+ # override the default output from pydantic by calling `to_dict()` of quantity
133
+ if self.quantity:
134
+ _dict['quantity'] = self.quantity.to_dict()
135
+ # override the default output from pydantic by calling `to_dict()` of gross_unit_price
136
+ if self.gross_unit_price:
137
+ _dict['gross_unit_price'] = self.gross_unit_price.to_dict()
138
+ # override the default output from pydantic by calling `to_dict()` of unit_net_price
139
+ if self.unit_net_price:
140
+ _dict['unit_net_price'] = self.unit_net_price.to_dict()
141
+ # override the default output from pydantic by calling `to_dict()` of price_basis_quantity
142
+ if self.price_basis_quantity:
143
+ _dict['price_basis_quantity'] = self.price_basis_quantity.to_dict()
144
+ # override the default output from pydantic by calling `to_dict()` of price_allowance_amount
145
+ if self.price_allowance_amount:
146
+ _dict['price_allowance_amount'] = self.price_allowance_amount.to_dict()
147
+ # override the default output from pydantic by calling `to_dict()` of line_net_amount
148
+ if self.line_net_amount:
149
+ _dict['lineNetAmount'] = self.line_net_amount.to_dict()
150
+ # override the default output from pydantic by calling `to_dict()` of allowance_amount
151
+ if self.allowance_amount:
152
+ _dict['allowanceAmount'] = self.allowance_amount.to_dict()
153
+ # override the default output from pydantic by calling `to_dict()` of each item in allowances_charges (list)
154
+ _items = []
155
+ if self.allowances_charges:
156
+ for _item_allowances_charges in self.allowances_charges:
157
+ if _item_allowances_charges:
158
+ _items.append(_item_allowances_charges.to_dict())
159
+ _dict['allowances_charges'] = _items
160
+ # override the default output from pydantic by calling `to_dict()` of manual_vat_rate
161
+ if self.manual_vat_rate:
162
+ _dict['manual_vat_rate'] = self.manual_vat_rate.to_dict()
163
+ # override the default output from pydantic by calling `to_dict()` of each item in additional_documents (list)
164
+ _items = []
165
+ if self.additional_documents:
166
+ for _item_additional_documents in self.additional_documents:
167
+ if _item_additional_documents:
168
+ _items.append(_item_additional_documents.to_dict())
169
+ _dict['additional_documents'] = _items
170
+ # override the default output from pydantic by calling `to_dict()` of each item in line_notes (list)
171
+ _items = []
172
+ if self.line_notes:
173
+ for _item_line_notes in self.line_notes:
174
+ if _item_line_notes:
175
+ _items.append(_item_line_notes.to_dict())
176
+ _dict['line_notes'] = _items
177
+ # set to None if line_note (nullable) is None
178
+ # and model_fields_set contains the field
179
+ if self.line_note is None and "line_note" in self.model_fields_set:
180
+ _dict['line_note'] = None
181
+
182
+ # set to None if reference (nullable) is None
183
+ # and model_fields_set contains the field
184
+ if self.reference is None and "reference" in self.model_fields_set:
185
+ _dict['reference'] = None
186
+
187
+ # set to None if buyer_assigned_id (nullable) is None
188
+ # and model_fields_set contains the field
189
+ if self.buyer_assigned_id is None and "buyer_assigned_id" in self.model_fields_set:
190
+ _dict['buyer_assigned_id'] = None
191
+
192
+ # set to None if product_global_id (nullable) is None
193
+ # and model_fields_set contains the field
194
+ if self.product_global_id is None and "product_global_id" in self.model_fields_set:
195
+ _dict['product_global_id'] = None
196
+
197
+ # set to None if product_global_id_scheme (nullable) is None
198
+ # and model_fields_set contains the field
199
+ if self.product_global_id_scheme is None and "product_global_id_scheme" in self.model_fields_set:
200
+ _dict['product_global_id_scheme'] = None
201
+
202
+ # set to None if item_description (nullable) is None
203
+ # and model_fields_set contains the field
204
+ if self.item_description is None and "item_description" in self.model_fields_set:
205
+ _dict['item_description'] = None
206
+
207
+ # set to None if origin_country (nullable) is None
208
+ # and model_fields_set contains the field
209
+ if self.origin_country is None and "origin_country" in self.model_fields_set:
210
+ _dict['origin_country'] = None
211
+
212
+ # set to None if characteristics (nullable) is None
213
+ # and model_fields_set contains the field
214
+ if self.characteristics is None and "characteristics" in self.model_fields_set:
215
+ _dict['characteristics'] = None
216
+
217
+ # set to None if classifications (nullable) is None
218
+ # and model_fields_set contains the field
219
+ if self.classifications is None and "classifications" in self.model_fields_set:
220
+ _dict['classifications'] = None
221
+
222
+ # set to None if gross_unit_price (nullable) is None
223
+ # and model_fields_set contains the field
224
+ if self.gross_unit_price is None and "gross_unit_price" in self.model_fields_set:
225
+ _dict['gross_unit_price'] = None
226
+
227
+ # set to None if price_basis_quantity (nullable) is None
228
+ # and model_fields_set contains the field
229
+ if self.price_basis_quantity is None and "price_basis_quantity" in self.model_fields_set:
230
+ _dict['price_basis_quantity'] = None
231
+
232
+ # set to None if price_basis_unit (nullable) is None
233
+ # and model_fields_set contains the field
234
+ if self.price_basis_unit is None and "price_basis_unit" in self.model_fields_set:
235
+ _dict['price_basis_unit'] = None
236
+
237
+ # set to None if price_allowance_amount (nullable) is None
238
+ # and model_fields_set contains the field
239
+ if self.price_allowance_amount is None and "price_allowance_amount" in self.model_fields_set:
240
+ _dict['price_allowance_amount'] = None
241
+
242
+ # set to None if line_net_amount (nullable) is None
243
+ # and model_fields_set contains the field
244
+ if self.line_net_amount is None and "line_net_amount" in self.model_fields_set:
245
+ _dict['lineNetAmount'] = None
246
+
247
+ # set to None if allowance_amount (nullable) is None
248
+ # and model_fields_set contains the field
249
+ if self.allowance_amount is None and "allowance_amount" in self.model_fields_set:
250
+ _dict['allowanceAmount'] = None
251
+
252
+ # set to None if allowance_reason_code (nullable) is None
253
+ # and model_fields_set contains the field
254
+ if self.allowance_reason_code is None and "allowance_reason_code" in self.model_fields_set:
255
+ _dict['allowanceReasonCode'] = None
256
+
257
+ # set to None if allowance_reason (nullable) is None
258
+ # and model_fields_set contains the field
259
+ if self.allowance_reason is None and "allowance_reason" in self.model_fields_set:
260
+ _dict['allowanceReason'] = None
261
+
262
+ # set to None if allowances_charges (nullable) is None
263
+ # and model_fields_set contains the field
264
+ if self.allowances_charges is None and "allowances_charges" in self.model_fields_set:
265
+ _dict['allowances_charges'] = None
266
+
267
+ # set to None if vat_rate (nullable) is None
268
+ # and model_fields_set contains the field
269
+ if self.vat_rate is None and "vat_rate" in self.model_fields_set:
270
+ _dict['vat_rate'] = None
271
+
272
+ # set to None if vat_category (nullable) is None
273
+ # and model_fields_set contains the field
274
+ if self.vat_category is None and "vat_category" in self.model_fields_set:
275
+ _dict['vat_category'] = None
276
+
277
+ # set to None if period_start_date (nullable) is None
278
+ # and model_fields_set contains the field
279
+ if self.period_start_date is None and "period_start_date" in self.model_fields_set:
280
+ _dict['period_start_date'] = None
281
+
282
+ # set to None if period_end_date (nullable) is None
283
+ # and model_fields_set contains the field
284
+ if self.period_end_date is None and "period_end_date" in self.model_fields_set:
285
+ _dict['period_end_date'] = None
286
+
287
+ # set to None if purchase_order_line_ref (nullable) is None
288
+ # and model_fields_set contains the field
289
+ if self.purchase_order_line_ref is None and "purchase_order_line_ref" in self.model_fields_set:
290
+ _dict['purchase_order_line_ref'] = None
291
+
292
+ # set to None if accounting_account (nullable) is None
293
+ # and model_fields_set contains the field
294
+ if self.accounting_account is None and "accounting_account" in self.model_fields_set:
295
+ _dict['accounting_account'] = None
296
+
297
+ # set to None if additional_documents (nullable) is None
298
+ # and model_fields_set contains the field
299
+ if self.additional_documents is None and "additional_documents" in self.model_fields_set:
300
+ _dict['additional_documents'] = None
301
+
302
+ # set to None if line_notes (nullable) is None
303
+ # and model_fields_set contains the field
304
+ if self.line_notes is None and "line_notes" in self.model_fields_set:
305
+ _dict['line_notes'] = None
306
+
307
+ return _dict
308
+
309
+ @classmethod
310
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
311
+ """Create an instance of InvoiceLine from a dict"""
312
+ if obj is None:
313
+ return None
314
+
315
+ if not isinstance(obj, dict):
316
+ return cls.model_validate(obj)
317
+
318
+ _obj = cls.model_validate({
319
+ "line_number": obj.get("line_number"),
320
+ "line_note": obj.get("line_note"),
321
+ "reference": obj.get("reference"),
322
+ "buyer_assigned_id": obj.get("buyer_assigned_id"),
323
+ "product_global_id": obj.get("product_global_id"),
324
+ "product_global_id_scheme": obj.get("product_global_id_scheme"),
325
+ "item_name": obj.get("item_name"),
326
+ "item_description": obj.get("item_description"),
327
+ "origin_country": obj.get("origin_country"),
328
+ "characteristics": [ProductCharacteristic.from_dict(_item) for _item in obj["characteristics"]] if obj.get("characteristics") is not None else None,
329
+ "classifications": [ProductClassification.from_dict(_item) for _item in obj["classifications"]] if obj.get("classifications") is not None else None,
330
+ "quantity": Quantity.from_dict(obj["quantity"]) if obj.get("quantity") is not None else None,
331
+ "unit": obj.get("unit"),
332
+ "gross_unit_price": GrossUnitPrice.from_dict(obj["gross_unit_price"]) if obj.get("gross_unit_price") is not None else None,
333
+ "unit_net_price": UnitNetPrice.from_dict(obj["unit_net_price"]) if obj.get("unit_net_price") is not None else None,
334
+ "price_basis_quantity": PriceBasisQuantity.from_dict(obj["price_basis_quantity"]) if obj.get("price_basis_quantity") is not None else None,
335
+ "price_basis_unit": obj.get("price_basis_unit"),
336
+ "price_allowance_amount": PriceAllowanceAmount.from_dict(obj["price_allowance_amount"]) if obj.get("price_allowance_amount") is not None else None,
337
+ "lineNetAmount": LineNetAmount.from_dict(obj["lineNetAmount"]) if obj.get("lineNetAmount") is not None else None,
338
+ "allowanceAmount": InvoiceLineAllowanceAmount.from_dict(obj["allowanceAmount"]) if obj.get("allowanceAmount") is not None else None,
339
+ "allowanceReasonCode": obj.get("allowanceReasonCode"),
340
+ "allowanceReason": obj.get("allowanceReason"),
341
+ "allowances_charges": [AllowanceCharge.from_dict(_item) for _item in obj["allowances_charges"]] if obj.get("allowances_charges") is not None else None,
342
+ "vat_rate": obj.get("vat_rate"),
343
+ "manual_vat_rate": ManualVatRate.from_dict(obj["manual_vat_rate"]) if obj.get("manual_vat_rate") is not None else None,
344
+ "vat_category": obj.get("vat_category"),
345
+ "period_start_date": obj.get("period_start_date"),
346
+ "period_end_date": obj.get("period_end_date"),
347
+ "purchase_order_line_ref": obj.get("purchase_order_line_ref"),
348
+ "accounting_account": obj.get("accounting_account"),
349
+ "additional_documents": [AdditionalDocument.from_dict(_item) for _item in obj["additional_documents"]] if obj.get("additional_documents") is not None else None,
350
+ "line_notes": [InvoiceNote.from_dict(_item) for _item in obj["line_notes"]] if obj.get("line_notes") is not None else None
351
+ })
352
+ return _obj
353
+
354
+
@@ -0,0 +1,145 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ FactPulse REST API
5
+
6
+ REST API for electronic invoicing in France: Factur-X, AFNOR PDP/PA, electronic signatures. ## 🎯 Main Features ### 📄 Factur-X Invoice Generation - **Formats**: XML only or PDF/A-3 with embedded XML - **Profiles**: MINIMUM, BASIC, EN16931, EXTENDED - **Standards**: EN 16931 (EU directive 2014/55), ISO 19005-3 (PDF/A-3), CII (UN/CEFACT) - **🆕 Simplified Format**: Generation from SIRET + auto-enrichment (Chorus Pro API + Business Search) ### ✅ Validation and Compliance - **XML Validation**: Schematron (45 to 210+ rules depending on profile) - **PDF Validation**: PDF/A-3, Factur-X XMP metadata, electronic signatures - **VeraPDF**: Strict PDF/A validation (146+ ISO 19005-3 rules) - **Asynchronous Processing**: Celery support for heavy validations (VeraPDF) ### 📡 AFNOR PDP/PA Integration (XP Z12-013) - **Flow Submission**: Send invoices to Partner Dematerialization Platforms - **Flow Search**: View submitted invoices - **Download**: Retrieve PDF/A-3 with XML - **Directory Service**: Company search (SIREN/SIRET) - **Multi-client**: Support for multiple PDP configs per user (stored credentials or zero-storage) ### ✍️ PDF Electronic Signature - **Standards**: PAdES-B-B, PAdES-B-T (RFC 3161 timestamping), PAdES-B-LT (long-term archival) - **eIDAS Levels**: SES (self-signed), AdES (commercial CA), QES (QTSP) - **Validation**: Cryptographic integrity and certificate verification - **Certificate Generation**: Self-signed X.509 certificates for testing ### 🔄 Asynchronous Processing - **Celery**: Asynchronous generation, validation and signing - **Polling**: Status tracking via `/tasks/{task_id}/status` - **No timeout**: Ideal for large files or heavy validations ## 🔒 Authentication All requests require a **JWT token** in the Authorization header: ``` Authorization: Bearer YOUR_JWT_TOKEN ``` ### How to obtain a JWT token? #### 🔑 Method 1: `/api/token/` API (Recommended) **URL:** `https://www.factpulse.fr/api/token/` This method is **recommended** for integration in your applications and CI/CD workflows. **Prerequisites:** Having set a password on your account **For users registered via email/password:** - You already have a password, use it directly **For users registered via OAuth (Google/GitHub):** - You must first set a password at: https://www.factpulse.fr/accounts/password/set/ - Once the password is created, you can use the API **Request example:** ```bash curl -X POST https://www.factpulse.fr/api/token/ \\ -H \"Content-Type: application/json\" \\ -d '{ \"username\": \"your_email@example.com\", \"password\": \"your_password\" }' ``` **Optional `client_uid` parameter:** To select credentials for a specific client (PA/PDP, Chorus Pro, signing certificates), add `client_uid`: ```bash curl -X POST https://www.factpulse.fr/api/token/ \\ -H \"Content-Type: application/json\" \\ -d '{ \"username\": \"your_email@example.com\", \"password\": \"your_password\", \"client_uid\": \"550e8400-e29b-41d4-a716-446655440000\" }' ``` The `client_uid` will be included in the JWT and allow the API to automatically use: - AFNOR/PDP credentials configured for this client - Chorus Pro credentials configured for this client - Electronic signature certificates configured for this client **Response:** ```json { \"access\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\", // Access token (validity: 30 min) \"refresh\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\" // Refresh token (validity: 7 days) } ``` **Advantages:** - ✅ Full automation (CI/CD, scripts) - ✅ Programmatic token management - ✅ Refresh token support for automatic access renewal - ✅ Easy integration in any language/tool #### 🖥️ Method 2: Dashboard Generation (Alternative) **URL:** https://www.factpulse.fr/dashboard/ This method is suitable for quick tests or occasional use via the graphical interface. **How it works:** - Log in to the dashboard - Use the \"Generate Test Token\" or \"Generate Production Token\" buttons - Works for **all** users (OAuth and email/password), without requiring a password **Token types:** - **Test Token**: 24h validity, 1000 calls/day quota (free) - **Production Token**: 7 days validity, quota based on your plan **Advantages:** - ✅ Quick for API testing - ✅ No password required - ✅ Simple visual interface **Disadvantages:** - ❌ Requires manual action - ❌ No refresh token - ❌ Less suited for automation ### 📚 Full Documentation For more information on authentication and API usage: https://www.factpulse.fr/documentation-api/
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ from inspect import getfullargspec
17
+ import json
18
+ import pprint
19
+ import re # noqa: F401
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr, ValidationError, field_validator
21
+ from typing import Optional, Union
22
+ from typing_extensions import Annotated
23
+ from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict
24
+ from typing_extensions import Literal, Self
25
+ from pydantic import Field
26
+
27
+ INVOICELINEALLOWANCEAMOUNT_ANY_OF_SCHEMAS = ["float", "str"]
28
+
29
+ class InvoiceLineAllowanceAmount(BaseModel):
30
+ """
31
+ Simple line allowance amount (BT-136). (Accepte number, string ou integer)
32
+ """
33
+
34
+ # data type: float
35
+ anyof_schema_1_validator: Optional[Union[Annotated[float, Field(strict=True, ge=0.0)], Annotated[int, Field(strict=True, ge=0)]]] = None
36
+ # data type: str
37
+ anyof_schema_2_validator: Optional[Annotated[str, Field(strict=True)]] = None
38
+ if TYPE_CHECKING:
39
+ actual_instance: Optional[Union[float, str]] = None
40
+ else:
41
+ actual_instance: Any = None
42
+ any_of_schemas: Set[str] = { "float", "str" }
43
+
44
+ model_config = {
45
+ "validate_assignment": True,
46
+ "protected_namespaces": (),
47
+ }
48
+
49
+ def __init__(self, *args, **kwargs) -> None:
50
+ if args:
51
+ if len(args) > 1:
52
+ raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`")
53
+ if kwargs:
54
+ raise ValueError("If a position argument is used, keyword arguments cannot be used.")
55
+ super().__init__(actual_instance=args[0])
56
+ else:
57
+ super().__init__(**kwargs)
58
+
59
+ @field_validator('actual_instance')
60
+ def actual_instance_must_validate_anyof(cls, v):
61
+ if v is None:
62
+ return v
63
+
64
+ instance = InvoiceLineAllowanceAmount.model_construct()
65
+ error_messages = []
66
+ # validate data type: float
67
+ try:
68
+ instance.anyof_schema_1_validator = v
69
+ return v
70
+ except (ValidationError, ValueError) as e:
71
+ error_messages.append(str(e))
72
+ # validate data type: str
73
+ try:
74
+ instance.anyof_schema_2_validator = v
75
+ return v
76
+ except (ValidationError, ValueError) as e:
77
+ error_messages.append(str(e))
78
+ if error_messages:
79
+ # no match
80
+ raise ValueError("No match found when setting the actual_instance in InvoiceLineAllowanceAmount with anyOf schemas: float, str. Details: " + ", ".join(error_messages))
81
+ else:
82
+ return v
83
+
84
+ @classmethod
85
+ def from_dict(cls, obj: Dict[str, Any]) -> Self:
86
+ return cls.from_json(json.dumps(obj))
87
+
88
+ @classmethod
89
+ def from_json(cls, json_str: str) -> Self:
90
+ """Returns the object represented by the json string"""
91
+ instance = cls.model_construct()
92
+ if json_str is None:
93
+ return instance
94
+
95
+ error_messages = []
96
+ # deserialize data into float
97
+ try:
98
+ # validation
99
+ instance.anyof_schema_1_validator = json.loads(json_str)
100
+ # assign value to actual_instance
101
+ instance.actual_instance = instance.anyof_schema_1_validator
102
+ return instance
103
+ except (ValidationError, ValueError) as e:
104
+ error_messages.append(str(e))
105
+ # deserialize data into str
106
+ try:
107
+ # validation
108
+ instance.anyof_schema_2_validator = json.loads(json_str)
109
+ # assign value to actual_instance
110
+ instance.actual_instance = instance.anyof_schema_2_validator
111
+ return instance
112
+ except (ValidationError, ValueError) as e:
113
+ error_messages.append(str(e))
114
+
115
+ if error_messages:
116
+ # no match
117
+ raise ValueError("No match found when deserializing the JSON string into InvoiceLineAllowanceAmount with anyOf schemas: float, str. Details: " + ", ".join(error_messages))
118
+ else:
119
+ return instance
120
+
121
+ def to_json(self) -> str:
122
+ """Returns the JSON representation of the actual instance"""
123
+ if self.actual_instance is None:
124
+ return "null"
125
+
126
+ if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json):
127
+ return self.actual_instance.to_json()
128
+ else:
129
+ return json.dumps(self.actual_instance)
130
+
131
+ def to_dict(self) -> Optional[Union[Dict[str, Any], float, str]]:
132
+ """Returns the dict representation of the actual instance"""
133
+ if self.actual_instance is None:
134
+ return None
135
+
136
+ if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict):
137
+ return self.actual_instance.to_dict()
138
+ else:
139
+ return self.actual_instance
140
+
141
+ def to_str(self) -> str:
142
+ """Returns the string representation of the actual instance"""
143
+ return pprint.pformat(self.model_dump())
144
+
145
+
@@ -0,0 +1,94 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ FactPulse REST API
5
+
6
+ REST API for electronic invoicing in France: Factur-X, AFNOR PDP/PA, electronic signatures. ## 🎯 Main Features ### 📄 Factur-X Invoice Generation - **Formats**: XML only or PDF/A-3 with embedded XML - **Profiles**: MINIMUM, BASIC, EN16931, EXTENDED - **Standards**: EN 16931 (EU directive 2014/55), ISO 19005-3 (PDF/A-3), CII (UN/CEFACT) - **🆕 Simplified Format**: Generation from SIRET + auto-enrichment (Chorus Pro API + Business Search) ### ✅ Validation and Compliance - **XML Validation**: Schematron (45 to 210+ rules depending on profile) - **PDF Validation**: PDF/A-3, Factur-X XMP metadata, electronic signatures - **VeraPDF**: Strict PDF/A validation (146+ ISO 19005-3 rules) - **Asynchronous Processing**: Celery support for heavy validations (VeraPDF) ### 📡 AFNOR PDP/PA Integration (XP Z12-013) - **Flow Submission**: Send invoices to Partner Dematerialization Platforms - **Flow Search**: View submitted invoices - **Download**: Retrieve PDF/A-3 with XML - **Directory Service**: Company search (SIREN/SIRET) - **Multi-client**: Support for multiple PDP configs per user (stored credentials or zero-storage) ### ✍️ PDF Electronic Signature - **Standards**: PAdES-B-B, PAdES-B-T (RFC 3161 timestamping), PAdES-B-LT (long-term archival) - **eIDAS Levels**: SES (self-signed), AdES (commercial CA), QES (QTSP) - **Validation**: Cryptographic integrity and certificate verification - **Certificate Generation**: Self-signed X.509 certificates for testing ### 🔄 Asynchronous Processing - **Celery**: Asynchronous generation, validation and signing - **Polling**: Status tracking via `/tasks/{task_id}/status` - **No timeout**: Ideal for large files or heavy validations ## 🔒 Authentication All requests require a **JWT token** in the Authorization header: ``` Authorization: Bearer YOUR_JWT_TOKEN ``` ### How to obtain a JWT token? #### 🔑 Method 1: `/api/token/` API (Recommended) **URL:** `https://www.factpulse.fr/api/token/` This method is **recommended** for integration in your applications and CI/CD workflows. **Prerequisites:** Having set a password on your account **For users registered via email/password:** - You already have a password, use it directly **For users registered via OAuth (Google/GitHub):** - You must first set a password at: https://www.factpulse.fr/accounts/password/set/ - Once the password is created, you can use the API **Request example:** ```bash curl -X POST https://www.factpulse.fr/api/token/ \\ -H \"Content-Type: application/json\" \\ -d '{ \"username\": \"your_email@example.com\", \"password\": \"your_password\" }' ``` **Optional `client_uid` parameter:** To select credentials for a specific client (PA/PDP, Chorus Pro, signing certificates), add `client_uid`: ```bash curl -X POST https://www.factpulse.fr/api/token/ \\ -H \"Content-Type: application/json\" \\ -d '{ \"username\": \"your_email@example.com\", \"password\": \"your_password\", \"client_uid\": \"550e8400-e29b-41d4-a716-446655440000\" }' ``` The `client_uid` will be included in the JWT and allow the API to automatically use: - AFNOR/PDP credentials configured for this client - Chorus Pro credentials configured for this client - Electronic signature certificates configured for this client **Response:** ```json { \"access\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\", // Access token (validity: 30 min) \"refresh\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\" // Refresh token (validity: 7 days) } ``` **Advantages:** - ✅ Full automation (CI/CD, scripts) - ✅ Programmatic token management - ✅ Refresh token support for automatic access renewal - ✅ Easy integration in any language/tool #### 🖥️ Method 2: Dashboard Generation (Alternative) **URL:** https://www.factpulse.fr/dashboard/ This method is suitable for quick tests or occasional use via the graphical interface. **How it works:** - Log in to the dashboard - Use the \"Generate Test Token\" or \"Generate Production Token\" buttons - Works for **all** users (OAuth and email/password), without requiring a password **Token types:** - **Test Token**: 24h validity, 1000 calls/day quota (free) - **Production Token**: 7 days validity, quota based on your plan **Advantages:** - ✅ Quick for API testing - ✅ No password required - ✅ Simple visual interface **Disadvantages:** - ❌ Requires manual action - ❌ No refresh token - ❌ Less suited for automation ### 📚 Full Documentation For more information on authentication and API usage: https://www.factpulse.fr/documentation-api/
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ import pprint
17
+ import re # noqa: F401
18
+ import json
19
+
20
+ from pydantic import BaseModel, ConfigDict, StrictStr
21
+ from typing import Any, ClassVar, Dict, List, Optional
22
+ from typing import Optional, Set
23
+ from typing_extensions import Self
24
+
25
+ class InvoiceNote(BaseModel):
26
+ """
27
+ Invoice note (IncludedNote in Factur-X). Mandatory notes for BR-FR-05 are: - PMT: Fixed recovery fee - PMD: Late payment penalties - AAB: Early payment discount
28
+ """ # noqa: E501
29
+ subject_code: Optional[StrictStr] = None
30
+ content: StrictStr
31
+ __properties: ClassVar[List[str]] = ["subject_code", "content"]
32
+
33
+ model_config = ConfigDict(
34
+ populate_by_name=True,
35
+ validate_assignment=True,
36
+ protected_namespaces=(),
37
+ )
38
+
39
+
40
+ def to_str(self) -> str:
41
+ """Returns the string representation of the model using alias"""
42
+ return pprint.pformat(self.model_dump(by_alias=True))
43
+
44
+ def to_json(self) -> str:
45
+ """Returns the JSON representation of the model using alias"""
46
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
47
+ return json.dumps(self.to_dict())
48
+
49
+ @classmethod
50
+ def from_json(cls, json_str: str) -> Optional[Self]:
51
+ """Create an instance of InvoiceNote from a JSON string"""
52
+ return cls.from_dict(json.loads(json_str))
53
+
54
+ def to_dict(self) -> Dict[str, Any]:
55
+ """Return the dictionary representation of the model using alias.
56
+
57
+ This has the following differences from calling pydantic's
58
+ `self.model_dump(by_alias=True)`:
59
+
60
+ * `None` is only added to the output dict for nullable fields that
61
+ were set at model initialization. Other fields with value `None`
62
+ are ignored.
63
+ """
64
+ excluded_fields: Set[str] = set([
65
+ ])
66
+
67
+ _dict = self.model_dump(
68
+ by_alias=True,
69
+ exclude=excluded_fields,
70
+ exclude_none=True,
71
+ )
72
+ # set to None if subject_code (nullable) is None
73
+ # and model_fields_set contains the field
74
+ if self.subject_code is None and "subject_code" in self.model_fields_set:
75
+ _dict['subject_code'] = None
76
+
77
+ return _dict
78
+
79
+ @classmethod
80
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
81
+ """Create an instance of InvoiceNote from a dict"""
82
+ if obj is None:
83
+ return None
84
+
85
+ if not isinstance(obj, dict):
86
+ return cls.model_validate(obj)
87
+
88
+ _obj = cls.model_validate({
89
+ "subject_code": obj.get("subject_code"),
90
+ "content": obj.get("content")
91
+ })
92
+ return _obj
93
+
94
+