Commit 3c4f9138ae3fe491f92fab3e26e1955555a21faf

Authored by Etienne Pallier
1 parent cf782ac0
Exists in master and in 1 other branch dev

BIG NEW FEATURE: Config des champs obligatoires et readonly via page web

=> plus besoin de modifier manuellement le fichier de config texte yaml
et aussi :
- ajout lien vers stats utilisateurs depuis page accueil superadmin
- modif page Apropos

v5.3.1-3.7.9
@@ -373,12 +373,6 @@ Par contre, ok avec FPDF @@ -373,12 +373,6 @@ Par contre, ok avec FPDF
373 373
374 *) LDAP trou sécu (autres labos) : user se connecte avec bad mdp 374 *) LDAP trou sécu (autres labos) : user se connecte avec bad mdp
375 375
376 - *)  
377 - Rendre modifiable la config via page web "Gérer les champs obligatoires"  
378 - - OFF_nom_du_champ : 'libellé'  
379 - - Restaurer la config par défaut  
380 - - Réactiver une variable : OFF_nom_du_champ => nom_du_champ  
381 -  
382 *) => update matos lifecycle diag 376 *) => update matos lifecycle diag
383 377
384 378
@@ -541,11 +535,19 @@ TODO : @@ -541,11 +535,19 @@ TODO :
541 535
542 - add_edit generic 536 - add_edit generic
543 537
544 -- fusionner groupe thematique et metier (et projet ?) 538 +- fusionner groupe thematique et metier (et projet ?) :
  539 + => faire hériter les Controller et les Table d'une meme superclasse GroupController et GroupTable
  540 + => avoir un seul template
  541 + => c'est vraiment stupide d'avoir 2 classes qui font la meme chose...
545 542
546 - Utiliser les vues "index" des entités associées pour la vue "view" de materiel (et suivi) : 543 - Utiliser les vues "index" des entités associées pour la vue "view" de materiel (et suivi) :
547 => éviter la redondance, le contenu est pratiquement le meme (???, sauf que les colonnes ne sont pas triables) 544 => éviter la redondance, le contenu est pratiquement le meme (???, sauf que les colonnes ne sont pas triables)
548 545
  546 +- Bien préciser quels sont les champs obligatoires avec une asterisque (et pour chaque LOT)
  547 +
  548 +- S/N à renseigner (recommended) ssi > 10000€
  549 + Ajouter champ attributes.condition = "prix > 10000"
  550 +
