From 8b39c73dbf646fdce6e9761ad4270fd51e86853b Mon Sep 17 00:00:00 2001 From: Thales Tomme Date: Thu, 22 Jan 2026 18:13:36 +0100 Subject: [PATCH 1/2] Added fetch pdf for invoice --- tests/test_invoices.py | 7 +++++++ twikey/invoice.py | 35 +++++++++++++++++++++++++++++++- twikey/model/invoice_response.py | 16 +++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 91c6985..4358afb 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -4,6 +4,8 @@ import time import uuid from datetime import date, timedelta +from dotenv import load_dotenv +load_dotenv() from twikey.model.invoice_request import Customer, InvoiceRequest, LineItem, UpdateInvoiceRequest, DetailsRequest, \ ActionRequest, ActionType, UblUploadRequest, BulkInvoiceRequest @@ -220,6 +222,11 @@ def test_feed(self): def test_payments(self): self._twikey.invoice.payment(MyPayments(), False) + def test_retrieve_pdf(self): + retrieved_pdf = self._twikey.invoice.retrieve_pdf(os.environ["INVOICEID"]) + retrieved_pdf.save("/tmp/pdf.pdf") + self.assertIsNotNone(retrieved_pdf) + class MyFeed(twikey.InvoiceFeed): def invoice(self, invoice:Invoice): diff --git a/twikey/invoice.py b/twikey/invoice.py index a0ec09f..f9e232b 100644 --- a/twikey/invoice.py +++ b/twikey/invoice.py @@ -5,7 +5,7 @@ from .model.invoice_request import InvoiceRequest, UpdateInvoiceRequest, DetailsRequest, ActionRequest, \ UblUploadRequest, BulkInvoiceRequest from .model.invoice_response import Event, Invoice, BulkInvoiceResponse, \ - BulkBatchDetailsResponse, InvoiceFeed, PaymentFeed + BulkBatchDetailsResponse, InvoiceFeed, PaymentFeed, PdfResponse class InvoiceService(object): def __init__(self, client) -> None: @@ -433,3 +433,36 @@ def payment(self, payment_feed: PaymentFeed, start_position=False): except requests.exceptions.RequestException as e: raise self.client.raise_error_from_request("Payment feed", e) + def retrieve_pdf(self, invoice_id: str) -> PdfResponse: + """ + See https://www.twikey.com/api/#retrieve-invoice-pdf + + retrieve the PDF of an invoice via GET request to the API. + + Args: + invoice_id (str): The UUID of the invoice + + Returns: + PdfResponse: A structured response object representing the server’s reply. + + Raises: + Exception: If the request to the PDF endpoint fails or response is invalid. + """ + + url = self.client.instance_url(f"/invoice/{invoice_id}/pdf") + try: + self.client.refresh_token_if_required() + response = requests.get( + url=url, headers=self.client.headers(), timeout=15 + ) + if "ApiErrorCode" in response.headers: + raise self.client.raise_error("pdf", response) + filename = None + if "Content-Disposition" in response.headers: + disposition = response.headers["Content-Disposition"] + parts = disposition.split("=") + if len(parts) == 2: + filename = parts[1].strip().strip('"') + return PdfResponse(content=response.content, filename=filename) + except requests.exceptions.RequestException as e: + raise self.client.raise_error_from_request("detail", e) \ No newline at end of file diff --git a/twikey/model/invoice_response.py b/twikey/model/invoice_response.py index 59b61c5..b0f2624 100644 --- a/twikey/model/invoice_response.py +++ b/twikey/model/invoice_response.py @@ -336,3 +336,19 @@ def __init__(self, raw: list): def __str__(self): return "\n".join(str(item) for item in self.results) + + +class PdfResponse: + def __init__(self, content: bytes, filename: str = None, content_type: str = "application/pdf"): + self.content = content + self.content_type = content_type + self.filename = filename or "invoice.pdf" + + def save(self, path: str = None): + path = path or self.filename + with open(path, "wb") as f: + f.write(self.content) + return path + + def __str__(self): + return f"PdfResponse(filename='{self.filename}', size={len(self.content)} bytes)" \ No newline at end of file From 93e287921c75a87ec57ecc38a2b8d4b58e119494 Mon Sep 17 00:00:00 2001 From: Thales Tomme Date: Thu, 22 Jan 2026 18:17:28 +0100 Subject: [PATCH 2/2] Added fetch pdf for invoice --- twikey/invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twikey/invoice.py b/twikey/invoice.py index f9e232b..306e0c8 100644 --- a/twikey/invoice.py +++ b/twikey/invoice.py @@ -437,7 +437,7 @@ def retrieve_pdf(self, invoice_id: str) -> PdfResponse: """ See https://www.twikey.com/api/#retrieve-invoice-pdf - retrieve the PDF of an invoice via GET request to the API. + retrieve the PDF of an invoice via GET request to the API Args: invoice_id (str): The UUID of the invoice