constec 0.5.3__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {constec-0.5.3 → constec-0.7.0}/PKG-INFO +1 -1
- constec-0.7.0/constec/db/migrations/0008_refactor_creator_fields.py +173 -0
- constec-0.7.0/constec/db/migrations/0009_rename_user_to_companyuser.py +40 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/__init__.py +12 -4
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/automation.py +79 -103
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/group.py +2 -2
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/user.py +11 -11
- {constec-0.5.3 → constec-0.7.0}/constec.egg-info/PKG-INFO +1 -1
- {constec-0.5.3 → constec-0.7.0}/constec.egg-info/SOURCES.txt +2 -0
- {constec-0.5.3 → constec-0.7.0}/pyproject.toml +1 -1
- {constec-0.5.3 → constec-0.7.0}/LICENSE +0 -0
- {constec-0.5.3 → constec-0.7.0}/README.md +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/__init__.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/apps.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0001_initial.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0002_module_level.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0003_remove_module_level.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0004_rename_entities_company_cuit_idx_entities_company_e2c50f_idx_and_more.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0005_event.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0006_automation_trigger_action_executionlog_notificationtemplate.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0007_add_organization_to_automations.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/migrations/__init__.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/base.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/company.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/contact.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/erp.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/erp_entity.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/flow.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/module.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/organization.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/person.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/session.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/db/models/tag.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/py.typed +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/services/__init__.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/services/encryption.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/shared/__init__.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/shared/exceptions.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/utils/__init__.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/utils/cuit.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec/utils/password.py +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec.egg-info/dependency_links.txt +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec.egg-info/requires.txt +0 -0
- {constec-0.5.3 → constec-0.7.0}/constec.egg-info/top_level.txt +0 -0
- {constec-0.5.3 → constec-0.7.0}/setup.cfg +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Generated manually - Refactor creator fields to support User and OrganizationUser
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def migrate_creator_data(apps, schema_editor):
|
|
8
|
+
"""Migrate existing created_by data to created_by_user."""
|
|
9
|
+
Automation = apps.get_model('constec_db', 'Automation')
|
|
10
|
+
NotificationTemplate = apps.get_model('constec_db', 'NotificationTemplate')
|
|
11
|
+
|
|
12
|
+
# Migrate Automation creators
|
|
13
|
+
for automation in Automation.objects.all():
|
|
14
|
+
if automation.created_by:
|
|
15
|
+
automation.created_by_user = automation.created_by
|
|
16
|
+
automation.save(update_fields=['created_by_user'])
|
|
17
|
+
|
|
18
|
+
# Migrate NotificationTemplate creators
|
|
19
|
+
for template in NotificationTemplate.objects.all():
|
|
20
|
+
if template.created_by:
|
|
21
|
+
template.created_by_user = template.created_by
|
|
22
|
+
template.save(update_fields=['created_by_user'])
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Migration(migrations.Migration):
|
|
26
|
+
|
|
27
|
+
dependencies = [
|
|
28
|
+
('constec_db', '0007_add_organization_to_automations'),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
operations = [
|
|
32
|
+
# 1. Add new creator fields to Automation (nullable temporarily for data migration)
|
|
33
|
+
migrations.AddField(
|
|
34
|
+
model_name='automation',
|
|
35
|
+
name='created_by_user',
|
|
36
|
+
field=models.ForeignKey(
|
|
37
|
+
blank=True,
|
|
38
|
+
help_text='User creator (company-level)',
|
|
39
|
+
null=True,
|
|
40
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
41
|
+
related_name='created_automations',
|
|
42
|
+
to='constec_db.user'
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
migrations.AddField(
|
|
46
|
+
model_name='automation',
|
|
47
|
+
name='created_by_org_user',
|
|
48
|
+
field=models.ForeignKey(
|
|
49
|
+
blank=True,
|
|
50
|
+
help_text='OrganizationUser creator (org-level)',
|
|
51
|
+
null=True,
|
|
52
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
53
|
+
related_name='created_automations',
|
|
54
|
+
to='constec_db.organizationuser'
|
|
55
|
+
),
|
|
56
|
+
),
|
|
57
|
+
|
|
58
|
+
# 2. Add new creator fields to NotificationTemplate (nullable temporarily for data migration)
|
|
59
|
+
migrations.AddField(
|
|
60
|
+
model_name='notificationtemplate',
|
|
61
|
+
name='created_by_user',
|
|
62
|
+
field=models.ForeignKey(
|
|
63
|
+
blank=True,
|
|
64
|
+
help_text='User creator (company-level)',
|
|
65
|
+
null=True,
|
|
66
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
67
|
+
related_name='created_templates',
|
|
68
|
+
to='constec_db.user'
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
migrations.AddField(
|
|
72
|
+
model_name='notificationtemplate',
|
|
73
|
+
name='created_by_org_user',
|
|
74
|
+
field=models.ForeignKey(
|
|
75
|
+
blank=True,
|
|
76
|
+
help_text='OrganizationUser creator (org-level)',
|
|
77
|
+
null=True,
|
|
78
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
79
|
+
related_name='created_templates',
|
|
80
|
+
to='constec_db.organizationuser'
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
|
|
84
|
+
# 3. Add triggered_by_org_user to ExecutionLog
|
|
85
|
+
migrations.AddField(
|
|
86
|
+
model_name='executionlog',
|
|
87
|
+
name='triggered_by_org_user',
|
|
88
|
+
field=models.ForeignKey(
|
|
89
|
+
blank=True,
|
|
90
|
+
help_text='OrganizationUser who triggered execution (org-level)',
|
|
91
|
+
null=True,
|
|
92
|
+
on_delete=django.db.models.deletion.SET_NULL,
|
|
93
|
+
related_name='triggered_executions',
|
|
94
|
+
to='constec_db.organizationuser'
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
|
|
98
|
+
# 4. Update existing triggered_by_user field help text
|
|
99
|
+
migrations.AlterField(
|
|
100
|
+
model_name='executionlog',
|
|
101
|
+
name='triggered_by_user',
|
|
102
|
+
field=models.ForeignKey(
|
|
103
|
+
blank=True,
|
|
104
|
+
help_text='User who triggered execution (company-level)',
|
|
105
|
+
null=True,
|
|
106
|
+
on_delete=django.db.models.deletion.SET_NULL,
|
|
107
|
+
related_name='triggered_executions',
|
|
108
|
+
to='constec_db.user'
|
|
109
|
+
),
|
|
110
|
+
),
|
|
111
|
+
|
|
112
|
+
# 5. Migrate data from old created_by to new created_by_user
|
|
113
|
+
migrations.RunPython(migrate_creator_data, reverse_code=migrations.RunPython.noop),
|
|
114
|
+
|
|
115
|
+
# 6. Remove old created_by fields
|
|
116
|
+
migrations.RemoveField(
|
|
117
|
+
model_name='automation',
|
|
118
|
+
name='created_by',
|
|
119
|
+
),
|
|
120
|
+
migrations.RemoveField(
|
|
121
|
+
model_name='notificationtemplate',
|
|
122
|
+
name='created_by',
|
|
123
|
+
),
|
|
124
|
+
|
|
125
|
+
# 7. Add constraints for Automation
|
|
126
|
+
migrations.AddConstraint(
|
|
127
|
+
model_name='automation',
|
|
128
|
+
constraint=models.CheckConstraint(
|
|
129
|
+
check=models.Q(
|
|
130
|
+
('created_by_user__isnull', False),
|
|
131
|
+
('created_by_org_user__isnull', True)
|
|
132
|
+
) | models.Q(
|
|
133
|
+
('created_by_user__isnull', True),
|
|
134
|
+
('created_by_org_user__isnull', False)
|
|
135
|
+
),
|
|
136
|
+
name='automation_single_creator'
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
migrations.AddConstraint(
|
|
140
|
+
model_name='automation',
|
|
141
|
+
constraint=models.CheckConstraint(
|
|
142
|
+
check=models.Q(('created_by_user__isnull', False)) | models.Q(('created_by_org_user__isnull', False)),
|
|
143
|
+
name='automation_requires_creator'
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
|
|
147
|
+
# 8. Add constraints for NotificationTemplate
|
|
148
|
+
migrations.AddConstraint(
|
|
149
|
+
model_name='notificationtemplate',
|
|
150
|
+
constraint=models.CheckConstraint(
|
|
151
|
+
check=models.Q(
|
|
152
|
+
('created_by_user__isnull', False),
|
|
153
|
+
('created_by_org_user__isnull', True)
|
|
154
|
+
) | models.Q(
|
|
155
|
+
('created_by_user__isnull', True),
|
|
156
|
+
('created_by_org_user__isnull', False)
|
|
157
|
+
),
|
|
158
|
+
name='template_single_creator'
|
|
159
|
+
),
|
|
160
|
+
),
|
|
161
|
+
migrations.AddConstraint(
|
|
162
|
+
model_name='notificationtemplate',
|
|
163
|
+
constraint=models.CheckConstraint(
|
|
164
|
+
check=models.Q(('created_by_user__isnull', False)) | models.Q(('created_by_org_user__isnull', False)),
|
|
165
|
+
name='template_requires_creator'
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
|
|
169
|
+
# 9. Delete deprecated Event model
|
|
170
|
+
migrations.DeleteModel(
|
|
171
|
+
name='Event',
|
|
172
|
+
),
|
|
173
|
+
]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Generated manually - Rename User to CompanyUser for clarity and consistency
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('constec_db', '0008_refactor_creator_fields'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
# Rename tables
|
|
14
|
+
migrations.RenameModel(
|
|
15
|
+
old_name='User',
|
|
16
|
+
new_name='CompanyUser',
|
|
17
|
+
),
|
|
18
|
+
migrations.RenameModel(
|
|
19
|
+
old_name='UserRole',
|
|
20
|
+
new_name='CompanyUserRole',
|
|
21
|
+
),
|
|
22
|
+
migrations.RenameModel(
|
|
23
|
+
old_name='UserCompanyAccess',
|
|
24
|
+
new_name='CompanyUserAccess',
|
|
25
|
+
),
|
|
26
|
+
|
|
27
|
+
# Update table names
|
|
28
|
+
migrations.AlterModelTable(
|
|
29
|
+
name='companyuser',
|
|
30
|
+
table='core"."company_users',
|
|
31
|
+
),
|
|
32
|
+
migrations.AlterModelTable(
|
|
33
|
+
name='companyuserrole',
|
|
34
|
+
table='core"."company_user_roles',
|
|
35
|
+
),
|
|
36
|
+
migrations.AlterModelTable(
|
|
37
|
+
name='companyuseraccess',
|
|
38
|
+
table='core"."company_user_access',
|
|
39
|
+
),
|
|
40
|
+
]
|
|
@@ -19,9 +19,14 @@ from .base import UUIDModel
|
|
|
19
19
|
# Core models (core schema)
|
|
20
20
|
from .organization import Organization, OrganizationRole, OrganizationUser
|
|
21
21
|
from .company import Company
|
|
22
|
-
from .user import
|
|
22
|
+
from .user import CompanyUser, CompanyUserRole, CompanyUserAccess
|
|
23
23
|
from .person import Person
|
|
24
24
|
from .group import UserGroup
|
|
25
|
+
|
|
26
|
+
# Backward compatibility aliases
|
|
27
|
+
User = CompanyUser
|
|
28
|
+
UserRole = CompanyUserRole
|
|
29
|
+
UserCompanyAccess = CompanyUserAccess
|
|
25
30
|
from .contact import ContactType, Contact, PersonContact
|
|
26
31
|
from .tag import TagCategory, PersonTag, PersonTagged
|
|
27
32
|
from .module import Module, CompanyModule, OrganizationModule
|
|
@@ -42,7 +47,7 @@ from .flow import FlowTemplate, Flow
|
|
|
42
47
|
from .session import Session, Message
|
|
43
48
|
|
|
44
49
|
# Automations models (automations schema)
|
|
45
|
-
from .automation import
|
|
50
|
+
from .automation import Automation, Trigger, Action, ExecutionLog, NotificationTemplate
|
|
46
51
|
|
|
47
52
|
|
|
48
53
|
__all__ = [
|
|
@@ -54,7 +59,11 @@ __all__ = [
|
|
|
54
59
|
'OrganizationUser',
|
|
55
60
|
# Company
|
|
56
61
|
'Company',
|
|
57
|
-
#
|
|
62
|
+
# CompanyUser
|
|
63
|
+
'CompanyUser',
|
|
64
|
+
'CompanyUserRole',
|
|
65
|
+
'CompanyUserAccess',
|
|
66
|
+
# Backward compatibility aliases
|
|
58
67
|
'User',
|
|
59
68
|
'UserRole',
|
|
60
69
|
'UserCompanyAccess',
|
|
@@ -93,7 +102,6 @@ __all__ = [
|
|
|
93
102
|
'Session',
|
|
94
103
|
'Message',
|
|
95
104
|
# Automations (automations schema)
|
|
96
|
-
'Event',
|
|
97
105
|
'Automation',
|
|
98
106
|
'Trigger',
|
|
99
107
|
'Action',
|
|
@@ -6,8 +6,8 @@ from django.db.models import Q
|
|
|
6
6
|
from django.core.exceptions import ValidationError
|
|
7
7
|
from .base import UUIDModel
|
|
8
8
|
from .company import Company
|
|
9
|
-
from .organization import Organization
|
|
10
|
-
from .user import
|
|
9
|
+
from .organization import Organization, OrganizationUser
|
|
10
|
+
from .user import CompanyUser
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class Automation(UUIDModel):
|
|
@@ -53,10 +53,23 @@ class Automation(UUIDModel):
|
|
|
53
53
|
db_index=True,
|
|
54
54
|
help_text="Organization para automations compartidas (opcional si se define company)"
|
|
55
55
|
)
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
|
|
57
|
+
# Creator - only ONE of these should be filled (XOR)
|
|
58
|
+
created_by_user = models.ForeignKey(
|
|
59
|
+
CompanyUser,
|
|
58
60
|
on_delete=models.CASCADE,
|
|
59
|
-
|
|
61
|
+
null=True,
|
|
62
|
+
blank=True,
|
|
63
|
+
related_name='created_automations',
|
|
64
|
+
help_text="User creator (company-level)"
|
|
65
|
+
)
|
|
66
|
+
created_by_org_user = models.ForeignKey(
|
|
67
|
+
OrganizationCompanyUser,
|
|
68
|
+
on_delete=models.CASCADE,
|
|
69
|
+
null=True,
|
|
70
|
+
blank=True,
|
|
71
|
+
related_name='created_automations',
|
|
72
|
+
help_text="OrganizationUser creator (org-level)"
|
|
60
73
|
)
|
|
61
74
|
|
|
62
75
|
# Info básica
|
|
@@ -107,12 +120,26 @@ class Automation(UUIDModel):
|
|
|
107
120
|
models.CheckConstraint(
|
|
108
121
|
check=Q(company__isnull=False) | Q(organization__isnull=False),
|
|
109
122
|
name='automation_requires_company_or_organization'
|
|
110
|
-
)
|
|
123
|
+
),
|
|
124
|
+
models.CheckConstraint(
|
|
125
|
+
check=Q(created_by_user__isnull=False) | Q(created_by_org_user__isnull=False),
|
|
126
|
+
name='automation_requires_creator'
|
|
127
|
+
),
|
|
128
|
+
models.CheckConstraint(
|
|
129
|
+
check=Q(created_by_user__isnull=False, created_by_org_user__isnull=True) |
|
|
130
|
+
Q(created_by_user__isnull=True, created_by_org_user__isnull=False),
|
|
131
|
+
name='automation_single_creator'
|
|
132
|
+
),
|
|
111
133
|
]
|
|
112
134
|
|
|
113
135
|
def __str__(self):
|
|
114
136
|
return f"{self.name} ({self.status})"
|
|
115
137
|
|
|
138
|
+
@property
|
|
139
|
+
def creator(self):
|
|
140
|
+
"""Helper to access creator regardless of type."""
|
|
141
|
+
return self.created_by_user or self.created_by_org_user
|
|
142
|
+
|
|
116
143
|
|
|
117
144
|
class Trigger(UUIDModel):
|
|
118
145
|
"""
|
|
@@ -290,11 +317,20 @@ class ExecutionLog(UUIDModel):
|
|
|
290
317
|
related_name='execution_logs'
|
|
291
318
|
)
|
|
292
319
|
triggered_by_user = models.ForeignKey(
|
|
293
|
-
|
|
320
|
+
CompanyUser,
|
|
321
|
+
on_delete=models.SET_NULL,
|
|
322
|
+
null=True,
|
|
323
|
+
blank=True,
|
|
324
|
+
related_name='triggered_executions',
|
|
325
|
+
help_text="User who triggered execution (company-level)"
|
|
326
|
+
)
|
|
327
|
+
triggered_by_org_user = models.ForeignKey(
|
|
328
|
+
OrganizationCompanyUser,
|
|
294
329
|
on_delete=models.SET_NULL,
|
|
295
330
|
null=True,
|
|
296
331
|
blank=True,
|
|
297
|
-
related_name='triggered_executions'
|
|
332
|
+
related_name='triggered_executions',
|
|
333
|
+
help_text="OrganizationUser who triggered execution (org-level)"
|
|
298
334
|
)
|
|
299
335
|
|
|
300
336
|
# Estado
|
|
@@ -341,6 +377,11 @@ class ExecutionLog(UUIDModel):
|
|
|
341
377
|
def __str__(self):
|
|
342
378
|
return f"{self.automation.name} - {self.status} ({self.started_at})"
|
|
343
379
|
|
|
380
|
+
@property
|
|
381
|
+
def trigger_user(self):
|
|
382
|
+
"""Helper to access trigger user regardless of type."""
|
|
383
|
+
return self.triggered_by_user or self.triggered_by_org_user
|
|
384
|
+
|
|
344
385
|
|
|
345
386
|
class NotificationTemplate(UUIDModel):
|
|
346
387
|
"""
|
|
@@ -374,10 +415,23 @@ class NotificationTemplate(UUIDModel):
|
|
|
374
415
|
db_index=True,
|
|
375
416
|
help_text="Organization para templates compartidos (opcional si se define company)"
|
|
376
417
|
)
|
|
377
|
-
|
|
378
|
-
|
|
418
|
+
|
|
419
|
+
# Creator - only ONE of these should be filled (XOR)
|
|
420
|
+
created_by_user = models.ForeignKey(
|
|
421
|
+
CompanyUser,
|
|
422
|
+
on_delete=models.CASCADE,
|
|
423
|
+
null=True,
|
|
424
|
+
blank=True,
|
|
425
|
+
related_name='created_templates',
|
|
426
|
+
help_text="User creator (company-level)"
|
|
427
|
+
)
|
|
428
|
+
created_by_org_user = models.ForeignKey(
|
|
429
|
+
OrganizationCompanyUser,
|
|
379
430
|
on_delete=models.CASCADE,
|
|
380
|
-
|
|
431
|
+
null=True,
|
|
432
|
+
blank=True,
|
|
433
|
+
related_name='created_templates',
|
|
434
|
+
help_text="OrganizationUser creator (org-level)"
|
|
381
435
|
)
|
|
382
436
|
|
|
383
437
|
# Info básica
|
|
@@ -413,100 +467,22 @@ class NotificationTemplate(UUIDModel):
|
|
|
413
467
|
models.CheckConstraint(
|
|
414
468
|
check=Q(company__isnull=False) | Q(organization__isnull=False),
|
|
415
469
|
name='template_requires_company_or_organization'
|
|
416
|
-
)
|
|
470
|
+
),
|
|
471
|
+
models.CheckConstraint(
|
|
472
|
+
check=Q(created_by_user__isnull=False) | Q(created_by_org_user__isnull=False),
|
|
473
|
+
name='template_requires_creator'
|
|
474
|
+
),
|
|
475
|
+
models.CheckConstraint(
|
|
476
|
+
check=Q(created_by_user__isnull=False, created_by_org_user__isnull=True) |
|
|
477
|
+
Q(created_by_user__isnull=True, created_by_org_user__isnull=False),
|
|
478
|
+
name='template_single_creator'
|
|
479
|
+
),
|
|
417
480
|
]
|
|
418
481
|
|
|
419
482
|
def __str__(self):
|
|
420
483
|
return f"{self.channel}: {self.name}"
|
|
421
484
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
Tipos soportados:
|
|
428
|
-
- notification: Notificaciones programadas
|
|
429
|
-
- automation: Automatizaciones programadas (acciones HTTP, workflows, etc.)
|
|
430
|
-
"""
|
|
431
|
-
|
|
432
|
-
EVENT_TYPE_CHOICES = [
|
|
433
|
-
('notification', 'Notification'),
|
|
434
|
-
('automation', 'Automation'),
|
|
435
|
-
]
|
|
436
|
-
|
|
437
|
-
STATUS_CHOICES = [
|
|
438
|
-
('active', 'Active'), # Activo y programado
|
|
439
|
-
('paused', 'Paused'), # Pausado temporalmente
|
|
440
|
-
('completed', 'Completed'), # Completado (para puntuales)
|
|
441
|
-
('failed', 'Failed'), # Falló la última ejecución
|
|
442
|
-
('cancelled', 'Cancelled'), # Cancelado permanentemente
|
|
443
|
-
]
|
|
444
|
-
|
|
445
|
-
RECURRENCE_TYPE_CHOICES = [
|
|
446
|
-
('punctual', 'Punctual'), # Una o más veces en fechas específicas
|
|
447
|
-
('periodic', 'Periodic'), # Recurrente
|
|
448
|
-
]
|
|
449
|
-
|
|
450
|
-
# FKs
|
|
451
|
-
company = models.ForeignKey(Company, on_delete=models.CASCADE, db_index=True)
|
|
452
|
-
|
|
453
|
-
# Tipo de evento
|
|
454
|
-
event_type = models.CharField(max_length=50, choices=EVENT_TYPE_CHOICES)
|
|
455
|
-
|
|
456
|
-
# Info básica
|
|
457
|
-
title = models.CharField(max_length=255)
|
|
458
|
-
description = models.TextField(blank=True)
|
|
459
|
-
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
|
|
460
|
-
|
|
461
|
-
# Recurrencia
|
|
462
|
-
recurrence_type = models.CharField(
|
|
463
|
-
max_length=20,
|
|
464
|
-
choices=RECURRENCE_TYPE_CHOICES,
|
|
465
|
-
help_text="Tipo de recurrencia: puntual (fechas específicas) o periódico"
|
|
466
|
-
)
|
|
467
|
-
recurrence_config = models.JSONField(
|
|
468
|
-
help_text="Configuración de recurrencia en formato JSON"
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
# Configuración específica del tipo de evento
|
|
472
|
-
config = models.JSONField(
|
|
473
|
-
default=dict,
|
|
474
|
-
help_text="Configuración específica según event_type (mensaje, URL, etc.)"
|
|
475
|
-
)
|
|
476
|
-
|
|
477
|
-
# Control de ejecución
|
|
478
|
-
next_execution_at = models.DateTimeField(
|
|
479
|
-
null=True,
|
|
480
|
-
blank=True,
|
|
481
|
-
db_index=True,
|
|
482
|
-
help_text="Próxima fecha/hora de ejecución calculada"
|
|
483
|
-
)
|
|
484
|
-
last_executed_at = models.DateTimeField(
|
|
485
|
-
null=True,
|
|
486
|
-
blank=True,
|
|
487
|
-
help_text="Última vez que se ejecutó"
|
|
488
|
-
)
|
|
489
|
-
execution_count = models.IntegerField(
|
|
490
|
-
default=0,
|
|
491
|
-
help_text="Cantidad de veces que se ha ejecutado"
|
|
492
|
-
)
|
|
493
|
-
|
|
494
|
-
# Auditoría
|
|
495
|
-
created_by = models.ForeignKey(
|
|
496
|
-
User,
|
|
497
|
-
on_delete=models.CASCADE,
|
|
498
|
-
related_name='created_events'
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
class Meta:
|
|
502
|
-
db_table = '"automations"."events"'
|
|
503
|
-
app_label = 'constec_db'
|
|
504
|
-
indexes = [
|
|
505
|
-
models.Index(fields=['company', 'event_type']),
|
|
506
|
-
models.Index(fields=['status', 'next_execution_at']),
|
|
507
|
-
models.Index(fields=['recurrence_type']),
|
|
508
|
-
]
|
|
509
|
-
ordering = ['next_execution_at']
|
|
510
|
-
|
|
511
|
-
def __str__(self):
|
|
512
|
-
return f"{self.event_type}: {self.title}"
|
|
485
|
+
@property
|
|
486
|
+
def creator(self):
|
|
487
|
+
"""Helper to access creator regardless of type."""
|
|
488
|
+
return self.created_by_user or self.created_by_org_user
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from django.db import models
|
|
2
2
|
from .base import UUIDModel
|
|
3
3
|
from .company import Company
|
|
4
|
-
from .user import
|
|
4
|
+
from .user import CompanyUser
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class UserGroup(UUIDModel):
|
|
@@ -23,7 +23,7 @@ class UserGroup(UUIDModel):
|
|
|
23
23
|
blank=True
|
|
24
24
|
)
|
|
25
25
|
users = models.ManyToManyField(
|
|
26
|
-
|
|
26
|
+
CompanyUser,
|
|
27
27
|
related_name="groups"
|
|
28
28
|
)
|
|
29
29
|
|
|
@@ -3,7 +3,7 @@ from .base import UUIDModel
|
|
|
3
3
|
from .company import Company
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
class
|
|
6
|
+
class CompanyUser(UUIDModel):
|
|
7
7
|
"""User belonging to a company."""
|
|
8
8
|
company = models.ForeignKey(
|
|
9
9
|
Company,
|
|
@@ -16,13 +16,13 @@ class User(UUIDModel):
|
|
|
16
16
|
|
|
17
17
|
class Meta:
|
|
18
18
|
app_label = 'constec_db'
|
|
19
|
-
db_table = 'core"."
|
|
19
|
+
db_table = 'core"."company_users'
|
|
20
20
|
|
|
21
21
|
def __str__(self):
|
|
22
22
|
return f"{self.name} ({self.email})"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
25
|
+
class CompanyUserRole(UUIDModel):
|
|
26
26
|
"""Role within a company."""
|
|
27
27
|
company = models.ForeignKey(
|
|
28
28
|
Company,
|
|
@@ -38,17 +38,17 @@ class UserRole(UUIDModel):
|
|
|
38
38
|
|
|
39
39
|
class Meta:
|
|
40
40
|
app_label = 'constec_db'
|
|
41
|
-
db_table = 'core"."
|
|
41
|
+
db_table = 'core"."company_user_roles'
|
|
42
42
|
unique_together = [['company', 'name']]
|
|
43
43
|
|
|
44
44
|
def __str__(self):
|
|
45
45
|
return f"{self.name}"
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
class
|
|
49
|
-
"""Cross-company access: one
|
|
48
|
+
class CompanyUserAccess(UUIDModel):
|
|
49
|
+
"""Cross-company access: one CompanyUser can access multiple Companies."""
|
|
50
50
|
user = models.ForeignKey(
|
|
51
|
-
|
|
51
|
+
CompanyUser,
|
|
52
52
|
on_delete=models.CASCADE,
|
|
53
53
|
related_name="company_accesses",
|
|
54
54
|
)
|
|
@@ -58,16 +58,16 @@ class UserCompanyAccess(UUIDModel):
|
|
|
58
58
|
related_name="user_accesses",
|
|
59
59
|
)
|
|
60
60
|
role = models.ForeignKey(
|
|
61
|
-
|
|
61
|
+
CompanyUserRole,
|
|
62
62
|
on_delete=models.PROTECT,
|
|
63
63
|
related_name="user_accesses",
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
class Meta:
|
|
67
67
|
app_label = 'constec_db'
|
|
68
|
-
db_table = 'core"."
|
|
69
|
-
verbose_name = '
|
|
70
|
-
verbose_name_plural = '
|
|
68
|
+
db_table = 'core"."company_user_access'
|
|
69
|
+
verbose_name = 'Company user access'
|
|
70
|
+
verbose_name_plural = 'Company user access'
|
|
71
71
|
unique_together = [['user', 'company']]
|
|
72
72
|
|
|
73
73
|
def __str__(self):
|
|
@@ -16,6 +16,8 @@ constec/db/migrations/0004_rename_entities_company_cuit_idx_entities_company_e2c
|
|
|
16
16
|
constec/db/migrations/0005_event.py
|
|
17
17
|
constec/db/migrations/0006_automation_trigger_action_executionlog_notificationtemplate.py
|
|
18
18
|
constec/db/migrations/0007_add_organization_to_automations.py
|
|
19
|
+
constec/db/migrations/0008_refactor_creator_fields.py
|
|
20
|
+
constec/db/migrations/0009_rename_user_to_companyuser.py
|
|
19
21
|
constec/db/migrations/__init__.py
|
|
20
22
|
constec/db/models/__init__.py
|
|
21
23
|
constec/db/models/automation.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "constec"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.0"
|
|
8
8
|
description = "Base library for the Constec ecosystem - shared utilities, models, and namespace foundation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{constec-0.5.3 → constec-0.7.0}/constec/db/migrations/0007_add_organization_to_automations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|