1<?php 2/* Copyright (C) 2003-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org> 3 * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net> 4 * Copyright (C) 2005-2011 Regis Houssin <regis.houssin@inodbox.com> 5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org> 6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be> 7 * Copyright (C) 2010-2013 Juanjo Menent <jmenent@2byte.es> 8 * Copyright (C) 2011-2018 Philippe Grand <philippe.grand@atoo-net.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 3 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24/** 25 * \file htdocs/admin/supplier_order.php 26 * \ingroup fournisseur 27 * \brief Page d'administration-configuration du module Fournisseur 28 */ 29 30require '../main.inc.php'; 31require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; 32require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; 33require_once DOL_DOCUMENT_ROOT.'/core/lib/fourn.lib.php'; 34require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php'; 35require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; 36 37// Load translation files required by the page 38$langs->loadLangs(array("admin", "other", "orders", "stocks")); 39 40if (!$user->admin) 41accessforbidden(); 42 43$type = GETPOST('type', 'alpha'); 44$value = GETPOST('value', 'alpha'); 45$label = GETPOST('label', 'alpha'); 46$action = GETPOST('action', 'aZ09'); 47$scandir = GETPOST('scan_dir', 'alpha'); 48 49$specimenthirdparty = new Societe($db); 50$specimenthirdparty->initAsSpecimen(); 51 52 53/* 54 * Actions 55 */ 56 57include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; 58 59if ($action == 'updateMask') 60{ 61 $maskconstorder = GETPOST('maskconstorder', 'alpha'); 62 $maskvalue = GETPOST('maskorder', 'alpha'); 63 64 if ($maskconstorder) $res = dolibarr_set_const($db, $maskconstorder, $maskvalue, 'chaine', 0, '', $conf->entity); 65 66 if (!($res > 0)) $error++; 67 68 if (!$error) 69 { 70 setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); 71 } else { 72 setEventMessages($langs->trans("Error"), null, 'errors'); 73 } 74} elseif ($action == 'specimen') // For orders 75{ 76 $modele = GETPOST('module', 'alpha'); 77 78 $commande = new CommandeFournisseur($db); 79 $commande->initAsSpecimen(); 80 $commande->thirdparty = $specimenthirdparty; 81 82 // Search template files 83 $file = ''; $classname = ''; $filefound = 0; 84 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); 85 foreach ($dirmodels as $reldir) 86 { 87 $file = dol_buildpath($reldir."core/modules/supplier_order/doc/pdf_".$modele.".modules.php", 0); 88 if (file_exists($file)) 89 { 90 $filefound = 1; 91 $classname = "pdf_".$modele; 92 break; 93 } 94 } 95 96 if ($filefound) 97 { 98 require_once $file; 99 100 $module = new $classname($db, $commande); 101 102 if ($module->write_file($commande, $langs) > 0) 103 { 104 header("Location: ".DOL_URL_ROOT."/document.php?modulepart=commande_fournisseur&file=SPECIMEN.pdf"); 105 return; 106 } else { 107 setEventMessages($module->error, $module->errors, 'errors'); 108 dol_syslog($module->error, LOG_ERR); 109 } 110 } else { 111 setEventMessages($langs->trans("ErrorModuleNotFound"), null, 'errors'); 112 dol_syslog($langs->trans("ErrorModuleNotFound"), LOG_ERR); 113 } 114} 115 116// Activate a model 117elseif ($action == 'set') 118{ 119 $ret = addDocumentModel($value, $type, $label, $scandir); 120} elseif ($action == 'del') 121{ 122 $ret = delDocumentModel($value, $type); 123 if ($ret > 0) 124 { 125 if ($conf->global->COMMANDE_SUPPLIER_ADDON_PDF == "$value") dolibarr_del_const($db, 'COMMANDE_SUPPLIER_ADDON_PDF', $conf->entity); 126 } 127} 128 129// Set default model 130elseif ($action == 'setdoc') 131{ 132 if (dolibarr_set_const($db, "COMMANDE_SUPPLIER_ADDON_PDF", $value, 'chaine', 0, '', $conf->entity)) 133 { 134 // La constante qui a ete lue en avant du nouveau set 135 // on passe donc par une variable pour avoir un affichage coherent 136 $conf->global->COMMANDE_SUPPLIER_ADDON_PDF = $value; 137 } 138 139 // On active le modele 140 $ret = delDocumentModel($value, $type); 141 if ($ret > 0) 142 { 143 $ret = addDocumentModel($value, $type, $label, $scandir); 144 } 145} elseif ($action == 'setmod') 146{ 147 // TODO Verifier si module numerotation choisi peut etre active 148 // par appel methode canBeActivated 149 150 dolibarr_set_const($db, "COMMANDE_SUPPLIER_ADDON_NUMBER", $value, 'chaine', 0, '', $conf->entity); 151} elseif ($action == 'addcat') 152{ 153 $fourn = new Fournisseur($db); 154 $fourn->CreateCategory($user, GETPOST('cat', 'alphanohtml')); 155} elseif ($action == 'set_SUPPLIER_ORDER_OTHER') 156{ 157 $freetext = GETPOST('SUPPLIER_ORDER_FREE_TEXT', 'restricthtml'); // No alpha here, we want exact string 158 $doubleapproval = GETPOST('SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED', 'alpha'); 159 $doubleapproval = price2num($doubleapproval); 160 161 $res1 = dolibarr_set_const($db, "SUPPLIER_ORDER_FREE_TEXT", $freetext, 'chaine', 0, '', $conf->entity); 162 $res2 = dolibarr_set_const($db, "SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED", $doubleapproval, 'chaine', 0, '', $conf->entity); 163 164 // TODO We add/delete permission here until permission can have a condition on a global var 165 include_once DOL_DOCUMENT_ROOT.'/core/modules/modFournisseur.class.php'; 166 $newmodule = new modFournisseur($db); 167 168 if ($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) 169 { 170 // clear default rights array 171 $newmodule->rights = array(); 172 // add new right 173 $r = 0; 174 $newmodule->rights[$r][0] = 1190; 175 $newmodule->rights[$r][1] = $langs->trans("Permission1190"); 176 $newmodule->rights[$r][2] = 'w'; 177 $newmodule->rights[$r][3] = 0; 178 $newmodule->rights[$r][4] = 'commande'; 179 $newmodule->rights[$r][5] = 'approve2'; 180 181 // Insert 182 $newmodule->insert_permissions(1); 183 } else { 184 // Remove all rights with Permission1190 185 $newmodule->delete_permissions(); 186 187 // Add all right without Permission1190 188 $newmodule->insert_permissions(1); 189 } 190} 191 192// Activate ask for payment bank 193elseif ($action == 'set_BANK_ASK_PAYMENT_BANK_DURING_SUPPLIER_ORDER') 194{ 195 $res = dolibarr_set_const($db, "BANK_ASK_PAYMENT_BANK_DURING_SUPPLIER_ORDER", $value, 'chaine', 0, '', $conf->entity); 196 197 if (!($res > 0)) $error++; 198 199 if (!$error) 200 { 201 setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); 202 } else { 203 setEventMessages($langs->trans("Error"), null, 'errors'); 204 } 205} 206 207 208/* 209 * View 210 */ 211 212$form = new Form($db); 213 214$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); 215 216llxHeader("", ""); 217 218$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>'; 219print load_fiche_titre($langs->trans("SuppliersSetup"), $linkback, 'title_setup'); 220 221print "<br>"; 222 223$head = supplierorder_admin_prepare_head(); 224 225print dol_get_fiche_head($head, 'order', $langs->trans("Suppliers"), -1, 'company'); 226 227 228// Supplier order numbering module 229 230print load_fiche_titre($langs->trans("OrdersNumberingModules"), '', ''); 231 232print '<table class="noborder centpercent">'; 233print '<tr class="liste_titre">'; 234print '<td width="100">'.$langs->trans("Name").'</td>'; 235print '<td>'.$langs->trans("Description").'</td>'; 236print '<td>'.$langs->trans("Example").'</td>'; 237print '<td align="center" width="60">'.$langs->trans("Status").'</td>'; 238print '<td align="center" width="16">'.$langs->trans("ShortInfo").'</td>'; 239print "</tr>\n"; 240 241clearstatcache(); 242 243foreach ($dirmodels as $reldir) 244{ 245 $dir = dol_buildpath($reldir."core/modules/supplier_order/"); 246 247 if (is_dir($dir)) 248 { 249 $handle = opendir($dir); 250 if (is_resource($handle)) 251 { 252 while (($file = readdir($handle)) !== false) 253 { 254 if (substr($file, 0, 25) == 'mod_commande_fournisseur_' && substr($file, dol_strlen($file) - 3, 3) == 'php') 255 { 256 $file = substr($file, 0, dol_strlen($file) - 4); 257 258 require_once $dir.$file.'.php'; 259 260 $module = new $file; 261 262 if ($module->isEnabled()) 263 { 264 // Show modules according to features level 265 if ($module->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2) continue; 266 if ($module->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1) continue; 267 268 269 print '<tr class="oddeven"><td>'.$module->nom."</td><td>\n"; 270 print $module->info(); 271 print '</td>'; 272 273 // Show example of numbering module 274 print '<td class="nowrap">'; 275 $tmp = $module->getExample(); 276 if (preg_match('/^Error/', $tmp)) { 277 $langs->load("errors"); 278 print '<div class="error">'.$langs->trans($tmp).'</div>'; 279 } elseif ($tmp == 'NotConfigured') print $langs->trans($tmp); 280 else print $tmp; 281 print '</td>'."\n"; 282 283 print '<td class="center">'; 284 if ($conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER == "$file") 285 { 286 print img_picto($langs->trans("Activated"), 'switch_on'); 287 } else { 288 print '<a class="reposition" href="'.$_SERVER["PHP_SELF"].'?action=setmod&token='.newToken().'&value='.urlencode($file).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'switch_off').'</a>'; 289 } 290 print '</td>'; 291 292 $commande = new CommandeFournisseur($db); 293 $commande->initAsSpecimen(); 294 295 // Info 296 $htmltooltip = ''; 297 $htmltooltip .= ''.$langs->trans("Version").': <b>'.$module->getVersion().'</b><br>'; 298 $nextval = $module->getNextValue($mysoc, $commande); 299 if ("$nextval" != $langs->trans("NotAvailable")) { // Keep " on nextval 300 $htmltooltip .= ''.$langs->trans("NextValue").': '; 301 if ($nextval) { 302 if (preg_match('/^Error/', $nextval) || $nextval == 'NotConfigured') 303 $nextval = $langs->trans($nextval); 304 $htmltooltip .= $nextval.'<br>'; 305 } else { 306 $htmltooltip .= $langs->trans($module->error).'<br>'; 307 } 308 } 309 310 print '<td class="center">'; 311 print $form->textwithpicto('', $htmltooltip, 1, 0); 312 print '</td>'; 313 314 print '</tr>'; 315 } 316 } 317 } 318 closedir($handle); 319 } 320 } 321} 322 323print '</table><br>'; 324 325 326/* 327 * Documents models for supplier orders 328 */ 329 330print load_fiche_titre($langs->trans("OrdersModelModule"), '', ''); 331 332// Defini tableau def de modele 333$def = array(); 334 335$sql = "SELECT nom"; 336$sql .= " FROM ".MAIN_DB_PREFIX."document_model"; 337$sql .= " WHERE type = 'order_supplier'"; 338$sql .= " AND entity = ".$conf->entity; 339 340$resql = $db->query($sql); 341if ($resql) 342{ 343 $i = 0; 344 $num_rows = $db->num_rows($resql); 345 while ($i < $num_rows) 346 { 347 $array = $db->fetch_array($resql); 348 array_push($def, $array[0]); 349 $i++; 350 } 351} else { 352 dol_print_error($db); 353} 354 355print '<table class="noborder centpercent">'."\n"; 356print '<tr class="liste_titre">'."\n"; 357print '<td width="100">'.$langs->trans("Name").'</td>'."\n"; 358print '<td>'.$langs->trans("Description").'</td>'."\n"; 359print '<td align="center" width="60">'.$langs->trans("Status").'</td>'."\n"; 360print '<td align="center" width="60">'.$langs->trans("Default").'</td>'."\n"; 361print '<td align="center" width="40">'.$langs->trans("ShortInfo").'</td>'; 362print '<td align="center" width="40">'.$langs->trans("Preview").'</td>'; 363print '</tr>'."\n"; 364 365clearstatcache(); 366 367foreach ($dirmodels as $reldir) 368{ 369 $realpath = $reldir."core/modules/supplier_order/doc"; 370 $dir = dol_buildpath($realpath); 371 372 if (is_dir($dir)) 373 { 374 $handle = opendir($dir); 375 if (is_resource($handle)) 376 { 377 while (($file = readdir($handle)) !== false) 378 { 379 if (preg_match('/\.modules\.php$/i', $file) && preg_match('/^(pdf_|doc_)/', $file)) 380 { 381 $name = substr($file, 4, dol_strlen($file) - 16); 382 $classname = substr($file, 0, dol_strlen($file) - 12); 383 384 require_once $dir.'/'.$file; 385 $module = new $classname($db, new CommandeFournisseur($db)); 386 387 388 print "<tr class=\"oddeven\">\n"; 389 print "<td>"; 390 print (empty($module->name) ? $name : $module->name); 391 print "</td>\n"; 392 print "<td>\n"; 393 require_once $dir.'/'.$file; 394 $module = new $classname($db, $specimenthirdparty); 395 if (method_exists($module, 'info')) print $module->info($langs); 396 else print $module->description; 397 print "</td>\n"; 398 399 // Active 400 if (in_array($name, $def)) 401 { 402 print '<td class="center">'."\n"; 403 if ($conf->global->COMMANDE_SUPPLIER_ADDON_PDF != "$name") 404 { 405 print '<a href="'.$_SERVER["PHP_SELF"].'?action=del&value='.$name.'&scan_dir='.$module->scandir.'&label='.urlencode($module->name).'&type=order_supplier">'; 406 print img_picto($langs->trans("Enabled"), 'switch_on'); 407 print '</a>'; 408 } else { 409 print img_picto($langs->trans("Enabled"), 'switch_on'); 410 } 411 print "</td>"; 412 } else { 413 print '<td class="center">'."\n"; 414 print '<a href="'.$_SERVER["PHP_SELF"].'?action=set&token='.newToken().'&value='.$name.'&scan_dir='.$module->scandir.'&label='.urlencode($module->name).'&type=order_supplier">'.img_picto($langs->trans("Disabled"), 'switch_off').'</a>'; 415 print "</td>"; 416 } 417 418 // Default 419 print '<td class="center">'; 420 if ($conf->global->COMMANDE_SUPPLIER_ADDON_PDF == "$name") 421 { 422 print img_picto($langs->trans("Default"), 'on'); 423 } else { 424 print '<a href="'.$_SERVER["PHP_SELF"].'?action=setdoc&token='.newToken().'&value='.$name.'&scan_dir='.$module->scandir.'&label='.urlencode($module->name).'&type=order_supplier"" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'off').'</a>'; 425 } 426 print '</td>'; 427 428 // Info 429 $htmltooltip = ''.$langs->trans("Name").': '.$module->name; 430 $htmltooltip .= '<br>'.$langs->trans("Type").': '.($module->type ? $module->type : $langs->trans("Unknown")); 431 $htmltooltip .= '<br>'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur; 432 $htmltooltip .= '<br>'.$langs->trans("Path").': '.preg_replace('/^\//', '', $realpath).'/'.$file; 433 434 $htmltooltip .= '<br><br><u>'.$langs->trans("FeaturesSupported").':</u>'; 435 $htmltooltip .= '<br>'.$langs->trans("Logo").': '.yn($module->option_logo, 1, 1); 436 $htmltooltip .= '<br>'.$langs->trans("PaymentMode").': '.yn($module->option_modereg, 1, 1); 437 $htmltooltip .= '<br>'.$langs->trans("PaymentConditions").': '.yn($module->option_condreg, 1, 1); 438 print '<td class="center">'; 439 print $form->textwithpicto('', $htmltooltip, 1, 0); 440 print '</td>'; 441 print '<td class="center">'; 442 print '<a href="'.$_SERVER["PHP_SELF"].'?action=specimen&module='.$name.'">'.img_object($langs->trans("Preview"), 'pdf').'</a>'; 443 print '</td>'; 444 445 print "</tr>\n"; 446 } 447 } 448 449 closedir($handle); 450 } 451 } 452} 453 454print '</table><br>'; 455 456/* 457 * Other options 458 */ 459 460print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">'; 461print '<input type="hidden" name="token" value="'.newToken().'">'; 462print '<input type="hidden" name="action" value="set_SUPPLIER_ORDER_OTHER">'; 463 464print load_fiche_titre($langs->trans("OtherOptions"), '', ''); 465print '<table class="noborder centpercent">'; 466print '<tr class="liste_titre">'; 467print '<td>'.$langs->trans("Parameter").'</td>'; 468print '<td align="center" width="60">'.$langs->trans("Value").'</td>'; 469print '<td width="80"> </td>'; 470print "</tr>\n"; 471 472print '<tr class="oddeven"><td>'; 473print $form->textwithpicto($langs->trans("UseDoubleApproval"), $langs->trans("Use3StepsApproval"), 1, 'help').'<br>'; 474print $langs->trans("IfSetToYesDontForgetPermission"); 475print '</td><td>'; 476print '<input type="text" size="6" name="SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED" value="'.$conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED.'">'; 477print '</td><td class="right">'; 478print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">'; 479print "</td></tr>\n"; 480 481 482// Ask for payment bank during supplier order 483/* Kept as hidden for the moment 484if ($conf->banque->enabled) 485{ 486 487 print '<tr class="oddeven"><td>'; 488 print $langs->trans("BANK_ASK_PAYMENT_BANK_DURING_SUPPLIER_ORDER").'</td><td> </td><td align="center">'; 489 if (! empty($conf->use_javascript_ajax)) 490 { 491 print ajax_constantonoff('BANK_ASK_PAYMENT_BANK_DURING_SUPPLIER_ORDER'); 492 } 493 else 494 { 495 if (empty($conf->global->BANK_ASK_PAYMENT_BANK_DURING_ORDER)) 496 { 497 print '<a href="'.$_SERVER['PHP_SELF'].'?action=set_BANK_ASK_PAYMENT_BANK_DURING_SUPPLIER_ORDER&token='.newToken().'&value=1">'.img_picto($langs->trans("Disabled"),'switch_off').'</a>'; 498 } 499 else 500 { 501 print '<a href="'.$_SERVER['PHP_SELF'].'?action=set_BANK_ASK_PAYMENT_BANK_DURING_SUPPLIER_ORDER&token='.newToken().'&value=0">'.img_picto($langs->trans("Enabled"),'switch_on').'</a>'; 502 } 503 } 504 print '</td></tr>'; 505} 506else 507{ 508 509 print '<tr class="oddeven"><td>'; 510 print $langs->trans("BANK_ASK_PAYMENT_BANK_DURING_SUPPLIER_ORDER").'</td><td> </td><td align="center">'.$langs->trans('NotAvailable').'</td></tr>'; 511} 512*/ 513 514$substitutionarray = pdf_getSubstitutionArray($langs, null, null, 2); 515$substitutionarray['__(AnyTranslationKey)__'] = $langs->trans("Translation"); 516$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>'; 517foreach ($substitutionarray as $key => $val) $htmltext .= $key.'<br>'; 518$htmltext .= '</i>'; 519 520print '<tr class="oddeven"><td colspan="2">'; 521print $form->textwithpicto($langs->trans("FreeLegalTextOnOrders"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext, 1, 'help', '', 0, 2, 'freetexttooltip').'<br>'; 522$variablename = 'SUPPLIER_ORDER_FREE_TEXT'; 523if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) 524{ 525 print '<textarea name="'.$variablename.'" class="flat" cols="120">'.$conf->global->$variablename.'</textarea>'; 526} else { 527 include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; 528 $doleditor = new DolEditor($variablename, $conf->global->$variablename, '', 80, 'dolibarr_notes'); 529 print $doleditor->Create(); 530} 531print '</td><td class="right">'; 532print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">'; 533print "</td></tr>\n"; 534 535// Option to add a quality/validation step, on products, after reception. 536print '<tr class="oddeven">'; 537print '<td>'.$langs->trans("UseDispatchStatus").'</td>'; 538print '<td></td>'; 539print '<td class="center">'; 540if ($conf->reception->enabled) 541{ 542 print '<span class="opacitymedium">'.$langs->trans("FeatureNotAvailableWithReceptionModule").'</span>'; 543} else { 544 if ($conf->use_javascript_ajax) { 545 print ajax_constantonoff('SUPPLIER_ORDER_USE_DISPATCH_STATUS'); 546 } else { 547 $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); 548 print $form->selectarray("SUPPLIER_ORDER_USE_DISPATCH_STATUS", $arrval, $conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS); 549 } 550} 551print "</td>\n"; 552print "</tr>\n"; 553 554print '</table><br>'; 555 556print '</form>'; 557 558 559/* 560 * Notifications 561 */ 562 563print load_fiche_titre($langs->trans("Notifications"), '', ''); 564print '<table class="noborder centpercent">'; 565print '<tr class="liste_titre">'; 566print '<td>'.$langs->trans("Parameter").'</td>'; 567print '<td align="center" width="60"></td>'; 568print '<td width="80"> </td>'; 569print "</tr>\n"; 570 571print '<tr class="oddeven"><td colspan="2">'; 572print $langs->trans("YouMayFindNotificationsFeaturesIntoModuleNotification").'<br>'; 573print '</td><td class="right">'; 574print "</td></tr>\n"; 575 576print '</table>'; 577 578// End of page 579llxFooter(); 580$db->close(); 581