megaplan-sdk 0.2.1__tar.gz → 0.2.3__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.2.1/src/megaplan_sdk.egg-info → megaplan_sdk-0.2.3}/PKG-INFO +108 -18
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/README.md +107 -17
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/pyproject.toml +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/__init__.py +6 -2
- megaplan_sdk-0.2.3/src/megaplan_sdk/constants.py +42 -0
- megaplan_sdk-0.2.3/src/megaplan_sdk/models/base.py +27 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/comment.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/common.py +39 -6
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/contractor.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/deal.py +36 -10
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/department.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/employee.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/filter.py +2 -2
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/group.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/milestone.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/project.py +2 -2
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/task.py +9 -2
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/comments.py +61 -4
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/deals.py +29 -19
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/full_details.py +15 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/projects.py +27 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/tasks.py +27 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3/src/megaplan_sdk.egg-info}/PKG-INFO +108 -18
- megaplan_sdk-0.2.1/src/megaplan_sdk/constants.py +0 -17
- megaplan_sdk-0.2.1/src/megaplan_sdk/models/base.py +0 -16
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/LICENSE +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/setup.cfg +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/auth.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/cache.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/client.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/exceptions.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/filter_builder.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/helpers.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/http_client.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/logging_config.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/__init__.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/models/participant.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/__init__.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/auth.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/base.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/contractors.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/departments.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/employees.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/resources/filters.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk/types.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk.egg-info/SOURCES.txt +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk.egg-info/dependency_links.txt +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/src/megaplan_sdk.egg-info/requires.txt +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.3}/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.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Professional Python SDK for Megaplan API v3
|
|
5
5
|
Author-email: Maxim Borzov <max@borzov.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,6 +73,7 @@ Dynamic: license-file
|
|
|
73
73
|
- [Глобальные дефолтные лимиты](#глобальные-дефолтные-лимиты)
|
|
74
74
|
- [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
|
|
75
75
|
- [Работа с фильтрами](#работа-с-фильтрами)
|
|
76
|
+
- [Работа с комментариями](#работа-с-комментариями)
|
|
76
77
|
- [Настройка HTTP-клиента](#настройка-http-клиента)
|
|
77
78
|
- [Работа через прокси](#работа-через-прокси)
|
|
78
79
|
- [Ручное управление токенами](#ручное-управление-токенами)
|
|
@@ -314,7 +315,7 @@ entity = await client.{resource}.update(
|
|
|
314
315
|
```python
|
|
315
316
|
task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
|
|
316
317
|
project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
|
|
317
|
-
deal = await client.deals.update(deal_id=200, deal_data={"
|
|
318
|
+
deal = await client.deals.update(deal_id=200, deal_data={"price": {"currency": "RUB", "value": 60000}})
|
|
318
319
|
```
|
|
319
320
|
|
|
320
321
|
#### Удаление (`delete`)
|
|
@@ -429,7 +430,7 @@ print(details.deal.name) # для сделок
|
|
|
429
430
|
# Связанные данные
|
|
430
431
|
if details.comments:
|
|
431
432
|
for comment in details.comments:
|
|
432
|
-
print(comment.
|
|
433
|
+
print(comment.content)
|
|
433
434
|
|
|
434
435
|
if details.history:
|
|
435
436
|
print(f"Записей в истории: {len(details.history)}")
|
|
@@ -517,8 +518,39 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
517
518
|
- `tags: list[BaseEntity]` - Теги
|
|
518
519
|
- `attaches: list[BaseEntity]` - Вложения (файлы)
|
|
519
520
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
520
|
-
- `
|
|
521
|
-
- `
|
|
521
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
522
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
523
|
+
- `activity: str | None` - Дата последней активности (API поле `activity`)
|
|
524
|
+
- `last_comment_time_created: str | None` - Время последнего комментария (API поле `lastCommentTimeCreated`)
|
|
525
|
+
- `status_change_time: str | None` - Время смены статуса (API поле `statusChangeTime`)
|
|
526
|
+
- `actual_start: str | None` - Фактическое время начала (API поле `actualStart`)
|
|
527
|
+
- `last_view: str | None` - Время последнего просмотра (API поле `lastView`)
|
|
528
|
+
|
|
529
|
+
### Фильтрация задач по временным полям
|
|
530
|
+
|
|
531
|
+
Временны́е поля (`activity`, `lastCommentTimeCreated` и др.) не возвращаются в `tasks.list()` по умолчанию.
|
|
532
|
+
Используйте константу `DEFAULT_TASK_LIST_FIELDS`, чтобы запросить их явно:
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
from megaplan_sdk import DEFAULT_TASK_LIST_FIELDS
|
|
536
|
+
|
|
537
|
+
# Запросить задачи с временными полями (activity, lastCommentTimeCreated и др.)
|
|
538
|
+
tasks = await client.tasks.list(
|
|
539
|
+
limit=50,
|
|
540
|
+
fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
541
|
+
)
|
|
542
|
+
for task in tasks:
|
|
543
|
+
print(f"{task.name}: активность {task.activity}")
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
> **Примечание:** Сортировка по `"timeUpdated"` не поддерживается API — используйте `"activity"`:
|
|
547
|
+
>
|
|
548
|
+
> ```python
|
|
549
|
+
> tasks = await client.tasks.list(
|
|
550
|
+
> sort_by=[{"fieldName": "activity", "order": "desc"}],
|
|
551
|
+
> fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
552
|
+
> )
|
|
553
|
+
> ```
|
|
522
554
|
|
|
523
555
|
### Упрощенные методы создания
|
|
524
556
|
|
|
@@ -775,8 +807,8 @@ milestone = await client.projects.add_milestone(
|
|
|
775
807
|
- `tags: list[BaseEntity]` - Теги
|
|
776
808
|
- `attaches: list[BaseEntity]` - Вложения
|
|
777
809
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
778
|
-
- `
|
|
779
|
-
- `
|
|
810
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
811
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
780
812
|
|
|
781
813
|
### Упрощенные методы создания
|
|
782
814
|
|
|
@@ -967,18 +999,26 @@ deals = await client.deals.list(filter=filter_obj)
|
|
|
967
999
|
### Поля модели Deal
|
|
968
1000
|
- `id: int` - Идентификатор сделки
|
|
969
1001
|
- `name: str` - Название сделки
|
|
1002
|
+
- `number: str` - Номер сделки
|
|
1003
|
+
- `short_description: str` - Краткое описание
|
|
970
1004
|
- `program: BaseEntity` - Программа (схема сделки)
|
|
971
1005
|
- `state: ProgramState` - Текущий статус в программе
|
|
972
1006
|
- `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
|
|
973
|
-
- `
|
|
974
|
-
- `
|
|
1007
|
+
- `manager: BaseEntity` - Ответственный (Employee, API поле `manager`)
|
|
1008
|
+
- `price: Money` - Сумма сделки (объект с полями `currency`, `value`)
|
|
1009
|
+
- `cost: Money` - Стоимость
|
|
1010
|
+
- `debt: Money` - Долг
|
|
1011
|
+
- `result: str` - Результат (`"positive"`, `"negative"`, `null`)
|
|
975
1012
|
- `currency: BaseEntity` - Валюта
|
|
976
1013
|
- `deadline: str` - Срок
|
|
977
|
-
- `description: str` - Описание
|
|
1014
|
+
- `description: str` - Описание (только в `deals.get()`, не в списке)
|
|
978
1015
|
- `tags: list[BaseEntity]` - Теги
|
|
979
1016
|
- `attaches: list[BaseEntity]` - Вложения
|
|
980
|
-
- `
|
|
981
|
-
- `
|
|
1017
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
1018
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
1019
|
+
- `state_time_updated: str` - Дата последнего изменения статуса
|
|
1020
|
+
|
|
1021
|
+
> **Примечание:** Поля `description`, `deadline` и пользовательские поля доступны только при запросе отдельной сделки через `deals.get(id)`, но не в списке.
|
|
982
1022
|
|
|
983
1023
|
**Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
|
|
984
1024
|
|
|
@@ -1063,7 +1103,7 @@ details = await client.deals.get_full_details(
|
|
|
1063
1103
|
include_history=True, # Загрузить историю изменений
|
|
1064
1104
|
include_status_history=True, # Загрузить историю статусов
|
|
1065
1105
|
include_auditors=True, # Загрузить список аудиторов
|
|
1066
|
-
|
|
1106
|
+
include_manager_details=True, # Загрузить полные данные ответственного
|
|
1067
1107
|
include_contractor_details=True, # Загрузить полные данные контрагента
|
|
1068
1108
|
include_related_tasks=True, # Загрузить связанные задачи
|
|
1069
1109
|
comments_limit=50, # Лимит комментариев (опционально)
|
|
@@ -1077,7 +1117,7 @@ details = await client.deals.get_full_details(
|
|
|
1077
1117
|
- `history: list[dict] | None` - История изменений
|
|
1078
1118
|
- `status_history: list[dict] | None` - История статусов
|
|
1079
1119
|
- `auditors: list[dict] | None` - Аудиторы
|
|
1080
|
-
- `
|
|
1120
|
+
- `manager_details: Employee | None` - Полные данные ответственного
|
|
1081
1121
|
- `contractor_details: Contractor | None` - Полные данные контрагента
|
|
1082
1122
|
- `related_tasks: list[Task] | None` - Связанные задачи
|
|
1083
1123
|
|
|
@@ -1278,14 +1318,14 @@ for task_full in tasks_full:
|
|
|
1278
1318
|
### Использование expand в сделках
|
|
1279
1319
|
|
|
1280
1320
|
```python
|
|
1281
|
-
deals_full = await client.deals.list(limit=10, expand=["
|
|
1321
|
+
deals_full = await client.deals.list(limit=10, expand=["manager", "contractor"])
|
|
1282
1322
|
|
|
1283
1323
|
for deal_full in deals_full:
|
|
1284
1324
|
deal = deal_full.deal
|
|
1285
1325
|
print(f"Сделка: {deal.name}")
|
|
1286
1326
|
|
|
1287
|
-
if deal_full.
|
|
1288
|
-
print(f"Ответственный: {deal_full.
|
|
1327
|
+
if deal_full.manager_details:
|
|
1328
|
+
print(f"Ответственный: {deal_full.manager_details.display_name()}")
|
|
1289
1329
|
|
|
1290
1330
|
if deal_full.contractor_details:
|
|
1291
1331
|
print(f"Контрагент: {deal_full.contractor_details.display_name()}")
|
|
@@ -1296,7 +1336,7 @@ for deal_full in deals_full:
|
|
|
1296
1336
|
```
|
|
1297
1337
|
|
|
1298
1338
|
**Поддерживаемые поля для expand в сделках:**
|
|
1299
|
-
- `
|
|
1339
|
+
- `manager` - ответственный сотрудник
|
|
1300
1340
|
- `contractor` - контрагент
|
|
1301
1341
|
|
|
1302
1342
|
### Использование expand в проектах
|
|
@@ -1494,6 +1534,56 @@ updated = await client.filters.update("task", filter_id=123, filter_config={...}
|
|
|
1494
1534
|
export_data = await client.filters.export("task", filter_id=123)
|
|
1495
1535
|
```
|
|
1496
1536
|
|
|
1537
|
+
### Работа с комментариями
|
|
1538
|
+
|
|
1539
|
+
Комментарии доступны для задач, проектов и сделок через `client.comments`.
|
|
1540
|
+
|
|
1541
|
+
#### Получение комментариев
|
|
1542
|
+
|
|
1543
|
+
```python
|
|
1544
|
+
# Комментарии задачи (entity_type по умолчанию "task")
|
|
1545
|
+
comments = await client.comments.list(entity_id=42)
|
|
1546
|
+
|
|
1547
|
+
# Комментарии проекта
|
|
1548
|
+
comments = await client.comments.list(entity_id=5, entity_type="project")
|
|
1549
|
+
|
|
1550
|
+
# Комментарии сделки
|
|
1551
|
+
comments = await client.comments.list(entity_id=200, entity_type="deal")
|
|
1552
|
+
|
|
1553
|
+
# Автоматическая пагинация
|
|
1554
|
+
async for comment in client.comments.iterate(entity_id=42):
|
|
1555
|
+
print(comment.content)
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
#### Подгрузка авторов через expand
|
|
1559
|
+
|
|
1560
|
+
API Мегаплана не раскрывает поле `owner` (автор комментария) в списке комментариев.
|
|
1561
|
+
Используйте `expand=["owner"]`, чтобы SDK дозагрузил авторов отдельными запросами (с кэшированием):
|
|
1562
|
+
|
|
1563
|
+
```python
|
|
1564
|
+
# Комментарии задачи с именами авторов
|
|
1565
|
+
comments = await client.comments.list(entity_id=42, expand=["owner"])
|
|
1566
|
+
for comment in comments:
|
|
1567
|
+
author_name = comment.owner.name if comment.owner else "неизвестен"
|
|
1568
|
+
print(f"{author_name}: {comment.content}")
|
|
1569
|
+
```
|
|
1570
|
+
|
|
1571
|
+
#### Создание комментария
|
|
1572
|
+
|
|
1573
|
+
```python
|
|
1574
|
+
# Комментарий к задаче
|
|
1575
|
+
comment = await client.comments.create(entity_id=42, comment_data={"text": "Текст комментария"})
|
|
1576
|
+
|
|
1577
|
+
# Комментарий к проекту
|
|
1578
|
+
comment = await client.comments.create(entity_id=5, comment_data={"text": "Текст"}, entity_type="project")
|
|
1579
|
+
|
|
1580
|
+
# Комментарий к сделке
|
|
1581
|
+
comment = await client.comments.create(entity_id=200, comment_data={"text": "Текст"}, entity_type="deal")
|
|
1582
|
+
```
|
|
1583
|
+
|
|
1584
|
+
> **Ограничение:** Комментарии контрагентов не поддерживаются API (возвращает 500).
|
|
1585
|
+
> Используйте комментарии в связанных сделках или задачах.
|
|
1586
|
+
|
|
1497
1587
|
### Настройка HTTP-клиента
|
|
1498
1588
|
|
|
1499
1589
|
```python
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
- [Глобальные дефолтные лимиты](#глобальные-дефолтные-лимиты)
|
|
42
42
|
- [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
|
|
43
43
|
- [Работа с фильтрами](#работа-с-фильтрами)
|
|
44
|
+
- [Работа с комментариями](#работа-с-комментариями)
|
|
44
45
|
- [Настройка HTTP-клиента](#настройка-http-клиента)
|
|
45
46
|
- [Работа через прокси](#работа-через-прокси)
|
|
46
47
|
- [Ручное управление токенами](#ручное-управление-токенами)
|
|
@@ -282,7 +283,7 @@ entity = await client.{resource}.update(
|
|
|
282
283
|
```python
|
|
283
284
|
task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
|
|
284
285
|
project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
|
|
285
|
-
deal = await client.deals.update(deal_id=200, deal_data={"
|
|
286
|
+
deal = await client.deals.update(deal_id=200, deal_data={"price": {"currency": "RUB", "value": 60000}})
|
|
286
287
|
```
|
|
287
288
|
|
|
288
289
|
#### Удаление (`delete`)
|
|
@@ -397,7 +398,7 @@ print(details.deal.name) # для сделок
|
|
|
397
398
|
# Связанные данные
|
|
398
399
|
if details.comments:
|
|
399
400
|
for comment in details.comments:
|
|
400
|
-
print(comment.
|
|
401
|
+
print(comment.content)
|
|
401
402
|
|
|
402
403
|
if details.history:
|
|
403
404
|
print(f"Записей в истории: {len(details.history)}")
|
|
@@ -485,8 +486,39 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
485
486
|
- `tags: list[BaseEntity]` - Теги
|
|
486
487
|
- `attaches: list[BaseEntity]` - Вложения (файлы)
|
|
487
488
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
488
|
-
- `
|
|
489
|
-
- `
|
|
489
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
490
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
491
|
+
- `activity: str | None` - Дата последней активности (API поле `activity`)
|
|
492
|
+
- `last_comment_time_created: str | None` - Время последнего комментария (API поле `lastCommentTimeCreated`)
|
|
493
|
+
- `status_change_time: str | None` - Время смены статуса (API поле `statusChangeTime`)
|
|
494
|
+
- `actual_start: str | None` - Фактическое время начала (API поле `actualStart`)
|
|
495
|
+
- `last_view: str | None` - Время последнего просмотра (API поле `lastView`)
|
|
496
|
+
|
|
497
|
+
### Фильтрация задач по временным полям
|
|
498
|
+
|
|
499
|
+
Временны́е поля (`activity`, `lastCommentTimeCreated` и др.) не возвращаются в `tasks.list()` по умолчанию.
|
|
500
|
+
Используйте константу `DEFAULT_TASK_LIST_FIELDS`, чтобы запросить их явно:
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
from megaplan_sdk import DEFAULT_TASK_LIST_FIELDS
|
|
504
|
+
|
|
505
|
+
# Запросить задачи с временными полями (activity, lastCommentTimeCreated и др.)
|
|
506
|
+
tasks = await client.tasks.list(
|
|
507
|
+
limit=50,
|
|
508
|
+
fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
509
|
+
)
|
|
510
|
+
for task in tasks:
|
|
511
|
+
print(f"{task.name}: активность {task.activity}")
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
> **Примечание:** Сортировка по `"timeUpdated"` не поддерживается API — используйте `"activity"`:
|
|
515
|
+
>
|
|
516
|
+
> ```python
|
|
517
|
+
> tasks = await client.tasks.list(
|
|
518
|
+
> sort_by=[{"fieldName": "activity", "order": "desc"}],
|
|
519
|
+
> fields=list(DEFAULT_TASK_LIST_FIELDS),
|
|
520
|
+
> )
|
|
521
|
+
> ```
|
|
490
522
|
|
|
491
523
|
### Упрощенные методы создания
|
|
492
524
|
|
|
@@ -743,8 +775,8 @@ milestone = await client.projects.add_milestone(
|
|
|
743
775
|
- `tags: list[BaseEntity]` - Теги
|
|
744
776
|
- `attaches: list[BaseEntity]` - Вложения
|
|
745
777
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
746
|
-
- `
|
|
747
|
-
- `
|
|
778
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
779
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
748
780
|
|
|
749
781
|
### Упрощенные методы создания
|
|
750
782
|
|
|
@@ -935,18 +967,26 @@ deals = await client.deals.list(filter=filter_obj)
|
|
|
935
967
|
### Поля модели Deal
|
|
936
968
|
- `id: int` - Идентификатор сделки
|
|
937
969
|
- `name: str` - Название сделки
|
|
970
|
+
- `number: str` - Номер сделки
|
|
971
|
+
- `short_description: str` - Краткое описание
|
|
938
972
|
- `program: BaseEntity` - Программа (схема сделки)
|
|
939
973
|
- `state: ProgramState` - Текущий статус в программе
|
|
940
974
|
- `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
|
|
941
|
-
- `
|
|
942
|
-
- `
|
|
975
|
+
- `manager: BaseEntity` - Ответственный (Employee, API поле `manager`)
|
|
976
|
+
- `price: Money` - Сумма сделки (объект с полями `currency`, `value`)
|
|
977
|
+
- `cost: Money` - Стоимость
|
|
978
|
+
- `debt: Money` - Долг
|
|
979
|
+
- `result: str` - Результат (`"positive"`, `"negative"`, `null`)
|
|
943
980
|
- `currency: BaseEntity` - Валюта
|
|
944
981
|
- `deadline: str` - Срок
|
|
945
|
-
- `description: str` - Описание
|
|
982
|
+
- `description: str` - Описание (только в `deals.get()`, не в списке)
|
|
946
983
|
- `tags: list[BaseEntity]` - Теги
|
|
947
984
|
- `attaches: list[BaseEntity]` - Вложения
|
|
948
|
-
- `
|
|
949
|
-
- `
|
|
985
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
986
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
987
|
+
- `state_time_updated: str` - Дата последнего изменения статуса
|
|
988
|
+
|
|
989
|
+
> **Примечание:** Поля `description`, `deadline` и пользовательские поля доступны только при запросе отдельной сделки через `deals.get(id)`, но не в списке.
|
|
950
990
|
|
|
951
991
|
**Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
|
|
952
992
|
|
|
@@ -1031,7 +1071,7 @@ details = await client.deals.get_full_details(
|
|
|
1031
1071
|
include_history=True, # Загрузить историю изменений
|
|
1032
1072
|
include_status_history=True, # Загрузить историю статусов
|
|
1033
1073
|
include_auditors=True, # Загрузить список аудиторов
|
|
1034
|
-
|
|
1074
|
+
include_manager_details=True, # Загрузить полные данные ответственного
|
|
1035
1075
|
include_contractor_details=True, # Загрузить полные данные контрагента
|
|
1036
1076
|
include_related_tasks=True, # Загрузить связанные задачи
|
|
1037
1077
|
comments_limit=50, # Лимит комментариев (опционально)
|
|
@@ -1045,7 +1085,7 @@ details = await client.deals.get_full_details(
|
|
|
1045
1085
|
- `history: list[dict] | None` - История изменений
|
|
1046
1086
|
- `status_history: list[dict] | None` - История статусов
|
|
1047
1087
|
- `auditors: list[dict] | None` - Аудиторы
|
|
1048
|
-
- `
|
|
1088
|
+
- `manager_details: Employee | None` - Полные данные ответственного
|
|
1049
1089
|
- `contractor_details: Contractor | None` - Полные данные контрагента
|
|
1050
1090
|
- `related_tasks: list[Task] | None` - Связанные задачи
|
|
1051
1091
|
|
|
@@ -1246,14 +1286,14 @@ for task_full in tasks_full:
|
|
|
1246
1286
|
### Использование expand в сделках
|
|
1247
1287
|
|
|
1248
1288
|
```python
|
|
1249
|
-
deals_full = await client.deals.list(limit=10, expand=["
|
|
1289
|
+
deals_full = await client.deals.list(limit=10, expand=["manager", "contractor"])
|
|
1250
1290
|
|
|
1251
1291
|
for deal_full in deals_full:
|
|
1252
1292
|
deal = deal_full.deal
|
|
1253
1293
|
print(f"Сделка: {deal.name}")
|
|
1254
1294
|
|
|
1255
|
-
if deal_full.
|
|
1256
|
-
print(f"Ответственный: {deal_full.
|
|
1295
|
+
if deal_full.manager_details:
|
|
1296
|
+
print(f"Ответственный: {deal_full.manager_details.display_name()}")
|
|
1257
1297
|
|
|
1258
1298
|
if deal_full.contractor_details:
|
|
1259
1299
|
print(f"Контрагент: {deal_full.contractor_details.display_name()}")
|
|
@@ -1264,7 +1304,7 @@ for deal_full in deals_full:
|
|
|
1264
1304
|
```
|
|
1265
1305
|
|
|
1266
1306
|
**Поддерживаемые поля для expand в сделках:**
|
|
1267
|
-
- `
|
|
1307
|
+
- `manager` - ответственный сотрудник
|
|
1268
1308
|
- `contractor` - контрагент
|
|
1269
1309
|
|
|
1270
1310
|
### Использование expand в проектах
|
|
@@ -1462,6 +1502,56 @@ updated = await client.filters.update("task", filter_id=123, filter_config={...}
|
|
|
1462
1502
|
export_data = await client.filters.export("task", filter_id=123)
|
|
1463
1503
|
```
|
|
1464
1504
|
|
|
1505
|
+
### Работа с комментариями
|
|
1506
|
+
|
|
1507
|
+
Комментарии доступны для задач, проектов и сделок через `client.comments`.
|
|
1508
|
+
|
|
1509
|
+
#### Получение комментариев
|
|
1510
|
+
|
|
1511
|
+
```python
|
|
1512
|
+
# Комментарии задачи (entity_type по умолчанию "task")
|
|
1513
|
+
comments = await client.comments.list(entity_id=42)
|
|
1514
|
+
|
|
1515
|
+
# Комментарии проекта
|
|
1516
|
+
comments = await client.comments.list(entity_id=5, entity_type="project")
|
|
1517
|
+
|
|
1518
|
+
# Комментарии сделки
|
|
1519
|
+
comments = await client.comments.list(entity_id=200, entity_type="deal")
|
|
1520
|
+
|
|
1521
|
+
# Автоматическая пагинация
|
|
1522
|
+
async for comment in client.comments.iterate(entity_id=42):
|
|
1523
|
+
print(comment.content)
|
|
1524
|
+
```
|
|
1525
|
+
|
|
1526
|
+
#### Подгрузка авторов через expand
|
|
1527
|
+
|
|
1528
|
+
API Мегаплана не раскрывает поле `owner` (автор комментария) в списке комментариев.
|
|
1529
|
+
Используйте `expand=["owner"]`, чтобы SDK дозагрузил авторов отдельными запросами (с кэшированием):
|
|
1530
|
+
|
|
1531
|
+
```python
|
|
1532
|
+
# Комментарии задачи с именами авторов
|
|
1533
|
+
comments = await client.comments.list(entity_id=42, expand=["owner"])
|
|
1534
|
+
for comment in comments:
|
|
1535
|
+
author_name = comment.owner.name if comment.owner else "неизвестен"
|
|
1536
|
+
print(f"{author_name}: {comment.content}")
|
|
1537
|
+
```
|
|
1538
|
+
|
|
1539
|
+
#### Создание комментария
|
|
1540
|
+
|
|
1541
|
+
```python
|
|
1542
|
+
# Комментарий к задаче
|
|
1543
|
+
comment = await client.comments.create(entity_id=42, comment_data={"text": "Текст комментария"})
|
|
1544
|
+
|
|
1545
|
+
# Комментарий к проекту
|
|
1546
|
+
comment = await client.comments.create(entity_id=5, comment_data={"text": "Текст"}, entity_type="project")
|
|
1547
|
+
|
|
1548
|
+
# Комментарий к сделке
|
|
1549
|
+
comment = await client.comments.create(entity_id=200, comment_data={"text": "Текст"}, entity_type="deal")
|
|
1550
|
+
```
|
|
1551
|
+
|
|
1552
|
+
> **Ограничение:** Комментарии контрагентов не поддерживаются API (возвращает 500).
|
|
1553
|
+
> Используйте комментарии в связанных сделках или задачах.
|
|
1554
|
+
|
|
1465
1555
|
### Настройка HTTP-клиента
|
|
1466
1556
|
|
|
1467
1557
|
```python
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Megaplan Python SDK - Professional SDK for Megaplan API v3."""
|
|
2
2
|
|
|
3
3
|
from megaplan_sdk.client import MegaplanClient
|
|
4
|
+
from megaplan_sdk.constants import DEFAULT_TASK_LIST_FIELDS
|
|
4
5
|
from megaplan_sdk.exceptions import (
|
|
5
6
|
AuthenticationError,
|
|
6
7
|
AuthorizationError,
|
|
@@ -26,7 +27,7 @@ from megaplan_sdk.helpers import (
|
|
|
26
27
|
)
|
|
27
28
|
from megaplan_sdk.logging_config import setup_logging
|
|
28
29
|
from megaplan_sdk.models.comment import Comment
|
|
29
|
-
from megaplan_sdk.models.common import DateTime
|
|
30
|
+
from megaplan_sdk.models.common import DateTime, Money
|
|
30
31
|
from megaplan_sdk.models.contractor import Contractor, ContractorCompany, ContractorHuman
|
|
31
32
|
from megaplan_sdk.models.deal import Deal, DealFullDetails
|
|
32
33
|
from megaplan_sdk.models.department import Department
|
|
@@ -66,6 +67,7 @@ __all__ = [
|
|
|
66
67
|
"Deal",
|
|
67
68
|
"DealFullDetails",
|
|
68
69
|
"Comment",
|
|
70
|
+
"Money",
|
|
69
71
|
"Contractor",
|
|
70
72
|
"ContractorCompany",
|
|
71
73
|
"ContractorHuman",
|
|
@@ -100,6 +102,8 @@ __all__ = [
|
|
|
100
102
|
"TaskFilterBuilder",
|
|
101
103
|
"TradeFilterBuilder",
|
|
102
104
|
"ProjectFilterBuilder",
|
|
105
|
+
# Constants
|
|
106
|
+
"DEFAULT_TASK_LIST_FIELDS",
|
|
103
107
|
]
|
|
104
108
|
|
|
105
|
-
__version__ = "0.
|
|
109
|
+
__version__ = "0.2.3"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Constants for Megaplan SDK."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ContentType:
|
|
5
|
+
"""Content type constants for Megaplan API entities."""
|
|
6
|
+
|
|
7
|
+
TASK = "Task"
|
|
8
|
+
PROJECT = "Project"
|
|
9
|
+
DEAL = "Deal"
|
|
10
|
+
EMPLOYEE = "Employee"
|
|
11
|
+
CONTRACTOR = "Contractor"
|
|
12
|
+
CONTRACTOR_COMPANY = "ContractorCompany"
|
|
13
|
+
CONTRACTOR_HUMAN = "ContractorHuman"
|
|
14
|
+
CONTRACTOR_CATEGORY = "ContractorCategory"
|
|
15
|
+
DEPARTMENT = "Department"
|
|
16
|
+
COMMENT = "Comment"
|
|
17
|
+
GROUP = "Group"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Task fields users commonly try to sort by that the Megaplan API rejects (422).
|
|
21
|
+
# Maps the unsupported field name to the supported replacement to suggest.
|
|
22
|
+
UNSUPPORTED_TASK_SORT_FIELDS: dict[str, str] = {
|
|
23
|
+
"timeUpdated": "activity",
|
|
24
|
+
"updatedAt": "activity",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Recommended `fields` set for tasks.list() so that date fields are populated.
|
|
28
|
+
# Megaplan list endpoints omit these unless explicitly requested, which makes
|
|
29
|
+
# client-side time-window filtering silently return nothing (#8).
|
|
30
|
+
# Only fields confirmed to exist on Task are included (no "timeUpdated" — see #7).
|
|
31
|
+
DEFAULT_TASK_LIST_FIELDS: tuple[str, ...] = (
|
|
32
|
+
"name",
|
|
33
|
+
"status",
|
|
34
|
+
"timeCreated",
|
|
35
|
+
"activity",
|
|
36
|
+
"lastCommentTimeCreated",
|
|
37
|
+
"statusChangeTime",
|
|
38
|
+
"actualStart",
|
|
39
|
+
"owner",
|
|
40
|
+
"responsible",
|
|
41
|
+
"commentsCount",
|
|
42
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Base models for Megaplan SDK."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseEntity(BaseModel):
|
|
7
|
+
"""Base entity with contentType, id, and optional name.
|
|
8
|
+
|
|
9
|
+
All Megaplan entities have ``contentType`` and ``id`` fields.
|
|
10
|
+
Many API responses also include a ``name`` field even in reference
|
|
11
|
+
objects, so it is captured here to avoid silent data loss.
|
|
12
|
+
|
|
13
|
+
Unknown fields from the API are preserved in ``model_extra`` via
|
|
14
|
+
``extra="allow"``, which enables forward compatibility and prevents
|
|
15
|
+
silent data loss when the API returns new fields.
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
content_type: Entity type identifier (e.g., "Employee", "Task").
|
|
19
|
+
id: Entity numeric identifier.
|
|
20
|
+
name: Optional display name returned by the API in many contexts.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
content_type: str = Field(alias="contentType")
|
|
24
|
+
id: int
|
|
25
|
+
name: str | None = None
|
|
26
|
+
|
|
27
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
@@ -40,4 +40,4 @@ class Comment(BaseEntity, TimestampMixin):
|
|
|
40
40
|
is_dropped: bool | None = Field(alias="isDropped", default=None)
|
|
41
41
|
completed: int | None = None
|
|
42
42
|
|
|
43
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
43
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
@@ -50,7 +50,7 @@ class File(BaseModel):
|
|
|
50
50
|
name: str | None = None
|
|
51
51
|
size: int | None = None
|
|
52
52
|
|
|
53
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
53
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class DateTime(BaseModel):
|
|
@@ -66,7 +66,37 @@ class DateTime(BaseModel):
|
|
|
66
66
|
content_type: str = Field(alias="contentType", default="DateTime")
|
|
67
67
|
value: str
|
|
68
68
|
|
|
69
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
69
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Money(BaseModel):
|
|
73
|
+
"""Money model for monetary value fields.
|
|
74
|
+
|
|
75
|
+
Megaplan API returns monetary values as structured objects with
|
|
76
|
+
currency, value, and optional exchange rate information.
|
|
77
|
+
|
|
78
|
+
Attributes:
|
|
79
|
+
content_type: Always "Money".
|
|
80
|
+
currency: ISO 4217 currency code (e.g., "RUB", "USD").
|
|
81
|
+
value: Amount in the original currency.
|
|
82
|
+
value_in_main: Amount converted to the main company currency.
|
|
83
|
+
rate: Exchange rate relative to the main currency.
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
>>> money = Money(contentType="Money", currency="RUB", value=18055000)
|
|
87
|
+
>>> money.value
|
|
88
|
+
18055000
|
|
89
|
+
>>> money.currency
|
|
90
|
+
'RUB'
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
content_type: str = Field(alias="contentType", default="Money")
|
|
94
|
+
currency: str = ""
|
|
95
|
+
value: float | int | None = None
|
|
96
|
+
value_in_main: float | int | None = Field(None, alias="valueInMain")
|
|
97
|
+
rate: float | None = None
|
|
98
|
+
|
|
99
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
70
100
|
|
|
71
101
|
|
|
72
102
|
class SortField(BaseModel):
|
|
@@ -84,10 +114,13 @@ class SortField(BaseModel):
|
|
|
84
114
|
class TimestampMixin(BaseModel):
|
|
85
115
|
"""Mixin for entities with creation and update timestamps.
|
|
86
116
|
|
|
117
|
+
Megaplan API returns timestamps using the field names ``timeCreated``
|
|
118
|
+
and ``timeUpdated`` (not ``createdAt``/``updatedAt``).
|
|
119
|
+
|
|
87
120
|
Attributes:
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
time_created: Entity creation timestamp (API field: ``timeCreated``).
|
|
122
|
+
time_updated: Entity last update timestamp (API field: ``timeUpdated``).
|
|
90
123
|
"""
|
|
91
124
|
|
|
92
|
-
|
|
93
|
-
|
|
125
|
+
time_created: str | DateTime | None = Field(alias="timeCreated", default=None)
|
|
126
|
+
time_updated: str | DateTime | None = Field(alias="timeUpdated", default=None)
|
|
@@ -40,7 +40,7 @@ class Contractor(BaseEntity, TimestampMixin):
|
|
|
40
40
|
tags: list[BaseEntity | str] | None = None # Can be Tag entities or strings
|
|
41
41
|
custom_fields: dict[str, Any] | None = Field(alias="customFields", default=None)
|
|
42
42
|
|
|
43
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
43
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
44
44
|
|
|
45
45
|
def display_name(self) -> str:
|
|
46
46
|
"""Get display name for contractor.
|