Metadata-Version: 2.5
Name: moyskladapi
Version: 1.0.0rc1
Summary: Асинхронный клиент для Мой Склад API
Project-URL: Homepage, https://github.com/serdukow/moyskladapi
Project-URL: Documentation, https://github.com/serdukow/moyskladapi#readme
Project-URL: Repository, https://github.com/serdukow/moyskladapi
Author-email: Andrei Serdiukov <serdukow1@gmail.com>
Maintainer-email: Andrei Serdiukov <serdukow1@gmail.com>
License: MIT
License-File: LICENSE
Keywords: api,async,client,moysklad,мой склад
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: <3.14,>=3.12
Requires-Dist: aiolimiter<2.0.0,>=1.2.1
Requires-Dist: httpx<0.29.0,>=0.28.1
Requires-Dist: msgspec>=0.21.1
Provides-Extra: dev
Requires-Dist: mypy<2.0.0,>=1.14.0; extra == 'dev'
Requires-Dist: pre-commit<5.0.0,>=4.3.0; extra == 'dev'
Requires-Dist: ruff<0.15.0,>=0.14.1; extra == 'dev'
Provides-Extra: test
Requires-Dist: pytest-asyncio<2.0.0,>=1.2.0; extra == 'test'
Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == 'test'
Requires-Dist: pytest-httpx<0.37.0,>=0.35.0; extra == 'test'
Requires-Dist: pytest<9.0.0,>=8.4.2; extra == 'test'
Description-Content-Type: text/markdown

<p align="center">
  <a href="https://api.moysklad.ru"><img src="https://www.moysklad.ru/upload/logos/logoMS500.png" alt="MoyskladAPI"></a>
</p>

<div align="center">

<p align="center">
Асинхронная библиотека для работы с API МойСклад
</p>

[![PyPI version](https://img.shields.io/pypi/v/moyskladapi.svg)](https://pypi.org/project/moyskladapi/)
[![Downloads](https://img.shields.io/pypi/dm/moyskladapi.svg)](https://pypi.python.org/pypi/moyskladapi)
[![Tests](https://github.com/serdukow/moysklad-api/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/serdukow/moysklad-api/actions/workflows/tests.yml)
[![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/serdukow/7be3b576bf0ed7b631ba2f5ba500401c/raw/moysklad-api-coverage.json)](https://github.com/serdukow/moysklad-api/actions/workflows/tests.yml)
[![Checked with mypy](https://img.shields.io/badge/mypy-checked-2A6DB2.svg)](https://mypy-lang.org/)
[![API Version](https://img.shields.io/badge/JSON_API-1.2-blue.svg)](https://dev.moysklad.ru/doc/api/remap/1.2/)
[![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue.svg)](https://pypi.org/project/moyskladapi/)
</a>

</div>

## Установка

Требуется Python 3.12+.

```console
pip install moyskladapi
```

## Примеры использования

### Создание, обновление и удаление товара

```python
import asyncio

from moyskladapi import MoyskladAPI
from moyskladapi.types import Product


async def main():
    async with MoyskladAPI(token="your_token_here") as api:
        product = await api.create_product(Product(name="Тестовый товар"))
        print(f"Создан: {product.name} [{product.id}]")

        product = await api.update_product(product.id, Product(description="Описание товара"))
        print(f"Описание: {product.description}")

        await api.delete_product(product.id)
        print("Удалён")


asyncio.run(main())
```

### Фильтрация товаров

```python
import asyncio

from moyskladapi import MoyskladAPI, F


async def main():
    async with MoyskladAPI(token="your_token_here") as api:
        results = await api.get_products(
            filters=[F.archived == False, F.weight > 1.0],
            expand="supplier",
            limit=10,
        )
        for p in results.rows:
            print(f"{p.name}  |  поставщик: {p.supplier.name if p.supplier else '—'}")


asyncio.run(main())
```

### Остатки

```python
import asyncio

from moyskladapi import MoyskladAPI


async def main():
    async with MoyskladAPI(token="your_token_here") as api:
        stock = await api.get_stock_current()
        low = [s for s in stock.rows if s.quantity and s.quantity < 5]
        print(f"Заканчиваются: {len(low)} позиций")


asyncio.run(main())
```

### Постраничный обход

Методы `iter_*` обходят выборку лениво: страницы подгружаются по мере
необходимости, а обработанные — освобождаются. Память не растёт с размером
каталога, поэтому это способ по умолчанию.

```python
import asyncio

from moyskladapi import MoyskladAPI


async def main():
    async with MoyskladAPI(token="your_token_here") as api:
        async for product in api.iter_products():
            print(product.name)


asyncio.run(main())
```

Если нужен готовый список, у методов `get_*` есть параметр `auto_paginate` —
он проходит все страницы и возвращает их одним `MetaArray`:

```python
everything = await api.get_products(auto_paginate=True)
print(f"Всего: {len(everything.rows)}")
```

Пользуйтесь им только для небольших выборок: все записи остаются в памяти
одновременно. На каталоге в 20 000 товаров это около 23 МБ против 2 МБ
у `iter_products()`, и дальше разница растёт линейно. Список из `iter_*`
собирается одной строкой, если он действительно нужен:

```python
everything = [product async for product in api.iter_products()]
```

### Лента событий документа

```python
import asyncio

from moyskladapi import MoyskladAPI
from moyskladapi.types import Note


async def main():
    async with MoyskladAPI(token="your_token_here") as api:
        order_id = "e4609c69-00bc-11ef-ac12-00120000001a"

        note = await api.create_note("customerorder", order_id, Note(description="Согласовано"))
        feed = await api.get_notes("customerorder", order_id)
        print(f"Событий: {len(feed.rows)}")

        await api.delete_note("customerorder", order_id, note.id)


asyncio.run(main())
```

### Обработка ошибок

```python
import asyncio

from moyskladapi import MoyskladAPI, MoyskladAPIError
from moyskladapi.types import Product


async def main():
    async with MoyskladAPI(token="your_token_here") as api:
        try:
            await api.create_product(Product())
        except MoyskladAPIError as exc:
            print(f"HTTP {exc.http_status}: {exc}")
            for error in exc.errors:
                print(f"  код {error.code}: {error.error_message or error.error}")


asyncio.run(main())
```

Ответы с кодом `429` повторяются автоматически с учётом заголовка
`X-Lognex-Retry-TimeInterval` (до `BaseSession.MAX_RETRIES` попыток).

## Лицензия

[MIT](LICENSE)
