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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""EmailService for email integration."""
|
|
2
|
+
|
|
3
|
+
from amsdal_data.transactions.decorators import async_transaction
|
|
4
|
+
from amsdal_data.transactions.decorators import transaction
|
|
5
|
+
|
|
6
|
+
from amsdal_crm.models.activity import ActivityRelatedTo
|
|
7
|
+
from amsdal_crm.models.activity import ActivityType
|
|
8
|
+
from amsdal_crm.models.activity import EmailActivity
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EmailService:
|
|
12
|
+
"""Service for email integration and logging."""
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
@transaction
|
|
16
|
+
def log_email(
|
|
17
|
+
cls,
|
|
18
|
+
subject: str,
|
|
19
|
+
body: str,
|
|
20
|
+
from_address: str,
|
|
21
|
+
to_addresses: list[str],
|
|
22
|
+
cc_addresses: list[str] | None,
|
|
23
|
+
related_to_type: ActivityRelatedTo,
|
|
24
|
+
related_to_id: str,
|
|
25
|
+
owner_email: str,
|
|
26
|
+
*,
|
|
27
|
+
is_outbound: bool = True,
|
|
28
|
+
) -> EmailActivity:
|
|
29
|
+
"""Log an email as an activity.
|
|
30
|
+
|
|
31
|
+
This can be called when:
|
|
32
|
+
- User sends email from CRM
|
|
33
|
+
- Incoming email is parsed and associated with CRM record
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
subject: Email subject
|
|
37
|
+
body: Email body
|
|
38
|
+
from_address: Sender email address
|
|
39
|
+
to_addresses: List of recipient email addresses
|
|
40
|
+
cc_addresses: List of CC email addresses
|
|
41
|
+
related_to_type: Type of related record (Contact, Account, Deal)
|
|
42
|
+
related_to_id: ID of related record
|
|
43
|
+
owner_email: Email of user who owns this activity
|
|
44
|
+
is_outbound: True if sent from CRM, False if received
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
The created EmailActivity
|
|
48
|
+
"""
|
|
49
|
+
email_activity = EmailActivity(
|
|
50
|
+
activity_type=ActivityType.EMAIL,
|
|
51
|
+
subject=subject,
|
|
52
|
+
body=body,
|
|
53
|
+
from_address=from_address,
|
|
54
|
+
to_addresses=to_addresses,
|
|
55
|
+
cc_addresses=cc_addresses,
|
|
56
|
+
related_to_type=related_to_type,
|
|
57
|
+
related_to_id=related_to_id,
|
|
58
|
+
owner_email=owner_email,
|
|
59
|
+
is_outbound=is_outbound,
|
|
60
|
+
description=f'Email: {subject}',
|
|
61
|
+
due_date=None,
|
|
62
|
+
completed_at=None,
|
|
63
|
+
is_completed=False,
|
|
64
|
+
)
|
|
65
|
+
email_activity.save(force_insert=True)
|
|
66
|
+
|
|
67
|
+
return email_activity
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
@async_transaction
|
|
71
|
+
async def alog_email(
|
|
72
|
+
cls,
|
|
73
|
+
subject: str,
|
|
74
|
+
body: str,
|
|
75
|
+
from_address: str,
|
|
76
|
+
to_addresses: list[str],
|
|
77
|
+
cc_addresses: list[str] | None,
|
|
78
|
+
related_to_type: ActivityRelatedTo,
|
|
79
|
+
related_to_id: str,
|
|
80
|
+
owner_email: str,
|
|
81
|
+
*,
|
|
82
|
+
is_outbound: bool = True,
|
|
83
|
+
) -> EmailActivity:
|
|
84
|
+
"""Async version of log_email.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
subject: Email subject
|
|
88
|
+
body: Email body
|
|
89
|
+
from_address: Sender email address
|
|
90
|
+
to_addresses: List of recipient email addresses
|
|
91
|
+
cc_addresses: List of CC email addresses
|
|
92
|
+
related_to_type: Type of related record (Contact, Account, Deal)
|
|
93
|
+
related_to_id: ID of related record
|
|
94
|
+
owner_email: Email of user who owns this activity
|
|
95
|
+
is_outbound: True if sent from CRM, False if received
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
The created EmailActivity
|
|
99
|
+
"""
|
|
100
|
+
email_activity = EmailActivity(
|
|
101
|
+
activity_type=ActivityType.EMAIL,
|
|
102
|
+
subject=subject,
|
|
103
|
+
body=body,
|
|
104
|
+
from_address=from_address,
|
|
105
|
+
to_addresses=to_addresses,
|
|
106
|
+
cc_addresses=cc_addresses,
|
|
107
|
+
related_to_type=related_to_type,
|
|
108
|
+
related_to_id=related_to_id,
|
|
109
|
+
owner_email=owner_email,
|
|
110
|
+
is_outbound=is_outbound,
|
|
111
|
+
description=f'Email: {subject}',
|
|
112
|
+
due_date=None,
|
|
113
|
+
completed_at=None,
|
|
114
|
+
is_completed=False,
|
|
115
|
+
)
|
|
116
|
+
await email_activity.asave(force_insert=True)
|
|
117
|
+
|
|
118
|
+
return email_activity
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""WorkflowService for executing workflow automation rules."""
|
|
2
|
+
|
|
3
|
+
from amsdal_models.classes.model import Model
|
|
4
|
+
from amsdal_utils.models.enums import Versions
|
|
5
|
+
|
|
6
|
+
from amsdal_crm.errors import WorkflowExecutionError
|
|
7
|
+
from amsdal_crm.models.activity import ActivityRelatedTo
|
|
8
|
+
from amsdal_crm.models.activity import ActivityType
|
|
9
|
+
from amsdal_crm.models.activity import Note
|
|
10
|
+
from amsdal_crm.models.workflow_rule import WorkflowRule
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class WorkflowService:
|
|
14
|
+
"""Execute workflow rules for automation."""
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def execute_rules(cls, entity_type: str, trigger_event: str, entity: Model) -> None:
|
|
18
|
+
"""Execute workflow rules for an entity event.
|
|
19
|
+
|
|
20
|
+
Called from lifecycle hooks (post_create, post_update, post_delete).
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
entity_type: Type of entity (Contact, Account, Deal, Activity)
|
|
24
|
+
trigger_event: Event that triggered the rule (create, update, delete)
|
|
25
|
+
entity: The entity instance
|
|
26
|
+
"""
|
|
27
|
+
# Load active rules for this entity and trigger
|
|
28
|
+
rules = WorkflowRule.objects.filter(
|
|
29
|
+
entity_type=entity_type,
|
|
30
|
+
trigger_event=trigger_event,
|
|
31
|
+
is_active=True,
|
|
32
|
+
_address__object_version=Versions.LATEST,
|
|
33
|
+
).execute()
|
|
34
|
+
|
|
35
|
+
for rule in rules:
|
|
36
|
+
try:
|
|
37
|
+
if cls._evaluate_condition(rule, entity):
|
|
38
|
+
cls._execute_action(rule, entity)
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
# Log error but don't fail the entire operation
|
|
41
|
+
error_msg = f'Failed to execute workflow rule {rule.name}: {exc}'
|
|
42
|
+
raise WorkflowExecutionError(error_msg) from exc
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
async def aexecute_rules(cls, entity_type: str, trigger_event: str, entity: Model) -> None:
|
|
46
|
+
"""Execute workflow rules for an entity event.
|
|
47
|
+
|
|
48
|
+
Called from lifecycle hooks (post_create, post_update, post_delete).
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
entity_type: Type of entity (Contact, Account, Deal, Activity)
|
|
52
|
+
trigger_event: Event that triggered the rule (create, update, delete)
|
|
53
|
+
entity: The entity instance
|
|
54
|
+
"""
|
|
55
|
+
# Load active rules for this entity and trigger
|
|
56
|
+
rules = await WorkflowRule.objects.filter(
|
|
57
|
+
entity_type=entity_type,
|
|
58
|
+
trigger_event=trigger_event,
|
|
59
|
+
is_active=True,
|
|
60
|
+
_address__object_version=Versions.LATEST,
|
|
61
|
+
).aexecute()
|
|
62
|
+
|
|
63
|
+
for rule in rules:
|
|
64
|
+
try:
|
|
65
|
+
if cls._evaluate_condition(rule, entity):
|
|
66
|
+
await cls._aexecute_action(rule, entity)
|
|
67
|
+
except Exception as exc:
|
|
68
|
+
# Log error but don't fail the entire operation
|
|
69
|
+
error_msg = f'Failed to execute workflow rule {rule.name}: {exc}'
|
|
70
|
+
raise WorkflowExecutionError(error_msg) from exc
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def _evaluate_condition(cls, rule: WorkflowRule, entity: Model) -> bool:
|
|
74
|
+
"""Evaluate if rule condition matches.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
rule: The workflow rule
|
|
78
|
+
entity: The entity to evaluate
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
True if condition matches, False otherwise
|
|
82
|
+
"""
|
|
83
|
+
if not rule.condition_field:
|
|
84
|
+
return True # No condition = always match
|
|
85
|
+
|
|
86
|
+
entity_value = getattr(entity, rule.condition_field, None)
|
|
87
|
+
|
|
88
|
+
if rule.condition_operator == 'equals':
|
|
89
|
+
return entity_value == rule.condition_value
|
|
90
|
+
elif rule.condition_operator == 'not_equals':
|
|
91
|
+
return entity_value != rule.condition_value
|
|
92
|
+
elif rule.condition_operator == 'contains':
|
|
93
|
+
if rule.condition_value is None or entity_value is None:
|
|
94
|
+
return False
|
|
95
|
+
return str(rule.condition_value) in str(entity_value)
|
|
96
|
+
elif rule.condition_operator == 'greater_than':
|
|
97
|
+
if entity_value is None or rule.condition_value is None:
|
|
98
|
+
return False
|
|
99
|
+
return entity_value > rule.condition_value
|
|
100
|
+
elif rule.condition_operator == 'less_than':
|
|
101
|
+
if entity_value is None or rule.condition_value is None:
|
|
102
|
+
return False
|
|
103
|
+
return entity_value < rule.condition_value
|
|
104
|
+
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def _execute_action(cls, rule: WorkflowRule, entity: Model) -> None:
|
|
109
|
+
"""Execute rule action.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
rule: The workflow rule
|
|
113
|
+
entity: The entity to act upon
|
|
114
|
+
"""
|
|
115
|
+
if rule.action_type == 'update_field':
|
|
116
|
+
# Update field on entity
|
|
117
|
+
field_name = rule.action_config.get('field_name')
|
|
118
|
+
new_value = rule.action_config.get('value')
|
|
119
|
+
if field_name:
|
|
120
|
+
setattr(entity, str(field_name), new_value)
|
|
121
|
+
entity.save()
|
|
122
|
+
|
|
123
|
+
elif rule.action_type == 'create_activity':
|
|
124
|
+
# Create a Note activity
|
|
125
|
+
note = Note(
|
|
126
|
+
activity_type=ActivityType.NOTE,
|
|
127
|
+
subject=rule.action_config.get('subject', f'Workflow: {rule.name}'),
|
|
128
|
+
description=rule.action_config.get('description', ''),
|
|
129
|
+
related_to_type=ActivityRelatedTo[rule.entity_type.upper()],
|
|
130
|
+
related_to_id=entity._object_id,
|
|
131
|
+
owner_email=entity.owner_email if hasattr(entity, 'owner_email') else '',
|
|
132
|
+
due_date=None,
|
|
133
|
+
completed_at=None,
|
|
134
|
+
is_completed=False,
|
|
135
|
+
)
|
|
136
|
+
note.save(force_insert=True)
|
|
137
|
+
|
|
138
|
+
elif rule.action_type == 'send_notification':
|
|
139
|
+
# TODO: Implement notification system
|
|
140
|
+
# Placeholder for future notification integration
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
async def _aexecute_action(cls, rule: WorkflowRule, entity: Model) -> None:
|
|
145
|
+
"""Execute rule action.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
rule: The workflow rule
|
|
149
|
+
entity: The entity to act upon
|
|
150
|
+
"""
|
|
151
|
+
if rule.action_type == 'update_field':
|
|
152
|
+
# Update field on entity
|
|
153
|
+
field_name = rule.action_config.get('field_name')
|
|
154
|
+
new_value = rule.action_config.get('value')
|
|
155
|
+
if field_name:
|
|
156
|
+
setattr(entity, str(field_name), new_value)
|
|
157
|
+
await entity.asave()
|
|
158
|
+
|
|
159
|
+
elif rule.action_type == 'create_activity':
|
|
160
|
+
# Create a Note activity
|
|
161
|
+
note = Note(
|
|
162
|
+
activity_type=ActivityType.NOTE,
|
|
163
|
+
subject=rule.action_config.get('subject', f'Workflow: {rule.name}'),
|
|
164
|
+
description=rule.action_config.get('description', ''),
|
|
165
|
+
related_to_type=ActivityRelatedTo[rule.entity_type.upper()],
|
|
166
|
+
related_to_id=entity._object_id,
|
|
167
|
+
owner_email=entity.owner_email if hasattr(entity, 'owner_email') else '',
|
|
168
|
+
due_date=None,
|
|
169
|
+
completed_at=None,
|
|
170
|
+
is_completed=False,
|
|
171
|
+
)
|
|
172
|
+
await note.asave(force_insert=True)
|
|
173
|
+
|
|
174
|
+
elif rule.action_type == 'send_notification':
|
|
175
|
+
# TODO: Implement notification system
|
|
176
|
+
# Placeholder for future notification integration
|
|
177
|
+
pass
|
amsdal_crm/settings.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""CRM Settings."""
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings
|
|
5
|
+
from pydantic_settings import SettingsConfigDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CRMSettings(BaseSettings):
|
|
9
|
+
"""Settings for the CRM module."""
|
|
10
|
+
|
|
11
|
+
model_config = SettingsConfigDict(env_prefix='AMSDAL_CRM_')
|
|
12
|
+
|
|
13
|
+
# Activity settings
|
|
14
|
+
DEFAULT_ACTIVITY_TIMELINE_LIMIT: int = Field(100, title='Default Activity Timeline Limit')
|
|
15
|
+
|
|
16
|
+
# Custom field settings
|
|
17
|
+
MAX_CUSTOM_FIELDS_PER_ENTITY: int = Field(50, title='Max Custom Fields Per Entity')
|
|
18
|
+
|
|
19
|
+
# Workflow settings
|
|
20
|
+
MAX_WORKFLOW_RULES_PER_ENTITY: int = Field(100, title='Max Workflow Rules Per Entity')
|
|
21
|
+
|
|
22
|
+
# Deal settings
|
|
23
|
+
DEFAULT_CURRENCY: str = Field('USD', title='Default Currency')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
crm_settings = CRMSettings()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: amsdal_crm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: amsdal-crm plugin for AMSDAL Framework
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: aiohttp==3.12.15
|
|
7
|
+
Requires-Dist: amsdal-cli>=0.5.7
|
|
8
|
+
Requires-Dist: amsdal-data>=0.5.9
|
|
9
|
+
Requires-Dist: amsdal-models>=0.5.9
|
|
10
|
+
Requires-Dist: amsdal-utils>=0.5.4
|
|
11
|
+
Requires-Dist: amsdal>=0.5.6
|
|
12
|
+
Requires-Dist: mcp>=0.1
|
|
13
|
+
Requires-Dist: openai==1.100.2
|
|
14
|
+
Requires-Dist: pydantic-settings~=2.12
|
|
15
|
+
Requires-Dist: pydantic~=2.12
|
|
16
|
+
Requires-Dist: pymupdf>=1.24.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# amsdal-crm
|
|
20
|
+
|
|
21
|
+
This plugin provides custom models, properties, transactions, and hooks for the AMSDAL Framework.
|
|
22
|
+
|
|
23
|
+
## Plugin Structure
|
|
24
|
+
|
|
25
|
+
- `src/models/` - Contains model definitions in Python format
|
|
26
|
+
- `src/transactions/` - Contains transaction definitions
|
|
27
|
+
- `pyproject.toml` - Plugin configuration file
|
|
28
|
+
- `config.yml` - Configuration for connections
|
|
29
|
+
|
|
30
|
+
## Installing this Plugin
|
|
31
|
+
|
|
32
|
+
To use this plugin in an AMSDAL application:
|
|
33
|
+
|
|
34
|
+
1. Copy the plugin directory to your AMSDAL application
|
|
35
|
+
2. Import the models and transactions as needed
|
|
36
|
+
3. Register the plugin in your application configuration
|
|
37
|
+
|
|
38
|
+
## Development
|
|
39
|
+
|
|
40
|
+
This plugin uses sync mode.
|
|
41
|
+
|
|
42
|
+
### Adding Models
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
amsdal generate model ModelName --format py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Adding Properties
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
amsdal generate property --model ModelName property_name
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Adding Transactions
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
amsdal generate transaction TransactionName
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Adding Hooks
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
amsdal generate hook --model ModelName on_create
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Testing
|
|
67
|
+
|
|
68
|
+
Test your plugin by integrating it with an AMSDAL application and running the application's test suite.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
amsdal_crm/__about__.py,sha256=IMjkMO3twhQzluVTo8Z6rE7Eg-9U79_LGKMcsWLKBkY,22
|
|
2
|
+
amsdal_crm/__init__.py,sha256=b4wxJYesA5Ctk1IrAvlw64i_0EU3SiK1Tw6sUYakd18,303
|
|
3
|
+
amsdal_crm/app.py,sha256=eQV30L4QB43jjmGQzK_2crLmKK9P886KdFz7zTyDB8Y,1029
|
|
4
|
+
amsdal_crm/constants.py,sha256=5Ga7q9zEKcQZnAoKv_SE_7w8WxvhPFkM9gY9NruOEaA,347
|
|
5
|
+
amsdal_crm/errors.py,sha256=kTcDyKb-sEWkoYQ6OtokyzLIySMdOq32O3_qoFYUXuQ,514
|
|
6
|
+
amsdal_crm/settings.py,sha256=YbwDeiKaahqipGoBGkMRzYKGk8flt7IrkmMTLDyC9OQ,751
|
|
7
|
+
amsdal_crm/fixtures/__init__.py,sha256=1tDNXZhcbZBd4tX3lTKKlom1NUg1TX2aa2IbymWO9f0,20
|
|
8
|
+
amsdal_crm/fixtures/permissions.py,sha256=cYA-gWkKQdoN79GymQVHtT0GyFXMzaskwp13Ietp9wE,1107
|
|
9
|
+
amsdal_crm/fixtures/pipelines.py,sha256=ZCLmgrA700Sl7Oy7l4IQ8FbIbC1378OkcJTrZe5701o,2064
|
|
10
|
+
amsdal_crm/lifecycle/__init__.py,sha256=B8nw19lEIr7U15Lnu6jh7yzZwF9LWWh4-p3X63sAicQ,31
|
|
11
|
+
amsdal_crm/lifecycle/consumer.py,sha256=7tjPxWYMUZY0x77IrzGYPbaq2ozqDpflkyn17G5E2Zc,1293
|
|
12
|
+
amsdal_crm/migrations/0000_initial.py,sha256=8XjM-sbrNKJfcyGE_K2ITW4fOz7gmUxWH_NbX48o4XI,57028
|
|
13
|
+
amsdal_crm/models/__init__.py,sha256=DSuGeLKPNL_EUGohWtrH6Eof6Nk--dHyZpfqbGWmYIY,1350
|
|
14
|
+
amsdal_crm/models/account.py,sha256=b9JguizB-eM1BkDar4nGhayZ-icdfIsQp5cdB4sVZaQ,4897
|
|
15
|
+
amsdal_crm/models/activity.py,sha256=UtO1--oSPfrQCfQwWfIlEkjdXuaRG9znesPrGe1JqGM,4775
|
|
16
|
+
amsdal_crm/models/attachment.py,sha256=CzS8sUMw0_8T_a4Ey6uzrSdEc12Fki5FbrFRXrsTExk,1525
|
|
17
|
+
amsdal_crm/models/contact.py,sha256=J_ULPcM947AjRI35UCXWa_CAXK7HDgijXmcIlN2UdSI,4903
|
|
18
|
+
amsdal_crm/models/custom_field_definition.py,sha256=Z0k_QR6rZ1hWkg4Wn-w8Rn9GSIgpOg_moUYRsmRPZQI,1666
|
|
19
|
+
amsdal_crm/models/deal.py,sha256=ECG5Jd1zy286YJFGfxKDlJjUPW9PhLEvvL3kIeGttws,6434
|
|
20
|
+
amsdal_crm/models/pipeline.py,sha256=DXJh5MbCCRctEHhDfxef5RxFWSKN0D4v6UK75q5ssL8,925
|
|
21
|
+
amsdal_crm/models/stage.py,sha256=Ch4O1Aj2LtBhGpDN7MqY4iNRPrEQig0q6QtEU5cp4hA,1608
|
|
22
|
+
amsdal_crm/models/workflow_rule.py,sha256=cnIEaX-hWcRYvN5gR4uPt7Cirr8sPPdJibZapD8VRpY,1547
|
|
23
|
+
amsdal_crm/services/__init__.py,sha256=ngHA-MUPrsHvga8vFP61b8v7rrAWl-h1VZTjhlJCXkI,465
|
|
24
|
+
amsdal_crm/services/activity_service.py,sha256=3iuEmaSicwm81rouLPum5VaWW0bAMnNWNPRCHkNhewU,1873
|
|
25
|
+
amsdal_crm/services/custom_field_service.py,sha256=rqTlzcmjc-tqc3nuo2m0cMFdMgo1lIIlPniDHIpJLv8,5146
|
|
26
|
+
amsdal_crm/services/deal_service.py,sha256=ZF7cAc6r10xgXZ8D8Oy3hnE-6GgAXq8fuG_Y4QerRGw,4839
|
|
27
|
+
amsdal_crm/services/email_service.py,sha256=kung83otZAzm5MjezbWFP_Bp9CJ2NXLlDdjMX9MvNIc,3788
|
|
28
|
+
amsdal_crm/services/workflow_service.py,sha256=oKOFJrwnMZxAiHuScs63tZxL801z-6ryrUuPMC7OXlE,7036
|
|
29
|
+
amsdal_crm-0.1.0.dist-info/METADATA,sha256=xZ3eDOo_eJ0pTI8uqQYCkS-F1Cgax3lx86xlZBs-cak,1596
|
|
30
|
+
amsdal_crm-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
31
|
+
amsdal_crm-0.1.0.dist-info/RECORD,,
|