megaplan-sdk 0.1.0__tar.gz → 0.2.0__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.
Files changed (47) hide show
  1. {megaplan_sdk-0.1.0/src/megaplan_sdk.egg-info → megaplan_sdk-0.2.0}/PKG-INFO +718 -484
  2. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/README.md +717 -483
  3. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/pyproject.toml +1 -1
  4. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/__init__.py +32 -0
  5. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/auth.py +2 -2
  6. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/cache.py +35 -21
  7. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/client.py +2 -0
  8. megaplan_sdk-0.2.0/src/megaplan_sdk/filter_builder.py +621 -0
  9. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/http_client.py +9 -12
  10. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/comment.py +0 -15
  11. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/common.py +1 -15
  12. megaplan_sdk-0.2.0/src/megaplan_sdk/models/filter.py +82 -0
  13. megaplan_sdk-0.2.0/src/megaplan_sdk/models/milestone.py +81 -0
  14. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/project.py +18 -2
  15. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/task.py +18 -2
  16. megaplan_sdk-0.2.0/src/megaplan_sdk/resources/__init__.py +25 -0
  17. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/base.py +181 -17
  18. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/comments.py +1 -1
  19. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/contractors.py +13 -8
  20. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/deals.py +127 -9
  21. megaplan_sdk-0.2.0/src/megaplan_sdk/resources/filters.py +444 -0
  22. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/projects.py +39 -22
  23. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/tasks.py +201 -31
  24. megaplan_sdk-0.2.0/src/megaplan_sdk/types.py +98 -0
  25. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0/src/megaplan_sdk.egg-info}/PKG-INFO +718 -484
  26. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk.egg-info/SOURCES.txt +4 -0
  27. megaplan_sdk-0.1.0/src/megaplan_sdk/resources/__init__.py +0 -15
  28. megaplan_sdk-0.1.0/src/megaplan_sdk/types.py +0 -56
  29. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/LICENSE +0 -0
  30. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/setup.cfg +0 -0
  31. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/constants.py +0 -0
  32. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/exceptions.py +0 -0
  33. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/helpers.py +0 -0
  34. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/logging_config.py +0 -0
  35. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/__init__.py +0 -0
  36. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/base.py +0 -0
  37. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/contractor.py +0 -0
  38. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/deal.py +0 -0
  39. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/department.py +0 -0
  40. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/models/employee.py +0 -0
  41. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/auth.py +0 -0
  42. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/departments.py +0 -0
  43. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/employees.py +0 -0
  44. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk/resources/full_details.py +0 -0
  45. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk.egg-info/dependency_links.txt +0 -0
  46. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/src/megaplan_sdk.egg-info/requires.txt +0 -0
  47. {megaplan_sdk-0.1.0 → megaplan_sdk-0.2.0}/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.0
