wbcrm 2.2.1__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.
Potentially problematic release.
This version of wbcrm might be problematic. Click here for more details.
- wbcrm-2.2.1/.gitignore +181 -0
- wbcrm-2.2.1/PKG-INFO +11 -0
- wbcrm-2.2.1/pyproject.toml +34 -0
- wbcrm-2.2.1/wbcrm/__init__.py +1 -0
- wbcrm-2.2.1/wbcrm/admin/__init__.py +4 -0
- wbcrm-2.2.1/wbcrm/admin/accounts.py +59 -0
- wbcrm-2.2.1/wbcrm/admin/activities.py +101 -0
- wbcrm-2.2.1/wbcrm/admin/groups.py +7 -0
- wbcrm-2.2.1/wbcrm/admin/products.py +8 -0
- wbcrm-2.2.1/wbcrm/apps.py +5 -0
- wbcrm-2.2.1/wbcrm/configurations/__init__.py +1 -0
- wbcrm-2.2.1/wbcrm/configurations/base.py +16 -0
- wbcrm-2.2.1/wbcrm/dynamic_preferences_registry.py +38 -0
- wbcrm-2.2.1/wbcrm/factories/__init__.py +14 -0
- wbcrm-2.2.1/wbcrm/factories/accounts.py +56 -0
- wbcrm-2.2.1/wbcrm/factories/activities.py +125 -0
- wbcrm-2.2.1/wbcrm/factories/groups.py +23 -0
- wbcrm-2.2.1/wbcrm/factories/products.py +10 -0
- wbcrm-2.2.1/wbcrm/filters/__init__.py +10 -0
- wbcrm-2.2.1/wbcrm/filters/accounts.py +67 -0
- wbcrm-2.2.1/wbcrm/filters/activities.py +181 -0
- wbcrm-2.2.1/wbcrm/filters/groups.py +20 -0
- wbcrm-2.2.1/wbcrm/filters/products.py +37 -0
- wbcrm-2.2.1/wbcrm/filters/signals.py +94 -0
- wbcrm-2.2.1/wbcrm/fixtures/wbcrm.json +1215 -0
- wbcrm-2.2.1/wbcrm/kpi_handlers/activities.py +171 -0
- wbcrm-2.2.1/wbcrm/locale/de/LC_MESSAGES/django.po +1538 -0
- wbcrm-2.2.1/wbcrm/migrations/0001_initial_squashed_squashed_0032_productcompanyrelationship_alter_product_prospects_and_more.py +3948 -0
- wbcrm-2.2.1/wbcrm/migrations/0002_alter_activity_repeat_choice.py +32 -0
- wbcrm-2.2.1/wbcrm/migrations/0003_remove_activity_external_id_and_more.py +63 -0
- wbcrm-2.2.1/wbcrm/migrations/0004_alter_activity_status.py +28 -0
- wbcrm-2.2.1/wbcrm/migrations/0005_account_accountrole_accountroletype_and_more.py +182 -0
- wbcrm-2.2.1/wbcrm/migrations/0006_alter_activity_location.py +17 -0
- wbcrm-2.2.1/wbcrm/migrations/0007_alter_account_status.py +23 -0
- wbcrm-2.2.1/wbcrm/migrations/0008_alter_activity_options.py +16 -0
- wbcrm-2.2.1/wbcrm/migrations/0009_alter_account_is_public.py +19 -0
- wbcrm-2.2.1/wbcrm/migrations/0010_alter_account_reference_id.py +17 -0
- wbcrm-2.2.1/wbcrm/migrations/0011_activity_summary.py +22 -0
- wbcrm-2.2.1/wbcrm/migrations/0012_alter_activity_summary.py +17 -0
- wbcrm-2.2.1/wbcrm/migrations/0013_account_action_plan_account_relationship_status_and_more.py +34 -0
- wbcrm-2.2.1/wbcrm/migrations/0014_alter_account_relationship_status.py +24 -0
- wbcrm-2.2.1/wbcrm/migrations/0015_alter_activity_type.py +23 -0
- wbcrm-2.2.1/wbcrm/migrations/0016_auto_20241205_1015.py +106 -0
- wbcrm-2.2.1/wbcrm/migrations/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/models/__init__.py +4 -0
- wbcrm-2.2.1/wbcrm/models/accounts.py +637 -0
- wbcrm-2.2.1/wbcrm/models/activities.py +1335 -0
- wbcrm-2.2.1/wbcrm/models/groups.py +118 -0
- wbcrm-2.2.1/wbcrm/models/llm/activity_summaries.py +33 -0
- wbcrm-2.2.1/wbcrm/models/llm/analyze_relationship.py +54 -0
- wbcrm-2.2.1/wbcrm/models/products.py +83 -0
- wbcrm-2.2.1/wbcrm/models/recurrence.py +279 -0
- wbcrm-2.2.1/wbcrm/preferences.py +14 -0
- wbcrm-2.2.1/wbcrm/report/activity_report.py +110 -0
- wbcrm-2.2.1/wbcrm/serializers/__init__.py +23 -0
- wbcrm-2.2.1/wbcrm/serializers/accounts.py +126 -0
- wbcrm-2.2.1/wbcrm/serializers/activities.py +526 -0
- wbcrm-2.2.1/wbcrm/serializers/groups.py +30 -0
- wbcrm-2.2.1/wbcrm/serializers/products.py +57 -0
- wbcrm-2.2.1/wbcrm/serializers/recurrence.py +90 -0
- wbcrm-2.2.1/wbcrm/serializers/signals.py +70 -0
- wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/activity.md +86 -0
- wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/activitytype.md +20 -0
- wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/group.md +2 -0
- wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/product.md +11 -0
- wbcrm-2.2.1/wbcrm/synchronization/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/admin.py +72 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backend.py +207 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/__init__.py +2 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/google_calendar_backend.py +399 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/__init__.py +16 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/create.py +75 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/delete.py +78 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/update.py +155 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/internal_to_external/update.py +180 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tasks.py +21 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/conftest.py +1 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/test_data.py +81 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/test_google_backend.py +319 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/test_utils.py +274 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/typing_informations.py +139 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/utils.py +216 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/backend.py +576 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/msgraph.py +438 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/parser.py +423 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/conftest.py +1 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/fixtures.py +606 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_admin.py +117 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_backend.py +269 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_controller.py +237 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_parser.py +173 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/controller.py +545 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/dynamic_preferences_registry.py +107 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/preferences.py +21 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/shortcuts.py +9 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/signals.py +28 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/tasks.py +21 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/urls.py +6 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/utils.py +46 -0
- wbcrm-2.2.1/wbcrm/synchronization/activity/views.py +37 -0
- wbcrm-2.2.1/wbcrm/synchronization/admin.py +1 -0
- wbcrm-2.2.1/wbcrm/synchronization/apps.py +15 -0
- wbcrm-2.2.1/wbcrm/synchronization/dynamic_preferences_registry.py +1 -0
- wbcrm-2.2.1/wbcrm/synchronization/management.py +36 -0
- wbcrm-2.2.1/wbcrm/synchronization/tasks.py +1 -0
- wbcrm-2.2.1/wbcrm/synchronization/urls.py +5 -0
- wbcrm-2.2.1/wbcrm/tasks.py +312 -0
- wbcrm-2.2.1/wbcrm/templates/email/activity.html +98 -0
- wbcrm-2.2.1/wbcrm/templates/email/activity_report.html +6 -0
- wbcrm-2.2.1/wbcrm/templates/email/daily_summary.html +72 -0
- wbcrm-2.2.1/wbcrm/templates/email/global_daily_summary.html +85 -0
- wbcrm-2.2.1/wbcrm/tests/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/tests/accounts/__init__.py +0 -0
- wbcrm-2.2.1/wbcrm/tests/accounts/test_models.py +380 -0
- wbcrm-2.2.1/wbcrm/tests/accounts/test_viewsets.py +87 -0
- wbcrm-2.2.1/wbcrm/tests/conftest.py +76 -0
- wbcrm-2.2.1/wbcrm/tests/disable_signals.py +52 -0
- wbcrm-2.2.1/wbcrm/tests/e2e/__init__.py +1 -0
- wbcrm-2.2.1/wbcrm/tests/e2e/e2e_wbcrm_utility.py +82 -0
- wbcrm-2.2.1/wbcrm/tests/e2e/test_e2e.py +369 -0
- wbcrm-2.2.1/wbcrm/tests/test_assignee_methods.py +39 -0
- wbcrm-2.2.1/wbcrm/tests/test_chartviewsets.py +111 -0
- wbcrm-2.2.1/wbcrm/tests/test_dto.py +63 -0
- wbcrm-2.2.1/wbcrm/tests/test_filters.py +51 -0
- wbcrm-2.2.1/wbcrm/tests/test_models.py +216 -0
- wbcrm-2.2.1/wbcrm/tests/test_recurrence.py +291 -0
- wbcrm-2.2.1/wbcrm/tests/test_report.py +20 -0
- wbcrm-2.2.1/wbcrm/tests/test_serializers.py +170 -0
- wbcrm-2.2.1/wbcrm/tests/test_tasks.py +94 -0
- wbcrm-2.2.1/wbcrm/tests/test_viewsets.py +967 -0
- wbcrm-2.2.1/wbcrm/tests/tests.py +120 -0
- wbcrm-2.2.1/wbcrm/typings.py +107 -0
- wbcrm-2.2.1/wbcrm/urls.py +67 -0
- wbcrm-2.2.1/wbcrm/viewsets/__init__.py +22 -0
- wbcrm-2.2.1/wbcrm/viewsets/accounts.py +121 -0
- wbcrm-2.2.1/wbcrm/viewsets/activities.py +315 -0
- wbcrm-2.2.1/wbcrm/viewsets/buttons/__init__.py +7 -0
- wbcrm-2.2.1/wbcrm/viewsets/buttons/accounts.py +27 -0
- wbcrm-2.2.1/wbcrm/viewsets/buttons/activities.py +68 -0
- wbcrm-2.2.1/wbcrm/viewsets/buttons/signals.py +17 -0
- wbcrm-2.2.1/wbcrm/viewsets/display/__init__.py +12 -0
- wbcrm-2.2.1/wbcrm/viewsets/display/accounts.py +110 -0
- wbcrm-2.2.1/wbcrm/viewsets/display/activities.py +443 -0
- wbcrm-2.2.1/wbcrm/viewsets/display/groups.py +22 -0
- wbcrm-2.2.1/wbcrm/viewsets/display/products.py +105 -0
- wbcrm-2.2.1/wbcrm/viewsets/endpoints/__init__.py +8 -0
- wbcrm-2.2.1/wbcrm/viewsets/endpoints/accounts.py +32 -0
- wbcrm-2.2.1/wbcrm/viewsets/endpoints/activities.py +30 -0
- wbcrm-2.2.1/wbcrm/viewsets/endpoints/groups.py +7 -0
- wbcrm-2.2.1/wbcrm/viewsets/endpoints/products.py +9 -0
- wbcrm-2.2.1/wbcrm/viewsets/groups.py +37 -0
- wbcrm-2.2.1/wbcrm/viewsets/menu/__init__.py +8 -0
- wbcrm-2.2.1/wbcrm/viewsets/menu/accounts.py +18 -0
- wbcrm-2.2.1/wbcrm/viewsets/menu/activities.py +61 -0
- wbcrm-2.2.1/wbcrm/viewsets/menu/groups.py +16 -0
- wbcrm-2.2.1/wbcrm/viewsets/menu/products.py +20 -0
- wbcrm-2.2.1/wbcrm/viewsets/mixins.py +34 -0
- wbcrm-2.2.1/wbcrm/viewsets/previews/__init__.py +1 -0
- wbcrm-2.2.1/wbcrm/viewsets/previews/activities.py +10 -0
- wbcrm-2.2.1/wbcrm/viewsets/products.py +56 -0
- wbcrm-2.2.1/wbcrm/viewsets/recurrence.py +26 -0
- wbcrm-2.2.1/wbcrm/viewsets/titles/__init__.py +13 -0
- wbcrm-2.2.1/wbcrm/viewsets/titles/accounts.py +22 -0
- wbcrm-2.2.1/wbcrm/viewsets/titles/activities.py +61 -0
- wbcrm-2.2.1/wbcrm/viewsets/titles/products.py +13 -0
- wbcrm-2.2.1/wbcrm/viewsets/titles/utils.py +46 -0
- wbcrm-2.2.1/wbcrm/workflows/__init__.py +1 -0
- wbcrm-2.2.1/wbcrm/workflows/assignee_methods.py +25 -0
wbcrm-2.2.1/.gitignore
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
~
|
|
2
|
+
|
|
3
|
+
# Docker volumes
|
|
4
|
+
volumes/
|
|
5
|
+
# Poetry auth file
|
|
6
|
+
auth.toml
|
|
7
|
+
|
|
8
|
+
media/*
|
|
9
|
+
media/
|
|
10
|
+
mediafiles/
|
|
11
|
+
mediafiles/*
|
|
12
|
+
test/*
|
|
13
|
+
staticfiles/*
|
|
14
|
+
staticfiles/
|
|
15
|
+
#
|
|
16
|
+
# Byte-compiled / optimized / DLL files
|
|
17
|
+
__pycache__/
|
|
18
|
+
*.py[cod]
|
|
19
|
+
*$py.class
|
|
20
|
+
|
|
21
|
+
# C extensions
|
|
22
|
+
*.so
|
|
23
|
+
|
|
24
|
+
# Distribution / packaging
|
|
25
|
+
.Python
|
|
26
|
+
build/
|
|
27
|
+
develop-eggs/
|
|
28
|
+
dist/
|
|
29
|
+
info/
|
|
30
|
+
downloads/
|
|
31
|
+
eggs/
|
|
32
|
+
.eggs/
|
|
33
|
+
lib/
|
|
34
|
+
lib64/
|
|
35
|
+
parts/
|
|
36
|
+
sdist/
|
|
37
|
+
var/
|
|
38
|
+
wheels/
|
|
39
|
+
share/python-wheels/
|
|
40
|
+
*.egg-info/
|
|
41
|
+
.installed.cfg
|
|
42
|
+
*.egg
|
|
43
|
+
MANIFEST
|
|
44
|
+
|
|
45
|
+
# PyInstaller
|
|
46
|
+
# Usually these files are written by a python script from a template
|
|
47
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
48
|
+
*.manifest
|
|
49
|
+
*.spec
|
|
50
|
+
|
|
51
|
+
# Installer logs
|
|
52
|
+
pip-log.txt
|
|
53
|
+
pip-delete-this-directory.txt
|
|
54
|
+
|
|
55
|
+
# Unit test / coverage reports
|
|
56
|
+
htmlcov/
|
|
57
|
+
.tox/
|
|
58
|
+
.nox/
|
|
59
|
+
.coverage
|
|
60
|
+
.coverage.*
|
|
61
|
+
.cache
|
|
62
|
+
.dccache
|
|
63
|
+
nosetests.xml
|
|
64
|
+
coverage.xml
|
|
65
|
+
*.cover
|
|
66
|
+
*.py,cover
|
|
67
|
+
.hypothesis/
|
|
68
|
+
.pytest_cache/
|
|
69
|
+
cover/
|
|
70
|
+
report.xml
|
|
71
|
+
*/report.xml
|
|
72
|
+
|
|
73
|
+
# Translations
|
|
74
|
+
*.mo
|
|
75
|
+
*.pot
|
|
76
|
+
|
|
77
|
+
# Django stuff:
|
|
78
|
+
*.log
|
|
79
|
+
local_settings.py
|
|
80
|
+
*.sqlite3
|
|
81
|
+
db.sqlite3-journal
|
|
82
|
+
|
|
83
|
+
# Flask stuff:
|
|
84
|
+
instance/
|
|
85
|
+
.webassets-cache
|
|
86
|
+
|
|
87
|
+
# Scrapy stuff:
|
|
88
|
+
.scrapy
|
|
89
|
+
|
|
90
|
+
# Sphinx documentation
|
|
91
|
+
docs/_build/
|
|
92
|
+
|
|
93
|
+
# PyBuilder
|
|
94
|
+
.pybuilder/
|
|
95
|
+
target/
|
|
96
|
+
|
|
97
|
+
# Jupyter Notebook
|
|
98
|
+
.ipynb_checkpoints
|
|
99
|
+
*.ipynb
|
|
100
|
+
# IPython
|
|
101
|
+
profile_default/
|
|
102
|
+
ipython_config.py
|
|
103
|
+
|
|
104
|
+
# pyenv
|
|
105
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
106
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
107
|
+
# .python-version
|
|
108
|
+
|
|
109
|
+
# pipenv
|
|
110
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
111
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
112
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
113
|
+
# install all needed dependencies.
|
|
114
|
+
#Pipfile.lock
|
|
115
|
+
|
|
116
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
117
|
+
__pypackages__/
|
|
118
|
+
|
|
119
|
+
# Celery stuff
|
|
120
|
+
celerybeat-schedule
|
|
121
|
+
celerybeat.pid
|
|
122
|
+
|
|
123
|
+
# SageMath parsed files
|
|
124
|
+
*.sage.py
|
|
125
|
+
|
|
126
|
+
# Environments
|
|
127
|
+
.env
|
|
128
|
+
.envrc
|
|
129
|
+
.venv
|
|
130
|
+
env/
|
|
131
|
+
venv/
|
|
132
|
+
ENV/
|
|
133
|
+
env.bak/
|
|
134
|
+
venv.bak/
|
|
135
|
+
.vscode/
|
|
136
|
+
.idea/
|
|
137
|
+
.idea.bkp/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
crm/
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# Gitlab Runner
|
|
164
|
+
builds
|
|
165
|
+
builds/
|
|
166
|
+
|
|
167
|
+
# Integrator Office 365 : reverse proxy tunnel for outlook365
|
|
168
|
+
ngrok
|
|
169
|
+
*/ngrok
|
|
170
|
+
/modules/**/system/
|
|
171
|
+
|
|
172
|
+
/modules/wbmailing/files/*
|
|
173
|
+
/modules/wbmailing/mailing/*
|
|
174
|
+
|
|
175
|
+
/projects/*/requirements.txt
|
|
176
|
+
public
|
|
177
|
+
|
|
178
|
+
# Ignore archive localization generated folder
|
|
179
|
+
backend/modules/**/archive/*
|
|
180
|
+
**/**/requirements.txt
|
|
181
|
+
CHANGELOG-*
|
wbcrm-2.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: wbcrm
|
|
3
|
+
Version: 2.2.1
|
|
4
|
+
Summary: A workbench module that contains all the functionality related to a customer relationship management.
|
|
5
|
+
Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
|
|
6
|
+
Requires-Dist: django-eventtools==1.*
|
|
7
|
+
Requires-Dist: google-api-python-client==2.62.*
|
|
8
|
+
Requires-Dist: google-auth-httplib2==0.1.*
|
|
9
|
+
Requires-Dist: google-auth-oauthlib==0.5.*
|
|
10
|
+
Requires-Dist: wbcore
|
|
11
|
+
Requires-Dist: xlsxwriter==3.*
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "wbcrm"
|
|
3
|
+
description = "A workbench module that contains all the functionality related to a customer relationship management."
|
|
4
|
+
authors = [{ name = "Christopher Wittlinger", email = "c.wittlinger@stainly.com"}]
|
|
5
|
+
dynamic = ["version"]
|
|
6
|
+
|
|
7
|
+
dependencies = [
|
|
8
|
+
"wbcore",
|
|
9
|
+
"django-eventtools == 1.*",
|
|
10
|
+
"google-api-python-client == 2.62.*",
|
|
11
|
+
"google-auth-httplib2 == 0.1.*",
|
|
12
|
+
"google-auth-oauthlib == 0.5.*",
|
|
13
|
+
"XlsxWriter == 3.*"
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[tool.uv.sources]
|
|
17
|
+
wbcore = { workspace = true }
|
|
18
|
+
|
|
19
|
+
[tool.uv]
|
|
20
|
+
package = true
|
|
21
|
+
|
|
22
|
+
[tool.hatch.version]
|
|
23
|
+
path = "../../pyproject.toml"
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.sdist]
|
|
26
|
+
include = ["wbcrm/*"]
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["wbcrm"]
|
|
30
|
+
only-packages = true
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["hatchling"]
|
|
34
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from mptt.admin import MPTTModelAdmin
|
|
3
|
+
from wbcrm.models.accounts import (
|
|
4
|
+
Account,
|
|
5
|
+
AccountRole,
|
|
6
|
+
AccountRoleType,
|
|
7
|
+
AccountRoleValidity,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AccountRoleValidityTabularInline(admin.TabularInline):
|
|
12
|
+
model = AccountRoleValidity
|
|
13
|
+
fields = ("timespan",)
|
|
14
|
+
extra = 0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AccountRoleTabularInline(admin.TabularInline):
|
|
18
|
+
model = AccountRole
|
|
19
|
+
fields = ("role_type", "entry", "weighting", "is_hidden")
|
|
20
|
+
autocomplete_fields = ["entry", "account"]
|
|
21
|
+
extra = 0
|
|
22
|
+
show_change_link = True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AccountTabularInline(admin.TabularInline):
|
|
26
|
+
model = Account
|
|
27
|
+
fields = ("title", "status", "owner", "is_terminal_account", "is_active", "is_public")
|
|
28
|
+
autocomplete_fields = ["owner", "parent"]
|
|
29
|
+
fk_name = "parent"
|
|
30
|
+
show_change_link = True
|
|
31
|
+
extra = 0
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@admin.register(AccountRoleType)
|
|
35
|
+
class AccountRoleTypeModelAdmin(admin.ModelAdmin):
|
|
36
|
+
list_display = ("title", "key")
|
|
37
|
+
search_fields = ("title", "key")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@admin.register(AccountRole)
|
|
41
|
+
class AccountRoleModelAdmin(admin.ModelAdmin):
|
|
42
|
+
list_display = ("role_type", "entry", "account", "is_hidden")
|
|
43
|
+
inlines = [AccountRoleValidityTabularInline]
|
|
44
|
+
|
|
45
|
+
def get_queryset(self, request):
|
|
46
|
+
return super().get_queryset(request).select_related("entry", "account")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@admin.register(Account)
|
|
50
|
+
class AccountModelAdmin(MPTTModelAdmin):
|
|
51
|
+
mptt_level_indent = 20
|
|
52
|
+
fsm_field = ["status"]
|
|
53
|
+
search_fields = ["title"]
|
|
54
|
+
list_display = ["computed_str", "status", "is_active", "is_terminal_account", "is_public", "owner"]
|
|
55
|
+
inlines = [AccountRoleTabularInline, AccountTabularInline]
|
|
56
|
+
autocomplete_fields = ["owner", "parent"]
|
|
57
|
+
|
|
58
|
+
def get_queryset(self, request):
|
|
59
|
+
return Account.all_objects.select_related("owner", "parent")
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
|
+
from reversion.admin import VersionAdmin
|
|
4
|
+
from wbcrm.models import Activity, ActivityParticipant, ActivityType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ParticipantInline(admin.TabularInline):
|
|
8
|
+
model = ActivityParticipant
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CompanyInline(admin.TabularInline):
|
|
12
|
+
model = ActivityParticipant
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@admin.register(ActivityType)
|
|
16
|
+
class ActivityTypeAdmin(admin.ModelAdmin):
|
|
17
|
+
search_fields = ("title",)
|
|
18
|
+
list_display = ("id", "title")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ActivityInline(admin.StackedInline):
|
|
22
|
+
model = Activity
|
|
23
|
+
fk_name = "parent_occurrence"
|
|
24
|
+
extra = 0
|
|
25
|
+
fieldsets = (
|
|
26
|
+
(
|
|
27
|
+
_("Main information"),
|
|
28
|
+
{
|
|
29
|
+
"fields": (
|
|
30
|
+
"title",
|
|
31
|
+
"description",
|
|
32
|
+
)
|
|
33
|
+
},
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@admin.register(Activity)
|
|
39
|
+
class ActivityAdmin(VersionAdmin):
|
|
40
|
+
search_fields = ("title",)
|
|
41
|
+
list_display = ("id", "status", "title", "period", "is_active", "parent_occurrence_id", "metadata")
|
|
42
|
+
fieldsets = (
|
|
43
|
+
(_("Main information"), {"fields": ("title", "description", "result", "creator")}),
|
|
44
|
+
(_("Meta"), {"fields": ("status", "type", "visibility", "is_active", "metadata")}),
|
|
45
|
+
(_("Temporal Information"), {"fields": ("period", "all_day")}),
|
|
46
|
+
(_("Geographical Information"), {"fields": ("location", "location_longitude", "location_latitude")}),
|
|
47
|
+
(_("Linked Entries"), {"fields": ("assigned_to", "groups")}),
|
|
48
|
+
(_("Linked Activities"), {"fields": ("preceded_by",)}),
|
|
49
|
+
(
|
|
50
|
+
_("Recurrence"),
|
|
51
|
+
{
|
|
52
|
+
"fields": (
|
|
53
|
+
"repeat_choice",
|
|
54
|
+
"parent_occurrence",
|
|
55
|
+
("recurrence_count", "recurrence_end"),
|
|
56
|
+
"propagate_for_all_children",
|
|
57
|
+
)
|
|
58
|
+
},
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
raw_id_fields = (
|
|
63
|
+
"assigned_to",
|
|
64
|
+
"participants",
|
|
65
|
+
"groups",
|
|
66
|
+
"preceded_by",
|
|
67
|
+
"creator",
|
|
68
|
+
"latest_reviewer",
|
|
69
|
+
"parent_occurrence",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
inlines = [ActivityInline, ParticipantInline, CompanyInline]
|
|
73
|
+
|
|
74
|
+
def reversion_register(self, model, **options):
|
|
75
|
+
options = {
|
|
76
|
+
"exclude": (
|
|
77
|
+
"created",
|
|
78
|
+
"creator",
|
|
79
|
+
"edited",
|
|
80
|
+
),
|
|
81
|
+
}
|
|
82
|
+
super().reversion_register(model, **options)
|
|
83
|
+
|
|
84
|
+
def get_queryset(self, request):
|
|
85
|
+
return Activity.all_objects.all()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@admin.register(ActivityParticipant)
|
|
89
|
+
class ActivityParticipantAdmin(VersionAdmin):
|
|
90
|
+
search_fields = ("activity", "participant")
|
|
91
|
+
list_display = ("id", "activity", "participant", "participation_status")
|
|
92
|
+
|
|
93
|
+
def reversion_register(self, model, **options):
|
|
94
|
+
options = {
|
|
95
|
+
"follow": (
|
|
96
|
+
"activity",
|
|
97
|
+
"participant",
|
|
98
|
+
"participation_status",
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
super().reversion_register(model, **options)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .base import CRMDevBaseConfiguration, CRMProductionBaseConfiguration
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from configurations import values
|
|
2
|
+
from wbcore.configurations import DevBaseConfiguration, ProductionBaseConfiguration
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CRMDevBaseConfiguration(DevBaseConfiguration):
|
|
6
|
+
SECRET_KEY = values.Value("THIS-IS-NOT-A-SECRET-KEY", environ_prefix=None)
|
|
7
|
+
DEBUG = values.BooleanValue(True, environ_prefix=None)
|
|
8
|
+
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
|
9
|
+
DISABLE_NOTIFICATION = values.BooleanValue(False, environ_prefix=None)
|
|
10
|
+
DEV_USER = values.Value(None, environ_prefix=None)
|
|
11
|
+
ADD_REVERSION_ADMIN = True
|
|
12
|
+
DEFAULT_CREATE_ENDPOINT_BASENAME = values.Value("wbcrm:activity-list", environ_prefix=None)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CRMProductionBaseConfiguration(ProductionBaseConfiguration):
|
|
16
|
+
pass
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from django.utils.translation import gettext as _
|
|
2
|
+
from dynamic_preferences.preferences import Section
|
|
3
|
+
from dynamic_preferences.registries import global_preferences_registry
|
|
4
|
+
from dynamic_preferences.types import BooleanPreference, IntegerPreference
|
|
5
|
+
|
|
6
|
+
general = Section("wbcrm")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@global_preferences_registry.register
|
|
10
|
+
class CheckForMandatoryParticipants(BooleanPreference):
|
|
11
|
+
section = general
|
|
12
|
+
name = "mandatory_participants"
|
|
13
|
+
default = True
|
|
14
|
+
|
|
15
|
+
verbose_name = _("Check for mandatory participants")
|
|
16
|
+
help_text = _(
|
|
17
|
+
'Determines whether or not companies must be entered as participants in the "Companies" field for activities.'
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@global_preferences_registry.register
|
|
22
|
+
class RecurrenceActivityEndDate(IntegerPreference):
|
|
23
|
+
section = general
|
|
24
|
+
name = "recurrence_maximum_allowed_days"
|
|
25
|
+
# this short default value, is just for the moment to test the process and until we know how to handle the recurring activities more efficient
|
|
26
|
+
default = 10 * 365
|
|
27
|
+
|
|
28
|
+
verbose_name = _("The default Maximum allowed days")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@global_preferences_registry.register
|
|
32
|
+
class RecurrenceActivityDateListLength(IntegerPreference):
|
|
33
|
+
section = general
|
|
34
|
+
name = "recurrence_maximum_count"
|
|
35
|
+
# this short default value, is just for the moment to test the process and until we know how to handle the recurring activities more efficient
|
|
36
|
+
default = 366
|
|
37
|
+
|
|
38
|
+
# verbose_name = _("For each date in the date list we create a child activity.number at which the date list will be cut.")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# from wbcore.contrib.currency.tests import factories
|
|
2
|
+
|
|
3
|
+
from .accounts import AccountFactory, AccountRoleFactory, AccountRoleTypeFactory, AccountWithOwnerFactory
|
|
4
|
+
from .activities import (
|
|
5
|
+
ActivityCompanyFactory,
|
|
6
|
+
ActivityFactory,
|
|
7
|
+
ActivityParticipantFactory,
|
|
8
|
+
ActivityPersonFactory,
|
|
9
|
+
ActivityTypeCALLFactory,
|
|
10
|
+
ActivityTypeFactory,
|
|
11
|
+
RecurringActivityFactory,
|
|
12
|
+
)
|
|
13
|
+
from .groups import GroupFactory
|
|
14
|
+
from .products import ProductFactory
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import factory
|
|
2
|
+
from slugify import slugify
|
|
3
|
+
from wbcrm.models.accounts import Account, AccountRole, AccountRoleType
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AccountFactory(factory.django.DjangoModelFactory):
|
|
7
|
+
title = factory.Faker("company")
|
|
8
|
+
status = Account.Status.OPEN
|
|
9
|
+
# owner =
|
|
10
|
+
is_public = True
|
|
11
|
+
is_active = True
|
|
12
|
+
is_terminal_account = True
|
|
13
|
+
|
|
14
|
+
class Meta:
|
|
15
|
+
model = Account
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AccountWithOwnerFactory(AccountFactory):
|
|
19
|
+
owner = factory.SubFactory("wbcore.contrib.directory.factories.entries.EntryFactory")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AccountRoleTypeFactory(factory.django.DjangoModelFactory):
|
|
23
|
+
title = factory.Faker("word")
|
|
24
|
+
key = factory.LazyAttribute(lambda o: slugify(o.title))
|
|
25
|
+
|
|
26
|
+
class Meta:
|
|
27
|
+
model = AccountRoleType
|
|
28
|
+
django_get_or_create = ["key"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AccountRoleFactory(factory.django.DjangoModelFactory):
|
|
32
|
+
role_type = factory.SubFactory("wbcrm.factories.AccountRoleTypeFactory")
|
|
33
|
+
entry = factory.SubFactory("wbcore.contrib.directory.factories.entries.EntryFactory")
|
|
34
|
+
account = factory.SubFactory("wbcrm.factories.AccountFactory", parent=None)
|
|
35
|
+
is_hidden = False
|
|
36
|
+
|
|
37
|
+
@factory.post_generation
|
|
38
|
+
def authorized_hidden_users(self, create, extracted, **kwargs):
|
|
39
|
+
if not create:
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
if extracted:
|
|
43
|
+
for user in extracted:
|
|
44
|
+
self.authorized_hidden_users.add(user)
|
|
45
|
+
|
|
46
|
+
@factory.post_generation
|
|
47
|
+
def visibility_daterange(self, create, extracted, **kwargs):
|
|
48
|
+
if not create:
|
|
49
|
+
return
|
|
50
|
+
if extracted:
|
|
51
|
+
v = self.validity_set.first()
|
|
52
|
+
v.timespan = extracted
|
|
53
|
+
v.save()
|
|
54
|
+
|
|
55
|
+
class Meta:
|
|
56
|
+
model = AccountRole
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
import factory
|
|
5
|
+
import pytz
|
|
6
|
+
from dynamic_preferences.registries import global_preferences_registry
|
|
7
|
+
from faker import Faker
|
|
8
|
+
from wbcore.contrib.authentication.factories import InternalUserFactory
|
|
9
|
+
from wbcore.contrib.directory.factories import CompanyFactory, PersonFactory
|
|
10
|
+
from wbcrm.models.activities import Activity, ActivityParticipant, ActivityType
|
|
11
|
+
|
|
12
|
+
fake = Faker()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ActivityTypeFactory(factory.django.DjangoModelFactory):
|
|
16
|
+
title = factory.Faker("text", max_nb_chars=32)
|
|
17
|
+
color = factory.Faker("color")
|
|
18
|
+
score = factory.Iterator([ActivityType.Score.HIGH, ActivityType.Score.MEDIUM, ActivityType.Score.LOW])
|
|
19
|
+
|
|
20
|
+
class Meta:
|
|
21
|
+
model = ActivityType
|
|
22
|
+
django_get_or_create = ("title",)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ActivityTypeCALLFactory(ActivityTypeFactory):
|
|
26
|
+
title = "Call"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ActivityFactory(factory.django.DjangoModelFactory):
|
|
30
|
+
class Meta:
|
|
31
|
+
model = Activity
|
|
32
|
+
|
|
33
|
+
title = factory.Faker("text", max_nb_chars=64)
|
|
34
|
+
description = factory.Faker("paragraph", nb_sentences=5)
|
|
35
|
+
start = factory.LazyAttribute(lambda _: fake.date_time(tzinfo=pytz.utc))
|
|
36
|
+
end = factory.LazyAttribute(
|
|
37
|
+
lambda _self: _self.start + datetime.timedelta(days=fake.pyint(0, 100), hours=fake.pyint(1, 23))
|
|
38
|
+
)
|
|
39
|
+
disable_participant_check = True
|
|
40
|
+
location = factory.Faker("local_latlng")
|
|
41
|
+
location_longitude = factory.Faker("longitude")
|
|
42
|
+
location_latitude = factory.Faker("latitude")
|
|
43
|
+
created = factory.Faker("date_time", tzinfo=pytz.utc)
|
|
44
|
+
creator = factory.LazyAttribute(lambda _: InternalUserFactory.create().profile)
|
|
45
|
+
latest_reviewer = factory.SubFactory(PersonFactory)
|
|
46
|
+
reviewed_at = factory.Faker("date_time", tzinfo=pytz.utc)
|
|
47
|
+
edited = factory.Faker("date_time", tzinfo=pytz.utc)
|
|
48
|
+
assigned_to = factory.SubFactory(PersonFactory)
|
|
49
|
+
preceded_by = None
|
|
50
|
+
parent_occurrence = None
|
|
51
|
+
propagate_for_all_children = False
|
|
52
|
+
recurrence_end = None
|
|
53
|
+
recurrence_count = None
|
|
54
|
+
repeat_choice = Activity.ReoccuranceChoice.NEVER
|
|
55
|
+
type = factory.SubFactory(ActivityTypeFactory)
|
|
56
|
+
item_type = "wbcrm.Activity"
|
|
57
|
+
|
|
58
|
+
@factory.post_generation
|
|
59
|
+
def participants(self, create, extracted, **kwargs):
|
|
60
|
+
if not create:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
if extracted:
|
|
64
|
+
for participant in extracted:
|
|
65
|
+
self.participants.add(participant)
|
|
66
|
+
|
|
67
|
+
@factory.post_generation
|
|
68
|
+
def companies(self, create, extracted, **kwargs):
|
|
69
|
+
if not create:
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if extracted:
|
|
73
|
+
for company in extracted:
|
|
74
|
+
self.companies.add(company)
|
|
75
|
+
|
|
76
|
+
@factory.post_generation
|
|
77
|
+
def groups(self, create, extracted, **kwargs):
|
|
78
|
+
if not create:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
if extracted:
|
|
82
|
+
for group in extracted:
|
|
83
|
+
self.groups.add(group)
|
|
84
|
+
|
|
85
|
+
@factory.post_generation
|
|
86
|
+
def entities(self, create, extracted, **kwargs):
|
|
87
|
+
if not create:
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
if extracted:
|
|
91
|
+
for entity in extracted:
|
|
92
|
+
self.entities.add(entity)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class RecurringActivityFactory(ActivityFactory):
|
|
96
|
+
repeat_choice = random.choice(list(filter(lambda x: x != "NEVER", Activity.ReoccuranceChoice.names)))
|
|
97
|
+
recurrence_count = 3
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ActivityCompanyFactory(ActivityFactory):
|
|
101
|
+
@factory.post_generation
|
|
102
|
+
def companies(self, create, extracted, **kwargs):
|
|
103
|
+
# Create company
|
|
104
|
+
company = CompanyFactory()
|
|
105
|
+
# Set global config main_company=company.id
|
|
106
|
+
global_preferences_registry.manager()["directory__main_company"] = company.id
|
|
107
|
+
global_preferences = global_preferences_registry.manager()
|
|
108
|
+
assert global_preferences
|
|
109
|
+
self.companies.add(company)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class ActivityPersonFactory(ActivityFactory):
|
|
113
|
+
@factory.post_generation
|
|
114
|
+
def participants(self, create, extracted, **kwargs):
|
|
115
|
+
# Create person
|
|
116
|
+
person = PersonFactory()
|
|
117
|
+
self.participants.add(person)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class ActivityParticipantFactory(factory.django.DjangoModelFactory):
|
|
121
|
+
class Meta:
|
|
122
|
+
model = ActivityParticipant
|
|
123
|
+
|
|
124
|
+
participant = factory.SubFactory(PersonFactory)
|
|
125
|
+
activity = factory.SubFactory(ActivityFactory)
|