amsdal_crm 0.1.10__py3-none-any.whl → 0.2.1__py3-none-any.whl

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.
@@ -4,45 +4,12 @@ from amsdal_utils.models.enums import ModuleType
4
4
 
5
5
  class Migration(migrations.Migration):
6
6
  operations: list[migrations.Operation] = [
7
- migrations.CreateClass(
8
- module_type=ModuleType.CONTRIB,
9
- class_name="Account",
10
- new_schema={
11
- "title": "Account",
12
- "required": ["name", "owner_email"],
13
- "properties": {
14
- "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
15
- "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
16
- "name": {"type": "string", "title": "Account Name"},
17
- "website": {"type": "string", "title": "Website"},
18
- "phone": {"type": "string", "title": "Phone"},
19
- "industry": {"type": "string", "title": "Industry"},
20
- "billing_street": {"type": "string", "title": "Billing Street"},
21
- "billing_city": {"type": "string", "title": "Billing City"},
22
- "billing_state": {"type": "string", "title": "Billing State"},
23
- "billing_postal_code": {"type": "string", "title": "Billing Postal Code"},
24
- "billing_country": {"type": "string", "title": "Billing Country"},
25
- "owner_email": {"type": "string", "title": "Owner Email"},
26
- "custom_fields": {"type": "anything", "title": "Custom Fields"},
27
- },
28
- "custom_code": 'from amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n """Return display name for the account."""\n return self.name\n\nasync def apost_update(self) -> None:\n """Async hook called after updating account."""\n from amsdal_crm.services.workflow_service import WorkflowService\n await WorkflowService.aexecute_rules(\'Account\', \'update\', self)\n\nasync def apre_create(self) -> None:\n """Async hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Account\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Account\', self.custom_fields)\n await super().apre_update()\n\ndef has_object_permission(self, user: \'User\', action: str) -> bool:\n """Check if user has permission to perform action on this account.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n """\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == \'*\' and permission.action in (\'*\', action):\n return True\n if permission.model == \'Account\' and permission.action in (\'*\', action):\n return True\n return False\n\ndef post_update(self) -> None:\n """Hook called after updating account."""\n from amsdal_crm.services.workflow_service import WorkflowService\n WorkflowService.execute_rules(\'Account\', \'update\', self)\n\ndef pre_create(self) -> None:\n """Hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Account\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Account\', self.custom_fields)\n super().pre_update()',
29
- "storage_metadata": {
30
- "table_name": "Account",
31
- "db_fields": {},
32
- "primary_key": ["partition_key"],
33
- "indexed": [["owner_email"], ["created_at"]],
34
- "unique": [["name", "owner_email"]],
35
- "foreign_keys": {},
36
- },
37
- "description": "Account (Company/Organization) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
38
- },
39
- ),
40
7
  migrations.CreateClass(
41
8
  module_type=ModuleType.CONTRIB,
42
9
  class_name="Activity",
43
10
  new_schema={
44
11
  "title": "Activity",
45
- "required": ["activity_type", "subject", "related_to_type", "related_to_id", "owner_email"],
12
+ "required": ["activity_type", "subject"],
46
13
  "properties": {
47
14
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
48
15
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
@@ -63,89 +30,113 @@ class Migration(migrations.Migration):
63
30
  "subject": {"type": "string", "title": "Subject"},
64
31
  "description": {"type": "string", "title": "Description"},
65
32
  "related_to_type": {
66
- "type": "ActivityRelatedTo",
67
- "options": [
68
- {"key": "CONTACT", "value": "Contact"},
69
- {"key": "ACCOUNT", "value": "Account"},
70
- {"key": "DEAL", "value": "Deal"},
71
- ],
72
- "title": "ActivityRelatedTo",
33
+ "type": "string",
34
+ "options": [{"key": "ENTITY", "value": "Entity"}, {"key": "DEAL", "value": "Deal"}],
35
+ "title": "Related To Type",
73
36
  "description": "What type of record this activity is related to.",
74
- "enum": ["Contact", "Account", "Deal"],
75
- "x_enum_names": ["CONTACT", "ACCOUNT", "DEAL"],
37
+ "enum": ["Entity", "Deal"],
38
+ "x_enum_names": ["ENTITY", "DEAL"],
76
39
  },
77
40
  "related_to_id": {"type": "string", "title": "Related To ID"},
78
- "owner_email": {"type": "string", "title": "Owner Email"},
79
41
  "due_date": {"type": "datetime", "title": "Due Date", "format": "date-time"},
80
42
  "completed_at": {"type": "datetime", "title": "Completed At", "format": "date-time"},
81
43
  "is_completed": {"type": "boolean", "default": False, "title": "Is Completed"},
44
+ "assigned_to": {"type": "User", "title": "Assigned To"},
82
45
  },
83
- "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
46
+ "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
84
47
  "storage_metadata": {
85
48
  "table_name": "Activity",
86
- "db_fields": {},
49
+ "db_fields": {"assigned_to": ["assigned_to_partition_key"]},
87
50
  "primary_key": ["partition_key"],
88
- "indexed": [["related_to_id"], ["owner_email"], ["created_at"], ["due_date"]],
89
- "foreign_keys": {},
51
+ "indexed": [["related_to_id"], ["created_at"], ["due_date"]],
52
+ "foreign_keys": {
53
+ "assigned_to": [{"assigned_to_partition_key": "string"}, "User", ["partition_key"]]
54
+ },
90
55
  },
91
56
  "description": "Base activity model with polymorphic related_to field.\n\nActivities can be linked to Contacts, Accounts, or Deals using\na generic foreign key pattern (related_to_type + related_to_id).",
92
57
  },
93
58
  ),
94
59
  migrations.CreateClass(
95
60
  module_type=ModuleType.CONTRIB,
96
- class_name="Pipeline",
61
+ class_name="Entity",
97
62
  new_schema={
98
- "title": "Pipeline",
63
+ "title": "Entity",
99
64
  "required": ["name"],
100
65
  "properties": {
101
- "name": {"type": "string", "title": "Pipeline Name"},
102
- "description": {"type": "string", "title": "Description"},
103
- "is_active": {"type": "boolean", "default": True, "title": "Is Active"},
66
+ "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
67
+ "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
68
+ "name": {"type": "string", "title": "Entity Name"},
69
+ "legal_name": {"type": "string", "title": "Legal Name"},
70
+ "status": {
71
+ "type": "string",
72
+ "default": "Active",
73
+ "options": [{"key": "Active", "value": "Active"}, {"key": "Inactive", "value": "Inactive"}],
74
+ "title": "Status",
75
+ "enum": ["Active", "Inactive"],
76
+ },
77
+ "note": {"type": "string", "title": "Note"},
78
+ "custom_fields": {"type": "anything", "title": "Custom Fields"},
79
+ "assigned_to": {"type": "User", "title": "Assigned To"},
104
80
  },
105
- "custom_code": '@property\ndef display_name(self) -> str:\n """Return display name for the pipeline."""\n return self.name',
81
+ "custom_code": 'from amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n """Return display name for the account."""\n return self.name\n\nasync def apost_update(self) -> None:\n """Async hook called after updating account."""\n from amsdal_crm.services.workflow_service import WorkflowService\n await WorkflowService.aexecute_rules(\'Entity\', \'update\', self)\n\nasync def apre_create(self) -> None:\n """Async hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Entity\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Entity\', self.custom_fields)\n await super().apre_update()\n\ndef has_object_permission(self, user: \'User\', action: str) -> bool:\n """Check if user has permission to perform action on this account.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n """\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == \'*\' and permission.action in (\'*\', action):\n return True\n if permission.model == \'Entity\' and permission.action in (\'*\', action):\n return True\n return False\n\ndef post_update(self) -> None:\n """Hook called after updating account."""\n from amsdal_crm.services.workflow_service import WorkflowService\n WorkflowService.execute_rules(\'Entity\', \'update\', self)\n\ndef pre_create(self) -> None:\n """Hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Entity\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Entity\', self.custom_fields)\n super().pre_update()',
106
82
  "storage_metadata": {
107
- "table_name": "Pipeline",
108
- "db_fields": {},
83
+ "table_name": "Entity",
84
+ "db_fields": {"assigned_to": ["assigned_to_partition_key"]},
109
85
  "primary_key": ["partition_key"],
86
+ "indexed": [["created_at"]],
110
87
  "unique": [["name"]],
111
- "foreign_keys": {},
88
+ "foreign_keys": {
89
+ "assigned_to": [{"assigned_to_partition_key": "string"}, "User", ["partition_key"]]
90
+ },
112
91
  },
113
- "description": "Sales pipeline model.\n\nRepresents a sales pipeline with multiple stages.\nPipelines are system-wide and not owned by individual users.",
92
+ "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
114
93
  },
115
94
  ),
116
95
  migrations.CreateClass(
117
96
  module_type=ModuleType.CONTRIB,
118
- class_name="Contact",
97
+ class_name="EntityAddress",
119
98
  new_schema={
120
- "title": "Contact",
121
- "required": ["first_name", "last_name", "email", "owner_email"],
99
+ "title": "EntityAddress",
122
100
  "properties": {
123
101
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
124
102
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
125
- "first_name": {"type": "string", "title": "First Name"},
126
- "last_name": {"type": "string", "title": "Last Name"},
127
- "email": {"type": "string", "title": "Email"},
128
- "phone": {"type": "string", "title": "Phone Number"},
129
- "mobile": {"type": "string", "title": "Mobile Number"},
130
- "title": {"type": "string", "title": "Job Title"},
131
- "account": {
132
- "type": "Account",
133
- "title": "Account",
134
- "description": "Account (Company/Organization) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
135
- },
136
- "owner_email": {"type": "string", "title": "Owner Email"},
137
- "custom_fields": {"type": "anything", "title": "Custom Fields"},
103
+ "line1": {"type": "string", "title": "Address Line 1"},
104
+ "line2": {"type": "string", "title": "Address Line 2"},
105
+ "city": {"type": "string", "title": "City"},
106
+ "region": {"type": "string", "title": "Region/State"},
107
+ "postal_code": {"type": "string", "title": "Postal Code"},
108
+ "country": {"type": "string", "title": "Country"},
109
+ "is_primary": {"type": "boolean", "default": False, "title": "Is Primary"},
110
+ },
111
+ "custom_code": 'async def apre_create(self) -> None:\n """Async hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityAddress\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityAddress\', self.custom_fields)\n await super().apre_update()\n\ndef pre_create(self) -> None:\n """Hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityAddress\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityAddress\', self.custom_fields)\n super().pre_update()',
112
+ "storage_metadata": {
113
+ "table_name": "EntityAddress",
114
+ "db_fields": {},
115
+ "primary_key": ["partition_key"],
116
+ "foreign_keys": {},
117
+ },
118
+ },
119
+ ),
120
+ migrations.CreateClass(
121
+ module_type=ModuleType.CONTRIB,
122
+ class_name="Pipeline",
123
+ new_schema={
124
+ "title": "Pipeline",
125
+ "required": ["name"],
126
+ "properties": {
127
+ "name": {"type": "string", "title": "Pipeline Name"},
128
+ "description": {"type": "string", "title": "Description"},
129
+ "is_active": {"type": "boolean", "default": True, "title": "Is Active"},
138
130
  },
139
- "custom_code": 'from amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n """Return display name for the contact."""\n return f\'{self.first_name} {self.last_name}\'\n\n@property\ndef full_name(self) -> str:\n """Return full name of the contact."""\n return f\'{self.first_name} {self.last_name}\'\n\nasync def apost_update(self) -> None:\n """Async hook called after updating contact."""\n from amsdal_crm.services.workflow_service import WorkflowService\n await WorkflowService.aexecute_rules(\'Contact\', \'update\', self)\n\nasync def apre_create(self) -> None:\n """Async hook called before creating contact."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Contact\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating contact."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Contact\', self.custom_fields)\n await super().apre_update()\n\ndef has_object_permission(self, user: \'User\', action: str) -> bool:\n """Check if user has permission to perform action on this contact.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n """\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == \'*\' and permission.action in (\'*\', action):\n return True\n if permission.model == \'Contact\' and permission.action in (\'*\', action):\n return True\n return False\n\ndef post_update(self) -> None:\n """Hook called after updating contact."""\n from amsdal_crm.services.workflow_service import WorkflowService\n WorkflowService.execute_rules(\'Contact\', \'update\', self)\n\ndef pre_create(self) -> None:\n """Hook called before creating contact."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Contact\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating contact."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Contact\', self.custom_fields)\n super().pre_update()',
131
+ "custom_code": '@property\ndef display_name(self) -> str:\n """Return display name for the pipeline."""\n return self.name',
140
132
  "storage_metadata": {
141
- "table_name": "Contact",
142
- "db_fields": {"account": ["account_partition_key"]},
133
+ "table_name": "Pipeline",
134
+ "db_fields": {},
143
135
  "primary_key": ["partition_key"],
144
- "indexed": [["owner_email"], ["created_at"]],
145
- "unique": [["email"]],
146
- "foreign_keys": {"account": [{"account_partition_key": "string"}, "Account", ["partition_key"]]},
136
+ "unique": [["name"]],
137
+ "foreign_keys": {},
147
138
  },
148
- "description": "Contact (Person) model.\n\nRepresents a person in the CRM system, optionally linked to an Account.\nOwned by individual users with permission controls.",
139
+ "description": "Sales pipeline model.\n\nRepresents a sales pipeline with multiple stages.\nPipelines are system-wide and not owned by individual users.",
149
140
  },
150
141
  ),
151
142
  migrations.CreateClass(
@@ -154,7 +145,7 @@ class Migration(migrations.Migration):
154
145
  new_schema={
155
146
  "title": "Call",
156
147
  "type": "Activity",
157
- "required": ["subject", "related_to_type", "related_to_id", "owner_email", "phone_number"],
148
+ "required": ["subject", "phone_number"],
158
149
  "properties": {
159
150
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
160
151
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
@@ -162,32 +153,28 @@ class Migration(migrations.Migration):
162
153
  "subject": {"type": "string", "title": "Subject"},
163
154
  "description": {"type": "string", "title": "Description"},
164
155
  "related_to_type": {
165
- "type": "ActivityRelatedTo",
166
- "options": [
167
- {"key": "CONTACT", "value": "Contact"},
168
- {"key": "ACCOUNT", "value": "Account"},
169
- {"key": "DEAL", "value": "Deal"},
170
- ],
171
- "title": "ActivityRelatedTo",
156
+ "type": "string",
157
+ "options": [{"key": "ENTITY", "value": "Entity"}, {"key": "DEAL", "value": "Deal"}],
158
+ "title": "Related To Type",
172
159
  "description": "What type of record this activity is related to.",
173
- "enum": ["Contact", "Account", "Deal"],
174
- "x_enum_names": ["CONTACT", "ACCOUNT", "DEAL"],
160
+ "enum": ["Entity", "Deal"],
161
+ "x_enum_names": ["ENTITY", "DEAL"],
175
162
  },
176
163
  "related_to_id": {"type": "string", "title": "Related To ID"},
177
- "owner_email": {"type": "string", "title": "Owner Email"},
178
164
  "due_date": {"type": "datetime", "title": "Due Date", "format": "date-time"},
179
165
  "completed_at": {"type": "datetime", "title": "Completed At", "format": "date-time"},
180
166
  "is_completed": {"type": "boolean", "default": False, "title": "Is Completed"},
167
+ "assigned_to": {"type": "User", "title": "Assigned To"},
181
168
  "phone_number": {"type": "string", "title": "Phone Number"},
182
169
  "duration_seconds": {"type": "integer", "title": "Duration (seconds)"},
183
170
  "call_outcome": {"type": "string", "title": "Call Outcome"},
184
171
  },
185
- "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
172
+ "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
186
173
  "storage_metadata": {
187
174
  "table_name": "Call",
188
175
  "db_fields": {},
189
176
  "primary_key": ["partition_key"],
190
- "indexed": [["related_to_id"], ["owner_email"], ["created_at"], ["due_date"]],
177
+ "indexed": [["related_to_id"], ["created_at"], ["due_date"]],
191
178
  "foreign_keys": {},
192
179
  },
193
180
  "description": "Phone call activity.",
@@ -199,15 +186,7 @@ class Migration(migrations.Migration):
199
186
  new_schema={
200
187
  "title": "EmailActivity",
201
188
  "type": "Activity",
202
- "required": [
203
- "subject",
204
- "related_to_type",
205
- "related_to_id",
206
- "owner_email",
207
- "from_address",
208
- "to_addresses",
209
- "body",
210
- ],
189
+ "required": ["subject", "from_address", "to_addresses", "body"],
211
190
  "properties": {
212
191
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
213
192
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
@@ -215,34 +194,30 @@ class Migration(migrations.Migration):
215
194
  "subject": {"type": "string", "title": "Subject"},
216
195
  "description": {"type": "string", "title": "Description"},
217
196
  "related_to_type": {
218
- "type": "ActivityRelatedTo",
219
- "options": [
220
- {"key": "CONTACT", "value": "Contact"},
221
- {"key": "ACCOUNT", "value": "Account"},
222
- {"key": "DEAL", "value": "Deal"},
223
- ],
224
- "title": "ActivityRelatedTo",
197
+ "type": "string",
198
+ "options": [{"key": "ENTITY", "value": "Entity"}, {"key": "DEAL", "value": "Deal"}],
199
+ "title": "Related To Type",
225
200
  "description": "What type of record this activity is related to.",
226
- "enum": ["Contact", "Account", "Deal"],
227
- "x_enum_names": ["CONTACT", "ACCOUNT", "DEAL"],
201
+ "enum": ["Entity", "Deal"],
202
+ "x_enum_names": ["ENTITY", "DEAL"],
228
203
  },
229
204
  "related_to_id": {"type": "string", "title": "Related To ID"},
230
- "owner_email": {"type": "string", "title": "Owner Email"},
231
205
  "due_date": {"type": "datetime", "title": "Due Date", "format": "date-time"},
232
206
  "completed_at": {"type": "datetime", "title": "Completed At", "format": "date-time"},
233
207
  "is_completed": {"type": "boolean", "default": False, "title": "Is Completed"},
208
+ "assigned_to": {"type": "User", "title": "Assigned To"},
234
209
  "from_address": {"type": "string", "title": "From Address"},
235
210
  "to_addresses": {"type": "array", "items": {"type": "string"}, "title": "To Addresses"},
236
211
  "cc_addresses": {"type": "array", "items": {"type": "string"}, "title": "CC Addresses"},
237
212
  "body": {"type": "string", "title": "Email Body"},
238
213
  "is_outbound": {"type": "boolean", "default": True, "title": "Is Outbound"},
239
214
  },
240
- "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
215
+ "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
241
216
  "storage_metadata": {
242
217
  "table_name": "EmailActivity",
243
218
  "db_fields": {},
244
219
  "primary_key": ["partition_key"],
245
- "indexed": [["related_to_id"], ["owner_email"], ["created_at"], ["due_date"]],
220
+ "indexed": [["related_to_id"], ["created_at"], ["due_date"]],
246
221
  "foreign_keys": {},
247
222
  },
248
223
  "description": "Email activity with sender/recipients.",
@@ -254,7 +229,7 @@ class Migration(migrations.Migration):
254
229
  new_schema={
255
230
  "title": "Event",
256
231
  "type": "Activity",
257
- "required": ["subject", "related_to_type", "related_to_id", "owner_email", "start_time", "end_time"],
232
+ "required": ["subject", "start_time", "end_time"],
258
233
  "properties": {
259
234
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
260
235
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
@@ -262,32 +237,28 @@ class Migration(migrations.Migration):
262
237
  "subject": {"type": "string", "title": "Subject"},
263
238
  "description": {"type": "string", "title": "Description"},
264
239
  "related_to_type": {
265
- "type": "ActivityRelatedTo",
266
- "options": [
267
- {"key": "CONTACT", "value": "Contact"},
268
- {"key": "ACCOUNT", "value": "Account"},
269
- {"key": "DEAL", "value": "Deal"},
270
- ],
271
- "title": "ActivityRelatedTo",
240
+ "type": "string",
241
+ "options": [{"key": "ENTITY", "value": "Entity"}, {"key": "DEAL", "value": "Deal"}],
242
+ "title": "Related To Type",
272
243
  "description": "What type of record this activity is related to.",
273
- "enum": ["Contact", "Account", "Deal"],
274
- "x_enum_names": ["CONTACT", "ACCOUNT", "DEAL"],
244
+ "enum": ["Entity", "Deal"],
245
+ "x_enum_names": ["ENTITY", "DEAL"],
275
246
  },
276
247
  "related_to_id": {"type": "string", "title": "Related To ID"},
277
- "owner_email": {"type": "string", "title": "Owner Email"},
278
248
  "due_date": {"type": "datetime", "title": "Due Date", "format": "date-time"},
279
249
  "completed_at": {"type": "datetime", "title": "Completed At", "format": "date-time"},
280
250
  "is_completed": {"type": "boolean", "default": False, "title": "Is Completed"},
251
+ "assigned_to": {"type": "User", "title": "Assigned To"},
281
252
  "start_time": {"type": "datetime", "title": "Start Time", "format": "date-time"},
282
253
  "end_time": {"type": "datetime", "title": "End Time", "format": "date-time"},
283
254
  "location": {"type": "string", "title": "Location"},
284
255
  },
285
- "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
256
+ "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
286
257
  "storage_metadata": {
287
258
  "table_name": "Event",
288
259
  "db_fields": {},
289
260
  "primary_key": ["partition_key"],
290
- "indexed": [["related_to_id"], ["owner_email"], ["created_at"], ["due_date"]],
261
+ "indexed": [["related_to_id"], ["created_at"], ["due_date"]],
291
262
  "foreign_keys": {},
292
263
  },
293
264
  "description": "Event/meeting activity with start/end times.",
@@ -299,7 +270,7 @@ class Migration(migrations.Migration):
299
270
  new_schema={
300
271
  "title": "Note",
301
272
  "type": "Activity",
302
- "required": ["subject", "related_to_type", "related_to_id", "owner_email"],
273
+ "required": ["subject"],
303
274
  "properties": {
304
275
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
305
276
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
@@ -307,29 +278,25 @@ class Migration(migrations.Migration):
307
278
  "subject": {"type": "string", "title": "Subject"},
308
279
  "description": {"type": "string", "title": "Description"},
309
280
  "related_to_type": {
310
- "type": "ActivityRelatedTo",
311
- "options": [
312
- {"key": "CONTACT", "value": "Contact"},
313
- {"key": "ACCOUNT", "value": "Account"},
314
- {"key": "DEAL", "value": "Deal"},
315
- ],
316
- "title": "ActivityRelatedTo",
281
+ "type": "string",
282
+ "options": [{"key": "ENTITY", "value": "Entity"}, {"key": "DEAL", "value": "Deal"}],
283
+ "title": "Related To Type",
317
284
  "description": "What type of record this activity is related to.",
318
- "enum": ["Contact", "Account", "Deal"],
319
- "x_enum_names": ["CONTACT", "ACCOUNT", "DEAL"],
285
+ "enum": ["Entity", "Deal"],
286
+ "x_enum_names": ["ENTITY", "DEAL"],
320
287
  },
321
288
  "related_to_id": {"type": "string", "title": "Related To ID"},
322
- "owner_email": {"type": "string", "title": "Owner Email"},
323
289
  "due_date": {"type": "datetime", "title": "Due Date", "format": "date-time"},
324
290
  "completed_at": {"type": "datetime", "title": "Completed At", "format": "date-time"},
325
291
  "is_completed": {"type": "boolean", "default": False, "title": "Is Completed"},
292
+ "assigned_to": {"type": "User", "title": "Assigned To"},
326
293
  },
327
- "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
294
+ "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
328
295
  "storage_metadata": {
329
296
  "table_name": "Note",
330
297
  "db_fields": {},
331
298
  "primary_key": ["partition_key"],
332
- "indexed": [["related_to_id"], ["owner_email"], ["created_at"], ["due_date"]],
299
+ "indexed": [["related_to_id"], ["created_at"], ["due_date"]],
333
300
  "foreign_keys": {},
334
301
  },
335
302
  "description": "Simple note activity.",
@@ -341,7 +308,7 @@ class Migration(migrations.Migration):
341
308
  new_schema={
342
309
  "title": "Task",
343
310
  "type": "Activity",
344
- "required": ["subject", "related_to_type", "related_to_id", "owner_email"],
311
+ "required": ["subject"],
345
312
  "properties": {
346
313
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
347
314
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
@@ -349,22 +316,18 @@ class Migration(migrations.Migration):
349
316
  "subject": {"type": "string", "title": "Subject"},
350
317
  "description": {"type": "string", "title": "Description"},
351
318
  "related_to_type": {
352
- "type": "ActivityRelatedTo",
353
- "options": [
354
- {"key": "CONTACT", "value": "Contact"},
355
- {"key": "ACCOUNT", "value": "Account"},
356
- {"key": "DEAL", "value": "Deal"},
357
- ],
358
- "title": "ActivityRelatedTo",
319
+ "type": "string",
320
+ "options": [{"key": "ENTITY", "value": "Entity"}, {"key": "DEAL", "value": "Deal"}],
321
+ "title": "Related To Type",
359
322
  "description": "What type of record this activity is related to.",
360
- "enum": ["Contact", "Account", "Deal"],
361
- "x_enum_names": ["CONTACT", "ACCOUNT", "DEAL"],
323
+ "enum": ["Entity", "Deal"],
324
+ "x_enum_names": ["ENTITY", "DEAL"],
362
325
  },
363
326
  "related_to_id": {"type": "string", "title": "Related To ID"},
364
- "owner_email": {"type": "string", "title": "Owner Email"},
365
327
  "due_date": {"type": "datetime", "title": "Due Date", "format": "date-time"},
366
328
  "completed_at": {"type": "datetime", "title": "Completed At", "format": "date-time"},
367
329
  "is_completed": {"type": "boolean", "default": False, "title": "Is Completed"},
330
+ "assigned_to": {"type": "User", "title": "Assigned To"},
368
331
  "priority": {
369
332
  "type": "string",
370
333
  "default": "medium",
@@ -389,17 +352,109 @@ class Migration(migrations.Migration):
389
352
  "enum": ["not_started", "in_progress", "waiting", "completed"],
390
353
  },
391
354
  },
392
- "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
355
+ "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the activity.\"\"\"\n return f'{self.activity_type.value}: {self.subject}'\n\nasync def apre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = await self.aget_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this activity.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Activity' and permission.action in ('*', action):\n return True\n return False\n\ndef pre_create(self) -> None:\n self.created_at = _dt.datetime.now(tz=_dt.UTC)\n super().pre_create()\n\ndef pre_update(self) -> None:\n self.updated_at = _dt.datetime.now(tz=_dt.UTC)\n if not self.created_at:\n _metadata = self.get_metadata()\n self.created_at = _dt.datetime.fromtimestamp(_metadata.created_at / 1000, tz=_dt.UTC)\n super().pre_update()",
393
356
  "storage_metadata": {
394
357
  "table_name": "Task",
395
358
  "db_fields": {},
396
359
  "primary_key": ["partition_key"],
397
- "indexed": [["related_to_id"], ["owner_email"], ["created_at"], ["due_date"]],
360
+ "indexed": [["related_to_id"], ["created_at"], ["due_date"]],
398
361
  "foreign_keys": {},
399
362
  },
400
363
  "description": "Task activity with priority and status.",
401
364
  },
402
365
  ),
366
+ migrations.CreateClass(
367
+ module_type=ModuleType.CONTRIB,
368
+ class_name="EntityContactPoint",
369
+ new_schema={
370
+ "title": "EntityContactPoint",
371
+ "required": ["value", "entity"],
372
+ "properties": {
373
+ "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
374
+ "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
375
+ "value": {"type": "string", "title": "Contact Point Value"},
376
+ "is_primary": {"type": "boolean", "default": False, "title": "Is Primary"},
377
+ "can_contact": {"type": "boolean", "default": True, "title": "Can Contact"},
378
+ "entity": {
379
+ "type": "Entity",
380
+ "title": "Entity",
381
+ "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
382
+ },
383
+ },
384
+ "custom_code": 'async def apre_create(self) -> None:\n """Async hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityContactPoint\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityContactPoint\', self.custom_fields)\n await super().apre_update()\n\ndef pre_create(self) -> None:\n """Hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityContactPoint\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityContactPoint\', self.custom_fields)\n super().pre_update()',
385
+ "storage_metadata": {
386
+ "table_name": "EntityContactPoint",
387
+ "db_fields": {"entity": ["entity_partition_key"]},
388
+ "primary_key": ["partition_key"],
389
+ "foreign_keys": {"entity": [{"entity_partition_key": "string"}, "Entity", ["partition_key"]]},
390
+ },
391
+ },
392
+ ),
393
+ migrations.CreateClass(
394
+ module_type=ModuleType.CONTRIB,
395
+ class_name="EntityIdentifier",
396
+ new_schema={
397
+ "title": "EntityIdentifier",
398
+ "required": ["value", "entity"],
399
+ "properties": {
400
+ "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
401
+ "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
402
+ "value": {"type": "string", "title": "Identifier Value"},
403
+ "country": {"type": "string", "title": "Country"},
404
+ "is_primary": {"type": "boolean", "default": False, "title": "Is Primary"},
405
+ "entity": {
406
+ "type": "Entity",
407
+ "title": "Entity",
408
+ "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
409
+ },
410
+ },
411
+ "custom_code": 'async def apre_create(self) -> None:\n """Async hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityIdentifier\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityIdentifier\', self.custom_fields)\n await super().apre_update()\n\ndef pre_create(self) -> None:\n """Hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityIdentifier\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityIdentifier\', self.custom_fields)\n super().pre_update()',
412
+ "storage_metadata": {
413
+ "table_name": "EntityIdentifier",
414
+ "db_fields": {"entity": ["entity_partition_key"]},
415
+ "primary_key": ["partition_key"],
416
+ "foreign_keys": {"entity": [{"entity_partition_key": "string"}, "Entity", ["partition_key"]]},
417
+ },
418
+ },
419
+ ),
420
+ migrations.CreateClass(
421
+ module_type=ModuleType.CONTRIB,
422
+ class_name="EntityRelationship",
423
+ new_schema={
424
+ "title": "EntityRelationship",
425
+ "required": ["from_entity", "to_entity"],
426
+ "properties": {
427
+ "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
428
+ "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
429
+ "start_date": {"type": "string", "title": "Start Date"},
430
+ "end_date": {"type": "string", "title": "End Date"},
431
+ "relationship_group_name": {"type": "string", "title": "Relationship Group Name"},
432
+ "from_entity": {
433
+ "type": "Entity",
434
+ "title": "From Entity",
435
+ "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
436
+ },
437
+ "to_entity": {
438
+ "type": "Entity",
439
+ "title": "To Entity",
440
+ "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
441
+ },
442
+ },
443
+ "custom_code": 'async def apre_create(self) -> None:\n """Async hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityRelationship\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'EntityRelationship\', self.custom_fields)\n await super().apre_update()\n\ndef pre_create(self) -> None:\n """Hook called before creating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityRelationship\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating account."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'EntityRelationship\', self.custom_fields)\n super().pre_update()',
444
+ "storage_metadata": {
445
+ "table_name": "EntityRelationship",
446
+ "db_fields": {
447
+ "from_entity": ["from_entity_partition_key"],
448
+ "to_entity": ["to_entity_partition_key"],
449
+ },
450
+ "primary_key": ["partition_key"],
451
+ "foreign_keys": {
452
+ "from_entity": [{"from_entity_partition_key": "string"}, "Entity", ["partition_key"]],
453
+ "to_entity": [{"to_entity_partition_key": "string"}, "Entity", ["partition_key"]],
454
+ },
455
+ },
456
+ },
457
+ ),
403
458
  migrations.CreateClass(
404
459
  module_type=ModuleType.CONTRIB,
405
460
  class_name="Stage",
@@ -413,6 +468,7 @@ class Migration(migrations.Migration):
413
468
  "description": "Sales pipeline model.\n\nRepresents a sales pipeline with multiple stages.\nPipelines are system-wide and not owned by individual users.",
414
469
  },
415
470
  "name": {"type": "string", "title": "Stage Name"},
471
+ "description": {"type": "string", "title": "Description"},
416
472
  "order": {"type": "integer", "title": "Order"},
417
473
  "probability": {
418
474
  "type": "number",
@@ -421,8 +477,17 @@ class Migration(migrations.Migration):
421
477
  "maximum": 100,
422
478
  "minimum": 0,
423
479
  },
424
- "is_closed_won": {"type": "boolean", "default": False, "title": "Is Closed Won"},
425
- "is_closed_lost": {"type": "boolean", "default": False, "title": "Is Closed Lost"},
480
+ "status": {
481
+ "type": "string",
482
+ "default": "open",
483
+ "options": [
484
+ {"key": "open", "value": "open"},
485
+ {"key": "closed_won", "value": "closed_won"},
486
+ {"key": "closed_lost", "value": "closed_lost"},
487
+ ],
488
+ "title": "Status",
489
+ "enum": ["open", "closed_won", "closed_lost"],
490
+ },
426
491
  },
427
492
  "custom_code": '@property\ndef display_name(self) -> str:\n """Return display name for the stage."""\n if isinstance(self.pipeline, str):\n return f\'{self.pipeline} - {self.name}\'\n return f\'{self.pipeline.display_name} - {self.name}\'',
428
493
  "storage_metadata": {
@@ -440,48 +505,52 @@ class Migration(migrations.Migration):
440
505
  class_name="Deal",
441
506
  new_schema={
442
507
  "title": "Deal",
443
- "required": ["name", "stage", "owner_email"],
508
+ "required": ["name", "entity", "stage"],
444
509
  "properties": {
445
510
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
446
511
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
447
512
  "name": {"type": "string", "title": "Deal Name"},
448
513
  "amount": {"type": "number", "title": "Amount"},
449
514
  "currency": {"type": "string", "default": "USD", "title": "Currency"},
450
- "account": {
451
- "type": "Account",
452
- "title": "Account",
453
- "description": "Account (Company/Organization) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
454
- },
455
- "contact": {
456
- "type": "Contact",
457
- "title": "Primary Contact",
458
- "description": "Contact (Person) model.\n\nRepresents a person in the CRM system, optionally linked to an Account.\nOwned by individual users with permission controls.",
515
+ "entity": {
516
+ "type": "Entity",
517
+ "title": "Entity",
518
+ "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
459
519
  },
460
520
  "stage": {
461
521
  "type": "Stage",
462
522
  "title": "Stage",
463
523
  "description": "Pipeline stage model.\n\nRepresents a stage within a sales pipeline with win probability\nand closed status indicators.",
464
524
  },
465
- "owner_email": {"type": "string", "title": "Owner Email"},
466
525
  "expected_close_date": {"type": "datetime", "title": "Expected Close Date", "format": "date-time"},
467
526
  "closed_date": {"type": "datetime", "title": "Closed Date", "format": "date-time"},
468
- "is_closed": {"type": "boolean", "default": False, "title": "Is Closed"},
469
- "is_won": {"type": "boolean", "default": False, "title": "Is Won"},
527
+ "status": {
528
+ "type": "string",
529
+ "default": "open",
530
+ "options": [
531
+ {"key": "open", "value": "open"},
532
+ {"key": "closed_won", "value": "closed_won"},
533
+ {"key": "closed_lost", "value": "closed_lost"},
534
+ ],
535
+ "title": "Status",
536
+ "enum": ["open", "closed_won", "closed_lost"],
537
+ },
470
538
  "custom_fields": {"type": "anything", "title": "Custom Fields"},
539
+ "assigned_to": {"type": "User", "title": "Assigned To"},
471
540
  },
472
- "custom_code": 'import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\nfrom amsdal_utils.models.data_models.reference import Reference\n\n\n@property\ndef display_name(self) -> str:\n """Return display name for the deal."""\n return self.name\n\n@property\ndef stage_name(self) -> str:\n """Returns stage name for display."""\n if hasattr(self.stage, \'name\'):\n return self.stage.name\n return str(self.stage)\n\nasync def apost_update(self) -> None:\n """Async hook called after updating deal."""\n from amsdal_crm.services.workflow_service import WorkflowService\n await WorkflowService.aexecute_rules(\'Deal\', \'update\', self)\n\nasync def apre_create(self) -> None:\n """Async hook called before creating deal."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Deal\', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n """Async hook called before updating deal.\n\n Automatically syncs is_closed and is_won status with stage,\n and sets closed_date when deal is closed.\n """\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(\'Deal\', self.custom_fields)\n stage = await self.stage\n self.is_closed = stage.is_closed_won or stage.is_closed_lost\n self.is_won = stage.is_closed_won\n if self.is_closed and (not self.closed_date):\n self.closed_date = _dt.datetime.now(_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: \'User\', action: str) -> bool:\n """Check if user has permission to perform action on this deal.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n """\n if self.owner_email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == \'*\' and permission.action in (\'*\', action):\n return True\n if permission.model == \'Deal\' and permission.action in (\'*\', action):\n return True\n return False\n\ndef post_update(self) -> None:\n """Hook called after updating deal."""\n from amsdal_crm.services.workflow_service import WorkflowService\n WorkflowService.execute_rules(\'Deal\', \'update\', self)\n\ndef pre_create(self) -> None:\n """Hook called before creating deal."""\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Deal\', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n """Hook called before updating deal.\n\n Automatically syncs is_closed and is_won status with stage,\n and sets closed_date when deal is closed.\n """\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(\'Deal\', self.custom_fields)\n from amsdal_models.classes.helpers.reference_loader import ReferenceLoader\n stage = ReferenceLoader(self.stage).load_reference() if isinstance(self.stage, Reference) else self.stage\n self.is_closed = stage.is_closed_won or stage.is_closed_lost\n self.is_won = stage.is_closed_won\n if self.is_closed and (not self.closed_date):\n self.closed_date = _dt.datetime.now(_dt.UTC)\n super().pre_update()',
541
+ "custom_code": "import datetime as _dt\n\nfrom amsdal.contrib.auth.models.user import User\nfrom amsdal_utils.models.data_models.reference import Reference\n\n\n@property\ndef display_name(self) -> str:\n \"\"\"Return display name for the deal.\"\"\"\n return self.name\n\n@property\ndef stage_name(self) -> str:\n \"\"\"Returns stage name for display.\"\"\"\n if hasattr(self.stage, 'name'):\n return self.stage.name\n return str(self.stage)\n\nasync def apost_update(self) -> None:\n \"\"\"Async hook called after updating deal.\"\"\"\n from amsdal_crm.services.workflow_service import WorkflowService\n await WorkflowService.aexecute_rules('Deal', 'update', self)\n\nasync def apre_create(self) -> None:\n \"\"\"Async hook called before creating deal.\"\"\"\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields('Deal', self.custom_fields)\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n \"\"\"Async hook called before updating deal.\n\n Automatically syncs is_closed and is_won status with stage,\n and sets closed_date when deal is closed.\n \"\"\"\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields('Deal', self.custom_fields)\n stage = await self.stage\n if stage.status == 'open':\n self.status = 'open'\n if stage.status == 'closed_won':\n self.status = 'closed_won'\n if stage.status == 'closed_lost':\n self.status = 'closed_lost'\n if self.status in ('closed_won', 'closed_lost') and (not self.closed_date):\n self.closed_date = _dt.datetime.now(_dt.UTC)\n await super().apre_update()\n\ndef has_object_permission(self, user: 'User', action: str) -> bool:\n \"\"\"Check if user has permission to perform action on this deal.\n\n Args:\n user: The user attempting the action\n action: The action being attempted (read, create, update, delete)\n\n Returns:\n True if user has permission, False otherwise\n \"\"\"\n if self.assigned_to and self.assigned_to.email == user.email:\n return True\n if user.permissions:\n for permission in user.permissions:\n if permission.model == '*' and permission.action in ('*', action):\n return True\n if permission.model == 'Deal' and permission.action in ('*', action):\n return True\n return False\n\ndef post_update(self) -> None:\n \"\"\"Hook called after updating deal.\"\"\"\n from amsdal_crm.services.workflow_service import WorkflowService\n WorkflowService.execute_rules('Deal', 'update', self)\n\ndef pre_create(self) -> None:\n \"\"\"Hook called before creating deal.\"\"\"\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields('Deal', self.custom_fields)\n super().pre_create()\n\ndef pre_update(self) -> None:\n \"\"\"Hook called before updating deal.\n\n Automatically syncs is_closed and is_won status with stage,\n and sets closed_date when deal is closed.\n \"\"\"\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields('Deal', self.custom_fields)\n from amsdal_models.classes.helpers.reference_loader import ReferenceLoader\n stage = ReferenceLoader(self.stage).load_reference() if isinstance(self.stage, Reference) else self.stage\n if stage.status == 'open':\n self.status = 'open'\n if stage.status == 'closed_won':\n self.status = 'closed_won'\n if stage.status == 'closed_lost':\n self.status = 'closed_lost'\n if self.status in ('closed_won', 'closed_lost') and (not self.closed_date):\n self.closed_date = _dt.datetime.now(_dt.UTC)\n super().pre_update()",
473
542
  "storage_metadata": {
474
543
  "table_name": "Deal",
475
544
  "db_fields": {
476
- "account": ["account_partition_key"],
477
- "contact": ["contact_partition_key"],
545
+ "entity": ["entity_partition_key"],
478
546
  "stage": ["stage_partition_key"],
547
+ "assigned_to": ["assigned_to_partition_key"],
479
548
  },
480
549
  "primary_key": ["partition_key"],
481
- "indexed": [["owner_email"], ["expected_close_date"], ["created_at"]],
550
+ "indexed": [["expected_close_date"], ["created_at"]],
482
551
  "foreign_keys": {
483
- "account": [{"account_partition_key": "string"}, "Account", ["partition_key"]],
484
- "contact": [{"contact_partition_key": "string"}, "Contact", ["partition_key"]],
552
+ "assigned_to": [{"assigned_to_partition_key": "string"}, "User", ["partition_key"]],
553
+ "entity": [{"entity_partition_key": "string"}, "Entity", ["partition_key"]],
485
554
  "stage": [{"stage_partition_key": "string"}, "Stage", ["partition_key"]],
486
555
  },
487
556
  },
@@ -498,13 +567,12 @@ class Migration(migrations.Migration):
498
567
  "related_to_type": {
499
568
  "type": "string",
500
569
  "options": [
501
- {"key": "Contact", "value": "Contact"},
502
- {"key": "Account", "value": "Account"},
570
+ {"key": "Entity", "value": "Entity"},
503
571
  {"key": "Deal", "value": "Deal"},
504
572
  {"key": "Activity", "value": "Activity"},
505
573
  ],
506
574
  "title": "Related To Type",
507
- "enum": ["Contact", "Account", "Deal", "Activity"],
575
+ "enum": ["Entity", "Deal", "Activity"],
508
576
  },
509
577
  "related_to_id": {"type": "string", "title": "Related To ID"},
510
578
  "uploaded_by": {"type": "string", "title": "Uploaded By (User Email)"},
@@ -533,12 +601,22 @@ class Migration(migrations.Migration):
533
601
  "entity_type": {
534
602
  "type": "string",
535
603
  "options": [
536
- {"key": "Contact", "value": "Contact"},
537
- {"key": "Account", "value": "Account"},
604
+ {"key": "Entity", "value": "Entity"},
605
+ {"key": "EntityRelationship", "value": "EntityRelationship"},
538
606
  {"key": "Deal", "value": "Deal"},
607
+ {"key": "EntityIdentifier", "value": "EntityIdentifier"},
608
+ {"key": "EntityContactPoint", "value": "EntityContactPoint"},
609
+ {"key": "EntityAddress", "value": "EntityAddress"},
539
610
  ],
540
611
  "title": "Entity Type",
541
- "enum": ["Contact", "Account", "Deal"],
612
+ "enum": [
613
+ "Entity",
614
+ "EntityRelationship",
615
+ "Deal",
616
+ "EntityIdentifier",
617
+ "EntityContactPoint",
618
+ "EntityAddress",
619
+ ],
542
620
  },
543
621
  "field_name": {"type": "string", "title": "Field Name"},
544
622
  "field_label": {"type": "string", "title": "Field Label"},
@@ -581,13 +659,12 @@ class Migration(migrations.Migration):
581
659
  "entity_type": {
582
660
  "type": "string",
583
661
  "options": [
584
- {"key": "Contact", "value": "Contact"},
585
- {"key": "Account", "value": "Account"},
662
+ {"key": "Entity", "value": "Entity"},
586
663
  {"key": "Deal", "value": "Deal"},
587
664
  {"key": "Activity", "value": "Activity"},
588
665
  ],
589
666
  "title": "Entity Type",
590
- "enum": ["Contact", "Account", "Deal", "Activity"],
667
+ "enum": ["Entity", "Deal", "Activity"],
591
668
  },
592
669
  "trigger_event": {
593
670
  "type": "string",