3
+ Version: 0.2.0
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,34 @@ 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
+ - [Известные ограничения API](#известные-ограничения-api)
81
+ - [Архитектура](#архитектура)
82
+ - [Требования](#требования)
83
+ - [Разработка](#разработка)
84
+
57
85
  ## Возможности
58
86
 
59
87
  - Полный CRUD для задач, проектов и сделок
@@ -62,6 +90,12 @@ Dynamic: license-file
62
90
  - Типобезопасность с Pydantic-моделями и полной типизацией
63
91
  - Асинхронность — поддержка async/await во всех операциях
64
92
  - Автоматические повторы при ошибках сервера (5xx)
93
+ - Кэширование сущностей с LRU и TTL для оптимизации запросов
94
+ - FilterBuilder для создания фильтров с fluent API
95
+ - Параметр `expand` для автоматической подгрузки связанных сущностей
96
+ - Метод `iterate()` для автоматической пагинации больших списков
97
+ - Helper-функции для создания BaseEntity объектов
98
+ - Глобальные дефолтные лимиты для комментариев и истории
65
99
  - Модульная архитектура для легкого расширения
66
100
  - Комплексные тесты с покрытием 80%+
67
101
 
@@ -163,58 +197,311 @@ deal_ref = make_deal_entity(101)
163
197
  contractor_ref = make_contractor_entity(202)
164
198
  ```
165
199
 
166
- ## Работа с задачами
200
+ ## Обработка ошибок
167
201
 
168
- ### Получение списка задач
202
+ SDK предоставляет специфичные типы исключений для различных сценариев ошибок:
169
203
 
170
204
  ```python
171
- tasks = await client.tasks.list(
172
- filter=None, # TaskFilter: ID фильтра (int) или конфигурация (dict)
173
- statuses=None, # list[str]: Статусы задач для фильтрации
174
- limit=None, # int: Количество элементов на странице
175
- page_after=None, # dict: Загрузить страницу, начиная с этой сущности
176
- page_before=None, # dict: Загрузить страницу строго до этой сущности
177
- page_with=None, # dict: Загрузить страницу с наличием этой сущности
178
- fields=None, # any: Набор дополнительных полей
179
- sort_by=None, # list[dict]: Массив полей сортировки
180
- only_requested_fields=None # bool: Отдавать только перечисленные поля
205
+ from megaplan_sdk import (
206
+ AuthenticationError, # 401 - Ошибка аутентификации
207
+ AuthorizationError, # 403 - Ошибка авторизации (нет прав)
208
+ NotFoundError, # 404 - Ресурс не найден
209
+ ValidationError, # 422 - Ошибка валидации запроса
210
+ RateLimitError, # 429 - Превышен лимит запросов
211
+ ServerError # 5xx - Ошибка сервера
181
212
  )
182
- # Возвращает: list[Task] - список объектов Task
213
+
214
+ try:
215
+ task = await client.tasks.get(task_id=999)
216
+ except NotFoundError:
217
+ print("Задача не найдена")
218
+ except AuthenticationError:
219
+ print("Ошибка аутентификации")
220
+ except ValidationError as e:
221
+ print(f"Ошибки валидации: {e.errors}")
222
+ # e.errors содержит список ошибок из API
183
223
  ```
184
224
 
185
- **Примеры использования:**
225
+ ## Общие паттерны работы с сущностями
226
+
227
+ Большинство сущностей (задачи, проекты, сделки) поддерживают одинаковые операции CRUD и паттерны работы. В этом разделе описаны общие методы, которые применяются ко всем типам сущностей.
228
+
229
+ ### Базовые операции CRUD
230
+
231
+ Все ресурсы поддерживают стандартные операции:
232
+
233
+ #### Получение списка (`list`)
234
+
235
+ ```python
236
+ # Общий формат для всех ресурсов
237
+ entities = await client.{resource}.list(
238
+ limit=None, # int: Количество элементов на странице
239
+ page_after=None, # dict: Загрузить страницу, начиная с этой сущности
240
+ page_before=None, # dict: Загрузить страницу строго до этой сущности
241
+ page_with=None, # dict: Загрузить страницу с наличием этой сущности
242
+ fields=None, # any: Набор дополнительных полей
243
+ sort_by=None, # list[dict]: Массив полей сортировки
244
+ only_requested_fields=None # bool: Отдавать только перечисленные поля
245
+ )
246
+ ```
186
247
 
248
+ **Примеры:**
187
249
  ```python
188
250
  # Получить все задачи
189
251
  tasks = await client.tasks.list()
190
252
 
191
- # С фильтром по статусам
192
- tasks = await client.tasks.list(
193
- statuses=["assigned", "in_progress"],
194
- limit=50
253
+ # Получить проекты с лимитом
254
+ projects = await client.projects.list(limit=50)
255
+
256
+ # Получить сделки с пагинацией
257
+ deals = await client.deals.list(limit=100, page_after={"contentType": "Deal", "id": 100})
258
+ ```
259
+
260
+ #### Получение по ID (`get`)
261
+
262
+ ```python
263
+ entity = await client.{resource}.get({resource}_id=42)
264
+ # Возвращает: объект сущности со всеми полями
265
+ ```
266
+
267
+ **Примеры:**
268
+ ```python
269
+ task = await client.tasks.get(task_id=42)
270
+ project = await client.projects.get(project_id=5)
271
+ deal = await client.deals.get(deal_id=200)
272
+ ```
273
+
274
+ #### Создание (`create`)
275
+
276
+ ```python
277
+ entity = await client.{resource}.create({resource}_data={
278
+ "name": "Название", # Обязательное поле
279
+ # ... другие поля
280
+ })
281
+ # Возвращает: созданная сущность
282
+ ```
283
+
284
+ **Примеры:**
285
+ ```python
286
+ # Простое создание задачи
287
+ task = await client.tasks.create({"name": "Новая задача"})
288
+
289
+ # Создание проекта
290
+ project = await client.projects.create({"name": "Новый проект"})
291
+
292
+ # Создание сделки (требует program)
293
+ deal = await client.deals.create({
294
+ "name": "Новая сделка",
295
+ "program": {"contentType": "Program", "id": 10}
296
+ })
297
+ ```
298
+
299
+ #### Обновление (`update`)
300
+
301
+ ```python
302
+ entity = await client.{resource}.update(
303
+ {resource}_id=42,
304
+ {resource}_data={
305
+ "name": "Обновленное название",
306
+ # ... другие поля для обновления
307
+ }
195
308
  )
309
+ # Возвращает: обновленная сущность
310
+ ```
196
311
 
197
- # С фильтром по ID
198
- tasks = await client.tasks.list(filter=123)
312
+ **Примеры:**
313
+ ```python
314
+ task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
315
+ project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
316
+ deal = await client.deals.update(deal_id=200, deal_data={"sum_base": 60000.0})
317
+ ```
318
+
319
+ #### Удаление (`delete`)
320
+
321
+ ```python
322
+ await client.{resource}.delete({resource}_id=42)
323
+ # Возвращает: None
324
+ ```
325
+
326
+ **Примеры:**
327
+ ```python
328
+ await client.tasks.delete(task_id=42)
329
+ await client.projects.delete(project_id=5)
330
+ await client.deals.delete(deal_id=200)
331
+ ```
332
+
333
+ ### Пагинация
334
+
335
+ SDK поддерживает несколько способов работы с большими списками:
336
+
337
+ #### Ручная пагинация
338
+
339
+ ```python
340
+ # Пагинация "после" определенной сущности
341
+ entities = await client.tasks.list(
342
+ limit=50,
343
+ page_after={"contentType": "Task", "id": 100}
344
+ )
345
+
346
+ # Пагинация "до" определенной сущности
347
+ entities = await client.tasks.list(
348
+ limit=50,
349
+ page_before={"contentType": "Task", "id": 200}
350
+ )
351
+
352
+ # Пагинация "с" определенной сущностью
353
+ entities = await client.tasks.list(
354
+ limit=50,
355
+ page_with={"contentType": "Task", "id": 150}
356
+ )
357
+ ```
358
+
359
+ #### Автоматическая пагинация с `iterate()`
199
360
 
200
- # С фильтром по конфигурации
201
- tasks = await client.tasks.list(filter={"status": "active"})
361
+ Метод `iterate()` автоматически обрабатывает пагинацию и возвращает все элементы:
202
362
 
203
- # Итерация по всем задачам с автоматической пагинацией
363
+ ```python
364
+ # Итерация по всем задачам
204
365
  async for task in client.tasks.iterate(limit=100):
205
366
  print(task.name)
367
+
368
+ # Итерация по всем проектам
369
+ async for project in client.projects.iterate(limit=50):
370
+ print(project.name)
371
+
372
+ # Итерация по всем сделкам
373
+ async for deal in client.deals.iterate(limit=200):
374
+ print(deal.name)
206
375
  ```
207
376
 
208
- ### Получение задачи по ID
377
+ ### Получение полной информации (`get_full_details`)
378
+
379
+ Метод `get_full_details()` позволяет получить сущность со всеми связанными данными за один вызов. Все запросы выполняются параллельно для максимальной производительности.
209
380
 
381
+ **Общий формат:**
210
382
  ```python
211
- task = await client.tasks.get(task_id=42)
212
- # Параметры:
213
- # task_id: int - Идентификатор задачи
214
- # Возвращает: Task - объект задачи со всеми полями
383
+ details = await client.{resource}.get_full_details(
384
+ {resource}_id=42,
385
+ include_comments=True, # Загрузить комментарии
386
+ include_history=True, # Загрузить историю изменений
387
+ comments_limit=50, # Лимит комментариев (опционально)
388
+ history_limit=100 # Лимит записей истории (опционально)
389
+ # ... другие специфичные параметры для каждого типа
390
+ )
391
+ ```
392
+
393
+ **Примеры для разных типов:**
394
+
395
+ ```python
396
+ # Задача со всеми данными
397
+ task_details = await client.tasks.get_full_details(
398
+ task_id=42,
399
+ include_comments=True,
400
+ include_sub_tasks=True,
401
+ include_responsible_details=True
402
+ )
403
+
404
+ # Проект со всеми данными
405
+ project_details = await client.projects.get_full_details(
406
+ project_id=5,
407
+ include_deals=True,
408
+ include_issues=True,
409
+ include_comments=True
410
+ )
411
+
412
+ # Сделка со всеми данными
413
+ deal_details = await client.deals.get_full_details(
414
+ deal_id=200,
415
+ include_comments=True,
416
+ include_status_history=True,
417
+ include_contractor_details=True
418
+ )
215
419
  ```
216
420
 
217
- **Поля объекта Task:**
421
+ **Доступ к данным:**
422
+ ```python
423
+ # Основная сущность
424
+ print(details.task.name) # для задач
425
+ print(details.project.name) # для проектов
426
+ print(details.deal.name) # для сделок
427
+
428
+ # Связанные данные
429
+ if details.comments:
430
+ for comment in details.comments:
431
+ print(comment.text)
432
+
433
+ if details.history:
434
+ print(f"Записей в истории: {len(details.history)}")
435
+ ```
436
+
437
+ Подробнее о специфичных параметрах для каждого типа сущностей см. в соответствующих разделах:
438
+ - [Задачи](#работа-с-задачами)
439
+ - [Проекты](#работа-с-проектами)
440
+ - [Сделки](#работа-со-сделками)
441
+
442
+ ## Работа с задачами
443
+
444
+ > **Примечание:** Базовые операции CRUD (list, get, create, update, delete) и пагинация описаны в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
445
+
446
+ ### Специфичные параметры для задач
447
+
448
+ #### Получение списка задач с фильтрацией
449
+
450
+ Метод `list()` поддерживает дополнительные параметры для задач:
451
+
452
+ ```python
453
+ tasks = await client.tasks.list(
454
+ filter=None, # TaskFilter: ID фильтра (int/str) или FilterBuilder объект
455
+ statuses=None, # list[str]: Статусы задач для фильтрации
456
+ # ... остальные параметры из общих паттернов
457
+ )
458
+ ```
459
+
460
+ **Примеры использования фильтров:**
461
+
462
+ ```python
463
+ # С фильтром по статусам
464
+ tasks = await client.tasks.list(
465
+ statuses=["assigned", "in_progress"],
466
+ limit=50
467
+ )
468
+
469
+ # С фильтром по ID (int или str)
470
+ tasks = await client.tasks.list(filter=123)
471
+ tasks = await client.tasks.list(filter="incoming")
472
+
473
+ # С FilterBuilder для текстового поиска (рекомендуется)
474
+ from megaplan_sdk import TaskFilterBuilder
475
+
476
+ # Простой поиск по названию
477
+ filter_obj = TaskFilterBuilder().field("name").contains("договор").build()
478
+ tasks = await client.tasks.list(filter=filter_obj)
479
+
480
+ # Несколько условий с AND
481
+ filter_obj = (
482
+ TaskFilterBuilder()
483
+ .field("name").contains("договор")
484
+ .and_()
485
+ .field("name").starts_with("Важный")
486
+ .build()
487
+ )
488
+ tasks = await client.tasks.list(filter=filter_obj)
489
+
490
+ # Условия с OR
491
+ filter_obj = (
492
+ TaskFilterBuilder()
493
+ .field("name").contains("договор")
494
+ .or_()
495
+ .field("name").contains("соглашение")
496
+ .build()
497
+ )
498
+ tasks = await client.tasks.list(filter=filter_obj)
499
+ ```
500
+
501
+ Подробнее о работе с фильтрами см. раздел [Работа с фильтрами](#работа-с-фильтрами).
502
+
503
+ ### Поля модели Task
504
+
218
505
  - `id: int` - Идентификатор задачи
219
506
  - `name: str` - Название задачи
220
507
  - `description: str` - Описание
@@ -232,15 +519,11 @@ task = await client.tasks.get(task_id=42)
232
519
  - `created_at: str` - Дата создания
233
520
  - `updated_at: str` - Дата обновления
234
521
 
235
- ### Создание задачи
522
+ ### Упрощенные методы создания
236
523
 
237
- #### Упрощенное создание (рекомендуется)
524
+ Помимо стандартного `create()`, задачи поддерживают упрощенные методы:
238
525
 
239
526
  ```python
240
- # Простое создание задачи с автоматическим заполнением обязательных полей
241
- # Автоматически устанавливает isUrgent=False, isTemplate=False
242
- task = await client.tasks.create({"name": "Новая задача"})
243
-
244
527
  # Создание задачи с текущим пользователем как ответственным
245
528
  task = await client.tasks.create_simple(
246
529
  "Новая задача",
@@ -248,7 +531,6 @@ task = await client.tasks.create_simple(
248
531
  )
249
532
 
250
533
  # Создание задачи с указанным ответственным
251
- from megaplan_sdk import make_employee_entity
252
534
  task = await client.tasks.create_simple(
253
535
  "Новая задача",
254
536
  responsible_id=123
@@ -262,54 +544,7 @@ task = await client.tasks.create_in_project(
262
544
  )
263
545
  ```
264
546
 
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
- ```
547
+ **Примечание:** Стандартный метод `create()` также поддерживается. При создании задачи автоматически устанавливаются `isUrgent=False` и `isTemplate=False`, если они не указаны явно.
313
548
 
314
549
  ### Получение подзадач
315
550
 
@@ -353,48 +588,23 @@ tasks = await client.tasks.tree_level(
353
588
 
354
589
  ### Получение полной информации о задаче
355
590
 
356
- Метод `get_full_details()` позволяет получить задачу со всеми связанными данными за один вызов. Все запросы выполняются параллельно для максимальной производительности.
591
+ Метод `get_full_details()` для задач поддерживает следующие специфичные параметры:
357
592
 
358
593
  ```python
359
- from megaplan_sdk import MegaplanClient
360
-
361
- async with MegaplanClient(...) as client:
362
- # Получить задачу со всей информацией
363
- details = await client.tasks.get_full_details(
364
- task_id=42,
365
- include_sub_tasks=True, # Загрузить подзадачи
366
- include_actual_sub_tasks=True, # Загрузить актуальные подзадачи
367
- include_comments=True, # Загрузить комментарии
368
- include_history=True, # Загрузить историю изменений
369
- include_auditors=True, # Загрузить список аудиторов
370
- include_executors=True, # Загрузить соисполнителей
371
- include_milestones=True, # Загрузить вехи
372
- include_responsible_details=True, # Загрузить полные данные ответственного
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}")
594
+ details = await client.tasks.get_full_details(
595
+ task_id=42,
596
+ include_sub_tasks=True, # Загрузить подзадачи
597
+ include_actual_sub_tasks=True, # Загрузить актуальные подзадачи
598
+ include_comments=True, # Загрузить комментарии
599
+ include_history=True, # Загрузить историю изменений
600
+ include_auditors=True, # Загрузить список аудиторов
601
+ include_executors=True, # Загрузить соисполнителей
602
+ include_milestones=True, # Загрузить вехи
603
+ include_responsible_details=True, # Загрузить полные данные ответственного
604
+ include_owner_details=True, # Загрузить полные данные постановщика
605
+ comments_limit=50, # Лимит комментариев (опционально)
606
+ history_limit=100 # Лимит записей истории (опционально)
607
+ )
398
608
  ```
399
609
 
400
610
  **Поля объекта TaskFullDetails:**
@@ -405,64 +615,105 @@ async with MegaplanClient(...) as client:
405
615
  - `history: list[dict] | None` - История изменений
406
616
  - `auditors: list[dict] | None` - Аудиторы
407
617
  - `executors: list[dict] | None` - Соисполнители
408
- - `milestones: list[dict] | None` - Вехи
618
+ - `milestones: list[Milestone] | None` - Вехи
409
619
  - `responsible_details: Employee | None` - Полные данные ответственного
410
620
  - `owner_details: Employee | None` - Полные данные постановщика
411
621
 
412
- **Примеры использования:**
622
+ > **Примечание:** Общее описание метода `get_full_details()` и примеры использования см. в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
623
+
624
+ ### Работа с вехами (Milestones)
625
+
626
+ Вехи можно получать и создавать для задач и проектов.
627
+
628
+ #### Получение вех
413
629
 
414
630
  ```python
415
- # Минимальный вызов - только основная задача
416
- details = await client.tasks.get_full_details(task_id=42)
631
+ # Получить вехи задачи
632
+ milestones = await client.tasks.get_milestones(
633
+ task_id=123,
634
+ limit=50 # Опционально
635
+ )
417
636
 
418
- # Задача с комментариями и историей
419
- details = await client.tasks.get_full_details(
420
- task_id=42,
421
- include_comments=True,
422
- include_history=True,
423
- comments_limit=20
637
+ # Получить вехи проекта
638
+ milestones = await client.projects.get_milestones(
639
+ project_id=456,
640
+ limit=50 # Опционально
424
641
  )
425
642
 
426
- # Полная информация для отчета
643
+ # Вехи также доступны через get_full_details()
427
644
  details = await client.tasks.get_full_details(
428
- task_id=42,
429
- include_sub_tasks=True,
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
645
+ task_id=123,
646
+ include_milestones=True
436
647
  )
648
+ if details.milestones:
649
+ for milestone in details.milestones:
650
+ print(f"{milestone.name}: {milestone.type}")
437
651
  ```
438
652
 
439
- ## Работа с проектами
440
-
441
- ### Получение списка проектов
653
+ #### Создание вехи
442
654
 
443
655
  ```python
444
- projects = await client.projects.list(
445
- limit=None, # int: Количество элементов на странице
446
- page_after=None, # dict: Загрузить страницу, начиная с этой сущности
447
- page_before=None, # dict: Загрузить страницу строго до этой сущности
448
- page_with=None, # dict: Загрузить страницу с наличием этой сущности
449
- fields=None, # any: Набор дополнительных полей
450
- sort_by=None, # list[dict]: Массив полей сортировки
451
- only_requested_fields=None # bool: Отдавать только перечисленные поля
656
+ from megaplan_sdk.models.milestone import Milestone
657
+
658
+ # Создать веху для задачи
659
+ milestone = await client.tasks.add_milestone(
660
+ task_id=123,
661
+ milestone_data={
662
+ "name": "Release 1.0",
663
+ "description": "Release milestone description", # Обязательное поле
664
+ "type": "report", # Обязательное: "report", "reminder", или "note"
665
+ "date": "2026-02-01T10:00:00Z" # Обязательное: ISO 8601 формат
666
+ }
452
667
  )
453
- # Возвращает: list[Project] - список объектов Project
454
- ```
455
668
 
456
- ### Получение проекта по ID
669
+ # Или использовать модель Milestone
670
+ milestone = await client.tasks.add_milestone(
671
+ task_id=123,
672
+ milestone_data=Milestone(
673
+ name="Release 1.0",
674
+ description="Release milestone description",
675
+ type="report",
676
+ date="2026-02-01T10:00:00Z"
677
+ )
678
+ )
457
679
 
458
- ```python
459
- project = await client.projects.get(project_id=5)
460
- # Параметры:
461
- # project_id: int - Идентификатор проекта
462
- # Возвращает: Project - объект проекта со всеми полями
680
+ # Создать веху для проекта
681
+ milestone = await client.projects.add_milestone(
682
+ project_id=456,
683
+ milestone_data={
684
+ "description": "Phase 1 completion",
685
+ "type": "reminder",
686
+ "date": "2026-03-15T14:00:00Z"
687
+ }
688
+ )
463
689
  ```
464
690
 
465
- **Поля объекта Project:**
691
+ **Обязательные поля при создании вехи:**
692
+ - `description: str` - Описание вехи
693
+ - `type: str` - Тип вехи: `"report"`, `"reminder"`, или `"note"`
694
+ - `date: str | DateTime | dict` - Дата и время вехи (ISO 8601 строка или объект DateTime)
695
+
696
+ **Поля модели Milestone:**
697
+ - `id: int` - Идентификатор вехи
698
+ - `name: str | None` - Название вехи
699
+ - `description: str | None` - Описание
700
+ - `completed: bool | None` - Признак завершенности
701
+ - `type: str | None` - Тип вехи
702
+ - `date: str | DateTime | dict | None` - Дата и время
703
+ - `owner: BaseEntity | None` - Создатель (Employee)
704
+ - `responsible: BaseEntity | None` - Ответственный (Employee)
705
+ - `task: BaseEntity | None` - Связанная задача
706
+ - `project: BaseEntity | None` - Связанный проект
707
+
708
+ **Примечание:** Метод `get_milestones()` может вернуть пустой список для некоторых задач/проектов из-за ограничений API (ошибка 500). Это обрабатывается автоматически.
709
+
710
+ ## Работа с проектами
711
+
712
+ > **Примечание:** Базовые операции CRUD (list, get, create, update, delete) описаны в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
713
+
714
+ **Важно:** Проекты не поддерживают фильтрацию через API (параметр `filter` недоступен).
715
+
716
+ ### Поля модели Project
466
717
  - `id: int` - Идентификатор проекта
467
718
  - `name: str` - Название проекта
468
719
  - `description: str` - Описание
@@ -479,15 +730,11 @@ project = await client.projects.get(project_id=5)
479
730
  - `created_at: str` - Дата создания
480
731
  - `updated_at: str` - Дата обновления
481
732
 
482
- ### Создание проекта
733
+ ### Упрощенные методы создания
483
734
 
484
- #### Упрощенное создание (рекомендуется)
735
+ Помимо стандартного `create()`, проекты поддерживают упрощенный метод:
485
736
 
486
737
  ```python
487
- # Простое создание проекта с автоматическим заполнением обязательных полей
488
- # Автоматически устанавливает isTemplate=False
489
- project = await client.projects.create({"name": "Новый проект"})
490
-
491
738
  # Создание проекта с текущим пользователем как владельцем и ответственным
492
739
  project = await client.projects.create_simple(
493
740
  "Новый проект",
@@ -502,44 +749,7 @@ project = await client.projects.create_simple(
502
749
  )
503
750
  ```
504
751
 
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
- ```
752
+ **Примечание:** При создании проекта автоматически устанавливается `isTemplate=False`, если не указано явно.
543
753
 
544
754
  ### Получение сделок проекта
545
755
 
@@ -582,52 +792,24 @@ actual_issues = await client.projects.get_actual_issues(
582
792
 
583
793
  ### Получение полной информации о проекте
584
794
 
585
- Метод `get_full_details()` позволяет получить проект со всеми связанными данными за один вызов. Все запросы выполняются параллельно для максимальной производительности.
795
+ Метод `get_full_details()` для проектов поддерживает следующие специфичные параметры:
586
796
 
587
797
  ```python
588
- from megaplan_sdk import MegaplanClient
589
-
590
- async with MegaplanClient(...) as client:
591
- # Получить проект со всей информацией
592
- details = await client.projects.get_full_details(
593
- project_id=5,
594
- include_deals=True, # Загрузить связанные сделки
595
- include_issues=True, # Загрузить задачи проекта
596
- include_actual_issues=True, # Загрузить актуальные задачи
597
- include_comments=True, # Загрузить комментарии
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
- )
607
-
608
- # Доступ к основным данным проекта
609
- print(f"Проект: {details.project.name}")
610
- print(f"Статус: {details.project.status}")
611
-
612
- # Доступ к связанным данным
613
- if details.deals:
614
- print(f"Сделок: {len(details.deals)}")
615
- for deal in details.deals:
616
- print(f" - {deal.name}")
617
-
618
- if details.issues:
619
- print(f"Задач: {len(details.issues)}")
620
- for task in details.issues:
621
- print(f" - {task.name}")
622
-
623
- if details.comments:
624
- print(f"Комментариев: {len(details.comments)}")
625
-
626
- if details.responsible_details:
627
- print(f"Ответственный: {details.responsible_details.first_name} {details.responsible_details.last_name}")
628
-
629
- if details.owner_details:
630
- print(f"Владелец: {details.owner_details.first_name} {details.owner_details.last_name}")
798
+ details = await client.projects.get_full_details(
799
+ project_id=5,
800
+ include_deals=True, # Загрузить связанные сделки
801
+ include_issues=True, # Загрузить задачи проекта
802
+ include_actual_issues=True, # Загрузить актуальные задачи
803
+ include_comments=True, # Загрузить комментарии
804
+ include_history=True, # Загрузить историю изменений
805
+ include_auditors=True, # Загрузить список аудиторов
806
+ include_executors=True, # Загрузить соисполнителей
807
+ include_milestones=True, # Загрузить вехи
808
+ include_responsible_details=True, # Загрузить полные данные ответственного
809
+ include_owner_details=True, # Загрузить полные данные владельца
810
+ comments_limit=50, # Лимит комментариев (опционально)
811
+ history_limit=100 # Лимит записей истории (опционально)
812
+ )
631
813
  ```
632
814
 
633
815
  **Поля объекта ProjectFullDetails:**
@@ -639,66 +821,63 @@ async with MegaplanClient(...) as client:
639
821
  - `history: list[dict] | None` - История изменений
640
822
  - `auditors: list[dict] | None` - Аудиторы
641
823
  - `executors: list[dict] | None` - Соисполнители
642
- - `milestones: list[dict] | None` - Вехи
824
+ - `milestones: list[Milestone] | None` - Вехи
643
825
  - `responsible_details: Employee | None` - Полные данные ответственного
644
826
  - `owner_details: Employee | None` - Полные данные владельца
645
827
 
646
- **Примеры использования:**
828
+ > **Примечание:** Общее описание метода `get_full_details()` и примеры использования см. в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
647
829
 
648
- ```python
649
- # Минимальный вызов - только основной проект
650
- details = await client.projects.get_full_details(project_id=5)
830
+ ### Работа с вехами (Milestones)
651
831
 
652
- # Проект со сделками и задачами
653
- details = await client.projects.get_full_details(
654
- project_id=5,
655
- include_deals=True,
656
- include_issues=True
657
- )
658
-
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
- ```
832
+ Вехи для проектов работают аналогично вехам для задач. См. раздел [Работа с вехами](#работа-с-вехами-milestones) в разделе "Работа с задачами" для подробностей.
670
833
 
671
834
  ## Работа со сделками
672
835
 
673
- ### Получение списка сделок
836
+ > **Примечание:** Базовые операции CRUD (list, get, create, update, delete) описаны в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
837
+
838
+ ### Специфичные параметры для сделок
839
+
840
+ #### Получение списка сделок с фильтрацией
841
+
842
+ Метод `list()` поддерживает дополнительные параметры для сделок:
674
843
 
675
844
  ```python
676
845
  deals = await client.deals.list(
677
- filter=None, # TradeFilter: ID фильтра (int) или конфигурация (dict)
678
- status=None, # ProgramState: Статус программы для фильтрации
679
- q=None, # str: Поисковый запрос
680
- base_on=None, # BaseEntity: Базовая сущность для фильтрации
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: Только запрошенные поля
846
+ filter=None, # TradeFilter: ID фильтра (int/str) или FilterBuilder объект
847
+ status=None, # ProgramState: Статус программы для фильтрации
848
+ base_on=None, # BaseEntity: Базовая сущность для фильтрации
849
+ # ... остальные параметры из общих паттернов
688
850
  )
689
- # Возвращает: list[Deal] - список объектов Deal
690
851
  ```
691
852
 
692
- ### Получение сделки по ID
853
+ **Примеры использования фильтров:**
693
854
 
694
855
  ```python
695
- deal = await client.deals.get(deal_id=200)
696
- # Параметры:
697
- # deal_id: int - Идентификатор сделки
698
- # Возвращает: Deal - объект сделки со всеми полями
856
+ # С фильтром по ID
857
+ deals = await client.deals.list(filter=123)
858
+ deals = await client.deals.list(filter="active")
859
+
860
+ # С FilterBuilder для текстового поиска (рекомендуется)
861
+ from megaplan_sdk import TradeFilterBuilder
862
+
863
+ # Простой поиск по названию
864
+ filter_obj = TradeFilterBuilder().field("name").contains("Leader").build()
865
+ deals = await client.deals.list(filter=filter_obj)
866
+
867
+ # Несколько условий
868
+ filter_obj = (
869
+ TradeFilterBuilder()
870
+ .field("name").contains("Leader")
871
+ .and_()
872
+ .field("name").starts_with("Важная")
873
+ .build()
874
+ )
875
+ deals = await client.deals.list(filter=filter_obj)
699
876
  ```
700
877
 
701
- **Поля объекта Deal:**
878
+ Подробнее о работе с фильтрами см. раздел [Работа с фильтрами](#работа-с-фильтрами).
879
+
880
+ ### Поля модели Deal
702
881
  - `id: int` - Идентификатор сделки
703
882
  - `name: str` - Название сделки
704
883
  - `program: BaseEntity` - Программа (схема сделки)
@@ -714,53 +893,11 @@ deal = await client.deals.get(deal_id=200)
714
893
  - `created_at: str` - Дата создания
715
894
  - `updated_at: str` - Дата обновления
716
895
 
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
- ### Обновление сделки
896
+ **Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
742
897
 
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
- ```
753
-
754
- ### Удаление сделки
755
-
756
- ```python
757
- await client.deals.delete(deal_id=200)
758
- # Параметры:
759
- # deal_id: int - Идентификатор сделки
760
- # Возвращает: None
761
- ```
898
+ ### Специфичные методы сделок
762
899
 
763
- ### Применение перехода (изменение статуса)
900
+ #### Применение перехода (изменение статуса)
764
901
 
765
902
  ```python
766
903
  deal = await client.deals.apply_transition(
@@ -813,54 +950,21 @@ exists = await client.deals.check_exists(deal_params={
813
950
 
814
951
  ### Получение полной информации о сделке
815
952
 
816
- Метод `get_full_details()` позволяет получить сделку со всеми связанными данными за один вызов. Все запросы выполняются параллельно для максимальной производительности.
953
+ Метод `get_full_details()` для сделок поддерживает следующие специфичные параметры:
817
954
 
818
955
  ```python
819
- from megaplan_sdk import MegaplanClient
820
-
821
- async with MegaplanClient(...) as client:
822
- # Получить сделку со всей информацией
823
- details = await client.deals.get_full_details(
824
- deal_id=200,
825
- include_comments=True, # Загрузить комментарии
826
- include_history=True, # Загрузить историю изменений
827
- include_status_history=True, # Загрузить историю статусов
828
- include_auditors=True, # Загрузить список аудиторов
829
- include_responsible_details=True, # Загрузить полные данные ответственного
830
- include_contractor_details=True, # Загрузить полные данные контрагента
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}")
956
+ details = await client.deals.get_full_details(
957
+ deal_id=200,
958
+ include_comments=True, # Загрузить комментарии
959
+ include_history=True, # Загрузить историю изменений
960
+ include_status_history=True, # Загрузить историю статусов
961
+ include_auditors=True, # Загрузить список аудиторов
962
+ include_responsible_details=True, # Загрузить полные данные ответственного
963
+ include_contractor_details=True, # Загрузить полные данные контрагента
964
+ include_related_tasks=True, # Загрузить связанные задачи
965
+ comments_limit=50, # Лимит комментариев (опционально)
966
+ history_limit=100 # Лимит записей истории (опционально)
967
+ )
864
968
  ```
865
969
 
866
970
  **Поля объекта DealFullDetails:**
@@ -873,101 +977,11 @@ async with MegaplanClient(...) as client:
873
977
  - `contractor_details: Contractor | None` - Полные данные контрагента
874
978
  - `related_tasks: list[Task] | None` - Связанные задачи
875
979
 
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
- ## Продвинутое использование
980
+ > **Примечание:** Общее описание метода `get_full_details()` и примеры использования см. в разделе [Общие паттерны работы с сущностями](#общие-паттерны-работы-с-сущностями).
937
981
 
938
- ### Настройка HTTP-клиента
982
+ ## Продвинутые возможности
939
983
 
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")
965
-
966
- # Очистить токены
967
- client.auth.clear_tokens()
968
- ```
969
-
970
- ## Кэширование сущностей
984
+ ### Кэширование сущностей
971
985
 
972
986
  SDK автоматически кэширует справочные сущности (сотрудники, контрагенты, отделы) для уменьшения количества API запросов и повышения производительности.
973
987
 
@@ -1014,7 +1028,7 @@ if client._cache:
1014
1028
  - Кэш работает автоматически, не требуя изменений в коде
1015
1029
  - При использовании `expand` уникальные сущности загружаются параллельно
1016
1030
 
1017
- ## Глобальные дефолтные лимиты
1031
+ ### Глобальные дефолтные лимиты
1018
1032
 
1019
1033
  SDK позволяет задать глобальные дефолтные значения для параметров `comments_limit` и `history_limit` на уровне клиента. Эти значения будут применяться ко всем вызовам `get_full_details()` для задач, проектов и сделок, если не переопределены явно.
1020
1034
 
@@ -1131,7 +1145,7 @@ projects_details = await client.projects.get_full_details(
1131
1145
  )
1132
1146
  ```
1133
1147
 
1134
- ## Автоматическая подгрузка связанных сущностей
1148
+ ### Автоматическая подгрузка связанных сущностей
1135
1149
 
1136
1150
  Параметр `expand` позволяет автоматически подгружать связанные сущности (сотрудников, контрагентов, отделы) вместо получения только ID.
1137
1151
 
@@ -1263,7 +1277,154 @@ tasks_full = await client.tasks.list(limit=100, expand=["responsible"])
1263
1277
  - С expand: 2 запроса (1 список + 1 батч на 5 сотрудников)
1264
1278
  - **Экономия: 99 запросов (98%)**
1265
1279
 
1266
- ## Известные ограничения API
1280
+ ### Работа с фильтрами
1281
+
1282
+ SDK предоставляет удобный `FilterBuilder` для создания фильтров с использованием fluent API. Фильтры поддерживаются для задач (`TaskFilter`) и сделок (`TradeFilter`). **Проекты не поддерживают фильтрацию через API.**
1283
+
1284
+ ### Базовое использование
1285
+
1286
+ ```python
1287
+ from megaplan_sdk import TaskFilterBuilder, TradeFilterBuilder
1288
+
1289
+ # Простой текстовый поиск в задачах
1290
+ filter_obj = TaskFilterBuilder().field("name").contains("договор").build()
1291
+ tasks = await client.tasks.list(filter=filter_obj)
1292
+
1293
+ # Простой текстовый поиск в сделках
1294
+ filter_obj = TradeFilterBuilder().field("name").contains("Leader").build()
1295
+ deals = await client.deals.list(filter=filter_obj)
1296
+ ```
1297
+
1298
+ ### Доступные операции для строковых полей
1299
+
1300
+ ```python
1301
+ # Поиск подстроки (рекомендуется для текстового поиска)
1302
+ filter_obj = TaskFilterBuilder().field("name").contains("договор").build()
1303
+
1304
+ # Поиск по началу строки
1305
+ filter_obj = TaskFilterBuilder().field("name").starts_with("Важный").build()
1306
+
1307
+ # Точное совпадение
1308
+ filter_obj = TaskFilterBuilder().field("status").equals("active").build()
1309
+
1310
+ # Исключение подстроки
1311
+ filter_obj = TaskFilterBuilder().field("name").not_contains("архив").build()
1312
+
1313
+ # Не равно
1314
+ filter_obj = TaskFilterBuilder().field("status").not_equals("completed").build()
1315
+ ```
1316
+
1317
+ ### Комбинирование условий
1318
+
1319
+ ```python
1320
+ # Несколько условий с AND
1321
+ filter_obj = (
1322
+ TaskFilterBuilder()
1323
+ .field("name").contains("договор")
1324
+ .and_()
1325
+ .field("name").starts_with("Важный")
1326
+ .build()
1327
+ )
1328
+
1329
+ # Условия с OR
1330
+ filter_obj = (
1331
+ TaskFilterBuilder()
1332
+ .field("name").contains("договор")
1333
+ .or_()
1334
+ .field("name").contains("соглашение")
1335
+ .build()
1336
+ )
1337
+ ```
1338
+
1339
+ ### Использование фильтров по ID
1340
+
1341
+ Помимо `FilterBuilder`, можно использовать сохраненные фильтры по их ID:
1342
+
1343
+ ```python
1344
+ # Фильтр по числовому ID
1345
+ tasks = await client.tasks.list(filter=123)
1346
+
1347
+ # Фильтр по строковому ID
1348
+ tasks = await client.tasks.list(filter="incoming")
1349
+ deals = await client.deals.list(filter="active")
1350
+ ```
1351
+
1352
+ ### Управление фильтрами
1353
+
1354
+ SDK предоставляет методы для работы с сохраненными фильтрами:
1355
+
1356
+ ```python
1357
+ # Получить список всех фильтров для задач
1358
+ filters = await client.filters.list("task")
1359
+
1360
+ # Получить конкретный фильтр
1361
+ filter_obj = await client.filters.get("task", filter_id=123)
1362
+
1363
+ # Создать новый фильтр
1364
+ new_filter = await client.filters.create(
1365
+ "task",
1366
+ filter_id="my_custom_filter",
1367
+ filter_config={
1368
+ "config": {
1369
+ "contentType": "FilterConfig",
1370
+ "termGroup": {
1371
+ "contentType": "FilterTermGroup",
1372
+ "join": "and",
1373
+ "terms": [
1374
+ {
1375
+ "contentType": "FilterTermString",
1376
+ "field": "name",
1377
+ "comparison": "contains",
1378
+ "value": "договор"
1379
+ }
1380
+ ]
1381
+ }
1382
+ }
1383
+ }
1384
+ )
1385
+
1386
+ # Обновить существующий фильтр
1387
+ updated = await client.filters.update("task", filter_id=123, filter_config={...})
1388
+
1389
+ # Экспортировать фильтр
1390
+ export_data = await client.filters.export("task", filter_id=123)
1391
+ ```
1392
+
1393
+ ### Настройка HTTP-клиента
1394
+
1395
+ ```python
1396
+ client = MegaplanClient(
1397
+ base_url="https://my.megaplan.ru",
1398
+ username="user@example.com",
1399
+ password="password",
1400
+ timeout=60.0, # float: Таймаут запросов в секундах (по умолчанию 30.0)
1401
+ max_retries=5 # int: Максимальное количество повторов при 5xx ошибках (по умолчанию 3)
1402
+ )
1403
+ ```
1404
+
1405
+ ### Ручное управление токенами
1406
+
1407
+ ```python
1408
+ # Получить токен доступа
1409
+ token = await client.auth.authenticate("user@example.com", "password")
1410
+ # Возвращает: str - access_token
1411
+
1412
+ # Обновить токен
1413
+ new_token = await client.auth.refresh_token(refresh_token="refresh_token")
1414
+ # Параметры:
1415
+ # refresh_token: str | None - Токен обновления (опционально, используется сохраненный)
1416
+ # Возвращает: str - новый access_token
1417
+
1418
+ # Установить токен вручную
1419
+ client.set_access_token("your_token")
1420
+
1421
+ # Очистить токены
1422
+ client.auth.clear_tokens()
1423
+ ```
1424
+
1425
+ ## Справочная информация
1426
+
1427
+ ### Известные ограничения API
1267
1428
 
1268
1429
  Некоторые эндпоинты Megaplan API имеют ограничения или известные проблемы:
1269
1430
 
@@ -1298,13 +1459,86 @@ employees = await client.employees.list(q="Иван Иванов")
1298
1459
  employees = await client.employees.list(q="ivan@example.com")
1299
1460
 
1300
1461
  # Или загрузите всех сотрудников и фильтруйте локально
1462
+ ```
1463
+
1464
+ ### Проверка существования сделки (check_exists)
1465
+
1466
+ Метод `check_exists()` для сделок может возвращать ошибки 500 или 422 из-за ограничений API. SDK автоматически обрабатывает эти ошибки и возвращает `False`. Для проверки существования сделки рекомендуется использовать альтернативные методы:
1467
+
1468
+ ```python
1469
+ # Может вернуть 500/422 ошибку
1470
+ # exists = await client.deals.check_exists(query="Deal name")
1471
+
1472
+ # Альтернатива: используйте поиск через list()
1473
+ deals = await client.deals.list(q="Deal name", limit=1)
1474
+ exists = len(deals) > 0
1475
+
1476
+ # Или используйте FilterBuilder
1477
+ from megaplan_sdk import TradeFilterBuilder
1478
+ filter_obj = TradeFilterBuilder().field("name").equals("Deal name").build()
1479
+ deals = await client.deals.list(filter=filter_obj, limit=1)
1480
+ exists = len(deals) > 0
1481
+ ```
1482
+
1483
+ **Примечание:** SDK автоматически нормализует BaseEntity объекты (конвертирует строковые ID в int), но это не решает проблему багов API.
1484
+
1485
+ ### Параметр statuses для задач
1486
+
1487
+ Параметр `statuses` для фильтрации задач по статусам может возвращать ошибку 422 ValidationError из-за ограничений API. Рекомендуется использовать FilterBuilder для надежной фильтрации:
1488
+
1489
+ ```python
1490
+ # Может вернуть 422 ошибку
1491
+ # tasks = await client.tasks.list(statuses=["assigned", "in_progress"])
1492
+
1493
+ # Рекомендуется: используйте FilterBuilder
1494
+ from megaplan_sdk import TaskFilterBuilder
1495
+ filter_obj = TaskFilterBuilder().field_enum("status").in_list(["assigned", "in_progress"]).build()
1496
+ tasks = await client.tasks.list(filter=filter_obj)
1497
+ ```
1498
+
1499
+ ### Параметр baseOn для сделок
1500
+
1501
+ Параметр `baseOn` для фильтрации сделок по связанной сущности может возвращать ошибку 422 ValidationError из-за ограничений API. SDK автоматически нормализует BaseEntity объекты (конвертирует строковые ID в int), но это не всегда решает проблему:
1502
+
1503
+ ```python
1504
+ # Может вернуть 422 ошибку
1505
+ # deals = await client.deals.list(base_on={"contentType": "Contractor", "id": 123})
1506
+
1507
+ # Альтернатива: используйте FilterBuilder
1508
+ from megaplan_sdk import TradeFilterBuilder
1509
+ filter_obj = TradeFilterBuilder().field("contractor").equals({"contentType": "Contractor", "id": 123}).build()
1510
+ deals = await client.deals.list(filter=filter_obj)
1511
+ ```
1512
+
1513
+ ### Пагинация контрагентов
1514
+
1515
+ Пагинация через `page_after`, `page_before`, `page_with` для контрагентов может возвращать ошибку 422 ValidationError из-за ограничений API. SDK автоматически нормализует BaseEntity объекты, но рекомендуется использовать `limit` и ручную итерацию:
1516
+
1517
+ ```python
1518
+ # Может вернуть 422 ошибку
1519
+ # contractors = await client.contractors.list(page_after={"contentType": "Contractor", "id": 123})
1520
+
1521
+ # Рекомендуется: используйте limit и iterate()
1522
+ async for contractor in client.contractors.iterate(limit=50):
1523
+ # Обработка контрагента
1524
+ pass
1525
+ ```
1526
+
1527
+ ### Нормализация BaseEntity
1528
+
1529
+ SDK автоматически нормализует BaseEntity объекты во всех параметрах:
1530
+ - Конвертирует строковые ID в int (где возможно)
1531
+ - Обеспечивает правильный формат `contentType` и `id`
1532
+ - Применяется к параметрам: `page_after`, `page_before`, `page_with`, `baseOn`, вложенным объектам в `deal` для `check_exists()`
1533
+
1534
+ Это помогает избежать некоторых ошибок валидации, но не решает все проблемы API.
1301
1535
  all_employees = []
1302
1536
  async for emp in client.employees.iterate():
1303
1537
  if "Иван" in emp.first_name:
1304
1538
  all_employees.append(emp)
1305
1539
  ```
1306
1540
 
1307
- ## Архитектура
1541
+ ### Архитектура
1308
1542
 
1309
1543
  SDK спроектирован с учетом модульности:
1310
1544
 
@@ -1328,13 +1562,13 @@ class MegaplanClient:
1328
1562
  self.new_resource = NewResource(self._http)
1329
1563
  ```
1330
1564
 
1331
- ## Требования
1565
+ ### Требования
1332
1566
 
1333
1567
  - Python 3.11+
1334
1568
  - httpx >= 0.25.0
1335
1569
  - pydantic >= 2.0.0
1336
1570
 
1337
- ## Разработка
1571
+ ### Разработка
1338
1572
 
1339
1573
  ### Установка для разработки
1340
1574