Commit 9e0dc82296211c0963f675e07f48cc2e0eceb13f

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

Amélioration importante de la config des champs non modifiables

- ajout optionnel d'une exception pour un (ou plusieurs) role(s)
		=> Exemple pour interdire la modif du fournisseur (sauf pour le role
Administration) :
		"- fournisseur_id (except Administration)"

v5.2.17-3.7.9
CHANGELOG
... ... @@ -461,6 +461,12 @@ Commencer à implémenter le nouveau workflow v5 :
461 461 ======= CHANGES =======
462 462  
463 463 -------
  464 +22/10/2021 v5.2.17-3.7.9
  465 + - (i) Amélioration importante de la config des champs non modifiables : ajout optionnel d'une exception pour un (ou plusieurs) role(s)
  466 + => Exemple pour interdire la modif du fournisseur (sauf pour le role Administration) :
  467 + "- fournisseur_id (except Administration)"
  468 +
  469 +-------
464 470 22/10/2021 v5.2.16-3.7.9
465 471 - (b) bugfix champs readonly
466 472 - refactorisation des champs readonly, bien plus compact et lisible (clean)
... ...
README.md
... ... @@ -53,7 +53,7 @@ Logiciel testé et validé sur les configurations suivantes :
53 53 --------------------------------------------------------------------------------------------
54 54  
55 55 Date: 22/10/2021
56   -Version: v5.2.16-3.7.9
  56 +Version: v5.2.17-3.7.9
57 57  
58 58  
59 59  
... ...
config/app_labinvent_mandatory_fields.default.yml
... ... @@ -45,8 +45,88 @@ MANDATORY_FIELDS_LOT0:
45 45  
46 46  
47 47 # Liste des champs qui sont NON MODIFIABLES APRÈS la CRÉATION d'une fiche
48   -# A priori par défaut : aucun (mais ca reste configurable)
49   -UNEDITABLE_FIELDS_LOT0: []
  48 +# - Aucun champ
  49 +#UNEDITABLE_FIELDS_LOT0: []
  50 +# - Au moins un champ
  51 +UNEDITABLE_FIELDS_LOT0:
  52 +
  53 + # #### CHAMPS GÉNÉRAUX ####
  54 +
  55 + #- designation
  56 +
  57 + #- will_stay
  58 +
  59 + #- description
  60 +
  61 + #- hors_service
  62 +
  63 + #- sur_categorie_id
  64 +
  65 + #- categorie_id
  66 +
  67 + #- sous_categorie_id
  68 +
  69 + #- groupes_thematique_id
  70 +
  71 + #- groupes_metier_id
  72 +
  73 + #- projet_id
  74 +
  75 + #- materiel_technique
  76 + #- materiel_administratif
  77 +
  78 + #- metrologie
  79 +
  80 + #- etiquette
  81 +
  82 + #- site_id
  83 +
  84 + #- lieu_detail
  85 +
  86 + #- date_acquisition
  87 +
  88 + #- date_reception
  89 +
  90 + # Garantie
  91 + #- duree_garantie
  92 + #- unite_duree_garantie
  93 + #- date_fin_garantie
  94 +
  95 + # Super Administrateur only
  96 + #- status
  97 +
  98 + #- numero_serie
  99 +
  100 + # Utilisateur du materiel (destinataire du bien)
  101 + #- nom_user
  102 +
  103 + # - Acheteur (le Créateur de la fiche) - Un role "Utilisateur" ne peut pas changer ça, c'est par défaut lui-même
  104 + - nom_responsable (except Responsable, Administration)
  105 +
  106 + #- nom_ancien_responsable
  107 +
  108 + #- resp_credit
  109 +
  110 + #- gestionnaire_id
  111 +
  112 + #- fournisseur_id
  113 +
  114 + #- organisme_id
  115 +
  116 + #- prix_ht
  117 +
  118 + #- budgets
  119 +
  120 + # #### CHAMPS ADMINISTRATIFS : ####
  121 + - eotp (except Administration)
  122 + - numero_commande (except Administration)
  123 + - numero_laboratoire # READONLY toujours car généré automatiquement
  124 + - numero_inventaire_organisme (except Administration)
  125 + - numero_inventaire_old (except Administration)
  126 +
  127 +
  128 +
  129 +
