alxarafe /
alixar
| 1 | <?php |
||
| 2 | |||
| 3 | /* Copyright (C) 2005 Matthieu Valleton <[email protected]> |
||
| 4 | * Copyright (C) 2005 Eric Seigne <[email protected]> |
||
| 5 | * Copyright (C) 2006-2015 Laurent Destailleur <[email protected]> |
||
| 6 | * Copyright (C) 2007 Patrick Raguin <[email protected]> |
||
| 7 | * Copyright (C) 2005-2012 Regis Houssin <[email protected]> |
||
| 8 | * Copyright (C) 2019-2024 Frédéric France <[email protected]> |
||
| 9 | * Copyright (C) 2024 Rafael San José <[email protected]> |
||
| 10 | * |
||
| 11 | * This program is free software; you can redistribute it and/or modify |
||
| 12 | * it under the terms of the GNU General Public License as published by |
||
| 13 | * the Free Software Foundation; either version 3 of the License, or |
||
| 14 | * (at your option) any later version. |
||
| 15 | * |
||
| 16 | * This program is distributed in the hope that it will be useful, |
||
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 19 | * GNU General Public License for more details. |
||
| 20 | * |
||
| 21 | * You should have received a copy of the GNU General Public License |
||
| 22 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||
| 23 | */ |
||
| 24 | |||
| 25 | use Dolibarr\Code\Core\Classes\Form; |
||
| 26 | use Dolibarr\Code\User\Classes\User; |
||
| 27 | use Dolibarr\Lib\ViewMain; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * \file htdocs/user/hierarchy.php |
||
| 31 | * \ingroup user |
||
| 32 | * \brief Page of hierarchy view of user module |
||
| 33 | */ |
||
| 34 | |||
| 35 | // Load Dolibarr environment |
||
| 36 | require constant('DOL_DOCUMENT_ROOT') . '/main.inc.php'; |
||
| 37 | require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/treeview.lib.php'; |
||
| 38 | |||
| 39 | // Load translation files required by page |
||
| 40 | $langs->loadLangs(array('users', 'companies', 'hrm', 'salaries')); |
||
| 41 | |||
| 42 | // Security check (for external users) |
||
| 43 | $socid = 0; |
||
| 44 | if ($user->socid > 0) { |
||
| 45 | $socid = $user->socid; |
||
| 46 | } |
||
| 47 | |||
| 48 | $optioncss = GETPOST('optioncss', 'alpha'); |
||
| 49 | $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'userlist'; // To manage different context of search |
||
| 50 | $mode = GETPOST("mode", 'alpha'); |
||
| 51 | if (empty($mode)) { |
||
| 52 | $mode = 'hierarchy'; |
||
| 53 | } |
||
| 54 | |||
| 55 | $sortfield = GETPOST('sortfield', 'aZ09comma'); |
||
| 56 | $sortorder = GETPOST('sortorder', 'aZ09comma'); |
||
| 57 | $page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page"); |
||
| 58 | |||
| 59 | |||
| 60 | $search_status = GETPOST('search_status', 'intcomma'); |
||
| 61 | if ($search_status == '') { |
||
| 62 | $search_status = '1'; |
||
| 63 | } |
||
| 64 | |||
| 65 | if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // Both test are required to be compatible with all browsers |
||
| 66 | $search_status = ""; |
||
| 67 | } |
||
| 68 | |||
| 69 | $search_employee = -1; |
||
| 70 | if ($contextpage == 'employeelist') { |
||
| 71 | $search_employee = 1; |
||
| 72 | } |
||
| 73 | |||
| 74 | $userstatic = new User($db); |
||
| 75 | |||
| 76 | // Define value to know what current user can do on users |
||
| 77 | $canadduser = (!empty($user->admin) || $user->hasRight("user", "user", "write")); |
||
| 78 | |||
| 79 | // Permission to list |
||
| 80 | if (isModEnabled('salaries') && $contextpage == 'employeelist' && $search_employee == 1) { |
||
| 81 | if (!$user->hasRight("salaries", "read")) { |
||
| 82 | accessforbidden(); |
||
| 83 | } |
||
| 84 | } else { |
||
| 85 | if (!$user->hasRight("user", "user", "read") && empty($user->admin)) { |
||
| 86 | accessforbidden(); |
||
| 87 | } |
||
| 88 | } |
||
| 89 | |||
| 90 | $childids = $user->getAllChildIds(1); |
||
| 91 | |||
| 92 | /* |
||
| 93 | * View |
||
| 94 | */ |
||
| 95 | |||
| 96 | $form = new Form($db); |
||
| 97 | |||
| 98 | $help_url = 'EN:Module_Users|FR:Module_Utilisateurs|ES:Módulo_Usuarios|DE:Modul_Benutzer'; |
||
| 99 | if ($contextpage == 'employeelist' && $search_employee == 1) { |
||
| 100 | $title = $langs->trans("Employees"); |
||
| 101 | } else { |
||
| 102 | $title = $langs->trans("Users"); |
||
| 103 | } |
||
| 104 | $arrayofjs = array( |
||
| 105 | '/includes/jquery/plugins/jquerytreeview/jquery.treeview.js', |
||
| 106 | '/includes/jquery/plugins/jquerytreeview/lib/jquery.cookie.js', |
||
| 107 | ); |
||
| 108 | $arrayofcss = array('/includes/jquery/plugins/jquerytreeview/jquery.treeview.css'); |
||
| 109 | |||
| 110 | ViewMain::llxHeader('', $title, $help_url, '', 0, 0, $arrayofjs, $arrayofcss, '', 'bodyforlist mod-user page-hierarchy'); |
||
| 111 | |||
| 112 | $filters = []; |
||
| 113 | if (($search_status != '' && $search_status >= 0)) { |
||
| 114 | $filters[] = "statut = " . ((int)$search_status); |
||
| 115 | } |
||
| 116 | if (($search_employee != '' && $search_employee >= 0)) { |
||
| 117 | $filters[] = "employee = " . ((int)$search_employee); |
||
| 118 | } |
||
| 119 | $sqlfilter = ''; |
||
| 120 | if (!empty($filters)) { |
||
| 121 | $sqlfilter = implode(' AND ', $filters); |
||
| 122 | } |
||
| 123 | // Load hierarchy of users |
||
| 124 | $user_arbo_all = $userstatic->get_full_tree(0, ''); |
||
| 125 | if ($sqlfilter) { |
||
| 126 | $user_arbo = $userstatic->get_full_tree(0, $sqlfilter); |
||
| 127 | } else { |
||
| 128 | $user_arbo = $user_arbo_all; |
||
| 129 | } |
||
| 130 | |||
| 131 | // Count total nb of records |
||
| 132 | $nbtotalofrecords = count($user_arbo); |
||
| 133 | |||
| 134 | |||
| 135 | if (!is_array($user_arbo) && $user_arbo < 0) { |
||
| 136 | setEventMessages($userstatic->error, $userstatic->errors, 'warnings'); |
||
| 137 | } else { |
||
| 138 | // Define fulltree array |
||
| 139 | $fulltree = $user_arbo; |
||
| 140 | //var_dump($fulltree); |
||
| 141 | // Define data (format for treeview) |
||
| 142 | $data = array(); |
||
| 143 | $data[0] = array('rowid' => 0, 'fk_menu' => -1, 'title' => "racine", 'mainmenu' => '', 'leftmenu' => '', 'fk_mainmenu' => '', 'fk_leftmenu' => ''); |
||
| 144 | |||
| 145 | foreach ($fulltree as $key => $val) { |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 146 | $userstatic->id = $val['id']; |
||
| 147 | $userstatic->ref = (string)$val['id']; |
||
| 148 | $userstatic->login = $val['login']; |
||
| 149 | $userstatic->firstname = $val['firstname']; |
||
| 150 | $userstatic->lastname = $val['lastname']; |
||
| 151 | $userstatic->status = $val['statut']; |
||
| 152 | $userstatic->email = $val['email']; |
||
| 153 | $userstatic->gender = $val['gender']; |
||
| 154 | $userstatic->socid = $val['fk_soc']; |
||
| 155 | $userstatic->admin = $val['admin']; |
||
| 156 | $userstatic->entity = $val['entity']; |
||
| 157 | $userstatic->photo = $val['photo']; |
||
| 158 | |||
| 159 | $entity = $val['entity']; |
||
| 160 | $entitystring = ''; |
||
| 161 | |||
| 162 | // TODO Set of entitystring should be done with a hook |
||
| 163 | if (isModEnabled('multicompany') && is_object($mc)) { |
||
| 164 | if (empty($entity)) { |
||
| 165 | $entitystring = $langs->trans("AllEntities"); |
||
| 166 | } else { |
||
| 167 | $mc->getInfo($entity); |
||
| 168 | $entitystring = $mc->label; |
||
| 169 | } |
||
| 170 | } |
||
| 171 | |||
| 172 | $li = $userstatic->getNomUrl(-1, '', 0, 1); |
||
| 173 | if (isModEnabled('multicompany') && $userstatic->admin && !$userstatic->entity) { |
||
| 174 | $li .= img_picto($langs->trans("SuperAdministratorDesc"), 'redstar', 'class="valignmiddle paddingright paddingleft"'); |
||
| 175 | } elseif ($userstatic->admin) { |
||
| 176 | $li .= img_picto($langs->trans("AdministratorDesc"), 'star', 'class="valignmiddle paddingright paddingleft"'); |
||
| 177 | } |
||
| 178 | $li .= ' <span class="opacitymedium">(' . $val['login'] . ($entitystring ? ' - ' . $entitystring : '') . ')</span>'; |
||
| 179 | |||
| 180 | $entry = '<table class="nobordernopadding centpercent"><tr class="trtree"><td class="' . ($val['statut'] ? 'usertdenabled' : 'usertddisabled') . '">' . $li . '</td><td align="right" class="' . ($val['statut'] ? 'usertdenabled' : 'usertddisabled') . '">' . $userstatic->getLibStatut(2) . '</td></tr></table>'; |
||
| 181 | |||
| 182 | $data[$val['rowid']] = array( |
||
| 183 | 'rowid' => $val['rowid'], |
||
| 184 | 'fk_menu' => $val['fk_user'], // TODO Replace fk_menu with fk_parent |
||
| 185 | 'statut' => $val['statut'], |
||
| 186 | 'entry' => $entry |
||
| 187 | ); |
||
| 188 | } |
||
| 189 | |||
| 190 | // Loop on $data to link user linked to a parent that was excluded by the filter |
||
| 191 | foreach ($data as $key => $tmpdata) { |
||
| 192 | $idparent = $tmpdata['fk_menu']; |
||
| 193 | // Loop to check if parent exists |
||
| 194 | if ($idparent > 0) { |
||
| 195 | $parentfound = array_key_exists($idparent, $data) ? 1 : 0; |
||
| 196 | |||
| 197 | $i = 0; |
||
| 198 | while (!$parentfound && $i < 50) { |
||
| 199 | // Parent was not found but we need it to show the child, so we reintroduce the parent |
||
| 200 | if (!empty($user_arbo_all[$idparent])) { |
||
| 201 | $val = $user_arbo_all[$idparent]; |
||
| 202 | $userstatic->id = $val['id']; |
||
| 203 | $userstatic->ref = (string)$val['id']; |
||
| 204 | $userstatic->login = $val['login']; |
||
| 205 | $userstatic->firstname = $val['firstname']; |
||
| 206 | $userstatic->lastname = $val['lastname']; |
||
| 207 | $userstatic->status = $val['statut']; |
||
| 208 | $userstatic->email = $val['email']; |
||
| 209 | $userstatic->gender = $val['gender']; |
||
| 210 | $userstatic->socid = $val['fk_soc']; |
||
| 211 | $userstatic->admin = $val['admin']; |
||
| 212 | $userstatic->entity = $val['entity']; |
||
| 213 | $userstatic->photo = $val['photo']; |
||
| 214 | |||
| 215 | $entity = $val['entity']; |
||
| 216 | $entitystring = ''; |
||
| 217 | |||
| 218 | // TODO Set of entitystring should be done with a hook |
||
| 219 | if (isModEnabled('multicompany') && is_object($mc)) { |
||
| 220 | if (empty($entity)) { |
||
| 221 | $entitystring = $langs->trans("AllEntities"); |
||
| 222 | } else { |
||
| 223 | $mc->getInfo($entity); |
||
| 224 | $entitystring = $mc->label; |
||
| 225 | } |
||
| 226 | } |
||
| 227 | |||
| 228 | $li = '<span class="opacitymedium">'; |
||
| 229 | $li .= $userstatic->getNomUrl(-1, '', 0, 1); |
||
| 230 | if (isModEnabled('multicompany') && $userstatic->admin && !$userstatic->entity) { |
||
| 231 | $li .= img_picto($langs->trans("SuperAdministrator"), 'redstar'); |
||
| 232 | } elseif ($userstatic->admin) { |
||
| 233 | $li .= img_picto($langs->trans("Administrator"), 'star'); |
||
| 234 | } |
||
| 235 | $li .= ' <span class="opacitymedium">(' . $val['login'] . ($entitystring ? ' - ' . $entitystring : '') . ')</span>'; |
||
| 236 | $li .= ' - <span class="opacitymedium">' . $langs->trans("ExcludedByFilter") . '</span>'; |
||
| 237 | $li .= '</span>'; |
||
| 238 | |||
| 239 | $entry = '<table class="nobordernopadding centpercent"><tr class="trtree"><td class="' . ($val['statut'] ? 'usertdenabled' : 'usertddisabled') . '">' . $li . '</td><td align="right" class="' . ($val['statut'] ? 'usertdenabled' : 'usertddisabled') . '">' . $userstatic->getLibStatut(2) . '</td></tr></table>'; |
||
| 240 | |||
| 241 | $data[$idparent] = array( |
||
| 242 | 'rowid' => $idparent, |
||
| 243 | 'fk_menu' => $user_arbo_all[$idparent]['fk_user'], |
||
| 244 | 'statut' => $user_arbo_all[$idparent]['statut'], |
||
| 245 | 'entry' => $entry |
||
| 246 | ); |
||
| 247 | $idparent = $user_arbo_all[$idparent]['fk_user']; |
||
| 248 | if ($idparent > 0) { |
||
| 249 | $parentfound = array_key_exists($idparent, $data) ? 1 : 0; |
||
| 250 | } else { |
||
| 251 | $parentfound = 1; |
||
| 252 | } |
||
| 253 | //var_dump($data[$idparent]); |
||
| 254 | } else { |
||
| 255 | // We should not be here. If a record has a parent id, parent id should be into $user_arbo_all |
||
| 256 | $data[$key]['fk_menu'] = -2; |
||
| 257 | if (empty($data[-2])) { |
||
| 258 | $li = '<span class="opacitymedium">' . $langs->trans("WarningParentIDDoesNotExistAnymore") . '</span>'; |
||
| 259 | $entry = '<table class="nobordernopadding centpercent"><tr class="trtree"><td class="usertddisabled">' . $li . '</td><td align="right" class="usertddisabled"></td></tr></table>'; |
||
| 260 | $data[-2] = array( |
||
| 261 | 'rowid' => '-2', |
||
| 262 | 'fk_menu' => null, |
||
| 263 | 'statut' => '1', |
||
| 264 | 'entry' => $entry |
||
| 265 | ); |
||
| 266 | } |
||
| 267 | $parentfound = 1; |
||
| 268 | } |
||
| 269 | |||
| 270 | $i++; |
||
| 271 | } |
||
| 272 | } |
||
| 273 | } |
||
| 274 | //var_dump($data);exit; |
||
| 275 | |||
| 276 | $param = "&search_status=" . urlencode($search_status); |
||
| 277 | $param = "&contextpage=" . urlencode($contextpage); |
||
| 278 | |||
| 279 | $newcardbutton = ''; |
||
| 280 | $newcardbutton .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars paddingleft imgforviewmode', constant('BASE_URL') . '/user/list.php?mode=common' . preg_replace('/(&|\?)*mode=[^&]+/', '', $param), '', ((empty($mode) || $mode == 'common') ? 2 : 1), array('morecss' => 'reposition')); |
||
| 281 | $newcardbutton .= dolGetButtonTitle($langs->trans('HierarchicView'), '', 'fa fa-stream paddingleft imgforviewmode', constant('BASE_URL') . '/user/hierarchy.php?mode=hierarchy' . preg_replace('/(&|\?)*mode=[^&]+/', '', $param), '', (($mode == 'hierarchy') ? 2 : 1), array('morecss' => 'reposition')); |
||
| 282 | $newcardbutton .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', constant('BASE_URL') . '/user/list.php?mode=kanban' . preg_replace('/(&|\?)*mode=[^&]+/', '', $param), '', ($mode == 'kanban' ? 2 : 1), array('morecss' => 'reposition')); |
||
| 283 | $newcardbutton .= dolGetButtonTitleSeparator(); |
||
| 284 | $newcardbutton .= dolGetButtonTitle($langs->trans('NewUser'), '', 'fa fa-plus-circle', constant('BASE_URL') . '/user/card.php?action=create' . ($mode == 'employee' ? '&employee=1' : '') . '&leftmenu=', '', $canadduser); |
||
| 285 | |||
| 286 | $massactionbutton = ''; |
||
| 287 | $num = 0; |
||
| 288 | $limit = 0; |
||
| 289 | |||
| 290 | print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'user', 0, $newcardbutton, '', $limit, 0, 0, 1); |
||
| 291 | |||
| 292 | print '<form method="POST" id="searchFormList" action="' . $_SERVER["PHP_SELF"] . '">' . "\n"; |
||
| 293 | if ($optioncss != '') { |
||
| 294 | print '<input type="hidden" name="optioncss" value="' . $optioncss . '">'; |
||
| 295 | } |
||
| 296 | print '<input type="hidden" name="token" value="' . newToken() . '">'; |
||
| 297 | print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">'; |
||
| 298 | print '<input type="hidden" name="sortfield" value="' . $sortfield . '">'; |
||
| 299 | print '<input type="hidden" name="sortorder" value="' . $sortorder . '">'; |
||
| 300 | print '<input type="hidden" name="page" value="' . $page . '">'; |
||
| 301 | print '<input type="hidden" name="contextpage" value="' . $contextpage . '">'; |
||
| 302 | print '<input type="hidden" name="mode" value="' . $mode . '">'; |
||
| 303 | |||
| 304 | print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table |
||
| 305 | print '<table class="liste nohover centpercent">'; |
||
| 306 | |||
| 307 | print '<tr class="liste_titre_filter">'; |
||
| 308 | // Action column |
||
| 309 | if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { |
||
| 310 | print '<td class="liste_titre maxwidthsearch">'; |
||
| 311 | $searchpicto = $form->showFilterAndCheckAddButtons(0); |
||
| 312 | print $searchpicto; |
||
| 313 | print '</td>'; |
||
| 314 | } |
||
| 315 | print '<td class="liste_titre"> </td>'; |
||
| 316 | print '<td class="liste_titre"> </td>'; |
||
| 317 | // Status |
||
| 318 | print '<td class="liste_titre right parentonrightofpage">'; |
||
| 319 | print $form->selectarray('search_status', array('-1' => '', '0' => $langs->trans('Disabled'), '1' => $langs->trans('Enabled')), $search_status, 0, 0, 0, '', 0, 0, 0, '', 'minwidth75imp onrightofpage width100'); |
||
| 320 | print '</td>'; |
||
| 321 | // Action column |
||
| 322 | if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { |
||
| 323 | print '<td class="liste_titre maxwidthsearch">'; |
||
| 324 | $searchpicto = $form->showFilterAndCheckAddButtons(0); |
||
| 325 | print $searchpicto; |
||
| 326 | print '</td>'; |
||
| 327 | } |
||
| 328 | print '</tr>'; |
||
| 329 | |||
| 330 | print '<tr class="liste_titre">'; |
||
| 331 | // Action column |
||
| 332 | if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { |
||
| 333 | print_liste_field_titre('', $_SERVER["PHP_SELF"], "", '', '', '', '', '', 'maxwidthsearch '); |
||
| 334 | } |
||
| 335 | print_liste_field_titre("HierarchicView"); |
||
| 336 | print_liste_field_titre('<div id="iddivjstreecontrol"><a href="#">' . img_picto('', 'folder', 'class="paddingright"') . '<span class="hideonsmartphone">' . $langs->trans("UndoExpandAll") . '</span></a> | <a href="#">' . img_picto('', 'folder-open', 'class="paddingright"') . '<span class="hideonsmartphone">' . $langs->trans("ExpandAll") . '</span></a></div>', $_SERVER['PHP_SELF'], "", '', "", 'align="center"'); |
||
| 337 | print_liste_field_titre("Status", $_SERVER['PHP_SELF'], "", '', "", '', '', '', 'right onrightofpage'); |
||
| 338 | // Action column |
||
| 339 | if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { |
||
| 340 | print_liste_field_titre('', $_SERVER["PHP_SELF"], "", '', '', '', '', '', 'maxwidthsearch '); |
||
| 341 | } |
||
| 342 | print '</tr>'; |
||
| 343 | |||
| 344 | |||
| 345 | $nbofentries = (count($data) - 1); |
||
| 346 | |||
| 347 | if ($nbofentries > 0) { |
||
| 348 | print '<tr>'; |
||
| 349 | // Action column |
||
| 350 | if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { |
||
| 351 | print '<td></td>'; |
||
| 352 | } |
||
| 353 | print '<td colspan="3">'; |
||
| 354 | tree_recur($data, $data[0], 0); |
||
| 355 | print '</td>'; |
||
| 356 | // Action column |
||
| 357 | if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { |
||
| 358 | print '<td></td>'; |
||
| 359 | } |
||
| 360 | print '</tr>'; |
||
| 361 | } else { |
||
| 362 | print '<tr class="oddeven">'; |
||
| 363 | print '<td colspan="3">'; |
||
| 364 | print '<table class="nobordernopadding"><tr class="nobordernopadding"><td>' . img_picto_common('', 'treemenu/branchbottom.gif') . '</td>'; |
||
| 365 | print '<td valign="middle">'; |
||
| 366 | print $langs->trans("NoCategoryYet"); |
||
| 367 | print '</td>'; |
||
| 368 | print '<td> </td>'; |
||
| 369 | print '</table>'; |
||
| 370 | print '</td>'; |
||
| 371 | print '<td></td>'; |
||
| 372 | print '</tr>'; |
||
| 373 | } |
||
| 374 | |||
| 375 | print "</table>"; |
||
| 376 | print '</div>'; |
||
| 377 | |||
| 378 | print "</form>\n"; |
||
| 379 | } |
||
| 380 | |||
| 381 | // |
||
| 382 | /*print '<script type="text/javascript"> |
||
| 383 | jQuery(document).ready(function() { |
||
| 384 | function init_myfunc() |
||
| 385 | { |
||
| 386 | jQuery(".usertddisabled").hide(); |
||
| 387 | } |
||
| 388 | init_myfunc(); |
||
| 389 | }); |
||
| 390 | </script>'; |
||
| 391 | */ |
||
| 392 | |||
| 393 | // End of page |
||
| 394 | ViewMain::llxFooter(); |
||
| 395 | $db->close(); |
||
| 396 |