amsdal_crm 0.2.1__py3-none-any.whl → 0.2.3__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.
amsdal_crm/__about__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.2.1'
1
+ __version__ = '0.2.3'
@@ -65,6 +65,7 @@ class Migration(migrations.Migration):
65
65
  "properties": {
66
66
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
67
67
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
68
+ "custom_fields": {"type": "anything", "title": "Custom Fields"},
68
69
  "name": {"type": "string", "title": "Entity Name"},
69
70
  "legal_name": {"type": "string", "title": "Legal Name"},
70
71
  "status": {
@@ -75,10 +76,9 @@ class Migration(migrations.Migration):
75
76
  "enum": ["Active", "Inactive"],
76
77
  },
77
78
  "note": {"type": "string", "title": "Note"},
78
- "custom_fields": {"type": "anything", "title": "Custom Fields"},
79
79
  "assigned_to": {"type": "User", "title": "Assigned To"},
80
80
  },
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()',
81
+ "custom_code": 'from amsdal.contrib.auth.models.user import User\n\n\n@classmethod\ndef custom_fields_cell_template(cls) -> str:\n return \'JsonTemplate\'\n\n@property\ndef display_name(self) -> str:\n """Return display name for the account."""\n return self.name\n\nasync def _avalidate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(self.__class__.__name__, self.custom_fields)\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 await self._avalidate_custom_fields()\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_update()\n\ndef _validate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(self.__class__.__name__, self.custom_fields)\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 self._validate_custom_fields()\n super().pre_create()\n\ndef pre_update(self) -> None:\n self._validate_custom_fields()\n super().pre_update()',
82
82
  "storage_metadata": {
83
83
  "table_name": "Entity",
84
84
  "db_fields": {"assigned_to": ["assigned_to_partition_key"]},
@@ -100,6 +100,7 @@ class Migration(migrations.Migration):
100
100
  "properties": {
101
101
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
102
102
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
103
+ "custom_fields": {"type": "anything", "title": "Custom Fields"},
103
104
  "line1": {"type": "string", "title": "Address Line 1"},
104
105
  "line2": {"type": "string", "title": "Address Line 2"},
105
106
  "city": {"type": "string", "title": "City"},
@@ -108,7 +109,7 @@ class Migration(migrations.Migration):
108
109
  "country": {"type": "string", "title": "Country"},
109
110
  "is_primary": {"type": "boolean", "default": False, "title": "Is Primary"},
110
111
  },
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
+ "custom_code": "@classmethod\ndef custom_fields_cell_template(cls) -> str:\n return 'JsonTemplate'\n\nasync def _avalidate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(self.__class__.__name__, self.custom_fields)\n\nasync def apre_create(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_update()\n\ndef _validate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(self.__class__.__name__, self.custom_fields)\n\ndef pre_create(self) -> None:\n self._validate_custom_fields()\n super().pre_create()\n\ndef pre_update(self) -> None:\n self._validate_custom_fields()\n super().pre_update()",
112
113
  "storage_metadata": {
113
114
  "table_name": "EntityAddress",
114
115
  "db_fields": {},
@@ -372,6 +373,7 @@ class Migration(migrations.Migration):
372
373
  "properties": {
373
374
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
374
375
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
376
+ "custom_fields": {"type": "anything", "title": "Custom Fields"},
375
377
  "value": {"type": "string", "title": "Contact Point Value"},
376
378
  "is_primary": {"type": "boolean", "default": False, "title": "Is Primary"},
377
379
  "can_contact": {"type": "boolean", "default": True, "title": "Can Contact"},
@@ -381,7 +383,7 @@ class Migration(migrations.Migration):
381
383
  "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
382
384
  },
383
385
  },
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()',
386
+ "custom_code": "@classmethod\ndef custom_fields_cell_template(cls) -> str:\n return 'JsonTemplate'\n\nasync def _avalidate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(self.__class__.__name__, self.custom_fields)\n\nasync def apre_create(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_update()\n\ndef _validate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(self.__class__.__name__, self.custom_fields)\n\ndef pre_create(self) -> None:\n self._validate_custom_fields()\n super().pre_create()\n\ndef pre_update(self) -> None:\n self._validate_custom_fields()\n super().pre_update()",
385
387
  "storage_metadata": {
386
388
  "table_name": "EntityContactPoint",
387
389
  "db_fields": {"entity": ["entity_partition_key"]},
@@ -399,6 +401,7 @@ class Migration(migrations.Migration):
399
401
  "properties": {
400
402
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
401
403
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
404
+ "custom_fields": {"type": "anything", "title": "Custom Fields"},
402
405
  "value": {"type": "string", "title": "Identifier Value"},
403
406
  "country": {"type": "string", "title": "Country"},
404
407
  "is_primary": {"type": "boolean", "default": False, "title": "Is Primary"},
@@ -408,7 +411,7 @@ class Migration(migrations.Migration):
408
411
  "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
409
412
  },
410
413
  },
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()',
414
+ "custom_code": "@classmethod\ndef custom_fields_cell_template(cls) -> str:\n return 'JsonTemplate'\n\nasync def _avalidate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(self.__class__.__name__, self.custom_fields)\n\nasync def apre_create(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_update()\n\ndef _validate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(self.__class__.__name__, self.custom_fields)\n\ndef pre_create(self) -> None:\n self._validate_custom_fields()\n super().pre_create()\n\ndef pre_update(self) -> None:\n self._validate_custom_fields()\n super().pre_update()",
412
415
  "storage_metadata": {
413
416
  "table_name": "EntityIdentifier",
414
417
  "db_fields": {"entity": ["entity_partition_key"]},
@@ -426,6 +429,7 @@ class Migration(migrations.Migration):
426
429
  "properties": {
427
430
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
428
431
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
432
+ "custom_fields": {"type": "anything", "title": "Custom Fields"},
429
433
  "start_date": {"type": "string", "title": "Start Date"},
430
434
  "end_date": {"type": "string", "title": "End Date"},
431
435
  "relationship_group_name": {"type": "string", "title": "Relationship Group Name"},
@@ -440,7 +444,7 @@ class Migration(migrations.Migration):
440
444
  "description": "Entity (Person/Organization/Trust) model.\n\nRepresents a company or organization in the CRM system.\nOwned by individual users with permission controls.",
441
445
  },
442
446
  },
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()',
447
+ "custom_code": "@classmethod\ndef custom_fields_cell_template(cls) -> str:\n return 'JsonTemplate'\n\nasync def _avalidate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(self.__class__.__name__, self.custom_fields)\n\nasync def apre_create(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_create()\n\nasync def apre_update(self) -> None:\n await self._avalidate_custom_fields()\n await super().apre_update()\n\ndef _validate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(self.__class__.__name__, self.custom_fields)\n\ndef pre_create(self) -> None:\n self._validate_custom_fields()\n super().pre_create()\n\ndef pre_update(self) -> None:\n self._validate_custom_fields()\n super().pre_update()",
444
448
  "storage_metadata": {
445
449
  "table_name": "EntityRelationship",
446
450
  "db_fields": {
@@ -509,6 +513,7 @@ class Migration(migrations.Migration):
509
513
  "properties": {
510
514
  "created_at": {"type": "datetime", "title": "Created At", "format": "date-time"},
511
515
  "updated_at": {"type": "datetime", "title": "Updated At", "format": "date-time"},
516
+ "custom_fields": {"type": "anything", "title": "Custom Fields"},
512
517
  "name": {"type": "string", "title": "Deal Name"},
513
518
  "amount": {"type": "number", "title": "Amount"},
514
519
  "currency": {"type": "string", "default": "USD", "title": "Currency"},
@@ -535,10 +540,9 @@ class Migration(migrations.Migration):
535
540
  "title": "Status",
536
541
  "enum": ["open", "closed_won", "closed_lost"],
537
542
  },
538
- "custom_fields": {"type": "anything", "title": "Custom Fields"},
539
543
  "assigned_to": {"type": "User", "title": "Assigned To"},
540
544
  },
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()",
545
+ "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@classmethod\ndef custom_fields_cell_template(cls) -> str:\n return 'JsonTemplate'\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 _avalidate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = await CustomFieldService.avalidate_custom_fields(self.__class__.__name__, self.custom_fields)\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 await self._avalidate_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 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 _validate_custom_fields(self) -> None:\n if self.custom_fields:\n from amsdal_crm.services.custom_field_service import CustomFieldService\n self.custom_fields = CustomFieldService.validate_custom_fields(self.__class__.__name__, self.custom_fields)\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 self._validate_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 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()",
542
546
  "storage_metadata": {
543
547
  "table_name": "Deal",
544
548
  "db_fields": {
@@ -0,0 +1,45 @@
1
+ from typing import Any
2
+
3
+ from pydantic.fields import Field
4
+
5
+
6
+ class CustomFieldsMixin:
7
+ custom_fields: dict[str, Any] | None = Field(default=None, title='Custom Fields')
8
+
9
+ @classmethod
10
+ def custom_fields_cell_template(cls) -> str:
11
+ return 'JsonTemplate'
12
+
13
+ def pre_create(self) -> None:
14
+ self._validate_custom_fields()
15
+ super().pre_create()
16
+
17
+ async def apre_create(self) -> None:
18
+ await self._avalidate_custom_fields()
19
+ await super().apre_create()
20
+
21
+ def pre_update(self) -> None:
22
+ self._validate_custom_fields()
23
+ super().pre_update()
24
+
25
+ async def apre_update(self) -> None:
26
+ await self._avalidate_custom_fields()
27
+ await super().apre_update()
28
+
29
+ def _validate_custom_fields(self) -> None:
30
+ if self.custom_fields:
31
+ from amsdal_crm.services.custom_field_service import CustomFieldService
32
+
33
+ self.custom_fields = CustomFieldService.validate_custom_fields(
34
+ self.__class__.__name__,
35
+ self.custom_fields,
36
+ )
37
+
38
+ async def _avalidate_custom_fields(self) -> None:
39
+ if self.custom_fields:
40
+ from amsdal_crm.services.custom_field_service import CustomFieldService
41
+
42
+ self.custom_fields = await CustomFieldService.avalidate_custom_fields(
43
+ self.__class__.__name__,
44
+ self.custom_fields,
45
+ )
amsdal_crm/models/deal.py CHANGED
@@ -1,7 +1,6 @@
1
1
  """Deal Model."""
2
2
 
3
3
  import datetime as _dt
4
- from typing import Any
5
4
  from typing import ClassVar
6
5
  from typing import Literal
7
6
 
@@ -14,13 +13,15 @@ from amsdal_utils.models.data_models.reference import Reference
14
13
  from amsdal_utils.models.enums import ModuleType
15
14
  from pydantic.fields import Field
16
15
 
16
+ from models.common import CustomFieldsMixin
17
+
17
18
 
18
19
  class DealManager(Manager):
19
20
  def get_queryset(self) -> 'DealManager':
20
21
  return super().get_queryset().select_related('stage')
21
22
 
22
23
 
23
- class Deal(TimestampMixin, Model):
24
+ class Deal(CustomFieldsMixin, TimestampMixin, Model):
24
25
  """Deal (Sales Opportunity) model.
25
26
 
26
27
  Represents a sales opportunity linked to an account and contact,
@@ -50,9 +51,6 @@ class Deal(TimestampMixin, Model):
50
51
  # Status tracking
51
52
  status: Literal['open', 'closed_won', 'closed_lost'] = Field(default='open', title='Status')
52
53
 
53
- # Custom fields (JSON)
54
- custom_fields: dict[str, Any] | None = Field(default=None, title='Custom Fields')
55
-
56
54
  @property
57
55
  def display_name(self) -> str:
58
56
  """Return display name for the deal."""
@@ -90,34 +88,12 @@ class Deal(TimestampMixin, Model):
90
88
 
91
89
  return False
92
90
 
93
- def pre_create(self) -> None:
94
- """Hook called before creating deal."""
95
- if self.custom_fields:
96
- from amsdal_crm.services.custom_field_service import CustomFieldService
97
-
98
- self.custom_fields = CustomFieldService.validate_custom_fields('Deal', self.custom_fields)
99
- super().pre_create()
100
-
101
- async def apre_create(self) -> None:
102
- """Async hook called before creating deal."""
103
- if self.custom_fields:
104
- from amsdal_crm.services.custom_field_service import CustomFieldService
105
-
106
- self.custom_fields = await CustomFieldService.avalidate_custom_fields('Deal', self.custom_fields)
107
- await super().apre_create()
108
-
109
91
  def pre_update(self) -> None:
110
92
  """Hook called before updating deal.
111
93
 
112
94
  Automatically syncs is_closed and is_won status with stage,
113
95
  and sets closed_date when deal is closed.
114
96
  """
115
- # Validate custom fields first
116
- if self.custom_fields:
117
- from amsdal_crm.services.custom_field_service import CustomFieldService
118
-
119
- self.custom_fields = CustomFieldService.validate_custom_fields('Deal', self.custom_fields)
120
-
121
97
  # Load stage if it's a reference and sync closed status
122
98
  from amsdal_models.classes.helpers.reference_loader import ReferenceLoader
123
99
 
@@ -142,14 +118,7 @@ class Deal(TimestampMixin, Model):
142
118
 
143
119
  Automatically syncs is_closed and is_won status with stage,
144
120
  and sets closed_date when deal is closed.
145
- """
146
- # Validate custom fields first
147
- if self.custom_fields:
148
- from amsdal_crm.services.custom_field_service import CustomFieldService
149
-
150
- self.custom_fields = await CustomFieldService.avalidate_custom_fields('Deal', self.custom_fields)
151
-
152
- # Load stage if it's a reference and sync closed status
121
+ """ # Load stage if it's a reference and sync closed status
153
122
 
154
123
  stage = await self.stage
155
124
  if stage.status == 'open':
@@ -1,6 +1,5 @@
1
1
  """Account Model."""
2
2
 
3
- from typing import Any
4
3
  from typing import ClassVar
5
4
  from typing import Literal
6
5
 
@@ -12,8 +11,10 @@ from amsdal_models.classes.model import Model
12
11
  from amsdal_utils.models.enums import ModuleType
13
12
  from pydantic.fields import Field
14
13
 
14
+ from models.common import CustomFieldsMixin
15
15
 
16
- class Entity(TimestampMixin, Model):
16
+
17
+ class Entity(CustomFieldsMixin, TimestampMixin, Model):
17
18
  """Entity (Person/Organization/Trust) model.
18
19
 
19
20
  Represents a company or organization in the CRM system.
@@ -34,9 +35,6 @@ class Entity(TimestampMixin, Model):
34
35
 
35
36
  assigned_to: User | None = Field(default=None, title='Assigned To')
36
37
 
37
- # Custom fields (JSON)
38
- custom_fields: dict[str, Any] | None = Field(default=None, title='Custom Fields')
39
-
40
38
  @property
41
39
  def display_name(self) -> str:
42
40
  """Return display name for the account."""
@@ -65,44 +63,6 @@ class Entity(TimestampMixin, Model):
65
63
 
66
64
  return False
67
65
 
68
- def pre_create(self) -> None:
69
- """Hook called before creating account."""
70
- if self.custom_fields:
71
- from amsdal_crm.services.custom_field_service import CustomFieldService
72
-
73
- self.custom_fields = CustomFieldService.validate_custom_fields('Entity', self.custom_fields)
74
- super().pre_create()
75
-
76
- async def apre_create(self) -> None:
77
- """Async hook called before creating account."""
78
- if self.custom_fields:
79
- from amsdal_crm.services.custom_field_service import CustomFieldService
80
-
81
- self.custom_fields = await CustomFieldService.avalidate_custom_fields('Entity', self.custom_fields)
82
- await super().apre_create()
83
-
84
- def pre_update(self) -> None:
85
- """Hook called before updating account."""
86
- # Validate custom fields first
87
- if self.custom_fields:
88
- from amsdal_crm.services.custom_field_service import CustomFieldService
89
-
90
- self.custom_fields = CustomFieldService.validate_custom_fields('Entity', self.custom_fields)
91
-
92
- # Call parent to handle timestamps
93
- super().pre_update()
94
-
95
- async def apre_update(self) -> None:
96
- """Async hook called before updating account."""
97
- # Validate custom fields first
98
- if self.custom_fields:
99
- from amsdal_crm.services.custom_field_service import CustomFieldService
100
-
101
- self.custom_fields = await CustomFieldService.avalidate_custom_fields('Entity', self.custom_fields)
102
-
103
- # Call parent to handle timestamps
104
- await super().apre_update()
105
-
106
66
  def post_update(self) -> None:
107
67
  """Hook called after updating account."""
108
68
  from amsdal_crm.services.workflow_service import WorkflowService
@@ -116,7 +76,7 @@ class Entity(TimestampMixin, Model):
116
76
  await WorkflowService.aexecute_rules('Entity', 'update', self)
117
77
 
118
78
 
119
- class EntityRelationship(TimestampMixin, Model):
79
+ class EntityRelationship(CustomFieldsMixin, TimestampMixin, Model):
120
80
  __module_type__: ClassVar[ModuleType] = ModuleType.CONTRIB
121
81
 
122
82
  from_entity: Entity = Field(title='From Entity')
@@ -125,50 +85,8 @@ class EntityRelationship(TimestampMixin, Model):
125
85
  end_date: str | None = Field(default=None, title='End Date')
126
86
  relationship_group_name: str | None = Field(default=None, title='Relationship Group Name')
127
87
 
128
- def pre_create(self) -> None:
129
- """Hook called before creating account."""
130
- if self.custom_fields:
131
- from amsdal_crm.services.custom_field_service import CustomFieldService
132
-
133
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityRelationship', self.custom_fields)
134
- super().pre_create()
135
88
 
136
- async def apre_create(self) -> None:
137
- """Async hook called before creating account."""
138
- if self.custom_fields:
139
- from amsdal_crm.services.custom_field_service import CustomFieldService
140
-
141
- self.custom_fields = await CustomFieldService.avalidate_custom_fields(
142
- 'EntityRelationship', self.custom_fields
143
- )
144
- await super().apre_create()
145
-
146
- def pre_update(self) -> None:
147
- """Hook called before updating account."""
148
- # Validate custom fields first
149
- if self.custom_fields:
150
- from amsdal_crm.services.custom_field_service import CustomFieldService
151
-
152
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityRelationship', self.custom_fields)
153
-
154
- # Call parent to handle timestamps
155
- super().pre_update()
156
-
157
- async def apre_update(self) -> None:
158
- """Async hook called before updating account."""
159
- # Validate custom fields first
160
- if self.custom_fields:
161
- from amsdal_crm.services.custom_field_service import CustomFieldService
162
-
163
- self.custom_fields = await CustomFieldService.avalidate_custom_fields(
164
- 'EntityRelationship', self.custom_fields
165
- )
166
-
167
- # Call parent to handle timestamps
168
- await super().apre_update()
169
-
170
-
171
- class EntityIdentifier(TimestampMixin, Model):
89
+ class EntityIdentifier(CustomFieldsMixin, TimestampMixin, Model):
172
90
  __module_type__: ClassVar[ModuleType] = ModuleType.CONTRIB
173
91
 
174
92
  entity: Entity = Field(title='Entity')
@@ -178,50 +96,8 @@ class EntityIdentifier(TimestampMixin, Model):
178
96
  # TODO: validate one per entity
179
97
  is_primary: bool = Field(default=False, title='Is Primary')
180
98
 
181
- def pre_create(self) -> None:
182
- """Hook called before creating account."""
183
- if self.custom_fields:
184
- from amsdal_crm.services.custom_field_service import CustomFieldService
185
-
186
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityIdentifier', self.custom_fields)
187
- super().pre_create()
188
-
189
- async def apre_create(self) -> None:
190
- """Async hook called before creating account."""
191
- if self.custom_fields:
192
- from amsdal_crm.services.custom_field_service import CustomFieldService
193
-
194
- self.custom_fields = await CustomFieldService.avalidate_custom_fields(
195
- 'EntityIdentifier', self.custom_fields
196
- )
197
- await super().apre_create()
198
-
199
- def pre_update(self) -> None:
200
- """Hook called before updating account."""
201
- # Validate custom fields first
202
- if self.custom_fields:
203
- from amsdal_crm.services.custom_field_service import CustomFieldService
204
99
 
205
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityIdentifier', self.custom_fields)
206
-
207
- # Call parent to handle timestamps
208
- super().pre_update()
209
-
210
- async def apre_update(self) -> None:
211
- """Async hook called before updating account."""
212
- # Validate custom fields first
213
- if self.custom_fields:
214
- from amsdal_crm.services.custom_field_service import CustomFieldService
215
-
216
- self.custom_fields = await CustomFieldService.avalidate_custom_fields(
217
- 'EntityIdentifier', self.custom_fields
218
- )
219
-
220
- # Call parent to handle timestamps
221
- await super().apre_update()
222
-
223
-
224
- class EntityContactPoint(TimestampMixin, Model):
100
+ class EntityContactPoint(CustomFieldsMixin, TimestampMixin, Model):
225
101
  __module_type__: ClassVar[ModuleType] = ModuleType.CONTRIB
226
102
 
227
103
  entity: Entity = Field(title='Entity')
@@ -231,50 +107,8 @@ class EntityContactPoint(TimestampMixin, Model):
231
107
  is_primary: bool = Field(default=False, title='Is Primary')
232
108
  can_contact: bool = Field(default=True, title='Can Contact')
233
109
 
234
- def pre_create(self) -> None:
235
- """Hook called before creating account."""
236
- if self.custom_fields:
237
- from amsdal_crm.services.custom_field_service import CustomFieldService
238
-
239
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityContactPoint', self.custom_fields)
240
- super().pre_create()
241
-
242
- async def apre_create(self) -> None:
243
- """Async hook called before creating account."""
244
- if self.custom_fields:
245
- from amsdal_crm.services.custom_field_service import CustomFieldService
246
-
247
- self.custom_fields = await CustomFieldService.avalidate_custom_fields(
248
- 'EntityContactPoint', self.custom_fields
249
- )
250
- await super().apre_create()
251
-
252
- def pre_update(self) -> None:
253
- """Hook called before updating account."""
254
- # Validate custom fields first
255
- if self.custom_fields:
256
- from amsdal_crm.services.custom_field_service import CustomFieldService
257
-
258
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityContactPoint', self.custom_fields)
259
-
260
- # Call parent to handle timestamps
261
- super().pre_update()
262
-
263
- async def apre_update(self) -> None:
264
- """Async hook called before updating account."""
265
- # Validate custom fields first
266
- if self.custom_fields:
267
- from amsdal_crm.services.custom_field_service import CustomFieldService
268
110
 
269
- self.custom_fields = await CustomFieldService.avalidate_custom_fields(
270
- 'EntityContactPoint', self.custom_fields
271
- )
272
-
273
- # Call parent to handle timestamps
274
- await super().apre_update()
275
-
276
-
277
- class EntityAddress(TimestampMixin, Model):
111
+ class EntityAddress(CustomFieldsMixin, TimestampMixin, Model):
278
112
  __module_type__: ClassVar[ModuleType] = ModuleType.CONTRIB
279
113
 
280
114
  line1: str | None = Field(title='Address Line 1')
@@ -286,41 +120,3 @@ class EntityAddress(TimestampMixin, Model):
286
120
 
287
121
  # TODO: validate one per entity
288
122
  is_primary: bool = Field(default=False, title='Is Primary')
289
-
290
- def pre_create(self) -> None:
291
- """Hook called before creating account."""
292
- if self.custom_fields:
293
- from amsdal_crm.services.custom_field_service import CustomFieldService
294
-
295
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityAddress', self.custom_fields)
296
- super().pre_create()
297
-
298
- async def apre_create(self) -> None:
299
- """Async hook called before creating account."""
300
- if self.custom_fields:
301
- from amsdal_crm.services.custom_field_service import CustomFieldService
302
-
303
- self.custom_fields = await CustomFieldService.avalidate_custom_fields('EntityAddress', self.custom_fields)
304
- await super().apre_create()
305
-
306
- def pre_update(self) -> None:
307
- """Hook called before updating account."""
308
- # Validate custom fields first
309
- if self.custom_fields:
310
- from amsdal_crm.services.custom_field_service import CustomFieldService
311
-
312
- self.custom_fields = CustomFieldService.validate_custom_fields('EntityAddress', self.custom_fields)
313
-
314
- # Call parent to handle timestamps
315
- super().pre_update()
316
-
317
- async def apre_update(self) -> None:
318
- """Async hook called before updating account."""
319
- # Validate custom fields first
320
- if self.custom_fields:
321
- from amsdal_crm.services.custom_field_service import CustomFieldService
322
-
323
- self.custom_fields = await CustomFieldService.avalidate_custom_fields('EntityAddress', self.custom_fields)
324
-
325
- # Call parent to handle timestamps
326
- await super().apre_update()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amsdal_crm
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: amsdal-crm plugin for AMSDAL Framework
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: aiohttp==3.12.15
@@ -1,5 +1,5 @@
1
1
  amsdal_crm/Third-Party Materials - AMSDAL Dependencies - License Notices.md,sha256=ML7PqsHrTMNNZn8E_rA-LzDCAafMSxMcrmSg8YOi-wo,113896
2
- amsdal_crm/__about__.py,sha256=PmcQ2PI2oP8irnLtJLJby2YfW6sBvLAmL-VpABzTqwc,22
2
+ amsdal_crm/__about__.py,sha256=XtWUl6HPylv5jZLd2KkgtPptuzuda93kC2REmOrF-Cs,22
3
3
  amsdal_crm/__init__.py,sha256=b4wxJYesA5Ctk1IrAvlw64i_0EU3SiK1Tw6sUYakd18,303
4
4
  amsdal_crm/app.py,sha256=JLvueh_2KURQLDWMQlq4Z6gAFsqBDTRf6pK095ehp14,1373
5
5
  amsdal_crm/constants.py,sha256=5Ga7q9zEKcQZnAoKv_SE_7w8WxvhPFkM9gY9NruOEaA,347
@@ -10,13 +10,14 @@ amsdal_crm/fixtures/permissions.py,sha256=TQEY_zX60GSudSZqmonaYqWCaod6oBsQw1NQPu
10
10
  amsdal_crm/fixtures/pipelines.py,sha256=ZCLmgrA700Sl7Oy7l4IQ8FbIbC1378OkcJTrZe5701o,2064
11
11
  amsdal_crm/lifecycle/__init__.py,sha256=B8nw19lEIr7U15Lnu6jh7yzZwF9LWWh4-p3X63sAicQ,31
12
12
  amsdal_crm/lifecycle/consumer.py,sha256=owS9kXKPs3Lzy7RiN_jI6hHEO6c90Tf-DT6aNqQ3uj4,8893
13
- amsdal_crm/migrations/0000_initial.py,sha256=nWiMoc1S_--qXIW0WXwbFjKS3S6klfHgS4MYseVsuGE,63474
13
+ amsdal_crm/migrations/0000_initial.py,sha256=Jf7iw4wOzwM9DIFfNGZoaaB_AsmUnRz8OB8iy7skUqM,62079
14
14
  amsdal_crm/models/__init__.py,sha256=3c-gqBkw2PXg4rp_wH2EN6viRu7Ff926Etn_BkQa5RU,18
15
15
  amsdal_crm/models/activity.py,sha256=Hwy_Z_zhk96yopTcm4vU4-SZbPg152r7N9er96Jkdho,4732
16
16
  amsdal_crm/models/attachment.py,sha256=B-6IuPrF6-VDnsFz_Q4UCh09YcFMs4o4zyT4VpvLe3U,1513
17
+ amsdal_crm/models/common.py,sha256=cKArZNNO7hsXrd9s4GF36nqZbsLkVldHovcCH5cJ-aM,1398
17
18
  amsdal_crm/models/custom_field_definition.py,sha256=0IIWcetZ0vXT8lPRGahyt7phoNKuCq2MQO6C8wEGk4Q,1749
18
- amsdal_crm/models/deal.py,sha256=xmJOCE_fvY6asnOp2_eeWLW3clSiS-MoV9Bm-1gtEF0,6583
19
- amsdal_crm/models/entity.py,sha256=VTE7inPvViGZbJi6vTKB9O-O19TCyK7ub0zF_wpSdyg,12824
19
+ amsdal_crm/models/deal.py,sha256=zAzDXl-9KroniU9XN42qrBu3fRNE-WFDoc95BBpfHL0,5289
20
+ amsdal_crm/models/entity.py,sha256=zNS9gxHgzXXWABfG5G8gzjl1sLdP4W_h3BAlA2cKYmE,4631
20
21
  amsdal_crm/models/pipeline.py,sha256=DXJh5MbCCRctEHhDfxef5RxFWSKN0D4v6UK75q5ssL8,925
21
22
  amsdal_crm/models/stage.py,sha256=57rhfA4Oib8DKjDifWQe3EFxh-Auww3_PgOPfj4-L0E,1666
22
23
  amsdal_crm/models/workflow_rule.py,sha256=g-vAdJHKyA1PsiB3vPhd474Uvq94USHz35W1c5XyrAQ,1535
@@ -26,6 +27,6 @@ amsdal_crm/services/custom_field_service.py,sha256=r2dr2gijTbi9r56XV64bSArx0jTGw
26
27
  amsdal_crm/services/deal_service.py,sha256=PTxCQy6YqMyrtL4sRhyCU09V6OemrmZ405sQk-eBUlA,4803
27
28
  amsdal_crm/services/email_service.py,sha256=L9o-WaOET0tjo9g-zzC8GGCtfDZVheziHM_ob10jO2M,3518
28
29
  amsdal_crm/services/workflow_service.py,sha256=7m_vbk9FL_FCy9KS9LM4ueztO9ZsLwbvubdo9NA345g,7020
29
- amsdal_crm-0.2.1.dist-info/METADATA,sha256=VULaunY-6kM7g_jGLMyUWJK3iy36pjkpk-BLYo6vcN4,1596
30
- amsdal_crm-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
31
- amsdal_crm-0.2.1.dist-info/RECORD,,
30
+ amsdal_crm-0.2.3.dist-info/METADATA,sha256=XG7HQuAYO-TyK1Wz1Y-pOi0kzner4ZmiXdU6TKxcdSE,1596
31
+ amsdal_crm-0.2.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
32
+ amsdal_crm-0.2.3.dist-info/RECORD,,