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.
Files changed (48) hide show
  1. {megaplan_sdk-0.2.1/src/megaplan_sdk.egg-info → megaplan_sdk-0.2.2}/PKG-INFO +25 -17
  2. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/README.md +24 -16
  3. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/pyproject.toml +1 -1
  4. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/__init__.py +3 -2
  5. megaplan_sdk-0.2.2/src/megaplan_sdk/models/base.py +27 -0
  6. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/comment.py +1 -1
  7. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/common.py +39 -6
  8. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/contractor.py +1 -1
  9. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/deal.py +36 -10
  10. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/department.py +1 -1
  11. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/employee.py +1 -1
  12. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/filter.py +2 -2
  13. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/group.py +1 -1
  14. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/milestone.py +1 -1
  15. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/project.py +2 -2
  16. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/task.py +2 -2
  17. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/deals.py +22 -19
  18. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/projects.py +20 -0
  19. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2/src/megaplan_sdk.egg-info}/PKG-INFO +25 -17
  20. megaplan_sdk-0.2.1/src/megaplan_sdk/models/base.py +0 -16
  21. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/LICENSE +0 -0
  22. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/setup.cfg +0 -0
  23. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/auth.py +0 -0
  24. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/cache.py +0 -0
  25. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/client.py +0 -0
  26. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/constants.py +0 -0
  27. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/exceptions.py +0 -0
  28. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/filter_builder.py +0 -0
  29. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/helpers.py +0 -0
  30. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/http_client.py +0 -0
  31. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/logging_config.py +0 -0
  32. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/__init__.py +0 -0
  33. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/models/participant.py +0 -0
  34. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/__init__.py +0 -0
  35. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/auth.py +0 -0
  36. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/base.py +0 -0
  37. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/comments.py +0 -0
  38. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/contractors.py +0 -0
  39. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/departments.py +0 -0
  40. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/employees.py +0 -0
  41. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/filters.py +0 -0
  42. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/full_details.py +0 -0
  43. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/resources/tasks.py +0 -0
  44. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk/types.py +0 -0
  45. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/SOURCES.txt +0 -0
  46. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/dependency_links.txt +0 -0
  47. {megaplan_sdk-0.2.1 → megaplan_sdk-0.2.2}/src/megaplan_sdk.egg-info/requires.txt +0 -0
  48. {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.1
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={"sum_base": 60000.0})
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
- - `created_at: str` - Дата создания
521
- - `updated_at: str` - Дата обновления
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
- - `created_at: str` - Дата создания
779
- - `updated_at: str` - Дата обновления
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
- - `responsible: BaseEntity` - Ответственный (Employee)
974
- - `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`)
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
- - `created_at: str` - Дата создания
981
- - `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)`, но не в списке.
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
- include_responsible_details=True, # Загрузить полные данные ответственного
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
- - `responsible_details: Employee | None` - Полные данные ответственного
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=["responsible", "contractor"])
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.responsible_details:
1288
- print(f"Ответственный: {deal_full.responsible_details.display_name()}")
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
- - `responsible` - ответственный сотрудник
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={"sum_base": 60000.0})
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
- - `created_at: str` - Дата создания
489
- - `updated_at: str` - Дата обновления
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
- - `created_at: str` - Дата создания
747
- - `updated_at: str` - Дата обновления
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
- - `responsible: BaseEntity` - Ответственный (Employee)
942
- - `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`)
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
- - `created_at: str` - Дата создания
949
- - `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)`, но не в списке.
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
- include_responsible_details=True, # Загрузить полные данные ответственного
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
- - `responsible_details: Employee | None` - Полные данные ответственного
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=["responsible", "contractor"])
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.responsible_details:
1256
- print(f"Ответственный: {deal_full.responsible_details.display_name()}")
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
- - `responsible` - ответственный сотрудник
1275
+ - `manager` - ответственный сотрудник
1268
1276
  - `contractor` - контрагент
1269
1277
 
1270
1278
  ### Использование expand в проектах
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "megaplan-sdk"
7
- version = "0.2.1"
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"
@@ -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.1.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="ignore")
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="ignore")
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="ignore")
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
- created_at: Entity creation timestamp.
89
- updated_at: Entity last update timestamp.
121
+ time_created: Entity creation timestamp (API field: ``timeCreated``).
122
+ time_updated: Entity last update timestamp (API field: ``timeUpdated``).
90
123
  """
91
124
 
92
- created_at: str | DateTime | None = Field(alias="createdAt", default=None)
93
- updated_at: str | DateTime | None = Field(alias="updatedAt", default=None)
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="ignore")
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="ignore")
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
- deadline: str | DateTime | dict[str, Any] | None = None # Can be DateOnly, DateTime, or string
66
- description: str | None = None
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="ignore")
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
- responsible_details: Full responsible employee details (if requested).
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
- responsible_details: Any | None = None
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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="ignore")
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
- "responsible_details",
53
- "include_responsible_details",
52
+ "manager_details",
53
+ "include_manager_details",
54
54
  None,
55
- entity_field="responsible",
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 include.
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., ["responsible", "contractor"]).
197
- Supported values: "responsible", "contractor".
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 responsible and contractor
210
+ >>> # Get deals with expanded manager and contractor
208
211
  >>> deals_full = await client.deals.list(
209
- ... limit=10, expand=["responsible", "contractor"]
212
+ ... limit=10, expand=["manager", "contractor"]
210
213
  ... )
211
214
  >>> for deal_full in deals_full:
212
- ... if deal_full.responsible_details:
213
- ... print(deal_full.responsible_details.display_name())
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
- "responsible": ("employee", Employee, ContentType.EMPLOYEE),
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
- responsible_map = expanded.get("responsible", {})
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
- resp_details = None
275
+ mgr_details = None
273
276
  contr_details = None
274
277
 
275
- if deal.responsible and deal.responsible.id in responsible_map:
276
- resp_details = responsible_map[deal.responsible.id]
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
- responsible_details=resp_details,
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
- include_responsible_details: bool = False,
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
- include_responsible_details: Load full responsible (Employee) details.
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
- include_responsible_details=include_responsible_details,
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.1
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={"sum_base": 60000.0})
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
- - `created_at: str` - Дата создания
521
- - `updated_at: str` - Дата обновления
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
- - `created_at: str` - Дата создания
779
- - `updated_at: str` - Дата обновления
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
- - `responsible: BaseEntity` - Ответственный (Employee)
974
- - `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`)
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
- - `created_at: str` - Дата создания
981
- - `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)`, но не в списке.
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
- include_responsible_details=True, # Загрузить полные данные ответственного
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
- - `responsible_details: Employee | None` - Полные данные ответственного
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=["responsible", "contractor"])
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.responsible_details:
1288
- print(f"Ответственный: {deal_full.responsible_details.display_name()}")
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
- - `responsible` - ответственный сотрудник
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