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.

Files changed (173) hide show
  1. wbcrm-2.2.1/.gitignore +181 -0
  2. wbcrm-2.2.1/PKG-INFO +11 -0
  3. wbcrm-2.2.1/pyproject.toml +34 -0
  4. wbcrm-2.2.1/wbcrm/__init__.py +1 -0
  5. wbcrm-2.2.1/wbcrm/admin/__init__.py +4 -0
  6. wbcrm-2.2.1/wbcrm/admin/accounts.py +59 -0
  7. wbcrm-2.2.1/wbcrm/admin/activities.py +101 -0
  8. wbcrm-2.2.1/wbcrm/admin/groups.py +7 -0
  9. wbcrm-2.2.1/wbcrm/admin/products.py +8 -0
  10. wbcrm-2.2.1/wbcrm/apps.py +5 -0
  11. wbcrm-2.2.1/wbcrm/configurations/__init__.py +1 -0
  12. wbcrm-2.2.1/wbcrm/configurations/base.py +16 -0
  13. wbcrm-2.2.1/wbcrm/dynamic_preferences_registry.py +38 -0
  14. wbcrm-2.2.1/wbcrm/factories/__init__.py +14 -0
  15. wbcrm-2.2.1/wbcrm/factories/accounts.py +56 -0
  16. wbcrm-2.2.1/wbcrm/factories/activities.py +125 -0
  17. wbcrm-2.2.1/wbcrm/factories/groups.py +23 -0
  18. wbcrm-2.2.1/wbcrm/factories/products.py +10 -0
  19. wbcrm-2.2.1/wbcrm/filters/__init__.py +10 -0
  20. wbcrm-2.2.1/wbcrm/filters/accounts.py +67 -0
  21. wbcrm-2.2.1/wbcrm/filters/activities.py +181 -0
  22. wbcrm-2.2.1/wbcrm/filters/groups.py +20 -0
  23. wbcrm-2.2.1/wbcrm/filters/products.py +37 -0
  24. wbcrm-2.2.1/wbcrm/filters/signals.py +94 -0
  25. wbcrm-2.2.1/wbcrm/fixtures/wbcrm.json +1215 -0
  26. wbcrm-2.2.1/wbcrm/kpi_handlers/activities.py +171 -0
  27. wbcrm-2.2.1/wbcrm/locale/de/LC_MESSAGES/django.po +1538 -0
  28. wbcrm-2.2.1/wbcrm/migrations/0001_initial_squashed_squashed_0032_productcompanyrelationship_alter_product_prospects_and_more.py +3948 -0
  29. wbcrm-2.2.1/wbcrm/migrations/0002_alter_activity_repeat_choice.py +32 -0
  30. wbcrm-2.2.1/wbcrm/migrations/0003_remove_activity_external_id_and_more.py +63 -0
  31. wbcrm-2.2.1/wbcrm/migrations/0004_alter_activity_status.py +28 -0
  32. wbcrm-2.2.1/wbcrm/migrations/0005_account_accountrole_accountroletype_and_more.py +182 -0
  33. wbcrm-2.2.1/wbcrm/migrations/0006_alter_activity_location.py +17 -0
  34. wbcrm-2.2.1/wbcrm/migrations/0007_alter_account_status.py +23 -0
  35. wbcrm-2.2.1/wbcrm/migrations/0008_alter_activity_options.py +16 -0
  36. wbcrm-2.2.1/wbcrm/migrations/0009_alter_account_is_public.py +19 -0
  37. wbcrm-2.2.1/wbcrm/migrations/0010_alter_account_reference_id.py +17 -0
  38. wbcrm-2.2.1/wbcrm/migrations/0011_activity_summary.py +22 -0
  39. wbcrm-2.2.1/wbcrm/migrations/0012_alter_activity_summary.py +17 -0
  40. wbcrm-2.2.1/wbcrm/migrations/0013_account_action_plan_account_relationship_status_and_more.py +34 -0
  41. wbcrm-2.2.1/wbcrm/migrations/0014_alter_account_relationship_status.py +24 -0
  42. wbcrm-2.2.1/wbcrm/migrations/0015_alter_activity_type.py +23 -0
  43. wbcrm-2.2.1/wbcrm/migrations/0016_auto_20241205_1015.py +106 -0
  44. wbcrm-2.2.1/wbcrm/migrations/__init__.py +0 -0
  45. wbcrm-2.2.1/wbcrm/models/__init__.py +4 -0
  46. wbcrm-2.2.1/wbcrm/models/accounts.py +637 -0
  47. wbcrm-2.2.1/wbcrm/models/activities.py +1335 -0
  48. wbcrm-2.2.1/wbcrm/models/groups.py +118 -0
  49. wbcrm-2.2.1/wbcrm/models/llm/activity_summaries.py +33 -0
  50. wbcrm-2.2.1/wbcrm/models/llm/analyze_relationship.py +54 -0
  51. wbcrm-2.2.1/wbcrm/models/products.py +83 -0
  52. wbcrm-2.2.1/wbcrm/models/recurrence.py +279 -0
  53. wbcrm-2.2.1/wbcrm/preferences.py +14 -0
  54. wbcrm-2.2.1/wbcrm/report/activity_report.py +110 -0
  55. wbcrm-2.2.1/wbcrm/serializers/__init__.py +23 -0
  56. wbcrm-2.2.1/wbcrm/serializers/accounts.py +126 -0
  57. wbcrm-2.2.1/wbcrm/serializers/activities.py +526 -0
  58. wbcrm-2.2.1/wbcrm/serializers/groups.py +30 -0
  59. wbcrm-2.2.1/wbcrm/serializers/products.py +57 -0
  60. wbcrm-2.2.1/wbcrm/serializers/recurrence.py +90 -0
  61. wbcrm-2.2.1/wbcrm/serializers/signals.py +70 -0
  62. wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/activity.md +86 -0
  63. wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/activitytype.md +20 -0
  64. wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/group.md +2 -0
  65. wbcrm-2.2.1/wbcrm/static/wbcrm/markdown/documentation/product.md +11 -0
  66. wbcrm-2.2.1/wbcrm/synchronization/__init__.py +0 -0
  67. wbcrm-2.2.1/wbcrm/synchronization/activity/__init__.py +0 -0
  68. wbcrm-2.2.1/wbcrm/synchronization/activity/admin.py +72 -0
  69. wbcrm-2.2.1/wbcrm/synchronization/activity/backend.py +207 -0
  70. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/__init__.py +0 -0
  71. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/__init__.py +2 -0
  72. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/google_calendar_backend.py +399 -0
  73. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/__init__.py +16 -0
  74. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/create.py +75 -0
  75. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/delete.py +78 -0
  76. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/update.py +155 -0
  77. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/request_utils/internal_to_external/update.py +180 -0
  78. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tasks.py +21 -0
  79. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/__init__.py +0 -0
  80. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/conftest.py +1 -0
  81. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/test_data.py +81 -0
  82. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/test_google_backend.py +319 -0
  83. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/tests/test_utils.py +274 -0
  84. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/typing_informations.py +139 -0
  85. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/google/utils.py +216 -0
  86. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/__init__.py +0 -0
  87. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/backend.py +576 -0
  88. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/msgraph.py +438 -0
  89. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/parser.py +423 -0
  90. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/__init__.py +0 -0
  91. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/conftest.py +1 -0
  92. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/fixtures.py +606 -0
  93. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_admin.py +117 -0
  94. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_backend.py +269 -0
  95. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_controller.py +237 -0
  96. wbcrm-2.2.1/wbcrm/synchronization/activity/backends/outlook/tests/test_parser.py +173 -0
  97. wbcrm-2.2.1/wbcrm/synchronization/activity/controller.py +545 -0
  98. wbcrm-2.2.1/wbcrm/synchronization/activity/dynamic_preferences_registry.py +107 -0
  99. wbcrm-2.2.1/wbcrm/synchronization/activity/preferences.py +21 -0
  100. wbcrm-2.2.1/wbcrm/synchronization/activity/shortcuts.py +9 -0
  101. wbcrm-2.2.1/wbcrm/synchronization/activity/signals.py +28 -0
  102. wbcrm-2.2.1/wbcrm/synchronization/activity/tasks.py +21 -0
  103. wbcrm-2.2.1/wbcrm/synchronization/activity/urls.py +6 -0
  104. wbcrm-2.2.1/wbcrm/synchronization/activity/utils.py +46 -0
  105. wbcrm-2.2.1/wbcrm/synchronization/activity/views.py +37 -0
  106. wbcrm-2.2.1/wbcrm/synchronization/admin.py +1 -0
  107. wbcrm-2.2.1/wbcrm/synchronization/apps.py +15 -0
  108. wbcrm-2.2.1/wbcrm/synchronization/dynamic_preferences_registry.py +1 -0
  109. wbcrm-2.2.1/wbcrm/synchronization/management.py +36 -0
  110. wbcrm-2.2.1/wbcrm/synchronization/tasks.py +1 -0
  111. wbcrm-2.2.1/wbcrm/synchronization/urls.py +5 -0
  112. wbcrm-2.2.1/wbcrm/tasks.py +312 -0
  113. wbcrm-2.2.1/wbcrm/templates/email/activity.html +98 -0
  114. wbcrm-2.2.1/wbcrm/templates/email/activity_report.html +6 -0
  115. wbcrm-2.2.1/wbcrm/templates/email/daily_summary.html +72 -0
  116. wbcrm-2.2.1/wbcrm/templates/email/global_daily_summary.html +85 -0
  117. wbcrm-2.2.1/wbcrm/tests/__init__.py +0 -0
  118. wbcrm-2.2.1/wbcrm/tests/accounts/__init__.py +0 -0
  119. wbcrm-2.2.1/wbcrm/tests/accounts/test_models.py +380 -0
  120. wbcrm-2.2.1/wbcrm/tests/accounts/test_viewsets.py +87 -0
  121. wbcrm-2.2.1/wbcrm/tests/conftest.py +76 -0
  122. wbcrm-2.2.1/wbcrm/tests/disable_signals.py +52 -0
  123. wbcrm-2.2.1/wbcrm/tests/e2e/__init__.py +1 -0
  124. wbcrm-2.2.1/wbcrm/tests/e2e/e2e_wbcrm_utility.py +82 -0
  125. wbcrm-2.2.1/wbcrm/tests/e2e/test_e2e.py +369 -0
  126. wbcrm-2.2.1/wbcrm/tests/test_assignee_methods.py +39 -0
  127. wbcrm-2.2.1/wbcrm/tests/test_chartviewsets.py +111 -0
  128. wbcrm-2.2.1/wbcrm/tests/test_dto.py +63 -0
  129. wbcrm-2.2.1/wbcrm/tests/test_filters.py +51 -0
  130. wbcrm-2.2.1/wbcrm/tests/test_models.py +216 -0
  131. wbcrm-2.2.1/wbcrm/tests/test_recurrence.py +291 -0
  132. wbcrm-2.2.1/wbcrm/tests/test_report.py +20 -0
  133. wbcrm-2.2.1/wbcrm/tests/test_serializers.py +170 -0
  134. wbcrm-2.2.1/wbcrm/tests/test_tasks.py +94 -0
  135. wbcrm-2.2.1/wbcrm/tests/test_viewsets.py +967 -0
  136. wbcrm-2.2.1/wbcrm/tests/tests.py +120 -0
  137. wbcrm-2.2.1/wbcrm/typings.py +107 -0
  138. wbcrm-2.2.1/wbcrm/urls.py +67 -0
  139. wbcrm-2.2.1/wbcrm/viewsets/__init__.py +22 -0
  140. wbcrm-2.2.1/wbcrm/viewsets/accounts.py +121 -0
  141. wbcrm-2.2.1/wbcrm/viewsets/activities.py +315 -0
  142. wbcrm-2.2.1/wbcrm/viewsets/buttons/__init__.py +7 -0
  143. wbcrm-2.2.1/wbcrm/viewsets/buttons/accounts.py +27 -0
  144. wbcrm-2.2.1/wbcrm/viewsets/buttons/activities.py +68 -0
  145. wbcrm-2.2.1/wbcrm/viewsets/buttons/signals.py +17 -0
  146. wbcrm-2.2.1/wbcrm/viewsets/display/__init__.py +12 -0
  147. wbcrm-2.2.1/wbcrm/viewsets/display/accounts.py +110 -0
  148. wbcrm-2.2.1/wbcrm/viewsets/display/activities.py +443 -0
  149. wbcrm-2.2.1/wbcrm/viewsets/display/groups.py +22 -0
  150. wbcrm-2.2.1/wbcrm/viewsets/display/products.py +105 -0
  151. wbcrm-2.2.1/wbcrm/viewsets/endpoints/__init__.py +8 -0
  152. wbcrm-2.2.1/wbcrm/viewsets/endpoints/accounts.py +32 -0
  153. wbcrm-2.2.1/wbcrm/viewsets/endpoints/activities.py +30 -0
  154. wbcrm-2.2.1/wbcrm/viewsets/endpoints/groups.py +7 -0
  155. wbcrm-2.2.1/wbcrm/viewsets/endpoints/products.py +9 -0
  156. wbcrm-2.2.1/wbcrm/viewsets/groups.py +37 -0
  157. wbcrm-2.2.1/wbcrm/viewsets/menu/__init__.py +8 -0
  158. wbcrm-2.2.1/wbcrm/viewsets/menu/accounts.py +18 -0
  159. wbcrm-2.2.1/wbcrm/viewsets/menu/activities.py +61 -0
  160. wbcrm-2.2.1/wbcrm/viewsets/menu/groups.py +16 -0
  161. wbcrm-2.2.1/wbcrm/viewsets/menu/products.py +20 -0
  162. wbcrm-2.2.1/wbcrm/viewsets/mixins.py +34 -0
  163. wbcrm-2.2.1/wbcrm/viewsets/previews/__init__.py +1 -0
  164. wbcrm-2.2.1/wbcrm/viewsets/previews/activities.py +10 -0
  165. wbcrm-2.2.1/wbcrm/viewsets/products.py +56 -0
  166. wbcrm-2.2.1/wbcrm/viewsets/recurrence.py +26 -0
  167. wbcrm-2.2.1/wbcrm/viewsets/titles/__init__.py +13 -0
  168. wbcrm-2.2.1/wbcrm/viewsets/titles/accounts.py +22 -0
  169. wbcrm-2.2.1/wbcrm/viewsets/titles/activities.py +61 -0
  170. wbcrm-2.2.1/wbcrm/viewsets/titles/products.py +13 -0
  171. wbcrm-2.2.1/wbcrm/viewsets/titles/utils.py +46 -0
  172. wbcrm-2.2.1/wbcrm/workflows/__init__.py +1 -0
  173. 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,4 @@
1
+ from .accounts import AccountRoleModelAdmin, AccountModelAdmin
2
+ from .activities import ActivityAdmin, ActivityTypeAdmin
3
+ from .groups import GroupModelAdmin
4
+ from .products import ProductAdmin
@@ -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,7 @@
1
+ from django.contrib import admin
2
+ from wbcrm.models import Group
3
+
4
+
5
+ @admin.register(Group)
6
+ class GroupModelAdmin(admin.ModelAdmin):
7
+ autocomplete_fields = ("members",)
@@ -0,0 +1,8 @@
1
+ from django.contrib import admin
2
+ from wbcrm.models import Product
3
+
4
+
5
+ @admin.register(Product)
6
+ class ProductAdmin(admin.ModelAdmin):
7
+ search_fields = ("title",)
8
+ list_display = ("title", "id")
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class WbcrmConfig(AppConfig):
5
+ name = "wbcrm"
@@ -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)