si on modifie le (1), il faut modifier le (2) en conséquence * */ // - 1) Les champs FK utilisés dans la recherche const CONTAIN_FOR_SEARCH = ['Sites', 'Fournisseurs', 'Categories', 'Organismes', 'Projets', 'Users']; // - 2) Les champs (directs et FK) utilisés comme colonnes TRIABLES //const LIMIT = 20; const LIMIT_MAX = 1000; const SORT_LIST_FOR_SEARCH = [ // Nb éléments par page //'limit' => LIMIT, // Nb éléments en tout (on en récupère pas plus pour éviter de saturer la RAM) 'maxLimit' => self::LIMIT_MAX, 'sortWhitelist' => [ // - Champs directs 'designation', 'numero_laboratoire', // tutelle 'numero_inventaire_organisme', 'numero_commande', //'nom_responsable', 'nom_user', 'status', 'date_acquisition', 'prix_ht', //'etiquette', //'lieu_detail', // - Champs FK (HasOne only) : les champs cités ici doivent être présents dans le (1) : // Si c'était possible, il faudrait faire qqch comme : // isset(self::CONTAIN_FOR_SEARCH['Sites']) ? 'Sites.nom' : null, 'Sites.nom', 'Categories.nom', // Gestionnaire 'Users.nom', 'Fournisseurs.nom', //'Organismes.nom', //'Projets.nom', ], 'order' => [ 'numero_laboratoire' => 'desc' // ATTENTION, écrit comme ceci ca rentrait en conflit avec la colonne de tri numero_laboratoire... //'Materiels.numero_laboratoire' => 'desc' ], ]; // SORT_LIST_FOR_SEARCH //const CREATED = 1; const CREATED = 0; const TOBEORDERED = 1; const VALIDATED = 2; const TOBEARCHIVED = 3; const ARCHIVED = 4; const statuses = [ 'CREATED' => self::CREATED, 'TOBEORDERED' => self::TOBEORDERED, 'VALIDATED' => self::VALIDATED, 'TOBEARCHIVED' => self::TOBEARCHIVED, 'ARCHIVED' => self::ARCHIVED ]; const statuses_color = [ //'CREATED' => 'blue', 'CREATED' => 'orange', 'TOBEORDERED' => 'red', 'VALIDATED' => 'black', 'TOBEARCHIVED' => 'red', 'ARCHIVED' => 'blue' ]; /* const statuses_display = [ //'CREATED' => 'A commander', 'CREATED' => 'Fiche créée', 'TOBEORDERED' => 'Commandé', 'VALIDATED' => 'Livré, validé', 'TOBEARCHIVED' => 'A archiver', 'ARCHIVED' => 'Archivé' ]; */ // - ATTRIBUTS VARIABLES /* // Nom singulier affichable pour cette entité // @Override public function getNiceNameSingularLowerCase() { return "matériel"; } */ // Nom pluriel affichable pour cette entité //@Override public function getNiceNamePluralLowerCase($alias_controller_name=null) { return 'matériels'; } private $NOTARCHIVED = [ 'CREATED', 'TOBEORDERED', 'VALIDATED', 'TOBEARCHIVED' ]; // EP 08/2017 private $FIELDS = array( 'name', 'description', 'prix_ht' // ... ); // EP 08/2017 // protected $easyACL = array( const OLD_easyACL = array( /** * Default ACL for ALL (logged) users * * Les actions non mentionnées sont accessibles à tous (par défaut), * exemple 'find', 'index'... * Ces default ACL peuvent être surchargées pour un profil précis * (par exemple pour 'USER' qui est plus restreint) */ // 'ALL' => array ( 'DEFAULT' => array( // 'action' => 'condition for execution' (= 'Y', 'N', or ''), // with like "'field name' == 'value'" // !!! Used for test, DO NOT REMOVE : !!! 'action_CAS2_Y' => 'Y', 'action_CAS2_N' => 'N', // YOUR RULES : // CRUD actions : // 'index' => 'Y', // read all // 'view' => 'Y', // read one // 'add' => 'Y', // create // 'find' => 'Y', // create 'edit' => '(status == CREATED || status == VALIDATED)', // update 'delete' => '(status == CREATED)' ), // Ajoute des ACL plus spécifiques (ci-dessus) pour le profil USER qui est plus restreint // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') 'USER' => array( // !!! Used for test, DO NOT REMOVE : !!! 'action_CAS1_Y' => 'Y', 'action_CAS1_N' => 'N', 'action_CAS6_resp_Y' => 'Y', 'action_CAS6_resp_N' => 'N', // YOUR RULES : 'edit' => '&& (is_creator || is_user)', // is_owner = (nom_createur == CURRENT_USER_NAME) // except fields status, owner, etiquette, and admin data, VALIDATED (only some fields, cf $modifiableFields in View/Materiels/scaffold.form.ctp) 'delete' => '&& is_owner', /* ceci n'a aucun sens car l'action sur le modèle "Pages" s'appelle toujours "display" (et non pas tools, infos, ou printers...) 'tools' => 'N', 'infos' => 'N', 'printers' => 'N', */ 'statusCreated' => 'N', // Dé-valider (in-valider), rétrograder le statut (admin+) 'statusValidated' => 'N', // Valider (admin+) 'statusTobearchived' => 'Y', // Demander la sortie de l'inventaire 'statusArchived' => 'N', // Sortir de l'inventaire, archiver (admin+) 'execActions' => 'N' // calls updateSelectedStatus() (admin+) ), // Surcharge des ACL par défaut (ci-dessus) pour le profil RESPONSABLE qui est plus restreint // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') 'RESPONSABLE' => array( // !!! Used for test, DO NOT REMOVE : !!! 'action_CAS6_admin_Y' => 'Y', 'action_CAS6_admin_N' => 'N', // YOUR RULES : 'edit' => '&& is_resp' // is_owner = (nom_createur == CURRENT_USER_NAME) // 'delete' => 'is_resp && (status == CREATED)', // Valider les matos dont il est responsable : // is_resp = (groupe_thematique == CURRENT_USER_NAME.groupe_thematique [[ groupe_metier == CURRENT_USER_NAME.groupe_metier) // 'statusValidated' => 'N', ), // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMIN 'ADMIN' => array( // 'execActions' => 'Y', // calls updateSelectedStatus(), admin+ // 'add' => 'Y', // create // 'edit' => '(status == CREATED || status == VALIDATED)', // update // 'edit' => '=ALL', // update // 'delete' => '=ALL', // update 'statusCreated' => 'Y', // Dé-valider (in-valider), rétrograder le statut (admin+) 'statusValidated' => 'Y', // Valider (admin+) 'statusTobearchived' => 'Y', // Demander la sortie de l'inventaire 'statusArchived' => 'Y', // Sortir de l'inventaire, archiver (admin+) 'execActions' => 'Y' // calls updateSelectedStatus() (admin+) ), /* // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMINPLUS 'ADMINPLUS' => array( // 'edit' => 'Y', // update ), */ // Surcharge des ACL par défaut (ci-dessus) pour le profil SUPERADMIN 'SUPERADMIN' => array( //'statusArchived' => 'Y', // Sortir de l'inventaire, archiver (admin+) // 'execActions' => 'Y', // calls updateSelectedStatus(), admin+ // 'add' => 'Y', // create // 'edit' => 'Y', // update // 'delete' => 'Y', ) ); // $ACL /* * Toute première méthode appelée, * * AVANT authentification * */ public function initialize() { $this->myDebug("step 0A (specific): MaterielsController.initialize()"); //$this->loadComponent('RequestHandler'); parent::initialize(); // On autorise l'action add SANS authentification (unauthenticated) //$this->Auth->allow(['add']); } // @override parent // Default 'contain' protected function getMinimumListOfRelatedEntitiesToLoad() { return [ 'SurCategories', 'Categories', 'SousCategories' ]; } /* * Cette méthode est appelée pendant l’event Controller.initialize qui se produit avant chaque action du controller. * C’est un endroit pratique pour vérifier le statut d’une session ou les permissions d’un utilisateur. * Attention: La méthode beforeFilter() sera appelée pour les actions manquantes. * Retourner une réponse à partir d’une méthode beforeFilter ne va pas empêcher l’appel des autres écouteurs du même event. * Vous devez explicitement stopper l’event (cf https://book.cakephp.org/3.0/fr/controllers.html) * */ //use Cake\Event\Event; public function beforeFilter(\Cake\Event\Event $event) { $this->myDebug("step 1A (specific): MaterielsController.beforeFilter()"); parent::beforeFilter($event); // Si on veut autoriser des actions SANS connexion, suffit de décommenter cette ligne //$this->LdapAuth->allow([ 'view', 'index' ]); } /* * EP added 13/6/17 * Set some useful global variables for all (Materiel) views * Overload beforeRender() * * Cette méthode est appelée pendant l’event Controller.beforeRender qui se produit * APRES l’action du controller mais AVANT que la vue ne soit rendue. * Ce callback n’est pas souvent utilisé, mais peut-être nécessaire si vous appelez render() manuellement à la fin d’une action donnée * * Voir aussi beforeFilter() appellée AVANT l'action du controller */ public function beforeRender(\Cake\Event\Event $event) { $this->myDebug("step 4A (specific) : MaterielsController.beforeRender() - [step 3 = action() si existe]"); parent::beforeRender($event); // $this->layout = 'default'; $this->set('CREATED', self::CREATED); $this->set('TOBEORDERED', self::TOBEORDERED); $this->set('VALIDATED', self::VALIDATED); $this->set('TOBEARCHIVED', self::TOBEARCHIVED); $this->set('ARCHIVED', self::ARCHIVED); /* $CAN_EDIT = $IS_CREATED && ( $USER_IS_ADMIN_OR_MORE || $USER_IS_UTILISATEUR_AND_CREATOR_OR_OWNER || $USER_IS_RESPONSABLE_AND_SAME_GROUP || $USER_IS_RESPONSABLE_AND_CREATOR_OR_OWNER ); $this->set(compact('CAN_EDIT')); */ /* * @todo EP 08/2017 Nouvelle organisation des ACL avec $easyACL */ // !!! MOVED TO APPCONTROLLER !!! /* * $action = $this->getActionPassed(); * if (in_array($action, array('add', 'edit', 'view', 'index'))) { * $hiddenFields = $this->getHiddenFieldsForAction($action); * $this->set('hiddenFields', $hiddenFields); * if (in_array($action, array('add', 'edit'))) { * $mandatoryFields = $this->getMandatoryFieldsForAction($action); * $this->set('mandatoryFields', $mandatoryFields); * $readOnlyFields = $this->getReadOnlyFieldsForAction($action); * $this->set('readOnlyFields', $readOnlyFields); * $haveDefaultValueFields = $this->getDefaultValueFieldsForAction($action); * $this->set('haveDefaultValueFields', $haveDefaultValueFields); * } * } */ //TODO: temporaire, à MOVE dans AppController /* $action = $this->getActionPassed(); if ($action == 'view') { $CAN_EDIT = $IS_CREATED && ($USER_IS_ADMIN_OR_MORE || $USER_IS_UTILISATEUR_AND_CREATOR_OR_OWNER || $USER_IS_RESPONSABLE_AND_SAME_GROUP || $USER_IS_RESPONSABLE_AND_CREATOR_OR_OWNER); $this->set(compact('CAN_EDIT')); } */ } // beforeRender() // @Override public function getNameFieldLabel() { return 'designation'; } /* moved to AppController // Les matos peuvent-ils être commandés (bouton commander) ? // vrai par défaut (sauf si explicité dans la config) public static function hasOrderButton() { // OPTIM : si déjà lu, on relit pas if (! is_null(self::$HAS_ORDER_BUTTON)) return self::$HAS_ORDER_BUTTON; // Pas encore lu, on lit $has_order_button = Configure::read('HAS_ORDER_BUTTON'); if (is_null($has_order_button)) $has_order_button = true; self::$HAS_ORDER_BUTTON = $has_order_button; return $has_order_button; } */ /* * @Override * * Initialisation des autorisations PAR DÉFAUT (générales à tous les labos) pour les actions de ce controleur * */ protected function setAuthorizations() { //debug("GENERIC!"); /* * a) Noms et verbes à utiliser (surtout dans les notifications) pour les actions * */ $this->setActionsNounAndPastVerb([ 'statusCreated' => ['Dé-validation','dé-validé'], 'statusTobeordered' => ['COMMANDE','commandé'], 'statusValidated' => ['Validation','validé'], //TODO: spécial 'statusTobearchived' => ["Demande d'archivage","demandé l'archivage", '', "d'un "], 'statusArchived' => ['Archivage','archivé'], 'setLabelIsPlaced' => ["Positionnement d'étiquette","posé l'étiquette", 'sur un ', 'sur le '], 'printLabelRuban' => ["Impression de l'étiquette", "imprimé l'étiquette", '', 'sur le '], ]); /* * b) Actions de ce controleur qui enverront des notifications (log et/ou email) * * 'log' = logger seulement * 'mail' => envoyer un mail seulement * 'both' = faire les 2 (logger ET envoyer un mail) * */ $this->setNotificationAllowedOnActions([ // CRUD 'add' => 'both', //'edit' => 'log', //'edit' => 'mail', 'edit' => 'both', 'delete' => 'both', //'view', // status changed 'statusCreated' => 'log', 'statusTobeordered' => 'both', 'statusValidated' => 'both', 'statusTobearchived' => 'both', 'statusArchived' => 'both', // ... // autres 'printLabelRuban' => 'log', 'setLabelIsPlaced' => 'log', ]); /* $this->setNotificationAllowedOnActions([ 'add', 'edit', 'delete', //'view', 'statusCreated', 'statusTobearchived', 'statusValidated', 'statusArchived', // ... ]); */ /* * c) Règles d'accès (ACLs) * */ // - Action 'stats' (bugfix) => autorisée à tous $this->setAuthorizationsForAction('stats', 0); // - Action 'execSqlRequestForBugfix' (bugfix) => autorisé seulement à superadmin $this->setAuthorizationsForAction('execSqlRequestForBugfix (bugfix)', -1, [ 'super' => 0, ]); // - Action 'add' (ajout d'un nouveau matériel) => autorisé pour tous //$this->setAuthorizationsForAction('add', 0); $this->setAuthorizationsForAction('add (créer)', 0); //$this->setAuthorizationsForAction('view', 0); // - Action 'add' (ajout d'un nouveau matériel par copie d'un autre) $this->setAuthorizationsForAction('add_by_copy (ajout par copie)', ['CREATED',0], [ 'user' => ['CREATED',1], //'resp' => ['CREATED',0], //'resp' => 'default', //$admin = 'default', //$super = 'default' ]); /* Actions autorisées par défaut par AppController : index et view // - Action 'index' (affichage de la liste des matériels) $this->setAuthorizationsForAction('index', $default = [0,0] // = + vue spécialisée PAR statut //$user = 'default', // vue simplifiée tout statut confondu (sauf ARCHIVED) //$resp = 'default', //$admin = 'default', //$super = 'default' ); // - Action 'view' (vue détaillée d'un matériel) $this->setAuthorizationsForAction('view', $default = [0,0] //$user = 'default', //$resp = 'default', //$admin = 'default', //$super = 'default' // + champs techniques ); */ /* - Action 'edit' (modif d'un matériel) (EP) Changé depuis 2021-09 car désormais on peut TOUJOURS modifier un matériel (sauf si ARCHIVED) tant que certains champs restent obligatoires (les champs obligatoires correspondant au statut en cours du matos) */ //$this->setAuthorizationsForAction('edit (modifier)', ['CREATED',0], [ $this->setAuthorizationsForAction('edit (modifier)', ['NOT ARCHIVED',0], [ 'user' => ['CREATED||TOBEORDERED',1], 'resp' => 'user', //'resp' => -1, //'resp' => 0, //'resp' => ['CREATED',1], //$admin = 'default', //$super = 'default' // + champs techniques ]); // - Action 'delete' (suppression d'un matériel) $this->setAuthorizationsForAction('delete (supprimer)', ['CREATED',1]); // - Action 'devalidate' ou 'invalidate' (repasser le matériel au statut 'CREATED', c'est à dire le dé-valider ou l'invalider) // (VALIDATED ou TBA ou ARCHIVED) => CREATED //$this->setAuthorizationsForAction('devalidate', $this->setAuthorizationsForAction('statusCreated (invalider)', ['NOT CREATED',0], [ 'user' => -1, // PAS AUTORISÉ 'resp' => -1, //'resp' => ['NOT CREATED',1], ]); // - Action 'upgrade' (avancement du statut d'un matériel) // CREATED [=> TBO] => VALIDATED => TBA => ARCHIVED /* $this->setAuthorizationsForAction('upgrade', ['NOT ARCHIVED',0], [ // En fait, l'action fait juste passer au statut "suivant" //$default = ['PREVIOUS',0], // le matériel doit avoir le statut "précédent" du statut actuel 'user' => ['VALIDATED',1], // SEULEMENT l'action "demande d'archivage" //$resp = ['VALIDATED',1] // SEULEMENT l'action "demande d'archivage" //'resp' => 'Utilisateur' 'resp' => 'user' ]); */ // - Commande d'un materiel => TBO $this->setAuthorizationsForAction('statusTobeordered (commander)', ['CREATED',0], [ 'user' => ['CREATED',1], 'resp' => ['CREATED',1], //'resp' => -1 // interdit ]); // - Validation d'un materiel => VALIDATED $this->setAuthorizationsForAction('statusValidated (valider)', ['CREATED||TOBEORDERED',0], [ 'user' => -1, // interdit 'resp' => -1, // interdit ]); // - Demande d'archivage => TBA $this->setAuthorizationsForAction("statusTobearchived (demander l'archivage)", ['VALIDATED',0], [ //$user = ['default',1], 'user' => ['VALIDATED',1], //$resp = ['default',1] //$resp = ['VALIDATED',1] 'resp' => 'user', ]); // - Archivage => ARCHIVED $this->setAuthorizationsForAction('statusArchived (archiver)', ['TOBEARCHIVED',0], [ 'user' => -1, // interdit 'resp' => -1, // interdit ]); // Action 'printLabelRuban' (impression d'une étiquette) // On ne peut imprimer QUE si titreuse présente ET matos VALIDATED : //$this->setAuthorizationsForAction('printLabelRuban (imprimer étiquette)', ['VALIDATED && conf.hasPrinter',0] ); // On ne peut imprimer que si titreuse présente : $this->setAuthorizationsForAction('printLabelRuban (imprimer étiquette)', ['conf.hasPrinter',0] ); // Déclarer etiquette collée $this->setAuthorizationsForAction('setLabelIsPlaced (déclarer étiquette collée)', ['VALIDATED && conf.hasPrinter',0] ); $this->setAuthorizationsForAction('setLabelIsNotPlaced', ['VALIDATED && conf.hasPrinter',0] ); /* $this->setAuthorizationsForAction('setLabelIsPlaced', 0); // autorisé sans condition $this->setAuthorizationsForAction('setLabelIsNotPlaced', 0); // autorisé sans condition */ // Action 'execActions' $this->setAuthorizationsForAction('execActions', 0, [ // par défaut autorisé sans condition // sauf pour user et resp 'user' => -1, // PAS AUTORISÉ 'resp' => -1, // PAS AUTORISÉ ]); // Action 'export' $this->setAuthorizationsForAction('export', 0, [// autorisé sans condition 'user' => -1, // interdit ]); $this->setAuthorizationsForAction('getDateGarantie', 0); // autorisé sans condition /* // Action 'ficheMateriel' $this->setAuthorizationsForAction('createDocFicheMateriel', 0); // Action 'ficheMaterielPdf' // DOMPDF $this->setAuthorizationsForAction('createDocFicheMaterielPdf', 0); */ //debug($this->is_authorized_action);exit; } // setAuthorizations() /* * Méthode de définition des autorisations SPÉCIFIQUES au laboratoire IRAP * * Cette méthode surcharge la méthode générale "setAuthorizations()" ci-après. * Elle doit donc d'abord appeler la méthode générale (voir étape 1) puis surcharger ou ajouter quelques règles (voir étape 2) * * Pour faire la même chose pour un autre labo qui s'appelerait LABO (selon la valeur donnée par $this->confLabinvent->labNameShort), * il suffit de faire une COPIE de cette méthode et de la renommer "setAuthorizations_LABO" * * On doit faire ça dans CHAQUE controleur où l'on désire des adaptations * (ici, c'est seulement pour le contrôleur des Matériels, donc toutes les actions sur la table 'materiels') * * Pour vérifier que ces règles spécifiques sont bien appliquées, aller sur la page des autorisations /pages/acls * */ protected function setAuthorizations_IRAP() { $this->d("SPECIFIC IRAP!"); // 1) On appelle d'abord la méthode générale $this->setAuthorizations(); // 2) Puis on fait nos petites règles locales pour notre petit labo à nous tout seul /* * a) Noms et verbes à utiliser (surtout dans les notifications) pour les actions * */ /* $this->setActionsNounAndPastVerb([ // Changement des noms et verbes utilisés pour la notif sur l'action 'dévalider' 'statusCreated' => ['Dé-VAvalidation','dé-VAvalidé'], 'statusValidated' => ['Validation','validé'], 'statusTobearchived' => ["Demande d'archivage","demandé l'archivage", '', "d'un "], 'statusArchived' => ['Archivage','archivé'], 'setLabelIsPlaced' => ["Positionnement d'étiquette","posé l'étiquette", 'sur un ', 'sur le '], 'printLabelRuban' => ["Impression de l'étiquette", "imprimé l'étiquette", '', 'sur le '], ]); */ /* * b) Actions de ce controleur qui enverront des notifications (log et/ou email) * * 'log' = logger seulement * 'mail' => envoyer un mail seulement * 'both' = faire les 2 (logger ET envoyer un mail) * */ /* $this->setNotificationAllowedOnActions([ // CRUD 'add' => 'both', 'edit' => 'log', 'delete' => 'both', //'view', // status changed 'statusCreated' => 'log', 'statusTobearchived' => 'both', 'statusValidated' => 'both', 'statusArchived' => 'both', // ... // autres 'printLabelRuban' => 'log', 'setLabelIsPlaced' => 'log', ]); */ /* * c) Règles d'accès (ACLs) * */ /* // - Adaptation de la règle pour "ajout par copie" $this->setAuthorizationsForAction('add_by_copy', '', ['CREATED',0], [ 'user' => ['CREATED',1], 'resp' => 'default', ]); // - Adaptation de la règle pour "edit" (modif d'un matériel) $this->setAuthorizationsForAction('edit', '', ['CREATED',0], [ 'user' => ['CREATED',1], //'user' => [0,1], //'user' => 0, 'resp' => 'user', //'resp' => ['CREATED',1], //'resp' => -1 //$admin = 'default', //$super = 'default' // + champs techniques ]); */ } // Méthode de définition des autorisations SPÉCIFIQUES au laboratoire CRAL protected function setAuthorizations_CRAL() { $this->d("SPECIFIC CRAL!"); // 1) On appelle d'abord la méthode générale $this->setAuthorizations(); // 2) Puis on fait nos petites règles locales pour notre labo à nous tout seul // Voir pour exemple la méthode setAuthorizations_IRAP() ci-dessus. } // Méthode de définition des autorisations SPÉCIFIQUES au laboratoire LATMOS protected function setAuthorizations_LATMOS() { $this->d("SPECIFIC LATMOS !"); // 1) On appelle d'abord la méthode générale $this->setAuthorizations(); // 2) Puis on fait nos petites règles locales pour notre labo à nous tout seul // Voir pour exemple la méthode setAuthorizations_IRAP() ci-dessus. } // Méthode de définition des autorisations SPÉCIFIQUE au laboratoire IAS protected function setAuthorizations_IAS() { $this->d("SPECIFIC IAS !"); // 1) On appelle d'abord la méthode générale $this->setAuthorizations(); // 2) Puis on fait nos petites règles locales pour notre labo à nous tout seul // Voir pour exemple la méthode setAuthorizations_IRAP() ci-dessus. } /* * ATTENTION ! NE PAS SUPPRIMER ! * * Cette méthode est utilisée pour les tests * * Elle permet de vérifier que les autorisations "spécifiques" d'un labo sont prises en compte * * Méthode de définition des autorisations SPÉCIFIQUES au laboratoire nommé "TEST" * */ protected function setAuthorizations_TEST() { $this->d("SPECIFIC LABO 'TEST' !"); // 1) On appelle d'abord la méthode générale $this->setAuthorizations(); // 2) Puis on fait nos petites règles locales pour notre labo à nous tout seul // - Adaptation de la règle pour "edit" (modif d'un matériel) $this->setAuthorizationsForAction('edit', ['CREATED',0], [ 'user' => ['CREATED',1], 'resp' => 'user', //'resp' => ['CREATED',1], //'resp' => 0, //'resp' => -1, ]); } // TEST only public function getNextStatusFrom($status) { // On prend le statut d'indice SUIVANT $status_num = self::statuses[$status] + 1; // Si dernier, on revient au 1er (CREATED) if ($status_num==5) $status_num=0; $newstatus = array_keys(self::statuses)[$status_num]; //debug("new status is $newstatus"); return $newstatus; } public function isStatus($status) { //debug($status); return in_array($status, array_keys(self::statuses)); } /** * * //param $user * @param $user * @return boolean Give authorization for materiels * * On définit ici les actions autorisées ou pas par ce contrôleur spécifique * * CAKEPHP function * */ //public function isAuthorized($userFromSession) { //public function isAuthorized($user) { public function isAuthorized($user, // (EP) ajouté ceci (dans la fonction originale, il n'y avait QUE le paramètre $user, c'est tout) $action = null, $id=null, $role=null) { //$action = null, $id=null, $role=null, $userCname=null) { //if (parent::isAuthorized($userFromSession)) return TRUE; $this->myDebug("step 2A (specific): MaterielsController.isAuthorized(user)"); //$this->myDebug("step 2B (intermediaire): MaterielsController.isAuthorized($role, $action, $id, user, $userCname)"); $this->myDebug("step 2B (intermediaire): MaterielsController.isAuthorized($role, $action, $id, user)"); $this->myDebug("- user is:", $user); //$this->u = $this->getUserEntity(); assert($this->u != null); if ($this->u == null) throw new \Exception("pas d'utilisateur défini !!!"); //pr($this->u); if ($this->isLabinventDebugMode()) { debug($this->u); debug("user is xxx :"); debug($this->u->is_user); debug($this->u->is_resp); debug($this->u->is_admin); //debug($this->u->is_adminplus); debug($this->u->is_super); debug("user is xxx or more :"); debug($this->u->is_resp_or_more); debug($this->u->is_admin_or_more); debug("user is xxx or less :"); debug($this->u->is_resp_or_less); debug($this->u->is_admin_or_less); } // Si paramètres nuls, passer les paramètres de l'action courante if (!$action) $action = $this->a; //$id = $this->getIdPassed(); if (!$id) $id = $this->e_id; //debug("action $action, id $this->e_id"); //if (!$role) $role = $this->userRole; if (!$role) $role = $this->getUserRole($user); //if (!$user) $user = $this->userFromSession; // (EP 202005) inutile ///if (!$userCname) $userCname = $this->userCname; //$this->myDebug("isAuthorizedAction ? " . $this->OLD_isAuthorizedAction2($this, $this->userRole, $this->a, $id, $user)); ////$this->myDebug("isAuthorizedAction ? " . $this->OLD_isAuthorizedAction2($this, $this->user_role, $this->a, $id, $user)); //return $this->isAuthorizedActionForRole($role, $action, $id); // $user, $userCname if ($action=='add' && $id>0) { $action = 'add_by_copy'; } return $this->isAuthorizedActionForCurrentUser($action, $id); // $user, $userCname } // isAuthorized() public function isManageableByCurrentUser($id=null) { //if (is_null($id)) $id = $this->e_id; $id = $id ?:$this->e_id; return $this->isManageableByUser($this->u, $id); } public function isManageableByUser(User $user, $id=null) { // 3 façons de faire la meme chose : //if (is_null($id)) $id = $this->e_id; //is_null($id) && $id = $this->e_id; $id = $id ?:$this->e_id; return $this->getEntity($id)->isManageableByUser($user); } public function isDeleteableByCurrentUser($id=null) { //if (is_null($id)) $id = $this->e_id; $id = $id ?:$this->e_id; return $this->isDeleteableByUser($this->u, $id); } public function isDeleteableByUser(User $user, $id=null) { // 3 façons de faire la meme chose : //if (is_null($id)) $id = $this->e_id; //is_null($id) && $id = $this->e_id; $id = $id ?:$this->e_id; return $this->getEntity($id)->isDeleteableByUser($user); } public function isEditableOrCopiableByCurrentUser($id=null) { //if (is_null($id)) $id = $this->e_id; $id = $id ?:$this->e_id; return $this->isEditableOrCopiableByUser($this->u, $id); } public function isEditableOrCopiableByUser(User $user, $id=null) { // 3 façons de faire la meme chose : //if (is_null($id)) $id = $this->e_id; //is_null($id) && $id = $this->e_id; $id = $id ?:$this->e_id; return $this->getEntity($id)->isEditableOrCopiableByUser($user); } // True if materiel with id $id is owned by current user // Par défaut, si $id = null => id du materiel courant //public function isHis($id, $userFromSession) { //public function isHis($matos_id) { public function belongsToCurrentUser($id=null) { if (is_null($id)) $id = $this->e_id; //return ($this->isOwnedBy($id, $userFromSession['sn'][0] . ' ' . $userFromSession['givenname'][0])); //return $this->isOwnedBy($id, $this->u->nom); return $this->belongsToUser($this->u->nom, $id); } /* * @todo A déplacer dans Model/Table/TableMateriels * cf https://book.cakephp.org/3.0/fr/tutorials-and-examples/blog-auth-example/auth.html */ // True if materiel with id $id is owned by $nomCreateur //public function isOwnedBy($id, $username) public function belongsToUser($username, $id=null) { if (is_null($id)) $id = $this->e_id; //$entity = $this->getEntity($id); //return in_array($user_name, [$entity->nom_createur, $entity->nom_responsable]); //return $this->getEntity($id)->isOwnedOrDeclaredByUser($username); return $this->getEntity($id)->belongsToUser($username); /* return ($this->Materiels->exists([ 'id' => $id, 'nom_createur' => $nomCreateur ]) || $this->Materiels->exists([ 'id' => $id, 'nom_responsable' => $nomCreateur ])); */ } // Par défaut, si $id = null => id du materiel courant public function isSameGroupAsCurrentUser($id=null) { if (is_null($id)) $id = $this->e_id; $u = $this->u; return $this->isSameGroupAsUser($u->username, $id); } // Par défaut, si $id = null => id du materiel courant //public function isRespGroup($id, $loginResponsable) //public function isSameGroupAsUser($id, $loginResponsable) public function isSameGroupAsUser($userlogin, $id=null) { if (is_null($id)) $id = $this->e_id; // Get user if ($this->u && $this->u->username == $userlogin) $u = $this->u; else $u = $this->getUserByLogin($userlogin); /* $u = TableRegistry::getTableLocator()->get('Users') ->find() ->where(['username' => $userlogin]) ->first(); */ return $this->getEntity($id)->isSameGroupAsUser($u->groupes_metier_id, $u->groupes_thematique_id); /* OLD WAY // Responsable groupe métier ? $group = 'groupes_metier'; $group_fk = $group.'_id'; if ( $u[$group_fk] !== null && $u[$group_fk] != TableRegistry::getTableLocator()->get('GroupesMetiers') ->find() ->where(['nom =' => 'N/A']) ->first()['id'] ) return $this->getEntity($id)->$group_fk == $u[$group_fk]; /S return $this->Materiels->exists([ 'id' => $id, 'groupes_metier_id' => $u['groupes_metier_id'] ]); S/ // Responsable groupe thématique ? $group = 'groupes_thematique'; $group_fk = $group.'_id'; if ( $u[$group_fk] !== null && $u[$group_fk] != TableRegistry::getTableLocator()->get('GroupesThematiques') ->find() ->where(['nom =' => 'N/A']) ->first()['id'] ) return $this->getEntity($id)->$group_fk == $u[$group_fk]; /S return $this->Materiels->exists([ 'id' => $id, 'groupes_thematique_id' => $u['groupes_thematique_id'] ]); S/ // sinon, pas responsable de groupe return false; */ } // isSameGroupAsUser (ex isRespGroup) // $id = id du materiel => si null alors id du materiel courant public function hasStatus($id=null, $status) { if (is_null($id)) $id = $this->e_id; return $this->getEntity($id)->status == $status; /* return $this->Materiels->exists([ 'id' => $id, 'status' => $status ]); */ } public function isCreated($id=null) { return $this->hasStatus($id, 'CREATED'); } public function isToBeOrdered($id=null) { return $this->hasStatus($id, 'TOBEORDERED'); } public function isValidated($id=null) { return $this->hasStatus($id, 'VALIDATED'); } public function isToBeArchived($id=null) { return $this->hasStatus($id, 'TOBEARCHIVED'); } public function isArchived($id=null) { return $this->hasStatus($id, 'ARCHIVED'); } /** * Index method * * @return \Cake\Http\Response|null */ public function index() { $this->myDebug("step 3: MaterielsController.index()"); $HAS_ORDER_BUTTON = self::hasOrderButton(); $this->set(compact('HAS_ORDER_BUTTON')); //debug($this->request); $conditions = []; //$contain = []; //$contain = ['Sites', 'Fournisseurs', 'Categories', 'Organismes', 'Projets', 'Users']; $contain = MaterielsController::CONTAIN_FOR_SEARCH; // - FILTRE Statut ? (status=CREATED, status=VALIDATED, ...) // (EP 202007 OLD way pour gérer les statuts : /materiels/index/VALIDATED) /* if (isset($this->request->getAttribute('params')['pass'][0])) { $conditions = [ 'Materiels.status =' => $this->request->getAttribute('params')['pass'][0] ]; //$this->set('STATUS', $this->request->getAttribute('params')['pass'][0]); $this->set('SELECTED_STATUS', $this->request->getAttribute('params')['pass'][0]); } */ // (EP 202007 NEW way pour gérer les statuts : /materiels/index?status=VALIDATED) $SELECTED_STATUS = $this->request->getQuery('status'); if ( $SELECTED_STATUS && in_array($SELECTED_STATUS,['TOUS','CREATED','TOBEORDERED','VALIDATED','TOBEARCHIVED','ARCHIVED']) ) { if ($SELECTED_STATUS=='CREATED') $conditions[] = [ 'OR' => [ 0 => ['Materiels.status =' => $SELECTED_STATUS], 1 => ['Materiels.status =' => 'TOBEORDERED'] ] ]; else $conditions = [ 'Materiels.status =' => $SELECTED_STATUS ]; } else $SELECTED_STATUS = null; $this->set(compact('SELECTED_STATUS')); // /materiels/index?GT=2 // /materiels/index?GM=1 $GM = $this->request->getQuery('GM'); $GT = $this->request->getQuery('GT'); if ($GM !== null || $GT !== null) { //if ($GM !== null && $GM != TableRegistry::get('GroupesMetiers')->find() if ($GM !== null) /* && // $GM != 'N/A' $GM != TableRegistry::getTableLocator()->get('GroupesMetiers')->find() ->where(['nom =' => 'N/A']) ->first()['id'] ) */ $conditions = [ 'Materiels.groupes_metier_id =' => $GM, 'Materiels.status !=' => 'ARCHIVED' ]; //else if ($GT !== null && $GT != TableRegistry::get('GroupesThematiques')->find() else if ($GT !== null) /* // && $GT != 'N/A' && $GT != TableRegistry::getTableLocator()->get('GroupesThematiques')->find() ->where([ 'nom =' => 'N/A' ]) ->first()['id']) */ $conditions = [ 'Materiels.groupes_thematique_id =' => $GT, 'Materiels.status !=' => 'ARCHIVED' ]; else $conditions = [ 'Materiels.id =' => 0 ]; } // $GM !== null || $GT !== null $GMV = $this->request->getQuery('GMV'); $GTV = $this->request->getQuery('GTV'); if ($GMV || $GTV) { $group_type = $GMV ? 'metier' : 'thematique'; $group_id = $GMV ? $GMV : $GTV; $conditions = [ 'Materiels.groupes_'.$group_type.'_id =' => $group_id, 'Materiels.status =' => 'CREATED', 'Materiels.status !=' => 'ARCHIVED' ]; } //else $conditions = [ 'Materiels.id =' => 0 ]; /* if ($GMV !== null || $GTV !== null) { if ($GMV !== null) /S && $GMV != TableRegistry::getTableLocator()->get('GroupesMetiers') ->find() ->where([ 'nom =' => 'N/A' ]) ->first()['id'] ) S/ $conditions = [ 'Materiels.groupes_metier_id =' => $GMV, 'Materiels.status =' => 'CREATED', 'Materiels.status !=' => 'ARCHIVED' ]; else if ($GTV !== null) /S && $GTV != TableRegistry::getTableLocator()->get('GroupesThematiques') ->find() ->where([ 'nom =' => 'N/A' ]) ->first()['id'] ) S/ $conditions = [ 'Materiels.groupes_metier_id =' => $GTV, 'Materiels.status =' => 'CREATED', 'Materiels.status !=' => 'ARCHIVED' ]; else $conditions = [ 'Materiels.id =' => 0 ]; } */ // MES materiels seulement $MY = $this->request->getQuery('MY'); if ($MY !== null) { // if ( ! $this->USER_IS_ADMIN_AT_LEAST() ) // if (in_array($this->role, ['Utilisateur', 'Responsable'])) $conditions['Materiels.nom_responsable ='] = $this->request->getQuery('MY'); if (in_array($this->getUserRole(), [ 'Utilisateur', 'Responsable' ])) $conditions['Materiels.status !='] = 'ARCHIVED'; /* $conditions = [ 'Materiels.nom_responsable =' => $this->request->getQuery('MY'), 'Materiels.status !=' => 'ARCHIVED' ]; else $conditions = [ 'Materiels.nom_responsable =' => $this->request->getQuery('MY') ]; */ } // if (in_array($this->role, ['Utilisateur', 'Responsable']) && $condition == '') if (in_array($this->getUserRole(), [ 'Utilisateur', 'Responsable' ]) && $conditions == []) $conditions = [ 'Materiels.status !=' => 'ARCHIVED' ]; $config = $this->confLabinvent; /* * - FILTRE age (tranche d'âge) ? * * 5 = 0-5 ans * 10 = 5-10 ans * 15 = 10-15 ans * 20 = 15-20 ans * 21 = 20+ */ $age = $this->request->getQuery('age'); // Par défaut, 0-5 ans if (is_null($age)) $age = 5; // Si age==0 on ne fait rien (car on prend TOUS les materiels) if ($age>0) { $today_year = (new FrozenDate('now'))->year; // ex: si on est en 2020 : // + de 20 ans => on cherche "<2000" if ($age==21) { $year_min = 0; $year_max = $today_year - 20; // 2000 } // - 20 ans // ex: si $age = 10 et qu'on est en 2020 : else { $year_min = $today_year - $age; // 2010 $year_max = $year_min + 5; // 2015 } //debug("$year_min a $year_max"); // $year_min <= year(date_acquisition) <= $year_max //$condition['date_acquisition !='] = 'null'; // Date non nulle sinon ça plante !!! //$conditions['date_acquisition IS NOT'] = 'NULL'; // Date non nulle sinon ça plante !!! /* // Pour faire NON EXCLUSIF, faire ceci : $condition['year(date_acquisition) <='] = $year_max; // <= 2015 $condition['year(date_acquisition) >='] = $year_min; // >= 2010 */ // Pour bien faire EXCLUSIF, faire ceci : $conditions['year(date_acquisition) <='] = $year_max; // <= 2015 $conditions['year(date_acquisition) >'] = $year_min; // > 2010 } // Age inconnu (date nulle) /* elseif ($age==-1) { $conditions['date_acquisition IS'] = 'NULL'; } */ // - FILTRE Domaine ? $domaine_id = $this->request->getQuery('domaine'); // Par défaut, TOUS les domaines (id=0) if (is_null($domaine_id)) $domaine_id = 0; // Si $domain_id==0 on ne fait rien (car on prend TOUS les domaines) if ($domaine_id > 0) // OK $conditions['Materiels.sur_categorie_id'] = $domaine_id; // KO => Exception PDO ! //$conditions['sur_categorie_id'] = $domain_id; //debug($conditions); // - FILTRE Projet ? $projet_id = $this->request->getQuery('projet'); // Par défaut, TOUS les domaines (id=0) if (is_null($projet_id)) $projet_id = 0; // Si $domain_id==0 on ne fait rien (car on prend TOUS les domaines) if ($projet_id > 0) $conditions['Materiels.projet_id'] = $projet_id; // - FILTRE (pas vraiment un filtre) Nb lignes par page ? //$limit = $this->request->getQuery('aff'); $nbLinesPerPage = $this->request->getQuery('nblines'); // Par défaut, nb lignes demandées dans config if ($nbLinesPerPage===null) $nbLinesPerPage = $config['aff_par_defaut']; /* SERT À RIEN $this->paginate = [ 'limit' => $limit, 'maxLimit' => 1000, 'contain' => [ 'SurCategories', 'Categories', 'SousCategories', 'GroupesThematiques', 'GroupesMetiers', 'Organismes', 'Sites' ], 'order' => [ 'Materiels.numero_laboratoire' => 'desc' ] ]; */ /* * - FILTRE général, champ de recherche général 'searchfor' (ex 's_all_2') * * On cherche un texte entré dans le champ de recherche général * un peu "partout" dans presque tous les champs de l'entité Matériel * */ //$searchfor = $this->request->getQuery('s_all_2'); $searchfor = $this->request->getQuery('searchfor'); // Par défaut, TOUS les domaines (id=0) if (!is_null($searchfor) && $searchfor!='') { // Enlever les espaces en trop $searchfor=trim($searchfor); //debug($searchfor); /* $conditions['OR'] = [ 'Materiels.designation LIKE' => "%$searchfor%", 'Materiels.numero_laboratoire LIKE' => "%$searchfor%", ]; */ //$conditions['AND'] = $this->getFieldsConditionsForGeneralSearchOfWords($searchfor); //array_push($conditions, $this->getFieldsConditionsForGeneralSearchOfWords($searchfor) ); //$conditions = ['AND' => $conditions]; /* [ 'AND' => [ 'year(date_acquisition) <=' => (int) 2020, 'year(date_acquisition) >' => (int) 2015 ] ] */ //$conditions = []; //$conditions = [$conditions]; $conditions = array_merge($conditions, $this->getFieldsConditionsForGeneralSearchOfWords($searchfor) ); //debug($conditions); exit; //$conditions = $conditions[0]; /* $conditions = [ (int) 0 => [ 'OR' => [ 'Materiels.designation LIKE' => '%toto%', 'Materiels.numero_laboratoire LIKE' => '%toto%', 'Materiels.numero_inventaire_organisme LIKE' => '%toto%', 'Materiels.numero_inventaire_old LIKE' => '%toto%', 'Materiels.numero_commande LIKE' => '%toto%', 'Materiels.description LIKE' => '%toto%', 'Materiels.nom_responsable LIKE' => '%toto%', 'Materiels.email_responsable LIKE' => '%toto%', 'Materiels.code_comptable LIKE' => '%toto%', 'Materiels.numero_serie LIKE' => '%toto%', 'Materiels.date_acquisition LIKE' => '%toto%', 'Materiels.lieu_detail LIKE' => '%toto%', 'Fournisseurs.nom LIKE' => '%toto%', 'Categories.nom LIKE' => '%toto%' ] ] ]; */ //$contain = ['Fournisseurs', 'Categories', 'Organismes', 'Projets']; //$contain = ['Fournisseurs', 'Categories', 'Organismes', 'Projets', 'Users']; //debug($conditions); } // searchfor // EXEC REQUETE SQL find() // TOUS les materiels /* $this->set('nbMateriels', $this->Materiels->find('all', [ 'conditions' => $condition ])->count()); */ //debug($conditions); debug($contain); exit; $materiels = $this->Materiels /* ->find() ->where($conditions); */ ->find('all', [ 'conditions' => $conditions, //'contain' => ['Categories', 'Fournisseurs'] 'contain' => $contain ]); //->limit(1000); if ($age==-1) $materiels = $materiels ->where(function (QueryExpression $exp, Query $q) { return $exp->isNull('date_acquisition'); } ); // EXPORT DE LA LISTE EN COURS (selon le filtrage en cours) // Attention, l'appel à export() terminera l'action par un EXIT // Donc, cette action stoppe le processus en cours, et la vue ne sera pas réaffichée $export_current = $this->request->getQuery('exportcurrent'); if ($export_current == 1) { //debug("export current list"); $this->export($materiels); } // STOP //debug("age=$age, nbMateriels = $nbMateriels"); /* $this->set('nbMateriels', $this->Materiels //->find('all') ->find() ->where($conditions) ->count() ); */ // Seulement le nombre de materiels demandés par paginate //debug($condition); /* $materiels = $this->paginate($this->Materiels->find('all', [ 'conditions' => $conditions ])); */ # WHERE (population) IS NOT NULL /* $materiels = $this->Materiels ->find() ->where(function (QueryExpression $exp, Query $q) { return $exp->isNotNull('date_acquisition'); } ); $materiels = $this->paginate($materiels ->where($conditions) ); */ // Paginated materiels /* $materiels = $this->paginate($this->Materiels ->find() ->where($conditions) ); */ //debug($materiels->count()); //foreach ($materiels as $m) debug($m->id); $current_user = $this->getUserEntity(); //debug($current_user->nom); //debug($current_user->site_id); //debug($materiels); exit; if ($this->confLabinvent->labNameShort) { $labshortname = $this->confLabinvent->labNameShort; if ($labshortname != 'IRAP') $materiels = $materiels->find('filteredForUserSite', ['user_site_id' => $current_user->site_id]); } //$materiels = $materiels->find('filteredForUserSite', ['user_site_id' => null]); //foreach ($materiels as $m) debug($m->id); //debug($materiels->count()); try { $nbMateriels = $materiels->count(); } catch (\PDOException $e) { debug("Mauvais format de requete SQL (1), Exception PDO générée !"); exit; } $this->set(compact('nbMateriels')); try { $materiels = $this->paginateResults($materiels, $nbLinesPerPage); } catch (\PDOException $e) { debug("Mauvais format de requete SQL (0), Exception PDO générée !"); exit; } /* try { //$materiels = $this->paginate($materiels); $materiels = $this->paginate($materiels, [ 'limit' => $nbLinesPerPage, 'maxLimit' => 1000, 'sortWhitelist' => [ // - Champs directs 'designation', 'numero_laboratoire', // tutelle 'numero_inventaire_organisme', 'numero_commande', //'nom_responsable', 'nom_user', 'status', 'date_acquisition', 'prix_ht', //'etiquette', //'lieu_detail', // - Champs FK (HasOne only) 'Sites.nom', 'Categories.nom', //'Organismes.nom', // Gestionnaire 'Users.nom', 'Fournisseurs.nom', ], 'order' => [ 'numero_laboratoire' => 'desc' // ATTENTION, écrit comme ceci ca rentrait en conflit avec la colonne de tri numero_laboratoire... : //'Materiels.numero_laboratoire' => 'desc' ], ]); } catch (\PDOException $e) { debug("Mauvais format de requete SQL (2), Exception PDO générée !"); exit; } */ $this->set('statuses_color', self::statuses_color); $this->set(compact('materiels')); // Liste des DOMAINES //$domain_options = $this->Materiels->SurCategories->find()->toArray(); $domaine_options = $this->Materiels->SurCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', ])->toArray(); // Ajoute l'élément 0 => 'TOUS' en tête de tableau //$domain_options[0] = 'TOUS'; //debug($domain_options); array_unshift($domaine_options, "Tous"); //debug($domain_options); // Liste des PROJETS $projet_options = $this->Materiels->Projets->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', // implicite, pourquoi ??? //'order' => 'Projets.nom' ])->toArray(); //debug($projet_options); /* Ajoute l'élément 0 => 'TOUS' en tête de tableau * SANS changer les clés !!! */ // 1) on ajoute un élément vide à la clé "0", // et on trie le tableau sur les values (nom du projet) (en conservant les clés) // => l'élément vide se retrouve donc en tête $projet_options[0] = ''; asort($projet_options); // 2) on remplace l'élément vide (en tête) par 'Tous', qui est donc toujours en tête ! $projet_options[0] = 'Tous'; //debug($projet_options); // Liste des AGES (intervalles d'années) $age_options = [ '0' => 'Tous', '5' => 'Récents (5 ans max)', '10' => '5-10 ans', '15' => '10-15 ans', '20' => '15-20 ans', //'20plus' => '+ 20 ans', '21' => '+ 20 ans', //'unknown' => 'inconnu', '-1' => 'inconnu', ]; // Liste des NBLINES (Nb de matériels affichés par page) $nblines_options = [ '20' => 20, '30' => 30, '50' => 50, '100' => 100, '150' => 150, '200' => 200 ]; $this->set(compact( 'age_options', 'nblines_options', 'domaine_options', 'projet_options', 'searchfor' )); // Pas bien..., mais pratique : // on passe le controleur de materiels à la vue index pour qu'elle // puisse appeler la methode isAuthorizedAction() $this->set("controller", $this); /* (EP) inutile $this->set('_serialize', [ 'materiels' ]); */ //debug($conditions); } // index() //@Override // Pour le MaterielsController, le materiel associé est simplement lui-même :-) protected function getRelatedMaterielForId($id, $action=null) { return $this->getEntity($id); } /** * View method * * @param string|null $id * Materiel id. * @return \Cake\Http\Response|null * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function view($id = null) { $this->myDebug("step 3: MaterielsController.view()"); /* debug($this->getEntity($id)); debug("matos is status xxx:"); debug($this->getEntity($id)->is_created); debug($this->getEntity($id)->is_validated); */ $materiel = $this->Materiels->get($id, [ 'contain' => [ // 1) HasOne 'SurCategories', 'Categories', 'SousCategories', 'GroupesThematiques', 'GroupesMetiers', 'Projets', 'Organismes', 'Sites', 'Fournisseurs', //'Gestionnaires', 'Users', // 2) HasMany 'Documents', 'Documents.TypeDocuments', 'Emprunts', // Tous les suivis de ce $materiel (sans leur type) //'Suivis', // Tous les suivis de ce $materiel AVEC leur type de suivi 'Suivis.TypeSuivis', ] ]); $this->e = $materiel; $e = $materiel; //debug($materiel); // current user $u = $this->u; //debug("(start) user is:"); debug($u); exit; //debug($u->canManageMatos($e)); /* * (EP) TODO: * * Selon le role (profil) actif, positionner ces tableaux (listes de champs) * pour dire à quels champs ce role à accès * et dans quelles conditions (lecture seule, obligatoire, ...) * * $hiddenFields = [ fields list ] * $readonlyFields = [ fields list ] * $mandatoryFields = [ fields list ] * $defaultValueForFields = [ field1 => value1, field2 => value2, ..., fieldN => valueN ] * * Passer ensuite ces listes à la vue avec set() * */ $HAS_ORDER_BUTTON = self::hasOrderButton(); $this->set(compact('HAS_ORDER_BUTTON')); /* $IS_CREATED = ($materiel->status == 'CREATED'); $IS_VALIDATED = ($materiel->status == 'VALIDATED'); $IS_TOBEARCHIVED = ($materiel->status == 'TOBEARCHIVED'); $IS_ARCHIVED = ($materiel->status == 'ARCHIVED'); */ $IS_CREATED = $e->is_created; $IS_TOBEORDERED = $e->is_tobeordered; $IS_VALIDATED = $e->is_validated; $IS_TOBEARCHIVED = $e->is_tobearchived; $IS_ARCHIVED = $e->is_archived; $this->set(compact('IS_CREATED', 'IS_TOBEORDERED', 'IS_VALIDATED', 'IS_TOBEARCHIVED', 'IS_ARCHIVED')); // 1) DROITS SUR LE MATERIEL VU // - edit, copy, delete, etiquette $CAN_EDIT = $this->isAuthorizedActionForCurrentUser('edit', $id); //$CAN_EDIT = $this->isAuthorizedAction($this->userRole, 'edit', $id, $this->userFromSession, $this->userCname); $this->d("can edit ? "); $this->d2($CAN_EDIT); //$CAN_EDIT = $this->isAuthorized($u,'edit', $id); //pr("CAN_EDIT = ".(int)$CAN_EDIT." (pour id $id)"); //$CAN_EDIT = $IS_CREATED && $CAN_ATTACH_A_DOC; $CAN_COPY = $this->isAuthorizedActionForCurrentUser('add_by_copy', $id); $this->d("can copy ? "); $this->d2($CAN_COPY); //$CAN_COPY = $CAN_EDIT; //$CAN_DELETE = $this->isAuthorized($u,'delete', $id); $CAN_DELETE = $this->isAuthorizedActionForCurrentUser('delete', $id); $this->d("can delete ? "); $this->d2($CAN_DELETE); //debug("can delete ? $CAN_DELETE"); //debug($CAN_DELETE); $CAN_PRINT_LABEL = $this->isAuthorizedActionForCurrentUser('printLabelRuban', $id); //$CAN_PRINT_LABEL = $this->isAuthorized($u,'printLabelRuban'); //$CAN_PRINT_LABEL = $IS_VALIDATED && $this->confLabinvent->hasPrinter && $this->USER_IS_ADMIN_OR_MORE; //$CAN_PRINT_LABEL = $this->isAuthorizedAction($this->userRole, 'printLabelRuban', $id, $this->userFromSession, $this->userCname); $this->d("can print ? "); $this->d2($CAN_PRINT_LABEL); $this->set(compact('CAN_EDIT', 'CAN_DELETE', 'CAN_COPY', 'CAN_PRINT_LABEL')); // - commander, valider, devalider, tba, archiver $CAN_ORDER = $this->isAuthorizedActionForCurrentUser('statusTobeordered', $id); /* $CAN_VALIDATE_OR_INVALIDATE = $this->USER_IS_ADMIN_OR_MORE || ( $materiel->materiel_administratif==0 && $USER_IS_RESPONSABLE_AND_SAME_GROUP_AS_MATERIEL ); $CAN_VALIDATE = $IS_CREATED && $CAN_VALIDATE_OR_INVALIDATE; */ $CAN_VALIDATE = $this->isAuthorizedActionForCurrentUser('statusValidated', $id); //$CAN_VALIDATE = $this->isAuthorized($u,'statusValidated'); //$CAN_VALIDATE = $this->isAuthorizedAction('statusValidated'); $this->d("can validate ? "); $this->d2($CAN_VALIDATE); $CAN_INVALIDATE = $this->isAuthorizedActionForCurrentUser('statusCreated', $id); //$CAN_INVALIDATE = $this->isAuthorized($u,'statusCreated'); //$CAN_INVALIDATE = !$IS_CREATED && $CAN_VALIDATE_OR_INVALIDATE; $this->d("can invalidate ? "); $this->d2($CAN_INVALIDATE); $CAN_TBA = $this->isAuthorizedActionForCurrentUser('statusTobearchived', $id); //$CAN_TBA = $this->isAuthorized($u,'statusTobearchived'); //$CAN_TBA = $IS_VALIDATED && $CONTEXT1; $this->d("can CAN_TBA ? "); $this->d2($CAN_TBA); $CAN_ARCHIVE = $this->isAuthorizedActionForCurrentUser('statusArchived', $id); //$CAN_ARCHIVE = $this->isAuthorized($u,'statusArchived'); //$CAN_ARCHIVE = $IS_TOBEARCHIVED && $this->USER_IS_ADMIN_OR_MORE; $this->d("can CAN_ARCHIVE ? "); $this->d2($CAN_ARCHIVE); $this->set(compact( 'CAN_ORDER', 'CAN_VALIDATE', 'CAN_INVALIDATE', 'CAN_TBA', 'CAN_ARCHIVE' )); // - Emprunter et Suivre //$CAN_LEND = $CAN_FOLLOW = true; $ec = new EmpruntsController(); //$CAN_LEND = $ec->isAuthorizedAction('add', $id, $IS_RELATED_ENTITY_ID=true, $u); $CAN_LEND = $ec->isAuthorizedActionForCurrentUser('add', null, $id, $u); $this->d("can CAN_LEND ? "); $this->d2($CAN_LEND); $sc = new SuivisController(); //$CAN_DO_SUIVI = $sc->isAuthorizedAction('add', $id, true, $u); $CAN_DO_SUIVI = $sc->isAuthorizedActionForCurrentUser('add', null, $id, $u); $this->d("can CAN_DO_SUIVI ? "); $this->d2($CAN_DO_SUIVI); // - Attacher un doc $dc = new DocumentsController(); //$CAN_ATTACH_A_DOC = $dc->isAuthorizedAction('add', $id, true, $u); $CAN_ATTACH_A_DOC = $dc->isAuthorizedActionForCurrentUser('add', null, $id, $u); $this->d("CAN_ATTACH_A_DOC ? "); $this->d2($CAN_ATTACH_A_DOC); //$CAN_ATTACH_A_DOC = DocumentsController::isAuthorizedAction('add'); //$CAN_ATTACH_A_DOC = $CONTEXT1; // - Editer le doc d'admission //$CAN_EDIT_DOC_ADMISSION = $dc->isAuthorizedAction('admission', $id, true, $u); $CAN_EDIT_DOC_ADMISSION = $dc->isAuthorizedActionForCurrentUser('admission', null, $id, $u); $this->d("CAN_EDIT_DOC_ADMISSION ? "); $this->d2($CAN_EDIT_DOC_ADMISSION); //$CAN_EDIT_DOC_SORTIE = $dc->isAuthorizedAction('sortie', $id, true, $u); $CAN_EDIT_DOC_SORTIE = $dc->isAuthorizedActionForCurrentUser('sortie', null, $id, $u); $this->d("CAN_EDIT_DOC_SORTIE ? "); $this->d2($CAN_EDIT_DOC_SORTIE); //$CAN_LEND = $CAN_DO_SUIVI = $CAN_ATTACH_A_DOC = $CAN_EDIT_DOC_ADMISSION = $CAN_EDIT_DOC_SORTIE = TRUE; $this->set(compact( 'CAN_LEND', 'CAN_DO_SUIVI', 'CAN_ATTACH_A_DOC', 'CAN_EDIT_DOC_ADMISSION', 'CAN_EDIT_DOC_SORTIE' )); // Current user is creator or owner of current materiel /* $USER_IS_CREATOR_OR_OWNER = in_array($this->userName, [ $materiel->nom_createur, $materiel->nom_responsable ]); */ //$USER_IS_CREATOR_OR_OWNER = $e->isOwnedOrDeclaredByUser($this->userName); /* $USER_IS_CREATOR_OR_OWNER = $e->belongsToUser($this->userName); $this->set(compact('USER_IS_CREATOR_OR_OWNER')); */ // Current user is same group as current materiel /* //$USER_IS_SAME_GROUP_AS_MATERIEL = $e->isSameGroupAsUser($user_group_metier_id, $user_group_thematique_id); $USER_IS_SAME_GROUP_AS_MATERIEL = ( //( isset($this->priviledgedUser->groupes_metier_id) && $this->priviledgedUser->groupes_metier_id != $this->idGmNa && $materiel->groupes_metier_id == $this->priviledgedUser->groupes_metier_id ) ( isset($this->u->groupes_metier_id) && $this->u->groupes_metier_id != $this->idGmNa && $materiel->groupes_metier_id == $this->u->groupes_metier_id ) || ( isset($this->u->groupes_thematique_id) && $this->u->groupes_thematique_id != $this->idGtNa && $materiel->groupes_thematique_id == $this->u->groupes_thematique_id ) ); */ //$USER_IS_SAME_GROUP_AS_MATERIEL = $this->currentMaterielIsSameGroupAsCurrentUser(); /* $USER_IS_SAME_GROUP_AS_MATERIEL = $this->isSameGroupAsCurrentUser(); $this->set(compact('USER_IS_SAME_GROUP_AS_MATERIEL')); $USER_IS_RESPONSABLE_AND_SAME_GROUP_AS_MATERIEL = $this->USER_IS_RESPONSABLE && $USER_IS_SAME_GROUP_AS_MATERIEL; $this->set(compact('USER_IS_RESPONSABLE_AND_SAME_GROUP_AS_MATERIEL')); $CONTEXT1 = $this->USER_IS_ADMIN_OR_MORE || $USER_IS_CREATOR_OR_OWNER || $USER_IS_RESPONSABLE_AND_SAME_GROUP_AS_MATERIEL; */ // (EP 20200515) // - 2) DROITS SUR LES ENTITÉS LIÉES : suivis, emprunts, documents attachés /* - Suivis liés * (EP 20200514) * Avoir le droit d'éditer ou supprimer un suivi est lié seulement au matériel concerné * On va demander ce droit au controleur des Suivis avec l'id du 1er suivi de la liste * Le droit sera le même pour tous les autres suivis de cette liste */ // Par défaut => false (pas le droit) $CAN_MANAGE_SUIVIS = false; //$sc = new SuivisController(); //debug("EDIT de sc:"); debug($sc->is_authorized_action['edit']); //debug("EDIT de dc:"); debug($dc->is_authorized_action['edit']); //debug("EDIT de ec:"); debug($ec->is_authorized_action['edit']); if ($e->suivis) { $suivi1 = $e->suivis[0]; //$CAN_MANAGE_SUIVIS = $sc->isAuthorizedAction('edit', $suivi1->id, false, $u); $CAN_MANAGE_SUIVIS = $sc->isAuthorizedActionForCurrentUser('edit', $suivi1->id, null, $u); // on peut faire de même avec l'action delete //$CAN_MANAGE_SUIVIS = $sc->isAuthorized($u,'delete', $suivi1->id); } $this->d("CAN_MANAGE_SUIVIS ? "); $this->d2($CAN_MANAGE_SUIVIS); $CAN_EDIT_OR_DELETE_SUIVIS = $CAN_MANAGE_SUIVIS; //$CAN_MANAGE_SUIVIS = $CONTEXT1; //$CAN_MANAGE_SUIVIS = $sc->isAuthorized($u,'edit') && $sc->isAuthorized($u,'delete'); /* - PRETS liés * (EP 20200515) * Avoir le droit d'éditer ou supprimer un PRET est lié seulement au matériel concerné * On va demander ce droit au controleur des Emprunts avec l'id du 1er emprunt de la liste * Le droit sera le même pour tous les autres emprunts de cette liste */ //$CAN_EDIT_OR_DELETE_EMPRUNTS = $CAN_MANAGE_EMPRUNTS = $CAN_EDIT; // Par défaut => false (pas le droit) $CAN_MANAGE_EMPRUNTS = false; if ($e->emprunts) { $emprunt1 = $e->emprunts[0]; //debug($emprunt1); //$CAN_MANAGE_EMPRUNTS = $ec->isAuthorizedAction('edit', $emprunt1->id, false, $u); $CAN_MANAGE_EMPRUNTS = $ec->isAuthorizedActionForCurrentUser('edit', $emprunt1->id, null, $u); // on peut faire de même avec l'action delete //$CAN_MANAGE_EMPRUNTS = $sc->isAuthorized($u,'delete', $suivi1->id); } $this->d("CAN_MANAGE_EMPRUNTS ? "); $this->d2($CAN_MANAGE_EMPRUNTS); $CAN_EDIT_OR_DELETE_EMPRUNTS = $CAN_MANAGE_EMPRUNTS; //$CAN_MANAGE_EMPRUNTS = $CONTEXT1; //$CAN_MANAGE_EMPRUNTS = $ec->isAuthorized($u,'edit') && $ec->isAuthorized($u,'delete'); //$CAN_EDIT_OR_DELETE_EMPRUNTS = $CAN_MANAGE_EMPRUNTS; // - docs liés //$CAN_MANAGE_FILES = $CAN_EDIT; // edit, delete, ou envoi devis par mail //$CAN_MANAGE_FILES = $CONTEXT1; // edit, delete, ou envoi devis par mail //$CAN_MANAGE_FILES = $dc->isAuthorized($u,'edit') && $dc->isAuthorized($u,'delete') && $dc->isAuthorized($u,'mailDevis'); $CAN_MANAGE_FILES = false; if ($e->documents) { $e1 = $e->documents[0]; //debug($emprunt1); //$CAN_MANAGE_EMPRUNTS = $ec->isAuthorizedAction('edit', $emprunt1->id, false, $u); $CAN_MANAGE_FILES = $dc->isAuthorizedActionForCurrentUser('edit', $e1->id, null, $u); // on peut faire de même avec l'action delete //$CAN_MANAGE_EMPRUNTS = $sc->isAuthorized($u,'delete', $suivi1->id); } $this->d("CAN_MANAGE_FILES ? "); $this->d2($CAN_MANAGE_FILES); $CAN_EDIT_OR_DELETE_OR_SENDBYMAIL_FILES = $CAN_MANAGE_FILES; $this->set(compact('CAN_MANAGE_SUIVIS', 'CAN_MANAGE_EMPRUNTS', 'CAN_MANAGE_FILES')); // $status = $this->statuses[$materiel->status]; $status = self::statuses[$materiel->status]; $this->set(compact('status')); $sites = TableRegistry::getTableLocator()->get('Sites'); //$sites = TableLocator::get('Sites'); //$typeSuivis = TableRegistry::getTableLocator()->get('TypeSuivis'); /* $typeSuivisAll = $typeSuivis->find('list', [ 'keyField' => 'id', 'valueField' => 'is_regular', ])->toArray(); */ $typeDocuments = TableRegistry::getTableLocator()->get('TypeDocuments'); //$fournisseurs = TableRegistry::getTableLocator()->get('Fournisseurs'); //$fournisseurs = TableLocator::get('Fournisseurs'); if ($materiel->photo_id != null) { $imgMateriel = $materiel->photo_id . '.' . TableRegistry::getTableLocator()->get('Documents')->get($materiel->photo_id)->get('type_doc'); $this->set('imgMateriel', $imgMateriel); } $this->set('PDF_ENGINE', $this->confLabinvent->pdf_engine); $entity = $materiel; $status_color = self::statuses_color[$entity->status]; //$status_display = self::statuses_display[$entity->status]; $this->set(compact('entity')); $this->set(compact( 'status_color', //'status_display', 'sites', 'typeDocuments' //'materiel', // @deprecated //'fournisseurs' )); /* (EP) inutile $this->set('_serialize', [ 'materiel' ]); */ /* TODO: $CAN_EDIT = $IS_CREATED && ( $USER_IS_ADMIN_OR_MORE || $USER_IS_UTILISATEUR_AND_CREATOR_OR_OWNER || $USER_IS_RESPONSABLE_AND_SAME_GROUP || $USER_IS_RESPONSABLE_AND_CREATOR_OR_OWNER ); $this->set(compact('CAN_EDIT')); */ //debug("1"); debug($_SESSION); // (EP202009) moved to parent.afterFilter() //$emails = $this->sendNotificationForEntityAction($materiel); //debug($emails); /* * Pour le statut courant du matériel ("créé", "en commande", "validé", "archivé"...), * si il est recommandé (dans la config des champs matériel) de remplir certains champs ou de joindre certains documents (ou de coller l'étiquette)..., * on affiche un message de rappel (flash) en-tête de la fiche détaillée du matériel */ /* $LABEL_NOT_PRINTED = false; $NUM_INV_TUTELLES_NOT_FILLED = false; if ($entity->is_validated) { //debug($this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status)); //debug(array_key_exists('etiquette', $this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status))); if ($CAN_PRINT_LABEL && array_key_exists('etiquette', $this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status))) $LABEL_NOT_PRINTED = ! $entity->etiquette; if (array_key_exists('numero_inventaire_organisme', $this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status))) $NUM_INV_TUTELLES_NOT_FILLED = empty($entity->numero_inventaire_organisme); if ($LABEL_NOT_PRINTED) $this->Flash->set("Cette fiche est validée mais n'oubliez pas d'imprimer l'étiquette associée et de la coller sur le matériel"); if ($NUM_INV_TUTELLES_NOT_FILLED) $this->Flash->set("Cette fiche est validée mais vous n'avez pas encore rempli le champ 'N° inventaire comptable/tutelles'"); } */ $recommended_fields = $this->Materiels->getRecommendedFieldsForMaterielStatus($materiel->status); //debug($recommended_fields); /* * Ex : [ 'etiquette' => [ 'selected' => '1', 'comment' => 'd'imprimer l'étiquette associée et de la coller sur le matériel', 'roles' => '' ], 'numero_inventaire_organisme' => [ 'selected' => '1', 'comment' => 'de renseigner le champ 'N° inventaire comptable/tutelles _Organisme_'', 'roles' => '' ], 'DOC_BC' => [ 'selected' => '1', 'comment' => 'd'ajouter le Bon de Commande', 'roles' => '' ], 'DOC_BL' => [ 'selected' => '1', 'comment' => 'd'ajouter le Bon de Livraison', 'roles' => '' ], 'DOC_FACTURE' => [ 'selected' => '1', 'comment' => 'd'ajouter la Facture', 'roles' => '' ] */ //foreach ($recommended_fields as $fname => $msg) { //foreach ($recommended_fields as $fname => $attributes) { foreach ( array_keys($recommended_fields) as $fname ) { //debug($fname); // - Documents attachés (champs virtuels) // DOC_DEVIS, DOC_BC, ... if ( substr($fname,0,4) == 'DOC_' ) { if ($entity->hasAttachedDocOfType($fname)) unset($recommended_fields[$fname]); } // - Champs réels else { // Champ spécial "etiquette" => boolean if ($fname == 'etiquette' && $CAN_PRINT_LABEL) { if ($entity->$fname) unset($recommended_fields[$fname]); } // Tous les autres champs réels else { if (!empty($entity->$fname)) unset($recommended_fields[$fname]); } } } // Les champs restants sont les champs manquants => message flash de rappel foreach ($recommended_fields as $fname => $attributes) $this->Flash->set("Ce matériel est '".$entity->getNiceStatus()."' mais n'oubliez pas ".$attributes['comment']); } // view public function execSqlRequestForBugfix() { debug("START..."); //$this->execSqlRequestForBugfix_fournisseurs(); debug("...STOP"); exit; /* return $this->redirect([ 'controller' => 'pages', 'action' => 'tools', ]); */ } /** * Add or Edit method (do either add() or edit()) * => Factorisation de add() et edit() * (voir aussi https://book.cakephp.org/3.0/en/orm.html) * * Les autorisations du niveau 2 (VUE) pour cette action sont définies dans la documentation technique au chapître "Autorisations" * (https://docs.google.com/document/d/1-OhEeoi96j6ueUl5NQCQ9ZsTfbJTFw3ZVaWU2iYly_o/edit?pli=1#heading=h.uqfpcjutghc7) * * @param $IS_ADD: True = add ; False = edit * @return \Cake\Http\Response|void Redirects on successful add/edit, renders view otherwise. */ //protected function add_or_edit($IS_ADD, $id=null, $valeurs=null, $erreurs=null, protected function add_or_edit($IS_ADD, $id=null, $erreurs=null, // uniquement à cause de parent::add_or_edit() : $entity_name=null, array $associated_entities=[], $with_parent=false) { $this->myDebug("step 3: MaterielsController.add_or_edit()"); $IS_EDIT = !$IS_ADD; //debug($this->Materiels->MANDATORY_FIELDS); /* * (EP 20201207) * * La plupart des Autorisations de niveau 2 (c'est à dire liées à la VUE materiels add_edit) sont définies ici. * * Voir le tableau correspondant dans la doc techique : * https://docs.google.com/document/d/1-OhEeoi96j6ueUl5NQCQ9ZsTfbJTFw3ZVaWU2iYly_o/edit?pli=1#heading=h.uqfpcjutghc7 * * Il existe 5 types d'Autorisation de niveau Vue (AV) pour chaque attribut de l'entité (Entity Materiel) : * - \AV1. Obligatoire (défini dans classe Table pour LOT1, et ici pour LOT2) : cet attribut est-il obligatoire ? * - \AV2. Options (défini ici) : a-t-il un domaine de définition précis, c’est à dire un ensemble de valeurs possibles que l’on peut lister ? * - \AV3. Défaut (défini ici, ADD only) : a-t-il une valeur par défaut ? * - \AV4. Lecture seule (R, EDIT only) ou Caché (C) (défini ici) : est-il en lecture seule ? * - \AV5. Contrainte (défini dans classe Table) : existe-t-il sur lui une (ou des) contrainte d’intégrité ou une règle métier à respecter (ex: la date de livraison doit être supérieure à la date d’achat) ? * * Ces autorisations sont définies ci-dessous, précédées de leur type (\AV1, \AV2, ...) */ $usersTable = TableRegistry::getTableLocator()->get('Users'); // Set $materiel : soit un matériel vide (ADD new), soit un matériel de la BD à modifier (EDIT et ADD par copie) //$materiel = ($IS_ADD && !$id) ? $this->Materiels->newEntity() : $this->Materiels->get($id, ['contain' => []]); //debug($this->e); debug($this->getEntity($id)); exit; //$materiel = ($IS_ADD && !$id) ? $this->Materiels->newEntity() : $this->e; //$materiel = ($IS_ADD && !$id) ? $this->Materiels->newEntity() : $this->getEntity($id); $materiel = $IS_ADD ? $this->Materiels->newEntity() : $this->getEntity($id); if ($IS_ADD) { // ADD normal if (! $id) { // \AV3 : Valeurs par Défaut // matos permanent $materiel->will_stay = true; // l'Acheteur et l'Utilisateur du matériel c'est l'utilisateur courant (EDITEUR) $materiel->nom_responsable = $this->u->nom; $materiel->nom_user = $this->u->nom; // Si l'utilisateur courant (EDITEUR) est un gestionnaire : // - Gestionnaire de référence = EDITEUR (sinon, 'Je ne sais pas qui choisir') // - Date achat = today //$materiel->gestionnaire_id = null; $gestionnaires = $usersTable ->find('list', [ //'keyField' => 'nom', 'keyField' => 'id', 'valueField' => 'nom' ]) ->where([ 'role =' => 'Administration' ]) ->toArray(); if ( in_array($this->u->id, $gestionnaires) ) { $materiel->gestionnaire_id = $this->u->id; //$materiel->gestionnaire_id = $_SESSION['Auth']['User']['sn'][0]; $materiel->date_acquisition = date("d/m/Y"); } // Etiquette : pas imprimée $materiel->etiquette = false; // statut = CREATED $materiel->status = 'CREATED'; // Materiel à commander ? NON //$materiel->tobeordered = false; // Unité garantie = Ans $materiel->unite_duree_garantie = 'Ans'; // HS ? NON $materiel->hors_service = false; } // ADD par copie : mettre id et numero labo à null else { //if ($id) { // IMPORTANT: validate=False car sinon, les données sont validées avant la copie, // et le numero_laboratoire est vu comme invalide car déjà utilisé et doit etre unique !!! // et on a pour résultat : "le matériel n'a pas pu être ajouté" (sans savoir pourquoi !!!) //$materiel = $this->Materiels->newEntity($this->getEntity($id)->toArray(), ['validate' => false]); //$materiel = $this->Materiels->patchEntity($materiel, $this->getEntity($id, false, false)->toArray(), [ $materiel = $this->Materiels->patchEntity($materiel, $this->Materiels->get($id, ['contain' => []])->toArray(), [ 'validate' => false, // Ne pas valider non plus les entités associées /* 'associated' => [ 'SurCategories' => ['validate' => false], 'Categories' => ['validate' => false], 'SousCategories' => ['validate' => false], ] */ ]); // IMPORTANT: on ne doit pas laisser l'id égal à celui du matériel copié !!! il en faut un nouveau $materiel->id = null; // on supprime le champ numero_laboratoire car il va être généré automatiquement //unset($materiel->numero_laboratoire); $materiel->numero_laboratoire = null; } } /* * SI POST... * * Les données ont été saisies et postées * On va donc les sauvegarder (si ok) */ //if ( $this->request->is(['post','patch','put']) ) { $authorized_actions = $IS_ADD ? ['post'] : ['post','patch','put']; if ( $this->request->is($authorized_actions) ) { //debug($this->request->getData()); exit; // (1) On remplit $materiel avec les données de ce materiel $materiel = $this->Materiels->patchEntity($materiel, $this->request->getData()); //debug($materiel); /* * \AV1 : Attributs OBLIGATOIRES * On définit les infos obligatoires et on vérifie qu'elles sont bien présentes dans le POST */ //$mandatory_fields = MaterielsTable::getMandatoryFieldsForMaterielStatus($materiel->status); $mandatory_fields = $this->Materiels->getMandatoryFieldsForMateriel($materiel); //debug($mandatory_fields); //$mandatory_fields = $this->Materiels->getMandatoryFieldsForMaterielStatus($materiel->status); $verb = $IS_ADD ? "ajouté" : "modifié"; // On vérifie que les infos obligatoires sont présentes // Si au moins un champ obligatoire est nul ou vide => ERROR $ALL_MANDATORY_FIELDS_GIVEN = true; //foreach ($mandatory_fields as $fname => $fval) { //print_r($materiel); //foreach ($mandatory_fields as $fname=>$fname_nice) { foreach ($mandatory_fields as $fname=>$attributes) { /* * Champs obligatoires à ignorer : * - champs virtuels (n'existent pas physiquement) => DOC_XXX (DOC_DEVIS, DOC_BC, ...) * - champs spéciaux dont le caractère obligatoire est géré indirectement (via beforeSave()) => fournisseur * ... */ if ( substr($fname,0,4) == 'DOC_' ) continue; if ( in_array($fname, ['fournisseur_id']) ) continue; //if ( in_array($fname, ['DOC_DEVIS', 'fournisseur_id', 'etiquette', 'numero_inventaire_organisme']) ) continue; if ($materiel->$fname === null || $materiel->$fname == '') { $ALL_MANDATORY_FIELDS_GIVEN = false; /* (EP 2020 03) * Ce genre de ligne ($this->Flash->...) affichant un message flash en haut de page, * ne fonctionnait plus à cause de bootstrap (css). * Ca fonctionne uniquement parce qu'on a surchargé la règle "fade" * de bootstrap dans inventirap.css * (il se peut qu'on n'aie plus besoin de faire ça dans une prochaine version * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc) */ //$msgError1 = "Le champ suivant est obligatoire : ".$fname_nice.' du matériel'; //$error_msg = "Le champ suivant est obligatoire : ".$fname_nice; $error_msg = "Le champ suivant est obligatoire : ".$attributes['comment']; /////debug($msgError1); $this->Flash->error($error_msg); /* MARCHE PAS POURQUOI ? $materiel->setError($field, 'Ce champ ne doit pas être vide'); $materiel->setError('numero_commande', 'Ce champ ne doit pas être vide'); */ $materiel->setError($fname, 'Ce champ est obligatoire'); // (EP 2020 03) Si on ne veut pas de bouton de suppression du message : //$this->Flash->set($msgError1, ['params' => ['class' => 'alert alert-dismissible in alert-danger']]); //$this->Flash->set($msgError1, ['params' => ['class' => 'alert alert-dismissible fade in alert-danger']]); //return $this->redirect(['action'=>'edit',$id]); ///////////////$this->Flash->error(__("Le matériel n'a pas pu être $verb")); //return false; break; } // si champ obligatoire pas rempli } // foreach $mandatory_fields if ($ALL_MANDATORY_FIELDS_GIVEN) { // (3) On l'ajoute en BD, on envoie un email, et on affiche ok sur page accueil //$verb = $IS_ADD ? "ajouté" : "modifié"; //$action = $IS_ADD ? "add" : "edit"; //debug($materiel); exit; // (EP) pour provoquer une erreur de type "Action impossible" : //$materiel->will_stay=null; if (! $this->Materiels->save($materiel)) { //debug($this->Materiels->current_entity); exit; // (EP) Si ADD, l'id de la nouvelle entité a été mis à jour : /////$this->e_id = $materiel->id; //debug($document->id); // (EP202009) $this->e doit refléter le matériel mis à jour /////$this->e = $materiel; $this->myDebug($materiel->getErrors()); //$msgError1 = "Pour valider un matériel, le champ suivant ne doit pas être vide : ".'fourniss'.' du matériel'; //$this->Flash->error($msgError1); //debug($materiel->getErrors()); $flash_msg = "Le matériel n'a pas pu être $verb"; //if (is_null($materiel->Fournisseur)) $flash_msg .= ' (il faut préciser le fournisseur)'; $this->Flash->error(__($flash_msg)); //debug($materiel->getErrors()); //foreach ($materiel->getErrors() as $f=>$e) $this->Flash->error(__($e[0].' : '.$f)); foreach ($materiel->getErrors() as $f=>$e) foreach($e as $k=>$v) $this->Flash->error(__($v.' : '.$f)); } // save KO else { //debug($this->getCurrentEntity()); exit; //debug($this->Materiels->current_entity); exit; // (EP202009) $this->e doit refléter le matériel mis à jour // ok mais ne copie pas les données associées... (c'est toujours les anciennes...) ////$this->e = $materiel; // marche pas non plus //$this->e = clone $materiel; //debug($this->e); exit; /* debug($materiel->getErrors('jkl')); exit; if ($materiel->getError('numero_laboratoire')) { //debug("oui"); $this->Flash->success(__("Le numéro d'inventaire a été régénéré (car date achat changée)")); } */ $this->Flash->success(__("Le matériel a bien été $verb")); if ($IS_ADD) { //(EP202009) //$this->sendEmail($materiel); ///////$this->sendmail($materiel); $id = $materiel->id; //debug($this->e->id);exit; } //$this->dlog("Materiel $verb = '$materiel->designation' (id=$id)"); //////$this->ilog("Materiel $verb = '$materiel' (id=$id)"); /* * EDIT //En attendant un remaniement complet de la fonction //1 = img, doc = 2, mail normal = tout autre argument //$this->sendmail($materiel,5); */ return $this->redirect([ 'action' => 'view', $id ]); } // save KO } // $ALL_MANDATORY_FIELDS_FOR_GIVEN } // if POST... /* SINON (GET, PAS POST) * C'est la première fois qu'on vient sur cette vue, * donc on va préparer le formulaire de saisie) */ //debug($materiel); exit; /* * \AV2 - OPTIONS * (Domaine de définition de de chaque champ, si peut s'exprimer comme une liste) * * INITIALISATION DE LISTES qu'on va passer à la vue (edit ou add) pour assister la saisie (listes de choix proposés) * * Attention, le nom des variables utilisées est très important. * Ca doit être le MEME nom que le champ, mais au PLURIEL * Comme ça, c'est AUTOMAGIQUEMENT utilisé dans la vue comme liste d'options possibles * * ex: * - pour sur_categorie_id => surCategories * - pour sur_categorie_id => categories * - pour sur_categorie_id => sousCategories * - pour organisme_id => organismes * ... * */ // DOMAINES : LISTE COMPLETE $surCategories = $this->Materiels->SurCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'SurCategories.nom', // only for add() (was not in edit()) : 'conditions' => array( 'nom !=' => 'N/A' ) ]); // CATEGORIES ET SOUS-CATEG (modes EDIT et ADD-by-COPIE only) $categories = []; $sousCategories = []; //if ( !$is_add or ($is_add and isset($this->request->getAttribute('params')['pass'][0])) ) { if ( $IS_EDIT or ($IS_ADD and !is_null($materiel->sur_categorie_id)) ) { // - Seulement les categories correspondant au domaine choisi $categories = $this->Materiels->Categories->find('list', [ 'conditions' => [ 'Categories.sur_categorie_id' => $materiel->sur_categorie_id ], //'recursive' => - 1, 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'nom', //'order' => 'Categories.nom', ]); // - Seulement les sous-categ correspondant à la categorie choisie $sousCategories = $this->Materiels->SousCategories->find('list', [ 'conditions' => [ 'SousCategories.categorie_id' => $materiel->categorie_id ], //'recursive' => - 1, 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'nom' //'order' => 'SousCategories.nom' ]); } /* (EP) Totalement inutile !!! $categories = $this->Materiels->Categories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Categories.nom' ]); $sousCategories = $this->Materiels->SousCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'SousCategories.nom' ]); */ $groupesThematiques = $this->Materiels->GroupesThematiques->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'GroupesThematiques.nom' ]); $groupesMetiers = $this->Materiels->GroupesMetiers->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'GroupesMetiers.nom' ]); $projets = $this->Materiels->Projets->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Projets.nom' ]); $organismes = $this->Materiels->Organismes->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Organismes.nom' ]); $sites = $this->Materiels->Sites->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Sites.nom' ]); $designations = $this->Materiels->find('list', [ 'keyField' => 'designation', 'valueField' => 'designation', 'conditions' => array( 'designation !=' => '' ), 'order' => 'designation', 'group' => 'designation' ]); // autocomplete + saisie sites $lieu_detail = $this->Materiels->find('list', [ 'keyField' => 'lieu_detail', 'valueField' => 'lieu_detail', 'conditions' => array( 'lieu_detail !=' => '' ), 'order' => 'lieu_detail', // only for add() (was not in edit()) : 'group' => 'lieu_detail' ]); // Liste fournisseurs $fournisseurs = $this->Materiels->Fournisseurs->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Fournisseurs.nom' ]); // Liste des gestionnaires (admin) //$administrateurs = TableRegistry::get('Users')->find('list', [ $gestionnaires = $usersTable ->find('list', [ //'keyField' => 'nom', 'keyField' => 'id', 'valueField' => 'nom' ]) ->where([ 'role =' => 'Administration' ]) ->toArray(); $domaineresp = $usersTable->find() ->select('sur_categorie_id') ->where([ 'username =' => $this->LdapAuth->user($this->request->getSession() ->read('authType'))[0] ]) ->first()['sur_categorie_id']; if ($domaineresp == null) $domaineresp = false; // EDIT only if ($IS_EDIT) { $designation_edit = $this->Materiels->find('list', [ 'keyField' => 'id', 'valueField' => 'designation', 'conditions' => array( 'id =' => $materiel->id ) ])->toArray(); //$designation_edit = $designation_edit->toArray(); $lieu_detail_edit = $this->Materiels->find('list', [ 'keyField' => 'id', 'valueField' => 'lieu_detail', 'conditions' => array( 'id =' => $materiel->id ) ])->toArray(); //$lieu_detail_edit = $lieu_detail_edit->toArray(); //(EP) TODO: debug ce truc, c'est du grand n'importe nawak ! $dom = TableRegistry::get('Materiels')->find() ->select('sur_categorie_id') ->where([ 'id =' => $materiel->id ]) ->first()['sur_categorie_id']; $domaines = TableRegistry::get('Users')->find() ->select('sur_categorie_id') ->where([ 'username =' => $this->LdapAuth->user($this->request->getSession() ->read('authType'))[0] ]) ->first()['sur_categorie_id']; $role = TableRegistry::get('Users')->find() ->select('role') ->where([ 'username =' => $this->LdapAuth->user($this->request->getSession() ->read('authType'))[0] ]) ->first()['role']; $domaineresp = ($dom == $domaines); } // EDIT only // not used //$utilisateurconnect = $usersTable->find('all')->toArray(); $users_name = $this->setUsersLists(); /* //$users = TableRegistry::get('LdapConnections')->getListUsers(); //$users = TableRegistry::get('LdapConnections')->getUsersLoginAndEmail(); $users_login_and_email = TableRegistry::getTableLocator()->get('LdapConnections')->getUsersLoginAndEmail(); $users_name = array_keys($users_login_and_email); $users_option_list = []; for ($i = 0; $i < sizeof($users_name); $i ++) { // $users_option_list["Etienne Pallier"] = "Etienne Pallier" $users_option_list[$users_name[$i]] = $users_name[$i]; } */ //--- 2) INITIALISATION DE VARIABLES QU'ON VA PASSER A LA VUE (add.ctp ou edit.ctp) // Le mail de la personne loguée $mail_responsable = $usersTable->find() ->select('email') ->where([ 'username =' => $this->LdapAuth->user($this->request->getSession()->read('authType'))[0] ]) ->first()['email']; /* // ADD only if ($is_add) { // Un id a été passé en argument => Copie de materiel (on a cliqué sur "Copier ce materiel") if (isset($this->request->getAttribute('params')['pass'][0])) { $cpMateriel = $this->Materiels->get($this->request->getAttribute('params')['pass'][0]); $this->set('cpMateriel', $cpMateriel); } } else { */ // EDIT only if ($IS_EDIT) { if (! empty($materiel->get('nom_responsable'))) { //if (! in_array($materiel->get('nom_responsable'), $utilisateurs)) { if (! in_array($materiel->get('nom_responsable'), $users_name)) { $nom_ancien_responsable = $materiel->get('nom_responsable'); $this->set(compact('nom_ancien_responsable')); } } /* (EP 2021 10) moved to MyHelper // (EP) Fonction utilisée dans la vue, déclarée ici pour éviter les problèmes de tests $isReadonlyField = function ($fieldName, $myReadonlyFields) { // Fonctionnement inversé : TOUS readonly (*) SAUF certains champs (listés) if (!empty($myReadonlyFields) && $myReadonlyFields[0]=='*') { $modifiableFields = $myReadonlyFields; array_shift($modifiableFields); return ! in_array($fieldName, $modifiableFields); } // Fonctionnement normal : //return ( !empty($materiel->$fieldName) && in_array($fieldName, $myReadonlyFields) ); return ( in_array($fieldName, $myReadonlyFields) ); }; //$this->set('isReadonlyField', $isReadonlyField); $this->set(compact('isReadonlyField')); */ $this->set('IS_CREATED', $materiel->status == 'CREATED'); $this->set('IS_VALIDATED', $materiel->status == 'VALIDATED'); $this->set('IS_ARCHIVED', $materiel->status == 'ARCHIVED'); $this->set('IS_ARCHIVED_OR_TOBE', in_array($materiel->status, [ 'TOBEARCHIVED', 'ARCHIVED' ])); $this->set(compact( 'role', 'designation_edit', 'lieu_detail_edit' )); } // EDIT only // Si le user est un admin, on le met comme gestionnaire de référence // (sauf si le gestionnaire est déjà défini) if ($this->USER_IS_ADMIN() && empty($materiel->gestionnaire_id)) $materiel->gestionnaire_id = $this->getCurrentUserEntity()->id; /* About set and compact : * * The compact (php) function returns an associative array, built by taking the names specified in the input array, * using them as keys, and taking the values of the variables referenced by those names and making those the values. * For example: * $fred = 'Fred Flinstone'; * $barney = 'Barney Rubble'; * $names = compact('fred', 'barney'); * => is equivalent to: $names = array('fred' => 'Fred Flinstone', 'barney' => 'Barney Rubble') * * So when you use compact in conjunction with set, * you're using the single parameter form of the set function, * by passing it an associative array of key-value pairs. * * If you just have one variable you want to set on the view, and you want to use the single parameter form, you must invoke set in the same way: * $variable_to_pass = 'Fred'; * $this->set(compact('variable_to_pass')); * * Otherwise, the two parameter form of set can be used: * $variable_to_pass = 'Fred'; * $this->set('variable_to_pass', $variable_to_pass); * * Both achieve the same thing. */ //$this->set(compact('designation', 'utilisateurconnect', 'users', 'materiel', 'surCategories', 'categories', 'sousCategories', 'groupesThematiques', 'groupesMetiers', 'organismes', 'sites', 'utilisateurs', 'mail_responsable', 'domaineresp', 'lieu_detail', 'fournisseurs')); $statuses = [ 'CREATED' => 'CREATED', 'TOBEORDERED' => 'TOBEORDERED', 'VALIDATED' => 'VALIDATED', 'TOBEARCHIVED' => 'TOBEARCHIVED', 'ARCHIVED' => 'ARCHIVED' ]; $entity = $materiel; //debug($entity); $this->set(compact( 'IS_ADD', 'mail_responsable', 'entity', // new //'materiel', // @deprecated 'statuses', // not used //'utilisateurconnect', 'surCategories', 'categories', 'sousCategories', 'groupesThematiques', 'groupesMetiers', 'projets', 'organismes', 'sites', 'domaineresp', //'designations', 'lieu_detail', 'fournisseurs', //'utilisateurs', //'users_login_and_email', 'users_option_list', // Gestionnaires (id=>name): 'gestionnaires' )); $readonlyFields = $this->getUneditableFieldsForMaterielStatus($materiel->status); $this->set(compact('readonlyFields')); //debug($materiel->status); //debug($readonlyFields); //$this->set('readonlyFields', $IS_ADD ? [] : $this->getUneditableFieldsForMaterielStatus($materiel->status)); //debug($this->Materiels->getUneditableFieldsForMaterielStatus($status)); exit; //$this->set('readonlyFields', $IS_ADD ? [] : $this->Materiels->getUneditableFieldsForMaterielStatus($materiel->status)); //$this->set('CAN_PRINT_LABEL', $this->isAuthorizedActionForCurrentUser('printLabelRuban', $id)); $this->set('CAN_PRINT_LABEL', $IS_ADD ? $this->confLabinvent->hasPrinter : $this->isAuthorizedActionForCurrentUser('printLabelRuban')); /* (EP) INUTILE (sauf pour faire du j-son ou x-ml) !!! $this->set('_serialize', [ 'materiel', ]); */ } //add_or_edit() private function currentUserRoleInList($except_list) { //debug($this->user_role); return in_array($this->getUserRole(), $except_list); } public function getUneditableFieldsForMaterielStatusAndCurrentUser($materiel_status) { return $this->getUneditableFieldsForMaterielStatus($materiel_status); } private function getUneditableFieldsForMaterielStatus($materiel_status) { // Superadmin peut tout modifier if ($this->USER_IS_SUPERADMIN()) return []; $uneditable_fields = $this->Materiels->getUneditableFieldsForMaterielStatus($materiel_status); //foreach ($uneditable_fields as $fname=>$except_list) { foreach ($uneditable_fields as $fname=>$attributes) { // S'il y a une exception, voir si elle concerne le USER courant, sinon supprimer ce champ $fname if ($attributes['except_roles']) { //debug($except_list); if ($this->currentUserRoleInList($attributes['except_roles'])) unset($uneditable_fields[$fname]); //debug("remove $fname"); } } return $uneditable_fields; } /** * Add method * * @return \Cake\Http\Response | void Redirects on successful add, renders view otherwise. */ public function add($valeurs = null, $erreurs = null) { //$this->add_or_edit(TRUE, null, $valeurs, $erreurs); $this->add_or_edit(TRUE, $valeurs, $erreurs); } /** * Edit method * * @param string|null $id * Materiel id. * @return \Cake\Http\Response|void Redirects on successful edit, renders view otherwise. * @throws \Cake\Http\Exception\NotFoundException When record not found. */ public function edit($id = null) { $this->add_or_edit(FALSE, $id); } /** * Administrer method * * @param string|null $id * Materiel id. * @return \Cake\Http\Response|void Redirects on successful edit, renders view otherwise. * @throws \Cake\Http\Exception\NotFoundException When record not found. */ public function administrer($id = null) { $materiel = $this->Materiels->get($id, [ 'contain' => [] ]); if ($this->request->is([ 'patch', 'post', 'put' ])) { $materiel = $this->Materiels->patchEntity($materiel, $this->request->getData()); if ($this->Materiels->save($materiel, [ 'checkRules' => false ])) { $this->Flash->success(__('Le matériel a bien été édité.')); return $this->redirect([ 'action' => 'view', $id ]); } else $this->Flash->error(__('Le matériel n\'a pas pu être édité.')); } $surCategories = $this->Materiels->SurCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'SurCategories.nom' ]); $categories = $this->Materiels->Categories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Categories.nom' ]); $sousCategories = $this->Materiels->SousCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'SousCategories.nom' ]); $groupesThematiques = $this->Materiels->GroupesThematiques->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'GroupesThematiques.nom' ]); $groupesMetiers = $this->Materiels->GroupesMetiers->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'GroupesMetiers.nom' ]); $organismes = $this->Materiels->Organismes->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Organismes.nom' ]); $sites = $this->Materiels->Sites->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Sites.nom' ]); $fournisseurs = $this->Materiels->Fournisseurs->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Fournisseurs.nom' ]); $users = TableRegistry::getTableLocator()->get('LdapConnections')->getListUsers(); // Tri des utilisateurs par nom sort($users); $utilisateurs = []; for ($i = 0; $i < sizeof($users); $i ++) { $utilisateurs[$users[$i]] = $users[$i]; } if (! empty($materiel->get('nom_responsable'))) { if (! in_array($materiel->get('nom_responsable'), $utilisateurs)) { $nom_ancien_responsable = $materiel->get('nom_responsable'); $this->set(compact('nom_ancien_responsable')); } } $mail_responsable = TableRegistry::get('Users')->find() ->select('email') ->where([ 'username =' => $this->LdapAuth->user($this->request->getSession() ->read('authType'))[0] ]) ->first()['email']; $this->set('IS_CREATED', $materiel->status == 'CREATED'); $this->set('IS_VALIDATED', $materiel->status == 'VALIDATED'); $this->set('IS_ARCHIVED_OR_TOBE', in_array($materiel->status, [ 'TOBEARCHIVED', 'ARCHIVED' ])); $this->set(compact('materiel', 'surCategories', 'categories', 'sousCategories', 'groupesThematiques', 'groupesMetiers', 'organismes', 'sites', 'utilisateurs', 'mail_responsable', 'fournisseurs')); $this->set('_serialize', [ 'materiel' ]); } // administrer() /** * Delete method * * @param string|null $id * Materiel id. * @return \Cake\Http\Response|null Redirects to index. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. */ public function delete($id = null) { $DEBUG = false; // $this->request->allowMethod(['post', 'delete']); $verb = 'supprimé'; /* $materiel = $this->Materiels->get($id, [ 'contain'=>['SurCategories', 'Categories', 'SousCategories'] ] ); */ // Optimisation : ne charge l'entité QUE si pas déjà fait $materiel = $this->getEntity($id, false, ['SurCategories', 'Categories', 'SousCategories']); // DEBUG //$emails = $this->sendmail($materiel); //$emails = $this->sendNotificationForEntityAction($materiel); //debug($emails); //exit; //if ($materiel->has('sur_category')) debug($materiel->sur_category->nom); //debug($materiel); exit; //if ( $this->Materiels->delete($materiel) ) { if ( !$DEBUG && $this->Materiels->delete($materiel) ) { $this->Flash->success(__("Le matériel a bien été $verb")); /* this->ilog("Materiel $verb = '$materiel' (id=$id)"); //(EP202009) //$this->sendEmail($materiel); $this->sendmail($materiel); */ } else $this->Flash->error(__("Le matériel n'a pas pu être $verb")); // Retour à la liste des matos return $this->redirect([ 'action' => 'index' ]); } // delete() /** * StatusSetTo method * * @param string $newStatus * @param string $message * @param string $id * @param string $from * @return \Cake\Http\Response|NULL */ private function _statusSetTo($newStatus, $message, $id = null, $from = 'index', $onlyOneMateriel = True) { //$materiel = $this->Materiels->get($id)->set('status', $newStatus); $materiel = $this->Materiels->get($id, [ 'contain' => [ // 1) HasOne /* 'SurCategories', 'Categories', 'SousCategories', 'GroupesThematiques', 'GroupesMetiers', 'Projets', 'Organismes', 'Sites', 'Fournisseurs', //'Gestionnaires', 'Users', */ // 2) HasMany // Tous les Documents de ce $materiel (sans leur type) //'Documents', // Tous les Documents de ce $materiel AVEC leur type 'Documents.TypeDocuments', /* 'Emprunts', // Tous les Suivis de ce $materiel (sans leur type) //'Suivis', // Tous les Suivis de ce $materiel AVEC leur type de suivi 'Suivis.TypeSuivis', */ ] ]); //debug($materiel); exit; $msgError2 = "Le statut du matériel " . $materiel->designation . " (" . $materiel->numero_laboratoire . ") n'a pas pu être modifié"; /* * - INVALIDER (CREATED) * Repasse le matos au statut "PRÉCÉDENT" (et non pas systématiquement à CREATED) : * - ARCHIVED => TOBEARCHIVED * - TOBEARCHIVED => VALIDATED * - VALIDATED => CREATED (il faudra commander à nouveau si le matos était commandé...) */ if ($newStatus == 'CREATED') { if ($materiel->status == 'ARCHIVED') $materiel->status = 'TOBEARCHIVED'; elseif ($materiel->status == 'TOBEARCHIVED') $materiel->status = 'VALIDATED'; // Par défaut => CREATED (VALIDATED ou TOBEORDERED => CREATED) else $materiel->status = 'CREATED'; } // Set new status else $materiel->status = $newStatus; //$materiel->set('status', $newStatus); $ACTION = "%s ce matériel,"; //$ERROR_MSG_EMPTY_FIELD = "Pour $ACTION le champ suivant ne doit pas être vide : %s du matériel"; $ERROR_MSG_EMPTY_FIELD = "Pour $ACTION le champ suivant ne doit pas être vide : %s"; $ERROR_MSG_DOC_NOT_ATTACHED = "Pour $ACTION vous devez d'abord joindre un document de type '%s' à cette fiche (bouton 'Lier un doc.') "; // - TOBEORDERED if ($newStatus == 'TOBEORDERED') { // Attributs obligatoires pour la phase COMMANDE (LOT1) $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(1); //debug($mandatoryFields); $ACTION = 'ordonner la commande de'; // Si au moins un champ obligatoire est nul ou vide => ERROR foreach ($mandatoryFields as $fname => $attributes) { // Doc attaché obligatoire ? (champ virtuel) //if (strtoupper($fname) == 'DOC_DEVIS') { if ( substr($fname,0,4) == 'DOC_' ) { // DOC_DEVIS, DOC_BC, ... if (! $materiel->hasAttachedDocOfType($fname)) { //$msgError1 = Pour $ACTION ce matériel, vous devez d'abord joindre un document de type '".substr($fname,4)."' à cette fiche (bouton 'Lier un doc.') "; $error_msg = sprintf($ERROR_MSG_DOC_NOT_ATTACHED, $ACTION, substr($fname,4)); $this->Flash->error($error_msg); $this->ACTION_CANCELLED = TRUE; // => on reste sur "view" return $this->redirect(['action'=>'view',$id]); } /* if (! $materiel->hasDevis()) { //$msgError1 = "Pour ordonner la commande de ce matériel, vous devez d'abord joindre un devis à cette fiche (bouton 'Lier un doc.') "; $msgError1 = "Pour ordonner la commande de ce matériel, vous devez d'abord joindre un devis à cette fiche (bouton 'Lier un doc.') "; $this->Flash->error($msgError1); $this->ACTION_CANCELLED = TRUE; // => on reste sur "view" return $this->redirect(['action'=>'view',$id]); } */ // on passe au champ suivant continue; } // Champ physique (réel) obligatoire ? $fval = $materiel->$fname; if ($fval === null || $fval == '') { //$msgError1 = "Pour $ACTION ce matériel, le champ suivant ne doit pas être vide : ".$fnicename.' du matériel'; $error_msg = sprintf($ERROR_MSG_EMPTY_FIELD, $ACTION, $attributes['comment']); $this->Flash->error($error_msg); // Utile ??? (plante les tests) ////$this->e->setError($fname, 'Ce champ ne doit pas être vide'); $this->ACTION_CANCELLED = TRUE; // => on revient à "edit" return $this->redirect(['action'=>'edit',$id]); } } //TODO // Le matos est valide, on pourrait donc marquer la date de commande $materiel->tobeordered = true; ////$materiel->set('date_ordered', date('Y-m-d')); //$materiel->date_ordered = date('Y-m-d'); } // TOBEORDERED // - VALIDATED if ($newStatus == 'VALIDATED') { // Attributs obligatoires pour la phase VALIDATION (livré et payé) (LOT2) $mandatoryFields = $this->Materiels->getMandatoryFieldsForLot(2); //$mandatoryFields = $this->Materiels->getCategoryFieldsForLot('MANDATORY_FIELDS_FOR_LOT', 2) //$mandatoryFields = $this->Materiels->MANDATORY_FIELDS_FOR_LOT2; //debug($mandatoryFields);exit; /* $mandatoryFields = array( 'Nom utilisateur' => $materiel->nom_responsable, 'Fournisseur' => $materiel->fournisseur_id, 'Numéro de commande' => $materiel->numero_commande, 'Organisme' => $materiel->organisme_id, 'Date de reception' => $materiel->date_reception, 'Prix' => $materiel->prix_ht, // (EP202009) Un gestionnaire par défaut (de référence) doit être choisi AVANT validation 'Gestionnaire de référence' => $materiel->gestionnaire_id ); */ /* $mandatoryFields = array( $materiel->nom_responsable, $materiel->fournisseur_id, $materiel->numero_commande, $materiel->organisme_id, $materiel->date_reception, $materiel->prix_ht, ); $msgError1 = "Pour valider un matériel, les champs suivants ne doivent pas être vides : Date de reception, Nom utilisateur, Gestionnaire de référence, Fournisseur, Organisme, Prix, et Numéro de commande"; */ // Si au moins un champ obligatoire est nul ou vide => ERROR foreach ($mandatoryFields as $fname => $attributes) { $ACTION = 'valider'; /* * Champs obligatoires à ignorer : * - champs virtuels (n'existent pas physiquement) => DOC_XXX (DOC_DEVIS, DOC_BC, ...) * - champs spéciaux dont le caractère obligatoire est géré indirectement (via beforeSave()) => fournisseur //if ( in_array($fname, ['DOC_DEVIS', 'etiquette', 'numero_inventaire_organisme']) ) continue; if ( substr($fname,0,4) == 'DOC_' ) continue; */ // Doc attaché obligatoire ? (champ virtuel) if ( substr($fname,0,4) == 'DOC_' ) { // DOC_DEVIS, DOC_BC, ... if (! $materiel->hasAttachedDocOfType($fname)) { //$msgError1 = "Pour ordonner la commande de ce matériel, vous devez d'abord joindre un devis à cette fiche (bouton 'Lier un doc.') "; $error_msg = sprintf($ERROR_MSG_DOC_NOT_ATTACHED, $ACTION, substr($fname,4)); $this->Flash->error($error_msg); $this->ACTION_CANCELLED = TRUE; // => on reste sur "view" return $this->redirect(['action'=>'view',$id]); } // on passe au champ suivant continue; } // Champ obligatoire ? $fval = $materiel->$fname; //debug($fname); debug($fval); if ($fval === null || $fval == '') { //debug($fname); // Validation d'un seul matériel => on revient à "edit" if ($onlyOneMateriel) { /* (EP 2020 03) * Ce genre de ligne ($this->Flash->...) affichant un message flash en haut de page, * ne fonctionnait plus à cause de bootstrap (css). * Ca fonctionne uniquement parce qu'on a surchargé la règle "fade" * de bootstrap dans inventirap.css * (il se peut qu'on n'aie plus besoin de faire ça dans une prochaine version * de bootstrap, ou bootsrap-ui, ou cakephp..., à surveiller donc) */ //$msgError1 = "Pour $ACTION ce matériel, le champ suivant ne doit pas être vide : ".$fnicename.' du matériel'; //debug($fname); $error_msg = sprintf($ERROR_MSG_EMPTY_FIELD, $ACTION, $attributes['comment']); $this->Flash->error($error_msg); //debug($error_msg); /* MARCHE PAS POURQUOI ? $materiel->setError($field, 'Ce champ ne doit pas être vide'); $materiel->setError('numero_commande', 'Ce champ ne doit pas être vide'); */ $this->e->setError($fname, 'Ce champ ne doit pas être vide'); $this->ACTION_CANCELLED = TRUE; // (EP 2020 03) Si on ne veut pas de bouton de suppression du message : //$this->Flash->set($msgError1, ['params' => ['class' => 'alert alert-dismissible in alert-danger']]); //$this->Flash->set($msgError1, ['params' => ['class' => 'alert alert-dismissible fade in alert-danger']]); return $this->redirect(['action'=>'edit',$id]); } // Validation de plusieurs matériels (cochés) à la fois => on annule l'action de validation else { $this->Flash->error($msgError2); $this->ACTION_CANCELLED = TRUE; return False; } } } //debug("OK"); //exit; // Le matos est valide, on peut donc marquer la date de validation $materiel->set('date_validated', date('Y-m-d')); //$materiel->date_validated = date('Y-m-d'); //debug($materiel->date_validated); //exit; /* (EP202009) déplacé dans add_or_edit() // Si le current user est un admin => on le met comme "gestionnaire" de ce matériel //if ($newStatus == 'VALIDATED') { $current_user = $_SESSION['Auth']['User']['sn'][0]; if ( in_array( $current_user, //TableRegistry::get('Users')->find('list', [ TableRegistry::getTableLocator()->get('Users') ->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ]) ->where(['role =' => 'Administration']) ->toArray() ) ) { $gestionnaireID = TableRegistry::getTableLocator()->get('Users') ->find() ->where(['nom' => $current_user]) ->first() ->id; $materiel->gestionnaire_id = $gestionnaireID; } // if //} */ } // VALIDATED // - ARCHIVED if ($newStatus == 'ARCHIVED') $materiel->set('date_archived', date('Y-m-d')); // SAVE $success = False; // - SAVE KO if (! $this->Materiels->save($materiel, [ 'checkRules' => false, 'checkExisting' => false ])) $this->Flash->error(__($msgError2 . " (cannot be saved)")); // - SAVE OK else { $success = True; if ($onlyOneMateriel) $this->Flash->success(__($message)); ///$this->ilog("Changement de statut (passe à '$newStatus') du matériel '$materiel' (id=$id)"); //$this->sendEmail($materiel); //évite le double envoi car il y a un envoi de mail a la fonction add et à l'état created //évite les envois succcessifs si qqn fait une erreur sur la fiche, valide la fiche, et dévalide pour remodifier //if ($newStatus == 'ARCHIVED' || $newStatus == 'TOBEARCHIVED') { /* if (in_array($newStatus, [ //'CREATED', // Pour Création ET Dé-validation 'ARCHIVED','TOBEARCHIVED','VALIDATED']) ) $this->sendmail($materiel,3); */ } // SAVE OK if (! $onlyOneMateriel) return $success; if ($from == 'index') $id=null; /* return $this->redirect([ 'action' => $from ]); else */ return $this->redirect([ 'action' => $from, $id // null pour $from=index ]); } // statusSetTo() // Actions autorisées à envoyer un email //@Override parent protected function get_actions_with_email() { return [ 'add', 'edit', 'delete', 'statusTobeordered', 'statusCreated', 'statusTobearchived', 'statusValidated', 'statusArchived' // ... ]; } //@Override parent static public function getActions() { $actions = parent::getActions(); //$actions = array_merge($actions, [ array_push($actions, /* // - CRUD //'add', 'add','add_by_copy', 'view','index','edit','delete', //'view', //'index', */ //'administrer' // - Autres 'find', 'statusCreated', 'statusTobeordered', 'statusValidated','statusTobearchived','statusArchived', //'execActions', //'updateSelectedStatus', //'export', 'setLabelIsPlaced','setLabelIsNotPlaced', 'printLabelRuban' //'printLabel','printSheet', //TODO: avec des arguments //'getDateGarantie', ); $actions = ['printLabelRuban']; // Retourne toutes les méthodes publiques, mêmes celles qui ne sont pas des actions, donc pas bon //$actions = get_class_methods('App\Controller\MaterielsController'); return $actions; } /** * StatusCreated method (retrograder le statut en le repassant à CREATED) * De-Valider ou In-valider * * @param string $id * @param string $from */ public function statusCreated($id = null, $from = 'index') { $this->_statusSetTo('CREATED', "Le matériel a bien été rétrogradé au statut 'CRÉÉ'", $id, $from); } /** * StatusValidated method * * @param string $id * @param string $from */ public function statusTobeordered($id = null, $from = 'index') { $this->_statusSetTo('TOBEORDERED', 'La demande de commande du matériel a bien été transmise', $id, $from); /* * (EP) moved to statusSetTo() * if (in_array($_SESSION['Auth']['User']['sn'][0], TableRegistry::get('Users') * ->find('list', ['keyField' => 'id', 'valueField' => 'nom']) * ->where(['role =' => 'Administration']) * ->toArray())) { * $gestionnaireID = TableRegistry::get('Users')->find()->where(['nom' => $_SESSION['Auth']['User']['sn'][0]])->first()->id; * $materiel->gestionnaire_id = $gestionnaireID; * } * $this->sendEmail($this->Materiels->get($id)); */ } /** * StatusValidated method * * @param string $id * @param string $from */ public function statusValidated($id = null, $from = 'index') { //$this->_statusSetTo('VALIDATED', "Le matériel a bien été validé (n'oubliez pas de saisir le n° inventaire 'tutelle' une fois le 'service fait')", $id, $from); $this->_statusSetTo('VALIDATED', "Le matériel a bien été validé", $id, $from); /* * (EP) moved to statusSetTo() * if (in_array($_SESSION['Auth']['User']['sn'][0], TableRegistry::get('Users') * ->find('list', ['keyField' => 'id', 'valueField' => 'nom']) * ->where(['role =' => 'Administration']) * ->toArray())) { * $gestionnaireID = TableRegistry::get('Users')->find()->where(['nom' => $_SESSION['Auth']['User']['sn'][0]])->first()->id; * $materiel->gestionnaire_id = $gestionnaireID; * } * $this->sendEmail($this->Materiels->get($id)); */ } /** * statusTobearchived method * * @param string $id * @param string $from */ //public function statusTobearchived($id = null, $from = 'index') public function statusTobearchived($id = null, $from = 'index') { $this->_statusSetTo('TOBEARCHIVED', "La sortie d'inventaire a bien été demandée", $id, $from); // (EP) moved to statusSetTo() // $this->sendEmail($this->Materiels->get($id)); } public function stats() { /* return $this->redirect([ 'controller' => 'pages', 'action' => 'stats', ]); */ // créer des FAKE DATA pour tester la fonctionnalité ? $TEST = true; $TEST = false; // Nb années demandées par l'utilisateur $nbyears = $this->request->getQuery('nbyears'); // Initialisation des variables nécessaires // - Données Totales (complètes) (tronquées à $nbyears) $tot = [ 'CREATED' => (int) 1, 'VALIDATED' => (int) 0, 'TOBEARCHIVED' => (int) 0, 'ARCHIVED' => (int) 0, 'suivis' => (int) 0, 'prets' => (int) 0 ]; // - Donées Moyennes $avg = [ 'CREATED' => (int) 1, 'VALIDATED' => (int) 0, 'TOBEARCHIVED' => (int) 0, 'ARCHIVED' => (int) 0, 'suivis' => (int) 0, 'prets' => (int) 0 ]; // - Année en cours $now = new \DateTime('now'); //debug($now); // 2020 en 2020 $current_year = (int) $now->format('Y'); // - Données par Année $years = [ $current_year => [ 'CREATED' => (int) 1, 'VALIDATED' => (int) 0, 'TOBEARCHIVED' => (int) 0, 'ARCHIVED' => (int) 0, 'suivis' => (int) 0, 'prets' => (int) 0 ] ]; // Tables $materiels = TableRegistry::getTableLocator()->get('Materiels'); $suivis = TableRegistry::getTableLocator()->get('Suivis'); $prets = TableRegistry::getTableLocator()->get('Emprunts'); $associated_entities = ['suivis','prets']; $fk = 'materiel_id'; // Tous les matos qui ont une date d'achat non nulle $all_matos = $materiels->find()->where(['year(date_acquisition) >' => 0]); // On ne fait pas de stats s'il n'y a aucun matériel dans la BD if ($all_matos->count() == 0) { $year_min = $current_year; //$nbyears = 0; $this->set(compact('current_year', 'year_min', 'nbyears', 'tot', 'avg', 'years')); return; } // - Année min //$year_min = $materiels->find()->min(['year(date_acquisition)']); //$matos_year_min = $materiels->find()->where(['year(date_acquisition) >' => 0]) $matos_year_min = $all_matos->cleanCopy()->min(function($matos) { return $matos->date_acquisition->format('Y'); }); $year_min = (int) $matos_year_min->date_acquisition->format('Y'); // - Nb années au total : au moins 1 $nbyears_max = $current_year - $year_min + 1; // $nbyears demandé par l'utilisateur, par défaut TOUTES $nbyears = ( is_numeric($nbyears) && $nbyears>0 ) ? round($nbyears) : $nbyears_max; //debug($nbyears); // - Nb annnées à traiter (entre 1 et $nbyears_max) // ($nbyears=null si pas passé en paramètre) // ($nbyears='' si passé en paramètre mais sans valeur '?nbyears=') //$nbyears = $nbyears ? abs((int)$nbyears) : $nbyears_max; //debug($nbyears); $nbyears = min($nbyears,$nbyears_max); //debug($nbyears); //debug($nbyears); // Si on est au max (avec max>2), on enlève la 1ère et la dernière années (car a priori incomplètes) //sif ($nbyears==$nbyears_max && $nbyears>2) $nbyears -= 2; // Nouvelle année min $year_min = $current_year-$nbyears + 1; // Nouveau $all_matos, en tronquant à $nbyears $all_matos = $materiels->find() ->where(['year(date_acquisition) <=' => $current_year]) ->where(['year(date_acquisition) >=' => $year_min]); $all_matos_ids = $all_matos->cleanCopy()->select(['id']); /* $statuses = [ 'CREATED' => 'created', 'VALIDATED' => 'date_validated', 'ARCHIVED' => 'date_archived' ]; */ $statuses = [ 'CREATED', 'VALIDATED', 'TOBEARCHIVED', 'ARCHIVED' ]; // - tot : Données Totales (complètes) (tronquées à $nbyears) foreach ($statuses as $status) { $all_matos_copy = $all_matos->cleanCopy(); // Tous les matos qui ont une date d'achat non nulle if ($status != 'CREATED') $all_matos_copy->where(['status' => $status]); $tot[$status] = $all_matos_copy->count(); } // Suivis et Prets // Nb materiels qui ont des suivis (i.e. qui ont au moins 1 suivi) foreach ($associated_entities as $e) { //$nb_matos_suivis = $suivis->find() $tot[$e] = $$e->find() // materiel_id not null ->where(["$fk >" => 0]) ->select([$fk]) ->distinct([$fk]) ->where(["$fk IN" => $all_matos_ids]) ->count(); } /* // - tot2 : Données Totales Partielles (= tot sauf 1ère et dernière années) $all_matos2 = $materiels ->find() ->where(['year(date_acquisition) <' => $current_year]) ->where(['year(date_acquisition) >' => $current_year-$nbyears]); $all_matos_ids2 = $all_matos2->cleanCopy()->select(['id']); $tot2 = []; foreach ($statuses as $status) { $all_matos_copy = $all_matos2->cleanCopy(); // Tous les matos qui ont une date d'achat non nulle if ($status != 'CREATED') $all_matos_copy->where(['status' => $status]); $tot2[$status] = $all_matos_copy->count(); } // Suivis et Prets // Nb materiels qui sont suivis (i.e. qui ont au moins 1 suivi) foreach ($associated_entities as $e) { //$nb_matos_suivis = $suivis->find() $tot2[$e] = $$e->find() // materiel_id not null ->where(["$fk >" => 0]) ->select([$fk]) ->distinct([$fk]) ->where(["$fk IN" => $all_matos_ids2]) ->count(); } */ // - Donées Moyennes (avg) /* $avg['CREATED'] = 10; $avg['VALIDATED'] = 7; $avg['TOBEARCHIVED'] = 1; $avg['ARCHIVED'] = 1; */ foreach ($statuses as $status) { $avg[$status] = round($tot[$status]/$nbyears, 1); /* * (EP) * J'obtiens pas le meme resultat avec cette methode... * Peut-être que ca compte pas les années où il n'y a rien (0) */ /* //$all_matos = $materiels->find(); //$all_matos = $materiels->find()->where(['year(date_acquisition) >' => 0]); //->groupBy('year(date_acquisition)'); $all_matos_copy = $all_matos->cleanCopy(); if ($status != 'CREATED') $all_matos_copy->where(['status' => $status]); $avg[$status] = $all_matos_copy //->groupBy('year(date_acquisition)'); ->countBy(function($matos) { return $matos->date_acquisition->format('Y'); }) ->avg(); //->toList(); //->toArray(); //->count(); $avg[$status] = round($avg[$status]); */ } // Suivis et Prets // Nb materiels qui sont suivis (i.e. qui ont au moins 1 suivi) foreach ($associated_entities as $e) { $avg[$e] = round($tot[$e]/$nbyears, 1); /* $nb_matos_having_entity = $$e->find() // materiel_id not null ->where(["$fk >" => 0]) ->select([ 'year' => 'YEAR(created)' ]) ->group('year') ->toArray(); //->groupBy(function($entity) { ->group(function($entity) { return $entity->created->format('Y'); }) ->distinct([$fk]) ->count() ->avg(); $avg[$e] = $nb_matos_having_entity; */ //debug($nb_matos_having_entity); } // - Données par Année $all_matos = $materiels->find(); for ($y=$current_year ; $y>=$year_min ; $y--) { ////debug($y); $all_matos_for_year = $all_matos->cleanCopy()->where(['year(date_acquisition)' => $y]); ////debug($all_matos_for_year->count()); // - statut par année try { foreach ($statuses as $status) { /* $all_matos_for_year = $materiels->find(); ->where(['year(date_acquisition)' => $y]); $all_matos_for_year = $all_matos->cleanCopy()->where(['year(date_acquisition)' => $y]); */ $all_matos_for_year_and_status = $all_matos_for_year->cleanCopy(); if ($status != 'CREATED') $all_matos_for_year_and_status->where(['status' => $status]); $years[$y][$status] = $all_matos_for_year_and_status->count(); } } catch (\PDOException $e) { debug("Mauvais format de requete SQL (PagesController/stats statuses), Exception PDO générée !"); exit; } ////debug($years); // Suivis et Prets, par année $all_matos_suivis_for_year = $all_matos_for_year->cleanCopy(); foreach ($associated_entities as $e) try { ////debug($e); //debug($$e->find()->count()); /* * SQL equivalent : à tester... * * SELECT COUNT(DISTINCT(suivis.materiel_id)) FROM suivis LEFT JOIN materiels * ON suivis.materiel_id = materiels.id * WHERE YEAR(materiels.date_acquisition) = $y * * $years[$y]['suivis'] = $suivis->find()... * puis * $years[$y]['prets'] = $prets->find()... */ $years[$y][$e] = $$e // Tous les suivis/emprunts... ->find() // ... et leur materiel associé... ->leftJoinWith('Materiels') // ... LEFT JOIN ON (suivis.materiel_id = materiels.id) ... // ou emprunts.materiel_id //->enableAutoFields(true) // ... (uniquement les materiels de l'année $y) ... ->where(['year(date_acquisition)' => $y]) // ... en ne gardant qu'1 seul suivi/emprunt par materiel (meme si ce materiel en a plusieurs) ... ->distinct(['materiel_id']) // ... et enfin, on compte le nombre de suivis/emprunts (total pour l'année $y) ->count(); //->toArray(); //$all_matos_for_year //->contain(['Suivis']) //->select(['id', 'count_suivis => count(suivis)']) //->where(['count_suivis >' => 0]) //->count(); } catch (\PDOException $ex) { debug("Mauvais format de requete SQL (PagesController/stats suivis/prets), Exception PDO générée !"); debug($ex); exit; } //debug($years[$y]['suivis']); //$years[$y]['suivis'] = 3; //$years[$y]['prets'] = 4; } // foreach year //debug($years); // Set all variables pour la vue //$this->set(compact('current_year', 'year_min', 'tot', 'avg', 'years', 'suivis', 'prets')); //$this->set(compact('current_year', 'year_min', 'nbyears', 'tot', 'tot2', 'avg', 'years')); // FAKE DATA POUR TEST LOCAL if ($TEST) { $current_year = 2021; $test_nb_years = 0; $test_nb_years = 1; $test_nb_years = 2; $test_nb_years = 3; $i_year = $current_year; $i_nbyears = 0; $years = []; /* $years[2019] = [ 'CREATED' => (int) 27, 'VALIDATED' => (int) 8, 'TOBEARCHIVED' => (int) 1, 'ARCHIVED' => (int) 1, 'suivis' => (int) 2, 'prets' => (int) 2 ]; */ // 1 year if ($test_nb_years > $i_nbyears) $years[$i_year] = [ 'CREATED' => (int) 27, 'VALIDATED' => (int) 8, 'TOBEARCHIVED' => (int) 1, 'ARCHIVED' => (int) 1, 'suivis' => (int) 2, 'prets' => (int) 2 ]; // 2 years if ($test_nb_years > $i_nbyears++) $years[--$i_year] = [ 'CREATED' => (int) 0, 'VALIDATED' => (int) 0, 'TOBEARCHIVED' => (int) 0, 'ARCHIVED' => (int) 0, 'suivis' => (int) 0, 'prets' => (int) 0 ]; // 3 years if ($test_nb_years > $i_nbyears++) $years[--$i_year] = [ 'CREATED' => (int) 27, 'VALIDATED' => (int) 8, 'TOBEARCHIVED' => (int) 1, 'ARCHIVED' => (int) 1, 'suivis' => (int) 2, 'prets' => (int) 2 ]; if ($test_nb_years > $i_nbyears++) $years[--$i_year] = [ 'CREATED' => (int) 27, 'VALIDATED' => (int) 8, 'TOBEARCHIVED' => (int) 1, 'ARCHIVED' => (int) 1, 'suivis' => (int) 2, 'prets' => (int) 2 ]; $nbyears = count($years); $year_min = $nbyears>0 ? min(array_keys($years)) : $current_year; $tot = [ 'CREATED' => (int) 27, 'VALIDATED' => (int) 8, 'TOBEARCHIVED' => (int) 1, 'ARCHIVED' => (int) 1, 'suivis' => (int) 2, 'prets' => (int) 2 ]; $avg = [ 'CREATED' => (float) 27, 'VALIDATED' => (float) 8, 'TOBEARCHIVED' => (float) 1, 'ARCHIVED' => (float) 1, 'suivis' => (float) 2, 'prets' => (float) 2 ]; debug($current_year); debug($year_min); debug($years); } // TEST DATA $this->set(compact('current_year', 'year_min', 'nbyears', 'tot', 'avg', 'years')); /* debug($tot); //echo json_encode($avg); debug($avg); debug($years); */ } // stats() /** * StatusArchived method * * @param string $id * @param string $from */ public function statusArchived($id = null, $from = 'index') { $this->_statusSetTo('ARCHIVED', "Le matériel a bien été archivé (sorti de l'inventaire)", $id, $from); // (EP) moved to statusSetTo() // $this->sendEmail($this->Materiels->get($id)); } /** * GetConditionForField method * * @param $fieldName string * @return string[]|NULL */ private function getConditionForField($fieldName) { $searchFieldName = 's_' . $fieldName; //if ($this->request->getData($searchFieldName) !== null && ($this->request->getData($searchFieldName) != '')) if ($this->request->getData($searchFieldName)) return [ "Materiels.$fieldName LIKE" => '%' . $this->request->getData($searchFieldName) . '%' ]; return NULL; } /** * GetConditionForFieldNumber method * * @param $fieldName string * @return string[]|NULL */ private function getConditionForFieldNumber($fieldName) { $searchFieldName = 's_' . $fieldName; //if ($this->request->getData($searchFieldName) !== null && ($this->request->getData($searchFieldName) != '')) //if ($this->request->getData($searchFieldName)) return [ "Materiels.$fieldName =" => $this->request->getData($searchFieldName) ]; $val = $this->request->getData($searchFieldName); if ($val) { // Cas spécial du champ prix_ht : il faut ajouter '.00' à la fin pour que la valeur soit trouvée !!! if ($fieldName=='prix_ht') { $val = number_format($val, 2, '.', ''); /* //debug($val); if ( // pas de point count(explode('.', $val)) == 1 && // ni de virgule count(explode(',', $val)) == 1 ) $val = $val.'.00'; */ } return [ "Materiels.$fieldName LIKE" => $val ]; } return NULL; } /* * @param string $general_search_field_name : 's_all' or 's_all2' */ private function find_general($general_search_field_name) { $all = $this->request->getData($general_search_field_name); // 's_all' or 's_all2' $conditions = $this->getFieldsConditionsForGeneralSearchOfWords($all); //debug($conditions); return $conditions; } private function getFieldsConditionsForGeneralSearchOfWords($all) { //$generalFieldConditions = NULL; // Check for a date foreach ( [ "/", "-" ] as $symb) { $nbocc = substr_count($all, $symb); //$pos1 = strpos($all, $symb); // Première occurence //$pos2 = strripos($all, $symb); // Dernière occurence //debug($nbocc); debug($symb); //if ($pos1 !== false && $pos2 !== false && $pos2 != $pos1) { if ($nbocc == 2) { list ($dd, $mm, $yyyy) = explode($symb, $all); if (checkdate((int) $mm, (int) $dd, (int) $yyyy)) { $all = "$yyyy-$mm-$dd"; break; } } } // End datecheck // Si $all = "dell pallier", ça va chercher les lignes de la table matériel qui contiennent "dell" ET "pallier" dans n'importe quelle colonne $words = explode(' ', $all); $merge = []; foreach ($words as $word) { /* (EP) * For N words in $tabSearch, this will return $merge1 AND $merge2 AND ... $mergeN * (each merge being a list of OR conditions) * Ex: * Si $tabSearch = "dell pallier", ça va chercher les lignes de la table matériel qui contiennent "dell" ET "pallier" dans n'importe quelle colonne * * Utilisation de array() [] pour pouvoir mettre plusieurs fois la meme clé "AND". */ /* $merge = array_merge($merge, [ [ 'AND' => [ 'OR' => $this->getFieldsConditionsForGeneralSearchOfWord($word) ] ] ]); //$merge = array_merge($merge, $field_conditions); */ // (EP 202007) plus simple (car le AND est automatique) array_push($merge, //'AND' => [ 'OR' => $this->getFieldsConditionsForGeneralSearchOfWord($word) ] ); } //debug($merge); return $merge; //return [ 'OR' => $merge ]; } private function getFieldsConditionsForGeneralSearchOfWord($word) { $search_str = "%$word%"; $search_fields = [ // 1) Materiels table direct (straight) columns 'Materiels.designation', 'Materiels.numero_laboratoire', 'Materiels.numero_inventaire_organisme', 'Materiels.numero_inventaire_old', 'Materiels.numero_commande', 'Materiels.description', 'Materiels.nom_user', 'Materiels.nom_responsable', 'Materiels.email_responsable', 'Materiels.code_comptable', 'Materiels.numero_serie', 'Materiels.date_acquisition', 'Materiels.lieu_detail', // 2) Materiels table foreign keys (HasOne only) 'Sites.nom', 'Fournisseurs.nom', 'Categories.nom', 'Organismes.nom', 'Projets.nom', // - Gestionnaire 'Users.nom', ]; $conditions = []; foreach ($search_fields as $sf) $conditions[$sf.' LIKE'] = $search_str; return $conditions; /* return [ /S (EP) : * Utilisation de array() [] pour pouvoir mettre plusieurs fois la meme clé. * Exemple : la clé "Materiels.designation LIKE" pourra apparaître plusieurs fois si on fait une recherche de "mac pc" * On aura : "Materiels.designation LIKE" => '%mac%' et "Materiels.designation LIKE" => '%pc%' * Sinon on aurait uniquement eu : "Materiels.designation LIKE" => '%pc%' S/ // 1) Materiels table direct (straight) columns //[ 'Materiels.designation LIKE' => $search_str, //], //[ 'Materiels.numero_laboratoire LIKE' => $search_str, //], 'Materiels.numero_inventaire_organisme LIKE' => $search_str, 'Materiels.numero_inventaire_old LIKE' => $search_str, 'Materiels.numero_commande LIKE' => $search_str, 'Materiels.description LIKE' => $search_str, //[ 'Materiels.nom_responsable LIKE' => $search_str ], 'Materiels.nom_responsable LIKE' => $search_str, 'Materiels.email_responsable LIKE' => $search_str, 'Materiels.code_comptable LIKE' => $search_str, 'Materiels.numero_serie LIKE' => $search_str, 'Materiels.date_acquisition LIKE' => $search_str, 'Materiels.lieu_detail LIKE' => $search_str, // 2) Materiels table foreign keys 'Fournisseurs.nom LIKE' => $search_str, 'Categories.nom LIKE' => $search_str, 'Organismes.nom LIKE' => $search_str, 'Projets.nom LIKE' => $search_str, ]; */ } private function find_specific_fields() { // Materiel type $matostype = $this->request->getData('s_matostype'); $matostypeRequest = NULL; switch ($matostype) { // Administratif case 'A': $matostypeRequest['Materiels.materiel_administratif ='] = '1'; break; // Technique case 'T': $matostypeRequest['Materiels.materiel_technique ='] = '1'; break; // Admin et Tech case 'AT': $matostypeRequest['Materiels.materiel_administratif ='] = '1'; $matostypeRequest['Materiels.materiel_technique ='] = '1'; break; // Admin ONLY case 'AO': $matostypeRequest['Materiels.materiel_administratif ='] = '1'; $matostypeRequest['Materiels.materiel_technique ='] = '0'; break; // Tech ONLY case 'TO': $matostypeRequest['Materiels.materiel_administratif ='] = '0'; $matostypeRequest['Materiels.materiel_technique ='] = '1'; break; } $periode_acquisitionRequest = NULL; $date_acquisition = NULL; $salle = NULL; $fournisseur = NULL; if ($this->request->getData('s_periode_acquisition1') != '') $periode_acquisitionRequest['Materiels.date_acquisition >='] = date('Y-m-d', strtotime(str_replace('/', '-', $this->request->getData('s_periode_acquisition1')))); if ($this->request->getData('s_periode_acquisition2') != '') $periode_acquisitionRequest['Materiels.date_acquisition <='] = date('Y-m-d', strtotime(str_replace('/', '-', $this->request->getData('s_periode_acquisition2')))); if ($this->request->getData('s_date_acquisition') != '') $date_acquisition['Materiels.date_acquisition LIKE'] = '%' . date('Y-m-d', strtotime(str_replace('/', '-', $this->request->getData('s_date_acquisition')))) . '%'; $montantRequest = []; if ($this->request->getData('s_prix_ht_inf') != '') $montantRequest['Materiels.prix_ht <='] = $this->request->getData('s_prix_ht_inf'); if ($this->request->getData('s_prix_ht_sup') != '') $montantRequest['Materiels.prix_ht >='] = $this->request->getData('s_prix_ht_sup'); if ($this->request->getData('s_salle') !== null && $this->request->getData('s_salle') != '') $salle['Materiels.lieu_detail LIKE'] = '%' . $this->request->getData('s_salle') . '%'; if ($this->request->getData('s_fournisseur_id') !== null && $this->request->getData('s_fournisseur_id') != '') $fournisseur['Materiels.fournisseur_id ='] = $this->request->getData('s_fournisseur'); /* TODO: (EP 2019/01) NOUVEAU CODE POUR REMPLACER LE CODE SUPER REDONDANT D'AVANT (300 lignes !!!) */ $designation = NULL; if ($this->request->getData('s_designation')) { //$designation_words = []; //$designation_words = explode (" ", $this->request->getData('s_designation')); $designation = str_replace(" ","%", $this->request->getData('s_designation')); $designation = [ 'Materiels.designation LIKE' => '%'.$designation.'%' ]; #TODO: gérer un peu mieux le cas où le champ designation contient plusieurs mots: en cherchant chaque mot séparément (avec un "OU") : /* $designation_conditions = []; for ($i = 0; $i < count($designation); $i++) { //'Materiels.designation LIKE' => '%' . $designation[0]. '%', //'Materiels.designation LIKE' => '%' . $designation[1]. '%', $designation_conditions['Materiels.designation LIKE'] = '%'.$designation_words[$i].'%'); } */ } // Putting all these specific fields together $specificFieldsConditions = [ //'Materiels.designation LIKE' => '%' . $designation[0] . '%', //'Materiels.designation LIKE' => '%' . $this->request->getData('s_designation'). '%', $designation, //'Materiels.numero_laboratoire LIKE' => '%' . $this->request->getData('s_numero_laboratoire') . '%', $this->getConditionForField('numero_laboratoire'), $this->getConditionForField('numero_commande'), $date_acquisition, $periode_acquisitionRequest, $this->getConditionForFieldNumber('prix_ht'), $montantRequest, $this->getConditionForFieldNumber('sur_categorie_id'), $this->getConditionForFieldNumber('categorie_id'), $this->getConditionForFieldNumber('sous_categorie_id'), $this->getConditionForFieldNumber('gestionnaire_id'), $this->getConditionForField('nom_responsable'), $this->getConditionForField('numero_inventaire_organisme'), $this->getConditionForField('numero_inventaire_old'), $this->getConditionForFieldNumber('groupes_metier_id'), $this->getConditionForFieldNumber('groupes_thematique_id'), $salle, //$fournisseur, $this->getConditionForFieldNumber('fournisseur_id'), $this->getConditionForFieldNumber('organisme_id'), $matostypeRequest, $this->getConditionForFieldNumber('organisme_id'), $matostypeRequest, $this->getConditionForField('status'), ]; //debug($specificFieldsConditions); return $specificFieldsConditions; } // find_specific_fields() /* ******************** * Create all needed LISTS and pass them to the VIEW (find) * ******************** */ private function set_elements_lists_for_view_find() { $s_sur_categories = $this->Materiels->SurCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'SurCategories.nom' ]); $s_categories = $this->Materiels->Categories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Categories.nom' ]); $s_sous_categories = $this->Materiels->SousCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'SousCategories.nom' ]); $s_groupes_thematiques = $this->Materiels->GroupesThematiques->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'GroupesThematiques.nom' ]); $s_groupes_metiers = $this->Materiels->GroupesMetiers->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'GroupesMetiers.nom' ]); $s_organismes = $this->Materiels->Organismes->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Organismes.nom' ]); $s_fournisseurs = $this->Materiels->Fournisseurs->find('list', [ 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'Fournisseurs.nom' ]); //debug($s_fournisseurs); exit; $s_salles = $this->Materiels->find('list', [ 'keyField' => 'lieu_detail', 'valueField' => 'lieu_detail', 'order' => 'lieu_detail', 'conditions' => array( 'status !=' => 'ARCHIVED', 'lieu_detail !=' => 'null' ) ]); // (EP 202009) TOUS les Gestionnaires $s_nom_gest = $this->Materiels->Users->find('list', [ //'keyField' => 'nom', 'keyField' => 'id', 'valueField' => 'nom', 'order' => 'nom', /* 'conditions' => array( 'nom !=' => 'SUPERADMIN FAKE_LDAP', ) */ ]) ->where([ 'nom !=' => 'SUPERADMIN FAKE_LDAP', 'role =' => 'Administration' ]); // (EP 202007) TOUS les utilisateurs (même ceux qui n'ont aucun matériel) $s_nomresp = $this->Materiels->Users->find('list', [ 'keyField' => 'nom', 'valueField' => 'nom', 'order' => 'nom', 'conditions' => array( 'nom !=' => 'SUPERADMIN FAKE_LDAP', ) ]); // Uniquement les utilisateurs qui ont au moins 1 matériel /* $s_nomresp = $this->Materiels->find('list', [ 'keyField' => 'nom_responsable', 'valueField' => 'nom_responsable', 'order' => 'nom_responsable', 'conditions' => array( 'status !=' => 'ARCHIVED', 'nom_responsable !=' => 'null' ) ]); */ /* (EP) Depuis le 2/4/19, cette variable n'est plus utilisée * car on préfère la recherche plein texte plutot que dans une liste * On pourrait donc mettre en commentaire, mais bon... */ $s_numero_laboratoire = $this->Materiels->find('list', [ 'keyField' => 'numero_laboratoire', 'valueField' => 'numero_laboratoire', 'order' => 'numero_laboratoire', 'conditions' => array( 'status !=' => 'ARCHIVED', 'numero_laboratoire !=' => 'null' ) ]); $categories = $this->Materiels->Categories; // Pass all these LISTS to the view : $this->set(compact( 's_numero_laboratoire', 's_nom_gest', 's_nomresp', 's_sur_categories', 's_categories', 's_sous_categories', 'categories', 's_groupes_thematiques', 's_groupes_metiers', 's_organismes', 's_fournisseurs', 's_salles' )); } // set_elements_lists_for_view_find() private function paginateResults($results, $nbElementsParPage=null) { //$sort_list = MaterielsController::SORT_LIST_FOR_SEARCH; $sort_list = self::SORT_LIST_FOR_SEARCH; // - Nb éléments par page (20 par défaut si on dit rien) $sort_list['limit'] = $nbElementsParPage; // - Nb éléments en tout (on en récupère pas plus pour éviter de saturer la RAM) //$sort_list['maxLimit'] = $LIMIT_MAX; return $this->paginate($results, $sort_list); /* [ 'limit' => $limit, 'maxLimit' => $LIMIT_MAX, 'sortWhitelist' => [ // - Champs directs 'designation', 'numero_laboratoire', // tutelle 'numero_inventaire_organisme', 'numero_commande', //'nom_responsable', 'nom_user', 'status', 'date_acquisition', 'prix_ht', //'etiquette', //'lieu_detail', // - Champs FK (HasOne only) 'Sites.nom', 'Categories.nom', //'Organismes.nom', // Gestionnaire 'Users.nom', 'Fournisseurs.nom', ], 'order' => [ 'numero_laboratoire' => 'desc' // ATTENTION, écrit comme ceci ca rentrait en conflit avec la colonne de tri numero_laboratoire... //'Materiels.numero_laboratoire' => 'desc' ], ]); */ } // paginateResults() /** * Find method * (EP) tout refait le 18/1/17 pour que ça soit plus lisible et moins bugué... */ public function find() { //$LIMIT_MAX = 1000; $this->set_elements_lists_for_view_find(); // Get the previous search result (from session) set in the view (find.ctp) $resultTri = $this->request->getSession()->read("resultTri"); // Do not show Archived materiel if not Admin $conditionNotArchived = $this->USER_IS_ADMIN_AT_LEAST() ? '' : [ 'Materiels.status !=' => 'ARCHIVED' ]; // if (! (in_array($this->role, ['Administration', 'Administration Plus', 'Super Administrateur']))) /* ******************** * Plusieurs cas possibles: * - (1) SI POST (getData()) : * - (1.1) WITH DATA (au moins un champ rempli, avec ou sans critère de tri) : * - (1.1.1) recherche globale (s_all ou s_all2) : * - (1.1.1.1) s_all2 (champ de recherche global en bas à gauche) => recherche globale (toutes les tables) * - (1.1.1.2) s_all (champ de recherche général matériel tout au début du formulaire de recherche, en haut de page) => recherche générale sur la table matériel (et tables liées) * - (1.1.2) recherche spécifique : 1 ou plusieurs champ(s) spécifique(s) (s_designation, ...) => recherche sur champs spécifiques * - (1.2) NO DATA, only empty fields : Une recherche a été soumise sans remplir aucun champ !! => ne rien faire * - (2) SINON : * - (2.1) avec critères de tri => sort_result() : Devant le résultat d'une recherche, on a simplement cliqué sur une colonne pour afficher le résultat TRIÉ (sorted) * - (2.2) sans critère de tri => c'est la première fois qu'on vient sur la vue, rien à faire de plus * ******************** */ // (1) POST if ($this->request->is('post')) { $has_data = FALSE; foreach ($this->request->getData() as $k=>$v) { if ($v != '') { $has_data = TRUE; break; } } // (1.1) POST with data if ($has_data) { ///debug("post with data"); // (1.1.1) Recherche globale (s_all_2 ou s_all) : /* * TODO: Distinguer la recherche globale dans TOUTES les tables (s_all_2) de la recherche générale seulement dans la table matériel (s_all) * - Pour le moment, les résultats sont les mêmes pour ces 2 champs (normal, on fait le même traitement), c'est pas normal !!! * - Recherche s_all ok : il faudrait juste améliorer la recherche dans toutes les tables liées au matériel (fournisseur, organismes, catégories...) * - Recherche s_all_2 pas faite : à inventer... * - Ajouter des tests de recherche (nombreux cas possibles) */ if ( $this->request->getData('s_all_2') || $this->request->getData('s_all') ) { // - Recherche globale // debug("recherche globale"); exit; $general_search_field_name = $this->request->getData('s_all_2') ? 's_all_2' : 's_all'; $conditions = $this->find_general($general_search_field_name); //debug($conditions); exit; /* if ($this->request->getData('s_all_2')) debug("s_all2, recherche globale (toutes les tables)"); else if ($this->request->getData('s_all')) debug("s_all, recherche générale dans la table des matériels (et tables associées)"); */ } // (1.1.2) recherche spécifique : 1 ou plusieurs champ(s) spécifique(s) (s_designation, ...) => recherche sur champs spécifiques else { // - Recherche de champs spécifiques dans la table des matériels ///debug("recherche de champs spécifiques dans la table des matériels"); $conditions = $this->find_specific_fields(); } // CONSTRUCTION DE LA REQUETE SQL COMPLETE = $specificFieldsConditions OR $generalFieldConditions (mais entre chaque champ, c'est un AND) // by default, no sort $conditions = [ $conditions, $conditionNotArchived ]; //debug($conditions); exit; $lastResults = $this->Materiels->find('all', [ 'conditions' => $conditions, 'contain' => MaterielsController::CONTAIN_FOR_SEARCH, ])->limit(self::LIMIT_MAX); /* $this->paginate = [ 'maxLimit' => $LIMIT_MAX, 'limit' => $LIMIT_MAX ]; //debug($lastResults); exit; $_results = $this->paginate($lastResults); */ try { $_results = $this->paginateResults($lastResults, self::LIMIT_MAX); } catch (\PDOException $e) { debug("Mauvais format de requete SQL (0), Exception PDO générée !"); exit; } /* try { $_results = $this->paginate($lastResults, [ 'limit' => $LIMIT_MAX, 'maxLimit' => $LIMIT_MAX, 'sortWhitelist' => [ // - Champs directs 'designation', 'numero_laboratoire', // tutelle 'numero_inventaire_organisme', 'numero_commande', //'nom_responsable', 'nom_user', 'status', 'date_acquisition', 'prix_ht', //'etiquette', //'lieu_detail', // - Champs FK (HasOne only) 'Sites.nom', 'Categories.nom', //'Organismes.nom', // Gestionnaire 'Users.nom', 'Fournisseurs.nom', ], 'order' => [ 'numero_laboratoire' => 'desc' // ATTENTION, écrit comme ceci ca rentrait en conflit avec la colonne de tri numero_laboratoire... //'Materiels.numero_laboratoire' => 'desc' ], ]); } catch (\PDOException $e) { debug("Mauvais format de requete SQL (0), Exception PDO générée !"); exit; } */ $this->set(compact('_results')); // pour l'export $this->request->getSession()->write("result", $lastResults->toArray()); $this->set('statuses_color', self::statuses_color); // Pas bien..., mais pratique : // on passe le controleur de materiels à la vue index pour qu'elle // puisse appeler la methode isAuthorizedAction() $this->set("controller", $this); } // $has_data // (1.2) POST without data else { ///debug("post without data => do nothing"); // with sort field => trier un résultat // without sort field => c'est la première visite de cette vue } } // (2) no POST (soit c'est la première venue sur la vue, soit on a cliqué sur une colonne pour demander un tri) else { ///debug("no POST"); // (2.1) AVEC critère de tri (sort) => seulement demandé un tri du résultat, no data posted /* * TODO: on pourrait faire autrement: * A chaque fois qu'on fait un tri sur une colonne, * au lieu de sauvegarder le résultat de la recherche dans une variable de session puis recharger ce résultat en mémoire pour le trier, * il vaudrait mieux relancer la recherche à chaque fois, mais juste changer le critère de tri, c'est plus simple, plus naturel */ //debug($this->request->getRequestTarget()); //if ($resultTri!==null && strstr($this->request->here(),'sort')!=false && strstr($this->request->here(),'direction')!=false) { if ($resultTri !== null) { //if ( strstr($this->request->here(),'sort')!=false && strstr($this->request->here(),'direction')!=false ) { $foundMateriel = []; foreach ($resultTri as $r) { array_push($foundMateriel, $r->id); } /* $res = $this->Materiels->find('all', [ //'contain' => ['Categories', 'Fournisseurs'], 'limit' => 1000 ]); for ($i = 0; $i < sizeof($foundMateriel); $i ++) { $res->orWhere([ 'id =' => $foundMateriel[$i] ]); } */ $res = $this->Materiels->find() ->limit(self::LIMIT_MAX) // (EP) Attention, si on ajoute un "->contain", on devra utiliser 'Materiels.id' et non pas 'id' tout seul (ci-dessous) //->contain(['Categories', 'Fournisseurs', 'Organismes']); ->contain(MaterielsController::CONTAIN_FOR_SEARCH); //->firstOrFail(); for ($i=0; $iorWhere([ // Ca plante si on met que 'id' => action impossible, tu parles d'un message parlant !!! // OK seulement si pas de ->contain() //'id =' => $foundMateriel[$i] // Si on utilise ->contain(), il faut préciser 'Materiels.id' 'Materiels.id =' => $foundMateriel[$i] ]); } /* $this->paginate = [ 'maxLimit' => $LIMIT_MAX, 'limit' => $LIMIT_MAX ]; $_results = $this->paginate($res); */ try { $_results = $this->paginateResults($res, self::LIMIT_MAX); } catch (\PDOException $e) { debug("Mauvais format de requete SQL (0), Exception PDO générée !"); exit; } /* try { $_results = $this->paginate($res, [ 'limit' => $LIMIT_MAX, 'maxLimit' => $LIMIT_MAX, 'sortWhitelist' => [ // - Champs directs 'designation', 'numero_laboratoire', // tutelle 'numero_inventaire_organisme', 'numero_commande', //'nom_responsable', 'nom_user', 'status', 'date_acquisition', 'prix_ht', //'etiquette', //'lieu_detail', // - Champs FK (HasOne only) 'Sites.nom', 'Categories.nom', //'Organismes.nom', // Gestionnaire 'Users.nom', 'Fournisseurs.nom', ], 'order' => [ 'numero_laboratoire' => 'desc' // ATTENTION, écrit comme ceci ca rentrait en conflit avec la colonne de tri numero_laboratoire... //'Materiels.numero_laboratoire' => 'desc' ], ]); } catch (\PDOException $e) { debug("Mauvais format de requete SQL (0), Exception PDO générée !"); exit; } */ $this->set(compact('_results')); // pour l'export $this->request->getSession()->write("result", $res->toArray()); $this->set('statuses_color', self::statuses_color); // Pas bien..., mais pratique : // on passe le controleur de materiels à la vue index pour qu'elle // puisse appeler la methode isAuthorizedAction() $this->set("controller", $this); //} } // (2.2) SANS critère de tri => c'est la première fois qu'on vient sur la vue, rien à faire de plus else { ///debug("1ère venue sur la vue"); } } // no POST } // find() /** * group update status + exportAll */ public function execActions() { // var_dump($this->request->getData('updateSelectedStatus')); if ($this->request->getData('updateSelectedStatus') !== null) $this->updateSelectedStatus(); else $this->export(); } /** * group update status (selected from materiels/index) */ private function updateSelectedStatus() { /* * var_dump($nb); * var_dump($this->request->getData); * var_dump($this->request->getData('updateSelectedStatus')); * var_dump($this->request->getData()); */ //if (isset($this->request->getData()) && sizeof($this->request->getData()) > 0) { $data = $this->request->getData(); if (sizeof($data) > 0) { //debug($data); exit; $this->myDebug("IN UPDATE"); $this->myDebug($this->request->getData()); $what = $this->request->getData('what'); $nb = 0; if (in_array($what, $this->NOTARCHIVED)) { foreach ($data as $id => $value) { // seulement les post data integers (id des materiels checked ou non) if (! is_int($id)) continue; // seulement les id des materiels CHECKED (==1) if ($value == 0) continue; $materiel = $this->Materiels->get($id); $materielIdentification = $materiel->designation . '-' . $materiel->numero_laboratoire; switch ($what) { case 'CREATED': case 'TOBEORDERED': $new = 'VALIDATED'; //$msgError = "le materiel " . $materielIdentification . " n'a pas pu être commandé (un champ obligatoire est manquant)"; $msgError = "le materiel " . $materielIdentification . " n'a pas pu être validé (un champ obligatoire est manquant)"; break; case 'VALIDATED': $new = 'TOBEARCHIVED'; $msgError = "La demande de sortie de l'inventaire du matériel " . $materielIdentification . " n'a pas pu s'effectuer."; break; case 'TOBEARCHIVED': $new = 'ARCHIVED'; $msgError = "le materiel " . $materielIdentification . " n'a pas pu être archivé."; break; } // if mode_debug desactivate (POURQUOI ???) if (! $this->isLabinventDebugMode()) { if (! $this->_statusSetTo($new, 'Le matériel a bien été ???', $id, 'index', false)) $nb --; /* * //debug($materiel); exit; * if ($materiel->nom_responsable === null || $materiel->fournisseur_id === null || $materiel->numero_commande === null || $materiel->organisme_id === null || $materiel->date_reception === null || $materiel->prix_ht === null) { * $this->Flash->error($msgError); * $nb --; * } else { * $materiel->set('status', $new); * $this->Materiels->save($materiel, ['checkRules' => false, 'checkExisting' => false]); * } */ } $nb ++; } // foreach if ($nb > 0) $this->Flash->success(__($nb . ' matériel(s) mis à jour')); $this->myDebug("Nb matos = " . $nb); if (! $this->isLabinventDebugMode()) return $this->redirect([ 'action' => 'index', 'page' => $this->request->getQuery('page'), $what ]); } } } // find() /** * Export method * Generation d'un fichier .csv comportant la date du jour * CSV export of the list of materiels * This action is called from 3 different sources : * - tools * - materiels/find * - materiels/index (from the 2 submit green buttons named "Exporter la liste...") * Optional parameter "what:value" may be passed when calling this action, and is read from $this->getAttribute('params')['named']['what'] * ex : materiels/export/what:toValidate */ public function export($param = null) { function getElementFromList($listName, $materiel, $fkName) { $ISOK = ( !is_null($materiel->$fkName) && !is_null($listName[$materiel->$fkName]) ); return $ISOK ? $listName[$materiel->$fkName] : ""; } //$materiels = NULL; $materiels = []; $surCategories = $this->Materiels->SurCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ])->toArray(); $categories = $this->Materiels->Categories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ])->toArray(); $sousCategories = $this->Materiels->SousCategories->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ])->toArray(); $groupesThematiques = $this->Materiels->GroupesThematiques->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ])->toArray(); $groupesMetiers = $this->Materiels->GroupesMetiers->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ])->toArray(); $organismes = $this->Materiels->Organismes->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ])->toArray(); $sites = $this->Materiels->Sites->find('list', [ 'keyField' => 'id', 'valueField' => 'nom' ])->toArray(); $what = ''; $this->myDebug($this->request->getData()); // Critères pour requete sql sur materiels $contain = [ 'Fournisseurs', 'Users', // Pour les gestionnaires (gestionnaire_id) ]; $order = ['numero_laboratoire' => 'DESC']; $CAS = 0; //debug($this->request->getData()); exit; // CAS 1 - EXPORT uniquement les materiels selectionnés (cochés) if ($this->request->getData('export') !== null) { $CAS = 1; $this->myDebug("IN EXPORT"); $what = $this->request->getData('what'); $selectedMateriels = []; $i = 0; //$data = $this->request->getData(); $data = $this->request->getData(); foreach ($data as $val) { if ($val == 1) { $selectedMateriels[$i] = key($data); $i ++; } next($data); } $this->myDebug($selectedMateriels); //debug($selectedMateriels); exit; /* // Si la liste de materiels cochés est vide, ne rien faire if (empty($selectedMateriels)) { if ($what != '' && $what != 'search') return $this->redirect(['action' => 'index', $what]); else if ($what == 'search') return $this->redirect('javascript:window.history.go(-3)'); else return $this->redirect(['action' => 'index']); } */ //if (sizeof($selectedMateriels) > 0) { if (! empty($selectedMateriels)) { /* $materiels = $this->Materiels->find('all') ->where(['Materiels.id' => $selectedMateriels[0]]) ->contain($contain); for ($j = 0; $j < sizeof($selectedMateriels); $j ++) { $materiels->orWhere([ 'Materiels.id' => $selectedMateriels[$j] ]); } */ //$materielsTable = TableRegistry::getTableLocator()->get('Materiels'); //$materiels = $materielsTable->find() $materiels = $this->Materiels->find() ->contain($contain) ->where(['Materiels.id' => $selectedMateriels[0]]); for ($j = 0; $j < sizeof($selectedMateriels); $j ++) $materiels->orWhere( ['Materiels.id' => $selectedMateriels[$j]] ); $materiels->order($order); /* $materiels = $this->Materiels->find() ->contain($contain) ->where([ //'OR' => [['id' => 2], ['id' => 10]], 'OR' => [$selectedMateriels], ]); */ } //else debug("vide"); exit; //$this->myDebug($materiels); //} } // CAS 2 - EXPORT liste courante // (cette fonction export() a été appelée par index()) //else if (is_array($param) && len($param)>0) { else if ($this->request->getQuery('exportcurrent') !== null && ($param->count())>0) { //foreach ($param as $m) debug($m->designation); exit; $CAS = 2; $this->myDebug("IN exportCurrent"); $materiels = $param; //debug($materiels->toArray()); exit; } // CAS 2 // CAS 3 - EXPORT TOUS les matériels de la BD else if ($this->request->getData('exportAll') !== null) { $CAS = 3; $this->myDebug("IN EXPORT ALL"); // Exporte seulement les materiels du statut coché if ($this->request->getData('what') !== null) { $what = $this->request->getData('what'); $status = $what; /* $materiels = $this->Materiels->find('all', [ 'conditions' => [ 'Materiels.status =' => $status ], 'contain' => $contain ]); */ $materiels = $this->Materiels ->find() ->where(['Materiels.status =' => $status]) ->contain($contain) ->order($order); // Exporte seulement TOUS les materiels (sauf ARCHIVED) } else { $status = 'ARCHIVED'; /* $materiels = $this->Materiels->find('all', [ 'conditions' => [ 'Materiels.status !=' => $status ], 'contain' => $contain ]); */ $materiels = $this->Materiels ->find() ->where(['Materiels.status !=' => $status]) ->contain($contain) ->order($order); } $this->myDebug("what is " . $what); $this->myDebug("statut is " . $status); } // CAS 4 - Export du résultat de Recherche (find) else if ( $param == 'search' // C'est la meme chose que ci-dessus, mais bon... && isset($this->request->getAttribute('params')['pass'][0]) && $this->request->getAttribute('params')['pass'][0] == 'search' ) { $CAS = 4; $this->myDebug("RECHERCHE"); $what = $this->request->getAttribute('params')['pass'][0]; //if ($what == 'search') { $materiels = $this->request->getSession()->read("result"); //} } // CAS 5 (???) - Tous les matériels (sauf ARCHIVED) else { $CAS = 5; $this->myDebug("OTHER CASE 4"); /* $materiels = $this->Materiels->find('all', [ 'conditions' => [ 'Materiels.status !=' => 'ARCHIVED' ], 'contain' => $contain ]); */ $materiels = $this->Materiels ->find() ->where(['Materiels.status !=' => 'ARCHIVED']) ->contain($contain) ->order($order); } // (EP) Si aucun materiel retourné par la sélection => terminé, ne rien faire, basta if (empty($materiels)) { //debug($what); exit; if ($what != '' && $what != 'search') return $this->redirect(['action' => 'index', $what]); else if ($what == 'search') return $this->redirect('javascript:window.history.go(-3)'); //else return $this->redirect(['action' => 'index']); } /* // (EP ) Tri des materiels par numéro inventaire $num_labos = array_column($materiels->toArray(), 'numero_laboratoire'); // Ajoute $materiels en tant que dernier paramètre, pour trier par la clé commune array_multisort($num_labos, SORT_DESC, $materiels->toArray()); */ // NOW, CREATE THE CSV file from $materiels //debug($materiels[0]);exit; if ($CAS == 4) // Tableau de materiels, chaque materiel étant un objet query $this->myDebug("Nb matos = " . count($materiels)); else // Objet query $this->myDebug("Nb matos = " .$materiels->count()); /* foreach ($materiels as $result) $this->myDebug($result); exit; */ //$this->myDebug($materiels, true); //$this->myDebug($materiels); $configuration = $this->confLabinvent; $nomgroupemetier = $configuration->nom_groupe_metier; $nomgroupethematique = $configuration->nom_groupe_thematique; $nomgroupemetier = htmlentities($nomgroupemetier, ENT_NOQUOTES, 'utf-8'); $nomgroupemetier = preg_replace('#&([A-za-z])(?:acute|cedil|caron|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $nomgroupemetier); $nomgroupemetier = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $nomgroupemetier); // pour les ligatures e.g. 'œ' $nomgroupemetier = preg_replace('#&[^;]+;#', '', $nomgroupemetier); // supprime les autres caractères $nomgroupethematique = htmlentities($nomgroupethematique, ENT_NOQUOTES, 'utf-8'); $nomgroupethematique = preg_replace('#&([A-za-z])(?:acute|cedil|caron|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $nomgroupethematique); $nomgroupethematique = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $nomgroupethematique); // pour les ligatures e.g. 'œ' $nomgroupethematique = preg_replace('#&[^;]+;#', '', $nomgroupethematique); // supprime les autres caractères //"id", $header_row = [ "Désignation", "Sur-catégorie", "Catégorie", "Sous-catégorie", "Numéro interne", "Description", "Organisme", "Mat. administratif", "Mat. technique", "Statut", "Date d'acquisition", "Date de réception", "Fournisseur", "Prix HT", "EOTP", "Numéro de commande", "Code comptable", "Numéro de série", $nomgroupethematique, $nomgroupemetier, "Numéro inventaire organisme", "Ancien Numéro inventaire", "Site stockage", "Gestionnaire", "Nom responsable", "Email responsable" ]; // DEBUG = true => n'envoie pas le fichier, l'affiche seulement $DEBUG=false; //$DEBUG=true; !$DEBUG && ini_set('max_execution_time', 600); $filename = 'export_' . date("Y-m-d") . '.csv'; !$DEBUG && $csv_file = fopen('php://output', 'w'); !$DEBUG && $this->response->header([ // CRAL: //'Content-Type: text/csv' 'Content-Type: application/csv', //CRAL: //"Content-disposition: attachment; filename=Demande_bureaux_" . date("Ymd").".csv"); 'Content-Disposition: attachment; filename="' . $filename . '"' ]); !$DEBUG && $this->response->sendHeaders(); // 1) Write HEADER row !$DEBUG && fputcsv($csv_file, $header_row, ';'); // 2) Write DATA rows, 1 by 1 foreach ($materiels as $result) { //$this->myDebug($result); $row = [ //utf8_encode($result->id), $result->designation ]; array_push($row, getElementFromList($surCategories, $result, 'sur_categorie_id')); /* $fkName = 'sur_categorie_id'; $listName = $surCategories; $ISOK = ( !is_null($result->$fkName) && !is_null($listName[$result->$fkName]) ); array_push($row, $ISOK ? $listName[$result->$fkName] : ""); */ /* if ($surCategories[$result->sur_categorie_id] !== null) { array_push($row, $surCategories[$result->sur_categorie_id]); } else { array_push($row, ""); } */ array_push($row, getElementFromList($categories, $result, 'categorie_id')); /* $fkName = 'categorie_id'; $listName = $categories; $ISOK = ( !is_null($result->$fkName) && !is_null($listName[$result->$fkName]) ); array_push($row, $ISOK ? $listName[$result->$fkName] : ""); //if ($categories[$result->categorie_id] !== null) { if ($result->categorie_id !== null && $categories[$result->categorie_id] !== null) { array_push($row, $categories[$result->categorie_id]); } else { array_push($row, ""); } */ array_push($row, getElementFromList($sousCategories, $result, 'sous_categorie_id')); /* $fkName = 'sous_categorie_id'; $listName = $sousCategories; $ISOK = ( !is_null($result->$fkName) && !is_null($listName[$result->$fkName]) ); array_push($row, $ISOK ? $listName[$result->$fkName] : ""); if ($result->sous_categorie_id !== null && $sousCategories[$result->sous_categorie_id] !== null) { array_push($row, $sousCategories[$result->sous_categorie_id]); } else { array_push($row, ""); } */ array_push($row, $result->numero_laboratoire); array_push($row, $result->description); array_push($row, getElementFromList($organismes, $result, 'organisme_id')); /* $fkName = 'organisme_id'; $listName = $organismes; $ISOK = ( !is_null($result->$fkName) && !is_null($listName[$result->$fkName]) ); array_push($row, $ISOK ? $listName[$result->$fkName] : ""); if ( !is_null($result->organisme_id) && !is_null($organismes[$result->organisme_id] ) ) { array_push($row, $organismes[$result->organisme_id]); } else { array_push($row, ""); } */ array_push($row, $result->materiel_administratif); array_push($row, $result->materiel_technique); array_push($row, $result->status); array_push($row, $result->date_acquisition); array_push($row, $result->date_reception ? $result->date_reception : ''); //array_push($row, $result->fournisseur); array_push($row, $result->fournisseur_id ? $result->fournisseur->nom:''); array_push($row, $result->prix_ht); array_push($row, $result->eotp); array_push($row, $result->numero_commande ? $result->numero_commande:''); array_push($row, $result->code_comptable); array_push($row, $result->numero_serie); array_push($row, getElementFromList($groupesThematiques, $result, 'groupes_thematique_id')); /* $fkName = 'groupes_thematique_id'; $listName = $groupesThematiques; $ISOK = ( !is_null($result->$fkName) && !is_null($listName[$result->$fkName]) ); array_push($row, $ISOK ? $listName[$result->$fkName] : ""); */ array_push($row, getElementFromList($groupesMetiers, $result, 'groupes_metier_id')); /* $fkName = 'groupes_metier_id'; $listName = $groupesMetiers; $ISOK = ( !is_null($result->$fkName) && !is_null($listName[$result->$fkName]) ); array_push($row, $ISOK ? $listName[$result->$fkName] : ""); if ($groupesMetiers[$result->groupes_metier_id] !== null) { array_push($row, $groupesMetiers[$result->groupes_metier_id]); } else { array_push($row, ""); } */ array_push($row, $result->numero_inventaire_organisme); array_push($row, $result->numero_inventaire_old); array_push($row, getElementFromList($sites, $result, 'site_id')); /* $fkName = 'site_id'; $listName = $sites; $ISOK = ( !is_null($result->$fkName) && !is_null($listName[$result->$fkName]) ); array_push($row, $ISOK ? $listName[$result->$fkName] . '-' . $result->lieu_detail : ""); if ($sites[$result->site_id] !== null) { array_push($row, $sites[$result->site_id] . '-' . $result->lieu_detail); } else { array_push($row, ""); } */ array_push($row, $result->gestionnaire_id ? $result->user->nom:''); array_push($row, $result->nom_responsable); array_push($row, $result->email_responsable); $DEBUG && debug($row); //$DEBUG && exit; !$DEBUG && fputcsv($csv_file, $row, ';', '"'); } sleep(3); !$DEBUG && fclose($csv_file); exit(); // Sans le exit() ça ne fonctionne pas pour des petites listes, et dans tout les cas une ligne en javascript est écrite si il n'y a pas exit() // La redirection suivante fonctionne parfaitement, mais inutile à cause du exit() /* * if ($what != '' && $what != 'search') return $this->redirect(['action' => 'index', $what]); * else if ($what == 'search') return $this->redirect('javascript:window.history.go(-3)'); * else return $this->redirect(['action' => 'index']); */ } /** * SetLabelIsPlacedOrNotPlaced method * * @param string $id * @param string $from * @param string $printed * @return \Cake\Http\Response|NULL */ private function _setLabelIsPlacedOrNotPlaced($id, $from=null, $printed = TRUE) { //debug("id $id from $from"); exit; //$materiel = $this->Materiels->get($id)->set('etiquette', $printed); $materiel = $this->Materiels->get($id); $materiel->etiquette = $printed; //debug($materiel); exit; if ($this->Materiels->save($materiel, [ 'checkRules' => false, 'checkExisting' => false ])) { //debug("save OK"); $verb = $printed ? 'a' : "n'a pas"; $this->Flash->success(__("Je prends note que l'étiquette $verb été collée sur le matériel")); /* // Envoi email seulement si etiquette posée if ($printed) { // $this->sendEmailToManagement($id); //(EP202009) //$this->sendEmail($materiel); $this->sendmail($materiel); } */ } else { //debug("save KO"); $this->Flash->error(__("Il y a eu un problème lors de la sauvegarde du statut 'étiquette collée' !")); } // Retour à la vue d'origine return $this->redirect($this->referer()); } /** * SetLabelIsPlaced method * * @param string $id * @param string $from */ public function setLabelIsPlaced($id, $from=null) { //debug("id $id from $from"); exit; $this->_setLabelIsPlacedOrNotPlaced($id, $from, TRUE); } /** * SetLabelIsNotPlaced method * * @param string $id * @param string $from */ public function setLabelIsNotPlaced($id, $from=null) { $this->_setLabelIsPlacedOrNotPlaced($id, $from, FALSE); } /** * PrintLabelRuban method * * @param string $id * @param string $from */ public function printLabelRuban($id, $from = 'view') { // (EP 202006) ANCIENNE METHODE => ne nécessite pas de template Materiels/[xml/]print_label_ruban.ctp //$this->OLD_printLabel('ruban', $id, $from); // (EP 202006) NEW METHODE => nécessite le template Materiels/xml/print_label_ruban.ctp $this->printLabel('ruban', $id, $from); //(EP202009) //$this->sendEmail($this->Materiels->get($id)); ////$this->sendmail($this->Materiels->get($id)); } /** * PrintLabelEtiquette method * * @param string $id * @param string $from * public function printLabelEtiquette($id, $from='view') { * $this->printLabel('etiquette', $id, $from); * } */ /* (EP 202006) La bonne façon de faire, mais marche pas encore nickel chrome * car nécessite encore un template Materiels/xml/print_label_ruban.ctp... * (L'ancienne méthode a été renommée OLD_printLabel) * * Voir * https://book.cakephp.org/3/fr/views/json-and-xml-views.html#exemple-d-utilisation * et aussi * https://book.cakephp.org/3/en/controllers/request-response.html#sending-a-string-as-file * et aussi * https://book.cakephp.org/3/en/controllers/request-response.html#sending-files * */ private function printLabel($type = 'ruban', $id, $from = 'view') { //$this->viewBuilder()->setLayout(false); // Définit le format de la Vue $this->viewBuilder()->setClassName('Xml'); //$this->viewBuilder()->setLayout(false); //$materiel = $this->Materiels->get($id); $materiel = $this->Materiels->get($id, [ 'contain' => ['Organismes'] ]); //debug($materiel); exit; $numeroLab = $materiel->numero_laboratoire; //$organisme = $materiel->organisme_id; $organisme = $materiel->organisme ? $materiel->organisme->nom : 'Tutelle'; $numeroInventaireOrganisme = $materiel->numero_inventaire_organisme; /* $dateAcquisition = substr(str_replace('/','-',$materiel->date_acquisition), 0,-4); // '06-12-' $dateAcquisition .= substr($materiel->date_acquisition,-2); // '06-12-17' */ $dateAcquisition = $materiel->date_acquisition; // On imprime 'JJ-MM-AA' si date achat absente $dateAcquisition = empty($dateAcquisition) ? 'JJ-MM-AA' : $materiel->date_acquisition->format('d-m-y'); // '06-12-17' /* $file = fopen('php://output', 'w'); */ $filename = 'inventirap_label.label'; /* $this->response = $this->response ->withHeader('Content-Type','application/xml') ->withHeader('Content-Disposition','attachment; filename="'.$filename.'"'); */ //$this->response->sendHeaders(); // Select current default format (set from configuration) : etiquette_format1, or etiquette_format2, or etiquette_format3, etc... //debug($this->confLabinvent); $label_format_num = $this->confLabinvent->label_format_num; //debug($this->confLabinvent); //debug("label_format_num is $label_format_num"); $etiquetteFormatFunction = "etiquette_format".$label_format_num; // Select IRAP format //$etiquetteFormatFunction = "etiquette_format1"; // Select CRAL format //$etiquetteFormatFunction = "etiquette_format2"; /* if ($type == 'ruban') fputs($file, $this->$etiquetteFormatFunction($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme )); if ($type == 'etiquette') fputs($file, $this->_getLabelEtiquette($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme)); if ($type == 'ruban3lines') fputs($file, $this->_getLabelRuban3($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme)); */ //debug($etiquetteFormatFunction); exit; // etiquette_format1 //$xml_content = $this->$etiquetteFormatFunction($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme); $xml_content = $this->$etiquetteFormatFunction($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme); /* $response = $this->response; // Inject string content into response body (3.4.0+) $response = $response->withStringBody($xml_content); $response = $response->withType('xml'); // Optionally force file download $response = $response->withDownload($filename); // Return response object to prevent controller from trying to render // a view. return $response; //$response->download('inventirap_label.label'); */ //$this->viewBuilder()->setOption('rootNode', 'ContinuousLabel'); //$this->set('_rootNode','ContinuousLabel'); // Définir les Données de la Vue //$this->set('ContinuousLabel',$xml_content); $this->set(compact('xml_content')); //$this->set('_serialize', ['ContinuousLabel']); //$this->set('_serialize', 'xml_content'); //$this->viewBuilder()->setOption('serialize', ['xml_content']); // Définit le téléchargement forcé // Avant 3.4.0 // $this->response->download('report-' . date('YmdHis') . '.' . $format); //return $this->response->withDownload('report-' . date('YmdHis') . '.' . 'xml'); //return $this->response->withDownload('inventirap_label.label'); //$this->response->download('inventirap_label.xml'); $this->response->download($filename); //return $this->response->withFile('inventirap_label.label'); } /** * PrintLabel method * * @param string $type * @param string $id * @param string $from */ //private function printLabel($type = 'etiquette', $id, $from = 'view') private function OLD_printLabel($type = 'ruban', $id, $from = 'view') { //$this->viewBuilder()->setLayout(false); //$materiel = $this->Materiels->get($id); $materiel = $this->Materiels->get($id, [ 'contain' => ['Organismes'] ]); $numeroLab = $materiel->numero_laboratoire; //$organisme = $materiel->organisme_id; $organisme = $materiel->organisme ? $materiel->organisme->nom : ''; $numeroInventaireOrganisme = $materiel->numero_inventaire_organisme; /* $dateAcquisition = substr(str_replace('/','-',$materiel->date_acquisition), 0,-4); // '06-12-' $dateAcquisition .= substr($materiel->date_acquisition,-2); // '06-12-17' */ $dateAcquisition = $materiel->date_acquisition->format('d-m-y'); // '06-12-17' $filename = 'inventirap_label.label'; $file = fopen('php://output', 'w'); /* $this->response->header([ 'Content-type: application/xml', 'Content-Disposition: attachment; filename="' . $filename . '"' ]); */ // MARCHE... $this->response = $this->response ->withHeader('Content-Type','application/xml') ->withHeader('Content-Disposition','attachment; filename="'.$filename.'"'); //debug($this->response); exit; //$this->response = $this->response->withHeader('Content-Disposition','attachment; filename="'.$filename.'"'); // MARCHE PAS, WHY ??? //$this->response = $this->response->withHeader("Content-Disposition","attachment; filename='$filename'"); $this->response->sendHeaders(); // Select current default format (set from configuration) : etiquette_format1, or etiquette_format2, or etiquette_format3, etc... $label_format_num = $this->confLabinvent->label_format_num; //debug($this->confLabinvent); //debug("label_format_num is $label_format_num"); $etiquetteFormatFunction = "etiquette_format".$label_format_num; // Select IRAP format //$etiquetteFormatFunction = "etiquette_format1"; // Select CRAL format //$etiquetteFormatFunction = "etiquette_format2"; //if ($type == 'ruban') fputs($file, $this->_getLabelRuban($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme )); if ($type == 'ruban') fputs($file, $this->$etiquetteFormatFunction($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme )); // Anciens formats d'étiquettes ou ruban if ($type == 'etiquette') fputs($file, $this->_getLabelEtiquette($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme)); if ($type == 'ruban3lines') fputs($file, $this->_getLabelRuban3($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme)); sleep(3); fclose($file); // Si on veut éviter d'avoir à créer un template Materiels/print_label_ruban.ctp : exit; //exit(); // Ou encore : /* return $this->redirect([ 'action' => 'view', $id ]); */ } /* * RUBAN (12mm) * LABEL FOR PRINTER DYMO LabelManager PnP : 2 lines WITH LOGO * Format IRAP */ //private function _getLabelRuban($numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { return //private function etiquette_format1($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { private function etiquette_format4($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { // Texte à imprimer (colonne de gauche) $nb_text_lines = 2; /* $text_line1 = trim("$numeroLab / " . "$dateAcquisition"); $text_line2 = "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); */ // Image(s) à imprimer (colonne de droite) - code hexa // LOGO IRAP N&B $img_logo_black = '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'; // LOGO IRAP Bleu $img_logo_blue = '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'; $img_logo = $img_logo_black; // DYMO LabelManager PnP $tape_size = '12'; $label_length = '0'; $root_cell_length = '0'; $font_size = '24'; $object_margin_left="200"; $object_margin_right="200"; $cell1_length = '2606.94'; $length_mode = 'Fixed'; $cell2_length = '863.9999'; return $this->etiquette_dls_formatX( $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme, $nb_text_lines, $img_logo, $tape_size, $label_length, $root_cell_length, $font_size, $object_margin_left, $object_margin_right, $cell1_length, $length_mode, $cell2_length ); /* // (EP202006) NB: La 1ère ligne (xml version...) pose problème, donc je la vire //' return ' Landscape Tape12mm 12mm Auto 0 0 Auto 0 Solid Horizontal TEXTE_1 Rotation0 False False Left Middle ShrinkToFit True False ' .$text_line1 . "\n" .$text_line2 . '' //.'' . trim("$numeroLab / " . "$organisme") . "\n" . "$dateAcquisition " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : '') . .' 2606.94 Fixed 0 Solid GRAPHISME Rotation0 False False ' .$img_logo. ' Uniform 0 Right Center 863.9999 Auto 0 Solid '; //. "\n"; */ } // etiquette_format4() /* * RUBAN (19mm) * LABEL FOR PRINTER Dymo LabelManager PCII : 2 lines WITH QrCode * * Format CRAL */ private function etiquette_format2($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { // Texte à imprimer (colonne de gauche) $nb_text_lines = 2; /* $text_line1 = trim("$numeroLab / " . "$dateAcquisition"); $text_line2 = "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); */ // Image(s) à imprimer (colonne de droite) - code hexa // (EP 14-3-19) Recuperation du QrCode qui a été créé par la vue détaillée "view" en cours (le QrCode actuellement affiché par la vue en cours) $qrc_file_full_name = $this->request->getSession()->read("qrCodePath"); $qrc = file_get_contents("file://".$qrc_file_full_name); $qrc = base64_encode($qrc); $img_logo = $qrc; $tape_size = '19'; $label_length = '0'; $root_cell_length = '0'; $font_size = '24'; $object_margin_left="200"; $object_margin_right="200"; $cell1_length = '2606.94'; $length_mode = 'Fixed'; $cell2_length = '863.9999'; return $this->etiquette_dls_formatX( $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme, $nb_text_lines, $img_logo, $tape_size, $label_length, $root_cell_length, $font_size, $object_margin_left, $object_margin_right, $cell1_length, $length_mode, $cell2_length ); /* // (EP202006) NB: La 1ère ligne (xml version...) pose problème, donc je la vire //' return ' Landscape Tape19mm 19mm Auto 0 0 Auto 0 Solid Horizontal TEXTE_1 Rotation0 False False Left Middle ShrinkToFit True False ' .$text_line1 . "\n" .$text_line2 . ' 2606.94 Fixed 0 Solid GRAPHISME Rotation0 False False ' .$img_logo. ' Uniform 0 Right Center 863.9999 Auto 0 Solid '; */ } // etiquette_format2() /* * Définition d'étiquette * * - Type : RUBAN (19mm) * - Etiqueteuse : DYMO MobileLabeler * - Contenu : texte sur 3 lines et QrCode * * (Idem format 1 mais avec logiciel DLS 8.7.x sur Mac/Win) * (Le QrCode est généré par LabInvent et affiché comme image) * */ private function etiquette_format3($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { // Texte à imprimer (colonne de gauche) sur 3 lignes $nb_text_lines = 3; /* //$text_line1 = trim("$numeroLab / " . "$dateAcquisition"); $text_line1 = trim("$numeroLab"); $text_line2 = trim("$dateAcquisition"); $text_line3 = "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); */ // Image(s) à imprimer (colonne de droite) - code hexa // (EP 14-3-19) Recuperation du QrCode qui a été créé par la vue détaillée "view" en cours (le QrCode actuellement affiché par la vue en cours) $qrc_file_full_name = $this->request->getSession()->read("qrCodePath"); $qrc = file_get_contents("file://".$qrc_file_full_name); $qrc = base64_encode($qrc); $img_logo = $qrc; // DYMO MobileLabeler 19mm $tape_size = '19'; $label_length = '5278.94'; $root_cell_length = '4078.94'; $font_size = '11'; $object_margin_left='0'; $object_margin_right='0'; $cell1_length = '1980'; $length_mode = 'Auto'; $cell2_length = '869.4'; /* // DYMO LabelManager PnP $tape_size = '12'; $label_length = '0'; $root_cell_length = '0'; $font_size = '24'; $object_margin_left="200"; $object_margin_right="200"; $cell1_length = '2606.94'; $length_mode = 'Fixed'; $cell2_length = '863.9999'; */ return $this->etiquette_dls_formatX( $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme, $nb_text_lines, $img_logo, $tape_size, $label_length, $root_cell_length, $font_size, $object_margin_left, $object_margin_right, $cell1_length, $length_mode, $cell2_length ); /* // (EP202006) NB: La 1ère ligne (xml version...) pose problème, donc je la vire //' return ' Landscape Tape'.$tape_size.'mm '.$tape_size.'mm Auto '.$label_length.' '.$root_cell_length.' Auto 0 Solid Horizontal TEXTE_1 Rotation0 False False Left Middle ShrinkToFit True False ' .$text_line1 . "\n" .$text_line2 . "\n" .$text_line3 . ' '.$cell1_length.' '.$length_mode.' 0 Solid GRAPHISME Rotation0 False False ' .$img_logo. ' Uniform 0 Right Center '.$cell2_length.' Auto 0 Solid '; */ } // etiquette_format3() /* * RUBAN (12mm) * LABEL FOR PRINTER DYMO LabelManager PnP : 2 lines WITH QrCode * Format IRAP, avec QRCODE * * (mais QrCode trop petit pour être identifiable) */ //private function etiquette_format4($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { private function etiquette_format5($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { // Texte à imprimer (colonne de gauche) $nb_text_lines = 2; /* $text_line1 = trim("$numeroLab / " . "$dateAcquisition"); $text_line2 = "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); */ // Image(s) à imprimer (colonne de droite) - code hexa // (EP 14-3-19) Recuperation du QrCode qui a été créé par la vue détaillée "view" en cours (le QrCode actuellement affiché par la vue en cours) $qrc_file_full_name = $this->request->getSession()->read("qrCodePath"); $qrc = file_get_contents("file://".$qrc_file_full_name); $qrc = base64_encode($qrc); $img_logo = $qrc; $tape_size = '12'; $label_length = '0'; $root_cell_length = '0'; $font_size = '24'; $object_margin_left="200"; $object_margin_right="200"; $cell1_length = '2606.94'; $length_mode = 'Fixed'; $cell2_length = '863.9999'; return $this->etiquette_dls_formatX( $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme, $nb_text_lines, $img_logo, $tape_size, $label_length, $root_cell_length, $font_size, $object_margin_left, $object_margin_right, $cell1_length, $length_mode, $cell2_length ); /* // (EP202006) NB: La 1ère ligne (xml version...) pose problème, donc je la vire //' return ' Landscape Tape12mm 12mm Auto 0 0 Auto 0 Solid Horizontal TEXTE_1 Rotation0 False False Left Middle ShrinkToFit True False ' .$text_line1 . "\n" .$text_line2 . ' 2606.94 Fixed 0 Solid GRAPHISME Rotation0 False False ' .$img_logo. ' Uniform 0 Right Center 863.9999 Auto 0 Solid '; */ } // etiquette_format5() /* /* * Définition d'étiquette * * - Type : RUBAN (19mm) * (EP 20201124) * - Etiqueteuse : DYMO LabelManager 420P (ou ancienne MobileLabeler qui ne se fait plus) * - Contenu : texte sur 3 lines (arial 10, bold) + marge 1mm + QrCode * * (pour logiciel DCD 1.3.x Windows10) * (le QrCode est généré par DCD à partir d'un texte qu'on lui donne : l'URL à afficher) * */ //private function etiquette_format5($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { private function etiquette_format1($id, $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme) { // Texte à imprimer (colonne de gauche) sur 3 lignes $nb_text_lines = 3; /* //$text_line1 = trim("$numeroLab / " . "$dateAcquisition"); $text_line1 = trim("$numeroLab"); $text_line2 = trim("$dateAcquisition"); $text_line3 = "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); */ /* // Image(s) à imprimer (colonne de droite) - code hexa // (EP 14-3-19) Recuperation du QrCode qui a été créé par la vue détaillée "view" en cours (le QrCode actuellement affiché par la vue en cours) $qrc_file_full_name = $this->request->getSession()->read("qrCodePath"); $qrc = file_get_contents("file://".$qrc_file_full_name); $qrc = base64_encode($qrc); $img_logo = $qrc; */ // Texte à transformer en QrCode //$qr_code_text = "https://inventirap.irap.omp.eu/materiels/view/$id"; $qr_code_text = $this->getCurrentURL(false)."/materiels/view/".$id; //$qr_code_text = "tototata"; //debug($qr_code_text);exit; $tape_size = '19'; //$label_length = '5278.94'; //$root_cell_length = '4078.94'; $total_width = '1.641904'; $font_name = 'Arial'; $font_size = '10'; //$object_margin_left='0'; //$object_margin_right='1'; //$text_margin_right='1'; //$cell1_length = '1980'; $cell1_width = '1.115515'; //$length_mode = 'Auto'; //$cell2_length = '869.4'; $cell2_width = '0.5263889'; //return $this->etiquette_dcd_formatX( $etiq_text = $this->etiquette_dcd_formatX( $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme, $nb_text_lines, //$img_logo, $qr_code_text, // url de la fiche $tape_size, // 19 (mm) //$label_length, //$root_cell_length, $total_width, // 1.641904 $font_name, $font_size, // Arial 10 //$object_margin_left, //$object_margin_right, //$text_margin_right='1', //$cell1_length, $cell1_width, // 1.115515 //$length_mode, //$cell2_length $cell2_width // 0.5263889 ); //debug($etiq_text);exit; return $etiq_text; } // etiquette_format1() /* (EP 20201124) * * Définition d'étiquette (générale) * Compatible avec le logiciel DCD (Dymo Connect Desktop) version 1.3.2 * (uniquement utilisé sur Win, car MacOS utilise toujours l'ancien logiciel DLS) * */ private function etiquette_dcd_formatX( $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme, $nb_text_lines, //$img_logo, $qr_code_text, // url de la fiche $tape_size, // 19 (mm) //$label_length, //$root_cell_length, $total_width, // 1.641904 $font_name, $font_size, // Arial 10 //$object_margin_left, //$object_margin_right, //$text_margin_right='1', //$cell1_length, $cell1_width, // 1.115515 //$length_mode, //$cell2_length $cell2_width // 0.5263889 ) { // Texte à imprimer (colonne de gauche) : sur 2 ou 3 lignes /* $text_lines = trim("$numeroLab"); $text_lines .= $nb_text_lines == 3 ? "\n" : ' / '; $text_lines .= trim("$dateAcquisition") . "\n"; $text_lines .= "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); */ $text_line1_numinv = trim("$numeroLab"); $text_line2_date = trim("$dateAcquisition"); $text_line3_tutelle = "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); // (EP202006) NB: La 1ère ligne (xml version...) pose problème, donc je la vire //' return ' DYMO Label Landscape '.$tape_size.'X7-TAPE BLACK/WHITE 0.7555556 SolidLine 0.4166667 0.1145833 ' //1.641904 .''.$total_width.'' .'0.5263889 1 False ClearObjects ITextObject0 Rotation0 1 False SolidLine Left Middle None False None Left Middle False '.$text_line1_numinv.' ' //Arial //10 .''.$font_name.' '.$font_size.' True False False '.$text_line2_date.' '.$font_name.' '.$font_size.' True False False '.$text_line3_tutelle.' '.$font_name.' '.$font_size.' True False False 0.4166667 0.1145833 ' //1.115515 .''.$cell1_width.' 0.5263889 IQRCodeObject0 Rotation0 1 False SolidLine QRCode '.$qr_code_text.' Center Middle AutoFit QRCodeText ' //123 .''.$qr_code_text.' 1.532182 0.1145833 ' //0.5263889 .''.$cell2_width.' 0.5263889 Blank '; } // etiquette_dcd_formatX() /* * Définition d'étiquette (générale) * Compatible avec le logiciel DLS (Dymo Label Software) version 8.7.x * (toujours utilisé sur MacOS, mais remplacé par DCD sur Win) * */ private function etiquette_dls_formatX( $numeroLab, $organisme, $dateAcquisition, $numeroInventaireOrganisme, $nb_text_lines, $img_logo, $tape_size, $label_length, $root_cell_length, $font_size, $object_margin_left, $object_margin_right, $cell1_length, $length_mode, $cell2_length ) { // Texte à imprimer (colonne de gauche) : sur 2 ou 3 lignes $text_lines = trim("$numeroLab"); $text_lines .= $nb_text_lines == 3 ? "\n" : ' / '; $text_lines .= trim("$dateAcquisition") . "\n"; $text_lines .= "$organisme " . trim($numeroInventaireOrganisme ? '/ ' . "$numeroInventaireOrganisme" : ''); // (EP202006) NB: La 1ère ligne (xml version...) pose problème, donc je la vire //' return ' Landscape Tape'.$tape_size.'mm '.$tape_size.'mm' .'Auto' //Fixed .''.$label_length.'' //3648 .'' .''.$root_cell_length.'' //.'2448 .'Auto' //Fixed .'0 Solid Horizontal TEXTE_1 Rotation0 False False Left Middle ShrinkToFit' //None .'True False ' .$text_lines. ' ' // .' '.$cell1_length.' '.$length_mode.'' //.'1600 //Star .'0 Solid GRAPHISME Rotation0 False False ' .$img_logo. ' Uniform 0 Right Center '.$cell2_length.' Auto' //.'848.0001 //Star .'0 Solid '; } // etiquette_dls_formatX() /* * ETIQUETTE (19x51 mm) * LABEL FOR PRINTER DYMO LabelWriter 450 : WITH LOGO */ private function _getLabelEtiquette() { return ' Landscape MultiPurpose11355 11355 Multi-Purpose TEXTE Rotation0 False False Left Middle ShrinkToFit True False ' . "$numeroLab" . "\n" . "$dateAcquisition" . "\n" . "$organisme" . ($numeroInventaireOrganisme ? '-' . "$numeroInventaireOrganisme" : '') . ' ' . ' ' . ' GRAPHISME Rotation0 False False' . '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 Uniform 0 Right' . 'Center ' . ' ' . "\n"; } /* * RUBAN * LABEL FOR PRINTER DYMO LabelMznzhrt PnP : 3 lines WITH LOGO */ private function _getLabelRuban3() { return ' Landscape' /* // 9mm Tape9mm 9mm */ // 12mm .'Tape12mm 12mm Auto 0 0 Auto 0 Solid Horizontal TEXTE Rotation0 False False Left Middle AlwaysFit True False ' . "$numeroLab" . "\n" . "$organisme" . ($numeroInventaireOrganisme ? '-' . "$numeroInventaireOrganisme" : '') . "\n" . "$dateAcquisition" . ' 1477.513 Fixed 0 Solid GRAPHISME Rotation0 False False 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 Uniform 0 Right Center 800 Auto 0 Solid ' . "\n"; // fputs (ruban 3 lines) } // called from Javascript (Ajax) public function getDateGarantie($dateORjour, $dureeORmois, $uniteORannee, $duree = null, $unite = null) { //(EP 20200410 changement) //$sep = '-'; $sep = '/'; if ($duree != null && $unite != null) { //$date = $dateORjour . '-' . $dureeORmois . '-' . $uniteORannee; $date = $dateORjour . $sep . $dureeORmois . $sep . $uniteORannee; } else { $date = $dateORjour; $duree = $dureeORmois; $unite = $uniteORannee; } // (EP 20200410 changement format d-m-Y => d/m/Y) car c'est le format qui est affiché par défaut par cakephp dans les vues //$date_next = date_create_from_format('d-m-Y', $date); //$date_next = date_create_from_format('d/m/Y', $date); $date_next = \DateTime::createFromFormat('d/m/Y', $date); $unit = ($unite=='Ans') ? 'years' : 'months'; $date_next->add(\DateInterval::createFromDateString("$duree $unit")); /* switch ($unite) { case "Mois": date_add($date_next, date_interval_create_from_date_string($duree . ' months')); break; case "Ans": date_add($date_next, date_interval_create_from_date_string($duree . ' years')); break; } */ //debug("toto".$date_next->format('d/m/Y')."toto"); exit; //$this->set('date', date_format($date_next, 'd-m-Y')); //$this->set('date', date_format($date_next, 'd/m/Y')); $this->set('date', $date_next->format('d/m/Y')); $this->viewBuilder()->layout = 'ajax'; } public function printSheet($id = null) { $materiel = $this->Materiels->get($id, [ 'contain' => [ 'SurCategories', 'Categories', 'SousCategories', 'GroupesThematiques', 'GroupesMetiers', 'Organismes', 'Sites', 'Documents', 'Emprunts', 'Suivis' ] ]); $sites = TableRegistry::get('Sites'); $typeSuivis = TableRegistry::get('TypeSuivis'); $typeDocuments = TableRegistry::get('TypeDocuments'); if ($materiel->photo_id != null) { $imgMateriel = $materiel->photo_id . '.' . TableRegistry::get('Documents')->get($materiel->photo_id)->get('type_doc'); $this->set('imgMateriel', $imgMateriel); } $this->set('sites', $sites); $this->set('typeSuivis', $typeSuivis); $this->set('typeDocuments', $typeDocuments); $this->set('materiel', $materiel); $this->set('_serialize', [ 'materiel' ]); } } // MaterielsController