From 771405db368eabac2f7ad616d8d9baee372208c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:47:07 -0400 Subject: [PATCH 1/8] feat(invoicing): add v2 invoice add_lines endpoint --- fintoc/managers/v2/invoices_manager.py | 7 ++++++- tests/test_integration.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/fintoc/managers/v2/invoices_manager.py b/fintoc/managers/v2/invoices_manager.py index 9392c1d..a35672c 100644 --- a/fintoc/managers/v2/invoices_manager.py +++ b/fintoc/managers/v2/invoices_manager.py @@ -7,4 +7,9 @@ class InvoicesManager(ManagerMixin): """Represents an invoices manager.""" resource = "invoice" - methods = ["list", "get"] + methods = ["list", "get", "add_lines"] + + def _add_lines(self, identifier, **kwargs): + """Add line items to an invoice.""" + path = f"{self._build_path(**kwargs)}/{identifier}/add_lines" + return self._create(path_=path, **kwargs) diff --git a/tests/test_integration.py b/tests/test_integration.py index cedd439..12905db 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1155,6 +1155,24 @@ def test_v2_invoice_get(self): assert invoice.method == "get" assert invoice.url == f"v2/invoices/{invoice_id}" + def test_v2_invoice_add_lines(self): + """Test adding line items to an invoice using v2 API.""" + invoice_id = "test_invoice_id" + lines = [ + { + "amount": 10000, + "currency": "CLP", + "period_end": "2024-02-01", + "period_start": "2024-01-01", + "quantity": 1, + } + ] + + invoice = self.fintoc.v2.invoices.add_lines(invoice_id, lines=lines) + + assert invoice.method == "post" + assert invoice.url == f"v2/invoices/{invoice_id}/add_lines" + def test_v2_account_statements_list(self): """Test getting account statements from an account using v2 API.""" account_id = "acc_12345" From 0eac3943e65061b1679dabe472702706a57ccee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:47:35 -0400 Subject: [PATCH 2/8] feat(invoicing): add v2 invoice remove_lines endpoint --- fintoc/managers/v2/invoices_manager.py | 7 ++++++- tests/test_integration.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/fintoc/managers/v2/invoices_manager.py b/fintoc/managers/v2/invoices_manager.py index a35672c..f3e2eec 100644 --- a/fintoc/managers/v2/invoices_manager.py +++ b/fintoc/managers/v2/invoices_manager.py @@ -7,9 +7,14 @@ class InvoicesManager(ManagerMixin): """Represents an invoices manager.""" resource = "invoice" - methods = ["list", "get", "add_lines"] + methods = ["list", "get", "add_lines", "remove_lines"] def _add_lines(self, identifier, **kwargs): """Add line items to an invoice.""" path = f"{self._build_path(**kwargs)}/{identifier}/add_lines" return self._create(path_=path, **kwargs) + + def _remove_lines(self, identifier, **kwargs): + """Remove line items from an invoice.""" + path = f"{self._build_path(**kwargs)}/{identifier}/remove_lines" + return self._create(path_=path, **kwargs) diff --git a/tests/test_integration.py b/tests/test_integration.py index 12905db..afbdbfc 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1173,6 +1173,16 @@ def test_v2_invoice_add_lines(self): assert invoice.method == "post" assert invoice.url == f"v2/invoices/{invoice_id}/add_lines" + def test_v2_invoice_remove_lines(self): + """Test removing line items from an invoice using v2 API.""" + invoice_id = "test_invoice_id" + lines = ["il_test_line_id"] + + invoice = self.fintoc.v2.invoices.remove_lines(invoice_id, lines=lines) + + assert invoice.method == "post" + assert invoice.url == f"v2/invoices/{invoice_id}/remove_lines" + def test_v2_account_statements_list(self): """Test getting account statements from an account using v2 API.""" account_id = "acc_12345" From 17a7097ab593bea955495efa2dd4ff0aca716582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:48:56 -0400 Subject: [PATCH 3/8] feat(invoicing): add v2 invoice line update endpoint --- fintoc/managers/v2/__init__.py | 1 + fintoc/managers/v2/invoices_manager.py | 19 +++++++++++++++++++ fintoc/managers/v2/lines_manager.py | 10 ++++++++++ fintoc/resources/__init__.py | 1 + fintoc/resources/v2/__init__.py | 1 + fintoc/resources/v2/line.py | 7 +++++++ tests/test_integration.py | 12 ++++++++++++ 7 files changed, 51 insertions(+) create mode 100644 fintoc/managers/v2/lines_manager.py create mode 100644 fintoc/resources/v2/line.py diff --git a/fintoc/managers/v2/__init__.py b/fintoc/managers/v2/__init__.py index 5234f5f..785d353 100644 --- a/fintoc/managers/v2/__init__.py +++ b/fintoc/managers/v2/__init__.py @@ -8,6 +8,7 @@ from .customers_manager import CustomersManager from .entities_manager import EntitiesManager from .invoices_manager import InvoicesManager +from .lines_manager import LinesManager from .movements_manager import MovementsManager from .onboardings_manager import OnboardingsManager from .payment_intents_manager import PaymentIntentsManager diff --git a/fintoc/managers/v2/invoices_manager.py b/fintoc/managers/v2/invoices_manager.py index f3e2eec..3b90d98 100644 --- a/fintoc/managers/v2/invoices_manager.py +++ b/fintoc/managers/v2/invoices_manager.py @@ -1,5 +1,6 @@ """Module to hold the invoices manager.""" +from fintoc.managers.v2.lines_manager import LinesManager from fintoc.mixins import ManagerMixin @@ -9,6 +10,24 @@ class InvoicesManager(ManagerMixin): resource = "invoice" methods = ["list", "get", "add_lines", "remove_lines"] + def __init__(self, path, client): + super().__init__(path, client) + self.__lines_manager = None + + @property + def lines(self): + """Proxies the invoice lines manager.""" + if self.__lines_manager is None: + self.__lines_manager = LinesManager( + "/v2/invoices/{invoice_id}/lines", + self._client, + ) + return self.__lines_manager + + @lines.setter + def lines(self, new_value): # pylint: disable=no-self-use + raise NameError("Attribute name corresponds to a manager") + def _add_lines(self, identifier, **kwargs): """Add line items to an invoice.""" path = f"{self._build_path(**kwargs)}/{identifier}/add_lines" diff --git a/fintoc/managers/v2/lines_manager.py b/fintoc/managers/v2/lines_manager.py new file mode 100644 index 0000000..c7fa181 --- /dev/null +++ b/fintoc/managers/v2/lines_manager.py @@ -0,0 +1,10 @@ +"""Module to hold the invoice lines manager.""" + +from fintoc.mixins import ManagerMixin + + +class LinesManager(ManagerMixin): + """Represents an invoice lines manager.""" + + resource = "line" + methods = ["update"] diff --git a/fintoc/resources/__init__.py b/fintoc/resources/__init__.py index ea9e9ef..e45fb7b 100644 --- a/fintoc/resources/__init__.py +++ b/fintoc/resources/__init__.py @@ -31,6 +31,7 @@ from .v2.customer import Customer from .v2.entity import Entity from .v2.invoice import Invoice as InvoiceV2 +from .v2.line import Line from .v2.onboarding import Onboarding from .v2.onboarding_document import OnboardingDocument from .v2.onboarding_shareholder import OnboardingShareholder diff --git a/fintoc/resources/v2/__init__.py b/fintoc/resources/v2/__init__.py index b0e9e7e..ea5f343 100644 --- a/fintoc/resources/v2/__init__.py +++ b/fintoc/resources/v2/__init__.py @@ -6,6 +6,7 @@ from .account_verification import AccountVerification from .entity import Entity from .invoice import Invoice +from .line import Line from .movement import Movement from .onboarding import Onboarding from .onboarding_document import OnboardingDocument diff --git a/fintoc/resources/v2/line.py b/fintoc/resources/v2/line.py new file mode 100644 index 0000000..6f708c5 --- /dev/null +++ b/fintoc/resources/v2/line.py @@ -0,0 +1,7 @@ +"""Module to hold the Line resource.""" + +from fintoc.mixins import ResourceMixin + + +class Line(ResourceMixin): + """Represents a Fintoc Invoice Line.""" diff --git a/tests/test_integration.py b/tests/test_integration.py index afbdbfc..83af57c 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1183,6 +1183,18 @@ def test_v2_invoice_remove_lines(self): assert invoice.method == "post" assert invoice.url == f"v2/invoices/{invoice_id}/remove_lines" + def test_v2_invoice_line_update(self): + """Test updating an invoice line item using v2 API.""" + invoice_id = "test_invoice_id" + line_id = "il_test_line_id" + + line = self.fintoc.v2.invoices.lines.update( + line_id, invoice_id=invoice_id, quantity=2 + ) + + assert line.method == "patch" + assert line.url == f"v2/invoices/{invoice_id}/lines/{line_id}" + def test_v2_account_statements_list(self): """Test getting account statements from an account using v2 API.""" account_id = "acc_12345" From a76d3837d50da6eee483dc17108ed3334a2c434c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:49:21 -0400 Subject: [PATCH 4/8] feat(invoicing): add v2 subscription create endpoint --- fintoc/managers/v2/subscriptions_manager.py | 2 +- tests/test_integration.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fintoc/managers/v2/subscriptions_manager.py b/fintoc/managers/v2/subscriptions_manager.py index 5b12d32..245c367 100644 --- a/fintoc/managers/v2/subscriptions_manager.py +++ b/fintoc/managers/v2/subscriptions_manager.py @@ -7,7 +7,7 @@ class SubscriptionsManager(ManagerMixin): """Represents a subscriptions manager.""" resource = "subscription" - methods = ["list", "get", "cancel"] + methods = ["list", "get", "create", "cancel"] def _cancel(self, identifier, **kwargs): """Cancel a subscription.""" diff --git a/tests/test_integration.py b/tests/test_integration.py index 83af57c..63d8a24 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1103,6 +1103,19 @@ def test_v2_subscription_cancel(self): assert result.method == "post" assert result.url == f"v2/subscriptions/{subscription_id}/cancel" + def test_v2_subscription_create(self): + """Test creating a subscription using v2 API.""" + subscription_data = { + "customer": "cus_test_id", + "items": [{"price": "price_test_id", "quantity": 1}], + } + + subscription = self.fintoc.v2.subscriptions.create(**subscription_data) + + assert subscription.method == "post" + assert subscription.url == "v2/subscriptions" + assert subscription.json.customer == subscription_data["customer"] + def test_v2_products_list(self): """Test getting all products using v2 API.""" products = list(self.fintoc.v2.products.list()) From 8cf1ac013089b69ee7539dc0fc7f7c42aa5269ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:49:48 -0400 Subject: [PATCH 5/8] feat(invoicing): add v2 subscription update endpoint --- fintoc/managers/v2/subscriptions_manager.py | 2 +- tests/test_integration.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/fintoc/managers/v2/subscriptions_manager.py b/fintoc/managers/v2/subscriptions_manager.py index 245c367..115fac1 100644 --- a/fintoc/managers/v2/subscriptions_manager.py +++ b/fintoc/managers/v2/subscriptions_manager.py @@ -7,7 +7,7 @@ class SubscriptionsManager(ManagerMixin): """Represents a subscriptions manager.""" resource = "subscription" - methods = ["list", "get", "create", "cancel"] + methods = ["list", "get", "create", "update", "cancel"] def _cancel(self, identifier, **kwargs): """Cancel a subscription.""" diff --git a/tests/test_integration.py b/tests/test_integration.py index 63d8a24..72eefb5 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1116,6 +1116,17 @@ def test_v2_subscription_create(self): assert subscription.url == "v2/subscriptions" assert subscription.json.customer == subscription_data["customer"] + def test_v2_subscription_update(self): + """Test updating a subscription using v2 API.""" + subscription_id = "test_subscription_id" + + subscription = self.fintoc.v2.subscriptions.update( + subscription_id, trial_end="2024-12-31T00:00:00Z" + ) + + assert subscription.method == "patch" + assert subscription.url == f"v2/subscriptions/{subscription_id}" + def test_v2_products_list(self): """Test getting all products using v2 API.""" products = list(self.fintoc.v2.products.list()) From 58a156baed776f1b1c477f06f28595a938db482a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:50:55 -0400 Subject: [PATCH 6/8] feat(invoicing): add v2 subscription item create endpoint --- fintoc/managers/v2/__init__.py | 1 + .../managers/v2/subscription_items_manager.py | 10 ++++++++++ fintoc/managers/v2/subscriptions_manager.py | 19 +++++++++++++++++++ fintoc/resources/__init__.py | 1 + fintoc/resources/v2/__init__.py | 1 + fintoc/resources/v2/subscription_item.py | 7 +++++++ tests/test_integration.py | 13 +++++++++++++ 7 files changed, 52 insertions(+) create mode 100644 fintoc/managers/v2/subscription_items_manager.py create mode 100644 fintoc/resources/v2/subscription_item.py diff --git a/fintoc/managers/v2/__init__.py b/fintoc/managers/v2/__init__.py index 785d353..32b8d43 100644 --- a/fintoc/managers/v2/__init__.py +++ b/fintoc/managers/v2/__init__.py @@ -15,5 +15,6 @@ from .payment_methods_manager import PaymentMethodsManager from .products_manager import ProductsManager from .simulate_manager import SimulateManager +from .subscription_items_manager import SubscriptionItemsManager from .subscriptions_manager import SubscriptionsManager from .transfers_manager import TransfersManager diff --git a/fintoc/managers/v2/subscription_items_manager.py b/fintoc/managers/v2/subscription_items_manager.py new file mode 100644 index 0000000..3cc9204 --- /dev/null +++ b/fintoc/managers/v2/subscription_items_manager.py @@ -0,0 +1,10 @@ +"""Module to hold the subscription items manager.""" + +from fintoc.mixins import ManagerMixin + + +class SubscriptionItemsManager(ManagerMixin): + """Represents a subscription items manager.""" + + resource = "subscription_item" + methods = ["create"] diff --git a/fintoc/managers/v2/subscriptions_manager.py b/fintoc/managers/v2/subscriptions_manager.py index 115fac1..1ae27b6 100644 --- a/fintoc/managers/v2/subscriptions_manager.py +++ b/fintoc/managers/v2/subscriptions_manager.py @@ -1,5 +1,6 @@ """Module to hold the subscriptions manager.""" +from fintoc.managers.v2.subscription_items_manager import SubscriptionItemsManager from fintoc.mixins import ManagerMixin @@ -9,6 +10,24 @@ class SubscriptionsManager(ManagerMixin): resource = "subscription" methods = ["list", "get", "create", "update", "cancel"] + def __init__(self, path, client): + super().__init__(path, client) + self.__items_manager = None + + @property + def items(self): + """Proxies the subscription items manager.""" + if self.__items_manager is None: + self.__items_manager = SubscriptionItemsManager( + "/v2/subscriptions/{subscription_id}/items", + self._client, + ) + return self.__items_manager + + @items.setter + def items(self, new_value): # pylint: disable=no-self-use + raise NameError("Attribute name corresponds to a manager") + def _cancel(self, identifier, **kwargs): """Cancel a subscription.""" path = f"{self._build_path(**kwargs)}/{identifier}/cancel" diff --git a/fintoc/resources/__init__.py b/fintoc/resources/__init__.py index e45fb7b..bb8cac4 100644 --- a/fintoc/resources/__init__.py +++ b/fintoc/resources/__init__.py @@ -37,5 +37,6 @@ from .v2.onboarding_shareholder import OnboardingShareholder from .v2.payment_method import PaymentMethod from .v2.subscription import Subscription as SubscriptionV2 +from .v2.subscription_item import SubscriptionItem from .v2.transfer import Transfer from .webhook_endpoint import WebhookEndpoint diff --git a/fintoc/resources/v2/__init__.py b/fintoc/resources/v2/__init__.py index ea5f343..12d2306 100644 --- a/fintoc/resources/v2/__init__.py +++ b/fintoc/resources/v2/__init__.py @@ -14,4 +14,5 @@ from .payment_method import PaymentMethod from .product import Product from .subscription import Subscription +from .subscription_item import SubscriptionItem from .transfer import Transfer diff --git a/fintoc/resources/v2/subscription_item.py b/fintoc/resources/v2/subscription_item.py new file mode 100644 index 0000000..f20bf22 --- /dev/null +++ b/fintoc/resources/v2/subscription_item.py @@ -0,0 +1,7 @@ +"""Module to hold the SubscriptionItem resource.""" + +from fintoc.mixins import ResourceMixin + + +class SubscriptionItem(ResourceMixin): + """Represents a Fintoc Subscription Item.""" diff --git a/tests/test_integration.py b/tests/test_integration.py index 72eefb5..dbde3c8 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1127,6 +1127,19 @@ def test_v2_subscription_update(self): assert subscription.method == "patch" assert subscription.url == f"v2/subscriptions/{subscription_id}" + def test_v2_subscription_item_create(self): + """Test creating a subscription item using v2 API.""" + subscription_id = "test_subscription_id" + + item = self.fintoc.v2.subscriptions.items.create( + subscription_id=subscription_id, + price_data={"product": "prod_test_id", "amount": 10000, "currency": "CLP"}, + quantity=1, + ) + + assert item.method == "post" + assert item.url == f"v2/subscriptions/{subscription_id}/items" + def test_v2_products_list(self): """Test getting all products using v2 API.""" products = list(self.fintoc.v2.products.list()) From 4ef18b0ec240058a8d88ac450c17bcfb7d5a08ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:51:22 -0400 Subject: [PATCH 7/8] feat(invoicing): add v2 subscription item update endpoint --- fintoc/managers/v2/subscription_items_manager.py | 2 +- tests/test_integration.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fintoc/managers/v2/subscription_items_manager.py b/fintoc/managers/v2/subscription_items_manager.py index 3cc9204..d5dbcee 100644 --- a/fintoc/managers/v2/subscription_items_manager.py +++ b/fintoc/managers/v2/subscription_items_manager.py @@ -7,4 +7,4 @@ class SubscriptionItemsManager(ManagerMixin): """Represents a subscription items manager.""" resource = "subscription_item" - methods = ["create"] + methods = ["create", "update"] diff --git a/tests/test_integration.py b/tests/test_integration.py index dbde3c8..5e23df8 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1140,6 +1140,18 @@ def test_v2_subscription_item_create(self): assert item.method == "post" assert item.url == f"v2/subscriptions/{subscription_id}/items" + def test_v2_subscription_item_update(self): + """Test updating a subscription item using v2 API.""" + subscription_id = "test_subscription_id" + item_id = "si_test_item_id" + + item = self.fintoc.v2.subscriptions.items.update( + item_id, subscription_id=subscription_id, quantity=3 + ) + + assert item.method == "patch" + assert item.url == f"v2/subscriptions/{subscription_id}/items/{item_id}" + def test_v2_products_list(self): """Test getting all products using v2 API.""" products = list(self.fintoc.v2.products.list()) From 36a47ff4bd56071408e3c224405f852823efbd67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pablo=20Castro=20Leal?= Date: Tue, 23 Jun 2026 15:51:59 -0400 Subject: [PATCH 8/8] feat(invoicing): add v2 subscription item delete endpoint --- fintoc/managers/v2/subscription_items_manager.py | 2 +- tests/test_integration.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/fintoc/managers/v2/subscription_items_manager.py b/fintoc/managers/v2/subscription_items_manager.py index d5dbcee..2f43a76 100644 --- a/fintoc/managers/v2/subscription_items_manager.py +++ b/fintoc/managers/v2/subscription_items_manager.py @@ -7,4 +7,4 @@ class SubscriptionItemsManager(ManagerMixin): """Represents a subscription items manager.""" resource = "subscription_item" - methods = ["create", "update"] + methods = ["create", "update", "delete"] diff --git a/tests/test_integration.py b/tests/test_integration.py index 5e23df8..c200322 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1152,6 +1152,17 @@ def test_v2_subscription_item_update(self): assert item.method == "patch" assert item.url == f"v2/subscriptions/{subscription_id}/items/{item_id}" + def test_v2_subscription_item_delete(self): + """Test deleting a subscription item using v2 API.""" + subscription_id = "test_subscription_id" + item_id = "si_test_item_id" + + result = self.fintoc.v2.subscriptions.items.delete( + item_id, subscription_id=subscription_id + ) + + assert result == item_id + def test_v2_products_list(self): """Test getting all products using v2 API.""" products = list(self.fintoc.v2.products.list())