module-retour-client-revo 1.0.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.
- module_retour_client_revo-1.0.0/.gitignore +103 -0
- module_retour_client_revo-1.0.0/PKG-INFO +117 -0
- module_retour_client_revo-1.0.0/README.md +98 -0
- module_retour_client_revo-1.0.0/pyproject.toml +39 -0
- module_retour_client_revo-1.0.0/retour_client/__init__.py +1 -0
- module_retour_client_revo-1.0.0/retour_client/adapters.py +35 -0
- module_retour_client_revo-1.0.0/retour_client/admin.py +14 -0
- module_retour_client_revo-1.0.0/retour_client/apps.py +6 -0
- module_retour_client_revo-1.0.0/retour_client/constantes.py +65 -0
- module_retour_client_revo-1.0.0/retour_client/context_processors.py +48 -0
- module_retour_client_revo-1.0.0/retour_client/forms/__init__.py +2 -0
- module_retour_client_revo-1.0.0/retour_client/forms/commentaires_bug.py +31 -0
- module_retour_client_revo-1.0.0/retour_client/forms/retour_client.py +16 -0
- module_retour_client_revo-1.0.0/retour_client/http_utils.py +28 -0
- module_retour_client_revo-1.0.0/retour_client/management/__init__.py +0 -0
- module_retour_client_revo-1.0.0/retour_client/management/commands/__init__.py +0 -0
- module_retour_client_revo-1.0.0/retour_client/management/commands/envoi_mail_retour_client_preprod.py +15 -0
- module_retour_client_revo-1.0.0/retour_client/management/envoi_mail_retour_client_preprod.py +76 -0
- module_retour_client_revo-1.0.0/retour_client/migrations/__init__.py +0 -0
- module_retour_client_revo-1.0.0/retour_client/models/__init__.py +2 -0
- module_retour_client_revo-1.0.0/retour_client/models/reponse_bug.py +23 -0
- module_retour_client_revo-1.0.0/retour_client/models/retour_client.py +94 -0
- module_retour_client_revo-1.0.0/retour_client/services/__init__.py +0 -0
- module_retour_client_revo-1.0.0/retour_client/services/statuts_bug.py +85 -0
- module_retour_client_revo-1.0.0/retour_client/templates/css/kanban.css +188 -0
- module_retour_client_revo-1.0.0/retour_client/templates/css/pipeline.css +280 -0
- module_retour_client_revo-1.0.0/retour_client/templates/css/retour_client.css +281 -0
- module_retour_client_revo-1.0.0/retour_client/templates/detail-retour-client.html +119 -0
- module_retour_client_revo-1.0.0/retour_client/templates/formulaire_edition_modal.html +3 -0
- module_retour_client_revo-1.0.0/retour_client/templates/kanban-retour-client.html +163 -0
- module_retour_client_revo-1.0.0/retour_client/templates/liste-retour-client.html +17 -0
- module_retour_client_revo-1.0.0/retour_client/templates/mails/nouveau-ticket-client.tpl +1 -0
- module_retour_client_revo-1.0.0/retour_client/templates/mails/recap-journalier-client.tpl +1 -0
- module_retour_client_revo-1.0.0/retour_client/templates/mails/recap-journalier-revo.tpl +1 -0
- module_retour_client_revo-1.0.0/retour_client/templates/mails/reponse-ticket-client.tpl +1 -0
- module_retour_client_revo-1.0.0/retour_client/templates/pipeline-retour-client.html +60 -0
- module_retour_client_revo-1.0.0/retour_client/templates/recherche-filtre-bug.html +37 -0
- module_retour_client_revo-1.0.0/retour_client/templates/retour_client.html +304 -0
- module_retour_client_revo-1.0.0/retour_client/urls.py +25 -0
- module_retour_client_revo-1.0.0/retour_client/utils/__init__.py +1 -0
- module_retour_client_revo-1.0.0/retour_client/utils/anymail_utils.py +90 -0
- module_retour_client_revo-1.0.0/retour_client/views/__init__.py +2 -0
- module_retour_client_revo-1.0.0/retour_client/views/retour_client.py +571 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# General #
|
|
2
|
+
.htaccess
|
|
3
|
+
htaccess
|
|
4
|
+
tmp/
|
|
5
|
+
#public/
|
|
6
|
+
logs/
|
|
7
|
+
stderr.log
|
|
8
|
+
.DS_Store
|
|
9
|
+
|
|
10
|
+
# Django #
|
|
11
|
+
*.log
|
|
12
|
+
*.pot
|
|
13
|
+
*.pyc
|
|
14
|
+
*.db
|
|
15
|
+
__pycache__
|
|
16
|
+
db.sqlite3
|
|
17
|
+
media
|
|
18
|
+
staticfiles
|
|
19
|
+
|
|
20
|
+
# Backup files #
|
|
21
|
+
*.bak
|
|
22
|
+
|
|
23
|
+
# If you are using PyCharm #
|
|
24
|
+
*/.idea/*
|
|
25
|
+
.idea
|
|
26
|
+
.idea/**/workspace.xml
|
|
27
|
+
.idea/**/tasks.xml
|
|
28
|
+
.idea/dictionaries
|
|
29
|
+
.idea/**/dataSources/
|
|
30
|
+
.idea/**/dataSources.ids
|
|
31
|
+
.idea/**/dataSources.xml
|
|
32
|
+
.idea/**/dataSources.local.xml
|
|
33
|
+
.idea/**/sqlDataSources.xml
|
|
34
|
+
.idea/**/dynamic.xml
|
|
35
|
+
.idea/**/uiDesigner.xml
|
|
36
|
+
.idea/**/gradle.xml
|
|
37
|
+
.idea/**/libraries
|
|
38
|
+
*.iws /out/
|
|
39
|
+
|
|
40
|
+
# If your are using VSCode #
|
|
41
|
+
.vscode/*
|
|
42
|
+
!.vscode/settings.json
|
|
43
|
+
!.vscode/tasks.json
|
|
44
|
+
!.vscode/launch.json
|
|
45
|
+
!.vscode/extensions.json
|
|
46
|
+
!.vscode/*.code-snippets
|
|
47
|
+
.history/
|
|
48
|
+
*.vsix
|
|
49
|
+
.vscode/
|
|
50
|
+
|
|
51
|
+
# Python #
|
|
52
|
+
*.py[cod]
|
|
53
|
+
*$py.class
|
|
54
|
+
|
|
55
|
+
# Distribution / packaging
|
|
56
|
+
.Python build/
|
|
57
|
+
develop-eggs/
|
|
58
|
+
downloads/
|
|
59
|
+
eggs/
|
|
60
|
+
.eggs/
|
|
61
|
+
#lib/
|
|
62
|
+
lib64/
|
|
63
|
+
parts/
|
|
64
|
+
sdist/
|
|
65
|
+
var/
|
|
66
|
+
wheels/
|
|
67
|
+
*.egg-info/
|
|
68
|
+
.installed.cfg
|
|
69
|
+
*.egg
|
|
70
|
+
*.manifest
|
|
71
|
+
*.spec
|
|
72
|
+
|
|
73
|
+
# Installer logs
|
|
74
|
+
pip-log.txt
|
|
75
|
+
pip-delete-this-directory.txt
|
|
76
|
+
|
|
77
|
+
# Unit test / coverage reports
|
|
78
|
+
htmlcov/
|
|
79
|
+
.tox/
|
|
80
|
+
.coverage
|
|
81
|
+
.coverage.*
|
|
82
|
+
.cache
|
|
83
|
+
.pytest_cache/
|
|
84
|
+
nosetests.xml
|
|
85
|
+
coverage.xml
|
|
86
|
+
*.cover
|
|
87
|
+
.hypothesis/
|
|
88
|
+
|
|
89
|
+
# Jupyter Notebook
|
|
90
|
+
.ipynb_checkpoints
|
|
91
|
+
|
|
92
|
+
# pyenv
|
|
93
|
+
.python-version
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Environments
|
|
97
|
+
.env
|
|
98
|
+
.venv
|
|
99
|
+
env/
|
|
100
|
+
venv/
|
|
101
|
+
ENV/
|
|
102
|
+
env.bak/
|
|
103
|
+
venv.bak/
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: module-retour-client-revo
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Module Django pour la gestion des retours clients et bugs
|
|
5
|
+
Author-email: Revolucy <hello@revolucy.fr>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Framework :: Django
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Requires-Dist: django-anymail[brevo]>=12.0
|
|
16
|
+
Requires-Dist: django<6.0,>=4.2
|
|
17
|
+
Requires-Dist: requests<3,>=2.31
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Module de retour Client
|
|
21
|
+
|
|
22
|
+
## Conception
|
|
23
|
+
|
|
24
|
+
- [Django](https://www.djangoproject.com/)
|
|
25
|
+
- [Bootstrap](https://getbootstrap.com/)
|
|
26
|
+
- [JQuery 3.5.1](https://api.jquery.com/category/version/3.5/)
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
#### 1 - Ajouter le package dans les requirements
|
|
31
|
+
#### 2 - Ajouter dans votre .env :
|
|
32
|
+
=> Pour la préprod/Prod :<br>
|
|
33
|
+
```html
|
|
34
|
+
#====================================== RETOUR CLIENT =======================================
|
|
35
|
+
EMAIL_CLIENT_RETOUR='test@test.fr'
|
|
36
|
+
SIREN_CLIENT='1245648'
|
|
37
|
+
DATE_MISE_EN_PRODUCTION='2024-12-30'
|
|
38
|
+
DATE_FIN_GARANTIE='2024-12-30'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### 3 - Ajouter dans votre fichier settings.py :
|
|
42
|
+
```html
|
|
43
|
+
INSTALLED_APPS = [
|
|
44
|
+
'retour_client',
|
|
45
|
+
]
|
|
46
|
+
```
|
|
47
|
+
```html
|
|
48
|
+
'context_processors': [
|
|
49
|
+
'retour_client.context_processors.formulaire_retour_client',
|
|
50
|
+
]
|
|
51
|
+
```
|
|
52
|
+
```html
|
|
53
|
+
########### Configuration RETOUR CLIENT.
|
|
54
|
+
EMAIL_CLIENT_RETOUR = env('EMAIL_CLIENT_RETOUR')
|
|
55
|
+
SIREN_CLIENT = env('SIREN_CLIENT')
|
|
56
|
+
DATE_MISE_EN_PRODUCTION = env('DATE_MISE_EN_PRODUCTION')
|
|
57
|
+
DATE_FIN_GARANTIE = env('DATE_FIN_GARANTIE')
|
|
58
|
+
```
|
|
59
|
+
#### 4 - Ajouter dans votre fichier urls.py :
|
|
60
|
+
```html
|
|
61
|
+
path('retour-client/', include("retour_client.urls")),
|
|
62
|
+
```
|
|
63
|
+
#### 5 - Ajouter dans le footer.html de votre base :
|
|
64
|
+
```html
|
|
65
|
+
<!-- AJOUT POPUP RETOUR CLIENT -->
|
|
66
|
+
{% include 'retour_client.html' %}
|
|
67
|
+
```
|
|
68
|
+
#### 6 - Ajouter un cron journalier 09h00 sur le serveur pour envoi des mails en préprod:
|
|
69
|
+
```html
|
|
70
|
+
Commande : envoi_mail_retour_client_preprod
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Licence
|
|
74
|
+
|
|
75
|
+
[Revolucy](https://www.revolucy.fr)
|
|
76
|
+
|
|
77
|
+
## Versionning
|
|
78
|
+
|
|
79
|
+
- V1.0.0 | Création du module de retour
|
|
80
|
+
|
|
81
|
+
## Flux
|
|
82
|
+
|
|
83
|
+
### PREPROD :
|
|
84
|
+
=> Module de retour visible pour tous les utilisateurs
|
|
85
|
+
|
|
86
|
+
1 - Création d'un ticket par le client :
|
|
87
|
+
- Enregistrement du ticket sur le site
|
|
88
|
+
- 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
|
|
89
|
+
|
|
90
|
+
### PRODUCTION - EN PERIODE DE GARANTIE :
|
|
91
|
+
=> Module de retour visible pour les administrateurs seulement
|
|
92
|
+
=> Affichage du compteur de jours restant de garantie
|
|
93
|
+
|
|
94
|
+
1 - Création d'un ticket par le client :
|
|
95
|
+
- Enregistrement du ticket sur le site
|
|
96
|
+
- 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
|
|
97
|
+
|
|
98
|
+
### PRODUCTION - HORS PERIODE DE GARANTIE :
|
|
99
|
+
=> Module de retour visible pour les administrateurs seulement
|
|
100
|
+
=> Affichage du compteur de nombre de crédit ticket restant
|
|
101
|
+
=> Si moins d’1H de crédit : Ajout d'un lien pour prendre un pack de crédit 5H ou 10H, puis création de la facture associée dans Lucy
|
|
102
|
+
|
|
103
|
+
1 - Création d'un ticket par le client :
|
|
104
|
+
- Enregistrement du ticket sur le site
|
|
105
|
+
- Mail Chef de projet revo à l'ajout du ticket
|
|
106
|
+
|
|
107
|
+
2 - Réponse par le chef de projet :
|
|
108
|
+
- Indication du nombre d'heure passé
|
|
109
|
+
- Déduction du crédit de temps dans Lucy par API
|
|
110
|
+
- Mail Client à l'ajout d'une réponse au ticket
|
|
111
|
+
|
|
112
|
+
## Déploiement Pypi
|
|
113
|
+
|
|
114
|
+
1 - pip install build twine<br>
|
|
115
|
+
2 - python -m build<br>
|
|
116
|
+
3 - python -m twine upload dist/*<br>
|
|
117
|
+
4 - Indiquer le token présent dans 1Password
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Module de retour Client
|
|
2
|
+
|
|
3
|
+
## Conception
|
|
4
|
+
|
|
5
|
+
- [Django](https://www.djangoproject.com/)
|
|
6
|
+
- [Bootstrap](https://getbootstrap.com/)
|
|
7
|
+
- [JQuery 3.5.1](https://api.jquery.com/category/version/3.5/)
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
#### 1 - Ajouter le package dans les requirements
|
|
12
|
+
#### 2 - Ajouter dans votre .env :
|
|
13
|
+
=> Pour la préprod/Prod :<br>
|
|
14
|
+
```html
|
|
15
|
+
#====================================== RETOUR CLIENT =======================================
|
|
16
|
+
EMAIL_CLIENT_RETOUR='test@test.fr'
|
|
17
|
+
SIREN_CLIENT='1245648'
|
|
18
|
+
DATE_MISE_EN_PRODUCTION='2024-12-30'
|
|
19
|
+
DATE_FIN_GARANTIE='2024-12-30'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
#### 3 - Ajouter dans votre fichier settings.py :
|
|
23
|
+
```html
|
|
24
|
+
INSTALLED_APPS = [
|
|
25
|
+
'retour_client',
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
```html
|
|
29
|
+
'context_processors': [
|
|
30
|
+
'retour_client.context_processors.formulaire_retour_client',
|
|
31
|
+
]
|
|
32
|
+
```
|
|
33
|
+
```html
|
|
34
|
+
########### Configuration RETOUR CLIENT.
|
|
35
|
+
EMAIL_CLIENT_RETOUR = env('EMAIL_CLIENT_RETOUR')
|
|
36
|
+
SIREN_CLIENT = env('SIREN_CLIENT')
|
|
37
|
+
DATE_MISE_EN_PRODUCTION = env('DATE_MISE_EN_PRODUCTION')
|
|
38
|
+
DATE_FIN_GARANTIE = env('DATE_FIN_GARANTIE')
|
|
39
|
+
```
|
|
40
|
+
#### 4 - Ajouter dans votre fichier urls.py :
|
|
41
|
+
```html
|
|
42
|
+
path('retour-client/', include("retour_client.urls")),
|
|
43
|
+
```
|
|
44
|
+
#### 5 - Ajouter dans le footer.html de votre base :
|
|
45
|
+
```html
|
|
46
|
+
<!-- AJOUT POPUP RETOUR CLIENT -->
|
|
47
|
+
{% include 'retour_client.html' %}
|
|
48
|
+
```
|
|
49
|
+
#### 6 - Ajouter un cron journalier 09h00 sur le serveur pour envoi des mails en préprod:
|
|
50
|
+
```html
|
|
51
|
+
Commande : envoi_mail_retour_client_preprod
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Licence
|
|
55
|
+
|
|
56
|
+
[Revolucy](https://www.revolucy.fr)
|
|
57
|
+
|
|
58
|
+
## Versionning
|
|
59
|
+
|
|
60
|
+
- V1.0.0 | Création du module de retour
|
|
61
|
+
|
|
62
|
+
## Flux
|
|
63
|
+
|
|
64
|
+
### PREPROD :
|
|
65
|
+
=> Module de retour visible pour tous les utilisateurs
|
|
66
|
+
|
|
67
|
+
1 - Création d'un ticket par le client :
|
|
68
|
+
- Enregistrement du ticket sur le site
|
|
69
|
+
- 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
|
|
70
|
+
|
|
71
|
+
### PRODUCTION - EN PERIODE DE GARANTIE :
|
|
72
|
+
=> Module de retour visible pour les administrateurs seulement
|
|
73
|
+
=> Affichage du compteur de jours restant de garantie
|
|
74
|
+
|
|
75
|
+
1 - Création d'un ticket par le client :
|
|
76
|
+
- Enregistrement du ticket sur le site
|
|
77
|
+
- 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
|
|
78
|
+
|
|
79
|
+
### PRODUCTION - HORS PERIODE DE GARANTIE :
|
|
80
|
+
=> Module de retour visible pour les administrateurs seulement
|
|
81
|
+
=> Affichage du compteur de nombre de crédit ticket restant
|
|
82
|
+
=> Si moins d’1H de crédit : Ajout d'un lien pour prendre un pack de crédit 5H ou 10H, puis création de la facture associée dans Lucy
|
|
83
|
+
|
|
84
|
+
1 - Création d'un ticket par le client :
|
|
85
|
+
- Enregistrement du ticket sur le site
|
|
86
|
+
- Mail Chef de projet revo à l'ajout du ticket
|
|
87
|
+
|
|
88
|
+
2 - Réponse par le chef de projet :
|
|
89
|
+
- Indication du nombre d'heure passé
|
|
90
|
+
- Déduction du crédit de temps dans Lucy par API
|
|
91
|
+
- Mail Client à l'ajout d'une réponse au ticket
|
|
92
|
+
|
|
93
|
+
## Déploiement Pypi
|
|
94
|
+
|
|
95
|
+
1 - pip install build twine<br>
|
|
96
|
+
2 - python -m build<br>
|
|
97
|
+
3 - python -m twine upload dist/*<br>
|
|
98
|
+
4 - Indiquer le token présent dans 1Password
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.20"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "module-retour-client-revo"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Module Django pour la gestion des retours clients et bugs"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{name = "Revolucy", email = "hello@revolucy.fr"}]
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
"Django>=4.2,<6.0",
|
|
16
|
+
"requests>=2.31,<3",
|
|
17
|
+
"django-anymail[brevo]>=12.0",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Framework :: Django",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["retour_client"]
|
|
31
|
+
include = [
|
|
32
|
+
"retour_client/templates/**",
|
|
33
|
+
"retour_client/static/**",
|
|
34
|
+
"retour_client/migrations/**",
|
|
35
|
+
"retour_client/management/**",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.version]
|
|
39
|
+
path = "retour_client/__init__.py"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.core.mail import EmailMultiAlternatives
|
|
3
|
+
from django.utils import timezone
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def now_date():
|
|
7
|
+
return timezone.now().date()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_user_admin(user):
|
|
11
|
+
fn = getattr(settings, "RETOUR_CLIENT_IS_ADMIN_FN", None)
|
|
12
|
+
return fn(user) if fn else bool(getattr(user, "is_staff", False))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_current_user(request):
|
|
16
|
+
return getattr(request, "user", None)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def image_upload_to(instance, filename):
|
|
20
|
+
fn = getattr(settings, "RETOUR_CLIENT_IMAGE_UPLOAD_TO", None)
|
|
21
|
+
return fn(instance, filename) if fn else f"retour_client/{filename}"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def send_mail_html(subject, to, body_html):
|
|
25
|
+
fn = getattr(settings, "RETOUR_CLIENT_SEND_MAIL_FN", None)
|
|
26
|
+
if fn:
|
|
27
|
+
return fn(subject, to, body_html)
|
|
28
|
+
mail = EmailMultiAlternatives(subject, body_html, to=to)
|
|
29
|
+
mail.attach_alternative(body_html, "text/html")
|
|
30
|
+
mail.send()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_lucy_collaborateur_email(siren):
|
|
34
|
+
fn = getattr(settings, "RETOUR_CLIENT_GET_COLLAB_EMAIL_FN", None)
|
|
35
|
+
return fn(siren) if fn else None
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
|
|
3
|
+
from retour_client.models import RetourClient, ReponseBug
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BlockReponseBug(admin.StackedInline):
|
|
7
|
+
model = ReponseBug
|
|
8
|
+
extra = 1
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@admin.register(RetourClient)
|
|
12
|
+
class RetourClientAdmin(admin.ModelAdmin):
|
|
13
|
+
list_display = ('pk', 'priorite', 'statut', 'description')
|
|
14
|
+
inlines = [BlockReponseBug]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
class RetourClientConstantes:
|
|
2
|
+
INCONNU = 'Inconnu'
|
|
3
|
+
|
|
4
|
+
CRITIQUE = 'C'
|
|
5
|
+
NORMAL = 'N'
|
|
6
|
+
NON_URGENT = 'B'
|
|
7
|
+
|
|
8
|
+
priorite = [
|
|
9
|
+
(CRITIQUE, 'Critique'),
|
|
10
|
+
(NORMAL, 'Normal'),
|
|
11
|
+
(NON_URGENT, 'Non Urgent'),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
EN_ATTENTE = 'A'
|
|
15
|
+
EN_TRAITEMENT = 'E'
|
|
16
|
+
TRAITE = 'T'
|
|
17
|
+
VALIDE = 'V'
|
|
18
|
+
V2 = 'Z'
|
|
19
|
+
|
|
20
|
+
statut = [
|
|
21
|
+
(EN_ATTENTE, 'En attente'),
|
|
22
|
+
(EN_TRAITEMENT, 'En cours'),
|
|
23
|
+
(TRAITE, 'Traité par Revolucy'),
|
|
24
|
+
(VALIDE, 'Retour validé'),
|
|
25
|
+
(V2, 'Demande V2'),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
statut_client = [
|
|
29
|
+
(EN_ATTENTE, 'En attente'),
|
|
30
|
+
(EN_TRAITEMENT, 'En cours'),
|
|
31
|
+
(TRAITE, 'Traité par Revolucy'),
|
|
32
|
+
(V2, 'Demande V2'),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
NON_CLASSE = 'N'
|
|
36
|
+
QUESTION = 'Q'
|
|
37
|
+
BUG = 'B'
|
|
38
|
+
AMELIORATION = 'A'
|
|
39
|
+
|
|
40
|
+
type = [
|
|
41
|
+
(NON_CLASSE, 'Non classé'),
|
|
42
|
+
(QUESTION, 'Question'),
|
|
43
|
+
(BUG, 'Bug'),
|
|
44
|
+
(AMELIORATION, 'Amélioration'),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
STATUS_MAPPING_YT = {
|
|
48
|
+
'En attente': 'A',
|
|
49
|
+
'En cours': 'E',
|
|
50
|
+
'Terminé': 'T',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
REVOLUCY = 'R'
|
|
54
|
+
CLIENT = 'C'
|
|
55
|
+
|
|
56
|
+
repondant = [
|
|
57
|
+
(REVOLUCY, 'Revolucy'),
|
|
58
|
+
(CLIENT, 'Client'),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
class Environnement:
|
|
62
|
+
LOCAL = 'local'
|
|
63
|
+
RECETTE = 'recette'
|
|
64
|
+
PREPRODUCTION = 'preproduction'
|
|
65
|
+
PRODUCTION = 'production'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
|
|
6
|
+
from .constantes import RetourClientConstantes
|
|
7
|
+
from .forms import RetourClientForm
|
|
8
|
+
from .http_utils import safe_get_json
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def formulaire_retour_client(request):
|
|
14
|
+
form_bug = RetourClientForm()
|
|
15
|
+
jrs_restant_garantie = 0
|
|
16
|
+
solde_ticket = 0
|
|
17
|
+
current_user = request.user
|
|
18
|
+
|
|
19
|
+
if not request.user.is_anonymous and current_user.email == 'info.marseille@alyseparking.com':
|
|
20
|
+
siren = settings.SIREN_CLIENT_MARSEILLE
|
|
21
|
+
else:
|
|
22
|
+
siren = settings.SIREN_CLIENT
|
|
23
|
+
|
|
24
|
+
if (settings.ENVIRONNEMENT == RetourClientConstantes.Environnement.PRODUCTION
|
|
25
|
+
and not current_user.is_anonymous
|
|
26
|
+
and getattr(current_user, "is_administrateur", False)):
|
|
27
|
+
|
|
28
|
+
jrs_restant_garantie = (
|
|
29
|
+
datetime.strptime(settings.DATE_FIN_GARANTIE, '%Y-%m-%d').date()
|
|
30
|
+
- datetime.today().date()
|
|
31
|
+
).days
|
|
32
|
+
|
|
33
|
+
url = f"https://app.lucy-crm.fr/api/credit-client/{siren}"
|
|
34
|
+
|
|
35
|
+
data, err = safe_get_json(url, timeout=(2, 5)) # ≤ ~5 s
|
|
36
|
+
if data:
|
|
37
|
+
solde_ticket = data.get("solde_ticket", 0)
|
|
38
|
+
else:
|
|
39
|
+
# On loggue mais on NE BLOQUE PAS : on garde solde_ticket=0
|
|
40
|
+
logger.error(f"[Lucy API] Impossible de récupérer solde_ticket: {err}")
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
'form_bug': form_bug,
|
|
44
|
+
'environnement': settings.ENVIRONNEMENT,
|
|
45
|
+
'siren_client': siren,
|
|
46
|
+
'jrs_restant_garantie': jrs_restant_garantie,
|
|
47
|
+
'solde_ticket': solde_ticket,
|
|
48
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
|
|
3
|
+
from retour_client.constantes import RetourClientConstantes
|
|
4
|
+
from retour_client.models import ReponseBug
|
|
5
|
+
from retour_client.adapters import is_user_admin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReponseBugForm(forms.ModelForm):
|
|
9
|
+
class Meta:
|
|
10
|
+
model = ReponseBug
|
|
11
|
+
fields = ["reponse", "repondant", "temps_passe", "piece_jointe"]
|
|
12
|
+
|
|
13
|
+
reponse = forms.CharField(
|
|
14
|
+
widget=forms.Textarea(attrs={"class": "form-control", "rows": 6}),
|
|
15
|
+
required=True,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def __init__(self, *args, **kwargs):
|
|
19
|
+
# récupère l’utilisateur sans casser l’API Django/admin
|
|
20
|
+
current_user = kwargs.pop("current_user", None)
|
|
21
|
+
super().__init__(*args, **kwargs)
|
|
22
|
+
|
|
23
|
+
# valeur initiale selon le rôle
|
|
24
|
+
if current_user and not getattr(current_user, "is_anonymous", False) and is_user_admin(current_user):
|
|
25
|
+
self.fields["repondant"].initial = RetourClientConstantes.REVOLUCY
|
|
26
|
+
else:
|
|
27
|
+
self.fields["repondant"].initial = RetourClientConstantes.CLIENT
|
|
28
|
+
|
|
29
|
+
def clean_reponse(self):
|
|
30
|
+
reponse = self.cleaned_data.get("reponse", "")
|
|
31
|
+
return reponse.replace("\n", "<br>")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
|
|
3
|
+
from retour_client.models import RetourClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RetourClientForm(forms.ModelForm):
|
|
7
|
+
class Meta:
|
|
8
|
+
model = RetourClient
|
|
9
|
+
exclude = ['date_ajout', 'statut', 'utilisateur', 'image', 'navigateur', 'lien_url', 'type']
|
|
10
|
+
|
|
11
|
+
description = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 6}), required=True)
|
|
12
|
+
|
|
13
|
+
def clean_description(self):
|
|
14
|
+
description = self.cleaned_data.get('description')
|
|
15
|
+
# Convertit les sauts de ligne en <br> dans la description
|
|
16
|
+
return description.replace('\n', '<br>')
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from requests.exceptions import RequestException, Timeout, ConnectionError
|
|
3
|
+
|
|
4
|
+
DEFAULT_TIMEOUT = (2, 5) # (connect_timeout, read_timeout) -> total ≤ ~5s
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def safe_get_json(url, headers=None, timeout=DEFAULT_TIMEOUT):
|
|
8
|
+
try:
|
|
9
|
+
r = requests.get(url, headers=headers or {}, timeout=timeout)
|
|
10
|
+
if r.ok:
|
|
11
|
+
return r.json(), None
|
|
12
|
+
return None, f"HTTP {r.status_code}"
|
|
13
|
+
except (Timeout, ConnectionError) as e:
|
|
14
|
+
return None, f"timeout/conn error: {e}"
|
|
15
|
+
except RequestException as e:
|
|
16
|
+
return None, f"request error: {e}"
|
|
17
|
+
except ValueError as e: # JSON decode
|
|
18
|
+
return None, f"json decode error: {e}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def safe_post_json(url, payload, headers=None, timeout=DEFAULT_TIMEOUT):
|
|
22
|
+
try:
|
|
23
|
+
r = requests.post(url, json=payload, headers=headers or {}, timeout=timeout)
|
|
24
|
+
return r, None
|
|
25
|
+
except (Timeout, ConnectionError) as e:
|
|
26
|
+
return None, f"timeout/conn error: {e}"
|
|
27
|
+
except RequestException as e:
|
|
28
|
+
return None, f"request error: {e}"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django.core.management import BaseCommand
|
|
4
|
+
|
|
5
|
+
from retour_client.management.envoi_mail_retour_client_preprod import EnvoiMailRetourClientBatch
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Command(BaseCommand):
|
|
11
|
+
help = "Envoyer les recap des tickets en cours au client et chef de projet"
|
|
12
|
+
|
|
13
|
+
def handle(self, *args, **options):
|
|
14
|
+
logger.info(f"Lancement de la commande '{self.help}'")
|
|
15
|
+
EnvoiMailRetourClientBatch.do_process()
|