50 130  
51 131  
52 132  
... ... @@ -125,34 +205,73 @@ MANDATORY_FIELDS_LOT1:
125 205 # - Au moins un champ
126 206 UNEDITABLE_FIELDS_LOT1:
127 207  
  208 + # #### CHAMPS GÉNÉRAUX ####
  209 +
128 210 #- designation
129   -
  211 +
  212 + #- will_stay
  213 +
130 214 #- description
131 215  
  216 + #- hors_service
  217 +
  218 + # On ne devrait pas pouvoir changer la NATURE du bien
132 219 # Domaine & Catégorie
133   - - sur_categorie_id
134   - - categorie_id
  220 + - sur_categorie_id (except Administration) # Domaine
  221 + - categorie_id (except Administration) # Catégorie
  222 + #- sous_categorie_id
  223 +
  224 + #- groupes_thematique_id
  225 +
  226 + #- groupes_metier_id
  227 +
  228 + #- projet_id
  229 +
  230 + #- materiel_technique
  231 + #- materiel_administratif
135 232  
136   - # Utilisateur
  233 + #- metrologie
  234 +
  235 + #- etiquette
  236 +
  237 + #- site_id
  238 +
  239 + #- lieu_detail
  240 +
  241 + #- date_acquisition
  242 +
  243 + #- date_reception
  244 +
  245 + # Garantie
  246 + #- duree_garantie
  247 + #- unite_duree_garantie
  248 + #- date_fin_garantie
  249 +
  250 + # Super Administrateur only
  251 + #- status
  252 +
  253 + #- numero_serie
  254 +
  255 + # Utilisateur du materiel (destinataire du bien)
137 256 #- nom_user
138 257  
139   - # - Acheteur (le Créateur de la fiche)
140   - - nom_responsable
141   - # (rempli automatiquement)
142   - - email_responsable
  258 + # - Acheteur (le Créateur de la fiche) - Un role "Utilisateur" ne peut pas changer ça, c'est par défaut lui-même
  259 + #- nom_responsable (except Responsable, Administration)
  260 +
  261 + #- nom_ancien_responsable
143 262  
144 263 # (par défaut = acheteur)
145 264 #- resp_credit
146   -
147   - #- organisme_id
148   -
149   - #- prix_ht
150 265  
151 266 #- gestionnaire_id
152 267  
153 268 # Fournisseur
154   - #- fournisseur_id
  269 + - fournisseur_id (except Administration)
155 270  
  271 + #- organisme_id
  272 +
  273 + - prix_ht (except Administration)
  274 +
156 275 # Utilisé par la Gestion pour remplir le champ eotp
157 276 #- budgets
158 277  
... ... @@ -160,6 +279,17 @@ UNEDITABLE_FIELDS_LOT1:
160 279 # (TODO) Non encore configurable : cet aspect est encore géré "en dur" dans le code source
161 280 # Le devis attaché au matériel commandé n'est ni modifiable ni supprimable
162 281  
  282 + # #### CHAMPS ADMINISTRATIFS : ####
  283 + - eotp (except Administration)
  284 + - numero_commande (except Administration)
  285 + - numero_laboratoire # READONLY toujours car généré automatiquement
  286 + - numero_inventaire_organisme (except Administration)
  287 + - numero_inventaire_old (except Administration)
  288 +
  289 +
  290 +
  291 +
  292 +
163 293  
164 294  
165 295  
... ... @@ -216,7 +346,7 @@ MANDATORY_FIELDS_LOT2:
216 346 # - Au moins un champ
217 347 UNEDITABLE_FIELDS_LOT2:
218 348  
219   - # Champs généraux :
  349 + # #### CHAMPS GÉNÉRAUX ####
