amsdal_crm 0.1.0__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 +1 -0
- amsdal_crm/__init__.py +13 -0
- amsdal_crm/app.py +25 -0
- amsdal_crm/constants.py +13 -0
- amsdal_crm/errors.py +21 -0
- amsdal_crm/fixtures/__init__.py +1 -0
- amsdal_crm/fixtures/permissions.py +29 -0
- amsdal_crm/fixtures/pipelines.py +74 -0
- amsdal_crm/lifecycle/__init__.py +1 -0
- amsdal_crm/lifecycle/consumer.py +44 -0
- amsdal_crm/migrations/0000_initial.py +633 -0
- amsdal_crm/models/__init__.py +46 -0
- amsdal_crm/models/account.py +127 -0
- amsdal_crm/models/activity.py +140 -0
- amsdal_crm/models/attachment.py +43 -0
- amsdal_crm/models/contact.py +132 -0
- amsdal_crm/models/custom_field_definition.py +44 -0
- amsdal_crm/models/deal.py +172 -0
- amsdal_crm/models/pipeline.py +28 -0
- amsdal_crm/models/stage.py +47 -0
- amsdal_crm/models/workflow_rule.py +44 -0
- amsdal_crm/services/__init__.py +15 -0
- amsdal_crm/services/activity_service.py +56 -0
- amsdal_crm/services/custom_field_service.py +143 -0
- amsdal_crm/services/deal_service.py +131 -0
- amsdal_crm/services/email_service.py +118 -0
- amsdal_crm/services/workflow_service.py +177 -0
- amsdal_crm/settings.py +26 -0
- amsdal_crm-0.1.0.dist-info/METADATA +68 -0
- amsdal_crm-0.1.0.dist-info/RECORD +31 -0
- amsdal_crm-0.1.0.dist-info/WHEEL +4 -0
amsdal_crm/__about__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.1.0'
|
amsdal_crm/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""AMSDAL CRM Plugin.
|
|
2
|
+
|
|
3
|
+
A minimal MVP CRM plugin providing core CRM functionality including:
|
|
4
|
+
- Contact and Account management
|
|
5
|
+
- Deal pipeline management
|
|
6
|
+
- Activity tracking and timeline
|
|
7
|
+
- Custom fields support
|
|
8
|
+
- Basic workflow automation
|
|
9
|
+
- Email integration
|
|
10
|
+
- File attachments
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__version__ = '0.1.0'
|
amsdal_crm/app.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""CRM App Configuration."""
|
|
2
|
+
|
|
3
|
+
from amsdal.contrib.app_config import AppConfig
|
|
4
|
+
from amsdal_utils.lifecycle.enum import LifecycleEvent
|
|
5
|
+
from amsdal_utils.lifecycle.producer import LifecycleProducer
|
|
6
|
+
|
|
7
|
+
from amsdal_crm.constants import CRMLifecycleEvent
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CRMAppConfig(AppConfig):
|
|
11
|
+
"""Configuration for the CRM application."""
|
|
12
|
+
|
|
13
|
+
def on_ready(self) -> None:
|
|
14
|
+
"""Set up CRM lifecycle listeners and initialize module."""
|
|
15
|
+
from amsdal_crm.lifecycle.consumer import DealWonNotificationConsumer
|
|
16
|
+
from amsdal_crm.lifecycle.consumer import LoadCRMFixturesConsumer
|
|
17
|
+
|
|
18
|
+
# Load fixtures on startup
|
|
19
|
+
LifecycleProducer.add_listener(LifecycleEvent.ON_SERVER_STARTUP, LoadCRMFixturesConsumer)
|
|
20
|
+
|
|
21
|
+
# Custom CRM events
|
|
22
|
+
LifecycleProducer.add_listener(CRMLifecycleEvent.ON_DEAL_WON, DealWonNotificationConsumer) # type: ignore[arg-type]
|
|
23
|
+
|
|
24
|
+
# Additional event listeners can be added here
|
|
25
|
+
# LifecycleProducer.add_listener(CRMLifecycleEvent.ON_DEAL_LOST, DealLostNotificationConsumer)
|
amsdal_crm/constants.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""CRM Constants and Enums."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CRMLifecycleEvent(str, Enum):
|
|
7
|
+
"""Custom lifecycle events for CRM operations."""
|
|
8
|
+
|
|
9
|
+
ON_DEAL_STAGE_CHANGE = 'on_deal_stage_change'
|
|
10
|
+
ON_DEAL_WON = 'on_deal_won'
|
|
11
|
+
ON_DEAL_LOST = 'on_deal_lost'
|
|
12
|
+
ON_CONTACT_MERGE = 'on_contact_merge'
|
|
13
|
+
ON_ACCOUNT_MERGE = 'on_account_merge'
|
amsdal_crm/errors.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""CRM Custom Exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CRMError(Exception):
|
|
5
|
+
"""Base exception for CRM errors."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CustomFieldValidationError(CRMError):
|
|
9
|
+
"""Raised when custom field validation fails."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WorkflowExecutionError(CRMError):
|
|
13
|
+
"""Raised when workflow rule execution fails."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InvalidStageTransitionError(CRMError):
|
|
17
|
+
"""Raised when an invalid stage transition is attempted."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PermissionDeniedError(CRMError):
|
|
21
|
+
"""Raised when user doesn't have permission for an operation."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CRM Fixtures."""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""CRM permission fixtures."""
|
|
2
|
+
|
|
3
|
+
# CRM permissions for each entity type
|
|
4
|
+
CRM_PERMISSIONS = [
|
|
5
|
+
# Contact permissions
|
|
6
|
+
{'model': 'Contact', 'action': 'read'},
|
|
7
|
+
{'model': 'Contact', 'action': 'create'},
|
|
8
|
+
{'model': 'Contact', 'action': 'update'},
|
|
9
|
+
{'model': 'Contact', 'action': 'delete'},
|
|
10
|
+
# Account permissions
|
|
11
|
+
{'model': 'Account', 'action': 'read'},
|
|
12
|
+
{'model': 'Account', 'action': 'create'},
|
|
13
|
+
{'model': 'Account', 'action': 'update'},
|
|
14
|
+
{'model': 'Account', 'action': 'delete'},
|
|
15
|
+
# Deal permissions
|
|
16
|
+
{'model': 'Deal', 'action': 'read'},
|
|
17
|
+
{'model': 'Deal', 'action': 'create'},
|
|
18
|
+
{'model': 'Deal', 'action': 'update'},
|
|
19
|
+
{'model': 'Deal', 'action': 'delete'},
|
|
20
|
+
# Activity permissions
|
|
21
|
+
{'model': 'Activity', 'action': 'read'},
|
|
22
|
+
{'model': 'Activity', 'action': 'create'},
|
|
23
|
+
{'model': 'Activity', 'action': 'update'},
|
|
24
|
+
{'model': 'Activity', 'action': 'delete'},
|
|
25
|
+
# Pipeline permissions (read-only for non-admins)
|
|
26
|
+
{'model': 'Pipeline', 'action': 'read'},
|
|
27
|
+
# Stage permissions (read-only for non-admins)
|
|
28
|
+
{'model': 'Stage', 'action': 'read'},
|
|
29
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Default pipeline and stage fixtures."""
|
|
2
|
+
|
|
3
|
+
# Default sales pipeline with stages
|
|
4
|
+
DEFAULT_PIPELINES = [
|
|
5
|
+
{
|
|
6
|
+
'_external_id': 'crm_default_pipeline_sales',
|
|
7
|
+
'name': 'Sales Pipeline',
|
|
8
|
+
'description': 'Default sales pipeline',
|
|
9
|
+
'is_active': True,
|
|
10
|
+
},
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
DEFAULT_STAGES = [
|
|
14
|
+
{
|
|
15
|
+
'_external_id': 'crm_stage_lead',
|
|
16
|
+
'_order': 1.0,
|
|
17
|
+
'pipeline': {'_external_id': 'crm_default_pipeline_sales'},
|
|
18
|
+
'name': 'Lead',
|
|
19
|
+
'order': 1,
|
|
20
|
+
'probability': 10.0,
|
|
21
|
+
'is_closed_won': False,
|
|
22
|
+
'is_closed_lost': False,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
'_external_id': 'crm_stage_qualified',
|
|
26
|
+
'_order': 2.0,
|
|
27
|
+
'pipeline': {'_external_id': 'crm_default_pipeline_sales'},
|
|
28
|
+
'name': 'Qualified',
|
|
29
|
+
'order': 2,
|
|
30
|
+
'probability': 25.0,
|
|
31
|
+
'is_closed_won': False,
|
|
32
|
+
'is_closed_lost': False,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
'_external_id': 'crm_stage_proposal',
|
|
36
|
+
'_order': 3.0,
|
|
37
|
+
'pipeline': {'_external_id': 'crm_default_pipeline_sales'},
|
|
38
|
+
'name': 'Proposal',
|
|
39
|
+
'order': 3,
|
|
40
|
+
'probability': 50.0,
|
|
41
|
+
'is_closed_won': False,
|
|
42
|
+
'is_closed_lost': False,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
'_external_id': 'crm_stage_negotiation',
|
|
46
|
+
'_order': 4.0,
|
|
47
|
+
'pipeline': {'_external_id': 'crm_default_pipeline_sales'},
|
|
48
|
+
'name': 'Negotiation',
|
|
49
|
+
'order': 4,
|
|
50
|
+
'probability': 75.0,
|
|
51
|
+
'is_closed_won': False,
|
|
52
|
+
'is_closed_lost': False,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
'_external_id': 'crm_stage_closed_won',
|
|
56
|
+
'_order': 5.0,
|
|
57
|
+
'pipeline': {'_external_id': 'crm_default_pipeline_sales'},
|
|
58
|
+
'name': 'Closed Won',
|
|
59
|
+
'order': 5,
|
|
60
|
+
'probability': 100.0,
|
|
61
|
+
'is_closed_won': True,
|
|
62
|
+
'is_closed_lost': False,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
'_external_id': 'crm_stage_closed_lost',
|
|
66
|
+
'_order': 6.0,
|
|
67
|
+
'pipeline': {'_external_id': 'crm_default_pipeline_sales'},
|
|
68
|
+
'name': 'Closed Lost',
|
|
69
|
+
'order': 6,
|
|
70
|
+
'probability': 0.0,
|
|
71
|
+
'is_closed_won': False,
|
|
72
|
+
'is_closed_lost': True,
|
|
73
|
+
},
|
|
74
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CRM Lifecycle Consumers."""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""CRM Lifecycle Consumers."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from amsdal_utils.lifecycle.consumer import LifecycleConsumer
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from amsdal_crm.models.deal import Deal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LoadCRMFixturesConsumer(LifecycleConsumer):
|
|
12
|
+
"""Consumer that loads CRM fixtures on server startup."""
|
|
13
|
+
|
|
14
|
+
def on_event(self) -> None:
|
|
15
|
+
"""Load CRM fixtures (pipelines, stages, permissions)."""
|
|
16
|
+
# Note: Fixtures are typically loaded via the FixturesManager
|
|
17
|
+
# This consumer can be used for additional setup if needed
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
async def on_event_async(self) -> None:
|
|
21
|
+
"""Async version of on_event."""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DealWonNotificationConsumer(LifecycleConsumer):
|
|
26
|
+
"""Consumer that handles deal won events.
|
|
27
|
+
|
|
28
|
+
Placeholder for future notification system integration.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def on_event(self, deal: 'Deal', user_email: str) -> None:
|
|
32
|
+
"""Handle deal won event.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
deal: The deal that was won
|
|
36
|
+
user_email: Email of user who closed the deal
|
|
37
|
+
"""
|
|
38
|
+
# TODO: Implement notification logic
|
|
39
|
+
# Could integrate with email service, Slack, etc.
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
async def on_event_async(self, deal: 'Deal', user_email: str) -> None:
|
|
43
|
+
"""Async version of on_event."""
|
|
44
|
+
pass
|