From 0dbe188b113a90c1bd781f7e5cd687029987966c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:01:38 +0000 Subject: [PATCH 1/7] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index fd0cb7e8..c985d05c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 176 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-c24eebe942f400bff8922a6fbef1ce551ad14f61eb4da21b50d823a62ca42586.yml openapi_spec_hash: b79ed927e625dedff69cea29131a34d9 -config_hash: 693dddc4721eef512d75ab6c60897794 +config_hash: fbc424e01cca916048d63adcadaa8750 From ec268b7426be15578b2eaf92ca81107cbf9084e6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:38:12 +0000 Subject: [PATCH 2/7] chore: update mock server docs --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d68a8975..e758caa0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,8 +88,7 @@ $ pip install ./path-to-wheel-file.whl Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. ```sh -# you will need npm installed -$ npx prism mock path/to/your/openapi.yml +$ ./scripts/mock ``` ```sh From c4a37de9f0f7821e54d05702c4d1d809124fc7bc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 17:49:43 +0000 Subject: [PATCH 3/7] feat(api): Add INTEREST_AND_FEES_PAUSED substatus to financial account --- .stats.yml | 4 ++-- .../financial_accounts/financial_accounts.py | 20 +++++++++++++++++-- src/lithic/types/financial_account.py | 9 ++++++++- .../financial_account_update_status_params.py | 10 +++++++++- .../types/financial_accounts/loan_tape.py | 9 ++++++++- .../types/financial_accounts/statement.py | 9 ++++++++- 6 files changed, 53 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index c985d05c..2f34650e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 176 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-c24eebe942f400bff8922a6fbef1ce551ad14f61eb4da21b50d823a62ca42586.yml -openapi_spec_hash: b79ed927e625dedff69cea29131a34d9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-f85b60190db68921a3a877d0dd931670c27933ba1f5031fcdd27365e99adb5c9.yml +openapi_spec_hash: 4828c2dc7543ce2a39774a9921c73c80 config_hash: fbc424e01cca916048d63adcadaa8750 diff --git a/src/lithic/resources/financial_accounts/financial_accounts.py b/src/lithic/resources/financial_accounts/financial_accounts.py index af68c9d5..e42cc408 100644 --- a/src/lithic/resources/financial_accounts/financial_accounts.py +++ b/src/lithic/resources/financial_accounts/financial_accounts.py @@ -319,7 +319,15 @@ def update_status( financial_account_token: str, *, status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"], - substatus: Optional[Literal["CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "CHARGED_OFF_DELINQUENT"]], + substatus: Optional[ + Literal[ + "CHARGED_OFF_FRAUD", + "END_USER_REQUEST", + "BANK_REQUEST", + "CHARGED_OFF_DELINQUENT", + "INTEREST_AND_FEES_PAUSED", + ] + ], user_defined_status: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -621,7 +629,15 @@ async def update_status( financial_account_token: str, *, status: Literal["OPEN", "CLOSED", "SUSPENDED", "PENDING"], - substatus: Optional[Literal["CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "CHARGED_OFF_DELINQUENT"]], + substatus: Optional[ + Literal[ + "CHARGED_OFF_FRAUD", + "END_USER_REQUEST", + "BANK_REQUEST", + "CHARGED_OFF_DELINQUENT", + "INTEREST_AND_FEES_PAUSED", + ] + ], user_defined_status: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/lithic/types/financial_account.py b/src/lithic/types/financial_account.py index 9c232fa6..92e7ddd4 100644 --- a/src/lithic/types/financial_account.py +++ b/src/lithic/types/financial_account.py @@ -47,7 +47,14 @@ class FinancialAccount(BaseModel): """Status of the financial account""" substatus: Optional[ - Literal["CHARGED_OFF_DELINQUENT", "CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "DELINQUENT"] + Literal[ + "CHARGED_OFF_DELINQUENT", + "CHARGED_OFF_FRAUD", + "END_USER_REQUEST", + "BANK_REQUEST", + "DELINQUENT", + "INTEREST_AND_FEES_PAUSED", + ] ] = None """Substatus for the financial account""" diff --git a/src/lithic/types/financial_account_update_status_params.py b/src/lithic/types/financial_account_update_status_params.py index cc2d79a8..5680efee 100644 --- a/src/lithic/types/financial_account_update_status_params.py +++ b/src/lithic/types/financial_account_update_status_params.py @@ -13,7 +13,15 @@ class FinancialAccountUpdateStatusParams(TypedDict, total=False): """Status of the financial account""" substatus: Required[ - Optional[Literal["CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "CHARGED_OFF_DELINQUENT"]] + Optional[ + Literal[ + "CHARGED_OFF_FRAUD", + "END_USER_REQUEST", + "BANK_REQUEST", + "CHARGED_OFF_DELINQUENT", + "INTEREST_AND_FEES_PAUSED", + ] + ] ] """Substatus for the financial account""" diff --git a/src/lithic/types/financial_accounts/loan_tape.py b/src/lithic/types/financial_accounts/loan_tape.py index 6c16a8ca..583f98cd 100644 --- a/src/lithic/types/financial_accounts/loan_tape.py +++ b/src/lithic/types/financial_accounts/loan_tape.py @@ -28,7 +28,14 @@ class AccountStandingFinancialAccountState(BaseModel): """Status of the financial account""" substatus: Optional[ - Literal["CHARGED_OFF_DELINQUENT", "CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "DELINQUENT"] + Literal[ + "CHARGED_OFF_DELINQUENT", + "CHARGED_OFF_FRAUD", + "END_USER_REQUEST", + "BANK_REQUEST", + "DELINQUENT", + "INTEREST_AND_FEES_PAUSED", + ] ] = None """Substatus for the financial account""" diff --git a/src/lithic/types/financial_accounts/statement.py b/src/lithic/types/financial_accounts/statement.py index 37b8884d..513630fb 100644 --- a/src/lithic/types/financial_accounts/statement.py +++ b/src/lithic/types/financial_accounts/statement.py @@ -25,7 +25,14 @@ class AccountStandingFinancialAccountState(BaseModel): """Status of the financial account""" substatus: Optional[ - Literal["CHARGED_OFF_DELINQUENT", "CHARGED_OFF_FRAUD", "END_USER_REQUEST", "BANK_REQUEST", "DELINQUENT"] + Literal[ + "CHARGED_OFF_DELINQUENT", + "CHARGED_OFF_FRAUD", + "END_USER_REQUEST", + "BANK_REQUEST", + "DELINQUENT", + "INTEREST_AND_FEES_PAUSED", + ] ] = None """Substatus for the financial account""" From 1b49e550b0e6fe3c5203740a9634ee6264046489 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:31:42 +0000 Subject: [PATCH 4/7] chore(internal): add request options to SSE classes --- src/lithic/_legacy_response.py | 3 +++ src/lithic/_response.py | 3 +++ src/lithic/_streaming.py | 11 ++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/lithic/_legacy_response.py b/src/lithic/_legacy_response.py index d3758f2b..415c593c 100644 --- a/src/lithic/_legacy_response.py +++ b/src/lithic/_legacy_response.py @@ -214,6 +214,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -224,6 +225,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -237,6 +239,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/lithic/_response.py b/src/lithic/_response.py index 083b08df..f19ac6bc 100644 --- a/src/lithic/_response.py +++ b/src/lithic/_response.py @@ -152,6 +152,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -162,6 +163,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -175,6 +177,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/lithic/_streaming.py b/src/lithic/_streaming.py index 73afbbec..38d8cb5f 100644 --- a/src/lithic/_streaming.py +++ b/src/lithic/_streaming.py @@ -4,7 +4,7 @@ import json import inspect from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx @@ -13,6 +13,7 @@ if TYPE_CHECKING: from ._client import Lithic, AsyncLithic + from ._models import FinalRequestOptions _T = TypeVar("_T") @@ -22,7 +23,7 @@ class Stream(Generic[_T]): """Provides the core interface to iterate over a synchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEBytesDecoder def __init__( @@ -31,10 +32,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: Lithic, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() @@ -85,7 +88,7 @@ class AsyncStream(Generic[_T]): """Provides the core interface to iterate over an asynchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEDecoder | SSEBytesDecoder def __init__( @@ -94,10 +97,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: AsyncLithic, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() From 7dae7892de61a765ab4bca8c7af3046d90fe2ff4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:09:25 +0000 Subject: [PATCH 5/7] chore(dependencies): require standardwebhooks 1.0.1 this release includes an important bug fix. --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- requirements.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5f325713..b175a6da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ Repository = "https://github.com/lithic-com/lithic-python" [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] -webhooks = ["standardwebhooks"] +webhooks = ["standardwebhooks >= 1.0.1, < 2"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index 197f41b6..61575831 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -125,7 +125,7 @@ six==1.17.0 # via python-dateutil sniffio==1.3.1 # via lithic -standardwebhooks==1.0.0 +standardwebhooks==1.0.1 # via lithic time-machine==2.19.0 tomli==2.4.0 diff --git a/requirements.lock b/requirements.lock index 01e8660f..3ae4c2cf 100644 --- a/requirements.lock +++ b/requirements.lock @@ -69,7 +69,7 @@ six==1.17.0 # via python-dateutil sniffio==1.3.1 # via lithic -standardwebhooks==1.0.0 +standardwebhooks==1.0.1 # via lithic types-deprecated==1.3.1.20251101 # via standardwebhooks From 5c6bcd740933255291c0fd0b482021c4a50d0e78 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 18:30:15 +0000 Subject: [PATCH 6/7] feat(api): Expose MIL interest schedules and loan tape configuration endpoints --- .stats.yml | 8 +- api.md | 28 + .../resources/financial_accounts/__init__.py | 28 + .../financial_accounts/financial_accounts.py | 64 ++ .../interest_tier_schedule.py | 681 ++++++++++++++++++ .../loan_tape_configuration.py | 167 +++++ .../types/financial_accounts/__init__.py | 6 + .../interest_tier_schedule.py | 27 + .../interest_tier_schedule_create_params.py | 28 + .../interest_tier_schedule_list_params.py | 22 + .../interest_tier_schedule_update_params.py | 20 + .../loan_tape_configuration.py | 28 + .../loan_tape_rebuild_configuration.py | 21 + .../test_interest_tier_schedule.py | 566 +++++++++++++++ .../test_loan_tape_configuration.py | 104 +++ 15 files changed, 1794 insertions(+), 4 deletions(-) create mode 100644 src/lithic/resources/financial_accounts/interest_tier_schedule.py create mode 100644 src/lithic/resources/financial_accounts/loan_tape_configuration.py create mode 100644 src/lithic/types/financial_accounts/interest_tier_schedule.py create mode 100644 src/lithic/types/financial_accounts/interest_tier_schedule_create_params.py create mode 100644 src/lithic/types/financial_accounts/interest_tier_schedule_list_params.py create mode 100644 src/lithic/types/financial_accounts/interest_tier_schedule_update_params.py create mode 100644 src/lithic/types/financial_accounts/loan_tape_configuration.py create mode 100644 src/lithic/types/financial_accounts/loan_tape_rebuild_configuration.py create mode 100644 tests/api_resources/financial_accounts/test_interest_tier_schedule.py create mode 100644 tests/api_resources/financial_accounts/test_loan_tape_configuration.py diff --git a/.stats.yml b/.stats.yml index 2f34650e..258b775f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 176 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-f85b60190db68921a3a877d0dd931670c27933ba1f5031fcdd27365e99adb5c9.yml -openapi_spec_hash: 4828c2dc7543ce2a39774a9921c73c80 -config_hash: fbc424e01cca916048d63adcadaa8750 +configured_endpoints: 182 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-f99894d5b6eda608756c9e5e9868c81c4ce8c74c4d8958370cc3799766a13d65.yml +openapi_spec_hash: 2f364e16b58e5a9759fc9f772cb33f3c +config_hash: 2ee394874b7eb4cbe06f044b7376a6ba diff --git a/api.md b/api.md index 92414f8a..18672599 100644 --- a/api.md +++ b/api.md @@ -420,6 +420,34 @@ Methods: - client.financial_accounts.loan_tapes.retrieve(loan_tape_token, \*, financial_account_token) -> LoanTape - client.financial_accounts.loan_tapes.list(financial_account_token, \*\*params) -> SyncCursorPage[LoanTape] +## LoanTapeConfiguration + +Types: + +```python +from lithic.types.financial_accounts import LoanTapeConfiguration, LoanTapeRebuildConfiguration +``` + +Methods: + +- client.financial_accounts.loan_tape_configuration.retrieve(financial_account_token) -> LoanTapeConfiguration + +## InterestTierSchedule + +Types: + +```python +from lithic.types.financial_accounts import CategoryTier, InterestTierSchedule +``` + +Methods: + +- client.financial_accounts.interest_tier_schedule.create(financial_account_token, \*\*params) -> InterestTierSchedule +- client.financial_accounts.interest_tier_schedule.retrieve(effective_date, \*, financial_account_token) -> InterestTierSchedule +- client.financial_accounts.interest_tier_schedule.update(effective_date, \*, financial_account_token, \*\*params) -> InterestTierSchedule +- client.financial_accounts.interest_tier_schedule.list(financial_account_token, \*\*params) -> SyncSinglePage[InterestTierSchedule] +- client.financial_accounts.interest_tier_schedule.delete(effective_date, \*, financial_account_token) -> None + # Transactions Types: diff --git a/src/lithic/resources/financial_accounts/__init__.py b/src/lithic/resources/financial_accounts/__init__.py index ad35c907..74f0a994 100644 --- a/src/lithic/resources/financial_accounts/__init__.py +++ b/src/lithic/resources/financial_accounts/__init__.py @@ -48,6 +48,22 @@ FinancialTransactionsWithStreamingResponse, AsyncFinancialTransactionsWithStreamingResponse, ) +from .interest_tier_schedule import ( + InterestTierScheduleResource, + AsyncInterestTierScheduleResource, + InterestTierScheduleResourceWithRawResponse, + AsyncInterestTierScheduleResourceWithRawResponse, + InterestTierScheduleResourceWithStreamingResponse, + AsyncInterestTierScheduleResourceWithStreamingResponse, +) +from .loan_tape_configuration import ( + LoanTapeConfigurationResource, + AsyncLoanTapeConfigurationResource, + LoanTapeConfigurationResourceWithRawResponse, + AsyncLoanTapeConfigurationResourceWithRawResponse, + LoanTapeConfigurationResourceWithStreamingResponse, + AsyncLoanTapeConfigurationResourceWithStreamingResponse, +) __all__ = [ "Balances", @@ -80,6 +96,18 @@ "AsyncLoanTapesWithRawResponse", "LoanTapesWithStreamingResponse", "AsyncLoanTapesWithStreamingResponse", + "LoanTapeConfigurationResource", + "AsyncLoanTapeConfigurationResource", + "LoanTapeConfigurationResourceWithRawResponse", + "AsyncLoanTapeConfigurationResourceWithRawResponse", + "LoanTapeConfigurationResourceWithStreamingResponse", + "AsyncLoanTapeConfigurationResourceWithStreamingResponse", + "InterestTierScheduleResource", + "AsyncInterestTierScheduleResource", + "InterestTierScheduleResourceWithRawResponse", + "AsyncInterestTierScheduleResourceWithRawResponse", + "InterestTierScheduleResourceWithStreamingResponse", + "AsyncInterestTierScheduleResourceWithStreamingResponse", "FinancialAccounts", "AsyncFinancialAccounts", "FinancialAccountsWithRawResponse", diff --git a/src/lithic/resources/financial_accounts/financial_accounts.py b/src/lithic/resources/financial_accounts/financial_accounts.py index e42cc408..e26c023c 100644 --- a/src/lithic/resources/financial_accounts/financial_accounts.py +++ b/src/lithic/resources/financial_accounts/financial_accounts.py @@ -62,6 +62,22 @@ FinancialTransactionsWithStreamingResponse, AsyncFinancialTransactionsWithStreamingResponse, ) +from .interest_tier_schedule import ( + InterestTierScheduleResource, + AsyncInterestTierScheduleResource, + InterestTierScheduleResourceWithRawResponse, + AsyncInterestTierScheduleResourceWithRawResponse, + InterestTierScheduleResourceWithStreamingResponse, + AsyncInterestTierScheduleResourceWithStreamingResponse, +) +from .loan_tape_configuration import ( + LoanTapeConfigurationResource, + AsyncLoanTapeConfigurationResource, + LoanTapeConfigurationResourceWithRawResponse, + AsyncLoanTapeConfigurationResourceWithRawResponse, + LoanTapeConfigurationResourceWithStreamingResponse, + AsyncLoanTapeConfigurationResourceWithStreamingResponse, +) from ...types.financial_account import FinancialAccount __all__ = ["FinancialAccounts", "AsyncFinancialAccounts"] @@ -88,6 +104,14 @@ def statements(self) -> Statements: def loan_tapes(self) -> LoanTapes: return LoanTapes(self._client) + @cached_property + def loan_tape_configuration(self) -> LoanTapeConfigurationResource: + return LoanTapeConfigurationResource(self._client) + + @cached_property + def interest_tier_schedule(self) -> InterestTierScheduleResource: + return InterestTierScheduleResource(self._client) + @cached_property def with_raw_response(self) -> FinancialAccountsWithRawResponse: """ @@ -396,6 +420,14 @@ def statements(self) -> AsyncStatements: def loan_tapes(self) -> AsyncLoanTapes: return AsyncLoanTapes(self._client) + @cached_property + def loan_tape_configuration(self) -> AsyncLoanTapeConfigurationResource: + return AsyncLoanTapeConfigurationResource(self._client) + + @cached_property + def interest_tier_schedule(self) -> AsyncInterestTierScheduleResource: + return AsyncInterestTierScheduleResource(self._client) + @cached_property def with_raw_response(self) -> AsyncFinancialAccountsWithRawResponse: """ @@ -728,6 +760,14 @@ def statements(self) -> StatementsWithRawResponse: def loan_tapes(self) -> LoanTapesWithRawResponse: return LoanTapesWithRawResponse(self._financial_accounts.loan_tapes) + @cached_property + def loan_tape_configuration(self) -> LoanTapeConfigurationResourceWithRawResponse: + return LoanTapeConfigurationResourceWithRawResponse(self._financial_accounts.loan_tape_configuration) + + @cached_property + def interest_tier_schedule(self) -> InterestTierScheduleResourceWithRawResponse: + return InterestTierScheduleResourceWithRawResponse(self._financial_accounts.interest_tier_schedule) + class AsyncFinancialAccountsWithRawResponse: def __init__(self, financial_accounts: AsyncFinancialAccounts) -> None: @@ -772,6 +812,14 @@ def statements(self) -> AsyncStatementsWithRawResponse: def loan_tapes(self) -> AsyncLoanTapesWithRawResponse: return AsyncLoanTapesWithRawResponse(self._financial_accounts.loan_tapes) + @cached_property + def loan_tape_configuration(self) -> AsyncLoanTapeConfigurationResourceWithRawResponse: + return AsyncLoanTapeConfigurationResourceWithRawResponse(self._financial_accounts.loan_tape_configuration) + + @cached_property + def interest_tier_schedule(self) -> AsyncInterestTierScheduleResourceWithRawResponse: + return AsyncInterestTierScheduleResourceWithRawResponse(self._financial_accounts.interest_tier_schedule) + class FinancialAccountsWithStreamingResponse: def __init__(self, financial_accounts: FinancialAccounts) -> None: @@ -816,6 +864,14 @@ def statements(self) -> StatementsWithStreamingResponse: def loan_tapes(self) -> LoanTapesWithStreamingResponse: return LoanTapesWithStreamingResponse(self._financial_accounts.loan_tapes) + @cached_property + def loan_tape_configuration(self) -> LoanTapeConfigurationResourceWithStreamingResponse: + return LoanTapeConfigurationResourceWithStreamingResponse(self._financial_accounts.loan_tape_configuration) + + @cached_property + def interest_tier_schedule(self) -> InterestTierScheduleResourceWithStreamingResponse: + return InterestTierScheduleResourceWithStreamingResponse(self._financial_accounts.interest_tier_schedule) + class AsyncFinancialAccountsWithStreamingResponse: def __init__(self, financial_accounts: AsyncFinancialAccounts) -> None: @@ -859,3 +915,11 @@ def statements(self) -> AsyncStatementsWithStreamingResponse: @cached_property def loan_tapes(self) -> AsyncLoanTapesWithStreamingResponse: return AsyncLoanTapesWithStreamingResponse(self._financial_accounts.loan_tapes) + + @cached_property + def loan_tape_configuration(self) -> AsyncLoanTapeConfigurationResourceWithStreamingResponse: + return AsyncLoanTapeConfigurationResourceWithStreamingResponse(self._financial_accounts.loan_tape_configuration) + + @cached_property + def interest_tier_schedule(self) -> AsyncInterestTierScheduleResourceWithStreamingResponse: + return AsyncInterestTierScheduleResourceWithStreamingResponse(self._financial_accounts.interest_tier_schedule) diff --git a/src/lithic/resources/financial_accounts/interest_tier_schedule.py b/src/lithic/resources/financial_accounts/interest_tier_schedule.py new file mode 100644 index 00000000..55f13682 --- /dev/null +++ b/src/lithic/resources/financial_accounts/interest_tier_schedule.py @@ -0,0 +1,681 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date + +import httpx + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncSinglePage, AsyncSinglePage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.financial_accounts import ( + interest_tier_schedule_list_params, + interest_tier_schedule_create_params, + interest_tier_schedule_update_params, +) +from ...types.financial_accounts.interest_tier_schedule import InterestTierSchedule + +__all__ = ["InterestTierScheduleResource", "AsyncInterestTierScheduleResource"] + + +class InterestTierScheduleResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InterestTierScheduleResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return InterestTierScheduleResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InterestTierScheduleResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return InterestTierScheduleResourceWithStreamingResponse(self) + + def create( + self, + financial_account_token: str, + *, + credit_product_token: str, + effective_date: Union[str, date], + tier_name: str | Omit = omit, + tier_rates: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InterestTierSchedule: + """ + Create a new interest tier schedule entry for a supported financial account + + Args: + credit_product_token: Globally unique identifier for a credit product + + effective_date: Date the tier should be effective in YYYY-MM-DD format + + tier_name: Name of a tier contained in the credit product. Mutually exclusive with + tier_rates + + tier_rates: Custom rates per category. Mutually exclusive with tier_name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return self._post( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule", + body=maybe_transform( + { + "credit_product_token": credit_product_token, + "effective_date": effective_date, + "tier_name": tier_name, + "tier_rates": tier_rates, + }, + interest_tier_schedule_create_params.InterestTierScheduleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InterestTierSchedule, + ) + + def retrieve( + self, + effective_date: Union[str, date], + *, + financial_account_token: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InterestTierSchedule: + """ + Get a specific interest tier schedule by effective date + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not effective_date: + raise ValueError(f"Expected a non-empty value for `effective_date` but received {effective_date!r}") + return self._get( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule/{effective_date}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InterestTierSchedule, + ) + + def update( + self, + effective_date: Union[str, date], + *, + financial_account_token: str, + tier_name: str | Omit = omit, + tier_rates: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InterestTierSchedule: + """ + Update an existing interest tier schedule + + Args: + tier_name: Name of a tier contained in the credit product. Mutually exclusive with + tier_rates + + tier_rates: Custom rates per category. Mutually exclusive with tier_name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not effective_date: + raise ValueError(f"Expected a non-empty value for `effective_date` but received {effective_date!r}") + return self._put( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule/{effective_date}", + body=maybe_transform( + { + "tier_name": tier_name, + "tier_rates": tier_rates, + }, + interest_tier_schedule_update_params.InterestTierScheduleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InterestTierSchedule, + ) + + def list( + self, + financial_account_token: str, + *, + after_date: Union[str, date] | Omit = omit, + before_date: Union[str, date] | Omit = omit, + for_date: Union[str, date] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncSinglePage[InterestTierSchedule]: + """ + List interest tier schedules for a financial account with optional date + filtering. + + If no date parameters are provided, returns all tier schedules. If date + parameters are provided, uses filtering to return matching schedules (max 100). + + - for_date: Returns exact match (takes precedence over other dates) + - before_date: Returns schedules with effective_date <= before_date + - after_date: Returns schedules with effective_date >= after_date + - Both before_date and after_date: Returns schedules in range + + Args: + after_date: Return schedules with effective_date >= after_date (ISO format YYYY-MM-DD) + + before_date: Return schedules with effective_date <= before_date (ISO format YYYY-MM-DD) + + for_date: Return schedule with effective_date == for_date (ISO format YYYY-MM-DD) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return self._get_api_list( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule", + page=SyncSinglePage[InterestTierSchedule], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after_date": after_date, + "before_date": before_date, + "for_date": for_date, + }, + interest_tier_schedule_list_params.InterestTierScheduleListParams, + ), + ), + model=InterestTierSchedule, + ) + + def delete( + self, + effective_date: Union[str, date], + *, + financial_account_token: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete an interest tier schedule entry. + + Returns: + + - 400 Bad Request: Invalid effective_date format OR attempting to delete the + earliest tier schedule entry for a non-PENDING account + - 404 Not Found: Tier schedule entry not found for the given effective_date OR + ledger account not found + + Note: PENDING accounts can delete the earliest tier schedule entry (account + hasn't opened yet). Active/non-PENDING accounts cannot delete the earliest entry + to prevent orphaning the account. + + If the deleted tier schedule has a past effective_date and the account is + ACTIVE, the loan tape rebuild configuration will be updated to trigger rebuilds + from that date. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not effective_date: + raise ValueError(f"Expected a non-empty value for `effective_date` but received {effective_date!r}") + return self._delete( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule/{effective_date}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncInterestTierScheduleResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInterestTierScheduleResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncInterestTierScheduleResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInterestTierScheduleResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncInterestTierScheduleResourceWithStreamingResponse(self) + + async def create( + self, + financial_account_token: str, + *, + credit_product_token: str, + effective_date: Union[str, date], + tier_name: str | Omit = omit, + tier_rates: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InterestTierSchedule: + """ + Create a new interest tier schedule entry for a supported financial account + + Args: + credit_product_token: Globally unique identifier for a credit product + + effective_date: Date the tier should be effective in YYYY-MM-DD format + + tier_name: Name of a tier contained in the credit product. Mutually exclusive with + tier_rates + + tier_rates: Custom rates per category. Mutually exclusive with tier_name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return await self._post( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule", + body=await async_maybe_transform( + { + "credit_product_token": credit_product_token, + "effective_date": effective_date, + "tier_name": tier_name, + "tier_rates": tier_rates, + }, + interest_tier_schedule_create_params.InterestTierScheduleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InterestTierSchedule, + ) + + async def retrieve( + self, + effective_date: Union[str, date], + *, + financial_account_token: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InterestTierSchedule: + """ + Get a specific interest tier schedule by effective date + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not effective_date: + raise ValueError(f"Expected a non-empty value for `effective_date` but received {effective_date!r}") + return await self._get( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule/{effective_date}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InterestTierSchedule, + ) + + async def update( + self, + effective_date: Union[str, date], + *, + financial_account_token: str, + tier_name: str | Omit = omit, + tier_rates: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InterestTierSchedule: + """ + Update an existing interest tier schedule + + Args: + tier_name: Name of a tier contained in the credit product. Mutually exclusive with + tier_rates + + tier_rates: Custom rates per category. Mutually exclusive with tier_name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not effective_date: + raise ValueError(f"Expected a non-empty value for `effective_date` but received {effective_date!r}") + return await self._put( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule/{effective_date}", + body=await async_maybe_transform( + { + "tier_name": tier_name, + "tier_rates": tier_rates, + }, + interest_tier_schedule_update_params.InterestTierScheduleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InterestTierSchedule, + ) + + def list( + self, + financial_account_token: str, + *, + after_date: Union[str, date] | Omit = omit, + before_date: Union[str, date] | Omit = omit, + for_date: Union[str, date] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[InterestTierSchedule, AsyncSinglePage[InterestTierSchedule]]: + """ + List interest tier schedules for a financial account with optional date + filtering. + + If no date parameters are provided, returns all tier schedules. If date + parameters are provided, uses filtering to return matching schedules (max 100). + + - for_date: Returns exact match (takes precedence over other dates) + - before_date: Returns schedules with effective_date <= before_date + - after_date: Returns schedules with effective_date >= after_date + - Both before_date and after_date: Returns schedules in range + + Args: + after_date: Return schedules with effective_date >= after_date (ISO format YYYY-MM-DD) + + before_date: Return schedules with effective_date <= before_date (ISO format YYYY-MM-DD) + + for_date: Return schedule with effective_date == for_date (ISO format YYYY-MM-DD) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return self._get_api_list( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule", + page=AsyncSinglePage[InterestTierSchedule], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after_date": after_date, + "before_date": before_date, + "for_date": for_date, + }, + interest_tier_schedule_list_params.InterestTierScheduleListParams, + ), + ), + model=InterestTierSchedule, + ) + + async def delete( + self, + effective_date: Union[str, date], + *, + financial_account_token: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete an interest tier schedule entry. + + Returns: + + - 400 Bad Request: Invalid effective_date format OR attempting to delete the + earliest tier schedule entry for a non-PENDING account + - 404 Not Found: Tier schedule entry not found for the given effective_date OR + ledger account not found + + Note: PENDING accounts can delete the earliest tier schedule entry (account + hasn't opened yet). Active/non-PENDING accounts cannot delete the earliest entry + to prevent orphaning the account. + + If the deleted tier schedule has a past effective_date and the account is + ACTIVE, the loan tape rebuild configuration will be updated to trigger rebuilds + from that date. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + if not effective_date: + raise ValueError(f"Expected a non-empty value for `effective_date` but received {effective_date!r}") + return await self._delete( + f"/v1/financial_accounts/{financial_account_token}/interest_tier_schedule/{effective_date}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class InterestTierScheduleResourceWithRawResponse: + def __init__(self, interest_tier_schedule: InterestTierScheduleResource) -> None: + self._interest_tier_schedule = interest_tier_schedule + + self.create = _legacy_response.to_raw_response_wrapper( + interest_tier_schedule.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + interest_tier_schedule.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + interest_tier_schedule.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + interest_tier_schedule.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + interest_tier_schedule.delete, + ) + + +class AsyncInterestTierScheduleResourceWithRawResponse: + def __init__(self, interest_tier_schedule: AsyncInterestTierScheduleResource) -> None: + self._interest_tier_schedule = interest_tier_schedule + + self.create = _legacy_response.async_to_raw_response_wrapper( + interest_tier_schedule.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + interest_tier_schedule.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + interest_tier_schedule.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + interest_tier_schedule.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + interest_tier_schedule.delete, + ) + + +class InterestTierScheduleResourceWithStreamingResponse: + def __init__(self, interest_tier_schedule: InterestTierScheduleResource) -> None: + self._interest_tier_schedule = interest_tier_schedule + + self.create = to_streamed_response_wrapper( + interest_tier_schedule.create, + ) + self.retrieve = to_streamed_response_wrapper( + interest_tier_schedule.retrieve, + ) + self.update = to_streamed_response_wrapper( + interest_tier_schedule.update, + ) + self.list = to_streamed_response_wrapper( + interest_tier_schedule.list, + ) + self.delete = to_streamed_response_wrapper( + interest_tier_schedule.delete, + ) + + +class AsyncInterestTierScheduleResourceWithStreamingResponse: + def __init__(self, interest_tier_schedule: AsyncInterestTierScheduleResource) -> None: + self._interest_tier_schedule = interest_tier_schedule + + self.create = async_to_streamed_response_wrapper( + interest_tier_schedule.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + interest_tier_schedule.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + interest_tier_schedule.update, + ) + self.list = async_to_streamed_response_wrapper( + interest_tier_schedule.list, + ) + self.delete = async_to_streamed_response_wrapper( + interest_tier_schedule.delete, + ) diff --git a/src/lithic/resources/financial_accounts/loan_tape_configuration.py b/src/lithic/resources/financial_accounts/loan_tape_configuration.py new file mode 100644 index 00000000..b07b5006 --- /dev/null +++ b/src/lithic/resources/financial_accounts/loan_tape_configuration.py @@ -0,0 +1,167 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ... import _legacy_response +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.financial_accounts.loan_tape_configuration import LoanTapeConfiguration + +__all__ = ["LoanTapeConfigurationResource", "AsyncLoanTapeConfigurationResource"] + + +class LoanTapeConfigurationResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> LoanTapeConfigurationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return LoanTapeConfigurationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LoanTapeConfigurationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return LoanTapeConfigurationResourceWithStreamingResponse(self) + + def retrieve( + self, + financial_account_token: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoanTapeConfiguration: + """ + Get the loan tape configuration for a given financial account. + + Args: + financial_account_token: Globally unique identifier for financial account. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return self._get( + f"/v1/financial_accounts/{financial_account_token}/loan_tape_configuration", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoanTapeConfiguration, + ) + + +class AsyncLoanTapeConfigurationResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncLoanTapeConfigurationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/lithic-com/lithic-python#accessing-raw-response-data-eg-headers + """ + return AsyncLoanTapeConfigurationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLoanTapeConfigurationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/lithic-com/lithic-python#with_streaming_response + """ + return AsyncLoanTapeConfigurationResourceWithStreamingResponse(self) + + async def retrieve( + self, + financial_account_token: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LoanTapeConfiguration: + """ + Get the loan tape configuration for a given financial account. + + Args: + financial_account_token: Globally unique identifier for financial account. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not financial_account_token: + raise ValueError( + f"Expected a non-empty value for `financial_account_token` but received {financial_account_token!r}" + ) + return await self._get( + f"/v1/financial_accounts/{financial_account_token}/loan_tape_configuration", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LoanTapeConfiguration, + ) + + +class LoanTapeConfigurationResourceWithRawResponse: + def __init__(self, loan_tape_configuration: LoanTapeConfigurationResource) -> None: + self._loan_tape_configuration = loan_tape_configuration + + self.retrieve = _legacy_response.to_raw_response_wrapper( + loan_tape_configuration.retrieve, + ) + + +class AsyncLoanTapeConfigurationResourceWithRawResponse: + def __init__(self, loan_tape_configuration: AsyncLoanTapeConfigurationResource) -> None: + self._loan_tape_configuration = loan_tape_configuration + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + loan_tape_configuration.retrieve, + ) + + +class LoanTapeConfigurationResourceWithStreamingResponse: + def __init__(self, loan_tape_configuration: LoanTapeConfigurationResource) -> None: + self._loan_tape_configuration = loan_tape_configuration + + self.retrieve = to_streamed_response_wrapper( + loan_tape_configuration.retrieve, + ) + + +class AsyncLoanTapeConfigurationResourceWithStreamingResponse: + def __init__(self, loan_tape_configuration: AsyncLoanTapeConfigurationResource) -> None: + self._loan_tape_configuration = loan_tape_configuration + + self.retrieve = async_to_streamed_response_wrapper( + loan_tape_configuration.retrieve, + ) diff --git a/src/lithic/types/financial_accounts/__init__.py b/src/lithic/types/financial_accounts/__init__.py index 0a094585..1938293c 100644 --- a/src/lithic/types/financial_accounts/__init__.py +++ b/src/lithic/types/financial_accounts/__init__.py @@ -9,6 +9,12 @@ from .balance_list_params import BalanceListParams as BalanceListParams from .loan_tape_list_params import LoanTapeListParams as LoanTapeListParams from .statement_list_params import StatementListParams as StatementListParams +from .interest_tier_schedule import InterestTierSchedule as InterestTierSchedule +from .loan_tape_configuration import LoanTapeConfiguration as LoanTapeConfiguration from .financial_account_credit_config import FinancialAccountCreditConfig as FinancialAccountCreditConfig +from .loan_tape_rebuild_configuration import LoanTapeRebuildConfiguration as LoanTapeRebuildConfiguration from .financial_transaction_list_params import FinancialTransactionListParams as FinancialTransactionListParams from .credit_configuration_update_params import CreditConfigurationUpdateParams as CreditConfigurationUpdateParams +from .interest_tier_schedule_list_params import InterestTierScheduleListParams as InterestTierScheduleListParams +from .interest_tier_schedule_create_params import InterestTierScheduleCreateParams as InterestTierScheduleCreateParams +from .interest_tier_schedule_update_params import InterestTierScheduleUpdateParams as InterestTierScheduleUpdateParams diff --git a/src/lithic/types/financial_accounts/interest_tier_schedule.py b/src/lithic/types/financial_accounts/interest_tier_schedule.py new file mode 100644 index 00000000..de815562 --- /dev/null +++ b/src/lithic/types/financial_accounts/interest_tier_schedule.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import date + +from ..._models import BaseModel + +__all__ = ["InterestTierSchedule"] + + +class InterestTierSchedule(BaseModel): + """Entry in the Tier Schedule of an account""" + + credit_product_token: str + """Globally unique identifier for a credit product""" + + effective_date: date + """Date the tier should be effective in YYYY-MM-DD format""" + + tier_name: Optional[str] = None + """Name of a tier contained in the credit product. + + Mutually exclusive with tier_rates + """ + + tier_rates: Optional[object] = None + """Custom rates per category. Mutually exclusive with tier_name""" diff --git a/src/lithic/types/financial_accounts/interest_tier_schedule_create_params.py b/src/lithic/types/financial_accounts/interest_tier_schedule_create_params.py new file mode 100644 index 00000000..dc7c11e9 --- /dev/null +++ b/src/lithic/types/financial_accounts/interest_tier_schedule_create_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["InterestTierScheduleCreateParams"] + + +class InterestTierScheduleCreateParams(TypedDict, total=False): + credit_product_token: Required[str] + """Globally unique identifier for a credit product""" + + effective_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] + """Date the tier should be effective in YYYY-MM-DD format""" + + tier_name: str + """Name of a tier contained in the credit product. + + Mutually exclusive with tier_rates + """ + + tier_rates: object + """Custom rates per category. Mutually exclusive with tier_name""" diff --git a/src/lithic/types/financial_accounts/interest_tier_schedule_list_params.py b/src/lithic/types/financial_accounts/interest_tier_schedule_list_params.py new file mode 100644 index 00000000..094f9ac1 --- /dev/null +++ b/src/lithic/types/financial_accounts/interest_tier_schedule_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["InterestTierScheduleListParams"] + + +class InterestTierScheduleListParams(TypedDict, total=False): + after_date: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Return schedules with effective_date >= after_date (ISO format YYYY-MM-DD)""" + + before_date: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Return schedules with effective_date <= before_date (ISO format YYYY-MM-DD)""" + + for_date: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """Return schedule with effective_date == for_date (ISO format YYYY-MM-DD)""" diff --git a/src/lithic/types/financial_accounts/interest_tier_schedule_update_params.py b/src/lithic/types/financial_accounts/interest_tier_schedule_update_params.py new file mode 100644 index 00000000..dd1cd9c0 --- /dev/null +++ b/src/lithic/types/financial_accounts/interest_tier_schedule_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["InterestTierScheduleUpdateParams"] + + +class InterestTierScheduleUpdateParams(TypedDict, total=False): + financial_account_token: Required[str] + + tier_name: str + """Name of a tier contained in the credit product. + + Mutually exclusive with tier_rates + """ + + tier_rates: object + """Custom rates per category. Mutually exclusive with tier_name""" diff --git a/src/lithic/types/financial_accounts/loan_tape_configuration.py b/src/lithic/types/financial_accounts/loan_tape_configuration.py new file mode 100644 index 00000000..02554353 --- /dev/null +++ b/src/lithic/types/financial_accounts/loan_tape_configuration.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from ..._models import BaseModel +from .loan_tape_rebuild_configuration import LoanTapeRebuildConfiguration + +__all__ = ["LoanTapeConfiguration"] + + +class LoanTapeConfiguration(BaseModel): + """Configuration for loan tapes""" + + created_at: datetime + + financial_account_token: str + + instance_token: str + + updated_at: datetime + + credit_product_token: Optional[str] = None + + loan_tape_rebuild_configuration: Optional[LoanTapeRebuildConfiguration] = None + """Configuration for building loan tapes""" + + tier_schedule_changed_at: Optional[datetime] = None diff --git a/src/lithic/types/financial_accounts/loan_tape_rebuild_configuration.py b/src/lithic/types/financial_accounts/loan_tape_rebuild_configuration.py new file mode 100644 index 00000000..81fadc2c --- /dev/null +++ b/src/lithic/types/financial_accounts/loan_tape_rebuild_configuration.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import date + +from ..._models import BaseModel + +__all__ = ["LoanTapeRebuildConfiguration"] + + +class LoanTapeRebuildConfiguration(BaseModel): + """Configuration for building loan tapes""" + + rebuild_needed: bool + """Whether the account's loan tapes need to be rebuilt or not""" + + last_rebuild: Optional[date] = None + """The date for which the account's loan tapes were last rebuilt""" + + rebuild_from: Optional[date] = None + """Date from which to start rebuilding from if the account requires a rebuild""" diff --git a/tests/api_resources/financial_accounts/test_interest_tier_schedule.py b/tests/api_resources/financial_accounts/test_interest_tier_schedule.py new file mode 100644 index 00000000..fcac228d --- /dev/null +++ b/tests/api_resources/financial_accounts/test_interest_tier_schedule.py @@ -0,0 +1,566 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic._utils import parse_date +from lithic.pagination import SyncSinglePage, AsyncSinglePage +from lithic.types.financial_accounts import ( + InterestTierSchedule, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInterestTierSchedule: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + tier_name="tier_name", + tier_rates={}, + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Lithic) -> None: + response = client.financial_accounts.interest_tier_schedule.with_raw_response.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Lithic) -> None: + with client.financial_accounts.interest_tier_schedule.with_streaming_response.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.interest_tier_schedule.with_raw_response.create( + financial_account_token="", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.financial_accounts.interest_tier_schedule.with_raw_response.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.financial_accounts.interest_tier_schedule.with_streaming_response.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.interest_tier_schedule.with_raw_response.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `effective_date` but received ''"): + client.financial_accounts.interest_tier_schedule.with_raw_response.retrieve( + effective_date="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + @parametrize + def test_method_update(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + tier_name="tier_name", + tier_rates={}, + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Lithic) -> None: + response = client.financial_accounts.interest_tier_schedule.with_raw_response.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Lithic) -> None: + with client.financial_accounts.interest_tier_schedule.with_streaming_response.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.interest_tier_schedule.with_raw_response.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `effective_date` but received ''"): + client.financial_accounts.interest_tier_schedule.with_raw_response.update( + effective_date="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + @parametrize + def test_method_list(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(SyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + after_date=parse_date("2019-12-27"), + before_date=parse_date("2019-12-27"), + for_date=parse_date("2019-12-27"), + ) + assert_matches_type(SyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Lithic) -> None: + response = client.financial_accounts.interest_tier_schedule.with_raw_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(SyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Lithic) -> None: + with client.financial_accounts.interest_tier_schedule.with_streaming_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = response.parse() + assert_matches_type(SyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.interest_tier_schedule.with_raw_response.list( + financial_account_token="", + ) + + @parametrize + def test_method_delete(self, client: Lithic) -> None: + interest_tier_schedule = client.financial_accounts.interest_tier_schedule.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert interest_tier_schedule is None + + @parametrize + def test_raw_response_delete(self, client: Lithic) -> None: + response = client.financial_accounts.interest_tier_schedule.with_raw_response.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert interest_tier_schedule is None + + @parametrize + def test_streaming_response_delete(self, client: Lithic) -> None: + with client.financial_accounts.interest_tier_schedule.with_streaming_response.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = response.parse() + assert interest_tier_schedule is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.interest_tier_schedule.with_raw_response.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `effective_date` but received ''"): + client.financial_accounts.interest_tier_schedule.with_raw_response.delete( + effective_date="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + +class TestAsyncInterestTierSchedule: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + tier_name="tier_name", + tier_rates={}, + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.interest_tier_schedule.with_raw_response.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.interest_tier_schedule.with_streaming_response.create( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = await response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.create( + financial_account_token="", + credit_product_token="credit_product_token", + effective_date=parse_date("2019-12-27"), + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.interest_tier_schedule.with_raw_response.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.interest_tier_schedule.with_streaming_response.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = await response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.retrieve( + effective_date=parse_date("2019-12-27"), + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `effective_date` but received ''"): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.retrieve( + effective_date="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + tier_name="tier_name", + tier_rates={}, + ) + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.interest_tier_schedule.with_raw_response.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.interest_tier_schedule.with_streaming_response.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = await response.parse() + assert_matches_type(InterestTierSchedule, interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.update( + effective_date=parse_date("2019-12-27"), + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `effective_date` but received ''"): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.update( + effective_date="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(AsyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + after_date=parse_date("2019-12-27"), + before_date=parse_date("2019-12-27"), + for_date=parse_date("2019-12-27"), + ) + assert_matches_type(AsyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.interest_tier_schedule.with_raw_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert_matches_type(AsyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.interest_tier_schedule.with_streaming_response.list( + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = await response.parse() + assert_matches_type(AsyncSinglePage[InterestTierSchedule], interest_tier_schedule, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.list( + financial_account_token="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncLithic) -> None: + interest_tier_schedule = await async_client.financial_accounts.interest_tier_schedule.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert interest_tier_schedule is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.interest_tier_schedule.with_raw_response.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + interest_tier_schedule = response.parse() + assert interest_tier_schedule is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.interest_tier_schedule.with_streaming_response.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + interest_tier_schedule = await response.parse() + assert interest_tier_schedule is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.delete( + effective_date=parse_date("2019-12-27"), + financial_account_token="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `effective_date` but received ''"): + await async_client.financial_accounts.interest_tier_schedule.with_raw_response.delete( + effective_date="", + financial_account_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) diff --git a/tests/api_resources/financial_accounts/test_loan_tape_configuration.py b/tests/api_resources/financial_accounts/test_loan_tape_configuration.py new file mode 100644 index 00000000..e906241a --- /dev/null +++ b/tests/api_resources/financial_accounts/test_loan_tape_configuration.py @@ -0,0 +1,104 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from lithic import Lithic, AsyncLithic +from tests.utils import assert_matches_type +from lithic.types.financial_accounts import LoanTapeConfiguration + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLoanTapeConfiguration: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Lithic) -> None: + loan_tape_configuration = client.financial_accounts.loan_tape_configuration.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(LoanTapeConfiguration, loan_tape_configuration, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Lithic) -> None: + response = client.financial_accounts.loan_tape_configuration.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + loan_tape_configuration = response.parse() + assert_matches_type(LoanTapeConfiguration, loan_tape_configuration, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Lithic) -> None: + with client.financial_accounts.loan_tape_configuration.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + loan_tape_configuration = response.parse() + assert_matches_type(LoanTapeConfiguration, loan_tape_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Lithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + client.financial_accounts.loan_tape_configuration.with_raw_response.retrieve( + "", + ) + + +class TestAsyncLoanTapeConfiguration: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncLithic) -> None: + loan_tape_configuration = await async_client.financial_accounts.loan_tape_configuration.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(LoanTapeConfiguration, loan_tape_configuration, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncLithic) -> None: + response = await async_client.financial_accounts.loan_tape_configuration.with_raw_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + loan_tape_configuration = response.parse() + assert_matches_type(LoanTapeConfiguration, loan_tape_configuration, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncLithic) -> None: + async with async_client.financial_accounts.loan_tape_configuration.with_streaming_response.retrieve( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + loan_tape_configuration = await response.parse() + assert_matches_type(LoanTapeConfiguration, loan_tape_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncLithic) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `financial_account_token` but received ''" + ): + await async_client.financial_accounts.loan_tape_configuration.with_raw_response.retrieve( + "", + ) From ffb0cf875c92279d107d61b005b21be1aaeec856 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 18:30:48 +0000 Subject: [PATCH 7/7] release: 0.116.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/lithic/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d3dc9f51..988e843f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.115.0" + ".": "0.116.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 981334cd..6a7f138e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.116.0 (2026-02-23) + +Full Changelog: [v0.115.0...v0.116.0](https://github.com/lithic-com/lithic-python/compare/v0.115.0...v0.116.0) + +### Features + +* **api:** Add INTEREST_AND_FEES_PAUSED substatus to financial account ([c4a37de](https://github.com/lithic-com/lithic-python/commit/c4a37de9f0f7821e54d05702c4d1d809124fc7bc)) +* **api:** Expose MIL interest schedules and loan tape configuration endpoints ([5c6bcd7](https://github.com/lithic-com/lithic-python/commit/5c6bcd740933255291c0fd0b482021c4a50d0e78)) + + +### Chores + +* **dependencies:** require standardwebhooks 1.0.1 ([7dae789](https://github.com/lithic-com/lithic-python/commit/7dae7892de61a765ab4bca8c7af3046d90fe2ff4)) +* **internal:** add request options to SSE classes ([1b49e55](https://github.com/lithic-com/lithic-python/commit/1b49e550b0e6fe3c5203740a9634ee6264046489)) +* update mock server docs ([ec268b7](https://github.com/lithic-com/lithic-python/commit/ec268b7426be15578b2eaf92ca81107cbf9084e6)) + ## 0.115.0 (2026-02-13) Full Changelog: [v0.114.0...v0.115.0](https://github.com/lithic-com/lithic-python/compare/v0.114.0...v0.115.0) diff --git a/pyproject.toml b/pyproject.toml index b175a6da..18c9a87e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lithic" -version = "0.115.0" +version = "0.116.0" description = "The official Python library for the lithic API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/lithic/_version.py b/src/lithic/_version.py index 9b480c33..d7f6788a 100644 --- a/src/lithic/_version.py +++ b/src/lithic/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "lithic" -__version__ = "0.115.0" # x-release-please-version +__version__ = "0.116.0" # x-release-please-version