megaplan-sdk 0.1.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {megaplan_sdk-0.1.0/src/megaplan_sdk.egg-info → megaplan_sdk-0.2.1}/PKG-INFO +894 -488
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/README.md +893 -487
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/pyproject.toml +1 -1
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/__init__.py +38 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/auth.py +2 -2
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/cache.py +35 -21
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/client.py +6 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/constants.py +1 -0
- megaplan_sdk-0.2.1/src/megaplan_sdk/filter_builder.py +621 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/http_client.py +15 -13
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/__init__.py +6 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/comment.py +0 -15
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/common.py +1 -15
- megaplan_sdk-0.2.1/src/megaplan_sdk/models/filter.py +82 -0
- megaplan_sdk-0.2.1/src/megaplan_sdk/models/group.py +40 -0
- megaplan_sdk-0.2.1/src/megaplan_sdk/models/milestone.py +81 -0
- megaplan_sdk-0.2.1/src/megaplan_sdk/models/participant.py +74 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/project.py +18 -2
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/task.py +18 -2
- megaplan_sdk-0.2.1/src/megaplan_sdk/resources/__init__.py +25 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/base.py +233 -17
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/comments.py +1 -1
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/contractors.py +59 -8
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/deals.py +179 -9
- megaplan_sdk-0.2.1/src/megaplan_sdk/resources/filters.py +444 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/projects.py +199 -22
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/tasks.py +367 -31
- megaplan_sdk-0.2.1/src/megaplan_sdk/types.py +98 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1/src/megaplan_sdk.egg-info}/PKG-INFO +894 -488
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk.egg-info/SOURCES.txt +6 -0
- megaplan_sdk-0.1.0/src/megaplan_sdk/resources/__init__.py +0 -15
- megaplan_sdk-0.1.0/src/megaplan_sdk/types.py +0 -56
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/LICENSE +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/setup.cfg +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/exceptions.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/helpers.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/logging_config.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/base.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/contractor.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/deal.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/department.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/models/employee.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/auth.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/departments.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/employees.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk/resources/full_details.py +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk.egg-info/dependency_links.txt +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk.egg-info/requires.txt +0 -0
- {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.1}/src/megaplan_sdk.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: megaplan-sdk
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Professional Python SDK for Megaplan API v3
|
|
5
5
|
Author-email: Maxim Borzov <max@borzov.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -54,6 +54,35 @@ Dynamic: license-file
|
|
|
54
54
|
|
|
55
55
|
Библиотека полностью асинхронная, что позволяет эффективно работать с большими объемами данных и выполнять параллельные запросы. Встроенная логика повторных попыток при временных сбоях сервера делает интеграцию более надежной. Модульная архитектура позволяет легко расширять функциональность и добавлять поддержку новых модулей API.
|
|
56
56
|
|
|
57
|
+
## Содержание
|
|
58
|
+
|
|
59
|
+
### Основы
|
|
60
|
+
- [Быстрый старт](#быстрый-старт)
|
|
61
|
+
- [Авторизация](#авторизация)
|
|
62
|
+
- [Helper-функции](#helper-функции)
|
|
63
|
+
- [Обработка ошибок](#обработка-ошибок)
|
|
64
|
+
|
|
65
|
+
### Работа с сущностями
|
|
66
|
+
- [Общие паттерны](#общие-паттерны-работы-с-сущностями)
|
|
67
|
+
- [Задачи](#работа-с-задачами)
|
|
68
|
+
- [Проекты](#работа-с-проектами)
|
|
69
|
+
- [Сделки](#работа-со-сделками)
|
|
70
|
+
|
|
71
|
+
### Продвинутые возможности
|
|
72
|
+
- [Кэширование сущностей](#кэширование-сущностей)
|
|
73
|
+
- [Глобальные дефолтные лимиты](#глобальные-дефолтные-лимиты)
|
|
74
|
+
- [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
|
|
75
|
+
- [Работа с фильтрами](#работа-с-фильтрами)
|
|
76
|
+
- [Настройка HTTP-клиента](#настройка-http-клиента)
|
|
77
|
+
- [Работа через прокси](#работа-через-прокси)
|
|
78
|
+
- [Ручное управление токенами](#ручное-управление-токенами)
|
|
79
|
+
|
|
80
|
+
### Справочная информация
|
|
81
|
+
- [Известные ограничения API](#известные-ограничения-api)
|
|
82
|
+
- [Архитектура](#архитектура)
|
|
83
|
+
- [Требования](#требования)
|
|
84
|
+
- [Разработка](#разработка)
|
|
85
|
+
|
|
57
86
|
## Возможности
|
|
58
87
|
|
|
59
88
|
- Полный CRUD для задач, проектов и сделок
|
|
@@ -62,6 +91,12 @@ Dynamic: license-file
|
|
|
62
91
|
- Типобезопасность с Pydantic-моделями и полной типизацией
|
|
63
92
|
- Асинхронность — поддержка async/await во всех операциях
|
|
64
93
|
- Автоматические повторы при ошибках сервера (5xx)
|
|
94
|
+
- Кэширование сущностей с LRU и TTL для оптимизации запросов
|
|
95
|
+
- FilterBuilder для создания фильтров с fluent API
|
|
96
|
+
- Параметр `expand` для автоматической подгрузки связанных сущностей
|
|
97
|
+
- Метод `iterate()` для автоматической пагинации больших списков
|
|
98
|
+
- Helper-функции для создания BaseEntity объектов
|
|
99
|
+
- Глобальные дефолтные лимиты для комментариев и истории
|
|
65
100
|
- Модульная архитектура для легкого расширения
|
|
66
101
|
- Комплексные тесты с покрытием 80%+
|
|
67
102
|
|
|
@@ -163,58 +198,311 @@ deal_ref = make_deal_entity(101)
|
|
|
163
198
|
contractor_ref = make_contractor_entity(202)
|
|
164
199
|
```
|
|
165
200
|
|
|
166
|
-
##
|
|
201
|
+
## Обработка ошибок
|
|
167
202
|
|
|
168
|
-
|
|
203
|
+
SDK предоставляет специфичные типы исключений для различных сценариев ошибок:
|
|
169
204
|
|
|
170
205
|
```python
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
fields=None, # any: Набор дополнительных полей
|
|
179
|
-
sort_by=None, # list[dict]: Массив полей сортировки
|
|
180
|
-
only_requested_fields=None # bool: Отдавать только перечисленные поля
|
|
206
|
+
from megaplan_sdk import (
|
|
207
|
+
AuthenticationError, # 401 - Ошибка аутентификации
|
|
208
|
+
AuthorizationError, # 403 - Ошибка авторизации (нет прав)
|
|
209
|
+
NotFoundError, # 404 - Ресурс не найден
|
|
210
|
+
ValidationError, # 422 - Ошибка валидации запроса
|
|
211
|
+
RateLimitError, # 429 - Превышен лимит запросов
|
|
212
|
+
ServerError # 5xx - Ошибка сервера
|
|
181
213
|
)
|
|
182
|
-
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
task = await client.tasks.get(task_id=999)
|
|
217
|
+
except NotFoundError:
|
|
218
|
+
print("Задача не найдена")
|
|
219
|
+
except AuthenticationError:
|
|
220
|
+
print("Ошибка аутентификации")
|
|
221
|
+
except ValidationError as e:
|
|
222
|
+
print(f"Ошибки валидации: {e.errors}")
|
|
223
|
+
# e.errors содержит список ошибок из API
|
|
183
224
|
```
|
|
184
225
|
|
|
185
|
-
|
|
226
|
+
## Общие паттерны работы с сущностями
|
|
227
|
+
|
|
228
|
+
Большинство сущностей (задачи, проекты, сделки) поддерживают одинаковые операции CRUD и паттерны работы. В этом разделе описаны общие методы, которые применяются ко всем типам сущностей.
|
|
229
|
+
|
|
230
|
+
### Базовые операции CRUD
|
|
231
|
+
|
|
232
|
+
Все ресурсы поддерживают стандартные операции:
|
|
233
|
+
|
|
234
|
+
#### Получение списка (`list`)
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
# Общий формат для всех ресурсов
|
|
238
|
+
entities = await client.{resource}.list(
|
|
239
|
+
limit=None, # int: Количество элементов на странице
|
|
240
|
+
page_after=None, # dict: Загрузить страницу, начиная с этой сущности
|
|
241
|
+
page_before=None, # dict: Загрузить страницу строго до этой сущности
|
|
242
|
+
page_with=None, # dict: Загрузить страницу с наличием этой сущности
|
|
243
|
+
fields=None, # any: Набор дополнительных полей
|
|
244
|
+
sort_by=None, # list[dict]: Массив полей сортировки
|
|
245
|
+
only_requested_fields=None # bool: Отдавать только перечисленные поля
|
|
246
|
+
)
|
|
247
|
+
```
|
|
186
248
|
|
|
249
|
+
**Примеры:**
|
|
187
250
|
```python
|
|
188
251
|
# Получить все задачи
|
|
189
252
|
tasks = await client.tasks.list()
|
|
190
253
|
|
|
191
|
-
#
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
254
|
+
# Получить проекты с лимитом
|
|
255
|
+
projects = await client.projects.list(limit=50)
|
|
256
|
+
|
|
257
|
+
# Получить сделки с пагинацией
|
|
258
|
+
deals = await client.deals.list(limit=100, page_after={"contentType": "Deal", "id": 100})
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Получение по ID (`get`)
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
entity = await client.{resource}.get({resource}_id=42)
|
|
265
|
+
# Возвращает: объект сущности со всеми полями
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Примеры:**
|
|
269
|
+
```python
|
|
270
|
+
task = await client.tasks.get(task_id=42)
|
|
271
|
+
project = await client.projects.get(project_id=5)
|
|
272
|
+
deal = await client.deals.get(deal_id=200)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### Создание (`create`)
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
entity = await client.{resource}.create({resource}_data={
|
|
279
|
+
"name": "Название", # Обязательное поле
|
|
280
|
+
# ... другие поля
|
|
281
|
+
})
|
|
282
|
+
# Возвращает: созданная сущность
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Примеры:**
|
|
286
|
+
```python
|
|
287
|
+
# Простое создание задачи
|
|
288
|
+
task = await client.tasks.create({"name": "Новая задача"})
|
|
289
|
+
|
|
290
|
+
# Создание проекта
|
|
291
|
+
project = await client.projects.create({"name": "Новый проект"})
|
|
292
|
+
|
|
293
|
+
# Создание сделки (требует program)
|
|
294
|
+
deal = await client.deals.create({
|
|
295
|
+
"name": "Новая сделка",
|
|
296
|
+
"program": {"contentType": "Program", "id": 10}
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Обновление (`update`)
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
entity = await client.{resource}.update(
|
|
304
|
+
{resource}_id=42,
|
|
305
|
+
{resource}_data={
|
|
306
|
+
"name": "Обновленное название",
|
|
307
|
+
# ... другие поля для обновления
|
|
308
|
+
}
|
|
195
309
|
)
|
|
310
|
+
# Возвращает: обновленная сущность
|
|
311
|
+
```
|
|
196
312
|
|
|
197
|
-
|
|
198
|
-
|
|
313
|
+
**Примеры:**
|
|
314
|
+
```python
|
|
315
|
+
task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
|
|
316
|
+
project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
|
|
317
|
+
deal = await client.deals.update(deal_id=200, deal_data={"sum_base": 60000.0})
|
|
318
|
+
```
|
|
199
319
|
|
|
200
|
-
|
|
201
|
-
tasks = await client.tasks.list(filter={"status": "active"})
|
|
320
|
+
#### Удаление (`delete`)
|
|
202
321
|
|
|
203
|
-
|
|
322
|
+
```python
|
|
323
|
+
await client.{resource}.delete({resource}_id=42)
|
|
324
|
+
# Возвращает: None
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Примеры:**
|
|
328
|
+
```python
|
|
329
|
+
await client.tasks.delete(task_id=42)
|
|
330
|
+
await client.projects.delete(project_id=5)
|
|
331
|
+
await client.deals.delete(deal_id=200)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Пагинация
|
|
335
|
+
|
|
336
|
+
SDK поддерживает несколько способов работы с большими списками:
|
|
337
|
+
|
|
338
|
+
#### Ручная пагинация
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
# Пагинация "после" определенной сущности
|
|
342
|
+
entities = await client.tasks.list(
|
|
343
|
+
limit=50,
|
|
344
|
+
page_after={"contentType": "Task", "id": 100}
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Пагинация "до" определенной сущности
|
|
348
|
+
entities = await client.tasks.list(
|
|
349
|
+
limit=50,
|
|
350
|
+
page_before={"contentType": "Task", "id": 200}
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Пагинация "с" определенной сущностью
|
|
354
|
+
entities = await client.tasks.list(
|
|
355
|
+
limit=50,
|
|
356
|
+
page_with={"contentType": "Task", "id": 150}
|
|
357
|
+
)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Автоматическая пагинация с `iterate()`
|
|
361
|
+
|
|
362
|
+
Метод `iterate()` автоматически обрабатывает пагинацию и возвращает все элементы:
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
# Итерация по всем задачам
|
|
204
366
|
async for task in client.tasks.iterate(limit=100):
|
|
205
367
|
print(task.name)
|
|
368
|
+
|
|
369
|
+
# Итерация по всем проектам
|
|
370
|
+
async for project in client.projects.iterate(limit=50):
|
|
371
|
+
print(project.name)
|
|
372
|
+
|
|
373
|
+
# Итерация по всем сделкам
|
|
374
|
+
async for deal in client.deals.iterate(limit=200):
|
|
375
|
+
print(deal.name)
|
|
206
376
|
```
|
|
207
377
|
|
|
208
|
-
### Получение
|
|
378
|
+
### Получение полной информации (`get_full_details`)
|
|
379
|
+
|
|
380
|
+
Метод `get_full_details()` позволяет получить сущность со всеми связанными данными за один вызов. Все запросы выполняются параллельно для максимальной производительности.
|
|
209
381
|
|
|
382
|
+
**Общий формат:**
|
|
210
383
|
```python
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
#
|
|
214
|
-
#
|
|
384
|
+
details = await client.{resource}.get_full_details(
|
|
385
|
+
{resource}_id=42,
|
|
386
|
+
include_comments=True, # Загрузить комментарии
|
|
387
|
+
include_history=True, # Загрузить историю изменений
|
|
388
|
+
comments_limit=50, # Лимит комментариев (опционально)
|
|
389
|
+
history_limit=100 # Лимит записей истории (опционально)
|
|
390
|
+
# ... другие специфичные параметры для каждого типа
|
|
391
|
+
)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Примеры для разных типов:**
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
# Задача со всеми данными
|
|
398
|
+
task_details = await client.tasks.get_full_details(
|
|
399
|
+
task_id=42,
|
|
400
|
+
include_comments=True,
|
|
401
|
+
include_sub_tasks=True,
|
|
402
|
+
include_responsible_details=True
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Проект со всеми данными
|
|
406
|
+
project_details = await client.projects.get_full_details(
|
|
407
|
+
project_id=5,
|
|
408
|
+
include_deals=True,
|
|
409
|
+
include_issues=True,
|
|
410
|
+
include_comments=True
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Сделка со всеми данными
|
|
414
|
+
deal_details = await client.deals.get_full_details(
|
|
415
|
+
deal_id=200,
|
|
416
|
+
include_comments=True,
|
|
417
|
+
include_status_history=True,
|
|
418
|
+
include_contractor_details=True
|
|
419
|
+
)
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**Доступ к данным:**
|
|
423
|
+
```python
|
|
424
|
+
# Основная сущность
|
|
425
|
+
print(details.task.name) # для задач
|
|
426
|
+
print(details.project.name) # для проектов
|
|
427
|
+
print(details.deal.name) # для сделок
|
|
428
|
+
|
|
429
|
+
# Связанные данные
|
|
430
|
+
if details.comments:
|
|
431
|
+
for comment in details.comments:
|
|
432
|
+
print(comment.text)
|
|
433
|
+
|
|
434
|
+
if details.history:
|
|
435
|
+
print(f"Записей в истории: {len(details.history)}")
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Подробнее о специфичных параметрах для каждого типа сущностей см. в соответствующих разделах:
|
|
439
|
+
- [Задачи](#работа-с-задачами)
|
|
440
|
+
- [Проекты](#работа-с-проектами)
|
|
441
|
+
- [Сделки](#работа-со-сделками)
|
|
442
|
+
|
|
443
|
+
## Работа с задачами
|
|
444
|
+
|
|
445
|
+
> **Примечание:** Базовые операции CRUD (list, get, create, update, delete) и пагинация описаны в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
|
|
446
|
+
|
|
447
|
+
### Специфичные параметры для задач
|
|
448
|
+
|
|
449
|
+
#### Получение списка задач с фильтрацией
|
|
450
|
+
|
|
451
|
+
Метод `list()` поддерживает дополнительные параметры для задач:
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
tasks = await client.tasks.list(
|
|
455
|
+
filter=None, # TaskFilter: ID фильтра (int/str) или FilterBuilder объект
|
|
456
|
+
statuses=None, # list[str]: Статусы задач для фильтрации
|
|
457
|
+
# ... остальные параметры из общих паттернов
|
|
458
|
+
)
|
|
215
459
|
```
|
|
216
460
|
|
|
217
|
-
|
|
461
|
+
**Примеры использования фильтров:**
|
|
462
|
+
|
|
463
|
+
```python
|
|
464
|
+
# С фильтром по статусам
|
|
465
|
+
tasks = await client.tasks.list(
|
|
466
|
+
statuses=["assigned", "in_progress"],
|
|
467
|
+
limit=50
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
# С фильтром по ID (int или str)
|
|
471
|
+
tasks = await client.tasks.list(filter=123)
|
|
472
|
+
tasks = await client.tasks.list(filter="incoming")
|
|
473
|
+
|
|
474
|
+
# С FilterBuilder для текстового поиска (рекомендуется)
|
|
475
|
+
from megaplan_sdk import TaskFilterBuilder
|
|
476
|
+
|
|
477
|
+
# Простой поиск по названию
|
|
478
|
+
filter_obj = TaskFilterBuilder().field("name").contains("договор").build()
|
|
479
|
+
tasks = await client.tasks.list(filter=filter_obj)
|
|
480
|
+
|
|
481
|
+
# Несколько условий с AND
|
|
482
|
+
filter_obj = (
|
|
483
|
+
TaskFilterBuilder()
|
|
484
|
+
.field("name").contains("договор")
|
|
485
|
+
.and_()
|
|
486
|
+
.field("name").starts_with("Важный")
|
|
487
|
+
.build()
|
|
488
|
+
)
|
|
489
|
+
tasks = await client.tasks.list(filter=filter_obj)
|
|
490
|
+
|
|
491
|
+
# Условия с OR
|
|
492
|
+
filter_obj = (
|
|
493
|
+
TaskFilterBuilder()
|
|
494
|
+
.field("name").contains("договор")
|
|
495
|
+
.or_()
|
|
496
|
+
.field("name").contains("соглашение")
|
|
497
|
+
.build()
|
|
498
|
+
)
|
|
499
|
+
tasks = await client.tasks.list(filter=filter_obj)
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
Подробнее о работе с фильтрами см. раздел [Работа с фильтрами](#работа-с-фильтрами).
|
|
503
|
+
|
|
504
|
+
### Поля модели Task
|
|
505
|
+
|
|
218
506
|
- `id: int` - Идентификатор задачи
|
|
219
507
|
- `name: str` - Название задачи
|
|
220
508
|
- `description: str` - Описание
|
|
@@ -232,15 +520,11 @@ task = await client.tasks.get(task_id=42)
|
|
|
232
520
|
- `created_at: str` - Дата создания
|
|
233
521
|
- `updated_at: str` - Дата обновления
|
|
234
522
|
|
|
235
|
-
###
|
|
523
|
+
### Упрощенные методы создания
|
|
236
524
|
|
|
237
|
-
|
|
525
|
+
Помимо стандартного `create()`, задачи поддерживают упрощенные методы:
|
|
238
526
|
|
|
239
527
|
```python
|
|
240
|
-
# Простое создание задачи с автоматическим заполнением обязательных полей
|
|
241
|
-
# Автоматически устанавливает isUrgent=False, isTemplate=False
|
|
242
|
-
task = await client.tasks.create({"name": "Новая задача"})
|
|
243
|
-
|
|
244
528
|
# Создание задачи с текущим пользователем как ответственным
|
|
245
529
|
task = await client.tasks.create_simple(
|
|
246
530
|
"Новая задача",
|
|
@@ -248,7 +532,6 @@ task = await client.tasks.create_simple(
|
|
|
248
532
|
)
|
|
249
533
|
|
|
250
534
|
# Создание задачи с указанным ответственным
|
|
251
|
-
from megaplan_sdk import make_employee_entity
|
|
252
535
|
task = await client.tasks.create_simple(
|
|
253
536
|
"Новая задача",
|
|
254
537
|
responsible_id=123
|
|
@@ -262,54 +545,7 @@ task = await client.tasks.create_in_project(
|
|
|
262
545
|
)
|
|
263
546
|
```
|
|
264
547
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
```python
|
|
268
|
-
# Использование helper-функций для создания BaseEntity объектов
|
|
269
|
-
from megaplan_sdk import make_employee_entity, make_project_entity, make_task_entity
|
|
270
|
-
|
|
271
|
-
task = await client.tasks.create({
|
|
272
|
-
"name": "Новая задача", # str: Название задачи (обязательно)
|
|
273
|
-
"responsible": make_employee_entity(1), # BaseEntity: Ответственный (helper)
|
|
274
|
-
"deadline": "2024-12-31", # str: Срок выполнения
|
|
275
|
-
"subject": "Описание задачи", # str: Описание
|
|
276
|
-
"parent": make_project_entity(5), # BaseEntity: Родительский проект (helper)
|
|
277
|
-
"priority": "high", # str: Приоритет
|
|
278
|
-
"isUrgent": False, # bool: Горящая (обязательно, но можно не указывать)
|
|
279
|
-
"isTemplate": False, # bool: Шаблон (обязательно, но можно не указывать)
|
|
280
|
-
})
|
|
281
|
-
# Возвращает: Task - созданная задача
|
|
282
|
-
|
|
283
|
-
# Или вручную создавать BaseEntity
|
|
284
|
-
task = await client.tasks.create({
|
|
285
|
-
"name": "Новая задача",
|
|
286
|
-
"responsible": {"contentType": "Employee", "id": 1},
|
|
287
|
-
"parent": {"contentType": "Project", "id": 5},
|
|
288
|
-
})
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### Обновление задачи
|
|
292
|
-
|
|
293
|
-
```python
|
|
294
|
-
task = await client.tasks.update(
|
|
295
|
-
task_id=42, # int: Идентификатор задачи
|
|
296
|
-
task_data={ # dict: Данные для обновления
|
|
297
|
-
"status": "completed",
|
|
298
|
-
"actualFinish": "2024-01-15",
|
|
299
|
-
"name": "Обновленное название"
|
|
300
|
-
}
|
|
301
|
-
)
|
|
302
|
-
# Возвращает: Task - обновленная задача
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### Удаление задачи
|
|
306
|
-
|
|
307
|
-
```python
|
|
308
|
-
await client.tasks.delete(task_id=42)
|
|
309
|
-
# Параметры:
|
|
310
|
-
# task_id: int - Идентификатор задачи
|
|
311
|
-
# Возвращает: None
|
|
312
|
-
```
|
|
548
|
+
**Примечание:** Стандартный метод `create()` также поддерживается. При создании задачи автоматически устанавливаются `isUrgent=False` и `isTemplate=False`, если они не указаны явно.
|
|
313
549
|
|
|
314
550
|
### Получение подзадач
|
|
315
551
|
|
|
@@ -335,6 +571,53 @@ actual_subtasks = await client.tasks.get_actual_sub_tasks(
|
|
|
335
571
|
# Возвращает: list[Task] - список актуальных подзадач
|
|
336
572
|
```
|
|
337
573
|
|
|
574
|
+
### Получение доступных родителей
|
|
575
|
+
|
|
576
|
+
Методы для получения доступных надзадач и надпроектов (для выбора родителя при создании или перемещении задачи):
|
|
577
|
+
|
|
578
|
+
```python
|
|
579
|
+
# Глобальный поиск доступных родителей для новой задачи
|
|
580
|
+
# Возвращает список Task и Project объектов
|
|
581
|
+
parents = await client.tasks.get_available_parents(
|
|
582
|
+
is_template=False, # bool: Фильтр по шаблонам
|
|
583
|
+
limit=10, # int: Количество элементов
|
|
584
|
+
)
|
|
585
|
+
for parent in parents:
|
|
586
|
+
print(f"{type(parent).__name__}: {parent.name}") # "Task: ..." или "Project: ..."
|
|
587
|
+
|
|
588
|
+
# Доступные родители для существующей задачи
|
|
589
|
+
# Исключает саму задачу и её потомков
|
|
590
|
+
parents = await client.tasks.get_available_parents_for(
|
|
591
|
+
task_id=123,
|
|
592
|
+
is_template=False,
|
|
593
|
+
limit=10,
|
|
594
|
+
)
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
**Примечание:** Методы возвращают смешанный список объектов `Task` и `Project`, так как задача может быть вложена как в другую задачу, так и в проект.
|
|
598
|
+
|
|
599
|
+
### Получение всех участников задачи
|
|
600
|
+
|
|
601
|
+
Метод `get_all_participants()` возвращает полный список участников задачи (ответственный, соисполнители, аудиторы, владелец) в одном запросе:
|
|
602
|
+
|
|
603
|
+
```python
|
|
604
|
+
participants = await client.tasks.get_all_participants(
|
|
605
|
+
task_id=123,
|
|
606
|
+
limit=None, # int: Количество элементов
|
|
607
|
+
# ... стандартные параметры пагинации
|
|
608
|
+
)
|
|
609
|
+
# Возвращает: list[Employee | ContractorHuman | Group]
|
|
610
|
+
|
|
611
|
+
for participant in participants:
|
|
612
|
+
if hasattr(participant, 'display_name'):
|
|
613
|
+
print(participant.display_name())
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Типы участников:**
|
|
617
|
+
- `Employee` — сотрудник организации
|
|
618
|
+
- `ContractorHuman` — контрагент-физлицо
|
|
619
|
+
- `Group` — группа участников (например, отдел)
|
|
620
|
+
|
|
338
621
|
### Получение задач на уровне дерева
|
|
339
622
|
|
|
340
623
|
```python
|
|
@@ -353,48 +636,23 @@ tasks = await client.tasks.tree_level(
|
|
|
353
636
|
|
|
354
637
|
### Получение полной информации о задаче
|
|
355
638
|
|
|
356
|
-
Метод `get_full_details()`
|
|
639
|
+
Метод `get_full_details()` для задач поддерживает следующие специфичные параметры:
|
|
357
640
|
|
|
358
641
|
```python
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
include_owner_details=True, # Загрузить полные данные постановщика
|
|
374
|
-
comments_limit=50, # Лимит комментариев (опционально)
|
|
375
|
-
history_limit=100 # Лимит записей истории (опционально)
|
|
376
|
-
)
|
|
377
|
-
|
|
378
|
-
# Доступ к основным данным задачи
|
|
379
|
-
print(f"Задача: {details.task.name}")
|
|
380
|
-
print(f"Статус: {details.task.status}")
|
|
381
|
-
|
|
382
|
-
# Доступ к связанным данным
|
|
383
|
-
if details.comments:
|
|
384
|
-
print(f"Комментариев: {len(details.comments)}")
|
|
385
|
-
for comment in details.comments:
|
|
386
|
-
print(f" - {comment.text}")
|
|
387
|
-
|
|
388
|
-
if details.sub_tasks:
|
|
389
|
-
print(f"Подзадач: {len(details.sub_tasks)}")
|
|
390
|
-
for subtask in details.sub_tasks:
|
|
391
|
-
print(f" - {subtask.name}")
|
|
392
|
-
|
|
393
|
-
if details.responsible_details:
|
|
394
|
-
print(f"Ответственный: {details.responsible_details.first_name} {details.responsible_details.last_name}")
|
|
395
|
-
|
|
396
|
-
if details.owner_details:
|
|
397
|
-
print(f"Постановщик: {details.owner_details.first_name} {details.owner_details.last_name}")
|
|
642
|
+
details = await client.tasks.get_full_details(
|
|
643
|
+
task_id=42,
|
|
644
|
+
include_sub_tasks=True, # Загрузить подзадачи
|
|
645
|
+
include_actual_sub_tasks=True, # Загрузить актуальные подзадачи
|
|
646
|
+
include_comments=True, # Загрузить комментарии
|
|
647
|
+
include_history=True, # Загрузить историю изменений
|
|
648
|
+
include_auditors=True, # Загрузить список аудиторов
|
|
649
|
+
include_executors=True, # Загрузить соисполнителей
|
|
650
|
+
include_milestones=True, # Загрузить вехи
|
|
651
|
+
include_responsible_details=True, # Загрузить полные данные ответственного
|
|
652
|
+
include_owner_details=True, # Загрузить полные данные постановщика
|
|
653
|
+
comments_limit=50, # Лимит комментариев (опционально)
|
|
654
|
+
history_limit=100 # Лимит записей истории (опционально)
|
|
655
|
+
)
|
|
398
656
|
```
|
|
399
657
|
|
|
400
658
|
**Поля объекта TaskFullDetails:**
|
|
@@ -405,64 +663,105 @@ async with MegaplanClient(...) as client:
|
|
|
405
663
|
- `history: list[dict] | None` - История изменений
|
|
406
664
|
- `auditors: list[dict] | None` - Аудиторы
|
|
407
665
|
- `executors: list[dict] | None` - Соисполнители
|
|
408
|
-
- `milestones: list[
|
|
666
|
+
- `milestones: list[Milestone] | None` - Вехи
|
|
409
667
|
- `responsible_details: Employee | None` - Полные данные ответственного
|
|
410
668
|
- `owner_details: Employee | None` - Полные данные постановщика
|
|
411
669
|
|
|
412
|
-
|
|
670
|
+
> **Примечание:** Общее описание метода `get_full_details()` и примеры использования см. в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
|
|
671
|
+
|
|
672
|
+
### Работа с вехами (Milestones)
|
|
673
|
+
|
|
674
|
+
Вехи можно получать и создавать для задач и проектов.
|
|
675
|
+
|
|
676
|
+
#### Получение вех
|
|
413
677
|
|
|
414
678
|
```python
|
|
415
|
-
#
|
|
416
|
-
|
|
679
|
+
# Получить вехи задачи
|
|
680
|
+
milestones = await client.tasks.get_milestones(
|
|
681
|
+
task_id=123,
|
|
682
|
+
limit=50 # Опционально
|
|
683
|
+
)
|
|
417
684
|
|
|
418
|
-
#
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
include_history=True,
|
|
423
|
-
comments_limit=20
|
|
685
|
+
# Получить вехи проекта
|
|
686
|
+
milestones = await client.projects.get_milestones(
|
|
687
|
+
project_id=456,
|
|
688
|
+
limit=50 # Опционально
|
|
424
689
|
)
|
|
425
690
|
|
|
426
|
-
#
|
|
691
|
+
# Вехи также доступны через get_full_details()
|
|
427
692
|
details = await client.tasks.get_full_details(
|
|
428
|
-
task_id=
|
|
429
|
-
|
|
430
|
-
include_comments=True,
|
|
431
|
-
include_history=True,
|
|
432
|
-
include_auditors=True,
|
|
433
|
-
include_executors=True,
|
|
434
|
-
include_responsible_details=True,
|
|
435
|
-
include_owner_details=True
|
|
693
|
+
task_id=123,
|
|
694
|
+
include_milestones=True
|
|
436
695
|
)
|
|
696
|
+
if details.milestones:
|
|
697
|
+
for milestone in details.milestones:
|
|
698
|
+
print(f"{milestone.name}: {milestone.type}")
|
|
437
699
|
```
|
|
438
700
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
### Получение списка проектов
|
|
701
|
+
#### Создание вехи
|
|
442
702
|
|
|
443
703
|
```python
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
704
|
+
from megaplan_sdk.models.milestone import Milestone
|
|
705
|
+
|
|
706
|
+
# Создать веху для задачи
|
|
707
|
+
milestone = await client.tasks.add_milestone(
|
|
708
|
+
task_id=123,
|
|
709
|
+
milestone_data={
|
|
710
|
+
"name": "Release 1.0",
|
|
711
|
+
"description": "Release milestone description", # Обязательное поле
|
|
712
|
+
"type": "report", # Обязательное: "report", "reminder", или "note"
|
|
713
|
+
"date": "2026-02-01T10:00:00Z" # Обязательное: ISO 8601 формат
|
|
714
|
+
}
|
|
452
715
|
)
|
|
453
|
-
# Возвращает: list[Project] - список объектов Project
|
|
454
|
-
```
|
|
455
716
|
|
|
456
|
-
|
|
717
|
+
# Или использовать модель Milestone
|
|
718
|
+
milestone = await client.tasks.add_milestone(
|
|
719
|
+
task_id=123,
|
|
720
|
+
milestone_data=Milestone(
|
|
721
|
+
name="Release 1.0",
|
|
722
|
+
description="Release milestone description",
|
|
723
|
+
type="report",
|
|
724
|
+
date="2026-02-01T10:00:00Z"
|
|
725
|
+
)
|
|
726
|
+
)
|
|
457
727
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
728
|
+
# Создать веху для проекта
|
|
729
|
+
milestone = await client.projects.add_milestone(
|
|
730
|
+
project_id=456,
|
|
731
|
+
milestone_data={
|
|
732
|
+
"description": "Phase 1 completion",
|
|
733
|
+
"type": "reminder",
|
|
734
|
+
"date": "2026-03-15T14:00:00Z"
|
|
735
|
+
}
|
|
736
|
+
)
|
|
463
737
|
```
|
|
464
738
|
|
|
465
|
-
|
|
739
|
+
**Обязательные поля при создании вехи:**
|
|
740
|
+
- `description: str` - Описание вехи
|
|
741
|
+
- `type: str` - Тип вехи: `"report"`, `"reminder"`, или `"note"`
|
|
742
|
+
- `date: str | DateTime | dict` - Дата и время вехи (ISO 8601 строка или объект DateTime)
|
|
743
|
+
|
|
744
|
+
**Поля модели Milestone:**
|
|
745
|
+
- `id: int` - Идентификатор вехи
|
|
746
|
+
- `name: str | None` - Название вехи
|
|
747
|
+
- `description: str | None` - Описание
|
|
748
|
+
- `completed: bool | None` - Признак завершенности
|
|
749
|
+
- `type: str | None` - Тип вехи
|
|
750
|
+
- `date: str | DateTime | dict | None` - Дата и время
|
|
751
|
+
- `owner: BaseEntity | None` - Создатель (Employee)
|
|
752
|
+
- `responsible: BaseEntity | None` - Ответственный (Employee)
|
|
753
|
+
- `task: BaseEntity | None` - Связанная задача
|
|
754
|
+
- `project: BaseEntity | None` - Связанный проект
|
|
755
|
+
|
|
756
|
+
**Примечание:** Метод `get_milestones()` может вернуть пустой список для некоторых задач/проектов из-за ограничений API (ошибка 500). Это обрабатывается автоматически.
|
|
757
|
+
|
|
758
|
+
## Работа с проектами
|
|
759
|
+
|
|
760
|
+
> **Примечание:** Базовые операции CRUD (list, get, create, update, delete) описаны в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
|
|
761
|
+
|
|
762
|
+
**Важно:** Проекты не поддерживают фильтрацию через API (параметр `filter` недоступен).
|
|
763
|
+
|
|
764
|
+
### Поля модели Project
|
|
466
765
|
- `id: int` - Идентификатор проекта
|
|
467
766
|
- `name: str` - Название проекта
|
|
468
767
|
- `description: str` - Описание
|
|
@@ -479,15 +778,11 @@ project = await client.projects.get(project_id=5)
|
|
|
479
778
|
- `created_at: str` - Дата создания
|
|
480
779
|
- `updated_at: str` - Дата обновления
|
|
481
780
|
|
|
482
|
-
###
|
|
781
|
+
### Упрощенные методы создания
|
|
483
782
|
|
|
484
|
-
|
|
783
|
+
Помимо стандартного `create()`, проекты поддерживают упрощенный метод:
|
|
485
784
|
|
|
486
785
|
```python
|
|
487
|
-
# Простое создание проекта с автоматическим заполнением обязательных полей
|
|
488
|
-
# Автоматически устанавливает isTemplate=False
|
|
489
|
-
project = await client.projects.create({"name": "Новый проект"})
|
|
490
|
-
|
|
491
786
|
# Создание проекта с текущим пользователем как владельцем и ответственным
|
|
492
787
|
project = await client.projects.create_simple(
|
|
493
788
|
"Новый проект",
|
|
@@ -502,44 +797,7 @@ project = await client.projects.create_simple(
|
|
|
502
797
|
)
|
|
503
798
|
```
|
|
504
799
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
```python
|
|
508
|
-
# Использование helper-функций
|
|
509
|
-
from megaplan_sdk import make_employee_entity
|
|
510
|
-
|
|
511
|
-
project = await client.projects.create({
|
|
512
|
-
"name": "Новый проект", # str: Название проекта (обязательно)
|
|
513
|
-
"owner": make_employee_entity(1), # BaseEntity: Владелец (helper)
|
|
514
|
-
"responsible": make_employee_entity(2), # BaseEntity: Ответственный (helper)
|
|
515
|
-
"deadline": "2024-12-31", # str: Срок выполнения
|
|
516
|
-
"description": "Описание проекта", # str: Описание
|
|
517
|
-
"isTemplate": False, # bool: Шаблон (обязательно, но можно не указывать)
|
|
518
|
-
})
|
|
519
|
-
# Возвращает: Project - созданный проект
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### Обновление проекта
|
|
523
|
-
|
|
524
|
-
```python
|
|
525
|
-
project = await client.projects.update(
|
|
526
|
-
project_id=5, # int: Идентификатор проекта
|
|
527
|
-
project_data={ # dict: Данные для обновления
|
|
528
|
-
"name": "Обновленное название",
|
|
529
|
-
"status": "in_progress"
|
|
530
|
-
}
|
|
531
|
-
)
|
|
532
|
-
# Возвращает: Project - обновленный проект
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
### Удаление проекта
|
|
536
|
-
|
|
537
|
-
```python
|
|
538
|
-
await client.projects.delete(project_id=5)
|
|
539
|
-
# Параметры:
|
|
540
|
-
# project_id: int - Идентификатор проекта
|
|
541
|
-
# Возвращает: None
|
|
542
|
-
```
|
|
800
|
+
**Примечание:** При создании проекта автоматически устанавливается `isTemplate=False`, если не указано явно.
|
|
543
801
|
|
|
544
802
|
### Получение сделок проекта
|
|
545
803
|
|
|
@@ -580,54 +838,65 @@ actual_issues = await client.projects.get_actual_issues(
|
|
|
580
838
|
# Возвращает: list[Task] - список актуальных задач проекта
|
|
581
839
|
```
|
|
582
840
|
|
|
583
|
-
### Получение
|
|
841
|
+
### Получение доступных родителей
|
|
584
842
|
|
|
585
|
-
|
|
843
|
+
Методы для получения доступных родительских проектов (для выбора родителя при создании или перемещении проекта):
|
|
586
844
|
|
|
587
845
|
```python
|
|
588
|
-
|
|
846
|
+
# Глобальный поиск доступных родительских проектов
|
|
847
|
+
parents = await client.projects.get_available_parents(
|
|
848
|
+
is_template=False, # bool: Фильтр по шаблонам
|
|
849
|
+
limit=10, # int: Количество элементов
|
|
850
|
+
)
|
|
851
|
+
for parent in parents:
|
|
852
|
+
print(f"Project: {parent.name}")
|
|
589
853
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
include_history=True, # Загрузить историю изменений
|
|
599
|
-
include_auditors=True, # Загрузить список аудиторов
|
|
600
|
-
include_executors=True, # Загрузить соисполнителей
|
|
601
|
-
include_milestones=True, # Загрузить вехи
|
|
602
|
-
include_responsible_details=True, # Загрузить полные данные ответственного
|
|
603
|
-
include_owner_details=True, # Загрузить полные данные владельца
|
|
604
|
-
comments_limit=50, # Лимит комментариев (опционально)
|
|
605
|
-
history_limit=100 # Лимит записей истории (опционально)
|
|
606
|
-
)
|
|
854
|
+
# Доступные родители для существующего проекта
|
|
855
|
+
# Исключает сам проект и его потомков
|
|
856
|
+
parents = await client.projects.get_available_parents_for(
|
|
857
|
+
project_id=456,
|
|
858
|
+
is_template=False,
|
|
859
|
+
limit=10,
|
|
860
|
+
)
|
|
861
|
+
```
|
|
607
862
|
|
|
608
|
-
|
|
609
|
-
print(f"Проект: {details.project.name}")
|
|
610
|
-
print(f"Статус: {details.project.status}")
|
|
863
|
+
**Примечание:** В отличие от задач, проекты могут быть вложены только в другие проекты, поэтому возвращается список объектов `Project`.
|
|
611
864
|
|
|
612
|
-
|
|
613
|
-
if details.deals:
|
|
614
|
-
print(f"Сделок: {len(details.deals)}")
|
|
615
|
-
for deal in details.deals:
|
|
616
|
-
print(f" - {deal.name}")
|
|
865
|
+
### Получение всех участников проекта
|
|
617
866
|
|
|
618
|
-
|
|
619
|
-
print(f"Задач: {len(details.issues)}")
|
|
620
|
-
for task in details.issues:
|
|
621
|
-
print(f" - {task.name}")
|
|
867
|
+
Метод `get_all_participants()` возвращает полный список участников проекта в одном запросе:
|
|
622
868
|
|
|
623
|
-
|
|
624
|
-
|
|
869
|
+
```python
|
|
870
|
+
participants = await client.projects.get_all_participants(
|
|
871
|
+
project_id=123,
|
|
872
|
+
limit=None, # int: Количество элементов
|
|
873
|
+
)
|
|
874
|
+
# Возвращает: list[Employee | ContractorHuman | Group]
|
|
625
875
|
|
|
626
|
-
|
|
627
|
-
|
|
876
|
+
for participant in participants:
|
|
877
|
+
print(f"{type(participant).__name__}: {participant.display_name()}")
|
|
878
|
+
```
|
|
628
879
|
|
|
629
|
-
|
|
630
|
-
|
|
880
|
+
### Получение полной информации о проекте
|
|
881
|
+
|
|
882
|
+
Метод `get_full_details()` для проектов поддерживает следующие специфичные параметры:
|
|
883
|
+
|
|
884
|
+
```python
|
|
885
|
+
details = await client.projects.get_full_details(
|
|
886
|
+
project_id=5,
|
|
887
|
+
include_deals=True, # Загрузить связанные сделки
|
|
888
|
+
include_issues=True, # Загрузить задачи проекта
|
|
889
|
+
include_actual_issues=True, # Загрузить актуальные задачи
|
|
890
|
+
include_comments=True, # Загрузить комментарии
|
|
891
|
+
include_history=True, # Загрузить историю изменений
|
|
892
|
+
include_auditors=True, # Загрузить список аудиторов
|
|
893
|
+
include_executors=True, # Загрузить соисполнителей
|
|
894
|
+
include_milestones=True, # Загрузить вехи
|
|
895
|
+
include_responsible_details=True, # Загрузить полные данные ответственного
|
|
896
|
+
include_owner_details=True, # Загрузить полные данные владельца
|
|
897
|
+
comments_limit=50, # Лимит комментариев (опционально)
|
|
898
|
+
history_limit=100 # Лимит записей истории (опционально)
|
|
899
|
+
)
|
|
631
900
|
```
|
|
632
901
|
|
|
633
902
|
**Поля объекта ProjectFullDetails:**
|
|
@@ -639,66 +908,63 @@ async with MegaplanClient(...) as client:
|
|
|
639
908
|
- `history: list[dict] | None` - История изменений
|
|
640
909
|
- `auditors: list[dict] | None` - Аудиторы
|
|
641
910
|
- `executors: list[dict] | None` - Соисполнители
|
|
642
|
-
- `milestones: list[
|
|
911
|
+
- `milestones: list[Milestone] | None` - Вехи
|
|
643
912
|
- `responsible_details: Employee | None` - Полные данные ответственного
|
|
644
913
|
- `owner_details: Employee | None` - Полные данные владельца
|
|
645
914
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
```python
|
|
649
|
-
# Минимальный вызов - только основной проект
|
|
650
|
-
details = await client.projects.get_full_details(project_id=5)
|
|
915
|
+
> **Примечание:** Общее описание метода `get_full_details()` и примеры использования см. в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
|
|
651
916
|
|
|
652
|
-
|
|
653
|
-
details = await client.projects.get_full_details(
|
|
654
|
-
project_id=5,
|
|
655
|
-
include_deals=True,
|
|
656
|
-
include_issues=True
|
|
657
|
-
)
|
|
917
|
+
### Работа с вехами (Milestones)
|
|
658
918
|
|
|
659
|
-
|
|
660
|
-
details = await client.projects.get_full_details(
|
|
661
|
-
project_id=5,
|
|
662
|
-
include_deals=True,
|
|
663
|
-
include_issues=True,
|
|
664
|
-
include_comments=True,
|
|
665
|
-
include_history=True,
|
|
666
|
-
include_responsible_details=True,
|
|
667
|
-
include_owner_details=True
|
|
668
|
-
)
|
|
669
|
-
```
|
|
919
|
+
Вехи для проектов работают аналогично вехам для задач. См. раздел [Работа с вехами](#работа-с-вехами-milestones) в разделе "Работа с задачами" для подробностей.
|
|
670
920
|
|
|
671
921
|
## Работа со сделками
|
|
672
922
|
|
|
673
|
-
|
|
923
|
+
> **Примечание:** Базовые операции CRUD (list, get, create, update, delete) описаны в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
|
|
924
|
+
|
|
925
|
+
### Специфичные параметры для сделок
|
|
926
|
+
|
|
927
|
+
#### Получение списка сделок с фильтрацией
|
|
928
|
+
|
|
929
|
+
Метод `list()` поддерживает дополнительные параметры для сделок:
|
|
674
930
|
|
|
675
931
|
```python
|
|
676
932
|
deals = await client.deals.list(
|
|
677
|
-
filter=None,
|
|
678
|
-
status=None,
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
limit=None, # int: Количество элементов на странице
|
|
682
|
-
page_after=None, # dict: Пагинация после
|
|
683
|
-
page_before=None, # dict: Пагинация до
|
|
684
|
-
page_with=None, # dict: Пагинация с
|
|
685
|
-
fields=None, # any: Дополнительные поля
|
|
686
|
-
sort_by=None, # list[dict]: Сортировка
|
|
687
|
-
only_requested_fields=None # bool: Только запрошенные поля
|
|
933
|
+
filter=None, # TradeFilter: ID фильтра (int/str) или FilterBuilder объект
|
|
934
|
+
status=None, # ProgramState: Статус программы для фильтрации
|
|
935
|
+
base_on=None, # BaseEntity: Базовая сущность для фильтрации
|
|
936
|
+
# ... остальные параметры из общих паттернов
|
|
688
937
|
)
|
|
689
|
-
# Возвращает: list[Deal] - список объектов Deal
|
|
690
938
|
```
|
|
691
939
|
|
|
692
|
-
|
|
940
|
+
**Примеры использования фильтров:**
|
|
693
941
|
|
|
694
942
|
```python
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
943
|
+
# С фильтром по ID
|
|
944
|
+
deals = await client.deals.list(filter=123)
|
|
945
|
+
deals = await client.deals.list(filter="active")
|
|
946
|
+
|
|
947
|
+
# С FilterBuilder для текстового поиска (рекомендуется)
|
|
948
|
+
from megaplan_sdk import TradeFilterBuilder
|
|
949
|
+
|
|
950
|
+
# Простой поиск по названию
|
|
951
|
+
filter_obj = TradeFilterBuilder().field("name").contains("Leader").build()
|
|
952
|
+
deals = await client.deals.list(filter=filter_obj)
|
|
953
|
+
|
|
954
|
+
# Несколько условий
|
|
955
|
+
filter_obj = (
|
|
956
|
+
TradeFilterBuilder()
|
|
957
|
+
.field("name").contains("Leader")
|
|
958
|
+
.and_()
|
|
959
|
+
.field("name").starts_with("Важная")
|
|
960
|
+
.build()
|
|
961
|
+
)
|
|
962
|
+
deals = await client.deals.list(filter=filter_obj)
|
|
699
963
|
```
|
|
700
964
|
|
|
701
|
-
|
|
965
|
+
Подробнее о работе с фильтрами см. раздел [Работа с фильтрами](#работа-с-фильтрами).
|
|
966
|
+
|
|
967
|
+
### Поля модели Deal
|
|
702
968
|
- `id: int` - Идентификатор сделки
|
|
703
969
|
- `name: str` - Название сделки
|
|
704
970
|
- `program: BaseEntity` - Программа (схема сделки)
|
|
@@ -706,61 +972,19 @@ deal = await client.deals.get(deal_id=200)
|
|
|
706
972
|
- `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
|
|
707
973
|
- `responsible: BaseEntity` - Ответственный (Employee)
|
|
708
974
|
- `sum_base: float` - Сумма сделки
|
|
709
|
-
- `currency: BaseEntity` - Валюта
|
|
710
|
-
- `deadline: str` - Срок
|
|
711
|
-
- `description: str` - Описание
|
|
712
|
-
- `tags: list[BaseEntity]` - Теги
|
|
713
|
-
- `attaches: list[BaseEntity]` - Вложения
|
|
714
|
-
- `created_at: str` - Дата создания
|
|
715
|
-
- `updated_at: str` - Дата обновления
|
|
716
|
-
|
|
717
|
-
### Создание сделки
|
|
718
|
-
|
|
719
|
-
```python
|
|
720
|
-
deal = await client.deals.create(deal_data={
|
|
721
|
-
"program": { # BaseEntity: Программа (обязательно)
|
|
722
|
-
"contentType": "Program",
|
|
723
|
-
"id": 10
|
|
724
|
-
},
|
|
725
|
-
"name": "Новая сделка", # str: Название сделки (обязательно)
|
|
726
|
-
"contractor": { # BaseEntity: Контрагент
|
|
727
|
-
"contentType": "ContractorCompany",
|
|
728
|
-
"id": 100
|
|
729
|
-
},
|
|
730
|
-
"responsible": { # BaseEntity: Ответственный
|
|
731
|
-
"contentType": "Employee",
|
|
732
|
-
"id": 1
|
|
733
|
-
},
|
|
734
|
-
"sum_base": 50000.0, # float: Сумма сделки
|
|
735
|
-
"deadline": "2024-12-31", # str: Срок
|
|
736
|
-
"description": "Описание сделки" # str: Описание
|
|
737
|
-
})
|
|
738
|
-
# Возвращает: Deal - созданная сделка
|
|
739
|
-
```
|
|
740
|
-
|
|
741
|
-
### Обновление сделки
|
|
742
|
-
|
|
743
|
-
```python
|
|
744
|
-
deal = await client.deals.update(
|
|
745
|
-
deal_id=200, # int: Идентификатор сделки
|
|
746
|
-
deal_data={ # dict: Данные для обновления
|
|
747
|
-
"sum_base": 60000.0,
|
|
748
|
-
"status": "active"
|
|
749
|
-
}
|
|
750
|
-
)
|
|
751
|
-
# Возвращает: Deal - обновленная сделка
|
|
752
|
-
```
|
|
975
|
+
- `currency: BaseEntity` - Валюта
|
|
976
|
+
- `deadline: str` - Срок
|
|
977
|
+
- `description: str` - Описание
|
|
978
|
+
- `tags: list[BaseEntity]` - Теги
|
|
979
|
+
- `attaches: list[BaseEntity]` - Вложения
|
|
980
|
+
- `created_at: str` - Дата создания
|
|
981
|
+
- `updated_at: str` - Дата обновления
|
|
753
982
|
|
|
754
|
-
|
|
983
|
+
**Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
|
|
755
984
|
|
|
756
|
-
|
|
757
|
-
await client.deals.delete(deal_id=200)
|
|
758
|
-
# Параметры:
|
|
759
|
-
# deal_id: int - Идентификатор сделки
|
|
760
|
-
# Возвращает: None
|
|
761
|
-
```
|
|
985
|
+
### Специфичные методы сделок
|
|
762
986
|
|
|
763
|
-
|
|
987
|
+
#### Применение перехода (изменение статуса)
|
|
764
988
|
|
|
765
989
|
```python
|
|
766
990
|
deal = await client.deals.apply_transition(
|
|
@@ -780,6 +1004,23 @@ deal = await client.deals.apply_trigger(
|
|
|
780
1004
|
# Возвращает: Deal - обновленная сделка
|
|
781
1005
|
```
|
|
782
1006
|
|
|
1007
|
+
### Получение всех участников сделки
|
|
1008
|
+
|
|
1009
|
+
Метод `get_all_participants()` возвращает полный список участников сделки:
|
|
1010
|
+
|
|
1011
|
+
```python
|
|
1012
|
+
participants = await client.deals.get_all_participants(
|
|
1013
|
+
deal_id=200,
|
|
1014
|
+
limit=None, # int: Количество элементов
|
|
1015
|
+
)
|
|
1016
|
+
# Возвращает: list[Employee]
|
|
1017
|
+
|
|
1018
|
+
for employee in participants:
|
|
1019
|
+
print(employee.display_name())
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
**Примечание:** В отличие от задач и проектов, сделки возвращают только сотрудников (`Employee`).
|
|
1023
|
+
|
|
783
1024
|
### Получение аудиторов сделки
|
|
784
1025
|
|
|
785
1026
|
```python
|
|
@@ -813,54 +1054,21 @@ exists = await client.deals.check_exists(deal_params={
|
|
|
813
1054
|
|
|
814
1055
|
### Получение полной информации о сделке
|
|
815
1056
|
|
|
816
|
-
Метод `get_full_details()`
|
|
1057
|
+
Метод `get_full_details()` для сделок поддерживает следующие специфичные параметры:
|
|
817
1058
|
|
|
818
1059
|
```python
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
#
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
include_related_tasks=True, # Загрузить связанные задачи
|
|
832
|
-
comments_limit=50, # Лимит комментариев (опционально)
|
|
833
|
-
history_limit=100 # Лимит записей истории (опционально)
|
|
834
|
-
)
|
|
835
|
-
|
|
836
|
-
# Доступ к основным данным сделки
|
|
837
|
-
print(f"Сделка: {details.deal.name}")
|
|
838
|
-
print(f"Сумма: {details.deal.sum_base}")
|
|
839
|
-
print(f"Статус: {details.deal.state.name if details.deal.state else 'Не указан'}")
|
|
840
|
-
|
|
841
|
-
# Доступ к связанным данным
|
|
842
|
-
if details.comments:
|
|
843
|
-
print(f"Комментариев: {len(details.comments)}")
|
|
844
|
-
for comment in details.comments:
|
|
845
|
-
print(f" - {comment.text}")
|
|
846
|
-
|
|
847
|
-
if details.history:
|
|
848
|
-
print(f"Записей в истории: {len(details.history)}")
|
|
849
|
-
|
|
850
|
-
if details.status_history:
|
|
851
|
-
print(f"Изменений статуса: {len(details.status_history)}")
|
|
852
|
-
|
|
853
|
-
if details.responsible_details:
|
|
854
|
-
print(f"Ответственный: {details.responsible_details.first_name} {details.responsible_details.last_name}")
|
|
855
|
-
print(f"Email: {details.responsible_details.email}")
|
|
856
|
-
|
|
857
|
-
if details.contractor_details:
|
|
858
|
-
print(f"Контрагент: {details.contractor_details.name}")
|
|
859
|
-
|
|
860
|
-
if details.related_tasks:
|
|
861
|
-
print(f"Связанных задач: {len(details.related_tasks)}")
|
|
862
|
-
for task in details.related_tasks:
|
|
863
|
-
print(f" - {task.name}")
|
|
1060
|
+
details = await client.deals.get_full_details(
|
|
1061
|
+
deal_id=200,
|
|
1062
|
+
include_comments=True, # Загрузить комментарии
|
|
1063
|
+
include_history=True, # Загрузить историю изменений
|
|
1064
|
+
include_status_history=True, # Загрузить историю статусов
|
|
1065
|
+
include_auditors=True, # Загрузить список аудиторов
|
|
1066
|
+
include_responsible_details=True, # Загрузить полные данные ответственного
|
|
1067
|
+
include_contractor_details=True, # Загрузить полные данные контрагента
|
|
1068
|
+
include_related_tasks=True, # Загрузить связанные задачи
|
|
1069
|
+
comments_limit=50, # Лимит комментариев (опционально)
|
|
1070
|
+
history_limit=100 # Лимит записей истории (опционально)
|
|
1071
|
+
)
|
|
864
1072
|
```
|
|
865
1073
|
|
|
866
1074
|
**Поля объекта DealFullDetails:**
|
|
@@ -873,101 +1081,11 @@ async with MegaplanClient(...) as client:
|
|
|
873
1081
|
- `contractor_details: Contractor | None` - Полные данные контрагента
|
|
874
1082
|
- `related_tasks: list[Task] | None` - Связанные задачи
|
|
875
1083
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
```python
|
|
879
|
-
# Минимальный вызов - только основная сделка
|
|
880
|
-
details = await client.deals.get_full_details(deal_id=200)
|
|
881
|
-
|
|
882
|
-
# Сделка с комментариями и историей
|
|
883
|
-
details = await client.deals.get_full_details(
|
|
884
|
-
deal_id=200,
|
|
885
|
-
include_comments=True,
|
|
886
|
-
include_history=True,
|
|
887
|
-
include_status_history=True,
|
|
888
|
-
comments_limit=20
|
|
889
|
-
)
|
|
890
|
-
|
|
891
|
-
# Полная информация для отчета
|
|
892
|
-
details = await client.deals.get_full_details(
|
|
893
|
-
deal_id=200,
|
|
894
|
-
include_comments=True,
|
|
895
|
-
include_history=True,
|
|
896
|
-
include_status_history=True,
|
|
897
|
-
include_auditors=True,
|
|
898
|
-
include_responsible_details=True,
|
|
899
|
-
include_contractor_details=True,
|
|
900
|
-
include_related_tasks=True
|
|
901
|
-
)
|
|
902
|
-
|
|
903
|
-
# Только данные о людях
|
|
904
|
-
details = await client.deals.get_full_details(
|
|
905
|
-
deal_id=200,
|
|
906
|
-
include_responsible_details=True,
|
|
907
|
-
include_contractor_details=True
|
|
908
|
-
)
|
|
909
|
-
```
|
|
910
|
-
|
|
911
|
-
## Обработка ошибок
|
|
912
|
-
|
|
913
|
-
SDK предоставляет специфичные типы исключений для различных сценариев ошибок:
|
|
914
|
-
|
|
915
|
-
```python
|
|
916
|
-
from megaplan_sdk import (
|
|
917
|
-
AuthenticationError, # 401 - Ошибка аутентификации
|
|
918
|
-
AuthorizationError, # 403 - Ошибка авторизации (нет прав)
|
|
919
|
-
NotFoundError, # 404 - Ресурс не найден
|
|
920
|
-
ValidationError, # 422 - Ошибка валидации запроса
|
|
921
|
-
RateLimitError, # 429 - Превышен лимит запросов
|
|
922
|
-
ServerError # 5xx - Ошибка сервера
|
|
923
|
-
)
|
|
924
|
-
|
|
925
|
-
try:
|
|
926
|
-
task = await client.tasks.get(task_id=999)
|
|
927
|
-
except NotFoundError:
|
|
928
|
-
print("Задача не найдена")
|
|
929
|
-
except AuthenticationError:
|
|
930
|
-
print("Ошибка аутентификации")
|
|
931
|
-
except ValidationError as e:
|
|
932
|
-
print(f"Ошибки валидации: {e.errors}")
|
|
933
|
-
# e.errors содержит список ошибок из API
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
## Продвинутое использование
|
|
937
|
-
|
|
938
|
-
### Настройка HTTP-клиента
|
|
939
|
-
|
|
940
|
-
```python
|
|
941
|
-
client = MegaplanClient(
|
|
942
|
-
base_url="https://my.megaplan.ru",
|
|
943
|
-
username="user@example.com",
|
|
944
|
-
password="password",
|
|
945
|
-
timeout=60.0, # float: Таймаут запросов в секундах (по умолчанию 30.0)
|
|
946
|
-
max_retries=5 # int: Максимальное количество повторов при 5xx ошибках (по умолчанию 3)
|
|
947
|
-
)
|
|
948
|
-
```
|
|
949
|
-
|
|
950
|
-
### Ручное управление токенами
|
|
951
|
-
|
|
952
|
-
```python
|
|
953
|
-
# Получить токен доступа
|
|
954
|
-
token = await client.auth.authenticate("user@example.com", "password")
|
|
955
|
-
# Возвращает: str - access_token
|
|
956
|
-
|
|
957
|
-
# Обновить токен
|
|
958
|
-
new_token = await client.auth.refresh_token(refresh_token="refresh_token")
|
|
959
|
-
# Параметры:
|
|
960
|
-
# refresh_token: str | None - Токен обновления (опционально, используется сохраненный)
|
|
961
|
-
# Возвращает: str - новый access_token
|
|
962
|
-
|
|
963
|
-
# Установить токен вручную
|
|
964
|
-
client.set_access_token("your_token")
|
|
1084
|
+
> **Примечание:** Общее описание метода `get_full_details()` и примеры использования см. в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
|
|
965
1085
|
|
|
966
|
-
|
|
967
|
-
client.auth.clear_tokens()
|
|
968
|
-
```
|
|
1086
|
+
## Продвинутые возможности
|
|
969
1087
|
|
|
970
|
-
|
|
1088
|
+
### Кэширование сущностей
|
|
971
1089
|
|
|
972
1090
|
SDK автоматически кэширует справочные сущности (сотрудники, контрагенты, отделы) для уменьшения количества API запросов и повышения производительности.
|
|
973
1091
|
|
|
@@ -1014,7 +1132,7 @@ if client._cache:
|
|
|
1014
1132
|
- Кэш работает автоматически, не требуя изменений в коде
|
|
1015
1133
|
- При использовании `expand` уникальные сущности загружаются параллельно
|
|
1016
1134
|
|
|
1017
|
-
|
|
1135
|
+
### Глобальные дефолтные лимиты
|
|
1018
1136
|
|
|
1019
1137
|
SDK позволяет задать глобальные дефолтные значения для параметров `comments_limit` и `history_limit` на уровне клиента. Эти значения будут применяться ко всем вызовам `get_full_details()` для задач, проектов и сделок, если не переопределены явно.
|
|
1020
1138
|
|
|
@@ -1131,7 +1249,7 @@ projects_details = await client.projects.get_full_details(
|
|
|
1131
1249
|
)
|
|
1132
1250
|
```
|
|
1133
1251
|
|
|
1134
|
-
|
|
1252
|
+
### Автоматическая подгрузка связанных сущностей
|
|
1135
1253
|
|
|
1136
1254
|
Параметр `expand` позволяет автоматически подгружать связанные сущности (сотрудников, контрагентов, отделы) вместо получения только ID.
|
|
1137
1255
|
|
|
@@ -1263,7 +1381,196 @@ tasks_full = await client.tasks.list(limit=100, expand=["responsible"])
|
|
|
1263
1381
|
- С expand: 2 запроса (1 список + 1 батч на 5 сотрудников)
|
|
1264
1382
|
- **Экономия: 99 запросов (98%)**
|
|
1265
1383
|
|
|
1266
|
-
|
|
1384
|
+
### Работа с фильтрами
|
|
1385
|
+
|
|
1386
|
+
SDK предоставляет удобный `FilterBuilder` для создания фильтров с использованием fluent API. Фильтры поддерживаются для задач (`TaskFilter`) и сделок (`TradeFilter`). **Проекты не поддерживают фильтрацию через API.**
|
|
1387
|
+
|
|
1388
|
+
### Базовое использование
|
|
1389
|
+
|
|
1390
|
+
```python
|
|
1391
|
+
from megaplan_sdk import TaskFilterBuilder, TradeFilterBuilder
|
|
1392
|
+
|
|
1393
|
+
# Простой текстовый поиск в задачах
|
|
1394
|
+
filter_obj = TaskFilterBuilder().field("name").contains("договор").build()
|
|
1395
|
+
tasks = await client.tasks.list(filter=filter_obj)
|
|
1396
|
+
|
|
1397
|
+
# Простой текстовый поиск в сделках
|
|
1398
|
+
filter_obj = TradeFilterBuilder().field("name").contains("Leader").build()
|
|
1399
|
+
deals = await client.deals.list(filter=filter_obj)
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
### Доступные операции для строковых полей
|
|
1403
|
+
|
|
1404
|
+
```python
|
|
1405
|
+
# Поиск подстроки (рекомендуется для текстового поиска)
|
|
1406
|
+
filter_obj = TaskFilterBuilder().field("name").contains("договор").build()
|
|
1407
|
+
|
|
1408
|
+
# Поиск по началу строки
|
|
1409
|
+
filter_obj = TaskFilterBuilder().field("name").starts_with("Важный").build()
|
|
1410
|
+
|
|
1411
|
+
# Точное совпадение
|
|
1412
|
+
filter_obj = TaskFilterBuilder().field("status").equals("active").build()
|
|
1413
|
+
|
|
1414
|
+
# Исключение подстроки
|
|
1415
|
+
filter_obj = TaskFilterBuilder().field("name").not_contains("архив").build()
|
|
1416
|
+
|
|
1417
|
+
# Не равно
|
|
1418
|
+
filter_obj = TaskFilterBuilder().field("status").not_equals("completed").build()
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
### Комбинирование условий
|
|
1422
|
+
|
|
1423
|
+
```python
|
|
1424
|
+
# Несколько условий с AND
|
|
1425
|
+
filter_obj = (
|
|
1426
|
+
TaskFilterBuilder()
|
|
1427
|
+
.field("name").contains("договор")
|
|
1428
|
+
.and_()
|
|
1429
|
+
.field("name").starts_with("Важный")
|
|
1430
|
+
.build()
|
|
1431
|
+
)
|
|
1432
|
+
|
|
1433
|
+
# Условия с OR
|
|
1434
|
+
filter_obj = (
|
|
1435
|
+
TaskFilterBuilder()
|
|
1436
|
+
.field("name").contains("договор")
|
|
1437
|
+
.or_()
|
|
1438
|
+
.field("name").contains("соглашение")
|
|
1439
|
+
.build()
|
|
1440
|
+
)
|
|
1441
|
+
```
|
|
1442
|
+
|
|
1443
|
+
### Использование фильтров по ID
|
|
1444
|
+
|
|
1445
|
+
Помимо `FilterBuilder`, можно использовать сохраненные фильтры по их ID:
|
|
1446
|
+
|
|
1447
|
+
```python
|
|
1448
|
+
# Фильтр по числовому ID
|
|
1449
|
+
tasks = await client.tasks.list(filter=123)
|
|
1450
|
+
|
|
1451
|
+
# Фильтр по строковому ID
|
|
1452
|
+
tasks = await client.tasks.list(filter="incoming")
|
|
1453
|
+
deals = await client.deals.list(filter="active")
|
|
1454
|
+
```
|
|
1455
|
+
|
|
1456
|
+
### Управление фильтрами
|
|
1457
|
+
|
|
1458
|
+
SDK предоставляет методы для работы с сохраненными фильтрами:
|
|
1459
|
+
|
|
1460
|
+
```python
|
|
1461
|
+
# Получить список всех фильтров для задач
|
|
1462
|
+
filters = await client.filters.list("task")
|
|
1463
|
+
|
|
1464
|
+
# Получить конкретный фильтр
|
|
1465
|
+
filter_obj = await client.filters.get("task", filter_id=123)
|
|
1466
|
+
|
|
1467
|
+
# Создать новый фильтр
|
|
1468
|
+
new_filter = await client.filters.create(
|
|
1469
|
+
"task",
|
|
1470
|
+
filter_id="my_custom_filter",
|
|
1471
|
+
filter_config={
|
|
1472
|
+
"config": {
|
|
1473
|
+
"contentType": "FilterConfig",
|
|
1474
|
+
"termGroup": {
|
|
1475
|
+
"contentType": "FilterTermGroup",
|
|
1476
|
+
"join": "and",
|
|
1477
|
+
"terms": [
|
|
1478
|
+
{
|
|
1479
|
+
"contentType": "FilterTermString",
|
|
1480
|
+
"field": "name",
|
|
1481
|
+
"comparison": "contains",
|
|
1482
|
+
"value": "договор"
|
|
1483
|
+
}
|
|
1484
|
+
]
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
)
|
|
1489
|
+
|
|
1490
|
+
# Обновить существующий фильтр
|
|
1491
|
+
updated = await client.filters.update("task", filter_id=123, filter_config={...})
|
|
1492
|
+
|
|
1493
|
+
# Экспортировать фильтр
|
|
1494
|
+
export_data = await client.filters.export("task", filter_id=123)
|
|
1495
|
+
```
|
|
1496
|
+
|
|
1497
|
+
### Настройка HTTP-клиента
|
|
1498
|
+
|
|
1499
|
+
```python
|
|
1500
|
+
client = MegaplanClient(
|
|
1501
|
+
base_url="https://my.megaplan.ru",
|
|
1502
|
+
username="user@example.com",
|
|
1503
|
+
password="password",
|
|
1504
|
+
timeout=60.0, # float: Таймаут запросов в секундах (по умолчанию 30.0)
|
|
1505
|
+
max_retries=5 # int: Максимальное количество повторов при 5xx ошибках (по умолчанию 3)
|
|
1506
|
+
)
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
### Работа через прокси
|
|
1510
|
+
|
|
1511
|
+
SDK поддерживает работу через HTTP/HTTPS/SOCKS5 прокси-серверы. Это полезно для корпоративных сетей, где все запросы должны проходить через прокси.
|
|
1512
|
+
|
|
1513
|
+
```python
|
|
1514
|
+
# HTTP прокси с аутентификацией
|
|
1515
|
+
async with MegaplanClient(
|
|
1516
|
+
base_url="https://my.megaplan.ru",
|
|
1517
|
+
username="user@example.com",
|
|
1518
|
+
password="password",
|
|
1519
|
+
proxy="http://login:pass@proxy.corp.local:8080",
|
|
1520
|
+
) as client:
|
|
1521
|
+
tasks = await client.tasks.list()
|
|
1522
|
+
|
|
1523
|
+
# HTTP прокси без аутентификации
|
|
1524
|
+
client = MegaplanClient(
|
|
1525
|
+
base_url="https://my.megaplan.ru",
|
|
1526
|
+
access_token="token",
|
|
1527
|
+
proxy="http://proxy.corp.local:8080",
|
|
1528
|
+
)
|
|
1529
|
+
|
|
1530
|
+
# HTTPS прокси
|
|
1531
|
+
client = MegaplanClient(
|
|
1532
|
+
base_url="https://my.megaplan.ru",
|
|
1533
|
+
access_token="token",
|
|
1534
|
+
proxy="https://proxy.corp.local:8080",
|
|
1535
|
+
)
|
|
1536
|
+
|
|
1537
|
+
# SOCKS5 прокси (требует httpx[socks])
|
|
1538
|
+
client = MegaplanClient(
|
|
1539
|
+
base_url="https://my.megaplan.ru",
|
|
1540
|
+
access_token="token",
|
|
1541
|
+
proxy="socks5://user:pass@proxy.corp.local:1080",
|
|
1542
|
+
)
|
|
1543
|
+
```
|
|
1544
|
+
|
|
1545
|
+
**Поддерживаемые форматы прокси:**
|
|
1546
|
+
- `http://proxy:port` - HTTP прокси без аутентификации
|
|
1547
|
+
- `http://user:password@proxy:port` - HTTP прокси с аутентификацией
|
|
1548
|
+
- `https://proxy:port` - HTTPS прокси
|
|
1549
|
+
- `socks5://user:password@proxy:port` - SOCKS5 прокси (требует `pip install httpx[socks]`)
|
|
1550
|
+
|
|
1551
|
+
### Ручное управление токенами
|
|
1552
|
+
|
|
1553
|
+
```python
|
|
1554
|
+
# Получить токен доступа
|
|
1555
|
+
token = await client.auth.authenticate("user@example.com", "password")
|
|
1556
|
+
# Возвращает: str - access_token
|
|
1557
|
+
|
|
1558
|
+
# Обновить токен
|
|
1559
|
+
new_token = await client.auth.refresh_token(refresh_token="refresh_token")
|
|
1560
|
+
# Параметры:
|
|
1561
|
+
# refresh_token: str | None - Токен обновления (опционально, используется сохраненный)
|
|
1562
|
+
# Возвращает: str - новый access_token
|
|
1563
|
+
|
|
1564
|
+
# Установить токен вручную
|
|
1565
|
+
client.set_access_token("your_token")
|
|
1566
|
+
|
|
1567
|
+
# Очистить токены
|
|
1568
|
+
client.auth.clear_tokens()
|
|
1569
|
+
```
|
|
1570
|
+
|
|
1571
|
+
## Справочная информация
|
|
1572
|
+
|
|
1573
|
+
### Известные ограничения API
|
|
1267
1574
|
|
|
1268
1575
|
Некоторые эндпоинты Megaplan API имеют ограничения или известные проблемы:
|
|
1269
1576
|
|
|
@@ -1279,13 +1586,39 @@ API возвращает ошибку 500 при попытке получить
|
|
|
1279
1586
|
# comments = await client.contractors.get_comments(contractor_id=123)
|
|
1280
1587
|
|
|
1281
1588
|
# Вместо этого используйте комментарии в сделках контрагента
|
|
1282
|
-
deals = await client.
|
|
1283
|
-
base_on={"contentType": "Contractor", "id": 123}
|
|
1284
|
-
)
|
|
1589
|
+
deals = await client.contractors.get_deals(contractor_id=123)
|
|
1285
1590
|
for deal in deals:
|
|
1286
1591
|
comments = await client.deals.get_comments(deal.id)
|
|
1287
1592
|
```
|
|
1288
1593
|
|
|
1594
|
+
### Получение сделок контрагента
|
|
1595
|
+
|
|
1596
|
+
SDK предоставляет удобный метод `get_deals()` для получения сделок контрагента:
|
|
1597
|
+
|
|
1598
|
+
```python
|
|
1599
|
+
# Получить все сделки контрагента
|
|
1600
|
+
deals = await client.contractors.get_deals(
|
|
1601
|
+
contractor_id=123,
|
|
1602
|
+
limit=50 # Опционально
|
|
1603
|
+
)
|
|
1604
|
+
|
|
1605
|
+
for deal in deals:
|
|
1606
|
+
print(f"[{deal.id}] {deal.name}")
|
|
1607
|
+
if deal.state:
|
|
1608
|
+
print(f" Статус: {deal.state}")
|
|
1609
|
+
```
|
|
1610
|
+
|
|
1611
|
+
Это удобнее, чем использование `FilterBuilder`:
|
|
1612
|
+
|
|
1613
|
+
```python
|
|
1614
|
+
# Альтернатива через FilterBuilder (более сложный способ)
|
|
1615
|
+
from megaplan_sdk import TradeFilterBuilder
|
|
1616
|
+
filter_obj = TradeFilterBuilder().field("contractor").equals(
|
|
1617
|
+
{"contentType": "Contractor", "id": 123}
|
|
1618
|
+
).build()
|
|
1619
|
+
deals = await client.deals.list(filter=filter_obj)
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1289
1622
|
### Поиск сотрудников
|
|
1290
1623
|
|
|
1291
1624
|
Поиск сотрудников по имени или телефону может работать некорректно и возвращать 0 результатов. Для надежного поиска используйте точный email:
|
|
@@ -1298,13 +1631,86 @@ employees = await client.employees.list(q="Иван Иванов")
|
|
|
1298
1631
|
employees = await client.employees.list(q="ivan@example.com")
|
|
1299
1632
|
|
|
1300
1633
|
# Или загрузите всех сотрудников и фильтруйте локально
|
|
1634
|
+
```
|
|
1635
|
+
|
|
1636
|
+
### Проверка существования сделки (check_exists)
|
|
1637
|
+
|
|
1638
|
+
Метод `check_exists()` для сделок может возвращать ошибки 500 или 422 из-за ограничений API. SDK автоматически обрабатывает эти ошибки и возвращает `False`. Для проверки существования сделки рекомендуется использовать альтернативные методы:
|
|
1639
|
+
|
|
1640
|
+
```python
|
|
1641
|
+
# Может вернуть 500/422 ошибку
|
|
1642
|
+
# exists = await client.deals.check_exists(query="Deal name")
|
|
1643
|
+
|
|
1644
|
+
# Альтернатива: используйте поиск через list()
|
|
1645
|
+
deals = await client.deals.list(q="Deal name", limit=1)
|
|
1646
|
+
exists = len(deals) > 0
|
|
1647
|
+
|
|
1648
|
+
# Или используйте FilterBuilder
|
|
1649
|
+
from megaplan_sdk import TradeFilterBuilder
|
|
1650
|
+
filter_obj = TradeFilterBuilder().field("name").equals("Deal name").build()
|
|
1651
|
+
deals = await client.deals.list(filter=filter_obj, limit=1)
|
|
1652
|
+
exists = len(deals) > 0
|
|
1653
|
+
```
|
|
1654
|
+
|
|
1655
|
+
**Примечание:** SDK автоматически нормализует BaseEntity объекты (конвертирует строковые ID в int), но это не решает проблему багов API.
|
|
1656
|
+
|
|
1657
|
+
### Параметр statuses для задач
|
|
1658
|
+
|
|
1659
|
+
Параметр `statuses` для фильтрации задач по статусам может возвращать ошибку 422 ValidationError из-за ограничений API. Рекомендуется использовать FilterBuilder для надежной фильтрации:
|
|
1660
|
+
|
|
1661
|
+
```python
|
|
1662
|
+
# Может вернуть 422 ошибку
|
|
1663
|
+
# tasks = await client.tasks.list(statuses=["assigned", "in_progress"])
|
|
1664
|
+
|
|
1665
|
+
# Рекомендуется: используйте FilterBuilder
|
|
1666
|
+
from megaplan_sdk import TaskFilterBuilder
|
|
1667
|
+
filter_obj = TaskFilterBuilder().field_enum("status").in_list(["assigned", "in_progress"]).build()
|
|
1668
|
+
tasks = await client.tasks.list(filter=filter_obj)
|
|
1669
|
+
```
|
|
1670
|
+
|
|
1671
|
+
### Параметр baseOn для сделок
|
|
1672
|
+
|
|
1673
|
+
Параметр `baseOn` для фильтрации сделок по связанной сущности может возвращать ошибку 422 ValidationError из-за ограничений API. SDK автоматически нормализует BaseEntity объекты (конвертирует строковые ID в int), но это не всегда решает проблему:
|
|
1674
|
+
|
|
1675
|
+
```python
|
|
1676
|
+
# Может вернуть 422 ошибку
|
|
1677
|
+
# deals = await client.deals.list(base_on={"contentType": "Contractor", "id": 123})
|
|
1678
|
+
|
|
1679
|
+
# Альтернатива: используйте FilterBuilder
|
|
1680
|
+
from megaplan_sdk import TradeFilterBuilder
|
|
1681
|
+
filter_obj = TradeFilterBuilder().field("contractor").equals({"contentType": "Contractor", "id": 123}).build()
|
|
1682
|
+
deals = await client.deals.list(filter=filter_obj)
|
|
1683
|
+
```
|
|
1684
|
+
|
|
1685
|
+
### Пагинация контрагентов
|
|
1686
|
+
|
|
1687
|
+
Пагинация через `page_after`, `page_before`, `page_with` для контрагентов может возвращать ошибку 422 ValidationError из-за ограничений API. SDK автоматически нормализует BaseEntity объекты, но рекомендуется использовать `limit` и ручную итерацию:
|
|
1688
|
+
|
|
1689
|
+
```python
|
|
1690
|
+
# Может вернуть 422 ошибку
|
|
1691
|
+
# contractors = await client.contractors.list(page_after={"contentType": "Contractor", "id": 123})
|
|
1692
|
+
|
|
1693
|
+
# Рекомендуется: используйте limit и iterate()
|
|
1694
|
+
async for contractor in client.contractors.iterate(limit=50):
|
|
1695
|
+
# Обработка контрагента
|
|
1696
|
+
pass
|
|
1697
|
+
```
|
|
1698
|
+
|
|
1699
|
+
### Нормализация BaseEntity
|
|
1700
|
+
|
|
1701
|
+
SDK автоматически нормализует BaseEntity объекты во всех параметрах:
|
|
1702
|
+
- Конвертирует строковые ID в int (где возможно)
|
|
1703
|
+
- Обеспечивает правильный формат `contentType` и `id`
|
|
1704
|
+
- Применяется к параметрам: `page_after`, `page_before`, `page_with`, `baseOn`, вложенным объектам в `deal` для `check_exists()`
|
|
1705
|
+
|
|
1706
|
+
Это помогает избежать некоторых ошибок валидации, но не решает все проблемы API.
|
|
1301
1707
|
all_employees = []
|
|
1302
1708
|
async for emp in client.employees.iterate():
|
|
1303
1709
|
if "Иван" in emp.first_name:
|
|
1304
1710
|
all_employees.append(emp)
|
|
1305
1711
|
```
|
|
1306
1712
|
|
|
1307
|
-
|
|
1713
|
+
### Архитектура
|
|
1308
1714
|
|
|
1309
1715
|
SDK спроектирован с учетом модульности:
|
|
1310
1716
|
|
|
@@ -1328,13 +1734,13 @@ class MegaplanClient:
|
|
|
1328
1734
|
self.new_resource = NewResource(self._http)
|
|
1329
1735
|
```
|
|
1330
1736
|
|
|
1331
|
-
|
|
1737
|
+
### Требования
|
|
1332
1738
|
|
|
1333
1739
|
- Python 3.11+
|
|
1334
1740
|
- httpx >= 0.25.0
|
|
1335
1741
|
- pydantic >= 2.0.0
|
|
1336
1742
|
|
|
1337
|
-
|
|
1743
|
+
### Разработка
|
|
1338
1744
|
|
|
1339
1745
|
### Установка для разработки
|
|
1340
1746
|
|