megaplan-sdk 0.2.1__tar.gz → 0.2.2__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.2}/PKG-INFO +25 -17
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/README.md +24 -16
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/pyproject.toml +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/__init__.py +3 -2
- megaplan_sdk-0.2.2/src/megaplan_sdk/models/base.py +27 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/comment.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/common.py +39 -6
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/contractor.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/deal.py +36 -10
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/department.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/employee.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/filter.py +2 -2
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/group.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/milestone.py +1 -1
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/project.py +2 -2
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/task.py +2 -2
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/deals.py +22 -19
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/projects.py +20 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2/src/megaplan_sdk.egg-info}/PKG-INFO +25 -17
- megaplan_sdk-0.2.1/src/megaplan_sdk/models/base.py +0 -16
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/LICENSE +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/setup.cfg +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/auth.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/cache.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/client.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/constants.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/exceptions.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/filter_builder.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/helpers.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/http_client.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/logging_config.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/__init__.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/participant.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/__init__.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/auth.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/base.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/comments.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/contractors.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/departments.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/employees.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/filters.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/full_details.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/tasks.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/types.py +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/SOURCES.txt +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/dependency_links.txt +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/requires.txt +0 -0
- {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/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.2
|
|
4
4
|
Summary: Professional Python SDK for Megaplan API v3
|
|
5
5
|
Author-email: Maxim Borzov <max@borzov.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -314,7 +314,7 @@ entity = await client.{resource}.update(
|
|
|
314
314
|
```python
|
|
315
315
|
task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
|
|
316
316
|
project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
|
|
317
|
-
deal = await client.deals.update(deal_id=200, deal_data={"
|
|
317
|
+
deal = await client.deals.update(deal_id=200, deal_data={"price": {"currency": "RUB", "value": 60000}})
|
|
318
318
|
```
|
|
319
319
|
|
|
320
320
|
#### Удаление (`delete`)
|
|
@@ -517,8 +517,8 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
517
517
|
- `tags: list[BaseEntity]` - Теги
|
|
518
518
|
- `attaches: list[BaseEntity]` - Вложения (файлы)
|
|
519
519
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
520
|
-
- `
|
|
521
|
-
- `
|
|
520
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
521
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
522
522
|
|
|
523
523
|
### Упрощенные методы создания
|
|
524
524
|
|
|
@@ -775,8 +775,8 @@ milestone = await client.projects.add_milestone(
|
|
|
775
775
|
- `tags: list[BaseEntity]` - Теги
|
|
776
776
|
- `attaches: list[BaseEntity]` - Вложения
|
|
777
777
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
778
|
-
- `
|
|
779
|
-
- `
|
|
778
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
779
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
780
780
|
|
|
781
781
|
### Упрощенные методы создания
|
|
782
782
|
|
|
@@ -967,18 +967,26 @@ deals = await client.deals.list(filter=filter_obj)
|
|
|
967
967
|
### Поля модели Deal
|
|
968
968
|
- `id: int` - Идентификатор сделки
|
|
969
969
|
- `name: str` - Название сделки
|
|
970
|
+
- `number: str` - Номер сделки
|
|
971
|
+
- `short_description: str` - Краткое описание
|
|
970
972
|
- `program: BaseEntity` - Программа (схема сделки)
|
|
971
973
|
- `state: ProgramState` - Текущий статус в программе
|
|
972
974
|
- `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
|
|
973
|
-
- `
|
|
974
|
-
- `
|
|
975
|
+
- `manager: BaseEntity` - Ответственный (Employee, API поле `manager`)
|
|
976
|
+
- `price: Money` - Сумма сделки (объект с полями `currency`, `value`)
|
|
977
|
+
- `cost: Money` - Стоимость
|
|
978
|
+
- `debt: Money` - Долг
|
|
979
|
+
- `result: str` - Результат (`"positive"`, `"negative"`, `null`)
|
|
975
980
|
- `currency: BaseEntity` - Валюта
|
|
976
981
|
- `deadline: str` - Срок
|
|
977
|
-
- `description: str` - Описание
|
|
982
|
+
- `description: str` - Описание (только в `deals.get()`, не в списке)
|
|
978
983
|
- `tags: list[BaseEntity]` - Теги
|
|
979
984
|
- `attaches: list[BaseEntity]` - Вложения
|
|
980
|
-
- `
|
|
981
|
-
- `
|
|
985
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
986
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
987
|
+
- `state_time_updated: str` - Дата последнего изменения статуса
|
|
988
|
+
|
|
989
|
+
> **Примечание:** Поля `description`, `deadline` и пользовательские поля доступны только при запросе отдельной сделки через `deals.get(id)`, но не в списке.
|
|
982
990
|
|
|
983
991
|
**Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
|
|
984
992
|
|
|
@@ -1063,7 +1071,7 @@ details = await client.deals.get_full_details(
|
|
|
1063
1071
|
include_history=True, # Загрузить историю изменений
|
|
1064
1072
|
include_status_history=True, # Загрузить историю статусов
|
|
1065
1073
|
include_auditors=True, # Загрузить список аудиторов
|
|
1066
|
-
|
|
1074
|
+
include_manager_details=True, # Загрузить полные данные ответственного
|
|
1067
1075
|
include_contractor_details=True, # Загрузить полные данные контрагента
|
|
1068
1076
|
include_related_tasks=True, # Загрузить связанные задачи
|
|
1069
1077
|
comments_limit=50, # Лимит комментариев (опционально)
|
|
@@ -1077,7 +1085,7 @@ details = await client.deals.get_full_details(
|
|
|
1077
1085
|
- `history: list[dict] | None` - История изменений
|
|
1078
1086
|
- `status_history: list[dict] | None` - История статусов
|
|
1079
1087
|
- `auditors: list[dict] | None` - Аудиторы
|
|
1080
|
-
- `
|
|
1088
|
+
- `manager_details: Employee | None` - Полные данные ответственного
|
|
1081
1089
|
- `contractor_details: Contractor | None` - Полные данные контрагента
|
|
1082
1090
|
- `related_tasks: list[Task] | None` - Связанные задачи
|
|
1083
1091
|
|
|
@@ -1278,14 +1286,14 @@ for task_full in tasks_full:
|
|
|
1278
1286
|
### Использование expand в сделках
|
|
1279
1287
|
|
|
1280
1288
|
```python
|
|
1281
|
-
deals_full = await client.deals.list(limit=10, expand=["
|
|
1289
|
+
deals_full = await client.deals.list(limit=10, expand=["manager", "contractor"])
|
|
1282
1290
|
|
|
1283
1291
|
for deal_full in deals_full:
|
|
1284
1292
|
deal = deal_full.deal
|
|
1285
1293
|
print(f"Сделка: {deal.name}")
|
|
1286
1294
|
|
|
1287
|
-
if deal_full.
|
|
1288
|
-
print(f"Ответственный: {deal_full.
|
|
1295
|
+
if deal_full.manager_details:
|
|
1296
|
+
print(f"Ответственный: {deal_full.manager_details.display_name()}")
|
|
1289
1297
|
|
|
1290
1298
|
if deal_full.contractor_details:
|
|
1291
1299
|
print(f"Контрагент: {deal_full.contractor_details.display_name()}")
|
|
@@ -1296,7 +1304,7 @@ for deal_full in deals_full:
|
|
|
1296
1304
|
```
|
|
1297
1305
|
|
|
1298
1306
|
**Поддерживаемые поля для expand в сделках:**
|
|
1299
|
-
- `
|
|
1307
|
+
- `manager` - ответственный сотрудник
|
|
1300
1308
|
- `contractor` - контрагент
|
|
1301
1309
|
|
|
1302
1310
|
### Использование expand в проектах
|
|
@@ -282,7 +282,7 @@ entity = await client.{resource}.update(
|
|
|
282
282
|
```python
|
|
283
283
|
task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
|
|
284
284
|
project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
|
|
285
|
-
deal = await client.deals.update(deal_id=200, deal_data={"
|
|
285
|
+
deal = await client.deals.update(deal_id=200, deal_data={"price": {"currency": "RUB", "value": 60000}})
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
#### Удаление (`delete`)
|
|
@@ -485,8 +485,8 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
485
485
|
- `tags: list[BaseEntity]` - Теги
|
|
486
486
|
- `attaches: list[BaseEntity]` - Вложения (файлы)
|
|
487
487
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
488
|
-
- `
|
|
489
|
-
- `
|
|
488
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
489
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
490
490
|
|
|
491
491
|
### Упрощенные методы создания
|
|
492
492
|
|
|
@@ -743,8 +743,8 @@ milestone = await client.projects.add_milestone(
|
|
|
743
743
|
- `tags: list[BaseEntity]` - Теги
|
|
744
744
|
- `attaches: list[BaseEntity]` - Вложения
|
|
745
745
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
746
|
-
- `
|
|
747
|
-
- `
|
|
746
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
747
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
748
748
|
|
|
749
749
|
### Упрощенные методы создания
|
|
750
750
|
|
|
@@ -935,18 +935,26 @@ deals = await client.deals.list(filter=filter_obj)
|
|
|
935
935
|
### Поля модели Deal
|
|
936
936
|
- `id: int` - Идентификатор сделки
|
|
937
937
|
- `name: str` - Название сделки
|
|
938
|
+
- `number: str` - Номер сделки
|
|
939
|
+
- `short_description: str` - Краткое описание
|
|
938
940
|
- `program: BaseEntity` - Программа (схема сделки)
|
|
939
941
|
- `state: ProgramState` - Текущий статус в программе
|
|
940
942
|
- `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
|
|
941
|
-
- `
|
|
942
|
-
- `
|
|
943
|
+
- `manager: BaseEntity` - Ответственный (Employee, API поле `manager`)
|
|
944
|
+
- `price: Money` - Сумма сделки (объект с полями `currency`, `value`)
|
|
945
|
+
- `cost: Money` - Стоимость
|
|
946
|
+
- `debt: Money` - Долг
|
|
947
|
+
- `result: str` - Результат (`"positive"`, `"negative"`, `null`)
|
|
943
948
|
- `currency: BaseEntity` - Валюта
|
|
944
949
|
- `deadline: str` - Срок
|
|
945
|
-
- `description: str` - Описание
|
|
950
|
+
- `description: str` - Описание (только в `deals.get()`, не в списке)
|
|
946
951
|
- `tags: list[BaseEntity]` - Теги
|
|
947
952
|
- `attaches: list[BaseEntity]` - Вложения
|
|
948
|
-
- `
|
|
949
|
-
- `
|
|
953
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
954
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
955
|
+
- `state_time_updated: str` - Дата последнего изменения статуса
|
|
956
|
+
|
|
957
|
+
> **Примечание:** Поля `description`, `deadline` и пользовательские поля доступны только при запросе отдельной сделки через `deals.get(id)`, но не в списке.
|
|
950
958
|
|
|
951
959
|
**Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
|
|
952
960
|
|
|
@@ -1031,7 +1039,7 @@ details = await client.deals.get_full_details(
|
|
|
1031
1039
|
include_history=True, # Загрузить историю изменений
|
|
1032
1040
|
include_status_history=True, # Загрузить историю статусов
|
|
1033
1041
|
include_auditors=True, # Загрузить список аудиторов
|
|
1034
|
-
|
|
1042
|
+
include_manager_details=True, # Загрузить полные данные ответственного
|
|
1035
1043
|
include_contractor_details=True, # Загрузить полные данные контрагента
|
|
1036
1044
|
include_related_tasks=True, # Загрузить связанные задачи
|
|
1037
1045
|
comments_limit=50, # Лимит комментариев (опционально)
|
|
@@ -1045,7 +1053,7 @@ details = await client.deals.get_full_details(
|
|
|
1045
1053
|
- `history: list[dict] | None` - История изменений
|
|
1046
1054
|
- `status_history: list[dict] | None` - История статусов
|
|
1047
1055
|
- `auditors: list[dict] | None` - Аудиторы
|
|
1048
|
-
- `
|
|
1056
|
+
- `manager_details: Employee | None` - Полные данные ответственного
|
|
1049
1057
|
- `contractor_details: Contractor | None` - Полные данные контрагента
|
|
1050
1058
|
- `related_tasks: list[Task] | None` - Связанные задачи
|
|
1051
1059
|
|
|
@@ -1246,14 +1254,14 @@ for task_full in tasks_full:
|
|
|
1246
1254
|
### Использование expand в сделках
|
|
1247
1255
|
|
|
1248
1256
|
```python
|
|
1249
|
-
deals_full = await client.deals.list(limit=10, expand=["
|
|
1257
|
+
deals_full = await client.deals.list(limit=10, expand=["manager", "contractor"])
|
|
1250
1258
|
|
|
1251
1259
|
for deal_full in deals_full:
|
|
1252
1260
|
deal = deal_full.deal
|
|
1253
1261
|
print(f"Сделка: {deal.name}")
|
|
1254
1262
|
|
|
1255
|
-
if deal_full.
|
|
1256
|
-
print(f"Ответственный: {deal_full.
|
|
1263
|
+
if deal_full.manager_details:
|
|
1264
|
+
print(f"Ответственный: {deal_full.manager_details.display_name()}")
|
|
1257
1265
|
|
|
1258
1266
|
if deal_full.contractor_details:
|
|
1259
1267
|
print(f"Контрагент: {deal_full.contractor_details.display_name()}")
|
|
@@ -1264,7 +1272,7 @@ for deal_full in deals_full:
|
|
|
1264
1272
|
```
|
|
1265
1273
|
|
|
1266
1274
|
**Поддерживаемые поля для expand в сделках:**
|
|
1267
|
-
- `
|
|
1275
|
+
- `manager` - ответственный сотрудник
|
|
1268
1276
|
- `contractor` - контрагент
|
|
1269
1277
|
|
|
1270
1278
|
### Использование expand в проектах
|
|
@@ -26,7 +26,7 @@ from megaplan_sdk.helpers import (
|
|
|
26
26
|
)
|
|
27
27
|
from megaplan_sdk.logging_config import setup_logging
|
|
28
28
|
from megaplan_sdk.models.comment import Comment
|
|
29
|
-
from megaplan_sdk.models.common import DateTime
|
|
29
|
+
from megaplan_sdk.models.common import DateTime, Money
|
|
30
30
|
from megaplan_sdk.models.contractor import Contractor, ContractorCompany, ContractorHuman
|
|
31
31
|
from megaplan_sdk.models.deal import Deal, DealFullDetails
|
|
32
32
|
from megaplan_sdk.models.department import Department
|
|
@@ -66,6 +66,7 @@ __all__ = [
|
|
|
66
66
|
"Deal",
|
|
67
67
|
"DealFullDetails",
|
|
68
68
|
"Comment",
|
|
69
|
+
"Money",
|
|
69
70
|
"Contractor",
|
|
70
71
|
"ContractorCompany",
|
|
71
72
|
"ContractorHuman",
|
|
@@ -102,4 +103,4 @@ __all__ = [
|
|
|
102
103
|
"ProjectFilterBuilder",
|
|
103
104
|
]
|
|
104
105
|
|
|
105
|
-
__version__ = "0.
|
|
106
|
+
__version__ = "0.2.2"
|
|
@@ -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.
|
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
from pydantic import BaseModel, ConfigDict, Field
|
|
8
8
|
|
|
9
9
|
from megaplan_sdk.models.base import BaseEntity
|
|
10
|
-
from megaplan_sdk.models.common import DateTime, TimestampMixin
|
|
10
|
+
from megaplan_sdk.models.common import DateTime, Money, TimestampMixin
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class TradeFilter(BaseModel):
|
|
@@ -24,6 +24,12 @@ class ProgramState(BaseModel):
|
|
|
24
24
|
"""Program state model.
|
|
25
25
|
|
|
26
26
|
Represents a state in a deal program.
|
|
27
|
+
|
|
28
|
+
Note:
|
|
29
|
+
The ``name`` field may be absent in list endpoint responses.
|
|
30
|
+
In that case ``str(state)`` falls back to ``"State#<id>"``.
|
|
31
|
+
Use ``client.deals.get(deal_id)`` to retrieve the full state object
|
|
32
|
+
with the name populated.
|
|
27
33
|
"""
|
|
28
34
|
|
|
29
35
|
id: int
|
|
@@ -31,7 +37,7 @@ class ProgramState(BaseModel):
|
|
|
31
37
|
name: str | None = None
|
|
32
38
|
program: BaseEntity | None = None
|
|
33
39
|
|
|
34
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
40
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
35
41
|
|
|
36
42
|
def __str__(self) -> str:
|
|
37
43
|
"""Return state name for display.
|
|
@@ -51,23 +57,43 @@ class Deal(TimestampMixin):
|
|
|
51
57
|
"""Deal model.
|
|
52
58
|
|
|
53
59
|
Represents a deal in Megaplan with all its properties.
|
|
60
|
+
|
|
61
|
+
Note:
|
|
62
|
+
The list endpoint (``GET /api/v3/deal``) returns a subset of fields
|
|
63
|
+
compared to the single-entity endpoint (``GET /api/v3/deal/{id}``).
|
|
64
|
+
Fields like ``description``, ``deadline``, ``invoices``, and custom
|
|
65
|
+
category fields are only available via ``client.deals.get(deal_id)``.
|
|
66
|
+
|
|
67
|
+
When using the ``fields`` parameter in ``list()``, use the actual
|
|
68
|
+
API field names: ``manager``, ``price``, ``timeCreated``, etc.
|
|
69
|
+
|
|
70
|
+
Unknown API fields are preserved in ``model_extra`` (``extra="allow"``).
|
|
54
71
|
"""
|
|
55
72
|
|
|
56
73
|
id: int
|
|
57
74
|
content_type: str = Field(alias="contentType", default="Deal")
|
|
58
75
|
name: str | None = None
|
|
76
|
+
number: str | None = None
|
|
77
|
+
short_description: str | None = Field(None, alias="shortDescription")
|
|
78
|
+
description: str | None = None
|
|
79
|
+
|
|
80
|
+
manager: BaseEntity | None = None
|
|
59
81
|
program: BaseEntity | None = None
|
|
60
82
|
state: ProgramState | None = None
|
|
61
83
|
contractor: BaseEntity | None = None
|
|
62
|
-
responsible: BaseEntity | None = None
|
|
63
|
-
sum_base: float | None = Field(alias="sumBase", default=None)
|
|
64
84
|
currency: BaseEntity | None = None
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
|
|
86
|
+
price: Money | None = None
|
|
87
|
+
cost: Money | None = None
|
|
88
|
+
debt: Money | None = None
|
|
89
|
+
|
|
90
|
+
result: str | None = None
|
|
91
|
+
state_time_updated: str | DateTime | None = Field(None, alias="stateTimeUpdated")
|
|
92
|
+
deadline: str | DateTime | dict[str, Any] | None = None
|
|
67
93
|
tags: list[BaseEntity] | None = None
|
|
68
94
|
attaches: list[BaseEntity] | None = None
|
|
69
95
|
|
|
70
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
96
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
71
97
|
|
|
72
98
|
|
|
73
99
|
class DealFullDetails(BaseModel):
|
|
@@ -79,7 +105,7 @@ class DealFullDetails(BaseModel):
|
|
|
79
105
|
history: Change history entries (if requested).
|
|
80
106
|
status_history: Status change history (if requested).
|
|
81
107
|
auditors: List of auditors (if requested).
|
|
82
|
-
|
|
108
|
+
manager_details: Full manager (responsible) employee details (if requested).
|
|
83
109
|
contractor_details: Full contractor details (if requested).
|
|
84
110
|
related_tasks: Tasks related to this deal (if requested).
|
|
85
111
|
"""
|
|
@@ -89,8 +115,8 @@ class DealFullDetails(BaseModel):
|
|
|
89
115
|
history: list[dict[str, Any]] | None = None
|
|
90
116
|
status_history: list[dict[str, Any]] | None = None
|
|
91
117
|
auditors: list[dict[str, Any]] | None = None
|
|
92
|
-
|
|
118
|
+
manager_details: Any | None = None
|
|
93
119
|
contractor_details: Any | None = None
|
|
94
120
|
related_tasks: list[Any] | None = None
|
|
95
121
|
|
|
96
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
122
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
@@ -24,7 +24,7 @@ class Department(BaseEntity, TimestampMixin):
|
|
|
24
24
|
parent: BaseEntity | None = None
|
|
25
25
|
manager: BaseEntity | None = None
|
|
26
26
|
|
|
27
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
27
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
28
28
|
|
|
29
29
|
def __str__(self) -> str:
|
|
30
30
|
"""Return department name for display.
|
|
@@ -54,7 +54,7 @@ class Employee(BaseEntity, TimestampMixin):
|
|
|
54
54
|
access_role: BaseEntity | None = Field(alias="accessRole", default=None)
|
|
55
55
|
custom_fields: dict[str, Any] | None = Field(alias="customFields", default=None)
|
|
56
56
|
|
|
57
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
57
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
58
58
|
|
|
59
59
|
def full_name(self, include_middle: bool = True) -> str:
|
|
60
60
|
"""Get full name of the employee.
|
|
@@ -21,7 +21,7 @@ class BaseFilter(BaseModel):
|
|
|
21
21
|
name: str | None = None
|
|
22
22
|
config: dict[str, Any] | None = None
|
|
23
23
|
|
|
24
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
24
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class TaskFilter(BaseFilter):
|
|
@@ -57,7 +57,7 @@ class FilterExport(BaseModel):
|
|
|
57
57
|
|
|
58
58
|
file: BaseEntity | None = None
|
|
59
59
|
|
|
60
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
60
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
class UserSetting(BaseModel):
|
|
@@ -25,7 +25,7 @@ class Group(BaseEntity):
|
|
|
25
25
|
children: list[BaseEntity] | None = None
|
|
26
26
|
children_count: int | None = Field(alias="childrenCount", default=None)
|
|
27
27
|
|
|
28
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
28
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
29
29
|
|
|
30
30
|
def display_name(self) -> str:
|
|
31
31
|
"""Get display name for the group.
|
|
@@ -46,7 +46,7 @@ class Milestone(BaseEntity, TimestampMixin):
|
|
|
46
46
|
reminder: dict[str, Any] | None = None
|
|
47
47
|
possible_actions: list[str] | None = Field(alias="possibleActions", default=None)
|
|
48
48
|
|
|
49
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
49
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
50
50
|
|
|
51
51
|
def display_name(self) -> str:
|
|
52
52
|
"""Get display name for milestone.
|
|
@@ -44,7 +44,7 @@ class Project(TimestampMixin):
|
|
|
44
44
|
attaches: list[BaseEntity] | None = None
|
|
45
45
|
todos: list[BaseEntity] | None = None
|
|
46
46
|
|
|
47
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
47
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class ProjectFullDetails(BaseModel):
|
|
@@ -76,7 +76,7 @@ class ProjectFullDetails(BaseModel):
|
|
|
76
76
|
responsible_details: Any | None = None
|
|
77
77
|
owner_details: Any | None = None
|
|
78
78
|
|
|
79
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
79
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
# Rebuild models after Milestone is defined to resolve forward references
|
|
@@ -45,7 +45,7 @@ class Task(TimestampMixin):
|
|
|
45
45
|
attaches: list[BaseEntity] | None = None
|
|
46
46
|
todos: list[BaseEntity] | None = None
|
|
47
47
|
|
|
48
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
48
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
class TaskFullDetails(BaseModel):
|
|
@@ -75,7 +75,7 @@ class TaskFullDetails(BaseModel):
|
|
|
75
75
|
responsible_details: Any | None = None
|
|
76
76
|
owner_details: Any | None = None
|
|
77
77
|
|
|
78
|
-
model_config = ConfigDict(populate_by_name=True, extra="
|
|
78
|
+
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
# Rebuild models after Milestone is defined to resolve forward references
|
|
@@ -49,10 +49,10 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
49
49
|
RelatedDataConfig("status_history", "include_status_history", "get_status_history"),
|
|
50
50
|
RelatedDataConfig("auditors", "include_auditors", "get_auditors"),
|
|
51
51
|
RelatedDataConfig(
|
|
52
|
-
"
|
|
53
|
-
"
|
|
52
|
+
"manager_details",
|
|
53
|
+
"include_manager_details",
|
|
54
54
|
None,
|
|
55
|
-
entity_field="
|
|
55
|
+
entity_field="manager",
|
|
56
56
|
entity_type="employee",
|
|
57
57
|
),
|
|
58
58
|
RelatedDataConfig(
|
|
@@ -190,11 +190,14 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
190
190
|
page_after: Load page starting from this entity.
|
|
191
191
|
page_before: Load page strictly before this entity.
|
|
192
192
|
page_with: Load page containing this entity.
|
|
193
|
-
fields: Additional fields to
|
|
193
|
+
fields: Additional fields to request from the API.
|
|
194
|
+
Must use actual API field names: ``manager``, ``price``,
|
|
195
|
+
``timeCreated``, ``timeUpdated``, ``number``, ``cost``, ``debt``,
|
|
196
|
+
``result``, ``shortDescription``, ``stateTimeUpdated``.
|
|
194
197
|
sort_by: Sort fields.
|
|
195
198
|
only_requested_fields: Return only requested fields.
|
|
196
|
-
expand: List of fields to expand (e.g., ["
|
|
197
|
-
Supported values: "
|
|
199
|
+
expand: List of fields to expand (e.g., ["manager", "contractor"]).
|
|
200
|
+
Supported values: "manager", "contractor".
|
|
198
201
|
If provided, returns list[DealFullDetails] instead of list[Deal].
|
|
199
202
|
|
|
200
203
|
Returns:
|
|
@@ -204,13 +207,13 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
204
207
|
>>> # Get deals without expansion
|
|
205
208
|
>>> deals = await client.deals.list(limit=10)
|
|
206
209
|
>>>
|
|
207
|
-
>>> # Get deals with expanded
|
|
210
|
+
>>> # Get deals with expanded manager and contractor
|
|
208
211
|
>>> deals_full = await client.deals.list(
|
|
209
|
-
... limit=10, expand=["
|
|
212
|
+
... limit=10, expand=["manager", "contractor"]
|
|
210
213
|
... )
|
|
211
214
|
>>> for deal_full in deals_full:
|
|
212
|
-
... if deal_full.
|
|
213
|
-
... print(deal_full.
|
|
215
|
+
... if deal_full.manager_details:
|
|
216
|
+
... print(deal_full.manager_details.display_name())
|
|
214
217
|
... if deal_full.contractor_details:
|
|
215
218
|
... print(deal_full.contractor_details.display_name())
|
|
216
219
|
"""
|
|
@@ -258,22 +261,22 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
258
261
|
from megaplan_sdk.models.employee import Employee
|
|
259
262
|
|
|
260
263
|
expand_config: dict[str, tuple[str, type, str]] = {
|
|
261
|
-
"
|
|
264
|
+
"manager": ("employee", Employee, ContentType.EMPLOYEE),
|
|
262
265
|
"contractor": ("contractor", Contractor, ContentType.CONTRACTOR),
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
expanded = await self._expand_list_entities(deals, expand, expand_config)
|
|
266
|
-
|
|
269
|
+
manager_map = expanded.get("manager", {})
|
|
267
270
|
contractor_map = expanded.get("contractor", {})
|
|
268
271
|
|
|
269
272
|
# 4. Build DealFullDetails objects
|
|
270
273
|
results = []
|
|
271
274
|
for deal in deals:
|
|
272
|
-
|
|
275
|
+
mgr_details = None
|
|
273
276
|
contr_details = None
|
|
274
277
|
|
|
275
|
-
if deal.
|
|
276
|
-
|
|
278
|
+
if deal.manager and deal.manager.id in manager_map:
|
|
279
|
+
mgr_details = manager_map[deal.manager.id]
|
|
277
280
|
|
|
278
281
|
if deal.contractor and deal.contractor.id in contractor_map:
|
|
279
282
|
contr_details = contractor_map[deal.contractor.id]
|
|
@@ -281,7 +284,7 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
281
284
|
results.append(
|
|
282
285
|
DealFullDetails(
|
|
283
286
|
deal=deal,
|
|
284
|
-
|
|
287
|
+
manager_details=mgr_details,
|
|
285
288
|
contractor_details=contr_details,
|
|
286
289
|
)
|
|
287
290
|
)
|
|
@@ -683,7 +686,7 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
683
686
|
include_history: bool = False,
|
|
684
687
|
include_status_history: bool = False,
|
|
685
688
|
include_auditors: bool = False,
|
|
686
|
-
|
|
689
|
+
include_manager_details: bool = False,
|
|
687
690
|
include_contractor_details: bool = False,
|
|
688
691
|
include_related_tasks: bool = False,
|
|
689
692
|
comments_limit: int | None = None,
|
|
@@ -700,7 +703,7 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
700
703
|
include_history: Load change history.
|
|
701
704
|
include_status_history: Load status change history.
|
|
702
705
|
include_auditors: Load auditors list.
|
|
703
|
-
|
|
706
|
+
include_manager_details: Load full manager (responsible Employee) details.
|
|
704
707
|
include_contractor_details: Load full contractor details.
|
|
705
708
|
include_related_tasks: Load tasks related to this deal.
|
|
706
709
|
comments_limit: Limit for comments (if included).
|
|
@@ -736,7 +739,7 @@ class DealsResource(BaseResource, FullDetailsMixin):
|
|
|
736
739
|
include_history=include_history,
|
|
737
740
|
include_status_history=include_status_history,
|
|
738
741
|
include_auditors=include_auditors,
|
|
739
|
-
|
|
742
|
+
include_manager_details=include_manager_details,
|
|
740
743
|
include_contractor_details=include_contractor_details,
|
|
741
744
|
include_related_tasks=include_related_tasks,
|
|
742
745
|
comments_limit=comments_limit,
|
|
@@ -12,6 +12,7 @@ from megaplan_sdk.models.project import Project, ProjectFullDetails
|
|
|
12
12
|
from megaplan_sdk.models.task import Task
|
|
13
13
|
from megaplan_sdk.resources.base import BaseResource
|
|
14
14
|
from megaplan_sdk.resources.full_details import FullDetailsMixin, RelatedDataConfig
|
|
15
|
+
from megaplan_sdk.types import FilterType
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from megaplan_sdk.models.milestone import Milestone
|
|
@@ -172,6 +173,7 @@ class ProjectsResource(BaseResource, FullDetailsMixin):
|
|
|
172
173
|
async def list(
|
|
173
174
|
self,
|
|
174
175
|
*,
|
|
176
|
+
filter: FilterType | None = None,
|
|
175
177
|
limit: int | None = None,
|
|
176
178
|
page_after: dict[str, Any] | None = None,
|
|
177
179
|
page_before: dict[str, Any] | None = None,
|
|
@@ -186,6 +188,7 @@ class ProjectsResource(BaseResource, FullDetailsMixin):
|
|
|
186
188
|
async def list(
|
|
187
189
|
self,
|
|
188
190
|
*,
|
|
191
|
+
filter: FilterType | None = None,
|
|
189
192
|
limit: int | None = None,
|
|
190
193
|
page_after: dict[str, Any] | None = None,
|
|
191
194
|
page_before: dict[str, Any] | None = None,
|
|
@@ -198,6 +201,7 @@ class ProjectsResource(BaseResource, FullDetailsMixin):
|
|
|
198
201
|
|
|
199
202
|
async def list(
|
|
200
203
|
self,
|
|
204
|
+
filter: FilterType | None = None,
|
|
201
205
|
limit: int | None = None,
|
|
202
206
|
page_after: dict[str, Any] | None = None,
|
|
203
207
|
page_before: dict[str, Any] | None = None,
|
|
@@ -210,6 +214,16 @@ class ProjectsResource(BaseResource, FullDetailsMixin):
|
|
|
210
214
|
"""Get list of projects.
|
|
211
215
|
|
|
212
216
|
Args:
|
|
217
|
+
filter: Project filter (``ProjectFilterBuilder`` result or filter config dict).
|
|
218
|
+
Use ``ProjectFilterBuilder`` for fluent filter construction:
|
|
219
|
+
|
|
220
|
+
.. code-block:: python
|
|
221
|
+
|
|
222
|
+
from megaplan_sdk import ProjectFilterBuilder
|
|
223
|
+
|
|
224
|
+
f = ProjectFilterBuilder().field_date("timeCreated").greater_than("2025-01-01").build()
|
|
225
|
+
projects = await client.projects.list(filter=f)
|
|
226
|
+
|
|
213
227
|
limit: Number of items per page.
|
|
214
228
|
page_after: Load page starting from this entity.
|
|
215
229
|
page_before: Load page strictly before this entity.
|
|
@@ -228,6 +242,11 @@ class ProjectsResource(BaseResource, FullDetailsMixin):
|
|
|
228
242
|
>>> # Get projects without expansion
|
|
229
243
|
>>> projects = await client.projects.list(limit=10)
|
|
230
244
|
>>>
|
|
245
|
+
>>> # Get projects with filter
|
|
246
|
+
>>> from megaplan_sdk import ProjectFilterBuilder
|
|
247
|
+
>>> f = ProjectFilterBuilder().field("name").contains("SDK").build()
|
|
248
|
+
>>> projects = await client.projects.list(filter=f)
|
|
249
|
+
>>>
|
|
231
250
|
>>> # Get projects with expanded responsible and owner
|
|
232
251
|
>>> projects_full = await client.projects.list(
|
|
233
252
|
... limit=10, expand=["responsible", "owner"]
|
|
@@ -240,6 +259,7 @@ class ProjectsResource(BaseResource, FullDetailsMixin):
|
|
|
240
259
|
|
|
241
260
|
# Use base method to build params (DRY)
|
|
242
261
|
params = self._build_list_params(
|
|
262
|
+
filter=filter,
|
|
243
263
|
limit=limit,
|
|
244
264
|
page_after=page_after,
|
|
245
265
|
page_before=page_before,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: megaplan-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Professional Python SDK for Megaplan API v3
|
|
5
5
|
Author-email: Maxim Borzov <max@borzov.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -314,7 +314,7 @@ entity = await client.{resource}.update(
|
|
|
314
314
|
```python
|
|
315
315
|
task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
|
|
316
316
|
project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
|
|
317
|
-
deal = await client.deals.update(deal_id=200, deal_data={"
|
|
317
|
+
deal = await client.deals.update(deal_id=200, deal_data={"price": {"currency": "RUB", "value": 60000}})
|
|
318
318
|
```
|
|
319
319
|
|
|
320
320
|
#### Удаление (`delete`)
|
|
@@ -517,8 +517,8 @@ tasks = await client.tasks.list(filter=filter_obj)
|
|
|
517
517
|
- `tags: list[BaseEntity]` - Теги
|
|
518
518
|
- `attaches: list[BaseEntity]` - Вложения (файлы)
|
|
519
519
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
520
|
-
- `
|
|
521
|
-
- `
|
|
520
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
521
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
522
522
|
|
|
523
523
|
### Упрощенные методы создания
|
|
524
524
|
|
|
@@ -775,8 +775,8 @@ milestone = await client.projects.add_milestone(
|
|
|
775
775
|
- `tags: list[BaseEntity]` - Теги
|
|
776
776
|
- `attaches: list[BaseEntity]` - Вложения
|
|
777
777
|
- `todos: list[BaseEntity]` - Подзадачи-чеклисты
|
|
778
|
-
- `
|
|
779
|
-
- `
|
|
778
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
779
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
780
780
|
|
|
781
781
|
### Упрощенные методы создания
|
|
782
782
|
|
|
@@ -967,18 +967,26 @@ deals = await client.deals.list(filter=filter_obj)
|
|
|
967
967
|
### Поля модели Deal
|
|
968
968
|
- `id: int` - Идентификатор сделки
|
|
969
969
|
- `name: str` - Название сделки
|
|
970
|
+
- `number: str` - Номер сделки
|
|
971
|
+
- `short_description: str` - Краткое описание
|
|
970
972
|
- `program: BaseEntity` - Программа (схема сделки)
|
|
971
973
|
- `state: ProgramState` - Текущий статус в программе
|
|
972
974
|
- `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
|
|
973
|
-
- `
|
|
974
|
-
- `
|
|
975
|
+
- `manager: BaseEntity` - Ответственный (Employee, API поле `manager`)
|
|
976
|
+
- `price: Money` - Сумма сделки (объект с полями `currency`, `value`)
|
|
977
|
+
- `cost: Money` - Стоимость
|
|
978
|
+
- `debt: Money` - Долг
|
|
979
|
+
- `result: str` - Результат (`"positive"`, `"negative"`, `null`)
|
|
975
980
|
- `currency: BaseEntity` - Валюта
|
|
976
981
|
- `deadline: str` - Срок
|
|
977
|
-
- `description: str` - Описание
|
|
982
|
+
- `description: str` - Описание (только в `deals.get()`, не в списке)
|
|
978
983
|
- `tags: list[BaseEntity]` - Теги
|
|
979
984
|
- `attaches: list[BaseEntity]` - Вложения
|
|
980
|
-
- `
|
|
981
|
-
- `
|
|
985
|
+
- `time_created: str` - Дата создания (API поле `timeCreated`)
|
|
986
|
+
- `time_updated: str` - Дата обновления (API поле `timeUpdated`)
|
|
987
|
+
- `state_time_updated: str` - Дата последнего изменения статуса
|
|
988
|
+
|
|
989
|
+
> **Примечание:** Поля `description`, `deadline` и пользовательские поля доступны только при запросе отдельной сделки через `deals.get(id)`, но не в списке.
|
|
982
990
|
|
|
983
991
|
**Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
|
|
984
992
|
|
|
@@ -1063,7 +1071,7 @@ details = await client.deals.get_full_details(
|
|
|
1063
1071
|
include_history=True, # Загрузить историю изменений
|
|
1064
1072
|
include_status_history=True, # Загрузить историю статусов
|
|
1065
1073
|
include_auditors=True, # Загрузить список аудиторов
|
|
1066
|
-
|
|
1074
|
+
include_manager_details=True, # Загрузить полные данные ответственного
|
|
1067
1075
|
include_contractor_details=True, # Загрузить полные данные контрагента
|
|
1068
1076
|
include_related_tasks=True, # Загрузить связанные задачи
|
|
1069
1077
|
comments_limit=50, # Лимит комментариев (опционально)
|
|
@@ -1077,7 +1085,7 @@ details = await client.deals.get_full_details(
|
|
|
1077
1085
|
- `history: list[dict] | None` - История изменений
|
|
1078
1086
|
- `status_history: list[dict] | None` - История статусов
|
|
1079
1087
|
- `auditors: list[dict] | None` - Аудиторы
|
|
1080
|
-
- `
|
|
1088
|
+
- `manager_details: Employee | None` - Полные данные ответственного
|
|
1081
1089
|
- `contractor_details: Contractor | None` - Полные данные контрагента
|
|
1082
1090
|
- `related_tasks: list[Task] | None` - Связанные задачи
|
|
1083
1091
|
|
|
@@ -1278,14 +1286,14 @@ for task_full in tasks_full:
|
|
|
1278
1286
|
### Использование expand в сделках
|
|
1279
1287
|
|
|
1280
1288
|
```python
|
|
1281
|
-
deals_full = await client.deals.list(limit=10, expand=["
|
|
1289
|
+
deals_full = await client.deals.list(limit=10, expand=["manager", "contractor"])
|
|
1282
1290
|
|
|
1283
1291
|
for deal_full in deals_full:
|
|
1284
1292
|
deal = deal_full.deal
|
|
1285
1293
|
print(f"Сделка: {deal.name}")
|
|
1286
1294
|
|
|
1287
|
-
if deal_full.
|
|
1288
|
-
print(f"Ответственный: {deal_full.
|
|
1295
|
+
if deal_full.manager_details:
|
|
1296
|
+
print(f"Ответственный: {deal_full.manager_details.display_name()}")
|
|
1289
1297
|
|
|
1290
1298
|
if deal_full.contractor_details:
|
|
1291
1299
|
print(f"Контрагент: {deal_full.contractor_details.display_name()}")
|
|
@@ -1296,7 +1304,7 @@ for deal_full in deals_full:
|
|
|
1296
1304
|
```
|
|
1297
1305
|
|
|
1298
1306
|
**Поддерживаемые поля для expand в сделках:**
|
|
1299
|
-
- `
|
|
1307
|
+
- `manager` - ответственный сотрудник
|
|
1300
1308
|
- `contractor` - контрагент
|
|
1301
1309
|
|
|
1302
1310
|
### Использование expand в проектах
|
|
@@ -1,16 +0,0 @@
|
|
|
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 and id.
|
|
8
|
-
|
|
9
|
-
All Megaplan entities have contentType and id fields.
|
|
10
|
-
Link entities (for references) only contain these two fields.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
content_type: str = Field(alias="contentType")
|
|
14
|
-
id: int
|
|
15
|
-
|
|
16
|
-
model_config = ConfigDict(populate_by_name=True, extra="ignore")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|