diff --git a/htdocs/core/modules/modHoliday.class.php b/htdocs/core/modules/modHoliday.class.php index b9af68d29c332..f7566f4e3eb8e 100644 --- a/htdocs/core/modules/modHoliday.class.php +++ b/htdocs/core/modules/modHoliday.class.php @@ -310,6 +310,13 @@ public function __construct($db) */ public function init($options = '') { + // EN: Ensure holiday tables are created or updated during module activation. + // FR: Garantir la création ou la mise à jour des tables des congés lors de l'activation du module. + $result = $this->_load_tables('/install/mysql/', 'holiday'); + if ($result < 0) { + return -1; + } + // Permissions $this->remove($options); diff --git a/htdocs/holiday/card.php b/htdocs/holiday/card.php index b35e1bf64f06b..4a32a687a78cc 100644 --- a/htdocs/holiday/card.php +++ b/htdocs/holiday/card.php @@ -11,6 +11,7 @@ * Copyright (C) 2024 Charlene Benke * Copyright (C) 2025 MDW * + * Copyright (C) 2025 Pierre Ardoin * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, orwrite @@ -43,6 +44,65 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/holiday.lib.php'; require_once DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; +require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; + +/** + * Determine the default second approver based on the hierarchy of the first approver. + * Détermine le second valideur par défaut en fonction de la hiérarchie du premier valideur. + * + * @param DoliDB $db Database handler / Gestionnaire de base de données + * @param int $firstApproverId First approver identifier / Identifiant du premier valideur + * @param array $allowedApprovers Optional allowed approvers / Liste optionnelle des valideurs autorisés + * @return int Identifier of the second approver or 0 / Identifiant du second valideur ou 0 + */ +function holiday_get_default_second_approver(DoliDB $db, $firstApproverId, array $allowedApprovers = array()) +{ + // Secure the incoming identifier / Sécurise l'identifiant en entrée + $firstApproverId = (int) $firstApproverId; + if ($firstApproverId <= 0) { + return 0; + } + + // Load the selected first approver / Charge le premier valideur sélectionné + $firstApprover = new User($db); + if ($firstApprover->fetch($firstApproverId) <= 0) { + return 0; + } + + // Try the forced approver stored in llx_user.fk_user_approve2 / Tente le valideur forcé stocké dans llx_user.fk_user_approve2 + $forcedSecondApproverId = 0; + $sql = 'SELECT fk_user_approve2 FROM '.MAIN_DB_PREFIX."user WHERE rowid = ".$firstApproverId; + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $forcedSecondApproverId = (int) $obj->fk_user_approve2; + } + $db->free($resql); + } + // Fallback on dedicated holiday value / Replie sur la valeur dédiée aux congés + if ($forcedSecondApproverId <= 0) { + $forcedSecondApproverId = (int) $firstApprover->fk_user_holiday_validator2; + } + if ($forcedSecondApproverId > 0) { + if (empty($allowedApprovers) || in_array($forcedSecondApproverId, $allowedApprovers, true)) { + return $forcedSecondApproverId; + } + } + + // Extract the manager identifier from the hierarchy / Récupère l'identifiant du responsable hiérarchique + $managerId = (int) $firstApprover->fk_user; + if ($managerId <= 0) { + return 0; + } + + // Check if the manager can validate if a list is provided / Vérifie que le responsable peut valider si une liste est fournie + if (!empty($allowedApprovers) && !in_array($managerId, $allowedApprovers, true)) { + return 0; + } + + return $managerId; +} /** * @var Conf $conf @@ -280,9 +340,16 @@ setEventMessages($langs->transnoentitiesnoconv('InvalidValidator'), null, 'errors'); $error++; } - // Allow selection of the second approver + // Allow selection of the second approver / Gère la sélection du second valideur if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { - // Validate the chosen second approver + // Auto select the hierarchical manager if missing / Sélectionne automatiquement le responsable hiérarchique si absent + if ($approverid2 < 1) { + $defaultSecondApprover = holiday_get_default_second_approver($db, $approverid, $approverslist); + if ($defaultSecondApprover > 0) { + $approverid2 = $defaultSecondApprover; + } + } + // Validate the chosen second approver / Valide le second valideur choisi if ($approverid2 < 1) { setEventMessages($langs->trans('SecondApproverRequired'), null, 'errors'); $error++; @@ -305,9 +372,9 @@ $object->fk_user = $fuserid; $object->description = $description; $object->fk_validator = $approverid; - // Offer edition of the second approver - if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { - // Keep the expected second approver + // Offer edition of the second approver / Permet l'édition du second valideur + if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { + // Keep the expected second approver / Conserve le second valideur prévu $object->fk_user_approve2 = ($approverid2 > 0 ? $approverid2 : 0); } else { $object->fk_user_approve2 = 0; @@ -342,17 +409,25 @@ $object->oldcopy = dol_clone($object, 2); // @phan-suppress-current-line PhanTypeMismatchProperty - $object->fk_validator = GETPOSTINT('valideur'); - // Capture the second approver during quick edit + $approverid = GETPOSTINT('valideur'); + $object->fk_validator = $approverid; + // Capture the second approver during quick edit / Récupère le second valideur lors de l'édition rapide $approverid2 = GETPOSTINT('valideur2'); $localerror = 0; if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { - // Validate and store the second approver during quick edit + $approverslist = $object->fetch_users_approver_holiday(); + // Auto select the hierarchical manager if missing / Sélectionne automatiquement le responsable hiérarchique si absent + if ($approverid2 < 1) { + $defaultSecondApprover = holiday_get_default_second_approver($db, $approverid, $approverslist); + if ($defaultSecondApprover > 0) { + $approverid2 = $defaultSecondApprover; + } + } + // Validate and store the second approver during quick edit / Valide et sauvegarde le second valideur lors de l'édition rapide if ($approverid2 < 1) { setEventMessages($langs->trans('SecondApproverRequired'), null, 'warnings'); $localerror++; } - $approverslist = $object->fetch_users_approver_holiday(); if (!empty($approverslist) && !in_array($approverid2, $approverslist)) { setEventMessages($langs->trans('InvalidSecondValidatorCP'), null, 'warnings'); $localerror++; @@ -444,19 +519,28 @@ $action = 'edit'; } if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { - // Validate the second approver while editing - if ($approverid2 < 1) { - setEventMessages($langs->trans('SecondApproverRequired'), null, 'warnings'); - $error++; - $action = 'edit'; - } - $approverslist = $object->fetch_users_approver_holiday(); - if (!empty($approverslist) && !in_array($approverid2, $approverslist)) { - setEventMessages($langs->trans('InvalidSecondValidatorCP'), null, 'warnings'); - $error++; - $action = 'edit'; + $approverslist = $object->fetch_users_approver_holiday(); + // Auto select the hierarchical manager if missing / Sélectionne automatiquement le responsable hiérarchique si absent + if ($approverid2 < 1) { + $defaultSecondApprover = holiday_get_default_second_approver($db, $approverid, $approverslist); + if ($defaultSecondApprover > 0) { + $approverid2 = $defaultSecondApprover; } } + // Validate the second approver while editing / Valide le second valideur pendant l'édition + if ($approverid2 < 1) { + setEventMessages($langs->trans('SecondApproverRequired'), null, 'warnings'); + $error++; + $action = 'edit'; + } + if (!empty($approverslist) && !in_array($approverid2, $approverslist)) { + setEventMessages($langs->trans('InvalidSecondValidatorCP'), null, 'warnings'); + $error++; + $action = 'edit'; + } + } + + // If there is no Business Days within request $nbopenedday = num_open_day($date_debut_gmt, $date_fin_gmt, 0, 1, $halfday); @@ -1301,12 +1385,60 @@ if (empty($include_users)) { print img_warning().' '.$langs->trans("NobodyHasPermissionToValidateHolidays"); } else { + // Build a reusable mapping of first approvers to their manager / Construit un mapping réutilisable entre le premier valideur et son responsable + $defaultSecondApproversMap = array(); + foreach ($include_users as $potentialFirstApproverId) { + $possibleSecondApproverId = holiday_get_default_second_approver($db, $potentialFirstApproverId, $include_users); + if ($possibleSecondApproverId > 0) { + $defaultSecondApproversMap[$potentialFirstApproverId] = $possibleSecondApproverId; + } + } + $defaultselectuser2 = GETPOSTINT('valideur2'); if (empty($defaultselectuser2) && !empty($defaultselectuser)) { - $defaultselectuser2 = $defaultselectuser; + $defaultFromHierarchy = (isset($defaultSecondApproversMap[$defaultselectuser]) ? $defaultSecondApproversMap[$defaultselectuser] : holiday_get_default_second_approver($db, $defaultselectuser, $include_users)); + if ($defaultFromHierarchy > 0) { + $defaultselectuser2 = $defaultFromHierarchy; + } else { + $defaultselectuser2 = $defaultselectuser; + } } $s2 = $form->select_dolusers($defaultselectuser2, "valideur2", 1, '', 0, $include_users, '', '0,'.$conf->entity, 0, 0, '', 0, '', 'minwidth200 maxwidth500'); print img_picto('', 'user', 'class="pictofixedwidth"').$form->textwithpicto($s2, $langs->trans("AnyOtherInThisListCanValidate")); + if ($action == 'create' && !empty($defaultSecondApproversMap)) { + $defaultSecondApproversJson = json_encode($defaultSecondApproversMap); + print ''; + } } print ''; print ''; @@ -1617,12 +1749,18 @@ if (!in_array($object->fk_user_approve2, $include_users)) { $include_users[] = $object->fk_user_approve2; } - // Fetch the proposed second approver + // Fetch the proposed second approver / Récupère le second valideur proposé $defaultselectuser2 = $object->fk_user_approve2; - // Preserve the posted value for the second approver + if (empty($defaultselectuser2) && !empty($object->fk_validator)) { + $defaultFromHierarchy = holiday_get_default_second_approver($db, $object->fk_validator, $include_users); + if ($defaultFromHierarchy > 0) { + $defaultselectuser2 = $defaultFromHierarchy; + } + } + // Preserve the posted value for the second approver / Préserve la valeur postée pour le second valideur if ($action == 'editvalidator' && GETPOSTINT('valideur2') > 0) { - // Refresh the second approver with the posted value - $defaultselectuser2 = GETPOSTINT('valideur2'); + // Refresh the second approver with the posted value / Rafraîchit le second valideur avec la valeur postée + $defaultselectuser2 = GETPOSTINT('valideur2'); } $s2 = $form->select_dolusers($defaultselectuser2, "valideur2", (($action == 'editvalidator') ? 0 : 1), $arrayofvalidatorstoexclude, 0, $include_users); print '
'.$form->textwithpicto($s2, $langs->trans("AnyOtherInThisListCanValidate")).'
'; diff --git a/htdocs/holiday/class/holiday.class.php b/htdocs/holiday/class/holiday.class.php index ceb706546cb58..13a80bf4901cc 100644 --- a/htdocs/holiday/class/holiday.class.php +++ b/htdocs/holiday/class/holiday.class.php @@ -6,6 +6,7 @@ * Copyright (C) 2016 Juanjo Menent * Copyright (C) 2018-2025 Frédéric France * Copyright (C) 2024-2025 MDW + * Copyright (C) 2025 Pierre Ardoin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/htdocs/install/mysql/tables/llx_holiday-holiday.sql b/htdocs/install/mysql/tables/llx_holiday-holiday.sql new file mode 100644 index 0000000000000..dd4a28b2952cd --- /dev/null +++ b/htdocs/install/mysql/tables/llx_holiday-holiday.sql @@ -0,0 +1,8 @@ +-- EN: Ensure double approval columns exist for holiday module activation / FR: Garantit l'existence des colonnes de double approbation lors de l'activation du module Congés +ALTER TABLE llx_holiday +ADD COLUMN date_approval2 DATETIME DEFAULT NULL AFTER fk_user_approve, +ADD COLUMN fk_user_approve2 integer DEFAULT NULL AFTER date_approval2; + +-- EN: Ensure forced second holiday approver column exists / FR: Garantit la présence du second valideur de congés forcé +ALTER TABLE llx_user +ADD COLUMN fk_user_holiday_validator2 integer DEFAULT NULL AFTER fk_user_holiday_validator; diff --git a/htdocs/install/mysql/tables/llx_holiday.sql b/htdocs/install/mysql/tables/llx_holiday.sql index cc464f01d7272..4b5005ae8b3c3 100644 --- a/htdocs/install/mysql/tables/llx_holiday.sql +++ b/htdocs/install/mysql/tables/llx_holiday.sql @@ -18,37 +18,40 @@ CREATE TABLE llx_holiday ( -rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY, -ref varchar(30) NOT NULL, -ref_ext varchar(255), -entity integer DEFAULT 1 NOT NULL, -- Multi company id -fk_user integer NOT NULL, -fk_user_create integer, -fk_user_modif integer, -fk_type integer NOT NULL, -date_create DATETIME NOT NULL, -description VARCHAR( 255 ) NOT NULL, -date_debut DATE NOT NULL, -date_fin DATE NOT NULL, -halfday integer DEFAULT 0, -- 0=start morning and end afternoon, -1=start afternoon end afternoon, 1=start morning and end morning, 2=start afternoon and end morning -nb_open_day double(24,8) DEFAULT NULL, -- DENORMALIZED FIELD. number of open days of holiday. Not always set. More reliable when re-calculated with num_open_days(date_debut, date_fin, halfday). -statut integer NOT NULL DEFAULT 1, -- status of leave request -fk_validator integer NOT NULL, -- who should approve the leave -date_valid DATETIME DEFAULT NULL, -- date validation -fk_user_valid integer DEFAULT NULL, -- user validation -date_approval DATETIME DEFAULT NULL, -- date approval -fk_user_approve integer DEFAULT NULL, -- user approval - date_approval2 DATETIME DEFAULT NULL, -- EN: date of second approval / FR: date de la seconde approbation - fk_user_approve2 integer DEFAULT NULL, -- EN: user for second approval / FR: utilisateur pour la seconde approbation -date_refuse DATETIME DEFAULT NULL, -fk_user_refuse integer DEFAULT NULL, -date_cancel DATETIME DEFAULT NULL, -fk_user_cancel integer DEFAULT NULL, -detail_refuse varchar( 250 ) DEFAULT NULL, -note_private text, -note_public text, -tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -import_key varchar(14), -extraparams varchar(255) -- for other parameters with json format + rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY, + ref varchar(30) NOT NULL, + ref_ext varchar(255), + entity integer DEFAULT 1 NOT NULL, -- EN: Multi company id / FR: Identifiant multi-société + fk_user integer NOT NULL, + fk_user_create integer, + fk_user_modif integer, + fk_type integer NOT NULL, + date_create DATETIME NOT NULL, + description VARCHAR(255) NOT NULL, + date_debut DATE NOT NULL, + date_fin DATE NOT NULL, + halfday integer DEFAULT 0, -- EN: 0=start morning and end afternoon, -1=start afternoon end afternoon, 1=start morning and end morning, 2=start afternoon and end morning / FR: 0=début matin fin après-midi, -1=début après-midi fin après-midi, 1=début matin fin matin, 2=début après-midi fin matin + nb_open_day double(24,8) DEFAULT NULL, -- EN: DENORMALIZED FIELD. number of open days of holiday. Not always set. More reliable when re-calculated with num_open_days(date_debut, date_fin, halfday). / FR: CHAMP DÉNORMALISÉ. Nombre de jours ouvrés de congés. Pas toujours défini. Plus fiable lors d'un recalcul avec num_open_days(date_debut, date_fin, halfday). + statut integer NOT NULL DEFAULT 1, -- EN: status of leave request / FR: statut de la demande de congé + fk_validator integer NOT NULL, -- EN: who should approve the leave / FR: personne devant approuver le congé + date_valid DATETIME DEFAULT NULL, -- EN: date validation / FR: date de validation + fk_user_valid integer DEFAULT NULL, -- EN: user validation / FR: utilisateur de validation + date_approval DATETIME DEFAULT NULL, -- EN: date approval / FR: date d'approbation + fk_user_approve integer DEFAULT NULL, -- EN: user approval / FR: utilisateur approbateur + date_refuse DATETIME DEFAULT NULL, + fk_user_refuse integer DEFAULT NULL, + date_cancel DATETIME DEFAULT NULL, + fk_user_cancel integer DEFAULT NULL, + detail_refuse varchar(250) DEFAULT NULL, + note_private text, + note_public text, + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + import_key varchar(14), + extraparams varchar(255) -- EN: for other parameters with json format / FR: pour d'autres paramètres au format JSON ) ENGINE=innodb; + +-- EN: Add double approval columns if missing / FR: Ajoute les colonnes de double approbation si elles sont absentes +ALTER TABLE llx_holiday +ADD COLUMN date_approval2 DATETIME DEFAULT NULL AFTER fk_user_approve, +ADD COLUMN fk_user_approve2 integer DEFAULT NULL AFTER date_approval2; diff --git a/htdocs/install/mysql/tables/llx_user.sql b/htdocs/install/mysql/tables/llx_user.sql index 494584e414cf3..82ccab3b75d3f 100644 --- a/htdocs/install/mysql/tables/llx_user.sql +++ b/htdocs/install/mysql/tables/llx_user.sql @@ -74,6 +74,7 @@ create table llx_user fk_user integer NULL, -- Supervisor, hierarchic parent fk_user_expense_validator integer NULL, fk_user_holiday_validator integer NULL, + fk_user_holiday_validator2 integer NULL, -- EN: forced second holiday approver / FR: second valideur des congés forcé national_registration_number varchar(50), idpers1 varchar(128), diff --git a/htdocs/langs/en_US/users.lang b/htdocs/langs/en_US/users.lang index 8eddb0d1734ce..8b71a3242017f 100644 --- a/htdocs/langs/en_US/users.lang +++ b/htdocs/langs/en_US/users.lang @@ -124,6 +124,8 @@ CantDisableAnAdminUserWithMassActions=You can't disable users in a mass action w CantEnableAnAdminUserWithMassActions=You can't enable users in a mass action when one of them is an admin user (like "%s"). Try to disable the admin user(s) manually, one by one from their dedicated page. ForceUserExpenseValidator=Force expense report validator ForceUserHolidayValidator=Force leave request validator +ForceUserHolidaySecondValidator=Force second-level leave approver +SecondValidatorIsSupervisorByDefault=Second approver defaults to the first approver's manager ValidatorIsSupervisorByDefault=By default, the validator is the supervisor of the user. Keep empty to keep this behavior. UserPersonalEmail=Personal email UserPersonalMobile=Personal mobile phone diff --git a/htdocs/langs/fr_FR/users.lang b/htdocs/langs/fr_FR/users.lang index dce43a8099cb5..25ec6e888bb24 100644 --- a/htdocs/langs/fr_FR/users.lang +++ b/htdocs/langs/fr_FR/users.lang @@ -124,6 +124,8 @@ CantDisableAnAdminUserWithMassActions=Vous ne pouvez pas désactiver les utilisa CantEnableAnAdminUserWithMassActions=Vous ne pouvez pas activer des utilisateurs dans une action de masse si l'un d'eux est un Administrateur utilisateur (comme « %s »). Essayez de désactiver les Administrateur utilisateur manuellement, un par un depuis leur page dédiée. ForceUserExpenseValidator=Forcer le valideur des notes de frais ForceUserHolidayValidator=Forcer le valideur des congés +ForceUserHolidaySecondValidator=Forcer le second valideur des congés +SecondValidatorIsSupervisorByDefault=Le second valideur est par défaut le responsable du premier valideur ValidatorIsSupervisorByDefault=Par défaut, le valideur est le responsable hiérarchique de l'utilisateur. Gardez vide pour conserver ce comportement. UserPersonalEmail=Email personnel UserPersonalMobile=Téléphone portable personnel diff --git a/htdocs/user/card.php b/htdocs/user/card.php index 2cabb21a3e75c..7af3a61dc9b51 100644 --- a/htdocs/user/card.php +++ b/htdocs/user/card.php @@ -17,6 +17,8 @@ * Copyright (C) 2018 David Beniamine * Copyright (C) 2024-2025 MDW * + * Copyright (C) 2025 Pierre Ardoin + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or @@ -324,6 +326,8 @@ $object->fk_user = GETPOSTINT("fk_user") > 0 ? GETPOSTINT("fk_user") : 0; $object->fk_user_expense_validator = GETPOSTINT("fk_user_expense_validator") > 0 ? GETPOSTINT("fk_user_expense_validator") : 0; $object->fk_user_holiday_validator = GETPOSTINT("fk_user_holiday_validator") > 0 ? GETPOSTINT("fk_user_holiday_validator") : 0; + // Capture forced second approver selection / Récupère le second valideur forcé saisi + $object->fk_user_holiday_validator2 = GETPOSTINT("fk_user_holiday_validator2") > 0 ? GETPOSTINT("fk_user_holiday_validator2") : 0; $object->employee = GETPOSTINT('employee'); $object->thm = GETPOST("thm", 'alphanohtml') != '' ? GETPOST("thm", 'alphanohtml') : ''; @@ -1177,6 +1181,17 @@ print img_picto('', 'user', 'class="pictofixedwidth"').$form->select_dolusers($object->fk_user_holiday_validator, 'fk_user_holiday_validator', 1, array($object->id), 0, '', '', (string) $conf->entity, 0, 0, '', 0, '', 'maxwidth300 widthcentpercentminusx'); print ''; print "\n"; + if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { + print ''; + $secondText = $langs->trans("ForceUserHolidaySecondValidator"); + print $form->textwithpicto($secondText, $langs->trans("SecondValidatorIsSupervisorByDefault"), 1, 'help'); + print ''; + print ''; + // Display selector for forced second approver / Affiche le sélecteur du second valideur forcé + print img_picto('', 'user', 'class="pictofixedwidth"').$form->select_dolusers($object->fk_user_holiday_validator2, 'fk_user_holiday_validator2', 1, array($object->id), 0, '', '', (string) $conf->entity, 0, 0, '', 0, '', 'maxwidth300 widthcentpercentminusx'); + print ''; + print "\n"; + } } // External user @@ -1756,6 +1771,21 @@ } print ''; print "\n"; + if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { + print ''; + $secondText = $langs->trans("ForceUserHolidaySecondValidator"); + print $form->textwithpicto($secondText, $langs->trans("SecondValidatorIsSupervisorByDefault"), 1, 'help'); + print ''; + print ''; + // Display forced second approver summary / Affiche le résumé du second valideur forcé + if (!empty($object->fk_user_holiday_validator2)) { + $hvuser2 = new User($db); + $hvuser2->fetch($object->fk_user_holiday_validator2); + print $hvuser2->getNomUrl(-1); + } + print ''; + print "\n"; + } } } @@ -2608,6 +2638,24 @@ } print ''; print "\n"; + if (getDolGlobalString('HOLIDAY_REQUIRE_DOUBLE_APPROVAL')) { + print ''; + $secondText = $langs->trans("ForceUserHolidaySecondValidator"); + print $form->textwithpicto($secondText, $langs->trans("SecondValidatorIsSupervisorByDefault"), 1, 'help'); + print ''; + print ''; + // Display quick edit selector for forced second approver / Affiche le sélecteur rapide du second valideur forcé + if ($permissiontoedit) { + print img_picto('', 'user', 'class="pictofixedwidth"').$form->select_dolusers($object->fk_user_holiday_validator2, 'fk_user_holiday_validator2', 1, array($object->id), 0, '', '', (string) $object->entity, 0, 0, '', 0, '', 'widthcentpercentminusx maxwidth300'); + } else { + print ''; + $hvuser2 = new User($db); + $hvuser2->fetch($object->fk_user_holiday_validator2); + print $hvuser2->getNomUrl(-1); + } + print ''; + print "\n"; + } } } diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 1ca84ab21920b..fa9dc549e2cd0 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -13,6 +13,7 @@ * Copyright (C) 2018 charlene Benke * Copyright (C) 2018-2021 Nicolas ZABOURI * Copyright (C) 2019-2025 Frédéric France + * Copyright (C) 2025 Pierre Ardoin * Copyright (C) 2019 Abbes Bahfir * Copyright (C) 2024-2025 MDW * Copyright (C) 2024 Lenin Rivas @@ -253,10 +254,17 @@ class User extends CommonObject */ public $fk_user_expense_validator; - /** - * @var int User ID of holidays validator - */ - public $fk_user_holiday_validator; + /** + * @var int User ID of holidays validator + */ + public $fk_user_holiday_validator; + + /** + * User ID of second holidays validator / Identifiant utilisateur du second valideur de congés + * + * @var int + */ + public $fk_user_holiday_validator2; /** * @var string clicktodial url @@ -544,7 +552,8 @@ public function fetch($id = 0, $login = '', $sid = '', $loadpersonalconf = 0, $e $sql .= " u.address, u.zip, u.town, u.fk_state as state_id, u.fk_country as country_id,"; $sql .= " u.admin, u.login, u.note_private, u.note_public,"; $sql .= " u.pass, u.pass_crypted, u.pass_temp, u.api_key,"; - $sql .= " u.fk_soc, u.fk_socpeople, u.fk_member, u.fk_user, u.ldap_sid, u.fk_user_expense_validator, u.fk_user_holiday_validator,"; + // Load forced second approver metadata / Charge les métadonnées du second valideur forcé + $sql .= " u.fk_soc, u.fk_socpeople, u.fk_member, u.fk_user, u.ldap_sid, u.fk_user_expense_validator, u.fk_user_holiday_validator, u.fk_user_holiday_validator2,"; $sql .= " fk_user_creat as user_creation_id, fk_user_modif as user_modification_id,"; $sql .= " u.statut as status, u.lang, u.entity,"; $sql .= " u.datec as datec,"; @@ -729,6 +738,8 @@ public function fetch($id = 0, $login = '', $sid = '', $loadpersonalconf = 0, $e $this->fk_user = $obj->fk_user; $this->fk_user_expense_validator = $obj->fk_user_expense_validator; $this->fk_user_holiday_validator = $obj->fk_user_holiday_validator; + // Store forced second approver choice / Enregistre le second valideur forcé + $this->fk_user_holiday_validator2 = $obj->fk_user_holiday_validator2; $this->default_range = $obj->default_range; $this->default_c_exp_tax_cat = $obj->default_c_exp_tax_cat; @@ -2263,6 +2274,8 @@ public function update($user, $notrigger = 0, $nosyncmember = 0, $nosyncmemberpa $sql .= ", fk_user_modif = ".($this->user_modification_id > 0 ? "'".((int) $this->user_modification_id)."'" : "null"); $sql .= ", fk_user_expense_validator = ".($this->fk_user_expense_validator > 0 ? "'".((int) $this->fk_user_expense_validator)."'" : "null"); $sql .= ", fk_user_holiday_validator = ".($this->fk_user_holiday_validator > 0 ? "'".((int) $this->fk_user_holiday_validator)."'" : "null"); + // Persist forced second approver linkage / Sauvegarde le lien vers le second valideur forcé + $sql .= ", fk_user_holiday_validator2 = ".($this->fk_user_holiday_validator2 > 0 ? "'".((int) $this->fk_user_holiday_validator2)."'" : "null"); if (isset($this->thm) || $this->thm != '') { $sql .= ", thm= ".($this->thm != '' ? "'".$this->db->escape($this->thm)."'" : "null"); }