From 2965786b7e801854e799e8c9d725b6ef86ef00a3 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Mon, 19 Aug 2024 15:39:24 +0100 Subject: [PATCH] feat(dunning): Add payment requests endpoint (find_all) (#258) * feat(dunning): Add payment requests endpoint (find_all) * Remove not used import --- lago_python_client/client.py | 5 ++ lago_python_client/models/payment_request.py | 46 +++++++++++++ .../payment_requests/clients.py | 22 ++++++ tests/fixtures/payment_request_index.json | 67 +++++++++++++++++++ tests/test_payment_request_client.py | 45 +++++++++++++ 5 files changed, 185 insertions(+) create mode 100644 lago_python_client/models/payment_request.py create mode 100644 lago_python_client/payment_requests/clients.py create mode 100644 tests/fixtures/payment_request_index.json create mode 100644 tests/test_payment_request_client.py diff --git a/lago_python_client/client.py b/lago_python_client/client.py index 41b78a22..864eeaef 100644 --- a/lago_python_client/client.py +++ b/lago_python_client/client.py @@ -14,6 +14,7 @@ from .mrrs.clients import MrrClient from .organizations.clients import OrganizationClient from .overdue_balances.clients import OverdueBalanceClient +from .payment_requests.clients import PaymentRequestClient from .invoice_collections.clients import InvoiceCollectionClient from .plans.clients import PlanClient from .subscriptions.clients import SubscriptionClient @@ -96,6 +97,10 @@ def organizations(self) -> OrganizationClient: def overdue_balances(self) -> OverdueBalanceClient: return OverdueBalanceClient(self.base_api_url, self.api_key) + @callable_cached_property + def payment_requests(self) -> PaymentRequestClient: + return PaymentRequestClient(self.base_api_url, self.api_key) + @callable_cached_property def invoice_collections(self) -> InvoiceCollectionClient: return InvoiceCollectionClient(self.base_api_url, self.api_key) diff --git a/lago_python_client/models/payment_request.py b/lago_python_client/models/payment_request.py new file mode 100644 index 00000000..2bd67469 --- /dev/null +++ b/lago_python_client/models/payment_request.py @@ -0,0 +1,46 @@ +from typing import List, Optional + +from ..base_model import BaseModel, BaseResponseModel +from .customer import CustomerResponse + +class PaymentRequestInvoiceResponse(BaseResponseModel): + lago_id: str + sequential_id: Optional[int] + number: str + issuing_date: Optional[str] + payment_dispute_lost_at: Optional[str] + payment_due_date: Optional[str] + payment_overdue: bool + net_payment_term: int + invoice_type: str + version_number: int + status: str + payment_status: str + currency: str + fees_amount_cents: int + coupons_amount_cents: int + taxes_amount_cents: int + credit_notes_amount_cents: int + sub_total_excluding_taxes_amount_cents: int + sub_total_including_taxes_amount_cents: int + total_amount_cents: int + prepaid_credit_amount_cents: int + file_url: Optional[str] + + +class PaymentRequestInvoicesResponse(BaseResponseModel): + __root__: List[PaymentRequestInvoiceResponse] + + +class PaymentRequestResponse(BaseResponseModel): + lago_id: str + email: str + amount_cents: int + amount_currency: str + created_at: str + customer: Optional[CustomerResponse] + invoices: Optional[PaymentRequestInvoicesResponse] + + +class PaymentRequestsResponse(BaseResponseModel): + __root__: List[PaymentRequestResponse] diff --git a/lago_python_client/payment_requests/clients.py b/lago_python_client/payment_requests/clients.py new file mode 100644 index 00000000..0eb2e73d --- /dev/null +++ b/lago_python_client/payment_requests/clients.py @@ -0,0 +1,22 @@ +import sys +from typing import Any, ClassVar, Type, Union + +from ..base_client import BaseClient +from ..mixins import FindAllCommandMixin +from ..models.payment_request import PaymentRequestResponse +from ..services.request import make_headers, make_url, send_get_request +from ..services.response import get_response_data, prepare_index_response, Response + +if sys.version_info >= (3, 9): + from collections.abc import Mapping +else: + from typing import Mapping + + +class PaymentRequestClient( + FindAllCommandMixin[PaymentRequestResponse], + BaseClient, +): + API_RESOURCE: ClassVar[str] = 'payment_requests' + RESPONSE_MODEL: ClassVar[Type[PaymentRequestResponse]] = PaymentRequestResponse + ROOT_NAME: ClassVar[str] = 'payment_request' diff --git a/tests/fixtures/payment_request_index.json b/tests/fixtures/payment_request_index.json new file mode 100644 index 00000000..1cdab53b --- /dev/null +++ b/tests/fixtures/payment_request_index.json @@ -0,0 +1,67 @@ +{ + "payment_requests": [ + { + "lago_id": "89b6b61e-4dbc-4307-ac96-4abcfa9e3e2d", + "email": "gavin@overdue.test", + "amount_cents": 120, + "amount_currency": "EUR", + "created_at": "2024-06-30T10:59:51Z", + "customer": { + "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "external_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "address_line1": "5230 Penfield Ave", + "address_line2": null, + "city": "Woodland Hills", + "country": "US", + "created_at": "2022-04-29T08:59:51Z", + "updated_at": "2022-04-29T08:59:51Z", + "email": "dinesh@piedpiper.test", + "legal_name": "Coleman-Blair", + "legal_number": "49-008-2965", + "logo_url": "http://hooli.com/logo.png", + "name": "Gavin Belson", + "phone": "1-171-883-3711 x245", + "state": "CA", + "url": "http://hooli.com", + "zipcode": "91364", + "currency": "EUR", + "timezone": "Europe/Paris", + "applicable_timezone": "Europe/Paris", + "billing_configuration": { + "payment_provider": "stripe", + "payment_provider_code": "stripe-eu-1", + "provider_customer_id": "cus_12345" + }, + "metadata": [] + }, + "invoices": [ + { + "lago_id": "1a901a90-1a90-1a90-1a90-1a901a901a90", + "sequential_id": 15, + "number": "LAG-1234-001-002", + "issuing_date": "2022-06-02", + "payment_dispute_lost_at": "2022-04-29T08:59:51Z", + "payment_due_date": "2022-06-02", + "payment_overdue": true, + "invoice_type": "one_off", + "version_number": 2, + "status": "finalized", + "payment_status": "pending", + "currency": "EUR", + "net_payment_term": 0, + "fees_amount_cents": 100, + "taxes_amount_cents": 20, + "coupons_amount_cents": 0, + "credit_notes_amount_cents": 0, + "sub_total_excluding_taxes_amount_cents": 100, + "sub_total_including_taxes_amount_cents": 120, + "prepaid_credit_amount_cents": 0, + "total_amount_cents": 120 + } + ] + } + ], + "meta": { + "current_page": 1 + } +} diff --git a/tests/test_payment_request_client.py b/tests/test_payment_request_client.py new file mode 100644 index 00000000..6e04f2a7 --- /dev/null +++ b/tests/test_payment_request_client.py @@ -0,0 +1,45 @@ +import os + +import pytest +from pytest_httpx import HTTPXMock + +from lago_python_client.client import Client +from lago_python_client.exceptions import LagoApiError +from lago_python_client.models.payment_request import PaymentRequestResponse + + +def mock_collection_response(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + data_path = os.path.join(current_dir, 'fixtures/payment_request_index.json') + + with open(data_path, 'rb') as payment_requests_response: + return payment_requests_response.read() + + +def test_valid_find_all_payment_requests_request(httpx_mock: HTTPXMock): + client = Client(api_key='886fe239-927d-4072-ab72-6dd345e8dd0d') + + httpx_mock.add_response(method='GET', url='https://api.getlago.com/api/v1/payment_requests', content=mock_collection_response()) + response = client.payment_requests.find_all() + + assert response['payment_requests'][0].lago_id == '89b6b61e-4dbc-4307-ac96-4abcfa9e3e2d' + assert response['meta']['current_page'] == 1 + + +def test_valid_find_all_payment_requests_request_with_options(httpx_mock: HTTPXMock): + client = Client(api_key='886fe239-927d-4072-ab72-6dd345e8dd0d') + + httpx_mock.add_response(method='GET', url='https://api.getlago.com/api/v1/payment_requests?per_page=2&page=1', content=mock_collection_response()) + response = client.payment_requests.find_all({'per_page': 2, 'page': 1}) + + assert response['payment_requests'][0].lago_id == '89b6b61e-4dbc-4307-ac96-4abcfa9e3e2d' + assert response['meta']['current_page'] == 1 + + +def test_invalid_find_all_payment_requests_request(httpx_mock: HTTPXMock): + client = Client(api_key='invalid') + + httpx_mock.add_response(method='GET', url='https://api.getlago.com/api/v1/payment_requests', status_code=404, content=b'') + + with pytest.raises(LagoApiError): + client.payment_requests.find_all()