Coverage for src/henrri_connect/documents/asynchro.py: 82%
66 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-09 19:37 +0200
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-09 19:37 +0200
1"""
2Sous-client pour les endpoints /v1/documents.
4Classes:
5- ``henrri_connect.documents.asynchro.AsyncDocumentsClient``
6 Accès asynchrone aux endpoints documents.
8Notes:
9- Utiliser de préférence l'objet ``henrri_connect.AsyncHenrriClient`` pour acceder aux endpoints.
10"""
12from __future__ import annotations
14from typing import TYPE_CHECKING
16from ..models import (
17 Document,
18 DocumentQuery,
19 ListResponse,
20 PagedListResponse,
21 PaymentMilestone,
22 PdfUrlResponse,
23 TaxDetailArray,
24 ValidateDocumentRequest,
25)
26from ..utils import clean
28if TYPE_CHECKING:
29 from ..connect import AsyncHenrriClient
31DOCUMENTS_ENDPOINT = "/v1/documents"
34class AsyncDocumentsClient:
35 """
36 Accès asynchrone aux endpoints documents.
38 Arguments
39 - ``client`` : Objet ``henrri_connect.AsyncHenrriClient``.
41 Methods
42 - ``list_documents`` : Liste tous les documents.
43 - ``add`` : Ajoute un document.
44 - ``get`` : Accès au document par son ID.
45 - ``get_with_all`` : Accès au document par son ID avec toutes ses relations incluses.
46 - ``get_all_included`` : Accès au document par son ID avec toutes ses données incluses.
47 - ``modify`` : Modifie un document par son ID.
48 - ``delete`` : Supprime un document par son ID.
49 - ``get_tax_details`` : Accès aux taxes d'un document par son ID.
50 - ``validate`` : Valide un document.
51 - ``get_pdf_url`` : Accès au document par son ID et son GUID.
52 - ``get_pdf_bytes`` : Accès au document par son ID et son GUID.
53 - ``get_pdf_file`` : Accès au document par son ID et son GUID.
54 - ``get_display`` : Accès aux données d'affichage d'un document par son ID.
55 - ``get_payment_milestones`` : Accès aux milestones de paiement d'un document par son ID.
56 - ``finalize`` : Finalise un document.
57 - ``transform_to_invoice`` : Transforme un document (devis, bon de livraison...) en facture.
58 - ``get_next_code_batch`` : Accès au code de batch suivant.
59 - ``list_with_selected_fields`` : Liste les documents avec sélection de champs.
60 """
62 def __init__(self, client: AsyncHenrriClient) -> None:
63 self._c = client
65 async def list_documents(self, *, request: DocumentQuery) -> PagedListResponse[Document]:
66 """
67 Liste les documents avec pagination et filtres optionnels.
69 Arguments
70 - ``request`` (DocumentQuery) : Paramètres de recherche.
72 Returns
73 - ``PagedListResponse[Document]`` : Liste paginée de documents.
74 """
75 params = clean(request.model_dump(by_alias=True))
76 resp = await self._c.request("GET", DOCUMENTS_ENDPOINT, params=params)
77 return PagedListResponse[Document].model_validate(resp.json())
79 async def add(self, document: Document) -> Document:
80 """
81 Crée un nouveau document.
83 Arguments
84 - ``document`` (Document) : Document à créer.
86 Returns
87 - ``Document`` : Document créé.
88 """
89 resp = await self._c.request(
90 "POST",
91 DOCUMENTS_ENDPOINT,
92 json=document.model_dump(by_alias=True, exclude_unset=True, exclude_none=True),
93 )
94 return Document.model_validate(resp.json())
96 async def get(self, doc_id: int) -> Document:
97 """
98 Récupère un document par son identifiant.
100 Arguments
101 - ``doc_id`` (int) : Identifiant du document.
103 Returns
104 - ``Document`` : Document trouvé.
105 """
106 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}")
107 return Document.model_validate(resp.json())
109 async def get_with_all(self, doc_id: int) -> Document:
110 """
111 Récupère un document avec toutes ses relations incluses.
113 Arguments
114 - ``doc_id`` (int) : Identifiant du document.
116 Returns
117 - ``Document`` : Document avec toutes ses relations incluses.
118 """
119 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}/with-all")
120 return Document.model_validate(resp.json())
122 async def get_all_included(self, doc_id: int) -> Document:
123 """
124 Récupère un document avec toutes ses données incluses.
126 Arguments
127 - ``doc_id`` (int) : Identifiant du document.
129 Returns
130 - ``Document`` : Document avec toutes ses données incluses.
131 """
132 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}/all-included")
133 data = resp.json()
134 # L'API retourne parfois un objet enveloppé {"document": {...}, ...}
135 # Déballer automatiquement si nécessaire pour satisfaire le modèle `Document`.
136 if isinstance(data, dict) and "document" in data and isinstance(data["document"], dict):
137 data = data["document"]
138 return Document.model_validate(data)
140 async def modify(self, doc_id: int, document: Document) -> Document:
141 """
142 Met à jour un document existant.
144 Arguments
145 - ``doc_id`` (int) : Identifiant du document.
146 - ``document`` (Document) : Document à mettre à jour.
148 Returns
149 - ``Document`` : Document mis à jour.
150 """
151 resp = await self._c.request(
152 "PUT",
153 f"{DOCUMENTS_ENDPOINT}/{doc_id}",
154 json=document.model_dump(by_alias=True, exclude_unset=True, exclude_none=True),
155 )
156 return Document.model_validate(resp.json())
158 async def delete(self, doc_id: int) -> None:
159 """
160 Supprime un document.
162 Arguments
163 - ``doc_id`` (int) : Identifiant du document.
164 """
165 await self._c.request("DELETE", f"{DOCUMENTS_ENDPOINT}/{doc_id}")
167 async def get_tax_details(self, doc_id: int) -> TaxDetailArray:
168 """
169 Récupère le détail des taxes d'un document.
171 Arguments
172 - ``doc_id`` (int) : Identifiant du document.
174 Returns
175 - ``TaxDetailArray`` : Détail des taxes du document.
176 """
177 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}/tax-details")
178 return TaxDetailArray.model_validate(resp.json())
180 async def validate(self, doc_id: int, request: ValidateDocumentRequest) -> Document:
181 """
182 Valide électroniquement un document.
184 Arguments
185 - ``doc_id`` (int) : Identifiant du document.
186 - ``request`` (ValidateDocumentRequest) : Dossier à valider.
188 Returns
189 - ``Document`` : Document validé.
190 """
191 resp = await self._c.request(
192 "POST",
193 f"{DOCUMENTS_ENDPOINT}/{doc_id}/validate",
194 json=request.model_dump(by_alias=True, exclude_unset=True, exclude_none=True),
195 )
196 return Document.model_validate(resp.json())
198 async def get_pdf_url(self, doc_id: int) -> PdfUrlResponse:
199 """
200 Génère une URL de téléchargement pour le PDF d'un document.
202 Arguments
203 - ``doc_id`` (int) : Identifiant du document.
205 Returns
206 - ``PdfUrlResponse`` : URL de téléchargement du PDF du document.
207 """
208 resp = await self._c.request("POST", f"{DOCUMENTS_ENDPOINT}/{doc_id}/pdf/url")
209 return PdfUrlResponse.model_validate(resp.json())
211 async def get_pdf_bytes(self, doc_id: int) -> bytes:
212 """
213 Télécharge le PDF d'un document (retourne les octets bruts).
215 Arguments
216 - ``doc_id`` (int) : Identifiant du document.
218 Returns
219 - ``bytes`` : Octets bruts du PDF du document.
220 """
221 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}/pdf")
222 return resp.content
224 async def get_pdf_file(self, doc_id: int, guid: str) -> bytes:
225 """
226 Télécharge un fichier PDF identifié par son GUID.
228 Arguments
229 - ``doc_id`` (int) : Identifiant du document.
230 - ``guid`` (str) : GUID du fichier PDF.
232 Returns
233 - ``bytes`` : Octets bruts du fichier PDF.
234 """
235 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}/pdf/files/{guid}")
236 return resp.content
238 async def get_display(self, doc_id: int) -> Document:
239 """
240 Récupère les données d'affichage d'un document.
242 Arguments
243 - ``doc_id`` (int) : Identifiant du document.
245 Returns
246 - ``Document`` : Document avec les données d'affichage.
247 """
248 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}/display")
249 return Document.model_validate(resp.json())
251 async def get_payment_milestones(self, doc_id: int) -> ListResponse[PaymentMilestone]:
252 """
253 Récupère les jalons de paiement d'un document.
255 Arguments
256 - ``doc_id`` (int) : Identifiant du document.
258 Returns
259 - ``ListResponse[PaymentMilestone]`` : Liste des jalons de paiement du document.
260 """
261 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/{doc_id}/paymentmilestones")
262 return ListResponse[PaymentMilestone].model_validate(resp.json())
264 async def finalize(self, doc_id: int) -> Document:
265 """
266 Finalise un document.
268 Arguments
269 - ``doc_id`` (int) : Identifiant du document.
271 Returns
272 - ``Document`` : Document finalisé.
273 """
274 resp = await self._c.request("POST", f"{DOCUMENTS_ENDPOINT}/{doc_id}/finalize")
275 return Document.model_validate(resp.json())
277 async def transform_to_invoice(self, doc_id: int) -> Document:
278 """
279 Transforme un document (devis, bon de livraison…) en facture.
281 Arguments
282 - ``doc_id`` (int) : Identifiant du document.
284 Returns
285 - ``Document`` : Document transformé en facture.
286 """
287 resp = await self._c.request("POST", f"{DOCUMENTS_ENDPOINT}/{doc_id}/transform-to-invoice")
288 return Document.model_validate(resp.json())
290 async def get_next_quote_batch(self) -> object:
291 """
292 Récupère le prochain numéro de lot pour un devis.
294 Returns
295 - ``object`` : Numéro de lot.
296 """
297 resp = await self._c.request("GET", f"{DOCUMENTS_ENDPOINT}/next-quote-batch")
298 return resp.json()
300 async def list_with_selected_fields(
301 self,
302 *,
303 page: int = 1,
304 limit: int = 50,
305 fields: str | None = None,
306 **kwargs: object,
307 ) -> PagedListResponse[Document]:
308 """
309 Liste les documents avec sélection de champs.
311 Arguments
312 - ``page`` (int) : Numéro de page (par défaut 1).
313 - ``limit`` (int) : Nombre de documents par page (par défaut 50).
314 - ``fields`` (str | None) : Champs à retourner (par défaut None).
315 - ``**kwargs`` (object) : Autres paramètres.
317 Returns
318 - ``PagedListResponse[Document]`` : Liste de documents.
319 """
320 params = clean({"page": page, "limit": limit, "fields": fields, **kwargs})
321 resp = await self._c.request(
322 "GET",
323 f"{DOCUMENTS_ENDPOINT}/with-selected-fields",
324 params=params
325 )
326 return PagedListResponse[Document].model_validate(resp.json())