220 350  
221 351 #- designation
222 352  
... ... @@ -258,14 +388,17 @@ UNEDITABLE_FIELDS_LOT2:
258 388 #- unite_duree_garantie
259 389 #- date_fin_garantie
260 390  
  391 + # Super Administrateur only
261 392 #- status
262 393  
263 394 #- numero_serie
264 395  
  396 + # Utilisateur du materiel (destinataire du bien)
265 397 #- nom_user
266   - #- nom_responsable
267   - # (rempli automatiquement)
268   - #- email_responsable
  398 +
  399 + # - Acheteur (le Créateur de la fiche) - Un role "Utilisateur" ne peut pas changer ça, c'est par défaut lui-même
  400 + #- nom_responsable (except Responsable, Administration)
  401 +
269 402 #- nom_ancien_responsable
270 403  
271 404 #- resp_credit
... ... @@ -281,7 +414,7 @@ UNEDITABLE_FIELDS_LOT2:
281 414 #- budgets
282 415  
283 416  
284   - # #### CHAMPS ADMINISTRATIFS : ####
  417 + # #### CHAMPS ADMINISTRATIFS ####
285 418  
286 419 - eotp
287 420  
... ...
src/Controller/MaterielsController.php
... ... @@ -503,7 +503,7 @@ class MaterielsController extends AppController {
503 503 */
504 504 //$this->setAuthorizationsForAction('edit (modifier)', ['CREATED',0], [
505 505 $this->setAuthorizationsForAction('edit (modifier)', ['NOT ARCHIVED',0], [
506   - 'user' => ['CREATED',1],
  506 + 'user' => ['CREATED||TOBEORDERED',1],
507 507 'resp' => 'user',
508 508 //'resp' => -1,
509 509 //'resp' => 0,
... ... @@ -520,7 +520,8 @@ class MaterielsController extends AppController {
520 520 //$this->setAuthorizationsForAction('devalidate',
521 521 $this->setAuthorizationsForAction('statusCreated (invalider)', ['NOT CREATED',0], [
522 522 'user' => -1, // PAS AUTORISÉ
523   - 'resp' => ['NOT CREATED',1],
  523 + 'resp' => -1,
  524 + //'resp' => ['NOT CREATED',1],
524 525 ]);
525 526  
526 527 // - Action 'upgrade' (avancement du statut d'un matériel)
... ... @@ -2707,7 +2708,8 @@ class MaterielsController extends AppController {
2707 2708  
2708 2709  
2709 2710 //debug($this->Materiels->getUneditableFieldsForMaterielStatus($status)); exit;
2710   - $this->set('readonlyFields', $IS_ADD ? [] : $this->Materiels->getUneditableFieldsForMaterielStatus($materiel->status));
  2711 + $this->set('readonlyFields', $IS_ADD ? [] : $this->getUneditableFieldsForMaterielStatus($materiel->status));
  2712 + //$this->set('readonlyFields', $IS_ADD ? [] : $this->Materiels->getUneditableFieldsForMaterielStatus($materiel->status));
2711 2713 //$this->set('CAN_PRINT_LABEL', $this->isAuthorizedActionForCurrentUser('printLabelRuban', $id));
2712 2714 $this->set('CAN_PRINT_LABEL', $IS_ADD ? $this->confLabinvent->hasPrinter : $this->isAuthorizedActionForCurrentUser('printLabelRuban'));
2713 2715  
... ... @@ -2719,7 +2721,30 @@ class MaterielsController extends AppController {
2719 2721  
2720 2722 } //add_or_edit()
2721 2723  
2722   -
  2724 +
  2725 + private function currentUserRoleInList($except_list) {
  2726 + //debug($this->user_role);
  2727 + return in_array($this->getUserRole(), $except_list);
  2728 + }
  2729 +
  2730 + private function getUneditableFieldsForMaterielStatus($materiel_status) {
  2731 +
  2732 + // Superadmin peut tout modifier
  2733 + if ($this->USER_IS_SUPERADMIN()) return [];
  2734 +
  2735 + $uneditable_fields = $this->Materiels->getUneditableFieldsForMaterielStatus($materiel_status);
  2736 + foreach ($uneditable_fields as $fname=>$except_list) {
  2737 + // S'il y a une exception, voir si elle concerne le USER courant, sinon supprimer ce champ $fname
  2738 + if ($except_list) {
  2739 + //debug($except_list);
  2740 + if ($this->currentUserRoleInList($except_list)) unset($uneditable_fields[$fname]);
  2741 + //debug("remove $fname");
  2742 + }
  2743 + }
  2744 + return $uneditable_fields;
  2745 +
  2746 + }
  2747 +
2723 2748 /**
2724 2749 * Add method
2725 2750 *
... ...
src/Model/Table/AppTable.php
... ... @@ -11,6 +11,8 @@ use Cake\Datasource\EntityInterface;
11 11 class AppTable extends Table
12 12 {
13 13  
  14 + const ROLES = [ 'Utilisateur', 'Responsable', 'Administration', 'Super Administrateur' ];
  15 +
14 16 // Voir utilisation de cette propriété plus loin
15 17 public $current_entity = null;
16 18  
... ... @@ -18,6 +20,8 @@ class AppTable extends Table
18 20 // accents + - _ / () . , \s (=space)
19 21 private $chaine = "a-zA-Z0-9éèàùâêôîïôûç%().,\/\s\+\-_'";
20 22  
  23 + public function isValidRole($role) { return in_array($role, self::ROLES); }
  24 +
21 25 // Check prix > 0
22 26 public function checkPriceIsPositive($prix_ht) {
23 27 if (trim($prix_ht) != '') return is_numeric($prix_ht) && $prix_ht >= 0;
... ...
src/Model/Table/MaterielsTable.php
... ... @@ -262,9 +262,35 @@ class MaterielsTable extends AppTable
262 262 if ($lot_num < 0) return [];
263 263 $base_mandatory_fields = self::getMandatoryFieldsForLot($lot_num-1);
264 264 $new_mandatory_fields = Configure::readOrFail('MANDATORY_FIELDS_LOT'.$lot_num);
  265 + //debug($new_mandatory_fields);
265 266 return array_merge($base_mandatory_fields, $new_mandatory_fields);
266 267 }
267   -
  268 +
  269 + // (EP 2021 10)
  270 + public static function list_to_dict($uneditable_fields) {
  271 + $dict = [];
  272 + foreach ($uneditable_fields as $f) {
  273 + $except_roles = [];
  274 + $except_str = '(except';
  275 + $pos = strpos($f, $except_str);
  276 + // Si le nom du champ $f fini par "(except ...)", on récupère cette chaine except (sans le mot except ni la parenthèse finale) dans $except
  277 + if ($pos !== false) {
  278 + // EXCEPTION si pas de parenthèse finale, ou role mal orthographié
  279 + //$except_list = trim( substr($f, $pos + strlen($except_str), -1) );
  280 + if ( substr($f, -1) != ')' )
  281 + throw new \Exception("Erreur dans le fichier de configuration des champs non modifiables : il manque une parenthèse à la fin de la ligne '$f'");
  282 + $except_roles = str_replace(" ", "", substr($f, $pos + strlen($except_str), -1) );
  283 + $except_roles = explode(',', $except_roles);
  284 + foreach ($except_roles as $role) if (! self::isValidRole($role))
  285 + throw new \Exception("Erreur dans le fichier de configuration des champs non modifiables : le role '$role' est mal orthographié dans la ligne '$f'");
  286 + $f = trim( substr($f,0,$pos) );
  287 + }
  288 + $dict[$f] = $except_roles;
  289 + }
  290 + return $dict;
  291 + }
  292 +
  293 + // (EP 2021 09) fonction récursive
268 294 public static function getUneditableFieldsForLot($lot_num) {
269 295 /*
270 296 $specific_constant_name = "MANDATORY_FIELDS_LOT1_".self::getLabName();
... ... @@ -273,8 +299,12 @@ class MaterielsTable extends AppTable
273 299 */
274 300 if ($lot_num < 0) return [];
275 301 $base_uneditable_fields = self::getUneditableFieldsForLot($lot_num-1);
  302 + //debug($base_uneditable_fields);
276 303 $new_uneditable_fields = Configure::readOrFail('UNEDITABLE_FIELDS_LOT'.$lot_num);
277   - return array_unique(array_merge($base_uneditable_fields, $new_uneditable_fields));
  304 + $new_uneditable_fields = self::list_to_dict($new_uneditable_fields);
  305 + //debug($new_uneditable_fields);
  306 + //return array_unique(array_merge($base_uneditable_fields, $new_uneditable_fields));
  307 + return array_merge($base_uneditable_fields, $new_uneditable_fields);
278 308 }
279 309  
280 310 /*
... ...
src/Template/Materiels/add_edit.ctp
... ... @@ -129,7 +129,8 @@ else {
129 129 $lieu_detail_edit = $lieu_detail_edit; // [12007 => '']
130 130  
131 131 //$isReadonlyField = $isReadonlyField; // function
132   - if ($IS_ARCHIVED) $readonlyFields = array('*','status');
  132 + //if ($IS_ARCHIVED) $readonlyFields = array('*','status');
  133 + if ($IS_ARCHIVED) $readonlyFields = array('*'=>'status');
133 134 } // EDIT only
134 135 //debug($readonlyFields);
135 136  
... ... @@ -889,7 +890,7 @@ if (isset($cpMateriel)) {
889 890 echo $this->MyHelper->control($IS_ADD, $readonlyFields, 'nom_responsable', "Nom de l'Acheteur");
890 891 */
891 892 $f = 'nom_responsable';
892   - $readonly = $USER_IS_UTILISATEUR ? TRUE : FALSE;
  893 + //$readonly = $USER_IS_UTILISATEUR ? TRUE : FALSE;
893 894 echo $this->Form->control($f, [
894 895 'label' => "Nom de l'Acheteur",
895 896 //'empty' => 'Choisir une personne',
... ... @@ -900,7 +901,8 @@ if (isset($cpMateriel)) {
900 901 //'options' => $utilisateurs
901 902 //'readonly' => $IS_EDIT ? false : $readonly,
902 903 //'readonly' => $readonly,
903   - 'disabled' => $readonly || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
  904 + //'disabled' => $readonly || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
  905 + 'disabled' => $this->MyHelper->isReadonlyField($f, $readonlyFields),
904 906 ]);
905 907  
906 908 // EDIT only
... ... @@ -1135,7 +1137,7 @@ if (isset($cpMateriel)) {
1135 1137 */
1136 1138  
1137 1139 // Section READONLY sauf pour ADMIN
1138   - $READONLY = ! $USER_IS_ADMIN_OR_MORE;
  1140 + //$READONLY = ! $USER_IS_ADMIN_OR_MORE;
1139 1141 if ($IS_EDIT || ($IS_ADD && $USER_IS_ADMIN_OR_MORE)) {
1140 1142 //$DISABLED = $IS_ADD ? false : !$USER_IS_ADMIN_OR_MORE;
1141 1143 echo '<div style="border-top: 1px solid #CCC; border-bottom: 1px solid #CCC; margin-bottom: 0; background: #EEE;"><span style="font-size: 9px; color: red;">Partie administrative</span>';
... ... @@ -1170,8 +1172,9 @@ if (isset($cpMateriel)) {
1170 1172 //'label' => 'Centre(s) financier(s) / EOTP',
1171 1173 // EDIT only
1172 1174 //'disabled' => $DISABLED,
1173   - 'disabled' => $READONLY || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
1174   - 'placeholder' => 'Si plusieurs, séparer avec virgule'
  1175 + //'disabled' => $READONLY || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
  1176 + 'disabled' => $this->MyHelper->isReadonlyField($f, $readonlyFields),
  1177 + 'placeholder' => 'Si plusieurs, séparer avec virgule'
1175 1178 ]);
1176 1179  
1177 1180 // - Numéro BC
... ... @@ -1179,12 +1182,13 @@ if (isset($cpMateriel)) {
1179 1182 //echo $this->MyHelper->control($IS_ADD, $readonlyFields, 'numero_commande', 'Numéro BC');
1180 1183 $fname = 'numero_commande';
1181 1184 //$BC_DISABLED = $DISABLED;
1182   - $BC_DISABLED = $IS_ADD ? false : $this->MyHelper->isReadonlyField($fname, $readonlyFields);
  1185 + //$BC_DISABLED = $IS_ADD ? false : $this->MyHelper->isReadonlyField($fname, $readonlyFields);
1183 1186 if ($IS_EDIT && $USER_IS_RESPONSABLE_AND_SAME_GROUP_AS_MATERIEL && $materiel->materiel_administratif==0) $BC_DISABLED = False;
1184 1187 echo $this->Form->control($fname, [
1185 1188 'label' => 'Numéro BC',
1186 1189 // EDIT only
1187   - 'disabled' => $READONLY || $BC_DISABLED
  1190 + //'disabled' => $READONLY || $BC_DISABLED
  1191 + 'disabled' => $this->MyHelper->isReadonlyField($f, $readonlyFields),
1188 1192 ]);
1189 1193  
1190 1194 /* Champ désormais inutile
... ... @@ -1213,7 +1217,8 @@ if (isset($cpMateriel)) {
1213 1217 'label' => 'N° inventaire comptable/tutelles',
1214 1218 // EDIT only
1215 1219 //'disabled' => $DISABLED,
1216   - 'disabled' => $READONLY || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
  1220 + //'disabled' => $READONLY || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
  1221 + 'disabled' => $this->MyHelper->isReadonlyField($f, $readonlyFields),
1217 1222 ]);
1218 1223  
1219 1224 // Num inventaire (ancien)
... ... @@ -1223,7 +1228,8 @@ if (isset($cpMateriel)) {
1223 1228 'label' => 'Ancien N° inventaire',
1224 1229 // EDIT only
1225 1230 //'disabled' => $DISABLED,
1226   - 'disabled' => $READONLY || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
  1231 + //'disabled' => $READONLY || ( $IS_ADD ? false : $this->MyHelper->isReadonlyField($f, $readonlyFields) ),
  1232 + 'disabled' => $this->MyHelper->isReadonlyField($f, $readonlyFields),
1227 1233 ]);
1228 1234  
1229 1235 echo '</div>';
... ...
src/View/Helper/MyHelperHelper.php
... ... @@ -18,16 +18,24 @@ class MyHelperHelper extends Helper {
18 18  
19 19  
20 20 // (EP) Fonction utilisée dans la vue Materiels/add_or_edit
21   - function isReadonlyField ($fieldName, $myReadonlyFields) {
22   - // Fonctionnement inversé : TOUS readonly (*) SAUF certains champs (listés)
23   - if (!empty($myReadonlyFields) && $myReadonlyFields[0]=='*') {
24   - $modifiableFields = $myReadonlyFields;
  21 + function isReadonlyField ($fieldName, $readonlyFields) {
  22 +
  23 + // - Fonctionnement inversé : TOUS readonly (*) SAUF certains champs (listés)
  24 + //if (!empty($myReadonlyFields) && $myReadonlyFields[0]=='*') {
  25 + if (!empty($readonlyFields) && array_key_exists('*', $readonlyFields)) {
  26 + /*
  27 + $modifiableFields = $readonlyFields;
25 28 array_shift($modifiableFields);
  29 + */
  30 + $modifiableFields = $readonlyFields['*'];
26 31 return ! in_array($fieldName, $modifiableFields);
27 32 }
28   - // Fonctionnement normal :
  33 +
  34 + // - Fonctionnement normal :
29 35 //return ( !empty($materiel->$fieldName) && in_array($fieldName, $myReadonlyFields) );
30   - return ( in_array($fieldName, $myReadonlyFields) );
  36 + //return ( in_array($fieldName, $readonlyFields) );
  37 + return ( array_key_exists($fieldName, $readonlyFields) );
  38 +
31 39 }
32 40  
33 41 /*
... ...