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
CHANGELOG
... ... @@ -373,12 +373,6 @@ Par contre, ok avec FPDF
373 373  
374 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 376 *) => update matos lifecycle diag
383 377  
384 378  
... ... @@ -541,11 +535,19 @@ TODO :
541 535  
542 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 543 - Utiliser les vues "index" des entités associées pour la vue "view" de materiel (et suivi) :
547 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 551 ======= NEXT =======
550 552  
551 553 print etiquette pour les 2 serveurs + tester fin garantie orange
... ... @@ -563,9 +565,45 @@ Vues génériques (index et view) :
563 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 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 607 15/11/2021 v5.3.0-3.7.9
570 608 - (e) Refactorisation : Vue détaillée users (view) générique avec contenu enrichi ("(responsable)")
571 609 - (e) Refactorisation : Vue liste users (index) générique avec contenu enrichi ("(responsable)")
... ...
README.md
... ... @@ -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 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   -# 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 36 */
37 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 43 // Use composer to load the autoloader.
40 44 require ROOT . DS . 'vendor' . DS . 'autoload.php';
41 45  
... ... @@ -106,17 +110,31 @@ try {
106 110 }
107 111  
108 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 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 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 126 //Configure::load('app_labinvent_mandatory_fields_IP2I', 'yaml');
117 127 //Configure::load('app_labinvent_authorizations', 'yaml');
118 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 140 // Load an environment local configuration file.
... ...
src/Controller/ConfigurationFieldsController.php 0 → 100644
... ... @@ -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 309 \ No newline at end of file
... ...
src/Controller/MaterielsController.php
... ... @@ -1913,8 +1913,40 @@ class MaterielsController extends AppController {
1913 1913 }
1914 1914 */
1915 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 1950 //debug($fname);
1919 1951 // - Documents attachés (champs virtuels)
1920 1952 // DOC_DEVIS, DOC_BC, ...
... ... @@ -1937,8 +1969,8 @@ class MaterielsController extends AppController {
1937 1969  
1938 1970 }
1939 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 1976 } // view
... ... @@ -1974,7 +2006,6 @@ class MaterielsController extends AppController {
1974 2006 // uniquement à cause de parent::add_or_edit() :
1975 2007 $entity_name=null, array $associated_entities=[], $with_parent=false) {
1976 2008  
1977   - //debug("ici"); exit;
1978 2009 $this->myDebug("step 3: MaterielsController.add_or_edit()");
1979 2010 $IS_EDIT = !$IS_ADD;
1980 2011  
... ... @@ -2080,72 +2111,11 @@ class MaterielsController extends AppController {
2080 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 2119 * Les données ont été saisies et postées
2150 2120 * On va donc les sauvegarder (si ok)
2151 2121 */
... ... @@ -2154,123 +2124,32 @@ class MaterielsController extends AppController {
2154 2124 $authorized_actions = $IS_ADD ? ['post'] : ['post','patch','put'];
2155 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 2129 // (1) On remplit $materiel avec les données de ce materiel
2173 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 2134 * \AV1 : Attributs OBLIGATOIRES
2235 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 2138 //$mandatory_fields = MaterielsTable::getMandatoryFieldsForMaterielStatus($materiel->status);
2261 2139 $mandatory_fields = $this->Materiels->getMandatoryFieldsForMateriel($materiel);
  2140 + //debug($mandatory_fields);
2262 2141 //$mandatory_fields = $this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status);
2263   - //debug($mandatory_fields); exit;
2264 2142  
2265 2143 $verb = $IS_ADD ? "ajouté" : "modifié";
2266 2144  
2267 2145 // On vérifie que les infos obligatoires sont présentes
2268 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 2148 //foreach ($mandatory_fields as $fname => $fval) {
2271 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 2154 * Champs obligatoires à ignorer :
2276 2155 * - champs virtuels (n'existent pas physiquement) => DOC_XXX (DOC_DEVIS, DOC_BC, ...)
... ... @@ -2282,7 +2161,7 @@ class MaterielsController extends AppController {
2282 2161 //if ( in_array($fname, ['DOC_DEVIS', 'fournisseur_id', 'etiquette', 'numero_inventaire_organisme']) ) continue;
2283 2162  
2284 2163 if ($materiel->$fname === null || $materiel->$fname == '') {
2285   - $ALL_MANDATORY_FIELDS_FOR_GIVEN = false;
  2164 + $ALL_MANDATORY_FIELDS_GIVEN = false;
2286 2165 /* (EP 2020 03)
2287 2166 * Ce genre de ligne ($this->Flash->...) affichant un message flash en haut de page,
2288 2167 * ne fonctionnait plus à cause de bootstrap (css).
... ... @@ -2292,7 +2171,8 @@ class MaterielsController extends AppController {
2292 2171 * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc)
2293 2172 */
2294 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 2176 /////debug($msgError1);
2297 2177 $this->Flash->error($error_msg);
2298 2178 /* MARCHE PAS POURQUOI ?
... ... @@ -2307,10 +2187,11 @@ class MaterielsController extends AppController {
2307 2187 ///////////////$this->Flash->error(__("Le matériel n'a pas pu être $verb"));
2308 2188 //return false;
2309 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 2196 // (3) On l'ajoute en BD, on envoie un email, et on affiche ok sur page accueil
2316 2197 //$verb = $IS_ADD ? "ajouté" : "modifié";
... ... @@ -2342,7 +2223,8 @@ class MaterielsController extends AppController {
2342 2223 foreach ($materiel->getErrors() as $f=>$e)
2343 2224 foreach($e as $k=>$v) $this->Flash->error(__($v.' : '.$f));
2344 2225  
2345   - }
  2226 + } // save KO
  2227 +
2346 2228 else {
2347 2229 //debug($this->getCurrentEntity()); exit;
2348 2230 //debug($this->Materiels->current_entity); exit;
... ... @@ -2380,7 +2262,7 @@ class MaterielsController extends AppController {
2380 2262 'action' => 'view',
2381 2263 $id
2382 2264 ]);
2383   - }
  2265 + } // save KO
2384 2266  
2385 2267 } // $ALL_MANDATORY_FIELDS_FOR_GIVEN
2386 2268  
... ... @@ -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 2274 * C'est la première fois qu'on vient sur cette vue,
2394 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 2592 'ARCHIVED' => 'ARCHIVED'
2710 2593 ];
2711 2594 $entity = $materiel;
  2595 + //debug($entity);
2712 2596 $this->set(compact(
2713 2597 'IS_ADD',
2714 2598 'mail_responsable',
... ... @@ -2738,8 +2622,10 @@ class MaterielsController extends AppController {
2738 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 2629 //$this->set('readonlyFields', $IS_ADD ? [] : $this->getUneditableFieldsForMaterielStatus($materiel->status));
2744 2630 //debug($this->Materiels->getUneditableFieldsForMaterielStatus($status)); exit;
2745 2631 //$this->set('readonlyFields', $IS_ADD ? [] : $this->Materiels->getUneditableFieldsForMaterielStatus($materiel->status));
... ... @@ -2767,11 +2653,12 @@ class MaterielsController extends AppController {
2767 2653 if ($this->USER_IS_SUPERADMIN()) return [];
2768 2654  
2769 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 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 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 2662 //debug("remove $fname");
2776 2663 }
2777 2664 }
... ... @@ -3051,10 +2938,11 @@ class MaterielsController extends AppController {
3051 2938  
3052 2939 // Attributs obligatoires pour la phase COMMANDE (LOT1)
3053 2940 $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(1);
  2941 + //debug($mandatoryFields);
3054 2942 $ACTION = 'ordonner la commande de';
3055 2943  
3056 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 2947 // Doc attaché obligatoire ? (champ virtuel)
3060 2948 //if (strtoupper($fname) == 'DOC_DEVIS') {
... ... @@ -3085,7 +2973,7 @@ class MaterielsController extends AppController {
3085 2973 $fval = $materiel->$fname;
3086 2974 if ($fval === null || $fval == '') {
3087 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 2977 $this->Flash->error($error_msg);
3090 2978 // Utile ??? (plante les tests)
3091 2979 ////$this->e->setError($fname, 'Ce champ ne doit pas être vide');
... ... @@ -3108,6 +2996,7 @@ class MaterielsController extends AppController {
3108 2996 if ($newStatus == 'VALIDATED') {
3109 2997 // Attributs obligatoires pour la phase VALIDATION (livré et payé) (LOT2)
3110 2998 $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(2);
  2999 + //$mandatoryFields = $this->Materiels->getCategoryFieldsForLot('MANDATORY_FIELDS_FOR_LOT', 2)
3111 3000 //$mandatoryFields = $this->Materiels->MANDATORY_FIELDS_FOR_LOT2;
3112 3001 //debug($mandatoryFields);exit;
3113 3002 /*
... ... @@ -3142,7 +3031,7 @@ class MaterielsController extends AppController {
3142 3031 */
3143 3032  
3144 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 3036 $ACTION = 'valider';
3148 3037  
... ... @@ -3184,7 +3073,8 @@ class MaterielsController extends AppController {
3184 3073 * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc)
3185 3074 */
3186 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 3078 $this->Flash->error($error_msg);
3189 3079 //debug($error_msg);
3190 3080  
... ...
src/Controller/PagesController.php
... ... @@ -70,6 +70,7 @@ class PagesController extends AppController
70 70 {
71 71 $this->myDebug("step 0A (specific): PagesController.initialize()");
72 72 parent::initialize();
  73 + $this->modelClass = false;
73 74 // On autorise l'action add SANS authentification (unauthenticated)
74 75 //$this->Auth->allow(['add']);
75 76 //$this->LdapAuth->allow(['display']);
... ...
src/Controller/StatsController.php
... ... @@ -61,7 +61,7 @@ class StatsController extends AppController
61 61 'nice_name'=>"Temps connexion cumulé (h)", 'f'=>'getHourMnSecForDuration'],
62 62 'connex_nb'=>['nice_name'=>"Nb connexions"],
63 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 66 ['Users'],
67 67 false, false,
... ...
src/Controller/SurCategoriesController.php
... ... @@ -256,6 +256,8 @@ class SurCategoriesController extends AppController
256 256 $surCategory = $this->SurCategories->get($id, [
257 257 'contain' => []
258 258 ]);
  259 +
  260 + // En mode POST
259 261 if ($this->request->is([
260 262 'patch',
261 263 'post',
... ... @@ -274,10 +276,14 @@ class SurCategoriesController extends AppController
274 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 281 $this->set(compact('surCategory'));
  282 + /* (EP) inutile sauf si json
278 283 $this->set('_serialize', [
279 284 'surCategory'
280 285 ]);
  286 + */
281 287 }
282 288  
283 289 /**
... ...
src/Form/ConfigurationFieldsForm.php 0 → 100644
... ... @@ -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 48 \ No newline at end of file
... ...
src/Model/Table/MaterielsTable.php
... ... @@ -252,60 +252,84 @@ class MaterielsTable extends AppTable
252 252 * - LOT0 total = [] + LOT0
253 253 * - LOT1 total = LOT0 + LOT1
254 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 301 public static function getMandatoryFieldsForLot($lot_num) {
  302 + return self::getCategoryFieldsForLot('MANDATORY_FIELDS_FOR_LOT', $lot_num);
257 303 /*
258 304 $specific_constant_name = "MANDATORY_FIELDS_FOR_LOT1_".self::getLabName();
259 305 $mandatory_fields_lot1 = defined($specific_constant_name) ? constant($specific_constant_name) : MANDATORY_FIELDS_FOR_LOT1;
260 306 return $mandatory_fields_lot1;
261 307 */
  308 + /*
262 309 if ($lot_num < 0) return [];
263 310 $base_fields = self::getMandatoryFieldsForLot($lot_num-1);
264 311 $new_fields = Configure::readOrFail('MANDATORY_FIELDS_FOR_LOT'.$lot_num);
265 312 //debug($new_fields);
266 313 return array_merge($base_fields, $new_fields);
  314 + */
267 315 }
  316 + // RECURSIF
268 317 public static function getRecommendedFieldsForLot($lot_num) {
  318 + return self::getCategoryFieldsForLot('RECOMMENDED_FIELDS_AFTER_LOT', $lot_num);
  319 + /*
269 320 if ($lot_num < 0) return [];
270 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 323 //debug($new_fields);
273 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 327 // (EP 2021 09) fonction récursive :
306 328 // => champs readonly pour le lot N = champs readonly pour le lot N-1 + champs readonly pour le lot N :
307 329 public static function getUneditableFieldsForLot($lot_num) {
308   -
  330 + return self::getCategoryFieldsForLot('UNEDITABLE_FIELDS_AFTER_LOT', $lot_num, 'UNEDITABLE_FIELDS');
  331 + /*
  332 +
309 333 // Si N = -1 => on retourne la liste initiale de champs readonly (fin de la récursivité)
310 334 //if ($lot_num < 0) return [];
311 335 if ($lot_num < 0) return self::list_to_dict( Configure::readOrFail('UNEDITABLE_FIELDS') );
... ... @@ -323,10 +347,57 @@ class MaterielsTable extends AppTable
323 347 // => Somme des 2 (champs readonly pour le lot N-1 + champs readonly pour le lot N)
324 348 //return array_unique(array_merge($base_uneditable_fields, $new_uneditable_fields));
325 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 402 public static function getMandatoryFieldsLot1() {
332 403 /S
... ... @@ -353,7 +424,13 @@ class MaterielsTable extends AppTable
353 424 $fields = self::getMandatoryFieldsForMaterielStatus($status);
354 425 // Seulement si prix > 10K€ : exiger la facture jointe et le n° série et lieu de stockage précis
355 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 434 //TODO 202109
358 435 //$fields[] = 'lieu stockage';
359 436 //$fields[] = 'facture jointe';
... ... @@ -361,7 +438,9 @@ class MaterielsTable extends AppTable
361 438 return $fields;
362 439 }
363 440 public static function getMandatoryFieldsForMaterielStatus($status) {
364   -
  441 + //return self::getCategoryFieldsForMaterielStatus('MANDATORY_FIELDS_FOR_LOT', $status);
  442 + return self::getCategoryFieldsForMaterielStatus('MANDATORY', $status);
  443 + /*
365 444 if (is_null($status)) $status='CREATED';
366 445 // On recup le LOT qui convient au status courant du matos
367 446 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2);
... ... @@ -375,9 +454,12 @@ class MaterielsTable extends AppTable
375 454 //debug($fields);
376 455  
377 456 return $fields;
  457 + */
378 458 }
379 459 public static function getUneditableFieldsForMaterielStatus($status) {
380   -
  460 + //return self::getCategoryFieldsForMaterielStatus('UNEDITABLE_FIELDS_AFTER_LOT', $status);
  461 + return self::getCategoryFieldsForMaterielStatus('UNEDITABLE', $status);
  462 + /*
381 463 if (is_null($status)) $status='CREATED';
382 464 // On recup le LOT qui convient au status courant du matos
383 465 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2);
... ... @@ -390,12 +472,19 @@ class MaterielsTable extends AppTable
390 472 $fields = self::getUneditableFieldsForLot($lot_num);
391 473  
392 474 return $fields;
  475 + */
393 476 }
394 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 488 if (is_null($status)) $status='CREATED';
400 489 // On recup le LOT qui convient au status courant du matos
401 490 //$fields = ($status == 'CREATED') ? self::getMandatoryFieldsForLot(1) : self::getMandatoryFieldsForLot(2);
... ... @@ -1560,6 +1649,9 @@ class MaterielsTable extends AppTable
1560 1649 $fournisseur_asis = $entity->fournisseur ? $entity->fournisseur['name'] : '';
1561 1650 // Enlever les espaces superflus
1562 1651 $fournisseur = trim($fournisseur_asis);
  1652 + //debug($fournisseur);
  1653 + //debug($entity);
  1654 +
1563 1655  
1564 1656 // Si champ fournisseur obligatoire, vérification qu'il est bien rempli (ou emission d'une erreur)
1565 1657 //if (IP2I && $entity->fournisseur && $fournisseur == '') {
... ... @@ -1688,6 +1780,7 @@ class MaterielsTable extends AppTable
1688 1780 } // ssi changement
1689 1781 // Finalement, on supprime les champs 'fournisseur' car sinon erreur de sauvegarde,
1690 1782 // (normal ces champs n'existent pas dans materiel)
  1783 + //debug($entity); exit;
1691 1784 unset($entity->fournisseur_orig);
1692 1785 unset($entity->fournisseur);
1693 1786 //debug($entity); exit;
... ...
src/Template/ConfigurationFields/edit.ctp 0 → 100644
... ... @@ -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 @@
  1 +<?php
  2 +$this->extend('view');
0 3 \ No newline at end of file
... ...
src/Template/ConfigurationFields/view.ctp 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<?php
  2 +
  3 +$this->extend('edit');
  4 +
  5 +/*
  6 +$fields = $fields;
  7 +debug($fields);
  8 +*/
0 9 \ No newline at end of file
... ...
src/Template/Materiels/index.ctp
... ... @@ -525,7 +525,7 @@ $displayLegend = function() {
525 525 <i style='color:black'>noir=livré (validé)</i> ;"
526 526 //<i style='color:red'>rouge=à sortir</i> ;
527 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 529 </i></p>";
530 530 };
531 531  
... ...
src/Template/Pages/about.ctp
1 1 <div class="index col-lg-12">
  2 +
2 3 <?php
3 4 $softwareName = "LABINVENT";
4 5  
  6 +echo '<br/>';
5 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 22 echo <<<"EOD"
9 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 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 58 // On lit le fichier /WWWROOT/../LICENSE
41 59 $wwwroot_dir = new Cake\Filesystem\Folder(WWW_ROOT);
... ... @@ -75,7 +93,7 @@ $lines = new LimitIterator($f, $line_num_from);
75 93 //$lines_reversed = array_reverse(iterator_to_array($lines));
76 94 $lines = iterator_to_array($lines);
77 95 foreach ($lines as $line) {
78   - echo $line.'<br><br>';
  96 + echo $line.'<br>';
79 97 //if (mb_strpos($line, "$level: ") !== FALSE) echo $line.'<br><br>';
80 98 //if (mb_strpos($line, 'Info: ') !== FALSE) echo $line.'<br><br>';
81 99 //if (mb_strpos($line, '/materiels/edit/') !== FALSE) echo $line.'<br><br>';
... ... @@ -84,18 +102,10 @@ foreach ($lines as $line) {
84 102 // Close file
85 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 109 </div>
100 110  
101 111 <!--
... ...
src/Template/Pages/home_app.ctp
... ... @@ -4,6 +4,7 @@
4 4 //debug($this->viewVars);
5 5 $configuration = $configuration;
6 6 $USER_IS_RESPONSABLE = $USER_IS_RESPONSABLE;
  7 +$USER_IS_SUPERADMIN = $USER_IS_SUPERADMIN;
7 8 $HAS_ORDER_BUTTON = $HAS_ORDER_BUTTON;
8 9  
9 10  
... ... @@ -123,6 +124,12 @@ echo $SEP;
123 124 echo '<a href="/pages/stats">Statistiques sur les matériels</a>';
124 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 133 echo $TABLE_END;
127 134  
128 135  
... ...
src/Template/Pages/tools.ctp
... ... @@ -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 56 //if ($role == 'Super Administrateur') :
57 57 if ($USER_IS_SUPERADMIN)
58 58 $this->MyHelper->echoMenuItemWithIcon("config.png", "Configuration générale", 'configurations','view');
... ... @@ -66,7 +66,13 @@ $no_limit_mode = $no_limit_mode;
66 66 echo '</td></tr>';
67 67 */
68 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 76 if ($USER_IS_ADMIN_OR_MORE)
71 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 203 public function echoActionButton (
204 204 $icon_class, $buttonStyle, $title,
205 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 211 //if ($controller=='') $controller='materiels';
... ... @@ -212,28 +214,67 @@ class MyHelperHelper extends Helper {
212 214 $controllerArgs['action'] = $action;
213 215 $controllerArgs[] = $id;
214 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 278 public function echoDeleteButton (
238 279 $title,
239 280 $id, $controller=null, // si controleur null =>controleur par défaut
... ...