Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 0.2.1 - 2025-06-18
* add possibility to iterate over pages when doing all requests instead of loading everything in memory
* improve typing on mixins concerning map_model and raw_data parameters
* update models (add create journal, create bank account, create ledger account)

## 0.2.0 - 2025-06-09
* remove raw-data and x-chift-client-requestid from headers on uncessary endpoints

Expand Down
207 changes: 184 additions & 23 deletions chift/api/mixins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Generic, TypeVar
from typing import Any, Generator, Generic, Literal, TypeVar, overload

from chift.api.client import ChiftClient
from chift.openapi.models import ObjectWithRawData
Expand All @@ -12,6 +12,9 @@ def __init__(self, consumer_id, connection_id):
hasattr(self, "chift_vertical") and self.chift_vertical or None
)
self.chift_model = hasattr(self, "chift_model") and self.chift_model or None
self.chift_model_create = (
hasattr(self, "chift_model_create") and self.chift_model_create or self.chift_model
)
self.extra_path = hasattr(self, "extra_path") and self.extra_path or None
self.consumer_id = consumer_id
self.connection_id = connection_id
Expand All @@ -36,9 +39,49 @@ def delete(self, chift_id, client=None, params=None, client_request_id=None) ->


class ReadMixin(BaseMixin, Generic[T]):
@overload
def get(
self,
chift_id,
client=None,
params=None,
map_model: Literal[True] = True,
raw_data: Literal[False] = False,
) -> T: ...

@overload
def get(
self,
chift_id,
client=None,
params=None,
map_model: Literal[False] = False,
raw_data: Literal[False] = False,
) -> dict: ...

@overload
def get(
self,
chift_id,
client=None,
params=None,
map_model: Literal[True] = True,
raw_data: Literal[True] = True,
) -> ObjectWithRawData[T]: ...

@overload
def get(
self,
chift_id,
client=None,
params=None,
map_model: Literal[False] = False,
raw_data: Literal[True] = True,
) -> dict: ...

