megaplan-sdk 0.2.0__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.
Files changed (48) hide show
  1. {megaplan_sdk-0.2.0/src/megaplan_sdk.egg-info → megaplan_sdk-0.2.2}/PKG-INFO +200 -20
  2. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/README.md +199 -19
  3. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/pyproject.toml +1 -1
  4. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/__init__.py +9 -2
  5. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/client.py +4 -0
  6. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/constants.py +1 -0
  7. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/http_client.py +6 -1
  8. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/__init__.py +6 -0
  9. megaplan_sdk-0.2.2/src/megaplan_sdk/models/base.py +27 -0
  10. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/comment.py +1 -1
  11. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/common.py +39 -6
  12. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/contractor.py +1 -1
  13. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/deal.py +36 -10
  14. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/department.py +1 -1
  15. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/employee.py +1 -1
  16. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/filter.py +2 -2
  17. megaplan_sdk-0.2.2/src/megaplan_sdk/models/group.py +40 -0
  18. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/milestone.py +1 -1
  19. megaplan_sdk-0.2.2/src/megaplan_sdk/models/participant.py +74 -0
  20. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/project.py +2 -2
  21. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/task.py +2 -2
  22. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/base.py +52 -0
  23. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/contractors.py +46 -0
  24. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/deals.py +74 -19
  25. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/projects.py +180 -0
  26. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/tasks.py +166 -0
  27. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2/src/megaplan_sdk.egg-info}/PKG-INFO +200 -20
  28. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/SOURCES.txt +2 -0
  29. megaplan_sdk-0.2.0/src/megaplan_sdk/models/base.py +0 -16
  30. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/LICENSE +0 -0
  31. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/setup.cfg +0 -0
  32. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/auth.py +0 -0
  33. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/cache.py +0 -0
  34. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/exceptions.py +0 -0
  35. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/filter_builder.py +0 -0
  36. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/helpers.py +0 -0
  37. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/logging_config.py +0 -0
  38. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/__init__.py +0 -0
  39. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/auth.py +0 -0
  40. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/comments.py +0 -0
  41. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/departments.py +0 -0
  42. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/employees.py +0 -0
  43. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/filters.py +0 -0
  44. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/full_details.py +0 -0
  45. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk/types.py +0 -0
  46. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/dependency_links.txt +0 -0
  47. {megaplan_sdk-0.2.0 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/requires.txt +0 -0
  48. {megaplan_sdk-0.2.0 → 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.0
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
@@ -74,6 +74,7 @@ Dynamic: license-file
74
74
  - [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
75
75
  - [Работа с фильтрами](#работа-с-фильтрами)
76
76
  - [Настройка HTTP-клиента](#настройка-http-клиента)
77
+ - [Работа через прокси](#работа-через-прокси)
77
78
  - [Ручное управление токенами](#ручное-управление-токенами)
78
79
 
79
80
  ### Справочная информация
@@ -313,7 +314,7 @@ entity = await client.{resource}.update(
313
314
  ```python
314
315
  task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
315
316
  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
+ deal = await client.deals.update(deal_id=200, deal_data={"price": {"currency": "RUB", "value": 60000}})
317
318
  ```
318
319
 
319
320
  #### Удаление (`delete`)
@@ -516,8 +517,8 @@ tasks = await client.tasks.list(filter=filter_obj)
516
517
  - `tags: list[BaseEntity]` - Теги
517
518
  - `attaches: list[BaseEntity]` - Вложения (файлы)
518
519
  - `todos: list[BaseEntity]` - Подзадачи-чеклисты
519
- - `created_at: str` - Дата создания
520
- - `updated_at: str` - Дата обновления
520
+ - `time_created: str` - Дата создания (API поле `timeCreated`)
521
+ - `time_updated: str` - Дата обновления (API поле `timeUpdated`)
521
522
 
522
523
  ### Упрощенные методы создания
523
524
 
@@ -570,6 +571,53 @@ actual_subtasks = await client.tasks.get_actual_sub_tasks(
570
571
  # Возвращает: list[Task] - список актуальных подзадач
571
572
  ```
572
573
 
574
+ ### Получение доступных родителей
575
+
576
+ Методы для получения доступных надзадач и надпроектов (для выбора родителя при создании или перемещении задачи):
577
+
578
+ ```python
579
+ # Глобальный поиск доступных родителей для новой задачи
580
+ # Возвращает список Task и Project объектов
581
+ parents = await client.tasks.get_available_parents(
582
+ is_template=False, # bool: Фильтр по шаблонам
583
+ limit=10, # int: Количество элементов
584
+ )
585
+ for parent in parents:
586
+ print(f"{type(parent).__name__}: {parent.name}") # "Task: ..." или "Project: ..."
587
+
588
+ # Доступные родители для существующей задачи
589
+ # Исключает саму задачу и её потомков
590
+ parents = await client.tasks.get_available_parents_for(
591
+ task_id=123,
592
+ is_template=False,
593
+ limit=10,
594
+ )
595
+ ```
596
+
597
+ **Примечание:** Методы возвращают смешанный список объектов `Task` и `Project`, так как задача может быть вложена как в другую задачу, так и в проект.
598
+
599
+ ### Получение всех участников задачи
600
+
601
+ Метод `get_all_participants()` возвращает полный список участников задачи (ответственный, соисполнители, аудиторы, владелец) в одном запросе:
602
+
603
+ ```python
604
+ participants = await client.tasks.get_all_participants(
605
+ task_id=123,
606
+ limit=None, # int: Количество элементов
607
+ # ... стандартные параметры пагинации
608
+ )
609
+ # Возвращает: list[Employee | ContractorHuman | Group]
610
+
611
+ for participant in participants:
612
+ if hasattr(participant, 'display_name'):
613
+ print(participant.display_name())
614
+ ```
615
+
616
+ **Типы участников:**
617
+ - `Employee` — сотрудник организации
618
+ - `ContractorHuman` — контрагент-физлицо
619
+ - `Group` — группа участников (например, отдел)
620
+
573
621
  ### Получение задач на уровне дерева
574
622
 
575
623
  ```python
@@ -727,8 +775,8 @@ milestone = await client.projects.add_milestone(
727
775
  - `tags: list[BaseEntity]` - Теги
728
776
  - `attaches: list[BaseEntity]` - Вложения
729
777
  - `todos: list[BaseEntity]` - Подзадачи-чеклисты
730
- - `created_at: str` - Дата создания
731
- - `updated_at: str` - Дата обновления
778
+ - `time_created: str` - Дата создания (API поле `timeCreated`)
779
+ - `time_updated: str` - Дата обновления (API поле `timeUpdated`)
732
780
 
733
781
  ### Упрощенные методы создания
734
782
 
@@ -790,6 +838,45 @@ actual_issues = await client.projects.get_actual_issues(
790
838
  # Возвращает: list[Task] - список актуальных задач проекта
791
839
  ```
792
840
 
841
+ ### Получение доступных родителей
842
+
843
+ Методы для получения доступных родительских проектов (для выбора родителя при создании или перемещении проекта):
844
+
845
+ ```python
846
+ # Глобальный поиск доступных родительских проектов
847
+ parents = await client.projects.get_available_parents(
848
+ is_template=False, # bool: Фильтр по шаблонам
849
+ limit=10, # int: Количество элементов
850
+ )
851
+ for parent in parents:
852
+ print(f"Project: {parent.name}")
853
+
854
+ # Доступные родители для существующего проекта
855
+ # Исключает сам проект и его потомков
856
+ parents = await client.projects.get_available_parents_for(
857
+ project_id=456,
858
+ is_template=False,
859
+ limit=10,
860
+ )
861
+ ```
862
+
863
+ **Примечание:** В отличие от задач, проекты могут быть вложены только в другие проекты, поэтому возвращается список объектов `Project`.
864
+
865
+ ### Получение всех участников проекта
866
+
867
+ Метод `get_all_participants()` возвращает полный список участников проекта в одном запросе:
868
+
869
+ ```python
870
+ participants = await client.projects.get_all_participants(
871
+ project_id=123,
872
+ limit=None, # int: Количество элементов
873
+ )
874
+ # Возвращает: list[Employee | ContractorHuman | Group]
875
+
876
+ for participant in participants:
877
+ print(f"{type(participant).__name__}: {participant.display_name()}")
878
+ ```
879
+
793
880
  ### Получение полной информации о проекте
794
881
 
795
882
  Метод `get_full_details()` для проектов поддерживает следующие специфичные параметры:
@@ -880,18 +967,26 @@ deals = await client.deals.list(filter=filter_obj)
880
967
  ### Поля модели Deal
881
968
  - `id: int` - Идентификатор сделки
882
969
  - `name: str` - Название сделки
970
+ - `number: str` - Номер сделки
971
+ - `short_description: str` - Краткое описание
883
972
  - `program: BaseEntity` - Программа (схема сделки)
884
973
  - `state: ProgramState` - Текущий статус в программе
885
974
  - `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
886
- - `responsible: BaseEntity` - Ответственный (Employee)
887
- - `sum_base: float` - Сумма сделки
975
+ - `manager: BaseEntity` - Ответственный (Employee, API поле `manager`)
976
+ - `price: Money` - Сумма сделки (объект с полями `currency`, `value`)
977
+ - `cost: Money` - Стоимость
978
+ - `debt: Money` - Долг
979
+ - `result: str` - Результат (`"positive"`, `"negative"`, `null`)
888
980
  - `currency: BaseEntity` - Валюта
889
981
  - `deadline: str` - Срок
890
- - `description: str` - Описание
982
+ - `description: str` - Описание (только в `deals.get()`, не в списке)
891
983
  - `tags: list[BaseEntity]` - Теги
892
984
  - `attaches: list[BaseEntity]` - Вложения
893
- - `created_at: str` - Дата создания
894
- - `updated_at: str` - Дата обновления
985
+ - `time_created: str` - Дата создания (API поле `timeCreated`)
986
+ - `time_updated: str` - Дата обновления (API поле `timeUpdated`)
987
+ - `state_time_updated: str` - Дата последнего изменения статуса
988
+
989
+ > **Примечание:** Поля `description`, `deadline` и пользовательские поля доступны только при запросе отдельной сделки через `deals.get(id)`, но не в списке.
895
990
 
896
991
  **Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
897
992
 
@@ -917,6 +1012,23 @@ deal = await client.deals.apply_trigger(
917
1012
  # Возвращает: Deal - обновленная сделка
918
1013
  ```
919
1014
 
1015
+ ### Получение всех участников сделки
1016
+
1017
+ Метод `get_all_participants()` возвращает полный список участников сделки:
1018
+
1019
+ ```python
1020
+ participants = await client.deals.get_all_participants(
1021
+ deal_id=200,
1022
+ limit=None, # int: Количество элементов
1023
+ )
1024
+ # Возвращает: list[Employee]
1025
+
1026
+ for employee in participants:
1027
+ print(employee.display_name())
1028
+ ```
1029
+
1030
+ **Примечание:** В отличие от задач и проектов, сделки возвращают только сотрудников (`Employee`).
1031
+
920
1032
  ### Получение аудиторов сделки
921
1033
 
922
1034
  ```python
@@ -959,7 +1071,7 @@ details = await client.deals.get_full_details(
959
1071
  include_history=True, # Загрузить историю изменений
960
1072
  include_status_history=True, # Загрузить историю статусов
961
1073
  include_auditors=True, # Загрузить список аудиторов
962
- include_responsible_details=True, # Загрузить полные данные ответственного
1074
+ include_manager_details=True, # Загрузить полные данные ответственного
963
1075
  include_contractor_details=True, # Загрузить полные данные контрагента
964
1076
  include_related_tasks=True, # Загрузить связанные задачи
965
1077
  comments_limit=50, # Лимит комментариев (опционально)
@@ -973,7 +1085,7 @@ details = await client.deals.get_full_details(
973
1085
  - `history: list[dict] | None` - История изменений
974
1086
  - `status_history: list[dict] | None` - История статусов
975
1087
  - `auditors: list[dict] | None` - Аудиторы
976
- - `responsible_details: Employee | None` - Полные данные ответственного
1088
+ - `manager_details: Employee | None` - Полные данные ответственного
977
1089
  - `contractor_details: Contractor | None` - Полные данные контрагента
978
1090
  - `related_tasks: list[Task] | None` - Связанные задачи
979
1091
 
@@ -1174,14 +1286,14 @@ for task_full in tasks_full:
1174
1286
  ### Использование expand в сделках
1175
1287
 
1176
1288
  ```python
1177
- deals_full = await client.deals.list(limit=10, expand=["responsible", "contractor"])
1289
+ deals_full = await client.deals.list(limit=10, expand=["manager", "contractor"])
1178
1290
 
1179
1291
  for deal_full in deals_full:
1180
1292
  deal = deal_full.deal
1181
1293
  print(f"Сделка: {deal.name}")
1182
1294
 
1183
- if deal_full.responsible_details:
1184
- print(f"Ответственный: {deal_full.responsible_details.display_name()}")
1295
+ if deal_full.manager_details:
1296
+ print(f"Ответственный: {deal_full.manager_details.display_name()}")
1185
1297
 
1186
1298
  if deal_full.contractor_details:
1187
1299
  print(f"Контрагент: {deal_full.contractor_details.display_name()}")
@@ -1192,7 +1304,7 @@ for deal_full in deals_full:
1192
1304
  ```
1193
1305
 
1194
1306
  **Поддерживаемые поля для expand в сделках:**
1195
- - `responsible` - ответственный сотрудник
1307
+ - `manager` - ответственный сотрудник
1196
1308
  - `contractor` - контрагент
1197
1309
 
1198
1310
  ### Использование expand в проектах
@@ -1402,6 +1514,48 @@ client = MegaplanClient(
1402
1514
  )
1403
1515
  ```
1404
1516
 
1517
+ ### Работа через прокси
1518
+
1519
+ SDK поддерживает работу через HTTP/HTTPS/SOCKS5 прокси-серверы. Это полезно для корпоративных сетей, где все запросы должны проходить через прокси.
1520
+
1521
+ ```python
1522
+ # HTTP прокси с аутентификацией
1523
+ async with MegaplanClient(
1524
+ base_url="https://my.megaplan.ru",
1525
+ username="user@example.com",
1526
+ password="password",
1527
+ proxy="http://login:pass@proxy.corp.local:8080",
1528
+ ) as client:
1529
+ tasks = await client.tasks.list()
1530
+
1531
+ # HTTP прокси без аутентификации
1532
+ client = MegaplanClient(
1533
+ base_url="https://my.megaplan.ru",
1534
+ access_token="token",
1535
+ proxy="http://proxy.corp.local:8080",
1536
+ )
1537
+
1538
+ # HTTPS прокси
1539
+ client = MegaplanClient(
1540
+ base_url="https://my.megaplan.ru",
1541
+ access_token="token",
1542
+ proxy="https://proxy.corp.local:8080",
1543
+ )
1544
+
1545
+ # SOCKS5 прокси (требует httpx[socks])
1546
+ client = MegaplanClient(
1547
+ base_url="https://my.megaplan.ru",
1548
+ access_token="token",
1549
+ proxy="socks5://user:pass@proxy.corp.local:1080",
1550
+ )
1551
+ ```
1552
+
1553
+ **Поддерживаемые форматы прокси:**
1554
+ - `http://proxy:port` - HTTP прокси без аутентификации
1555
+ - `http://user:password@proxy:port` - HTTP прокси с аутентификацией
1556
+ - `https://proxy:port` - HTTPS прокси
1557
+ - `socks5://user:password@proxy:port` - SOCKS5 прокси (требует `pip install httpx[socks]`)
1558
+
1405
1559
  ### Ручное управление токенами
1406
1560
 
1407
1561
  ```python
@@ -1440,13 +1594,39 @@ API возвращает ошибку 500 при попытке получить
1440
1594
  # comments = await client.contractors.get_comments(contractor_id=123)
1441
1595
 
1442
1596
  # Вместо этого используйте комментарии в сделках контрагента
1443
- deals = await client.deals.list(
1444
- base_on={"contentType": "Contractor", "id": 123}
1445
- )
1597
+ deals = await client.contractors.get_deals(contractor_id=123)
1446
1598
  for deal in deals:
1447
1599
  comments = await client.deals.get_comments(deal.id)
1448
1600
  ```
1449
1601
 
1602
+ ### Получение сделок контрагента
1603
+
1604
+ SDK предоставляет удобный метод `get_deals()` для получения сделок контрагента:
1605
+
1606
+ ```python
1607
+ # Получить все сделки контрагента
1608
+ deals = await client.contractors.get_deals(
1609
+ contractor_id=123,
1610
+ limit=50 # Опционально
1611
+ )
1612
+
1613
+ for deal in deals:
1614
+ print(f"[{deal.id}] {deal.name}")
1615
+ if deal.state:
1616
+ print(f" Статус: {deal.state}")
1617
+ ```
1618
+
1619
+ Это удобнее, чем использование `FilterBuilder`:
1620
+
1621
+ ```python
1622
+ # Альтернатива через FilterBuilder (более сложный способ)
1623
+ from megaplan_sdk import TradeFilterBuilder
1624
+ filter_obj = TradeFilterBuilder().field("contractor").equals(
1625
+ {"contentType": "Contractor", "id": 123}
1626
+ ).build()
1627
+ deals = await client.deals.list(filter=filter_obj)
1628
+ ```
1629
+
1450
1630
  ### Поиск сотрудников
1451
1631
 
1452
1632
  Поиск сотрудников по имени или телефону может работать некорректно и возвращать 0 результатов. Для надежного поиска используйте точный email:
@@ -42,6 +42,7 @@
42
42
  - [Автоматическая подгрузка связанных сущностей](#автоматическая-подгрузка-связанных-сущностей)
43
43
  - [Работа с фильтрами](#работа-с-фильтрами)
44
44
  - [Настройка HTTP-клиента](#настройка-http-клиента)
45
+ - [Работа через прокси](#работа-через-прокси)
45
46
  - [Ручное управление токенами](#ручное-управление-токенами)
46
47
 
47
48
  ### Справочная информация
@@ -281,7 +282,7 @@ entity = await client.{resource}.update(
281
282
  ```python
282
283
  task = await client.tasks.update(task_id=42, task_data={"status": "completed"})
283
284
  project = await client.projects.update(project_id=5, project_data={"name": "Новое название"})
284
- deal = await client.deals.update(deal_id=200, deal_data={"sum_base": 60000.0})
285
+ deal = await client.deals.update(deal_id=200, deal_data={"price": {"currency": "RUB", "value": 60000}})
285
286
  ```
286
287
 
287
288
  #### Удаление (`delete`)
@@ -484,8 +485,8 @@ tasks = await client.tasks.list(filter=filter_obj)
484
485
  - `tags: list[BaseEntity]` - Теги
485
486
  - `attaches: list[BaseEntity]` - Вложения (файлы)
486
487
  - `todos: list[BaseEntity]` - Подзадачи-чеклисты
487
- - `created_at: str` - Дата создания
488
- - `updated_at: str` - Дата обновления
488
+ - `time_created: str` - Дата создания (API поле `timeCreated`)
489
+ - `time_updated: str` - Дата обновления (API поле `timeUpdated`)
489
490
 
490
491
  ### Упрощенные методы создания
491
492
 
@@ -538,6 +539,53 @@ actual_subtasks = await client.tasks.get_actual_sub_tasks(
538
539
  # Возвращает: list[Task] - список актуальных подзадач
539
540
  ```
540
541
 
542
+ ### Получение доступных родителей
543
+
544
+ Методы для получения доступных надзадач и надпроектов (для выбора родителя при создании или перемещении задачи):
545
+
546
+ ```python
547
+ # Глобальный поиск доступных родителей для новой задачи
548
+ # Возвращает список Task и Project объектов
549
+ parents = await client.tasks.get_available_parents(
550
+ is_template=False, # bool: Фильтр по шаблонам
551
+ limit=10, # int: Количество элементов
552
+ )
553
+ for parent in parents:
554
+ print(f"{type(parent).__name__}: {parent.name}") # "Task: ..." или "Project: ..."
555
+
556
+ # Доступные родители для существующей задачи
557
+ # Исключает саму задачу и её потомков
558
+ parents = await client.tasks.get_available_parents_for(
559
+ task_id=123,
560
+ is_template=False,
561
+ limit=10,
562
+ )
563
+ ```
564
+
565
+ **Примечание:** Методы возвращают смешанный список объектов `Task` и `Project`, так как задача может быть вложена как в другую задачу, так и в проект.
566
+
567
+ ### Получение всех участников задачи
568
+
569
+ Метод `get_all_participants()` возвращает полный список участников задачи (ответственный, соисполнители, аудиторы, владелец) в одном запросе:
570
+
571
+ ```python
572
+ participants = await client.tasks.get_all_participants(
573
+ task_id=123,
574
+ limit=None, # int: Количество элементов
575
+ # ... стандартные параметры пагинации
576
+ )
577
+ # Возвращает: list[Employee | ContractorHuman | Group]
578
+
579
+ for participant in participants:
580
+ if hasattr(participant, 'display_name'):
581
+ print(participant.display_name())
582
+ ```
583
+
584
+ **Типы участников:**
585
+ - `Employee` — сотрудник организации
586
+ - `ContractorHuman` — контрагент-физлицо
587
+ - `Group` — группа участников (например, отдел)
588
+
541
589
  ### Получение задач на уровне дерева
542
590
 
543
591
  ```python
@@ -695,8 +743,8 @@ milestone = await client.projects.add_milestone(
695
743
  - `tags: list[BaseEntity]` - Теги
696
744
  - `attaches: list[BaseEntity]` - Вложения
697
745
  - `todos: list[BaseEntity]` - Подзадачи-чеклисты
698
- - `created_at: str` - Дата создания
699
- - `updated_at: str` - Дата обновления
746
+ - `time_created: str` - Дата создания (API поле `timeCreated`)
747
+ - `time_updated: str` - Дата обновления (API поле `timeUpdated`)
700
748
 
701
749
  ### Упрощенные методы создания
702
750
 
@@ -758,6 +806,45 @@ actual_issues = await client.projects.get_actual_issues(
758
806
  # Возвращает: list[Task] - список актуальных задач проекта
759
807
  ```
760
808
 
809
+ ### Получение доступных родителей
810
+
811
+ Методы для получения доступных родительских проектов (для выбора родителя при создании или перемещении проекта):
812
+
813
+ ```python
814
+ # Глобальный поиск доступных родительских проектов
815
+ parents = await client.projects.get_available_parents(
816
+ is_template=False, # bool: Фильтр по шаблонам
817
+ limit=10, # int: Количество элементов
818
+ )
819
+ for parent in parents:
820
+ print(f"Project: {parent.name}")
821
+
822
+ # Доступные родители для существующего проекта
823
+ # Исключает сам проект и его потомков
824
+ parents = await client.projects.get_available_parents_for(
825
+ project_id=456,
826
+ is_template=False,
827
+ limit=10,
828
+ )
829
+ ```
830
+
831
+ **Примечание:** В отличие от задач, проекты могут быть вложены только в другие проекты, поэтому возвращается список объектов `Project`.
832
+
833
+ ### Получение всех участников проекта
834
+
835
+ Метод `get_all_participants()` возвращает полный список участников проекта в одном запросе:
836
+
837
+ ```python
838
+ participants = await client.projects.get_all_participants(
839
+ project_id=123,
840
+ limit=None, # int: Количество элементов
841
+ )
842
+ # Возвращает: list[Employee | ContractorHuman | Group]
843
+
844
+ for participant in participants:
845
+ print(f"{type(participant).__name__}: {participant.display_name()}")
846
+ ```
847
+
761
848
  ### Получение полной информации о проекте
762
849
 
763
850
  Метод `get_full_details()` для проектов поддерживает следующие специфичные параметры:
@@ -848,18 +935,26 @@ deals = await client.deals.list(filter=filter_obj)
848
935
  ### Поля модели Deal
849
936
  - `id: int` - Идентификатор сделки
850
937
  - `name: str` - Название сделки
938
+ - `number: str` - Номер сделки
939
+ - `short_description: str` - Краткое описание
851
940
  - `program: BaseEntity` - Программа (схема сделки)
852
941
  - `state: ProgramState` - Текущий статус в программе
853
942
  - `contractor: BaseEntity` - Контрагент (ContractorCompany/ContractorHuman)
854
- - `responsible: BaseEntity` - Ответственный (Employee)
855
- - `sum_base: float` - Сумма сделки
943
+ - `manager: BaseEntity` - Ответственный (Employee, API поле `manager`)
944
+ - `price: Money` - Сумма сделки (объект с полями `currency`, `value`)
945
+ - `cost: Money` - Стоимость
946
+ - `debt: Money` - Долг
947
+ - `result: str` - Результат (`"positive"`, `"negative"`, `null`)
856
948
  - `currency: BaseEntity` - Валюта
857
949
  - `deadline: str` - Срок
858
- - `description: str` - Описание
950
+ - `description: str` - Описание (только в `deals.get()`, не в списке)
859
951
  - `tags: list[BaseEntity]` - Теги
860
952
  - `attaches: list[BaseEntity]` - Вложения
861
- - `created_at: str` - Дата создания
862
- - `updated_at: str` - Дата обновления
953
+ - `time_created: str` - Дата создания (API поле `timeCreated`)
954
+ - `time_updated: str` - Дата обновления (API поле `timeUpdated`)
955
+ - `state_time_updated: str` - Дата последнего изменения статуса
956
+
957
+ > **Примечание:** Поля `description`, `deadline` и пользовательские поля доступны только при запросе отдельной сделки через `deals.get(id)`, но не в списке.
863
958
 
864
959
  **Важно:** При создании сделки обязательно указывать поле `program` (программа/схема сделки).
865
960
 
@@ -885,6 +980,23 @@ deal = await client.deals.apply_trigger(
885
980
  # Возвращает: Deal - обновленная сделка
886
981
  ```
887
982
 
983
+ ### Получение всех участников сделки
984
+
985
+ Метод `get_all_participants()` возвращает полный список участников сделки:
986
+
987
+ ```python
988
+ participants = await client.deals.get_all_participants(
989
+ deal_id=200,
990
+ limit=None, # int: Количество элементов
991
+ )
992
+ # Возвращает: list[Employee]
993
+
994
+ for employee in participants:
995
+ print(employee.display_name())
996
+ ```
997
+
998
+ **Примечание:** В отличие от задач и проектов, сделки возвращают только сотрудников (`Employee`).
999
+
888
1000
  ### Получение аудиторов сделки
889
1001
 
890
1002
  ```python
@@ -927,7 +1039,7 @@ details = await client.deals.get_full_details(
927
1039
  include_history=True, # Загрузить историю изменений
928
1040
  include_status_history=True, # Загрузить историю статусов
929
1041
  include_auditors=True, # Загрузить список аудиторов
930
- include_responsible_details=True, # Загрузить полные данные ответственного
1042
+ include_manager_details=True, # Загрузить полные данные ответственного
931
1043
  include_contractor_details=True, # Загрузить полные данные контрагента
932
1044
  include_related_tasks=True, # Загрузить связанные задачи
933
1045
  comments_limit=50, # Лимит комментариев (опционально)
@@ -941,7 +1053,7 @@ details = await client.deals.get_full_details(
941
1053
  - `history: list[dict] | None` - История изменений
942
1054
  - `status_history: list[dict] | None` - История статусов
943
1055
  - `auditors: list[dict] | None` - Аудиторы
944
- - `responsible_details: Employee | None` - Полные данные ответственного
1056
+ - `manager_details: Employee | None` - Полные данные ответственного
945
1057
  - `contractor_details: Contractor | None` - Полные данные контрагента
946
1058
  - `related_tasks: list[Task] | None` - Связанные задачи
947
1059
 
@@ -1142,14 +1254,14 @@ for task_full in tasks_full:
1142
1254
  ### Использование expand в сделках
1143
1255
 
1144
1256
  ```python
1145
- deals_full = await client.deals.list(limit=10, expand=["responsible", "contractor"])
1257
+ deals_full = await client.deals.list(limit=10, expand=["manager", "contractor"])
1146
1258
 
1147
1259
  for deal_full in deals_full:
1148
1260
  deal = deal_full.deal
1149
1261
  print(f"Сделка: {deal.name}")
1150
1262
 
1151
- if deal_full.responsible_details:
1152
- print(f"Ответственный: {deal_full.responsible_details.display_name()}")
1263
+ if deal_full.manager_details:
1264
+ print(f"Ответственный: {deal_full.manager_details.display_name()}")
1153
1265
 
1154
1266
  if deal_full.contractor_details:
1155
1267
  print(f"Контрагент: {deal_full.contractor_details.display_name()}")
@@ -1160,7 +1272,7 @@ for deal_full in deals_full:
1160
1272
  ```
1161
1273
 
1162
1274
  **Поддерживаемые поля для expand в сделках:**
1163
- - `responsible` - ответственный сотрудник
1275
+ - `manager` - ответственный сотрудник
1164
1276
  - `contractor` - контрагент
1165
1277
 
1166
1278
  ### Использование expand в проектах
@@ -1370,6 +1482,48 @@ client = MegaplanClient(
1370
1482
  )
1371
1483
  ```
1372
1484
 
1485
+ ### Работа через прокси
1486
+
1487
+ SDK поддерживает работу через HTTP/HTTPS/SOCKS5 прокси-серверы. Это полезно для корпоративных сетей, где все запросы должны проходить через прокси.
1488
+
1489
+ ```python
1490
+ # HTTP прокси с аутентификацией
1491
+ async with MegaplanClient(
1492
+ base_url="https://my.megaplan.ru",
1493
+ username="user@example.com",
1494
+ password="password",
1495
+ proxy="http://login:pass@proxy.corp.local:8080",
1496
+ ) as client:
1497
+ tasks = await client.tasks.list()
1498
+
1499
+ # HTTP прокси без аутентификации
1500
+ client = MegaplanClient(
1501
+ base_url="https://my.megaplan.ru",
1502
+ access_token="token",
1503
+ proxy="http://proxy.corp.local:8080",
1504
+ )
1505
+
1506
+ # HTTPS прокси
1507
+ client = MegaplanClient(
1508
+ base_url="https://my.megaplan.ru",
1509
+ access_token="token",
1510
+ proxy="https://proxy.corp.local:8080",
1511
+ )
1512
+
1513
+ # SOCKS5 прокси (требует httpx[socks])
1514
+ client = MegaplanClient(
1515
+ base_url="https://my.megaplan.ru",
1516
+ access_token="token",
1517
+ proxy="socks5://user:pass@proxy.corp.local:1080",
1518
+ )
1519
+ ```
1520
+
1521
+ **Поддерживаемые форматы прокси:**
1522
+ - `http://proxy:port` - HTTP прокси без аутентификации
1523
+ - `http://user:password@proxy:port` - HTTP прокси с аутентификацией
1524
+ - `https://proxy:port` - HTTPS прокси
1525
+ - `socks5://user:password@proxy:port` - SOCKS5 прокси (требует `pip install httpx[socks]`)
1526
+
1373
1527
  ### Ручное управление токенами
1374
1528
 
1375
1529
  ```python
@@ -1408,13 +1562,39 @@ API возвращает ошибку 500 при попытке получить
1408
1562
  # comments = await client.contractors.get_comments(contractor_id=123)
1409
1563
 
1410
1564
  # Вместо этого используйте комментарии в сделках контрагента
1411
- deals = await client.deals.list(
1412
- base_on={"contentType": "Contractor", "id": 123}
1413
- )
1565
+ deals = await client.contractors.get_deals(contractor_id=123)
1414
1566
  for deal in deals:
1415
1567
  comments = await client.deals.get_comments(deal.id)
1416
1568
  ```
1417
1569
 
1570
+ ### Получение сделок контрагента
1571
+
1572
+ SDK предоставляет удобный метод `get_deals()` для получения сделок контрагента:
1573
+
1574
+ ```python
1575
+ # Получить все сделки контрагента
1576
+ deals = await client.contractors.get_deals(
1577
+ contractor_id=123,
1578
+ limit=50 # Опционально
1579
+ )
1580
+
1581
+ for deal in deals:
1582
+ print(f"[{deal.id}] {deal.name}")
1583
+ if deal.state:
1584
+ print(f" Статус: {deal.state}")
1585
+ ```
1586
+
1587
+ Это удобнее, чем использование `FilterBuilder`:
1588
+
1589
+ ```python
1590
+ # Альтернатива через FilterBuilder (более сложный способ)
1591
+ from megaplan_sdk import TradeFilterBuilder
1592
+ filter_obj = TradeFilterBuilder().field("contractor").equals(
1593
+ {"contentType": "Contractor", "id": 123}
1594
+ ).build()
1595
+ deals = await client.deals.list(filter=filter_obj)
1596
+ ```
1597
+
1418
1598
  ### Поиск сотрудников
1419
1599
 
1420
1600
  Поиск сотрудников по имени или телефону может работать некорректно и возвращать 0 результатов. Для надежного поиска используйте точный email:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "megaplan-sdk"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Professional Python SDK for Megaplan API v3"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"