549 ======= NEXT ======= 551 ======= NEXT =======
550 552
551 print etiquette pour les 2 serveurs + tester fin garantie orange 553 print etiquette pour les 2 serveurs + tester fin garantie orange
@@ -563,9 +565,45 @@ Vues génériques (index et view) : @@ -563,9 +565,45 @@ Vues génériques (index et view) :
563 - Suivis.statut => "en cours" ou "à terminer" => à calculer auto 565 - Suivis.statut => "en cours" ou "à terminer" => à calculer auto
564 566
565 567
  568 + *)
  569 + Rendre modifiable la config via page web "Gérer les champs obligatoires"
  570 + - OFF_nom_du_champ : 'libellé'
  571 + - Restaurer la config par défaut
  572 + - Réactiver une variable : OFF_nom_du_champ => nom_du_champ
  573 +
  574 +
  575 +saisir les personnes du gt2i et de tous les groupes...
  576 +
  577 +groupe.users associés : ajouter "(responsable)" when relevant
  578 +
  579 +comment faire un tri sur la dernière colonne des stats (connexDurAvg) ?
  580 +
  581 +erreur download depuis page documents/ (ou depuis vue du matériel) sur inventirap : erreur 404 (action impossible)
  582 +
  583 +TODO config fields :
  584 +- réadapter lecture config à new file format
  585 +- compléter le fichier config avec tous les champs possibles à chaque lot !!!
  586 +- fichier read-write par web server
  587 +- ./UPDATE cral et ip2i + new config file
  588 +- pub
  589 +- soigner la présentation du form (peu lisible now)
  590 +
  591 +
  592 + - (b) Bugfix fournisseur perdu (et champ vide qui n'est plus modifiable !) après validation du matos
  593 + (quand il manque un champ pour valider), et pourtant bien enregistré dans listes des fournisseurs
  594 +
  595 +
566 ======= CHANGES ======= 596 ======= CHANGES =======
567 597
568 ------- 598 -------
  599 +22/11/2021 v5.3.1-3.7.9
  600 + - (e) BIG NEW FEATURE : Configuration des champs obligatoires et readonly se fait maintenant via page WEB !!!
  601 + => plus besoin de modifier manuellement le fichier de config texte yaml
  602 + - (e) ajout lien vers stats utilisateurs depuis page accueil superadmin
  603 + - (e) modif page Apropos
  604 +
  605 +
  606 +-------
569 15/11/2021 v5.3.0-3.7.9 607 15/11/2021 v5.3.0-3.7.9
570 - (e) Refactorisation : Vue détaillée users (view) générique avec contenu enrichi ("(responsable)") 608 - (e) Refactorisation : Vue détaillée users (view) générique avec contenu enrichi ("(responsable)")
571 - (e) Refactorisation : Vue liste users (index) générique avec contenu enrichi ("(responsable)") 609 - (e) Refactorisation : Vue liste users (index) générique avec contenu enrichi ("(responsable)")
@@ -52,8 +52,8 @@ Logiciel testé et validé sur les configurations suivantes : @@ -52,8 +52,8 @@ Logiciel testé et validé sur les configurations suivantes :
52 52
53 -------------------------------------------------------------------------------------------- 53 --------------------------------------------------------------------------------------------
54 54
55 -Date: 15/11/2021  
56 -Version: v5.3.0-3.7.9 55 +Date: 22/11/2021
  56 +Version: v5.3.1-3.7.9
57 57
58 58
59 HISTORIQUE DES CHANGEMENTS DE VERSION : voir le fichier CHANGES.txt (ou la page web /pages/changes) 59 HISTORIQUE DES CHANGEMENTS DE VERSION : voir le fichier CHANGES.txt (ou la page web /pages/changes)
config/app_labinvent_mandatory_fields.default.yml deleted
@@ -1,616 +0,0 @@ @@ -1,616 +0,0 @@
1 -# FICHIER DE CONFIGURATION PAR DÉFAUT POUR LES CHAMPS D'UN MATÉRIEL  
2 -  
3 -  
4 -# Activer la possibilité de commander un matériel (au service gestion) en cliquant sur un bouton "Commander" ?  
5 -# L'étape "A commander" (status TOBEORDERED) devient alors une étape intermédiaire OPTIONNELLE entre CREATED et VALIDATED  
6 -# Les transitions de statut d'un materiel sont alors : CREATED => [ TEBEORDERED (à commander) ] => VALIDATED => TOBEARCHIVED => ARCHIVED  
7 -HAS_ORDER_BUTTON: true  
8 -#HAS_ORDER_BUTTON: false  
9 -  
10 -  
11 -  
12 -# ************************************************************  
13 -# ************** CHAMPS INITIALEMENT READONLY ****************  
14 -# ************************************************************  
15 -  
16 -# Un champ peut etre readonly pour 3 raisons :  
17 -# - soit parce qu’il a une valeur par défaut non modifiable (ex: Acheteur = celui qui crée la fiche),  
18 -# - soit parce qu’il est réservé à l’Administration (champs administratifs, « Admin only »),  
19 -# - soit parce qu’il a servi à valider une étape et ne doit donc plus être changé (sous peine d’invalider l’étape)  
20 -  
21 -UNEDITABLE_FIELDS:  
22 -  
23 - # #### CHAMPS GÉNÉRAUX ####  
24 -  
25 - #- designation  
26 -  
27 - #- will_stay  
28 -  
29 - #- description  
30 -  
31 - #- hors_service  
32 -  
33 - #- sur_categorie_id  
34 -  
35 - #- categorie_id  
36 -  
37 - #- sous_categorie_id  
38 -  
39 - #- groupes_thematique_id  
40 -  
41 - #- groupes_metier_id  
42 -  
43 - #- projet_id  
44 -  
45 - #- materiel_technique  
46 - #- materiel_administratif  
47 -  
48 - #- metrologie  
49 -  
50 - #- etiquette  
51 -  
52 - #- site_id  
53 -  
54 - #- lieu_detail  
55 -  
56 - #- date_acquisition  
57 -  
58 - #- date_reception  
59 -  
60 - # Garantie  
61 - #- duree_garantie  
62 - #- unite_duree_garantie  
63 - #- date_fin_garantie  
64 -  
65 - # Super Administrateur only  
66 - #- status  
67 -  
68 - #- numero_serie  
69 -  
70 - # Utilisateur du materiel (destinataire du bien)  
71 - #- nom_user  
72 -  
73 - # - Acheteur (le Créateur de la fiche) - Un role "Utilisateur" ne peut pas changer ça, c'est par défaut lui-même  
74 - - nom_responsable (sauf Responsable, Administration)  
75 -  
76 - #- nom_ancien_responsable  
77 -  
78 - #- resp_credit  
79 -  
80 - #- gestionnaire_id  
81 -  
82 - #- fournisseur_id  
83 -  
84 - #- organisme_id  
85 -  
86 - #- prix_ht  
87 -  
88 - #- budgets  
89 -  
90 -  
91 - # #### CHAMPS ADMINISTRATIFS : ####  
92 -  
93 - # Entité(s) dépensière(s) (budget(s)) // ligne budgétaire (sur quel(s) budget(s)) ou entité(s) dépensière(s) ?)  
94 - - eotp (sauf Administration)  
95 - - numero_commande (sauf Administration)  
96 - - numero_inventaire_organisme (sauf Administration)  
97 - - numero_inventaire_old (sauf Administration)  
98 - - numero_laboratoire # READONLY toujours car généré automatiquement  
99 -  
100 -  
101 -  
102 -  
103 -  
104 -  
105 -# ***************************************************************  
106 -# ************** LOT 0 - CRÉATION FICHE MATÉRIEL ****************  
107 -# ***************************************************************  
108 -  
109 -# Champs OBLIGATOIRES pour CRÉER une fiche Matériel  
110 -# - Aucun champ  
111 -#MANDATORY_FIELDS_FOR_LOT0: []  
112 -# - Au moins un champ  
113 -MANDATORY_FIELDS_FOR_LOT0:  
114 -  
115 - # Infos toujours obligatoires (cachées car calculées automatiquement)  
116 - #'status',  
117 - #'tobeordered',  
118 -  
119 - designation: 'Désignation'  
120 -  
121 - description: 'Description'  
122 -  
123 - sur_categorie_id: 'Domaine'  
124 - categorie_id: 'Catégorie'  
125 -  
126 - # - Acheteur (le Créateur de la fiche)  
127 - nom_responsable: "Nom de l'Acheteur"  
128 - #// (rempli automatiquement)  
129 - email_responsable: "Email de l'Acheteur"  
130 -  
131 - # - Utilisateur  
132 - nom_user: "Nom de l'utilisateur"  
133 -  
134 - # Calculé auto au moment du save()  
135 - #'numero_laboratoire',  
136 -  
137 - # ******* END OF MANDATORY_FIELDS_LOT0 ********  
138 -  
139 -  
140 -# Liste des champs qui sont NON MODIFIABLES APRÈS la CRÉATION d'une fiche  
141 -# Attention, les champs UNEDITABLE_FIELDS (voir au début du fichier) sont aussi pris en compte (donc, inutile de les répéter)  
142 -  
143 -# - Aucun champ  
144 -UNEDITABLE_FIELDS_AFTER_LOT0: []  
145 -  
146 -# - Au moins un champ  
147 -#UNEDITABLE_FIELDS_AFTER_LOT0:  
148 -  
149 - # #### CHAMPS GÉNÉRAUX ####  
150 -  
151 - #- designation  
152 -  
153 - #- will_stay  
154 -  
155 - #- description  
156 -  
157 - #- hors_service  
158 -  
159 - #- sur_categorie_id  
160 -  
161 - #- categorie_id  
162 -  
163 - #- sous_categorie_id  
164 -  
165 - #- groupes_thematique_id  
166 -  
167 - #- groupes_metier_id  
168 -  
169 - #- projet_id  
170 -  
171 - #- materiel_technique  
172 - #- materiel_administratif  
173 -  
174 - #- metrologie  
175 -  
176 - #- etiquette  
177 -  
178 - #- site_id  
179 -  
180 - #- lieu_detail  
181 -  
182 - #- date_acquisition  
183 -  
184 - #- date_reception  
185 -  
186 - # Garantie  
187 - #- duree_garantie  
188 - #- unite_duree_garantie  
189 - #- date_fin_garantie  
190 -  
191 - # Super Administrateur only  
192 - #- status  
193 -  
194 - #- numero_serie  
195 -  
196 - # Utilisateur du materiel (destinataire du bien)  
197 - #- nom_user  
198 -  
199 - # - Acheteur (le Créateur de la fiche) - Un role "Utilisateur" ne peut pas changer ça, c'est par défaut lui-même  
200 - #- nom_responsable (sauf Responsable, Administration)  
201 -  
202 - #- nom_ancien_responsable  
203 -  
204 - #- resp_credit  
205 -  
206 - #- gestionnaire_id  
207 -  
208 - #- fournisseur_id  
209 -  
210 - #- organisme_id  
211 -  
212 - #- prix_ht  
213 -  
214 - #- budgets  
215 -  
216 - # #### CHAMPS ADMINISTRATIFS : ####  
217 - #- eotp (sauf Administration)  
218 - #- numero_commande (sauf Administration)  
219 - #- numero_laboratoire # READONLY toujours car généré automatiquement  
220 - #- numero_inventaire_organisme (sauf Administration)  
221 - #- numero_inventaire_old (sauf Administration)  
222 -  
223 -  
224 -# Champs NON OBLIGATOIRES MAIS FORTEMENT RECOMMANDÉS APRÈS COMMANDE  
225 -# Ces champs ne seront pas demandés à la saisie, mais un simple rappel sera affiché sur la vue détaillée du matériel (tant que ces éléments sont absents)  
226 -# Attention, les champs RECOMMENDED_FIELDS_XXX définis plus haut sont aussi pris en compte (donc, inutile de les répéter)  
227 -# - Aucun champ  
228 -RECOMMENDED_FIELDS_AFTER_LOT0: []  
229 -  
230 -  
231 -  
232 -  
233 -  
234 -  
235 -  
236 -  
237 -# ******************************************************************************  
238 -# ************** LOT 1 - COMMANDE (DEMANDE D'ACHAT) (optionnel) ****************  
239 -# ******************************************************************************  
240 -  
241 -# Champs OBLIGATOIRES POUR passer la COMMANDE (La commande est une action optionnelle)  
242 -# Attention, les champs MANDATORY_FIELDS_XXX définis plus haut sont aussi pris en compte (donc, inutile de les répéter)  
243 -# - Aucun champ  
244 -#MANDATORY_FIELDS_FOR_LOT1: []  
245 -# - Au moins un champ  
246 -MANDATORY_FIELDS_FOR_LOT1:  
247 -  
248 - # Infos toujours obligatoires (cachées car calculées automatiquement)  
249 - #'status',  
250 - #'tobeordered',  
251 -  
252 - #'hors_service', // O/N  
253 -  
254 - ##designation: 'Désignation'  
255 -  
256 - ##description: 'Description'  
257 -  
258 - #'permanent',  
259 - #'will_stay', // O/N  
260 -  
261 - ##sur_categorie_id: 'Domaine'  
262 - ##categorie_id: 'Catégorie'  
263 -  
264 - # - Utilisateur  
265 - #nom_user: "Nom de l'utilisateur de ce matériel"  
266 -  
267 - # - Acheteur  
268 - #nom_responsable: 'Nom du responsable'  
269 - # (rempli automatiquement)  
270 - #email_responsable: 'Email du responsable'  
271 -  
272 - # Calculé auto au moment du save()  
273 - #'numero_laboratoire',  
274 -  
275 - organisme_id: 'Organisme'  
276 -  
277 - prix_ht: 'Prix HT'  
278 -  
279 - # (par défaut = acheteur)  
280 - resp_credit: 'Responsable du crédit'  
281 -  
282 - # Gestionnaire de référence (recevra la commande)  
283 - gestionnaire_id: 'Gestionnaire de référence'  
284 -  
285 - # Fournisseur  
286 - fournisseur_id: 'Fournisseur'  
287 -  
288 - # Utilisé par la Gestion pour remplir le champ eotp  
289 - budgets: 'Budgets'  
290 -  
291 - # INFOS ADMINISTRATIVES  
292 - # - EOTP : obligatoire seulement dans LOT2  
293 -  
294 - # - Devis joint : c'est un champ virtuel, il n'existe pas physiquement (sauf dans la table Documents)  
295 - DOC_DEVIS: 'Devis'  
296 - #DOC_BC: "Bon de Commande"  
297 - #DOC_BL: "Bon de Livraison"  
298 - #DOC_FACTURE: "Facture"  
299 -  
300 -  
301 - # ******* END OF MANDATORY_FIELDS_LOT1 ********  
302 -  
303 -  
304 -# Liste des champs NON MODIFIABLES APRÈS la demande d'ACHAT (c'est à dire APRÈS avoir fait la "COMMANDE")  
305 -# Attention, les champs UNEDITABLE_FIELDS_XXX définis plus haut sont aussi pris en compte (donc, inutile de les répéter)  
306 -# - Aucun champ  
307 -#UNEDITABLE_FIELDS_AFTER_LOT1: []  
308 -# - Au moins un champ  
309 -UNEDITABLE_FIELDS_AFTER_LOT1:  
310 -  
311 - # #### CHAMPS GÉNÉRAUX ####  
312 -  
313 - #- designation  
314 -  
315 - #- will_stay  
316 -  
317 - #- description  
318 -  
319 - #- hors_service  
320 -  
321 - # On ne devrait pas pouvoir changer la NATURE du bien  
322 - # Domaine & Catégorie  
323 - - sur_categorie_id (sauf Administration) # Domaine  
324 - - categorie_id (sauf Administration) # Catégorie  
325 - #- sous_categorie_id  
326 -  
327 - #- groupes_thematique_id  
328 -  
329 - #- groupes_metier_id  
330 -  
331 - #- projet_id  
332 -  
333 - #- materiel_technique  
334 - #- materiel_administratif  
335 -  
336 - #- metrologie  
337 -  
338 - #- etiquette  
339 -  
340 - #- site_id  
341 -  
342 - #- lieu_detail  
343 -  
344 - #- date_acquisition  
345 -  
346 - #- date_reception  
347 -  
348 - # Garantie  
349 - #- duree_garantie  
350 - #- unite_duree_garantie  
351 - #- date_fin_garantie  
352 -  
353 - # Super Administrateur only  
354 - #- status  
355 -  
356 - #- numero_serie  
357 -  
358 - # Utilisateur du materiel (destinataire du bien)  
359 - #- nom_user  
360 -  
361 - # - Acheteur (le Créateur de la fiche) - Un role "Utilisateur" ne peut pas changer ça, c'est par défaut lui-même  
362 - #- nom_responsable (sauf Responsable, Administration)  
363 -  
364 - #- nom_ancien_responsable  
365 -  
366 - # (par défaut = acheteur)  
367 - - resp_credit (sauf Administration)  
368 -  
369 - - gestionnaire_id (sauf Administration)  
370 -  
371 - # Fournisseur  
372 - - fournisseur_id (sauf Administration)  
373 -  
374 - - organisme_id (sauf Administration)  
375 -  
376 - - prix_ht (sauf Administration)  
377 -  
378 - # Utilisé par la Gestion pour remplir le champ eotp  
379 - - budgets (sauf Administration)  
380 -  
381 -  
382 - # #### CHAMPS VIRTUELS : doc attachés ####  
383 - # Ces champs virtuels n'existent pas physiquement (sauf dans la table Documents)  
384 - # On indique ici les documents attachés à la fiche matériel, qui ne peuvent plus être ni modifiés ni supprimés  
385 - # - Devis  
386 - # Le devis attaché au matériel commandé n'est ni modifiable ni supprimable  
387 - - DOC_DEVIS  
388 - # - Bon de Commande (si un BC a été joint à la fiche, on ne peut plus le modifier ni le supprimer)  
389 - - DOC_BC (sauf Administration)  
390 - # - Bon de Livraison  
391 - #- DOC_BL  
392 - # - Facture  
393 - #- DOC_FACTURE  
394 -  
395 -  
396 - # #### CHAMPS ADMINISTRATIFS : ####  
397 - #- eotp (sauf Administration)  
398 - #- numero_commande (sauf Administration)  
399 - #- numero_laboratoire # READONLY toujours car généré automatiquement  
400 - #- numero_inventaire_organisme (sauf Administration)  
401 - #- numero_inventaire_old (sauf Administration)  
402 -  
403 -  
404 -  
405 -# Champs NON OBLIGATOIRES MAIS FORTEMENT RECOMMANDÉS APRÈS COMMANDE  
406 -# Ces champs ne seront pas demandés à la saisie, mais un simple rappel sera affiché sur la vue détaillée du matériel (tant que ces éléments sont absents)  
407 -# Attention, les champs RECOMMENDED_FIELDS_XXX définis plus haut sont aussi pris en compte (donc, inutile de les répéter)  
408 -# - Aucun champ  
409 -RECOMMENDED_FIELDS_AFTER_LOT1: []  
410 -#RECOMMENDED_FIELDS_AFTER_LOT1:  
411 - # - Etiquette posée sur le matériel  
412 - #etiquette: "d'imprimer l'étiquette associée et de la coller sur le matériel"  
413 -  
414 -  
415 -  
416 -  
417 -  
418 -  
419 -  
420 -  
421 -# ***************************************************************************  
422 -# ************** LOT 2 - VALIDATION (matériel livré et payé) ****************  
423 -# ***************************************************************************  
424 -  
425 -# Champs OBLIGATOIRES POUR VALIDER la livraison  
426 -# Attention, les champs MANDATORY_FIELDS_XXX définis plus haut sont aussi pris en compte (donc, inutile de les répéter)  
427 -# - Aucun champ  
428 -#MANDATORY_FIELDS_FOR_LOT2: []  
429 -# - Au moins un champ  
430 -MANDATORY_FIELDS_FOR_LOT2:  
431 -  
432 - #//'fournisseur_id' => 'Fournisseur',  
433 - #//'fournisseur' => 'Fournisseur',  
434 -  
435 - date_acquisition: "Date d'achat"  
436 -  
437 - date_reception: 'Date de livraison'  
438 -  
439 - site_id: 'Site'  
440 -  
441 - #TODO: seulement si prix > 10K€  
442 - lieu_detail: 'Lieu de stockage'  
443 - #numero_serie: 'S/N'  
444 -  
445 - #// INFOS ADMINISTRATIVES :  
446 -  
447 - #// La Gestion doit remplir ce champ a partir des infos qui sont dans le champ "budget" (rempli par acheteur)  
448 - #// ligne budgétaire (sur quel(s) budget(s)) ou entité(s) dépensière(s)  
449 - eotp: 'Entité(s) dépensière(s) (budget(s))'  
450 -  
451 - numero_commande: 'Num. BC'  
452 -  
453 - # Docs attachés obligatoires pour valider  
454 - DOC_DEVIS: 'Devis'  
455 - #DOC_BC: "Bon de Commande"  
456 - #DOC_BL: "Bon de Livraison"  
457 - #DOC_FACTURE: "Facture"  
458 -  
459 -  
460 - # ******* END OF $MANDATORY_FIELDS_LOT2 ********  
461 -  
462 -  
463 -  
464 -  
465 -# Champs NON OBLIGATOIRES MAIS FORTEMENT RECOMMANDÉS APRÈS VALIDATION de la livraison  
466 -# Ces champs ne seront pas demandés à la saisie, mais un simple rappel sera affiché sur la vue détaillée du matériel (tant que ces éléments sont absents)  
467 -# Attention, les champs RECOMMENDED_FIELDS_XXX définis plus haut sont aussi pris en compte (donc, inutile de les répéter)  
468 -# - Aucun champ  
469 -#RECOMMENDED_FIELDS_AFTER_LOT2: []  
470 -# - Au moins un champ  
471 -RECOMMENDED_FIELDS_AFTER_LOT2:  
472 -  
473 -  
474 - # - Etiquette posée sur le matériel  
475 - etiquette: "d'imprimer l'étiquette associée et de la coller sur le matériel"  
476 -  
477 - # - Numéro inventaire tutelles  
478 - # On aurait pu rendre cet élément obligatoire mais on ne peut pas l'exiger au moment de la livraison  
479 - # car le gestionnaire n'a cette info que lorsque "Service fait CNRS" (il récupère alors le n° inventaire "Tutelle" sur GESLAB)  
480 - numero_inventaire_organisme: "de renseigner le champ 'N° inventaire comptable/tutelles (Organisme)'"  
481 -  
482 -  
483 - # DOCS ATTACHÉS recommandés après validation  
484 - #DOC_DEVIS: "d'ajouter un Devis"  
485 - DOC_BC: "d'ajouter le Bon de Commande"  
486 - DOC_BL: "d'ajouter le Bon de Livraison"  
487 - DOC_FACTURE: "d'ajouter la Facture"  
488 -  
489 -  
490 -  
491 -  
492 -  
493 -# Liste des champs qui sont NON MODIFIABLES APRÈS la VALIDATION (livraison)  
494 -# Attention, les champs UNEDITABLE_FIELDS_XXX définis plus haut sont aussi pris en compte  
495 -# - Aucun champ  
496 -#UNEDITABLE_FIELDS_AFTER_LOT2: []  
497 -# - Au moins un champ  
498 -UNEDITABLE_FIELDS_AFTER_LOT2:  
499 -  
500 - # #### CHAMPS GÉNÉRAUX ####  
501 -  
502 - #- designation  
503 -  
504 - #- will_stay  
505 -  
506 - #- description  
507 -  
508 - #- hors_service  
509 -  
510 - - sur_categorie_id  
511 - - categorie_id  
512 -  
513 - #- sous_categorie_id  
514 -  
515 - #- groupes_thematique_id  
516 -  
517 - #- groupes_metier_id  
518 -  
519 - #- projet_id  
520 -  
521 - #- materiel_technique  
522 - #- materiel_administratif  
523 -  
524 - #- metrologie  
525 -  
526 - #- etiquette  
527 -  
528 - #- site_id  
529 -  
530 - #- lieu_detail  
531 -  
532 - - date_acquisition  
533 - - date_reception  
534 -  
535 - # Garantie  
536 - #- duree_garantie  
537 - #- unite_duree_garantie  
538 - #- date_fin_garantie  
539 -  
540 - # Super Administrateur only  
541 - #- status  
542 -  
543 - #- numero_serie  
544 -  
545 - # Utilisateur du materiel (destinataire du bien)  
546 - #- nom_user  
547 -  
548 - # - Acheteur (le Créateur de la fiche) - Un role "Utilisateur" ne peut pas changer ça, c'est par défaut lui-même  
549 - #- nom_responsable (sauf Responsable, Administration)  
550 -  
551 - #- nom_ancien_responsable  
552 -  
553 - #- resp_credit  
554 -  
555 - #- gestionnaire_id  
556 -  
557 - #- fournisseur_id  
558 -  
559 - #- organisme_id  
560 -  
561 - #- prix_ht  
562 -  
563 - #- budgets  
564 -  
565 -  
566 - # #### CHAMPS ADMINISTRATIFS ####  
567 -  
568 - - eotp  
569 - - numero_commande  
570 - # READONLY toujours car généré automatiquement  
571 - #- numero_laboratoire  
572 - #- numero_inventaire_organisme  
573 - #- numero_inventaire_old  
574 -  
575 -  
576 - # DOCS ATTACHÉS qui ne sont plus ni supprimables ni modifiables  
577 - # - Devis  
578 - #- DOC_DEVIS  
579 - # - Bon de Commande  
580 - - DOC_BC  
581 - # - Bon de Livraison  
582 - - DOC_BL  
583 - # - Facture  
584 - - DOC_FACTURE  
585 -  
586 -  
587 -  
588 -  
589 -# ******************************************************************************  
590 -# ************** LOT 3 - ARCHIVAGE (matériel à sortir ou sorti) ****************  
591 -# ******************************************************************************  
592 -  
593 -# (TODO) Ce 3e niveau n'est pas encore implémenté, il est pour l'instant codé en dur dans le code  
594 -  
595 -# - Aucun champ  
596 -MANDATORY_FIELDS_FOR_LOT3: []  
597 -  
598 -# - Aucun champ  
599 -UNEDITABLE_FIELDS_AFTER_LOT3: []  
600 -# En fait, TOUS LES CHAMPS SONT READONLY SAUF : description, ...  
601 -  
602 -  
603 -  
604 -  
605 -  
606 -  
607 -  
608 -  
609 -  
610 -  
611 -# Astuce utilisable :  
612 -# Pour faire : MANDATORY_FIELDS_LOT2 = MANDATORY_FIELDS_LOT1  
613 -# On fait comme ceci :  
614 -#MANDATORY_FIELDS_LOT1: &lot1  
615 -#MANDATORY_FIELDS_LOT2: *lot1  
616 -  
config/bootstrap.php
@@ -36,6 +36,10 @@ use Yaml\Configure\Engine\YamlConfig; @@ -36,6 +36,10 @@ use Yaml\Configure\Engine\YamlConfig;
36 */ 36 */
37 require __DIR__ . '/paths.php'; 37 require __DIR__ . '/paths.php';
38 38
  39 +/* EP 2021-11 : mes propres constantes */
  40 +define('CONFIG_MATERIEL_FIELDS_FILE_NAME', 'app_labinvent_mandatory_fields');
  41 +
  42 +
39 // Use composer to load the autoloader. 43 // Use composer to load the autoloader.
40 require ROOT . DS . 'vendor' . DS . 'autoload.php'; 44 require ROOT . DS . 'vendor' . DS . 'autoload.php';
41 45
@@ -106,17 +110,31 @@ try { @@ -106,17 +110,31 @@ try {
106 } 110 }
107 111
108 // (EP 2021 09 Ajout nouveaux fichiers config pour les champs obligatoires (et les autorisations)) 112 // (EP 2021 09 Ajout nouveaux fichiers config pour les champs obligatoires (et les autorisations))
109 -$config_mandatory_fields_file_name = 'app_labinvent_mandatory_fields'; 113 +//$config_mandatory_fields_file_name = 'app_labinvent_mandatory_fields';
110 // Si le fichier de conf n'existe pas, on le crée en copiant le fichier par défaut 114 // Si le fichier de conf n'existe pas, on le crée en copiant le fichier par défaut
111 -if ( !file_exists(CONFIG.DS.$config_mandatory_fields_file_name.'.yml') )  
112 - copy(CONFIG.DS.$config_mandatory_fields_file_name.'.default.yml', CONFIG.DS.$config_mandatory_fields_file_name.'.yml'); 115 +//if ( !file_exists(CONFIG.DS.$config_mandatory_fields_file_name.'.yml') )
  116 +//$config_matos_full_file_name = $config_matos_full_file_name_default = CONFIG.DS.CONFIG_MATERIEL_FIELDS_FILE_NAME;
  117 +$config_matos_full_file_name = $config_matos_full_file_name_default = CONFIG_MATERIEL_FIELDS_FILE_NAME;
  118 +//$config_matos_full_file_name_default = $config_matos_full_file_name;
  119 +$config_matos_full_file_name .= '.yml';
  120 +$config_matos_full_file_name_default .= '.default.yml';
  121 +//if ( !file_exists($config_matos_full_file_name) ) copy($config_matos_full_file_name_default, $config_matos_full_file_name);
113 try { 122 try {
114 - Configure::config('yaml', new YamlConfig());  
115 - Configure::load($config_mandatory_fields_file_name, 'yaml'); 123 + Configure::config('my_yaml_engine', new YamlConfig());
  124 + //Configure::load($config_mandatory_fields_file_name, 'yaml', true);
  125 + Configure::load(CONFIG_MATERIEL_FIELDS_FILE_NAME, 'my_yaml_engine');
116 //Configure::load('app_labinvent_mandatory_fields_IP2I', 'yaml'); 126 //Configure::load('app_labinvent_mandatory_fields_IP2I', 'yaml');
117 //Configure::load('app_labinvent_authorizations', 'yaml'); 127 //Configure::load('app_labinvent_authorizations', 'yaml');
118 } catch (\Exception $e) { 128 } catch (\Exception $e) {
119 - die('config/bootstrap.php: Unable to load yaml config file'); 129 + echo("<br>config/bootstrap.php: Impossible de charger le fichier de configuration des champs matériels (".CONFIG.DS.CONFIG_MATERIEL_FIELDS_FILE_NAME.".yml)");
  130 + echo("<br>- soit ce fichier n'est pas accessible en lecture par le serveur web (attention, il faut aussi qu'il soit accessible en écriture)");
  131 + echo("<br>- soit il n'existe pas => dans ce cas, créez le en faisant une copie du fichier de configuration par défaut :");
  132 + echo("<br> cd ".CONFIG);
  133 + echo("<br> cp $config_matos_full_file_name_default $config_matos_full_file_name");
  134 + echo("<br> chown webserver_user_name $config_matos_full_file_name");
  135 + echo("<br> chmod 600 $config_matos_full_file_name");
  136 + echo("<br> (si vous ne voulez pas faire le chown, faite plutot un chmod 666, moins propre, mais marche aussi)");
  137 + die();
120 } 138 }
121 139
122 // Load an environment local configuration file. 140 // Load an environment local configuration file.
src/Controller/ConfigurationFieldsController.php 0 → 100644
@@ -0,0 +1,308 @@ @@ -0,0 +1,308 @@
  1 +<?php
  2 +namespace App\Controller;
  3 +
  4 +use App\Controller\AppController;
  5 +use App\Form\ConfigurationFieldsForm;
  6 +use Cake\Core\Configure;
  7 +
  8 +use Symfony\Component\Yaml\Yaml;
  9 +
  10 +
  11 +// modelless form : voir https://book.cakephp.org/3/en/core-libraries/form.html
  12 +class ConfigurationFieldsController extends AppController
  13 +{
  14 +
  15 + /*
  16 + * @Override
  17 + *
  18 + * Initialisation des autorisations pour les actions spécifiques à ce controleur
  19 + *
  20 + */
  21 + protected function setAuthorizations() {
  22 +
  23 + // On supprime les autres actions par défaut (add, view, index, find)
  24 + //foreach (array_keys($this->is_authorized_action[]) as $a) if ($a != 'display') unset($this->is_authorized_action[$a]);
  25 + $this->is_authorized_action = [];
  26 +
  27 + // admin (+) only
  28 + $this->setAuthorizationsForAction('index', -1, ['admin'=>0, 'super'=>0]);
  29 + $this->setAuthorizationsForAction('view', -1, ['admin'=>0, 'super'=>0]);
  30 + $this->setAuthorizationsForAction('edit', -1, ['admin'=>0, 'super'=>0]);
  31 + $this->setAuthorizationsForAction('resetToDefault', -1, ['admin'=>0, 'super'=>0]);
  32 + // tous
  33 + //$this->setAuthorizationsForAction('index', 0);
  34 + // Superadmin only :
  35 + //$this->setAuthorizationsForAction('index', -1, ['super'=>0]);
  36 +
  37 + }
  38 +
  39 + public function index() {
  40 + $this->view();
  41 + }
  42 +
  43 + public function view() {
  44 +
  45 + $this->edit(true);
  46 + // Lecteurs configurés : 'default', 'yaml'
  47 + //debug(Configure::configured());
  48 +
  49 + //$fields = Configure::readOrFail('MANDATORY_FIELDS_FOR_LOT'.$lot_num);
  50 + /*
  51 + $fields = Configure::readOrFail('MANDATORY_AND_READONLY_FIELDS');
  52 + $this->set(compact('fields'));
  53 + */
  54 + }
  55 +
  56 + public function resetToDefault() {
  57 + $config_mandatory_fields_file_name = CONFIG.DS.CONFIG_MATERIEL_FIELDS_FILE_NAME.'.yml';
  58 + $config_mandatory_fields_file_name_default = CONFIG.DS.CONFIG_MATERIEL_FIELDS_FILE_NAME.'.default.yml';
  59 +
  60 + $config_default = Yaml::parse(file_get_contents($config_mandatory_fields_file_name_default)); //parse le fichier
  61 + $config_default_as_yaml = Yaml::dump($config_default); //remet en yaml
  62 + if (! file_put_contents($config_mandatory_fields_file_name, $config_default_as_yaml) )
  63 + throw new \ErrorException("Impossible de remettre la configuration des champs de matériel aux valeurs par défaut - le fichier de config $config_mandatory_fields_file_name n'est pas accessible en écriture");
  64 +
  65 + $this->Flash->success("La configuration des champs de matériel a bien été réinitialisée aux valeurs par défaut");
  66 + return $this->redirect(['action' => 'view']);
  67 + }
  68 +
  69 +
  70 + public function edit($READONLY=false) {
  71 +
  72 + $contact = new ConfigurationFieldsForm();
  73 + //debug($contact);
  74 +
  75 + // - En mode POST
  76 + if ($this->request->is('post')) {
  77 + /*
  78 + * We use the execute() method to run our form’s _execute() method only when the data is valid,
  79 + * and set flash messages accordingly.
  80 + * We could have also used the validate() method to only validate the request data:
  81 + * $isValid = $form->validate($this->request->getData());
  82 + */
  83 + //debug($this->request->getData());
  84 +
  85 + // - OK
  86 + if ($contact->execute($this->request->getData())) {
  87 + $fieldsets = $this->request->getData();
  88 + //debug($data);
  89 + $contact->setData($fieldsets);
  90 +
  91 + // On sauvegarde le contenu de $fieldsets dans le fichier de config (array => yaml)
  92 +
  93 + //debug($contact);
  94 + // See : https://book.cakephp.org/3/en/development/configuration.html#writing-configuration-data
  95 + /*
  96 + Configure::write('Company.name','Pizza, Inc.');
  97 + Configure::write('Company.slogan','Pizza for your body and soul');
  98 + // idem :
  99 + Configure::write('Company', [
  100 + 'name' => 'Pizza, Inc.',
  101 + 'slogan' => 'Pizza for your body and soul'
  102 + ]);
  103 + */
  104 + //$data1 = Configure::read('MANDATORY_AND_READONLY_FIELDS');
  105 + //debug($data1);
  106 + /*
  107 + Configure::write('MANDATORY_AND_READONLY_FIELDS',$data);
  108 + $data2 = Configure::read('MANDATORY_AND_READONLY_FIELDS');
  109 + */
  110 + //debug($data2);
  111 + //$name = $contact->getData('name');
  112 + /* NE MARCHE PAS, WHY ???
  113 + $config_mandatory_fields_file_name = 'app_labinvent_mandatory_fields';
  114 + if ( Configure::dump($config_mandatory_fields_file_name, 'my_yaml', ['MANDATORY_AND_READONLY_FIELDS']) )
  115 + $this->Flash->success("La configuration des champs matériels a bien été sauvegardée");
  116 + */
  117 + //$config_mandatory_fields_file_name = CONFIG.DS.'app_labinvent_mandatory_fields'.'.yml';
  118 + $config_mandatory_fields_file_name = CONFIG.DS.CONFIG_MATERIEL_FIELDS_FILE_NAME.'.yml';
  119 + // Symfony YAML component : https://symfony.com/doc/current/components/yaml.html
  120 + /*
  121 + $array = Yaml::parse(file_get_contents($config_mandatory_fields_file_name)); //parse le fichier
  122 + debug($array);
  123 + $res = Yaml::dump($array); //remet en yaml
  124 + */
  125 + $fieldsets_new = [];
  126 + $fieldsets_new['MANDATORY_AND_READONLY_FIELDS']=$fieldsets;
  127 + //debug($fieldsets_new);
  128 +
  129 + $fieldsets_as_yaml = Yaml::dump($fieldsets_new); //remet en yaml
  130 + //debug($res); exit;
  131 + if (! file_put_contents($config_mandatory_fields_file_name, $fieldsets_as_yaml) )
  132 + throw new \ErrorException("Impossible d'enregistrer la configuration des champs de matériel - le fichier de config $config_mandatory_fields_file_name n'est pas accessible en écriture");
  133 + $this->Flash->success("La configuration des champs de matériel a bien été sauvegardée");
  134 + return $this->redirect(['action' => 'view']);
  135 + }
  136 +
  137 + // - KO
  138 + else {
  139 + // Once a form has been validated you can retrieve the errors from it:
  140 + $errors = $form->getErrors(); // $form->errors(); // prior to 3.7.0
  141 + debug($errors);
  142 + /* $errors contains
  143 + [
  144 + 'email' => ['A valid email address is required']
  145 + ]
  146 + */
  147 + $this->Flash->error("La configuration n'a pas pu être enregistrée");
  148 + }
  149 + }
  150 +
  151 + // - En mode GET (ou POST avec erreur)
  152 +
  153 + if ($this->request->is('get')) {
  154 + /*
  155 + $contact->setData([
  156 + 'name' => 'John Doe',
  157 + 'email' => 'john.doe@example.com'
  158 + ]);
  159 + */
  160 + }
  161 +
  162 + $this->set('contact', $contact);
  163 +
  164 + $fieldsets = Configure::readOrFail('MANDATORY_AND_READONLY_FIELDS');
  165 + $this->set(compact('READONLY', 'fieldsets'));
  166 +
  167 + } // edit()
  168 +
  169 +
  170 + public function OLD_edit($READONLY=false)
  171 + {
  172 + $contact = new ConfigurationFieldsForm();
  173 + //debug($contact);
  174 +
  175 + // - En mode POST
  176 + if ($this->request->is('post')) {
  177 + /*
  178 + * We use the execute() method to run our form’s _execute() method only when the data is valid,
  179 + * and set flash messages accordingly.
  180 + * We could have also used the validate() method to only validate the request data:
  181 + * $isValid = $form->validate($this->request->getData());
  182 + */
  183 + //debug($this->request->getData());
  184 +
  185 + // - OK
  186 + if ($contact->execute($this->request->getData())) {
  187 + $fieldsets = $this->request->getData();
  188 + //debug($data);
  189 + $contact->setData($fieldsets);
  190 +
  191 + /*
  192 + * 1) On retouche $fieldsets pour qu'il puisse être sauvegardé au format fichier texte (yaml) :
  193 + *
  194 + * Ex:
  195 + 'designation' => [
  196 + 'selected' => '0',
  197 + 'labl' => 'commentaire',
  198 + 'roles' => [
  199 + (int) 0 => 'Responsable',
  200 + (int) 1 => 'Administration'
  201 + ]
  202 + ],
  203 + *
  204 + * - '.selected' : si = 0 => on préfixe le nom du champ par "OFF_"
  205 + * => 'OFF_designation'
  206 + * - '.labl' : on l'ajoute entre parenthèse à la suite du nom du champ
  207 + * => 'OFF_designation (commentaire)'
  208 + * - '.roles' : on les ajoute entre parenthèse à la fin du nom du champ =>
  209 + * => 'OFF_designation (commentaire) (sauf Responsable, Administration)'
  210 + */
  211 + $fieldsets_new = [];
  212 + $fieldsets_new['MANDATORY_AND_READONLY_FIELDS']=[];
  213 + //$fn = &$fieldsets_new['MANDATORY_AND_READONLY_FIELDS'];
  214 + foreach ($fieldsets as $fieldset_name=>$fields) {
  215 + $fieldsets_new['MANDATORY_AND_READONLY_FIELDS'][$fieldset_name] = [];
  216 + foreach ($fields as $field_name=>$attributes) {
  217 + $field_name_new = $field_name;
  218 + // - selected
  219 + if (! $attributes['selected'] )
  220 + $field_name_new = 'OFF_'.$field_name_new;
  221 + // - labl
  222 + if ($attributes['labl'])
  223 + $field_name_new .= ' ('. $attributes['labl'] .')';
  224 + // - roles
  225 + if (isset($attributes['except_roles']) && $attributes['except_roles'])
  226 + $field_name_new .= ' (sauf '. implode(',',$attributes['except_roles']) .')';
  227 + //debug($field_name_new);
  228 + $fieldsets_new['MANDATORY_AND_READONLY_FIELDS'][$fieldset_name][] = $field_name_new;
  229 + }
  230 + }
  231 + //debug($fieldsets_new); exit;
  232 +
  233 + // 2) On sauvegarde le contenu de $fieldsets dans le fichier de config (array => yaml)
  234 +
  235 + //debug($contact);
  236 + // See : https://book.cakephp.org/3/en/development/configuration.html#writing-configuration-data
  237 + /*
  238 + Configure::write('Company.name','Pizza, Inc.');
  239 + Configure::write('Company.slogan','Pizza for your body and soul');
  240 + // idem :
  241 + Configure::write('Company', [
  242 + 'name' => 'Pizza, Inc.',
  243 + 'slogan' => 'Pizza for your body and soul'
  244 + ]);
  245 + */
  246 + //$data1 = Configure::read('MANDATORY_AND_READONLY_FIELDS');
  247 + //debug($data1);
  248 + /*
  249 + Configure::write('MANDATORY_AND_READONLY_FIELDS',$data);
  250 + $data2 = Configure::read('MANDATORY_AND_READONLY_FIELDS');
  251 + */
  252 + //debug($data2);
  253 + //$name = $contact->getData('name');
  254 + /* NE MARCHE PAS, WHY ???
  255 + $config_mandatory_fields_file_name = 'app_labinvent_mandatory_fields';
  256 + if ( Configure::dump($config_mandatory_fields_file_name, 'my_yaml', ['MANDATORY_AND_READONLY_FIELDS']) )
  257 + $this->Flash->success("La configuration des champs matériels a bien été sauvegardée");
  258 + */
  259 + //$config_mandatory_fields_file_name = CONFIG.DS.'app_labinvent_mandatory_fields'.'.yml';
  260 + $config_mandatory_fields_file_name = CONFIG.DS.CONFIG_MATERIEL_FIELDS_FILE_NAME.'.yml';
  261 + // Symfony YAML component : https://symfony.com/doc/current/components/yaml.html
  262 + /*
  263 + $array = Yaml::parse(file_get_contents($config_mandatory_fields_file_name)); //parse le fichier
  264 + debug($array);
  265 + $res = Yaml::dump($array); //remet en yaml
  266 + */
  267 + $fieldsets_new_as_yaml = Yaml::dump($fieldsets_new); //remet en yaml
  268 + //debug($res); exit;
  269 + if ( file_put_contents($config_mandatory_fields_file_name, $fieldsets_new_as_yaml) )
  270 + $this->Flash->success("La configuration des champs matériels a bien été sauvegardée");
  271 + return $this->redirect(['action' => 'view']);
  272 + }
  273 +
  274 + // - KO
  275 + else {
  276 + // Once a form has been validated you can retrieve the errors from it:
  277 + $errors = $form->getErrors(); // $form->errors(); // prior to 3.7.0
  278 + debug($errors);
  279 + /* $errors contains
  280 + [
  281 + 'email' => ['A valid email address is required']
  282 + ]
  283 + */
  284 + $this->Flash->error("La configuration n'a pas pu être enregistrée");
  285 + }
  286 + }
  287 +
  288 + // - En mode GET (ou POST avec erreur)
  289 +
  290 + if ($this->request->is('get')) {
  291 + /*
  292 + $contact->setData([
  293 + 'name' => 'John Doe',
  294 + 'email' => 'john.doe@example.com'
  295 + ]);
  296 + */
  297 + }
  298 +
  299 + $this->set('contact', $contact);
  300 +
  301 + $fieldsets = Configure::readOrFail('MANDATORY_AND_READONLY_FIELDS');
  302 + $this->set(compact('READONLY', 'fieldsets'));
  303 +
  304 + } // edit()
  305 +
  306 +
  307 +
  308 +} // end of class
0 \ No newline at end of file 309 \ No newline at end of file
src/Controller/MaterielsController.php
@@ -1913,8 +1913,40 @@ class MaterielsController extends AppController { @@ -1913,8 +1913,40 @@ class MaterielsController extends AppController {
1913 } 1913 }
1914 */ 1914 */
1915 $recommended_fields = $this->Materiels->getRecommendedFieldsForMaterielStatus($materiel->status); 1915 $recommended_fields = $this->Materiels->getRecommendedFieldsForMaterielStatus($materiel->status);
1916 - foreach ($recommended_fields as $fname => $msg) {  
1917 - 1916 + //debug($recommended_fields);
  1917 + /*
  1918 + * Ex :
  1919 + [
  1920 + 'etiquette' => [
  1921 + 'selected' => '1',
  1922 + 'comment' => 'd'imprimer l'étiquette associée et de la coller sur le matériel',
  1923 + 'roles' => ''
  1924 + ],
  1925 + 'numero_inventaire_organisme' => [
  1926 + 'selected' => '1',
  1927 + 'comment' => 'de renseigner le champ 'N° inventaire comptable/tutelles _Organisme_'',
  1928 + 'roles' => ''
  1929 + ],
  1930 + 'DOC_BC' => [
  1931 + 'selected' => '1',
  1932 + 'comment' => 'd'ajouter le Bon de Commande',
  1933 + 'roles' => ''
  1934 + ],
  1935 + 'DOC_BL' => [
  1936 + 'selected' => '1',
  1937 + 'comment' => 'd'ajouter le Bon de Livraison',
  1938 + 'roles' => ''
  1939 + ],
  1940 + 'DOC_FACTURE' => [
  1941 + 'selected' => '1',
  1942 + 'comment' => 'd'ajouter la Facture',
  1943 + 'roles' => ''
  1944 + ]
  1945 + */
  1946 + //foreach ($recommended_fields as $fname => $msg) {
  1947 + //foreach ($recommended_fields as $fname => $attributes) {
  1948 + foreach ( array_keys($recommended_fields) as $fname ) {
  1949 +
1918 //debug($fname); 1950 //debug($fname);
1919 // - Documents attachés (champs virtuels) 1951 // - Documents attachés (champs virtuels)
1920 // DOC_DEVIS, DOC_BC, ... 1952 // DOC_DEVIS, DOC_BC, ...
@@ -1937,8 +1969,8 @@ class MaterielsController extends AppController { @@ -1937,8 +1969,8 @@ class MaterielsController extends AppController {
1937 1969
1938 } 1970 }
1939 // Les champs restants sont les champs manquants => message flash de rappel 1971 // Les champs restants sont les champs manquants => message flash de rappel
1940 - foreach ($recommended_fields as $fname => $reminder_msg)  
1941 - $this->Flash->set("Ce matériel est '".$entity->getNiceStatus()."' mais n'oubliez pas $reminder_msg"); 1972 + foreach ($recommended_fields as $fname => $attributes)
  1973 + $this->Flash->set("Ce matériel est '".$entity->getNiceStatus()."' mais n'oubliez pas ".$attributes['comment']);
1942 1974
1943 1975
1944 } // view 1976 } // view
@@ -1974,7 +2006,6 @@ class MaterielsController extends AppController { @@ -1974,7 +2006,6 @@ class MaterielsController extends AppController {
1974 // uniquement à cause de parent::add_or_edit() : 2006 // uniquement à cause de parent::add_or_edit() :
1975 $entity_name=null, array $associated_entities=[], $with_parent=false) { 2007 $entity_name=null, array $associated_entities=[], $with_parent=false) {
1976 2008
1977 - //debug("ici"); exit;  
1978 $this->myDebug("step 3: MaterielsController.add_or_edit()"); 2009 $this->myDebug("step 3: MaterielsController.add_or_edit()");
1979 $IS_EDIT = !$IS_ADD; 2010 $IS_EDIT = !$IS_ADD;
1980 2011
@@ -2080,72 +2111,11 @@ class MaterielsController extends AppController { @@ -2080,72 +2111,11 @@ class MaterielsController extends AppController {
2080 $materiel->numero_laboratoire = null; 2111 $materiel->numero_laboratoire = null;
2081 } 2112 }
2082 } 2113 }
2083 - //debug($materiel); exit;  
2084 - /*  
2085 - // ADD  
2086 - if ($IS_ADD) {  
2087 - // 1) on crée un materiel vide  
2088 - $materiel = $this->Materiels->newEntity();  
2089 - // - add_by_copy : COPIE de materiel (on a cliqué sur "Copier ce materiel") => id passé en argument  
2090 - if (isset($this->request->getAttribute('params')['pass'][0])) {  
2091 - // On récupère le materiel à copier et on le copie dans $materiel  
2092 - /S  
2093 - $materiel_to_copy = $this->Materiels->get($this->request->getAttribute('params')['pass'][0]);  
2094 - $materiel_to_copy = $materiel_to_copy->toArray();  
2095 - S/  
2096 - $materiel_to_copy = $this->e->toArray();  
2097 - //var_dump($materiel_to_copy);  
2098 - ///foreach ($materiel_to_copy as $key=>$value) $materiel->$key = $value;  
2099 - // IMPORTANT: validate=False car sinon, les données sont validées avant la copie,  
2100 - // et le numero_laboratoire est vu comme invalide car déjà utilisé et doit etre unique !!!  
2101 - // et on a pour résultat : "le matériel n'a pas pu être ajouté" (sans savoir pourquoi !!!)  
2102 - $materiel = $this->Materiels->patchEntity($materiel, $materiel_to_copy, ['validate' => false]);  
2103 - //TODO: On pourrait traiter les erreurs de validation déjà ici  
2104 - if ($materiel->errors()) {  
2105 - // traitement  
2106 - }  
2107 - // Du coup, on supprime le champ numero_laboratoire car il va être généré automatiquement  
2108 - unset($materiel->numero_laboratoire);  
2109 - // IMPORTANT: on ne doit pas laisser l'id égal à celui du matériel copié !!! il en faut un nouveau  
2110 - $materiel->id = NULL;  
2111 - //$materiel->id = False;  
2112 - //unset($materiel->id);  
2113 -  
2114 - /S  
2115 - $attribute="[original]";  
2116 - $materiel->$attribute = [];  
2117 - S/  
2118 - //$materiel->id = FALSE;  
2119 - //$materiel->'[new]' => true,  
2120 - }  
2121 - // - NOUVEAU materiel (on a cliqué sur "Nouveau materiel")  
2122 - else {  
2123 - //$materiel = $this->Materiels->newEntity();  
2124 - /S (EP 20200505 plus nécessaire car on utilise désormais NULL)  
2125 - // Set default values : "N/A"  
2126 - $materiel->groupes_thematique_id = 1;  
2127 - $materiel->groupes_metier_id = 1;  
2128 - $materiel->site_id = 9;  
2129 - S/  
2130 - }  
2131 - }  
2132 - // EDIT  
2133 - else {  
2134 - /S  
2135 - $this->log("Edit a doc1 !", 'debug');  
2136 - Log::write('debug',"Edit a doc2 !");  
2137 - $this->dlog("Edit a doc1 !");  
2138 - S/  
2139 - $materiel = $this->Materiels->get($id, [  
2140 - 'contain' => [] // load associated entities  
2141 - ]);  
2142 - //$this->myDebug($materiel);  
2143 - //debug($materiel);  
2144 - }  
2145 - */  
2146 2114
2147 2115
2148 - /* SI POST... 2116 + /*
  2117 + * SI POST...
  2118 + *
2149 * Les données ont été saisies et postées 2119 * Les données ont été saisies et postées
2150 * On va donc les sauvegarder (si ok) 2120 * On va donc les sauvegarder (si ok)
2151 */ 2121 */
@@ -2154,123 +2124,32 @@ class MaterielsController extends AppController { @@ -2154,123 +2124,32 @@ class MaterielsController extends AppController {
2154 $authorized_actions = $IS_ADD ? ['post'] : ['post','patch','put']; 2124 $authorized_actions = $IS_ADD ? ['post'] : ['post','patch','put'];
2155 if ( $this->request->is($authorized_actions) ) { 2125 if ( $this->request->is($authorized_actions) ) {
2156 2126
2157 - //debug($this->request->getData());  
2158 - //exit;  
2159 -  
2160 - /*  
2161 - // ADD  
2162 - // Nouveau materiel saisi et posted  
2163 - ($is_add && $this->request->is('post'))  
2164 -  
2165 - ||  
2166 -  
2167 - // EDIT  
2168 - // materiel modifié et posted  
2169 - ( (!$is_add) && $this->request->is(['patch','post','put']) )  
2170 - */ 2127 + //debug($this->request->getData()); exit;
2171 2128
2172 // (1) On remplit $materiel avec les données de ce materiel 2129 // (1) On remplit $materiel avec les données de ce materiel
2173 $materiel = $this->Materiels->patchEntity($materiel, $this->request->getData()); 2130 $materiel = $this->Materiels->patchEntity($materiel, $this->request->getData());
2174 -  
2175 - /*  
2176 - // AVIRER  
2177 - // BUGFIX temporaire pour php5 !!!  
2178 - if ($this->isLabinventDebugMode()) {  
2179 - $d2 = $materiel->date_reception;  
2180 - $d1 = $materiel->date_acquisition;  
2181 - debug("d1 achat avant:"); debug($d1);  
2182 - debug("d2 recep avant:"); debug($d2);  
2183 - /S  
2184 - $tz = new \DateTimeZone('Europe/Paris');  
2185 - $d1 = new \DateTime(strtr($d1,'/','-'),$tz);  
2186 - $d2 = new \DateTime(strtr($d2,'/','-'),$tz);  
2187 - debug("d1 achat après:"); debug($d1);  
2188 - debug("d2 recep après:"); debug($d2);  
2189 - S/  
2190 - if ($d2 < $d1) debug("d2 < d1 !!!");  
2191 - // $d2 > $d1 oui mais pas trop...  
2192 - $diff = $d2->diff($d1);  
2193 - //debug($diff->y);  
2194 - debug("diff:"); debug($diff);  
2195 - debug("diff->y:"); debug($diff->y);  
2196 - debug("diff->days:"); debug($diff->days);  
2197 - exit;  
2198 - }  
2199 - // FIN BUGFIX  
2200 - */  
2201 -  
2202 - // ADD : Set the user_id from the session.  
2203 - //$materiel->user_id = $this->Auth->user('id');  
2204 - // EDIT : Added: Disable modification of user_id.  
2205 - //'accessibleFields' => ['user_id' => false]  
2206 -  
2207 - /* (EP 7/12/20 : déjà fait dans la vue, donc inutile !!!)  
2208 - // (2) Si l'utilisateur courant est un "administratif" => le mettre comme gestionnaire du materiel  
2209 - // (tout ça pour ça !!! Faudra réduire ce bazar !)  
2210 - $current_user_name = $_SESSION['Auth']['User']['sn'][0];  
2211 - if (in_array(  
2212 - $current_user_name,  
2213 - $usersTable  
2214 - ->find('list', [  
2215 - 'keyField' => 'id',  
2216 - 'valueField' => 'nom'  
2217 - ])  
2218 - ->where([  
2219 - 'role =' => 'Administration'  
2220 - ])  
2221 - ->toArray()  
2222 - )) {  
2223 - $materiel->gestionnaire_id = $usersTable  
2224 - ->find()  
2225 - ->where([  
2226 - 'nom' => $current_user_name  
2227 - ])  
2228 - ->first()->id;  
2229 - }  
2230 - */  
2231 -  
2232 - 2131 + //debug($materiel);
  2132 +
2233 /* 2133 /*
2234 * \AV1 : Attributs OBLIGATOIRES 2134 * \AV1 : Attributs OBLIGATOIRES
2235 * On définit les infos obligatoires et on vérifie qu'elles sont bien présentes dans le POST 2135 * On définit les infos obligatoires et on vérifie qu'elles sont bien présentes dans le POST
2236 */ 2136 */
2237 2137
2238 - /*  
2239 - // Attributs obligatoires pour la phase COMMANDE  
2240 - //$LOT1 = $this->Materiels->MANDATORY_FIELDS_FOR_LOT1;  
2241 - $LOT1 = $this->Materiels->getMandatoryFieldsForLot(1);  
2242 -  
2243 - // Attributs obligatoires pour la phase VALIDATION (livré et payé)  
2244 - //$LOT2 = $this->Materiels->MANDATORY_FIELDS_FOR_LOT2;  
2245 - $LOT2 = $this->Materiels->getMandatoryFieldsForLot(2);  
2246 -  
2247 - //TODO 202109  
2248 - // Seulement si prix > 10K€ : exiger la facture jointe et le n° série  
2249 - if ($materiel->prix_ht > 10000) {  
2250 - //$LOT2[] = 'facture jointe';  
2251 - $LOT2['numero_serie'] = 'S/N';  
2252 - }  
2253 - // LOT2 = LOT1 + LOT2;  
2254 - ////$LOT2 = array_merge($LOT1, $LOT2);  
2255 - //debug($LOT2);exit;  
2256 - // Champs obligatoires = LOT1 si CREATED, LOT2 sinon (>CREATED, c'est à dire VALIDATED ou plus)  
2257 - $mandatory_fields = ($materiel->status == 'CREATED') ? $LOT1 : $LOT2;  
2258 - //debug($mandatory_fields); exit;  
2259 - */  
2260 //$mandatory_fields = MaterielsTable::getMandatoryFieldsForMaterielStatus($materiel->status); 2138 //$mandatory_fields = MaterielsTable::getMandatoryFieldsForMaterielStatus($materiel->status);
2261 $mandatory_fields = $this->Materiels->getMandatoryFieldsForMateriel($materiel); 2139 $mandatory_fields = $this->Materiels->getMandatoryFieldsForMateriel($materiel);
  2140 + //debug($mandatory_fields);
2262 //$mandatory_fields = $this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status); 2141 //$mandatory_fields = $this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status);
2263 - //debug($mandatory_fields); exit;  
2264 2142
2265 $verb = $IS_ADD ? "ajouté" : "modifié"; 2143 $verb = $IS_ADD ? "ajouté" : "modifié";
2266 2144
2267 // On vérifie que les infos obligatoires sont présentes 2145 // On vérifie que les infos obligatoires sont présentes
2268 // Si au moins un champ obligatoire est nul ou vide => ERROR 2146 // Si au moins un champ obligatoire est nul ou vide => ERROR
2269 - $ALL_MANDATORY_FIELDS_FOR_GIVEN = true; 2147 + $ALL_MANDATORY_FIELDS_GIVEN = true;
2270 //foreach ($mandatory_fields as $fname => $fval) { 2148 //foreach ($mandatory_fields as $fname => $fval) {
2271 //print_r($materiel); 2149 //print_r($materiel);
2272 - foreach ($mandatory_fields as $fname=>$fname_nice) {  
2273 - 2150 + //foreach ($mandatory_fields as $fname=>$fname_nice) {
  2151 + foreach ($mandatory_fields as $fname=>$attributes) {
  2152 +
2274 /* 2153 /*
2275 * Champs obligatoires à ignorer : 2154 * Champs obligatoires à ignorer :
2276 * - champs virtuels (n'existent pas physiquement) => DOC_XXX (DOC_DEVIS, DOC_BC, ...) 2155 * - champs virtuels (n'existent pas physiquement) => DOC_XXX (DOC_DEVIS, DOC_BC, ...)
@@ -2282,7 +2161,7 @@ class MaterielsController extends AppController { @@ -2282,7 +2161,7 @@ class MaterielsController extends AppController {
2282 //if ( in_array($fname, ['DOC_DEVIS', 'fournisseur_id', 'etiquette', 'numero_inventaire_organisme']) ) continue; 2161 //if ( in_array($fname, ['DOC_DEVIS', 'fournisseur_id', 'etiquette', 'numero_inventaire_organisme']) ) continue;
2283 2162
2284 if ($materiel->$fname === null || $materiel->$fname == '') { 2163 if ($materiel->$fname === null || $materiel->$fname == '') {
2285 - $ALL_MANDATORY_FIELDS_FOR_GIVEN = false; 2164 + $ALL_MANDATORY_FIELDS_GIVEN = false;
2286 /* (EP 2020 03) 2165 /* (EP 2020 03)
2287 * Ce genre de ligne ($this->Flash->...) affichant un message flash en haut de page, 2166 * Ce genre de ligne ($this->Flash->...) affichant un message flash en haut de page,
2288 * ne fonctionnait plus à cause de bootstrap (css). 2167 * ne fonctionnait plus à cause de bootstrap (css).
@@ -2292,7 +2171,8 @@ class MaterielsController extends AppController { @@ -2292,7 +2171,8 @@ class MaterielsController extends AppController {
2292 * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc) 2171 * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc)
2293 */ 2172 */
2294 //$msgError1 = "Le champ suivant est obligatoire : ".$fname_nice.' du matériel'; 2173 //$msgError1 = "Le champ suivant est obligatoire : ".$fname_nice.' du matériel';
2295 - $error_msg = "Le champ suivant est obligatoire : ".$fname_nice; 2174 + //$error_msg = "Le champ suivant est obligatoire : ".$fname_nice;
  2175 + $error_msg = "Le champ suivant est obligatoire : ".$attributes['comment'];
2296 /////debug($msgError1); 2176 /////debug($msgError1);
2297 $this->Flash->error($error_msg); 2177 $this->Flash->error($error_msg);
2298 /* MARCHE PAS POURQUOI ? 2178 /* MARCHE PAS POURQUOI ?
@@ -2307,10 +2187,11 @@ class MaterielsController extends AppController { @@ -2307,10 +2187,11 @@ class MaterielsController extends AppController {
2307 ///////////////$this->Flash->error(__("Le matériel n'a pas pu être $verb")); 2187 ///////////////$this->Flash->error(__("Le matériel n'a pas pu être $verb"));
2308 //return false; 2188 //return false;
2309 break; 2189 break;
2310 - }  
2311 - } 2190 + } // si champ obligatoire pas rempli
  2191 +
  2192 + } // foreach $mandatory_fields
2312 2193
2313 - if ($ALL_MANDATORY_FIELDS_FOR_GIVEN) { 2194 + if ($ALL_MANDATORY_FIELDS_GIVEN) {
2314 2195
2315 // (3) On l'ajoute en BD, on envoie un email, et on affiche ok sur page accueil 2196 // (3) On l'ajoute en BD, on envoie un email, et on affiche ok sur page accueil
2316 //$verb = $IS_ADD ? "ajouté" : "modifié"; 2197 //$verb = $IS_ADD ? "ajouté" : "modifié";
@@ -2342,7 +2223,8 @@ class MaterielsController extends AppController { @@ -2342,7 +2223,8 @@ class MaterielsController extends AppController {
2342 foreach ($materiel->getErrors() as $f=>$e) 2223 foreach ($materiel->getErrors() as $f=>$e)
2343 foreach($e as $k=>$v) $this->Flash->error(__($v.' : '.$f)); 2224 foreach($e as $k=>$v) $this->Flash->error(__($v.' : '.$f));
2344 2225
2345 - } 2226 + } // save KO
  2227 +
2346 else { 2228 else {
2347 //debug($this->getCurrentEntity()); exit; 2229 //debug($this->getCurrentEntity()); exit;
2348 //debug($this->Materiels->current_entity); exit; 2230 //debug($this->Materiels->current_entity); exit;
@@ -2380,7 +2262,7 @@ class MaterielsController extends AppController { @@ -2380,7 +2262,7 @@ class MaterielsController extends AppController {
2380 'action' => 'view', 2262 'action' => 'view',
2381 $id 2263 $id
2382 ]); 2264 ]);
2383 - } 2265 + } // save KO
2384 2266
2385 } // $ALL_MANDATORY_FIELDS_FOR_GIVEN 2267 } // $ALL_MANDATORY_FIELDS_FOR_GIVEN
2386 2268
@@ -2388,11 +2270,12 @@ class MaterielsController extends AppController { @@ -2388,11 +2270,12 @@ class MaterielsController extends AppController {
2388 2270
2389 2271
2390 2272
2391 -  
2392 - /* SINON (PAS POST) 2273 + /* SINON (GET, PAS POST)
2393 * C'est la première fois qu'on vient sur cette vue, 2274 * C'est la première fois qu'on vient sur cette vue,
2394 * donc on va préparer le formulaire de saisie) 2275 * donc on va préparer le formulaire de saisie)
2395 */ 2276 */
  2277 + //debug($materiel); exit;
  2278 +
2396 2279
2397 2280
2398 /* 2281 /*
@@ -2709,6 +2592,7 @@ class MaterielsController extends AppController { @@ -2709,6 +2592,7 @@ class MaterielsController extends AppController {
2709 'ARCHIVED' => 'ARCHIVED' 2592 'ARCHIVED' => 'ARCHIVED'
2710 ]; 2593 ];
2711 $entity = $materiel; 2594 $entity = $materiel;
  2595 + //debug($entity);
2712 $this->set(compact( 2596 $this->set(compact(
2713 'IS_ADD', 2597 'IS_ADD',
2714 'mail_responsable', 2598 'mail_responsable',
@@ -2738,8 +2622,10 @@ class MaterielsController extends AppController { @@ -2738,8 +2622,10 @@ class MaterielsController extends AppController {
2738 'gestionnaires' 2622 'gestionnaires'
2739 )); 2623 ));
2740 2624
2741 -  
2742 - $this->set('readonlyFields', $this->getUneditableFieldsForMaterielStatus($materiel->status)); 2625 + $readonlyFields = $this->getUneditableFieldsForMaterielStatus($materiel->status);
  2626 + $this->set(compact('readonlyFields'));
  2627 + //debug($materiel->status);
  2628 + //debug($readonlyFields);
2743 //$this->set('readonlyFields', $IS_ADD ? [] : $this->getUneditableFieldsForMaterielStatus($materiel->status)); 2629 //$this->set('readonlyFields', $IS_ADD ? [] : $this->getUneditableFieldsForMaterielStatus($materiel->status));
2744 //debug($this->Materiels->getUneditableFieldsForMaterielStatus($status)); exit; 2630 //debug($this->Materiels->getUneditableFieldsForMaterielStatus($status)); exit;
2745 //$this->set('readonlyFields', $IS_ADD ? [] : $this->Materiels->getUneditableFieldsForMaterielStatus($materiel->status)); 2631 //$this->set('readonlyFields', $IS_ADD ? [] : $this->Materiels->getUneditableFieldsForMaterielStatus($materiel->status));
@@ -2767,11 +2653,12 @@ class MaterielsController extends AppController { @@ -2767,11 +2653,12 @@ class MaterielsController extends AppController {
2767 if ($this->USER_IS_SUPERADMIN()) return []; 2653 if ($this->USER_IS_SUPERADMIN()) return [];
2768 2654
2769 $uneditable_fields = $this->Materiels->getUneditableFieldsForMaterielStatus($materiel_status); 2655 $uneditable_fields = $this->Materiels->getUneditableFieldsForMaterielStatus($materiel_status);
2770 - foreach ($uneditable_fields as $fname=>$except_list) { 2656 + //foreach ($uneditable_fields as $fname=>$except_list) {
  2657 + foreach ($uneditable_fields as $fname=>$attributes) {
2771 // S'il y a une exception, voir si elle concerne le USER courant, sinon supprimer ce champ $fname 2658 // S'il y a une exception, voir si elle concerne le USER courant, sinon supprimer ce champ $fname
2772 - if ($except_list) { 2659 + if ($attributes['except_roles']) {
2773 //debug($except_list); 2660 //debug($except_list);
2774 - if ($this->currentUserRoleInList($except_list)) unset($uneditable_fields[$fname]); 2661 + if ($this->currentUserRoleInList($attributes['except_roles'])) unset($uneditable_fields[$fname]);
2775 //debug("remove $fname"); 2662 //debug("remove $fname");
2776 } 2663 }
2777 } 2664 }
@@ -3051,10 +2938,11 @@ class MaterielsController extends AppController { @@ -3051,10 +2938,11 @@ class MaterielsController extends AppController {
3051 2938
3052 // Attributs obligatoires pour la phase COMMANDE (LOT1) 2939 // Attributs obligatoires pour la phase COMMANDE (LOT1)
3053 $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(1); 2940 $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(1);
  2941 + //debug($mandatoryFields);
3054 $ACTION = 'ordonner la commande de'; 2942 $ACTION = 'ordonner la commande de';
3055 2943
3056 // Si au moins un champ obligatoire est nul ou vide => ERROR 2944 // Si au moins un champ obligatoire est nul ou vide => ERROR
3057 - foreach ($mandatoryFields as $fname => $fnicename) { 2945 + foreach ($mandatoryFields as $fname => $attributes) {
3058 2946
3059 // Doc attaché obligatoire ? (champ virtuel) 2947 // Doc attaché obligatoire ? (champ virtuel)
3060 //if (strtoupper($fname) == 'DOC_DEVIS') { 2948 //if (strtoupper($fname) == 'DOC_DEVIS') {
@@ -3085,7 +2973,7 @@ class MaterielsController extends AppController { @@ -3085,7 +2973,7 @@ class MaterielsController extends AppController {
3085 $fval = $materiel->$fname; 2973 $fval = $materiel->$fname;
3086 if ($fval === null || $fval == '') { 2974 if ($fval === null || $fval == '') {
3087 //$msgError1 = "Pour $ACTION ce matériel, le champ suivant ne doit pas être vide : ".$fnicename.' du matériel'; 2975 //$msgError1 = "Pour $ACTION ce matériel, le champ suivant ne doit pas être vide : ".$fnicename.' du matériel';
3088 - $error_msg = sprintf($ERROR_MSG_EMPTY_FIELD, $ACTION, $fnicename); 2976 + $error_msg = sprintf($ERROR_MSG_EMPTY_FIELD, $ACTION, $attributes['comment']);
3089 $this->Flash->error($error_msg); 2977 $this->Flash->error($error_msg);
3090 // Utile ??? (plante les tests) 2978 // Utile ??? (plante les tests)
3091 ////$this->e->setError($fname, 'Ce champ ne doit pas être vide'); 2979 ////$this->e->setError($fname, 'Ce champ ne doit pas être vide');
@@ -3108,6 +2996,7 @@ class MaterielsController extends AppController { @@ -3108,6 +2996,7 @@ class MaterielsController extends AppController {
3108 if ($newStatus == 'VALIDATED') { 2996 if ($newStatus == 'VALIDATED') {
3109 // Attributs obligatoires pour la phase VALIDATION (livré et payé) (LOT2) 2997 // Attributs obligatoires pour la phase VALIDATION (livré et payé) (LOT2)
3110 $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(2); 2998 $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(2);
  2999 + //$mandatoryFields = $this->Materiels->getCategoryFieldsForLot('MANDATORY_FIELDS_FOR_LOT', 2)
3111 //$mandatoryFields = $this->Materiels->MANDATORY_FIELDS_FOR_LOT2; 3000 //$mandatoryFields = $this->Materiels->MANDATORY_FIELDS_FOR_LOT2;
3112 //debug($mandatoryFields);exit; 3001 //debug($mandatoryFields);exit;
3113 /* 3002 /*
@@ -3142,7 +3031,7 @@ class MaterielsController extends AppController { @@ -3142,7 +3031,7 @@ class MaterielsController extends AppController {
3142 */ 3031 */
3143 3032
3144 // Si au moins un champ obligatoire est nul ou vide => ERROR 3033 // Si au moins un champ obligatoire est nul ou vide => ERROR
3145 - foreach ($mandatoryFields as $fname => $fnicename) { 3034 + foreach ($mandatoryFields as $fname => $attributes) {
3146 3035
3147 $ACTION = 'valider'; 3036 $ACTION = 'valider';
3148 3037
@@ -3184,7 +3073,8 @@ class MaterielsController extends AppController { @@ -3184,7 +3073,8 @@ class MaterielsController extends AppController {
3184 * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc) 3073 * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc)
3185 */ 3074 */
3186 //$msgError1 = "Pour $ACTION ce matériel, le champ suivant ne doit pas être vide : ".$fnicename.' du matériel'; 3075 //$msgError1 = "Pour $ACTION ce matériel, le champ suivant ne doit pas être vide : ".$fnicename.' du matériel';
3187 - $error_msg = sprintf($ERROR_MSG_EMPTY_FIELD, $ACTION, $fnicename); 3076 + //debug($fname);
  3077 + $error_msg = sprintf($ERROR_MSG_EMPTY_FIELD, $ACTION, $attributes['comment']);
3188 $this->Flash->error($error_msg); 3078 $this->Flash->error($error_msg);
3189 //debug($error_msg); 3079 //debug($error_msg);
3190 3080
src/Controller/PagesController.php
@@ -70,6 +70,7 @@ class PagesController extends AppController @@ -70,6 +70,7 @@ class PagesController extends AppController
70 { 70 {
71 $this->myDebug("step 0A (specific): PagesController.initialize()"); 71 $this->myDebug("step 0A (specific): PagesController.initialize()");
72 parent::initialize(); 72 parent::initialize();
  73 + $this->modelClass = false;
73 // On autorise l'action add SANS authentification (unauthenticated) 74 // On autorise l'action add SANS authentification (unauthenticated)
74 //$this->Auth->allow(['add']); 75 //$this->Auth->allow(['add']);
75 //$this->LdapAuth->allow(['display']); 76 //$this->LdapAuth->allow(['display']);
src/Controller/StatsController.php
@@ -61,7 +61,7 @@ class StatsController extends AppController @@ -61,7 +61,7 @@ class StatsController extends AppController
61 'nice_name'=>"Temps connexion cumulé (h)", 'f'=>'getHourMnSecForDuration'], 61 'nice_name'=>"Temps connexion cumulé (h)", 'f'=>'getHourMnSecForDuration'],
62 'connex_nb'=>['nice_name'=>"Nb connexions"], 62 'connex_nb'=>['nice_name'=>"Nb connexions"],
63 'connexDurAvg'=>[ 63 'connexDurAvg'=>[
64 - 'nice_name'=>"Durée connexion moyenne (sur année)", 'f'=>'getHourMnSecForDuration'] 64 + 'nice_name'=>"Durée connexion moyenne (sur 1 an)", 'f'=>'getHourMnSecForDuration']
65 ], 65 ],
66 ['Users'], 66 ['Users'],
67 false, false, 67 false, false,
src/Controller/SurCategoriesController.php
@@ -256,6 +256,8 @@ class SurCategoriesController extends AppController @@ -256,6 +256,8 @@ class SurCategoriesController extends AppController
256 $surCategory = $this->SurCategories->get($id, [ 256 $surCategory = $this->SurCategories->get($id, [
257 'contain' => [] 257 'contain' => []
258 ]); 258 ]);
  259 +
  260 + // En mode POST
259 if ($this->request->is([ 261 if ($this->request->is([
260 'patch', 262 'patch',
261 'post', 263 'post',
@@ -274,10 +276,14 @@ class SurCategoriesController extends AppController @@ -274,10 +276,14 @@ class SurCategoriesController extends AppController
274 $this->Flash->error(__('Le domaine n\'a pas pu être édité.')); 276 $this->Flash->error(__('Le domaine n\'a pas pu être édité.'));
275 } 277 }
276 } 278 }
  279 +
  280 + // En mode GET (ou alors POST après une erreur)
277 $this->set(compact('surCategory')); 281 $this->set(compact('surCategory'));
  282 + /* (EP) inutile sauf si json
278 $this->set('_serialize', [ 283 $this->set('_serialize', [
279 'surCategory' 284 'surCategory'
280 ]); 285 ]);
  286 + */
281 } 287 }
282 288
283 /** 289 /**
src/Form/ConfigurationFieldsForm.php 0 → 100644
@@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
  1 +<?php
  2 +// in src/Form/ContactForm.php
  3 +namespace App\Form;
  4 +
  5 +use Cake\Form\Form;
  6 +use Cake\Form\Schema;
  7 +use Cake\Validation\Validator;
  8 +use Cake\Core\Configure;
  9 +
  10 +// modelless form : voir https://book.cakephp.org/3/en/core-libraries/form.html
  11 +class ConfigurationFieldsForm extends Form
  12 +{
  13 + /*
  14 + protected function _buildSchema(Schema $schema)
  15 + {
  16 + //$fields = Configure::readOrFail('MANDATORY_AND_READONLY_FIELDS');
  17 + //debug($fields);
  18 + //foreach ($fields as $fname=>$val) debug($fname);
  19 +
  20 + return $schema
  21 + ->addField('name', 'string')
  22 + ->addField('email', ['type' => 'string'])
  23 + ->addField('body', ['type' => 'text']);
  24 + }
  25 + */
  26 +
  27 + /*
  28 + public function validationDefault(Validator $validator)
  29 + {
  30 + $validator->add('name', 'length', [
  31 + 'rule' => ['minLength', 5],
  32 + 'message' => 'A name is required'
  33 + ])->add('email', 'format', [
  34 + 'rule' => 'email',
  35 + 'message' => 'A valid email address is required',
  36 + ]);
  37 +
  38 + return $validator;
  39 + }
  40 + */
  41 +
  42 + protected function _execute(array $data)
  43 + {
  44 + // Send an email.
  45 + return true;
  46 + }
  47 +}
0 \ No newline at end of file 48 \ No newline at end of file
src/Model/Table/MaterielsTable.php
@@ -252,60 +252,84 @@ class MaterielsTable extends AppTable @@ -252,60 +252,84 @@ class MaterielsTable extends AppTable
252 * - LOT0 total = [] + LOT0 252 * - LOT0 total = [] + LOT0
253 * - LOT1 total = LOT0 + LOT1 253 * - LOT1 total = LOT0 + LOT1
254 * - LOT2 total = LOT1 + LOT2 254 * - LOT2 total = LOT1 + LOT2
  255 + *
  256 + * Algo RECURSIF
255 */ 257 */
  258 + public static function getCategoryFieldsForLot($categ, $lot_num, $categ_initial=null) {
  259 + //debug($lot_num);
  260 + // ex: $categ = RECOMMENDED_FIELDS_AFTER_LOT
  261 + //if ($lot_num < 0) return $categ_initial ? Configure::readOrFail('UNEDITABLE_FIELDS') : [];
  262 + if ($lot_num < 0) return $categ_initial ? Configure::readOrFail("MANDATORY_AND_READONLY_FIELDS.$categ_initial") : [];
  263 +
  264 + // Recursive call
  265 + $base_fields = self::getCategoryFieldsForLot($categ, $lot_num-1, $categ_initial);
  266 + $base_fields = self::removeUnselectedFieldsFrom($base_fields);
  267 + //debug($base_fields);
  268 +
  269 + //$new_fields = Configure::readOrFail('MANDATORY_AND_READONLY_FIELDS.RECOMMENDED_FIELDS_AFTER_LOT'.$lot_num);
  270 + $new_fields = Configure::readOrFail("MANDATORY_AND_READONLY_FIELDS.$categ".$lot_num);
  271 + $new_fields = self::removeUnselectedFieldsFrom($new_fields);
  272 + //debug($new_fields);
  273 +
  274 + //debug($new_fields);
  275 + return array_merge($base_fields, $new_fields);
  276 + }
  277 +
  278 +
  279 +
  280 + public static function removeUnselectedFieldsFrom($fields) {
  281 + /*
  282 + * Exemple :
  283 + Before remove :
  284 + {
  285 + designation: { selected: '0', comment: commentaire, roles: '' },
  286 + description: { selected: '0', comment: autre, roles: '' },
  287 + nom_responsable: { selected: '1', comment: 'nom du responsable',
  288 + }
  289 + After remove :
  290 + {
  291 + nom_responsable: { selected: '1', comment: 'nom du responsable',
  292 + }
  293 + */
  294 + //$fields_selected = [];
  295 + foreach ($fields as $field_name => $attributes) {
  296 + if ( $field_name=='fieldset_comment' || !$attributes['selected'] ) unset($fields[$field_name]);
  297 + }
  298 + return $fields;
  299 + }
  300 +
256 public static function getMandatoryFieldsForLot($lot_num) { 301 public static function getMandatoryFieldsForLot($lot_num) {
  302 + return self::getCategoryFieldsForLot('MANDATORY_FIELDS_FOR_LOT', $lot_num);
257 /* 303 /*
258 $specific_constant_name = "MANDATORY_FIELDS_FOR_LOT1_".self::getLabName(); 304 $specific_constant_name = "MANDATORY_FIELDS_FOR_LOT1_".self::getLabName();
259 $mandatory_fields_lot1 = defined($specific_constant_name) ? constant($specific_constant_name) : MANDATORY_FIELDS_FOR_LOT1; 305 $mandatory_fields_lot1 = defined($specific_constant_name) ? constant($specific_constant_name) : MANDATORY_FIELDS_FOR_LOT1;
260 return $mandatory_fields_lot1; 306 return $mandatory_fields_lot1;
261 */ 307 */
  308 + /*
262 if ($lot_num < 0) return []; 309 if ($lot_num < 0) return [];
263 $base_fields = self::getMandatoryFieldsForLot($lot_num-1); 310 $base_fields = self::getMandatoryFieldsForLot($lot_num-1);
264 $new_fields = Configure::readOrFail('MANDATORY_FIELDS_FOR_LOT'.$lot_num); 311 $new_fields = Configure::readOrFail('MANDATORY_FIELDS_FOR_LOT'.$lot_num);
265 //debug($new_fields); 312 //debug($new_fields);
266 return array_merge($base_fields, $new_fields); 313 return array_merge($base_fields, $new_fields);
  314 + */
267 } 315 }
  316 + // RECURSIF
268 public static function getRecommendedFieldsForLot($lot_num) { 317 public static function getRecommendedFieldsForLot($lot_num) {
  318 + return self::getCategoryFieldsForLot('RECOMMENDED_FIELDS_AFTER_LOT', $lot_num);
  319 + /*
269 if ($lot_num < 0) return []; 320 if ($lot_num < 0) return [];
270 $base_fields = self::getRecommendedFieldsForLot($lot_num-1); 321 $base_fields = self::getRecommendedFieldsForLot($lot_num-1);
271 - $new_fields = Configure::readOrFail('RECOMMENDED_FIELDS_AFTER_LOT'.$lot_num); 322 + $new_fields = Configure::readOrFail('MANDATORY_AND_READONLY_FIELDS.RECOMMENDED_FIELDS_AFTER_LOT'.$lot_num);
272 //debug($new_fields); 323 //debug($new_fields);
273 return array_merge($base_fields, $new_fields); 324 return array_merge($base_fields, $new_fields);
  325 + */
274 } 326 }
275 -  
276 - // (EP 2021 10)  
277 - public static function list_to_dict($uneditable_fields) {  
278 - $dict = [];  
279 - foreach ($uneditable_fields as $f) {  
280 - $except_roles = [];  
281 - $except_str = '(sauf';  
282 - $pos0 = strpos($f, '(');  
283 - $pos = strpos($f, $except_str);  
284 - // Si le nom du champ $f fini par "(sauf ...)", on récupère cette chaine except (sans le mot 'sauf' ni la parenthèse finale) dans $except  
285 - if ($pos0 !== false) {  
286 - $error_msg = "Erreur dans le fichier de configuration des champs non modifiables";  
287 - // EXCEPTION si parenthèse début mais pas suivie du mot-clé 'sauf'  
288 - if ($pos === false)  
289 - throw new \Exception("$error_msg : une parenthèse doit toujours être suivie du mot-clé 'sauf', ce qui n'est pas le cas de la ligne '$f'");  
290 - // EXCEPTION si pas de parenthèse finale, ou role mal orthographié  
291 - //$except_list = trim( substr($f, $pos + strlen($except_str), -1) );  
292 - if ( substr($f, -1) != ')' )  
293 - throw new \Exception("$error_msg : il manque une parenthèse à la fin de la ligne '$f'");  
294 - $except_roles = str_replace(" ", "", substr($f, $pos + strlen($except_str), -1) );  
295 - $except_roles = explode(',', $except_roles);  
296 - foreach ($except_roles as $role) if (! self::isValidRole($role))  
297 - throw new \Exception("$error_msg : le role '$role' est mal orthographié dans la ligne '$f'");  
298 - $f = trim( substr($f,0,$pos) );  
299 - }  
300 - $dict[$f] = $except_roles;  
301 - }  
302 - return $dict;  
303 - }  
304 -  
305 // (EP 2021 09) fonction récursive : 327 // (EP 2021 09) fonction récursive :
306 // => champs readonly pour le lot N = champs readonly pour le lot N-1 + champs readonly pour le lot N : 328 // => champs readonly pour le lot N = champs readonly pour le lot N-1 + champs readonly pour le lot N :
307 public static function getUneditableFieldsForLot($lot_num) { 329 public static function getUneditableFieldsForLot($lot_num) {
308 - 330 + return self::getCategoryFieldsForLot('UNEDITABLE_FIELDS_AFTER_LOT', $lot_num, 'UNEDITABLE_FIELDS');
  331 + /*
  332 +
309 // Si N = -1 => on retourne la liste initiale de champs readonly (fin de la récursivité) 333 // Si N = -1 => on retourne la liste initiale de champs readonly (fin de la récursivité)
310 //if ($lot_num < 0) return []; 334 //if ($lot_num < 0) return [];
311 if ($lot_num < 0) return self::list_to_dict( Configure::readOrFail('UNEDITABLE_FIELDS') ); 335 if ($lot_num < 0) return self::list_to_dict( Configure::readOrFail('UNEDITABLE_FIELDS') );
@@ -323,10 +347,57 @@ class MaterielsTable extends AppTable @@ -323,10 +347,57 @@ class MaterielsTable extends AppTable
323 // => Somme des 2 (champs readonly pour le lot N-1 + champs readonly pour le lot N) 347 // => Somme des 2 (champs readonly pour le lot N-1 + champs readonly pour le lot N)
324 //return array_unique(array_merge($base_uneditable_fields, $new_uneditable_fields)); 348 //return array_unique(array_merge($base_uneditable_fields, $new_uneditable_fields));
325 return array_merge($base_uneditable_fields, $new_uneditable_fields); 349 return array_merge($base_uneditable_fields, $new_uneditable_fields);
  350 + */
326 351
327 } 352 }
328 353
329 354
  355 + public static function separateFieldNameAndExceptedRoles($f) {
  356 + $except_roles = [];
  357 + $except_str = '(sauf';
  358 + $pos0 = strpos($f, '(');
  359 + $pos = strpos($f, $except_str);
  360 + // Si le nom du champ $f fini par "(sauf ...)", on récupère cette chaine except (sans le mot 'sauf' ni la parenthèse finale) dans $except
  361 + if ($pos0 !== false) {
  362 + $error_msg = "Erreur dans le fichier de configuration des champs non modifiables";
  363 +
  364 + // EXCEPTION si parenthèse début mais pas suivie du mot-clé 'sauf'
  365 + /*
  366 + if ($pos === false)
  367 + throw new \Exception("$error_msg : une parenthèse doit toujours être suivie du mot-clé 'sauf', ce qui n'est pas le cas de la ligne '$f'");
  368 + */
  369 + if ($pos !== false) {
  370 +
  371 + // EXCEPTION si pas de parenthèse finale, ou role mal orthographié
  372 + //$except_list = trim( substr($f, $pos + strlen($except_str), -1) );
  373 + if ( substr($f, -1) != ')' )
  374 + throw new \Exception("$error_msg : il manque une parenthèse à la fin de la ligne '$f'");
  375 +
  376 + $except_roles = str_replace(" ", "", substr($f, $pos + strlen($except_str), -1) );
  377 + $except_roles = explode(',', $except_roles);
  378 + foreach ($except_roles as $role) if (! self::isValidRole($role))
  379 + throw new \Exception("$error_msg : le role '$role' est mal orthographié dans la ligne '$f'");
  380 +
  381 + $f = trim( substr($f,0,$pos) );
  382 + }
  383 + }
  384 + //return [$f=>$except_roles];
  385 + return ['name'=>$f, 'roles'=>$except_roles];
  386 + }
  387 +
  388 + // (EP 2021 10)
  389 + public static function list_to_dict($uneditable_fields) {
  390 + $dict = [];
  391 + foreach ($uneditable_fields as $f) {
  392 + //$dict[$f] = $except_roles;
  393 + $fieldNameAndExceptedRoles = self::separateFieldNameAndExceptedRoles($f);
  394 + $dict[$fieldNameAndExceptedRoles['name']] = $fieldNameAndExceptedRoles['except_roles'];
  395 + }
  396 + return $dict;
  397 + }
  398 +
  399 +
  400 +
330 /* 401 /*
331 public static function getMandatoryFieldsLot1() { 402 public static function getMandatoryFieldsLot1() {
332 /S 403 /S
@@ -353,7 +424,13 @@ class MaterielsTable extends AppTable @@ -353,7 +424,13 @@ class MaterielsTable extends AppTable
353 $fields = self::getMandatoryFieldsForMaterielStatus($status); 424 $fields = self::getMandatoryFieldsForMaterielStatus($status);
354 // Seulement si prix > 10K€ : exiger la facture jointe et le n° série et lieu de stockage précis 425 // Seulement si prix > 10K€ : exiger la facture jointe et le n° série et lieu de stockage précis
355 if ($status == 'VALIDATED' && $mat->prix_ht && $mat->prix_ht > 10000) { 426 if ($status == 'VALIDATED' && $mat->prix_ht && $mat->prix_ht > 10000) {
356 - $fields['numero_serie'] = 'S/N'; 427 + //$fields['numero_serie'] = 'S/N';
  428 + /*
  429 + $fields['numero_serie'] = [
  430 + //'selected'=>'1',
  431 + 'comment'=>'S/N'
  432 + ];
  433 + */
357 //TODO 202109 434 //TODO 202109
358 //$fields[] = 'lieu stockage'; 435 //$fields[] = 'lieu stockage';
359 //$fields[] = 'facture jointe'; 436 //$fields[] = 'facture jointe';
@@ -361,7 +438,9 @@ class MaterielsTable extends AppTable @@ -361,7 +438,9 @@ class MaterielsTable extends AppTable
361 return $fields; 438 return $fields;
362 } 439 }
363 public static function getMandatoryFieldsForMaterielStatus($status) { 440 public static function getMandatoryFieldsForMaterielStatus($status) {
364 - 441 + //return self::getCategoryFieldsForMaterielStatus('MANDATORY_FIELDS_FOR_LOT', $status);
  442 + return self::getCategoryFieldsForMaterielStatus('MANDATORY', $status);
  443 + /*
365 if (is_null($status)) $status='CREATED'; 444 if (is_null($status)) $status='CREATED';
366 // On recup le LOT qui convient au status courant du matos 445 // On recup le LOT qui convient au status courant du matos
367 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2); 446 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2);
@@ -375,9 +454,12 @@ class MaterielsTable extends AppTable @@ -375,9 +454,12 @@ class MaterielsTable extends AppTable
375 //debug($fields); 454 //debug($fields);
376 455
377 return $fields; 456 return $fields;
  457 + */
378 } 458 }
379 public static function getUneditableFieldsForMaterielStatus($status) { 459 public static function getUneditableFieldsForMaterielStatus($status) {
380 - 460 + //return self::getCategoryFieldsForMaterielStatus('UNEDITABLE_FIELDS_AFTER_LOT', $status);
  461 + return self::getCategoryFieldsForMaterielStatus('UNEDITABLE', $status);
  462 + /*
381 if (is_null($status)) $status='CREATED'; 463 if (is_null($status)) $status='CREATED';
382 // On recup le LOT qui convient au status courant du matos 464 // On recup le LOT qui convient au status courant du matos
383 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2); 465 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2);
@@ -390,12 +472,19 @@ class MaterielsTable extends AppTable @@ -390,12 +472,19 @@ class MaterielsTable extends AppTable
390 $fields = self::getUneditableFieldsForLot($lot_num); 472 $fields = self::getUneditableFieldsForLot($lot_num);
391 473
392 return $fields; 474 return $fields;
  475 + */
393 } 476 }
394 public static function getRecommendedFieldsForMaterielStatus($status) { 477 public static function getRecommendedFieldsForMaterielStatus($status) {
395 - return self::getFieldsCategoryForMaterielStatus('RECOMMENDED', $status); 478 + //return self::getCategoryFieldsForMaterielStatus('RECOMMENDED', $status);
  479 + //return self::getCategoryFieldsForMaterielStatus('RECOMMENDED_FIELDS_AFTER_LOT', $status);
  480 + return self::getCategoryFieldsForMaterielStatus('RECOMMENDED', $status);
396 } 481 }
397 - public static function getFieldsCategoryForMaterielStatus($fields_categ, $status) {  
398 - $func_name = "get".ucfirst($fields_categ)."FieldsForLot"; // getUneditableFieldsForLot() 482 +
  483 + public static function getCategoryFieldsForMaterielStatus($categ, $status) {
  484 + //$func_name = "get".ucfirst($fields_categ)."FieldsForLot"; // getUneditableFieldsForLot()
  485 + //$func_name = "getCategoryFieldsForLot"; // getUneditableFieldsForLot()
  486 + $func_name = "get".ucfirst($categ)."FieldsForLot"; // getUneditableFieldsForLot()
  487 +
399 if (is_null($status)) $status='CREATED'; 488 if (is_null($status)) $status='CREATED';
400 // On recup le LOT qui convient au status courant du matos 489 // On recup le LOT qui convient au status courant du matos
401 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2); 490 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2);
@@ -1560,6 +1649,9 @@ class MaterielsTable extends AppTable @@ -1560,6 +1649,9 @@ class MaterielsTable extends AppTable
1560 $fournisseur_asis = $entity->fournisseur ? $entity->fournisseur['name'] : ''; 1649 $fournisseur_asis = $entity->fournisseur ? $entity->fournisseur['name'] : '';
1561 // Enlever les espaces superflus 1650 // Enlever les espaces superflus
1562 $fournisseur = trim($fournisseur_asis); 1651 $fournisseur = trim($fournisseur_asis);
  1652 + //debug($fournisseur);
  1653 + //debug($entity);
  1654 +
1563 1655
1564 // Si champ fournisseur obligatoire, vérification qu'il est bien rempli (ou emission d'une erreur) 1656 // Si champ fournisseur obligatoire, vérification qu'il est bien rempli (ou emission d'une erreur)
1565 //if (IP2I && $entity->fournisseur && $fournisseur == '') { 1657 //if (IP2I && $entity->fournisseur && $fournisseur == '') {
@@ -1688,6 +1780,7 @@ class MaterielsTable extends AppTable @@ -1688,6 +1780,7 @@ class MaterielsTable extends AppTable
1688 } // ssi changement 1780 } // ssi changement
1689 // Finalement, on supprime les champs 'fournisseur' car sinon erreur de sauvegarde, 1781 // Finalement, on supprime les champs 'fournisseur' car sinon erreur de sauvegarde,
1690 // (normal ces champs n'existent pas dans materiel) 1782 // (normal ces champs n'existent pas dans materiel)
  1783 + //debug($entity); exit;
1691 unset($entity->fournisseur_orig); 1784 unset($entity->fournisseur_orig);
1692 unset($entity->fournisseur); 1785 unset($entity->fournisseur);
1693 //debug($entity); exit; 1786 //debug($entity); exit;
src/Template/ConfigurationFields/edit.ctp 0 → 100644
@@ -0,0 +1,149 @@ @@ -0,0 +1,149 @@
  1 +<?php
  2 +
  3 +use App\Model\Table\MaterielsTable;
  4 +
  5 +$DEBUG = false;
  6 +//$DEBUG = true;
  7 +
  8 +
  9 +// Variables générales cakephp
  10 +$_REQUEST = $_REQUEST;
  11 +$_COOKIE = $_COOKIE;
  12 +$_ENV = $_ENV;
  13 +//debug($_COOKIE);
  14 +/*
  15 +[
  16 + 'CAKEPHP' => 'hiraigc02eb46spa06iiog6tvh'
  17 +]
  18 +*/
  19 +//debug($_ENV);
  20 +//debug($_REQUEST);
  21 +
  22 +// Variables passées par le controleur
  23 +$CAN_EDIT = true;
  24 +$CAN_EDIT = $CAN_EDIT;
  25 +$READONLY = $READONLY;
  26 +$contact = $contact;
  27 +$fieldsets = $fieldsets;
  28 +//debug($fieldsets);
  29 +
  30 +$icon = $READONLY ? '' : "<i class='icon-edit'></i>";
  31 +//$icon = $READONLY ? '' : "<i class='icon-pencil'></i>";
  32 +//$end = $READONLY ? "(Visualisation)" : "(Modification)";
  33 +//$title = "Configuration des champs obligatoires ou non modifiables de la fiche Matériel $end";
  34 +$title = "Configuration des champs obligatoires ou non modifiables de la fiche Matériel";
  35 +echo "<br><h2>$icon $title</h2><br><br>";
  36 +/*
  37 + Autres icones possibles :
  38 +echo "<br><h2><i class='icon-edit'></i> $title</h2><br><br>";
  39 +echo "<br><h2><i class='icon-list'></i> $title</h2><br><br>";
  40 +echo "<br><h2><i class='icon-plus'></i> $title</h2><br><br>";
  41 +echo "<br><h2><i class='icon-pencil'></i> $title</h2><br><br>";
  42 +echo "<br><h2><i class='icon-trash'></i> $title</h2><br><br>";
  43 +*/
  44 +
  45 +if ($READONLY && $CAN_EDIT) $this->MyHelper->echoEditButton();
  46 +if ($READONLY && $CAN_EDIT) $this->MyHelper->echoButtonForAction('icon-trash', 'reset-to-default', 'Remettre les valeurs par défaut', 'Remettre les valeurs par défaut', true);
  47 +
  48 +//echo $this->Html->icon('pencil');
  49 +
  50 +echo $this->Form->create($contact);
  51 +
  52 + if (!$READONLY) {
  53 + echo $this->Form->button('Enregistrer', ['class'=>'btn btn-outline-success', 'type'=>'submit']);
  54 + //echo $this->Form->button('Submit');
  55 + echo '<br><br>';
  56 + }
  57 +
  58 + //echo $this->Form->button($this->Html->icon('pencil'), ['escape' => false]);
  59 + // ...can be easily rewritten as:
  60 + //echo $this->Form->button('i:pencil');
  61 +
  62 + foreach ($fieldsets as $fieldset_name => $fields) {
  63 + $controls = [];
  64 +
  65 + //foreach ($fields as $field) {
  66 + foreach ($fields as $field_name => $attributes) {
  67 +
  68 + if ($field_name=='fieldset_comment') continue;
  69 +
  70 + // champ activé ou désactivé
  71 + $checked = $attributes['selected'];
  72 + $comment = $attributes['comment'];
  73 + $except_roles = isset($attributes['except_roles']) ? $attributes['except_roles'] : [];
  74 +
  75 + $name = "$fieldset_name.$field_name";
  76 +
  77 + // - CHAMP de la table materiel : Checkbox pour le dé/sélectionner
  78 + $label = $field_name;
  79 + if ($READONLY) {
  80 + if ($comment) $label .= ' ('.$comment.')';
  81 + if ($except_roles) $label .= ' (sauf '.implode(', ',$except_roles).')';
  82 + }
  83 + $controls["$name.selected"] = [
  84 + 'type' => 'checkbox',
  85 + //'hiddenField' => true,
  86 + 'label' => $label,
  87 + 'checked' => $checked,
  88 + 'disabled' => $READONLY,
  89 + //'readonly' =>$READONLY,
  90 + //'readonly' => 'readonly',
  91 + ];
  92 +
  93 + // (EDIT ONLY)
  94 + // - Label associé au champ (entre parenthèses) doit pouvoir être modifié
  95 + if (!$READONLY) $controls["$name.comment"] = [
  96 + //'type' => 'input',
  97 + 'label' => false,
  98 + 'val' => $comment,
  99 + //'size' => 20,
  100 + //'disabled' => $READONLY,
  101 + //'readonly' => $READONLY,
  102 + ];
  103 +
  104 + // (EDIT ONLY)
  105 + $fieldsets_with_roles = ['UNEDITABLE_FIELDS','UNEDITABLE_FIELDS_AFTER_LOT0', 'UNEDITABLE_FIELDS_AFTER_LOT1', 'UNEDITABLE_FIELDS_AFTER_LOT2'];
  106 + // - ROLES exceptés (select multiple list)
  107 + //if (!$READONLY && $field_name!='HAS_ORDER_BUTTON')
  108 + if ( !$READONLY && in_array($fieldset_name, $fieldsets_with_roles) ) $controls["$name.except_roles"] = [
  109 + 'type' => 'select',
  110 + 'label' => '(excepté pour les rôles)',
  111 + //'options' => ['<aucune exception>', 'user', 'admin', 'resp'],
  112 + //'options' => ['Utilisateur', 'Responsable', 'Administration'],
  113 + 'options' => [
  114 + ['text' => 'Utilisateur', 'value' => 'Utilisateur'],
  115 + ['text' => 'Responsable', 'value' => 'Responsable'],
  116 + ['text' => 'Administration', 'value' => 'Administration'],
  117 + ],
  118 + //'val' => ['Responsable', 'Utilisateur'],
  119 + 'val' => $except_roles,
  120 + 'multiple' => 'checkbox',
  121 + //'size' => 20,
  122 + 'disabled' => $READONLY,
  123 + ];
  124 +
  125 + } // foreach $fields
  126 +
  127 + // https://api.cakephp.org/3.5/class-Cake.View.Helper.FormHelper.html#_controls
  128 + $DEBUG && debug($controls);
  129 + $fieldset_name_with_comment = $fieldset_name;
  130 + if ( isset($fields['fieldset_comment']) ) $fieldset_name_with_comment .= ' ('. $fields['fieldset_comment'] .')';
  131 + echo $this->Form->controls($controls, ['legend' => $fieldset_name_with_comment.' :']);
  132 +
  133 + } // foreach $fieldsets
  134 +
  135 + /*
  136 + echo $this->Form->control('name');
  137 + echo $this->Form->control('email');
  138 + echo $this->Form->control('body');
  139 + */
  140 +
  141 + if (!$READONLY)
  142 + echo $this->Form->button('Enregistrer', ['class'=>'btn btn-outline-success', 'type'=>'submit']);
  143 + //echo $this->Form->button('Submit');
  144 +
  145 +
  146 +echo $this->Form->end();
  147 +
  148 +if ($READONLY && $CAN_EDIT) $this->MyHelper->echoEditButton();
  149 +
src/Template/ConfigurationFields/index.ctp 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +<?php
  2 +$this->extend('view');
0 \ No newline at end of file 3 \ No newline at end of file
src/Template/ConfigurationFields/view.ctp 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +<?php
  2 +
  3 +$this->extend('edit');
  4 +
  5 +/*
  6 +$fields = $fields;
  7 +debug($fields);
  8 +*/
0 \ No newline at end of file 9 \ No newline at end of file
src/Template/Materiels/index.ctp
@@ -525,7 +525,7 @@ $displayLegend = function() { @@ -525,7 +525,7 @@ $displayLegend = function() {
525 <i style='color:black'>noir=livré (validé)</i> ;" 525 <i style='color:black'>noir=livré (validé)</i> ;"
526 //<i style='color:red'>rouge=à sortir</i> ; 526 //<i style='color:red'>rouge=à sortir</i> ;
527 ." <i style='color:blue'>bleu=archivé</i> ; 527 ." <i style='color:blue'>bleu=archivé</i> ;
528 - (couleur date : <i style='color:red'>rouge=fin garantie</i>, <i style='color:orange'>orange=< 1 an</i>) 528 + (couleur date : <i style='color:red'>rouge=fin garantie</i>, <i style='color:orange'>orange=<1 an</i>)
529 </i></p>"; 529 </i></p>";
530 }; 530 };
531 531
src/Template/Pages/about.ctp
1 <div class="index col-lg-12"> 1 <div class="index col-lg-12">
  2 +
2 <?php 3 <?php
3 $softwareName = "LABINVENT"; 4 $softwareName = "LABINVENT";
4 5
  6 +echo '<br/>';
5 echo '<h2><i class="icon-idea"></i> A PROPOS DU LOGICIEL ' . $softwareName . '</h2>'; 7 echo '<h2><i class="icon-idea"></i> A PROPOS DU LOGICIEL ' . $softwareName . '</h2>';
6 8
  9 +echo '<br/>';
  10 +echo '<a href="/pages/changes">Changements faits sur le logiciel (nouveautés et historique)</a>';
  11 +
  12 +echo '<br/><br/>';
  13 +echo '<a href="https://tinyurl.com/labinvent">Documentation générale</a>';
  14 +
  15 +echo '<br/><br/>';
  16 +echo '<a href="' . $this->request->webroot . 'doc/userguide/labinvent2_userguide.pdf">Guide utilisateur</a>';
  17 +echo '<br/><br/>';
  18 +
  19 +
  20 +
7 /* 21 /*
8 echo <<<"EOD" 22 echo <<<"EOD"
9 <section id="cadre" class="col-lg-12"> 23 <section id="cadre" class="col-lg-12">
@@ -34,8 +48,12 @@ Licence GPL (http://www.gnu.org/copyleft/gpl.html)&lt;br&gt;&lt;br&gt; @@ -34,8 +48,12 @@ Licence GPL (http://www.gnu.org/copyleft/gpl.html)&lt;br&gt;&lt;br&gt;
34 EOD; 48 EOD;
35 */ 49 */
36 50
  51 +?>
37 52
38 -echo '<section id="cadre" class="col-lg-12">'; 53 +
  54 +<section id="cadre" class="col-lg-12">
  55 +
  56 +<?php
39 57
40 // On lit le fichier /WWWROOT/../LICENSE 58 // On lit le fichier /WWWROOT/../LICENSE
41 $wwwroot_dir = new Cake\Filesystem\Folder(WWW_ROOT); 59 $wwwroot_dir = new Cake\Filesystem\Folder(WWW_ROOT);
@@ -75,7 +93,7 @@ $lines = new LimitIterator($f, $line_num_from); @@ -75,7 +93,7 @@ $lines = new LimitIterator($f, $line_num_from);
75 //$lines_reversed = array_reverse(iterator_to_array($lines)); 93 //$lines_reversed = array_reverse(iterator_to_array($lines));
76 $lines = iterator_to_array($lines); 94 $lines = iterator_to_array($lines);
77 foreach ($lines as $line) { 95 foreach ($lines as $line) {
78 - echo $line.'<br><br>'; 96 + echo $line.'<br>';
79 //if (mb_strpos($line, "$level: ") !== FALSE) echo $line.'<br><br>'; 97 //if (mb_strpos($line, "$level: ") !== FALSE) echo $line.'<br><br>';
80 //if (mb_strpos($line, 'Info: ') !== FALSE) echo $line.'<br><br>'; 98 //if (mb_strpos($line, 'Info: ') !== FALSE) echo $line.'<br><br>';
81 //if (mb_strpos($line, '/materiels/edit/') !== FALSE) echo $line.'<br><br>'; 99 //if (mb_strpos($line, '/materiels/edit/') !== FALSE) echo $line.'<br><br>';
@@ -84,18 +102,10 @@ foreach ($lines as $line) { @@ -84,18 +102,10 @@ foreach ($lines as $line) {
84 // Close file 102 // Close file
85 $f=null; 103 $f=null;
86 104
87 -echo '</section>';  
88 -  
89 -  
90 -echo '<br/>';  
91 -echo '<a href="/pages/changes">Changements faits sur le logiciel (nouveautés et historique)</a>'; 105 +?>
92 106
93 -echo '<br/><br/>';  
94 -echo '<a href="https://tinyurl.com/labinvent">Documentation générale</a>'; 107 +</section>
95 108
96 -echo '<br/><br/>';  
97 -echo '<a href="' . $this->request->webroot . 'doc/userguide/labinvent2_userguide.pdf">Guide utilisateur</a>';  
98 -?>  
99 </div> 109 </div>
100 110
101 <!-- 111 <!--
src/Template/Pages/home_app.ctp
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 //debug($this->viewVars); 4 //debug($this->viewVars);
5 $configuration = $configuration; 5 $configuration = $configuration;
6 $USER_IS_RESPONSABLE = $USER_IS_RESPONSABLE; 6 $USER_IS_RESPONSABLE = $USER_IS_RESPONSABLE;
  7 +$USER_IS_SUPERADMIN = $USER_IS_SUPERADMIN;
7 $HAS_ORDER_BUTTON = $HAS_ORDER_BUTTON; 8 $HAS_ORDER_BUTTON = $HAS_ORDER_BUTTON;
8 9
9 10
@@ -123,6 +124,12 @@ echo $SEP; @@ -123,6 +124,12 @@ echo $SEP;
123 echo '<a href="/pages/stats">Statistiques sur les matériels</a>'; 124 echo '<a href="/pages/stats">Statistiques sur les matériels</a>';
124 echo '</td></tr>'; 125 echo '</td></tr>';
125 126
  127 + if ($USER_IS_SUPERADMIN) {
  128 + echo '<tr><td>';
  129 + echo '<a href="/stats">Statistiques sur les connexions utilisateurs</a>';
  130 + echo '</td></tr>';
  131 + }
  132 +
126 echo $TABLE_END; 133 echo $TABLE_END;
127 134
128 135
src/Template/Pages/tools.ctp
@@ -52,7 +52,7 @@ $no_limit_mode = $no_limit_mode; @@ -52,7 +52,7 @@ $no_limit_mode = $no_limit_mode;
52 52
53 53
54 54
55 - // - Page configuration 55 + // - Page configuration Générale
56 //if ($role == 'Super Administrateur') : 56 //if ($role == 'Super Administrateur') :
57 if ($USER_IS_SUPERADMIN) 57 if ($USER_IS_SUPERADMIN)
58 $this->MyHelper->echoMenuItemWithIcon("config.png", "Configuration générale", 'configurations','view'); 58 $this->MyHelper->echoMenuItemWithIcon("config.png", "Configuration générale", 'configurations','view');
@@ -66,7 +66,13 @@ $no_limit_mode = $no_limit_mode; @@ -66,7 +66,13 @@ $no_limit_mode = $no_limit_mode;
66 echo '</td></tr>'; 66 echo '</td></tr>';
67 */ 67 */
68 //endif; 68 //endif;
69 - 69 +
  70 + // - Page configuration des champs obligatoires et readonly
  71 + if ($USER_IS_ADMIN_OR_MORE)
  72 + $this->MyHelper->echoMenuItemWithIcon("config.png", "Configuration des champs obligatoires et readonly", 'configuration-fields','index');
  73 +
  74 +
  75 + // Configuration des listes
70 if ($USER_IS_ADMIN_OR_MORE) 76 if ($USER_IS_ADMIN_OR_MORE)
71 $this->MyHelper->echoMenuItemWithIcon("config2.png", "Gérer le contenu variable de l'application", 'pages','tools_sm'); 77 $this->MyHelper->echoMenuItemWithIcon("config2.png", "Gérer le contenu variable de l'application", 'pages','tools_sm');
72 78
src/View/Helper/MyHelperHelper.php
@@ -203,7 +203,9 @@ class MyHelperHelper extends Helper { @@ -203,7 +203,9 @@ class MyHelperHelper extends Helper {
203 public function echoActionButton ( 203 public function echoActionButton (
204 $icon_class, $buttonStyle, $title, 204 $icon_class, $buttonStyle, $title,
205 $action, $id, $controller=null, // si controleur null =>controleur par défaut 205 $action, $id, $controller=null, // si controleur null =>controleur par défaut
206 - $other_args=[], $tip='', $confirmMessage='', $moreButtonStyle='' 206 + $other_args=[], $tip='',
  207 + $confirmMessage='', $moreButtonStyle='',
  208 + $with_confirm=false
207 ) { 209 ) {
208 210
209 //if ($controller=='') $controller='materiels'; 211 //if ($controller=='') $controller='materiels';
@@ -212,28 +214,67 @@ class MyHelperHelper extends Helper { @@ -212,28 +214,67 @@ class MyHelperHelper extends Helper {
212 $controllerArgs['action'] = $action; 214 $controllerArgs['action'] = $action;
213 $controllerArgs[] = $id; 215 $controllerArgs[] = $id;
214 foreach ($other_args as $other_arg) $controllerArgs[] = $other_arg; 216 foreach ($other_args as $other_arg) $controllerArgs[] = $other_arg;
215 - echo $this->Html->link(  
216 - __("<i class=$icon_class></i>$title"),  
217 - $controllerArgs,  
218 - /*  
219 - [  
220 - 'controller' => $controller,  
221 - 'action' => $action,  
222 - $id,  
223 - $other_args  
224 - ],  
225 - */  
226 - [  
227 - 'title' => $tip,  
228 - 'escape' => false,  
229 - 'onclick' => 'return true;',  
230 - //'style' => 'margin-right: 10px'.$moreButtonStyle,  
231 - 'style' => $buttonStyle,  
232 - 'confirm' => $confirmMessage  
233 - ] 217 +
  218 + // Avec confirmation (avant de déclencher l'action)
  219 + if ($with_confirm) {
  220 + if (!$confirmMessage) $confirmMessage = __("Êtes-vous sur de vouloir $title cette entité ?");
  221 + echo $this->Form->postLink(__("<i class='$icon_class'></i>$title"),
  222 + $controllerArgs,
  223 + [
  224 + 'title' => $title,
  225 + //'style' => $buttonStyle,
  226 + 'style' => 'margin-left: 10px',
  227 + 'escape' => false,
  228 + 'confirm' => $confirmMessage,
  229 + //'confirm' => $confirmMessage
  230 + //'confirm' => __("Êtes-vous sur de vouloir $title cette entité ?"),
  231 + //'confirm' => __('Êtes-vous sur de vouloir supprimer # {0}?', $suivi->id)
  232 + ]);
  233 + }
  234 +
  235 + // Sans confirmation (action directe)
  236 + else {
  237 + echo $this->Html->link(
  238 + __("<i class=$icon_class></i>$title"),
  239 + $controllerArgs,
  240 + /*
  241 + [
  242 + 'controller' => $controller,
  243 + 'action' => $action,
  244 + $id,
  245 + $other_args
  246 + ],
  247 + */
  248 + [
  249 + 'title' => $tip,
  250 + 'escape' => false,
  251 + 'onclick' => 'return true;',
  252 + //'style' => 'margin-right: 10px'.$moreButtonStyle,
  253 + 'style' => $buttonStyle,
  254 + //'confirm' => $confirmMessage ? $confirmMessage : $tip,
  255 + ]
  256 + );
  257 + }
  258 +
  259 + }
  260 +
  261 +
  262 + public function echoEditButton () { $this->echoButtonForAction('icon-pencil', 'edit', 'Editer', 'Modifier'); }
  263 +
  264 + public function echoButtonForAction ($icon_class, $action, $label, $tooltip, $with_confirm=false) {
  265 + //$icon_class = 'icon-pencil';
  266 + $buttonStyle = 'margin-right: 10px';
  267 + echo '<div id="boutons" class="actions" style="margin-bottom:20px; width:100%; float:none; padding:5px 0;">';
  268 + $this->echoActionButton($icon_class, $buttonStyle,
  269 + " $label", $action, null, null, [], $tooltip,
  270 + '', '',
  271 + $with_confirm
234 ); 272 );
  273 + echo "</div>";
235 } 274 }
236 275
  276 +
  277 +
237 public function echoDeleteButton ( 278 public function echoDeleteButton (
238 $title, 279 $title,
239 $id, $controller=null, // si controleur null =>controleur par défaut 280 $id, $controller=null, // si controleur null =>controleur par défaut