Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dunning): Add payment requests endpoint (find_all) #258

Merged
merged 2 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lago_python_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
46 changes: 46 additions & 0 deletions lago_python_client/models/payment_request.py
Original file line number Diff line number Diff line change
@@ -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]
22 changes: 22 additions & 0 deletions lago_python_client/payment_requests/clients.py
Original file line number Diff line number Diff line change
@@ -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'
67 changes: 67 additions & 0 deletions tests/fixtures/payment_request_index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"payment_requests": [
{
"lago_id": "89b6b61e-4dbc-4307-ac96-4abcfa9e3e2d",
"email": "[email protected]",
"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": "[email protected]",
"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
}
}
45 changes: 45 additions & 0 deletions tests/test_payment_request_client.py
Original file line number Diff line number Diff line change
@@ -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()
Loading