def get(
self, chift_id, client=None, params=None, map_model=True, raw_data=False
) -> T:
) -> T | dict | ObjectWithRawData[T]:
if not client:
client = ChiftClient()
client.consumer_id = self.consumer_id
Expand All @@ -55,7 +98,7 @@ def get(
)
if raw_data:
if map_model:
return ObjectWithRawData(
return ObjectWithRawData[T](
chift_data=self.model(**json_data),
raw_data=json_data.get("raw_data") or {},
)
Expand All @@ -65,9 +108,29 @@ def get(


class CreateMixin(BaseMixin, Generic[T]):
@overload
def create(
self,
data,
client=None,
params=None,
map_model: Literal[True] = True,
client_request_id=None,
) -> T: ...

@overload
def create(
self,
data,
client=None,
params=None,
map_model: Literal[False] = False,
client_request_id=None,
) -> dict: ...

def create(
self, data, client=None, params=None, map_model=True, client_request_id=None
) -> T:
) -> T | dict:
if not client:
client = ChiftClient()
client.consumer_id = self.consumer_id
Expand All @@ -77,7 +140,7 @@ def create(

json_data = client.post_one(
self.chift_vertical,
self.chift_model,
self.chift_model_create,
data,
extra_path=self.extra_path,
params=params,
Expand All @@ -87,6 +150,28 @@ def create(


class UpdateMixin(BaseMixin, Generic[T]):
@overload
def update(
self,
chift_id,
data,
client=None,
params=None,
map_model: Literal[True] = True,
client_request_id=None,
) -> T: ...

@overload
def update(
self,
chift_id,
data,
client=None,
params=None,
map_model: Literal[False] = False,
client_request_id=None,
) -> dict: ...

def update(
self,
chift_id,
Expand All @@ -95,7 +180,7 @@ def update(
params=None,
map_model=True,
client_request_id=None,
) -> T:
) -> T | dict:
if not client:
client = ChiftClient()
client.consumer_id = self.consumer_id
Expand All @@ -116,9 +201,13 @@ def update(


class PaginationMixin(BaseMixin, Generic[T]):
def all(
self, params=None, client=None, map_model=True, limit=None, raw_data=False
) -> list[T]:
def __iter_page(
self,
params=None,
client=None,
limit=None,
raw_data=False,
) -> Generator[dict, Any, None]:
if not client:
client = ChiftClient()
client.consumer_id = self.consumer_id
Expand All @@ -131,8 +220,8 @@ def all(

size = limit if limit and limit < 100 else 100

all_items = []
page = 1
count = 0

while True:
json_data = client.get_all(
Expand All @@ -141,25 +230,97 @@ def all(
params={"page": page, "size": size} | params,
extra_path=self.extra_path,
)
yield json_data
page += 1
count += len(json_data.get("items", []))
total = json_data.get("total", 0)
if count >= total or (limit and count >= limit):
break

@overload
def all(
self,
params=None,
client=None,
map_model: Literal[True] = True,
limit=None,
raw_data: Literal[False] = False,
) -> list[T]: ...

@overload
def all(
self,
params=None,
client=None,
map_model: Literal[False] = False,
limit=None,
raw_data: Literal[False] = False,
) -> list[dict]: ...

@overload
def all(
self,
params=None,
client=None,
map_model=False,
limit=False,
raw_data: Literal[True] = True,
) -> dict: ...

def all(
self, params=None, client=None, map_model=True, limit=None, raw_data=False
) -> list[T | dict] | dict:
all_items = []
for page in self.__iter_page(
params=params,
client=client,
limit=limit,
raw_data=raw_data,
):
if raw_data:
return json_data.get("raw_data") or {}
return page.get("raw_data") or {}
all_items.extend(
[
self.model(**item) if map_model else item
for item in json_data.get("items", [])
]
self.model(**item) if map_model else item
for item in page.get("items", [])
)
page += 1
return all_items

if limit and len(all_items) >= limit:
break
@overload
def iter_all(
self,
params=None,
client=None,
map_model: Literal[True] = True,
limit=None,
) -> Generator[T, Any, None]: ...

if len(all_items) >= json_data.get("total", 0) or not json_data.get(
"items"
):
break
@overload
def iter_all(
self,
params=None,
client=None,
map_model: Literal[False] = False,
limit=None,
) -> Generator[dict, Any, None]: ...

return all_items
def iter_all(
self,
params=None,
client=None,
map_model=True,
limit=None,
) -> Generator[T | dict, Any, None]:
for page in self.__iter_page(
params=params,
client=client,
limit=limit,
raw_data=False,
):
for item in page.get("items", []):
if map_model:
yield self.model(**item)
else:
yield item


class ListMixin(BaseMixin, Generic[T]):
Expand Down
10 changes: 10 additions & 0 deletions chift/models/consumers/accounting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from chift.openapi.models import Outstanding as OutstandingModel
from chift.openapi.models import Supplier as SupplierModel
from chift.openapi.models import TaxAccounting as TaxAccountingModel
from chift.openapi.models import BankAccount as BankAccountModel


class AccountingRouter:
Expand All @@ -42,6 +43,7 @@ def __init__(self, consumer_id, connection_id):
self.EntryMatching = EntryMatching(consumer_id, connection_id)
self.MultipleEntryMatching = MultipleEntryMatching(consumer_id, connection_id)
self.Attachment = Attachment(consumer_id, connection_id)
self.BankAccount = BankAccount(consumer_id, connection_id)


class AnalyticPlan(PaginationMixin[AnalyticPlanModel]):
Expand Down Expand Up @@ -82,6 +84,7 @@ class Account(
):
chift_vertical: ClassVar = "accounting"
chift_model: ClassVar = "chart-of-accounts"
chift_model_create: ClassVar = "accounts"
model = AccountModel


Expand Down Expand Up @@ -155,10 +158,12 @@ class Outstanding(


class Journal(
CreateMixin[JournalModel],
PaginationMixin[JournalModel],
):
chift_vertical: ClassVar = "accounting"
chift_model: ClassVar = "journals"
chift_model_create: ClassVar = "journal"
model = JournalModel


Expand Down Expand Up @@ -189,3 +194,8 @@ def create(self, invoice_id, data, client=None, params=None):
self.chift_model = "invoices"
self.extra_path = f"pdf/{invoice_id}"
return super().create(data=data, client=client, params=params, map_model=False)

class BankAccount(CreateMixin[BankAccountModel]):
chift_vertical: ClassVar = "accounting"
chift_model: ClassVar = "bank-accounts"
model = BankAccountModel
13 changes: 10 additions & 3 deletions chift/openapi/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime
from enum import Enum
from typing import List, Optional
from typing import Generic, List, Optional, TypeVar

from pydantic import BaseModel, ConfigDict

Expand All @@ -14,6 +14,7 @@
BackboneApiAppRoutersConnectionsConnectionItem,
BackboneCommonModelsPosCommonProductCategoryItem,
BalanceItemOut,
BankAccountItemOut,
CategoryItem,
ClientItemOut,
ClosureItem,
Expand Down Expand Up @@ -74,6 +75,8 @@
WebhookItem,
)

VarModel = TypeVar("VarModel", bound=BaseModel)

# UNPUBLISHED MODELS


Expand Down Expand Up @@ -381,6 +384,10 @@ class Attachment(AttachmentItemOut):
pass


class BankAccount(BankAccountItemOut):
pass


# log
class Log(ConsumerLog):
pass
Expand Down Expand Up @@ -522,6 +529,6 @@ class PaymentRefund(RefundItemOut):
pass


class ObjectWithRawData(BaseModel):
chift_data: BaseModel
class ObjectWithRawData(BaseModel, Generic[VarModel]):
chift_data: VarModel
raw_data: dict
Loading
Loading