repair.php 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662
  1. <?php
  2. /* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  3. * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  4. * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
  5. * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  6. * Copyright (C) 2021 Frédéric France <frederic.france@free.fr>
  7. * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * \file htdocs/install/repair.php
  24. * \brief Run repair script
  25. */
  26. include_once 'inc.php';
  27. if (file_exists($conffile)) {
  28. include_once $conffile;
  29. }
  30. require_once $dolibarr_main_document_root.'/core/lib/admin.lib.php';
  31. include_once $dolibarr_main_document_root.'/core/lib/images.lib.php';
  32. require_once $dolibarr_main_document_root.'/core/class/extrafields.class.php';
  33. require_once 'lib/repair.lib.php';
  34. $step = 2;
  35. $ok = 0;
  36. // Cette page peut etre longue. On augmente le delai autorise.
  37. // Ne fonctionne que si on est pas en safe_mode.
  38. $err = error_reporting();
  39. error_reporting(0);
  40. @set_time_limit(120);
  41. error_reporting($err);
  42. $setuplang = GETPOST("selectlang", 'aZ09', 3) ?GETPOST("selectlang", 'aZ09', 3) : 'auto';
  43. $langs->setDefaultLang($setuplang);
  44. $langs->loadLangs(array("admin", "install", "other"));
  45. if ($dolibarr_main_db_type == "mysqli") {
  46. $choix = 1;
  47. }
  48. if ($dolibarr_main_db_type == "pgsql") {
  49. $choix = 2;
  50. }
  51. if ($dolibarr_main_db_type == "mssql") {
  52. $choix = 3;
  53. }
  54. dolibarr_install_syslog("--- repair: entering upgrade.php page");
  55. if (!is_object($conf)) {
  56. dolibarr_install_syslog("repair: conf file not initialized", LOG_ERR);
  57. }
  58. /*
  59. * View
  60. */
  61. pHeader('', "upgrade2", GETPOST('action', 'aZ09'));
  62. // Action to launch the repair script
  63. $actiondone = 1;
  64. print '<h3>'.$langs->trans("Repair").'</h3>';
  65. print 'Option standard (\'test\' or \'confirmed\') is '.(GETPOST('standard', 'alpha') ?GETPOST('standard', 'alpha') : 'undefined').'<br>'."\n";
  66. // Disable modules
  67. print 'Option force_disable_of_modules_not_found (\'test\' or \'confirmed\') is '.(GETPOST('force_disable_of_modules_not_found', 'alpha') ?GETPOST('force_disable_of_modules_not_found', 'alpha') : 'undefined').'<br>'."\n";
  68. // Files
  69. print 'Option restore_thirdparties_logos (\'test\' or \'confirmed\') is '.(GETPOST('restore_thirdparties_logos', 'alpha') ?GETPOST('restore_thirdparties_logos', 'alpha') : 'undefined').'<br>'."\n";
  70. print 'Option restore_user_pictures (\'test\' or \'confirmed\') is '.(GETPOST('restore_user_pictures', 'alpha') ?GETPOST('restore_user_pictures', 'alpha') : 'undefined').'<br>'."\n";
  71. print 'Option rebuild_product_thumbs (\'test\' or \'confirmed\') is '.(GETPOST('rebuild_product_thumbs', 'alpha') ?GETPOST('rebuild_product_thumbs', 'alpha') : 'undefined').'<br>'."\n";
  72. // Clean tables and data
  73. print 'Option clean_linked_elements (\'test\' or \'confirmed\') is '.(GETPOST('clean_linked_elements', 'alpha') ?GETPOST('clean_linked_elements', 'alpha') : 'undefined').'<br>'."\n";
  74. print 'Option clean_menus (\'test\' or \'confirmed\') is '.(GETPOST('clean_menus', 'alpha') ?GETPOST('clean_menus', 'alpha') : 'undefined').'<br>'."\n";
  75. print 'Option clean_orphelin_dir (\'test\' or \'confirmed\') is '.(GETPOST('clean_orphelin_dir', 'alpha') ?GETPOST('clean_orphelin_dir', 'alpha') : 'undefined').'<br>'."\n";
  76. print 'Option clean_product_stock_batch (\'test\' or \'confirmed\') is '.(GETPOST('clean_product_stock_batch', 'alpha') ?GETPOST('clean_product_stock_batch', 'alpha') : 'undefined').'<br>'."\n";
  77. print 'Option clean_perm_table (\'test\' or \'confirmed\') is '.(GETPOST('clean_perm_table', 'alpha') ?GETPOST('clean_perm_table', 'alpha') : 'undefined').'<br>'."\n";
  78. print 'Option repair_link_dispatch_lines_supplier_order_lines, (\'test\' or \'confirmed\') is '.(GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha') ?GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha') : 'undefined').'<br>'."\n";
  79. // Init data
  80. print 'Option set_empty_time_spent_amount (\'test\' or \'confirmed\') is '.(GETPOST('set_empty_time_spent_amount', 'alpha') ?GETPOST('set_empty_time_spent_amount', 'alpha') : 'undefined').'<br>'."\n";
  81. // Structure
  82. print 'Option force_utf8_on_tables (force utf8 + row=dynamic), for mysql/mariadb only (\'test\' or \'confirmed\') is '.(GETPOST('force_utf8_on_tables', 'alpha') ?GETPOST('force_utf8_on_tables', 'alpha') : 'undefined').'<br>'."\n";
  83. print "Option force_utf8mb4_on_tables (force utf8mb4 + row=dynamic, EXPERIMENTAL!), for mysql/mariadb only ('test' or 'confirmed') is ".(GETPOST('force_utf8mb4_on_tables', 'alpha') ? GETPOST('force_utf8mb4_on_tables', 'alpha') : 'undefined')."<br>\n";
  84. print "Option force_collation_from_conf_on_tables (force ".$conf->db->character_set."/".$conf->db->dolibarr_main_db_collation." + row=dynamic), for mysql/mariadb only ('test' or 'confirmed') is ".(GETPOST('force_collation_from_conf_on_tables', 'alpha') ? GETPOST('force_collation_from_conf_on_tables', 'alpha') : 'undefined')."<br>\n";
  85. // Rebuild sequence
  86. print 'Option rebuild_sequences, for postgresql only (\'test\' or \'confirmed\') is '.(GETPOST('rebuild_sequences', 'alpha') ?GETPOST('rebuild_sequences', 'alpha') : 'undefined').'<br>'."\n";
  87. print '<br>';
  88. print '<table cellspacing="0" cellpadding="1" border="0" width="100%">';
  89. $error = 0;
  90. // If password is encoded, we decode it
  91. if (preg_match('/crypted:/i', $dolibarr_main_db_pass) || !empty($dolibarr_main_db_encrypted_pass)) {
  92. require_once $dolibarr_main_document_root.'/core/lib/security.lib.php';
  93. if (preg_match('/crypted:/i', $dolibarr_main_db_pass)) {
  94. $dolibarr_main_db_pass = preg_replace('/crypted:/i', '', $dolibarr_main_db_pass);
  95. $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_pass);
  96. $dolibarr_main_db_encrypted_pass = $dolibarr_main_db_pass; // We need to set this as it is used to know the password was initially crypted
  97. } else {
  98. $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_encrypted_pass);
  99. }
  100. }
  101. // $conf is already instancied inside inc.php
  102. $conf->db->type = $dolibarr_main_db_type;
  103. $conf->db->host = $dolibarr_main_db_host;
  104. $conf->db->port = $dolibarr_main_db_port;
  105. $conf->db->name = $dolibarr_main_db_name;
  106. $conf->db->user = $dolibarr_main_db_user;
  107. $conf->db->pass = $dolibarr_main_db_pass;
  108. // For encryption
  109. $conf->db->dolibarr_main_db_encryption = isset($dolibarr_main_db_encryption) ? $dolibarr_main_db_encryption : '';
  110. $conf->db->dolibarr_main_db_cryptkey = isset($dolibarr_main_db_cryptkey) ? $dolibarr_main_db_cryptkey : '';
  111. $db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int) $conf->db->port);
  112. if ($db->connected) {
  113. print '<tr><td class="nowrap">';
  114. print $langs->trans("ServerConnection")." : $dolibarr_main_db_host</td><td class=\"right\">".$langs->trans("OK")."</td></tr>";
  115. dolibarr_install_syslog("repair: ".$langs->transnoentities("ServerConnection").": ".$dolibarr_main_db_host.$langs->transnoentities("OK"));
  116. $ok = 1;
  117. } else {
  118. print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name)."</td><td class=\"right\">".$langs->transnoentities("Error")."</td></tr>";
  119. dolibarr_install_syslog("repair: ".$langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
  120. $ok = 0;
  121. }
  122. if ($ok) {
  123. if ($db->database_selected) {
  124. print '<tr><td class="nowrap">';
  125. print $langs->trans("DatabaseConnection")." : ".$dolibarr_main_db_name."</td><td class=\"right\">".$langs->trans("OK")."</td></tr>";
  126. dolibarr_install_syslog("repair: database connection successful: ".$dolibarr_main_db_name);
  127. $ok = 1;
  128. } else {
  129. print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name)."</td><td class=\"right\">".$langs->trans("Error")."</td></tr>";
  130. dolibarr_install_syslog("repair: ".$langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
  131. $ok = 0;
  132. }
  133. }
  134. // Show database version
  135. if ($ok) {
  136. $version = $db->getVersion();
  137. $versionarray = $db->getVersionArray();
  138. print '<tr><td>'.$langs->trans("ServerVersion").'</td>';
  139. print '<td class="right">'.$version.'</td></tr>';
  140. dolibarr_install_syslog("repair: ".$langs->transnoentities("ServerVersion").": ".$version);
  141. //print '<td class="right">'.join('.',$versionarray).'</td></tr>';
  142. }
  143. $conf->setValues($db);
  144. // Reset forced setup after the setValues
  145. if (defined('SYSLOG_FILE')) {
  146. $conf->global->SYSLOG_FILE = constant('SYSLOG_FILE');
  147. }
  148. $conf->global->MAIN_ENABLE_LOG_TO_HTML = 1;
  149. /* Start action here */
  150. $oneoptionset = 0;
  151. $oneoptionset = (GETPOST('standard', 'alpha') || GETPOST('restore_thirdparties_logos', 'alpha') || GETPOST('clean_linked_elements', 'alpha') || GETPOST('clean_menus', 'alpha')
  152. || GETPOST('clean_orphelin_dir', 'alpha') || GETPOST('clean_product_stock_batch', 'alpha') || GETPOST('set_empty_time_spent_amount', 'alpha') || GETPOST('rebuild_product_thumbs', 'alpha')
  153. || GETPOST('clean_perm_table', 'alpha')
  154. || GETPOST('force_disable_of_modules_not_found', 'alpha')
  155. || GETPOST('force_utf8_on_tables', 'alpha') || GETPOST('force_utf8mb4_on_tables', 'alpha') || GETPOST('force_collation_from_conf_on_tables', 'alpha')
  156. || GETPOST('rebuild_sequences', 'alpha'));
  157. if ($ok && $oneoptionset) {
  158. // Show wait message
  159. print '<tr><td colspan="2">'.$langs->trans("PleaseBePatient").'<br><br></td></tr>';
  160. flush();
  161. }
  162. // run_sql: Run repair SQL file
  163. if ($ok && GETPOST('standard', 'alpha')) {
  164. $dir = "mysql/migration/";
  165. $filelist = array();
  166. $i = 0;
  167. $ok = 0;
  168. // Recupere list fichier
  169. $filesindir = array();
  170. $handle = opendir($dir);
  171. if (is_resource($handle)) {
  172. while (($file = readdir($handle)) !== false) {
  173. if (preg_match('/\.sql$/i', $file)) {
  174. $filesindir[] = $file;
  175. }
  176. }
  177. }
  178. sort($filesindir);
  179. foreach ($filesindir as $file) {
  180. if (preg_match('/repair/i', $file)) {
  181. $filelist[] = $file;
  182. }
  183. }
  184. // Loop on each file
  185. foreach ($filelist as $file) {
  186. print '<tr><td class="nowrap">*** ';
  187. print $langs->trans("Script").'</td><td class="right">'.$file.'</td></tr>';
  188. $name = substr($file, 0, dol_strlen($file) - 4);
  189. // Run sql script
  190. $ok = run_sql($dir.$file, 0, '', 1);
  191. }
  192. }
  193. // sync_extrafields: Search list of fields declared and list of fields created into databases, then create fields missing
  194. if ($ok && GETPOST('standard', 'alpha')) {
  195. $extrafields = new ExtraFields($db);
  196. // List of tables that has an extrafield table
  197. $listofmodulesextra = array('societe'=>'societe', 'adherent'=>'adherent', 'product'=>'product',
  198. 'socpeople'=>'socpeople', 'propal'=>'propal', 'commande'=>'commande',
  199. 'facture'=>'facture', 'facturedet'=>'facturedet', 'facture_rec'=>'facture_rec', 'facturedet_rec'=>'facturedet_rec',
  200. 'supplier_proposal'=>'supplier_proposal', 'commande_fournisseur'=>'commande_fournisseur',
  201. 'facture_fourn'=>'facture_fourn', 'facture_fourn_rec'=>'facture_fourn_rec', 'facture_fourn_det'=>'facture_fourn_det', 'facture_fourn_det_rec'=>'facture_fourn_det_rec',
  202. 'fichinter'=>'fichinter', 'fichinterdet'=>'fichinterdet',
  203. 'inventory'=>'inventory',
  204. 'actioncomm'=>'actioncomm', 'bom_bom'=>'bom_bom', 'mrp_mo'=>'mrp_mo',
  205. 'adherent_type'=>'adherent_type', 'user'=>'user', 'partnershiap'=>'partnershiap', 'projet'=>'projet', 'projet_task'=>'projet_task', 'ticket'=>'ticket');
  206. //$listofmodulesextra = array('fichinter'=>'fichinter');
  207. print '<tr><td colspan="2"><br>*** Check fields into extra table structure match table of definition. If not add column into table</td></tr>';
  208. foreach ($listofmodulesextra as $tablename => $elementtype) {
  209. // Get list of fields
  210. $tableextra = MAIN_DB_PREFIX.$tablename.'_extrafields';
  211. // Define $arrayoffieldsdesc
  212. $arrayoffieldsdesc = $extrafields->fetch_name_optionals_label($elementtype);
  213. // Define $arrayoffieldsfound
  214. $arrayoffieldsfound = array();
  215. $resql = $db->DDLDescTable($tableextra);
  216. if ($resql) {
  217. print '<tr><td>Check availability of extra field for '.$tableextra;
  218. $i = 0;
  219. while ($obj = $db->fetch_object($resql)) {
  220. $fieldname = $fieldtype = '';
  221. if (preg_match('/mysql/', $db->type)) {
  222. $fieldname = $obj->Field;
  223. $fieldtype = $obj->Type;
  224. } else {
  225. $fieldname = isset($obj->Key) ? $obj->Key : $obj->attname;
  226. $fieldtype = isset($obj->Type) ? $obj->Type : 'varchar';
  227. }
  228. if (empty($fieldname)) {
  229. continue;
  230. }
  231. if (in_array($fieldname, array('rowid', 'tms', 'fk_object', 'import_key'))) {
  232. continue;
  233. }
  234. $arrayoffieldsfound[$fieldname] = array('type'=>$fieldtype);
  235. }
  236. print ' - Found '.count($arrayoffieldsfound).' fields into table';
  237. if (count($arrayoffieldsfound) > 0) {
  238. print ' <span class="opacitymedium">('.join(', ', array_keys($arrayoffieldsfound)).')</span>';
  239. }
  240. print '<br>'."\n";
  241. // If it does not match, we create fields
  242. foreach ($arrayoffieldsdesc as $code => $label) {
  243. if (!in_array($code, array_keys($arrayoffieldsfound))) {
  244. print 'Found field '.$code.' declared into '.MAIN_DB_PREFIX.'extrafields table but not found into desc of table '.$tableextra." -> ";
  245. $type = $extrafields->attributes[$elementtype]['type'][$code]; $length = $extrafields->attributes[$elementtype]['size'][$code]; $attribute = ''; $default = ''; $extra = ''; $null = 'null';
  246. if ($type == 'boolean') {
  247. $typedb = 'int';
  248. $lengthdb = '1';
  249. } elseif ($type == 'price') {
  250. $typedb = 'double';
  251. $lengthdb = '24,8';
  252. } elseif ($type == 'phone') {
  253. $typedb = 'varchar';
  254. $lengthdb = '20';
  255. } elseif ($type == 'mail') {
  256. $typedb = 'varchar';
  257. $lengthdb = '128';
  258. } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
  259. $typedb = 'text';
  260. $lengthdb = '';
  261. } elseif ($type == 'link') {
  262. $typedb = 'int';
  263. $lengthdb = '11';
  264. } else {
  265. $typedb = $type;
  266. $lengthdb = $length;
  267. }
  268. $field_desc = array(
  269. 'type'=>$typedb,
  270. 'value'=>$lengthdb,
  271. 'attribute'=>$attribute,
  272. 'default'=>$default,
  273. 'extra'=>$extra,
  274. 'null'=>$null
  275. );
  276. //var_dump($field_desc);exit;
  277. $result = 0;
  278. if (GETPOST('standard', 'alpha') == 'confirmed') {
  279. $result = $db->DDLAddField($tableextra, $code, $field_desc, "");
  280. if ($result < 0) {
  281. print "KO ".$db->lasterror."<br>\n";
  282. } else {
  283. print "OK<br>\n";
  284. }
  285. } else {
  286. print ' - Mode test, no column added.';
  287. }
  288. }
  289. }
  290. print "</td><td>&nbsp;</td></tr>\n";
  291. } else {
  292. print '<tr><td>Table '.$tableextra.' is not found</td><td></td></tr>'."\n";
  293. }
  294. }
  295. }
  296. // clean_data_ecm_dir: Clean data into ecm_directories table
  297. if ($ok && GETPOST('standard', 'alpha')) {
  298. clean_data_ecm_directories();
  299. }
  300. // clean declaration constants
  301. if ($ok && GETPOST('standard', 'alpha')) {
  302. print '<tr><td colspan="2"><br>*** Clean constant record of modules not enabled</td></tr>';
  303. $sql = "SELECT name, entity, value";
  304. $sql .= " FROM ".MAIN_DB_PREFIX."const as c";
  305. $sql .= " WHERE name LIKE 'MAIN_MODULE_%_TPL' OR name LIKE 'MAIN_MODULE_%_CSS' OR name LIKE 'MAIN_MODULE_%_JS' OR name LIKE 'MAIN_MODULE_%_HOOKS'";
  306. $sql .= " OR name LIKE 'MAIN_MODULE_%_TRIGGERS' OR name LIKE 'MAIN_MODULE_%_THEME' OR name LIKE 'MAIN_MODULE_%_SUBSTITUTIONS' OR name LIKE 'MAIN_MODULE_%_MODELS'";
  307. $sql .= " OR name LIKE 'MAIN_MODULE_%_MENUS' OR name LIKE 'MAIN_MODULE_%_LOGIN' OR name LIKE 'MAIN_MODULE_%_BARCODE' OR name LIKE 'MAIN_MODULE_%_TABS_%'";
  308. $sql .= " OR name LIKE 'MAIN_MODULE_%_MODULEFOREXTERNAL'";
  309. $sql .= " ORDER BY name, entity";
  310. $resql = $db->query($sql);
  311. if ($resql) {
  312. $num = $db->num_rows($resql);
  313. if ($num) {
  314. $db->begin();
  315. $i = 0;
  316. while ($i < $num) {
  317. $obj = $db->fetch_object($resql);
  318. $reg = array();
  319. if (preg_match('/MAIN_MODULE_([^_]+)_(.+)/i', $obj->name, $reg)) {
  320. $name = $reg[1];
  321. $type = $reg[2];
  322. $sql2 = "SELECT COUNT(*) as nb";
  323. $sql2 .= " FROM ".MAIN_DB_PREFIX."const as c";
  324. $sql2 .= " WHERE name = 'MAIN_MODULE_".$name."'";
  325. $sql2 .= " AND entity = ".((int) $obj->entity);
  326. $resql2 = $db->query($sql2);
  327. if ($resql2) {
  328. $obj2 = $db->fetch_object($resql2);
  329. if ($obj2 && $obj2->nb == 0) {
  330. // Module not found, so we can remove entry
  331. $sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = '".$db->escape($obj->name)."' AND entity = ".((int) $obj->entity);
  332. if (GETPOST('standard', 'alpha') == 'confirmed') {
  333. $db->query($sqldelete);
  334. print '<tr><td>Widget '.$obj->name.' set in entity '.$obj->entity.' with value '.$obj->value.' -> Module '.$name.' not enabled in entity '.((int) $obj->entity).', we delete record</td></tr>';
  335. } else {
  336. print '<tr><td>Widget '.$obj->name.' set in entity '.$obj->entity.' with value '.$obj->value.' -> Module '.$name.' not enabled in entity '.((int) $obj->entity).', we should delete record (not done, mode test)</td></tr>';
  337. }
  338. } else {
  339. //print '<tr><td>Constant '.$obj->name.' set in entity '.$obj->entity.' with value '.$obj->value.' -> Module found in entity '.$obj->entity.', we keep record</td></tr>';
  340. }
  341. }
  342. }
  343. $i++;
  344. }
  345. $db->commit();
  346. }
  347. } else {
  348. dol_print_error($db);
  349. }
  350. }
  351. // clean box of not enabled modules
  352. if ($ok && GETPOST('standard', 'alpha')) {
  353. print '<tr><td colspan="2"><br>*** Clean definition of boxes of modules not enabled</td></tr>';
  354. $sql = "SELECT file, entity FROM ".MAIN_DB_PREFIX."boxes_def";
  355. $sql .= " WHERE file like '%@%'";
  356. $resql = $db->query($sql);
  357. if ($resql) {
  358. $num = $db->num_rows($resql);
  359. if ($num) {
  360. $db->begin();
  361. $i = 0;
  362. while ($i < $num) {
  363. $obj = $db->fetch_object($resql);
  364. $reg = array();
  365. if (preg_match('/^(.+)@(.+)$/i', $obj->file, $reg)) {
  366. $name = $reg[1];
  367. $module = $reg[2];
  368. $sql2 = "SELECT COUNT(*) as nb";
  369. $sql2 .= " FROM ".MAIN_DB_PREFIX."const as c";
  370. $sql2 .= " WHERE name = 'MAIN_MODULE_".strtoupper($module)."'";
  371. $sql2 .= " AND entity = ".((int) $obj->entity);
  372. $sql2 .= " AND value <> 0";
  373. $resql2 = $db->query($sql2);
  374. if ($resql2) {
  375. $obj2 = $db->fetch_object($resql2);
  376. if ($obj2 && $obj2->nb == 0) {
  377. // Module not found, so we canremove entry
  378. $sqldeletea = "DELETE FROM ".MAIN_DB_PREFIX."boxes WHERE entity = ".((int) $obj->entity)." AND box_id IN (SELECT rowid FROM ".MAIN_DB_PREFIX."boxes_def WHERE file = '".$db->escape($obj->file)."' AND entity = ".((int) $obj->entity).")";
  379. $sqldeleteb = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def WHERE file = '".$db->escape($obj->file)."' AND entity = ".((int) $obj->entity);
  380. if (GETPOST('standard', 'alpha') == 'confirmed') {
  381. $db->query($sqldeletea);
  382. $db->query($sqldeleteb);
  383. print '<tr><td>Constant '.$obj->file.' set in boxes_def for entity '.$obj->entity.' but MAIN_MODULE_'.strtoupper($module).' not defined in entity '.((int) $obj->entity).', we delete record</td></tr>';
  384. } else {
  385. print '<tr><td>Constant '.$obj->file.' set in boxes_def for entity '.$obj->entity.' but MAIN_MODULE_'.strtoupper($module).' not defined in entity '.((int) $obj->entity).', we should delete record (not done, mode test)</td></tr>';
  386. }
  387. } else {
  388. //print '<tr><td>Constant '.$obj->name.' set in entity '.$obj->entity.' with value '.$obj->value.' -> Module found in entity '.$obj->entity.', we keep record</td></tr>';
  389. }
  390. }
  391. }
  392. $i++;
  393. }
  394. $db->commit();
  395. }
  396. }
  397. }
  398. // restore_thirdparties_logos: Move logos to correct new directory.
  399. if ($ok && GETPOST('restore_thirdparties_logos')) {
  400. //$exts=array('gif','png','jpg');
  401. $ext = '';
  402. print '<tr><td colspan="2"><br>*** Restore thirdparties logo<br>';
  403. $sql = "SELECT s.rowid, s.nom as name, s.logo FROM ".MAIN_DB_PREFIX."societe as s ORDER BY s.nom";
  404. $resql = $db->query($sql);
  405. if ($resql) {
  406. $num = $db->num_rows($resql);
  407. $i = 0;
  408. while ($i < $num) {
  409. $obj = $db->fetch_object($resql);
  410. /*
  411. $name=preg_replace('/é/','',$obj->name);
  412. $name=preg_replace('/ /','_',$name);
  413. $name=preg_replace('/\'/','',$name);
  414. */
  415. $tmp = explode('.', $obj->logo);
  416. $name = $tmp[0];
  417. if (isset($tmp[1])) {
  418. $ext = '.'.$tmp[1];
  419. }
  420. if (!empty($name)) {
  421. $filetotest = $dolibarr_main_data_root.'/societe/logos/'.$name.$ext;
  422. $filetotestsmall = $dolibarr_main_data_root.'/societe/logos/thumbs/'.$name.'_small'.$ext;
  423. $exists = dol_is_file($filetotest);
  424. print 'Check thirdparty '.$obj->rowid.' name='.$obj->name.' logo='.$obj->logo.' file '.$filetotest." exists=".$exists."<br>\n";
  425. if ($exists) {
  426. $filetarget = $dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/'.$name.$ext;
  427. $filetargetsmall = $dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/thumbs/'.$name.'_small'.$ext;
  428. $existt = dol_is_file($filetarget);
  429. if (!$existt) {
  430. if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
  431. dol_mkdir($dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos');
  432. }
  433. print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotest." -> ".$filetarget."<br>\n";
  434. if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
  435. dol_copy($filetotest, $filetarget, '', 0);
  436. }
  437. }
  438. $existtt = dol_is_file($filetargetsmall);
  439. if (!$existtt) {
  440. if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
  441. dol_mkdir($dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/thumbs');
  442. }
  443. print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestsmall." -> ".$filetargetsmall."<br>\n";
  444. if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
  445. dol_copy($filetotestsmall, $filetargetsmall, '', 0);
  446. }
  447. }
  448. }
  449. }
  450. $i++;
  451. }
  452. } else {
  453. $ok = 0;
  454. dol_print_error($db);
  455. }
  456. print '</td></tr>';
  457. }
  458. // restore_user_pictures: Move pictures to correct new directory.
  459. if ($ok && GETPOST('restore_user_pictures', 'alpha')) {
  460. //$exts=array('gif','png','jpg');
  461. $ext = '';
  462. print '<tr><td colspan="2"><br>*** Restore user pictures<br>';
  463. $sql = "SELECT s.rowid, s.firstname, s.lastname, s.login, s.photo FROM ".MAIN_DB_PREFIX."user as s ORDER BY s.rowid";
  464. $resql = $db->query($sql);
  465. if ($resql) {
  466. $num = $db->num_rows($resql);
  467. $i = 0;
  468. while ($i < $num) {
  469. $obj = $db->fetch_object($resql);
  470. /*
  471. $name=preg_replace('/é/','',$obj->name);
  472. $name=preg_replace('/ /','_',$name);
  473. $name=preg_replace('/\'/','',$name);
  474. */
  475. $tmp = explode('.', $obj->photo);
  476. $name = $tmp[0];
  477. if (isset($tmp[1])) {
  478. $ext = '.'.$tmp[1];
  479. }
  480. if (!empty($name)) {
  481. $filetotest = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/'.$name.$ext;
  482. $filetotestsmall = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/thumbs/'.$name.'_small'.$ext;
  483. $filetotestmini = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/thumbs/'.$name.'_mini'.$ext;
  484. $exists = dol_is_file($filetotest);
  485. print 'Check user '.$obj->rowid.' lastname='.$obj->lastname.' firstname='.$obj->firstname.' photo='.$obj->photo.' file '.$filetotest." exists=".$exists."<br>\n";
  486. if ($exists) {
  487. $filetarget = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/'.$name.$ext;
  488. $filetargetsmall = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs/'.$name.'_small'.$ext;
  489. $filetargetmini = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs/'.$name.'_mini'.$ext;
  490. $existt = dol_is_file($filetarget);
  491. if (!$existt) {
  492. if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
  493. dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid);
  494. }
  495. print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotest." -> ".$filetarget."<br>\n";
  496. if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
  497. dol_copy($filetotest, $filetarget, '', 0);
  498. }
  499. }
  500. $existtt = dol_is_file($filetargetsmall);
  501. if (!$existtt) {
  502. if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
  503. dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs');
  504. }
  505. print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestsmall." -> ".$filetargetsmall."<br>\n";
  506. if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
  507. dol_copy($filetotestsmall, $filetargetsmall, '', 0);
  508. }
  509. }
  510. $existtt = dol_is_file($filetargetmini);
  511. if (!$existtt) {
  512. if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
  513. dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs');
  514. }
  515. print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestmini." -> ".$filetargetmini."<br>\n";
  516. if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
  517. dol_copy($filetotestmini, $filetargetmini, '', 0);
  518. }
  519. }
  520. }
  521. }
  522. $i++;
  523. }
  524. } else {
  525. $ok = 0;
  526. dol_print_error($db);
  527. }
  528. print '</td></tr>';
  529. }
  530. // rebuild_product_thumbs: Rebuild thumbs for product files
  531. if ($ok && GETPOST('rebuild_product_thumbs', 'alpha')) {
  532. $ext = '';
  533. global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini;
  534. print '<tr><td colspan="2"><br>*** Rebuild product thumbs<br>';
  535. $sql = "SELECT s.rowid, s.ref FROM ".MAIN_DB_PREFIX."product as s ORDER BY s.ref";
  536. $resql = $db->query($sql);
  537. if ($resql) {
  538. $num = $db->num_rows($resql);
  539. $i = 0;
  540. while ($i < $num) {
  541. $obj = $db->fetch_object($resql);
  542. if (!empty($obj->ref)) {
  543. $files = dol_dir_list($dolibarr_main_data_root.'/produit/'.$obj->ref, 'files', 0);
  544. foreach ($files as $file) {
  545. // Generate thumbs.
  546. if (image_format_supported($file['fullname']) == 1) {
  547. $imgThumbSmall = 'notbuild';
  548. if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
  549. // Used on logon for example
  550. $imgThumbSmall = vignette($file['fullname'], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
  551. }
  552. print 'Check product '.$obj->rowid.", file ".$file['fullname']." -> ".$imgThumbSmall." maxwidthsmall=".$maxwidthsmall." maxheightsmall=".$maxheightsmall."<br>\n";
  553. $imgThumbMini = 'notbuild';
  554. if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
  555. // Create mini thumbs for image (Ratio is near 16/9)
  556. // Used on menu or for setup page for example
  557. $imgThumbMini = vignette($file['fullname'], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
  558. }
  559. print 'Check product '.$obj->rowid.", file ".$file['fullname']." -> ".$imgThumbMini." maxwidthmini=".$maxwidthmini." maxheightmini=".$maxheightmini."<br>\n";
  560. }
  561. }
  562. }
  563. $i++;
  564. }
  565. } else {
  566. $ok = 0;
  567. dol_print_error($db);
  568. }
  569. print '</td></tr>';
  570. }
  571. // clean_linked_elements: Check and clean linked elements
  572. if ($ok && GETPOST('clean_linked_elements', 'alpha')) {
  573. print '<tr><td colspan="2"><br>*** Check table of linked elements and delete orphelins links</td></tr>';
  574. // propal => order
  575. print '<tr><td colspan="2">'.checkLinkedElements('propal', 'commande')."</td></tr>\n";
  576. // propal => invoice
  577. print '<tr><td colspan="2">'.checkLinkedElements('propal', 'facture')."</td></tr>\n";
  578. // order => invoice
  579. print '<tr><td colspan="2">'.checkLinkedElements('commande', 'facture')."</td></tr>\n";
  580. // order => shipping
  581. print '<tr><td colspan="2">'.checkLinkedElements('commande', 'shipping')."</td></tr>\n";
  582. // shipping => delivery
  583. print '<tr><td colspan="2">'.checkLinkedElements('shipping', 'delivery')."</td></tr>\n";
  584. // order_supplier => invoice_supplier
  585. print '<tr><td colspan="2">'.checkLinkedElements('order_supplier', 'invoice_supplier')."</td></tr>\n";
  586. }
  587. // clean_menus: Check orphelins menus
  588. if ($ok && GETPOST('clean_menus', 'alpha')) {
  589. print '<tr><td colspan="2"><br>*** Clean menu entries coming from disabled modules</td></tr>';
  590. $sql = "SELECT rowid, module";
  591. $sql .= " FROM ".MAIN_DB_PREFIX."menu as c";
  592. $sql .= " WHERE module IS NOT NULL AND module <> ''";
  593. $sql .= " ORDER BY module";
  594. $resql = $db->query($sql);
  595. if ($resql) {
  596. $num = $db->num_rows($resql);
  597. if ($num) {
  598. $i = 0;
  599. while ($i < $num) {
  600. $obj = $db->fetch_object($resql);
  601. $modulecond = $obj->module;
  602. $modulecondarray = explode('|', $obj->module); // Name of module
  603. print '<tr><td>';
  604. print $modulecond;
  605. $db->begin();
  606. if ($modulecond) { // And menu entry for module $modulecond was found in database.
  607. $moduleok = 0;
  608. foreach ($modulecondarray as $tmpname) {
  609. if ($tmpname == 'margins') {
  610. $tmpname = 'margin'; // TODO Remove this when normalized
  611. }
  612. $result = 0;
  613. if (!empty($conf->$tmpname)) {
  614. $result = $conf->$tmpname->enabled;
  615. }
  616. if ($result) {
  617. $moduleok++;
  618. }
  619. }
  620. if (!$moduleok && $modulecond) {
  621. print ' - Module condition '.$modulecond.' seems ko, we delete menu entry.';
  622. if (GETPOST('clean_menus') == 'confirmed') {
  623. $sql2 = "DELETE FROM ".MAIN_DB_PREFIX."menu WHERE module = '".$db->escape($modulecond)."'";
  624. $resql2 = $db->query($sql2);
  625. if (!$resql2) {
  626. $error++;
  627. dol_print_error($db);
  628. } else {
  629. print ' - <span class="warning">Cleaned</span>';
  630. }
  631. } else {
  632. print ' - <span class="warning">Canceled (test mode)</span>';
  633. }
  634. } else {
  635. print ' - Module condition '.$modulecond.' is ok, we do nothing.';
  636. }
  637. }
  638. if (!$error) {
  639. $db->commit();
  640. } else {
  641. $db->rollback();
  642. }
  643. print'</td></tr>';
  644. if ($error) {
  645. break;
  646. }
  647. $i++;
  648. }
  649. } else {
  650. print '<tr><td>No menu entries of disabled menus found</td></tr>';
  651. }
  652. } else {
  653. dol_print_error($db);
  654. }
  655. }
  656. // clean_orphelin_dir: Run purge of directory
  657. if ($ok && GETPOST('clean_orphelin_dir', 'alpha')) {
  658. $listmodulepart = array('company', 'invoice', 'invoice_supplier', 'propal', 'order', 'order_supplier', 'contract', 'tax');
  659. foreach ($listmodulepart as $modulepart) {
  660. $filearray = array();
  661. $upload_dir = isset($conf->$modulepart->dir_output) ? $conf->$modulepart->dir_output : '';
  662. if ($modulepart == 'company') {
  663. $upload_dir = $conf->societe->dir_output; // TODO change for multicompany sharing
  664. }
  665. if ($modulepart == 'invoice') {
  666. $upload_dir = $conf->facture->dir_output;
  667. }
  668. if ($modulepart == 'invoice_supplier') {
  669. $upload_dir = $conf->fournisseur->facture->dir_output;
  670. }
  671. if ($modulepart == 'order') {
  672. $upload_dir = $conf->commande->dir_output;
  673. }
  674. if ($modulepart == 'order_supplier') {
  675. $upload_dir = $conf->fournisseur->commande->dir_output;
  676. }
  677. if ($modulepart == 'contract') {
  678. $upload_dir = $conf->contrat->dir_output;
  679. }
  680. if (empty($upload_dir)) {
  681. continue;
  682. }
  683. print '<tr><td colspan="2"><br>*** Clean orphelins files into files '.$upload_dir.'</td></tr>';
  684. $filearray = dol_dir_list($upload_dir, "files", 1, '', array('^SPECIMEN\.pdf$', '^\.', '(\.meta|_preview.*\.png)$', '^temp$', '^payments$', '^CVS$', '^thumbs$'), '', SORT_DESC, 1, true);
  685. // To show ref or specific information according to view to show (defined by $module)
  686. if ($modulepart == 'company') {
  687. include_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
  688. $object_instance = new Societe($db);
  689. }
  690. if ($modulepart == 'invoice') {
  691. include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
  692. $object_instance = new Facture($db);
  693. } elseif ($modulepart == 'invoice_supplier') {
  694. include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
  695. $object_instance = new FactureFournisseur($db);
  696. } elseif ($modulepart == 'propal') {
  697. include_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
  698. $object_instance = new Propal($db);
  699. } elseif ($modulepart == 'order') {
  700. include_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
  701. $object_instance = new Commande($db);
  702. } elseif ($modulepart == 'order_supplier') {
  703. include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
  704. $object_instance = new CommandeFournisseur($db);
  705. } elseif ($modulepart == 'contract') {
  706. include_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
  707. $object_instance = new Contrat($db);
  708. } elseif ($modulepart == 'tax') {
  709. include_once DOL_DOCUMENT_ROOT.'/compta/sociales/class/chargesociales.class.php';
  710. $object_instance = new ChargeSociales($db);
  711. }
  712. foreach ($filearray as $key => $file) {
  713. if (!is_dir($file['name'])
  714. && $file['name'] != '.'
  715. && $file['name'] != '..'
  716. && $file['name'] != 'CVS'
  717. ) {
  718. // Define relative path used to store the file
  719. $relativefile = preg_replace('/'.preg_quote($upload_dir.'/', '/').'/', '', $file['fullname']);
  720. //var_dump($file);
  721. $id = 0; $ref = ''; $object_instance->id = 0; $object_instance->ref = ''; $label = '';
  722. // To show ref or specific information according to view to show (defined by $module)
  723. if ($modulepart == 'invoice') {
  724. preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg); $ref = $reg[1];
  725. }
  726. if ($modulepart == 'invoice_supplier') {
  727. preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg); $id = empty($reg[1]) ? '' : $reg[1];
  728. }
  729. if ($modulepart == 'propal') {
  730. preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg); $ref = $reg[1];
  731. }
  732. if ($modulepart == 'order') {
  733. preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg); $ref = $reg[1];
  734. }
  735. if ($modulepart == 'order_supplier') {
  736. preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg); $ref = $reg[1];
  737. }
  738. if ($modulepart == 'contract') {
  739. preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg); $ref = $reg[1];
  740. }
  741. if ($modulepart == 'tax') {
  742. preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg); $id = $reg[1];
  743. }
  744. if ($id || $ref) {
  745. //print 'Fetch '.$id.' or '.$ref.'<br>';
  746. $result = $object_instance->fetch($id, $ref);
  747. //print $result.'<br>';
  748. if ($result == 0) { // Not found but no error
  749. // Clean of orphelins directories are done into repair.php
  750. print '<tr><td colspan="2">';
  751. print 'Delete orphelins file '.$file['fullname'].'<br>';
  752. if (GETPOST('clean_orphelin_dir', 'alpha') == 'confirmed') {
  753. dol_delete_file($file['fullname'], 1, 1, 1);
  754. dol_delete_dir(dirname($file['fullname']), 1);
  755. }
  756. print "</td></tr>";
  757. } elseif ($result < 0) {
  758. print 'Error in '.get_class($object_instance).'.fetch of id'.$id.' ref='.$ref.', result='.$result.'<br>';
  759. }
  760. }
  761. }
  762. }
  763. }
  764. }
  765. // clean_linked_elements: Check and clean linked elements
  766. if ($ok && GETPOST('clean_product_stock_batch', 'alpha')) {
  767. $methodtofix = GETPOST('methodtofix', 'alpha') ?GETPOST('methodtofix', 'alpha') : 'updatestock';
  768. print '<tr><td colspan="2"><br>*** Clean table product_batch, methodtofix='.$methodtofix.' (possible values: updatestock or updatebatch)</td></tr>';
  769. $sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
  770. $sql .= " FROM ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."product_stock as ps LEFT JOIN ".MAIN_DB_PREFIX."product_batch as pb ON ps.rowid = pb.fk_product_stock";
  771. $sql .= " WHERE p.rowid = ps.fk_product";
  772. $sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
  773. $sql .= " HAVING (SUM(pb.qty) IS NOT NULL AND reel != SUM(pb.qty)) OR (SUM(pb.qty) IS NULL AND p.tobatch > 0)";
  774. print $sql;
  775. $resql = $db->query($sql);
  776. if ($resql) {
  777. $num = $db->num_rows($resql);
  778. if ($num) {
  779. $i = 0;
  780. while ($i < $num) {
  781. $obj = $db->fetch_object($resql);
  782. print '<tr><td>Product '.$obj->rowid.'-'.$obj->ref.' in warehouse id='.$obj->fk_entrepot.' (product_stock.id='.$obj->psrowid.'): '.$obj->reel.' (Stock product_stock.reel) != '.($obj->reelbatch ? $obj->reelbatch : '0').' (Stock batch sum product_batch)';
  783. // Fix is required
  784. if ($obj->reel != $obj->reelbatch) {
  785. if (empty($obj->tobatch)) {
  786. // If product is not a product that support batches, we can clean stock by deleting the product batch lines
  787. print ' -> Delete qty '.$obj->reelbatch.' for any lot linked to fk_product_stock='.$obj->psrowid;
  788. $sql2 = "DELETE FROM ".MAIN_DB_PREFIX."product_batch";
  789. $sql2 .= " WHERE fk_product_stock = ".((int) $obj->psrowid);
  790. print '<br>'.$sql2;
  791. if (GETPOST('clean_product_stock_batch') == 'confirmed') {
  792. $resql2 = $db->query($sql2);
  793. if (!$resql2) {
  794. $error++;
  795. dol_print_error($db);
  796. }
  797. }
  798. } else {
  799. if ($methodtofix == 'updatebatch') {
  800. // Method 1
  801. print ' -> Insert qty '.($obj->reel - $obj->reelbatch).' with lot 000000 linked to fk_product_stock='.$obj->psrowid;
  802. $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."product_batch(fk_product_stock, batch, qty)";
  803. $sql2 .= "VALUES(".((int) $obj->psrowid).", '000000', ".((float) ($obj->reel - $obj->reelbatch)).")";
  804. print '<br>'.$sql2;
  805. if (GETPOST('clean_product_stock_batch') == 'confirmed') {
  806. $resql2 = $db->query($sql2);
  807. if (!$resql2) {
  808. // TODO If it fails, we must make update
  809. //$sql2 ="UPDATE ".MAIN_DB_PREFIX."product_batch";
  810. //$sql2.=" SET ".$obj->psrowid.", '000000', ".($obj->reel - $obj->reelbatch).")";
  811. //$sql2.=" WHERE fk_product_stock = ".((int) $obj->psrowid)
  812. }
  813. }
  814. }
  815. if ($methodtofix == 'updatestock') {
  816. // Method 2
  817. print ' -> Update qty of product_stock with qty = '.($obj->reelbatch ? ((float) $obj->reelbatch) : '0').' for ps.rowid = '.((int) $obj->psrowid);
  818. $sql2 = "UPDATE ".MAIN_DB_PREFIX."product_stock";
  819. $sql2 .= " SET reel = ".($obj->reelbatch ? ((float) $obj->reelbatch) : '0')." WHERE rowid = ".((int) $obj->psrowid);
  820. print '<br>'.$sql2;
  821. if (GETPOST('clean_product_stock_batch') == 'confirmed') {
  822. $error = 0;
  823. $db->begin();
  824. $resql2 = $db->query($sql2);
  825. if ($resql2) {
  826. // We update product_stock, so we must fill p.stock into product too.
  827. $sql3 = 'UPDATE '.MAIN_DB_PREFIX.'product p SET p.stock= (SELECT SUM(ps.reel) FROM '.MAIN_DB_PREFIX.'product_stock ps WHERE ps.fk_product = p.rowid)';
  828. $resql3 = $db->query($sql3);
  829. if (!$resql3) {
  830. $error++;
  831. dol_print_error($db);
  832. }
  833. } else {
  834. $error++;
  835. dol_print_error($db);
  836. }
  837. if (!$error) {
  838. $db->commit();
  839. } else {
  840. $db->rollback();
  841. }
  842. }
  843. }
  844. }
  845. }
  846. print'</td></tr>';
  847. $i++;
  848. }
  849. } else {
  850. print '<tr><td colspan="2">Nothing to do</td></tr>';
  851. }
  852. } else {
  853. dol_print_error($db);
  854. }
  855. }
  856. // clean_product_stock_negative_if_batch
  857. if ($ok && GETPOST('clean_product_stock_negative_if_batch', 'alpha')) {
  858. print '<tr><td colspan="2"><br>Clean table product_batch, methodtofix='.$methodtofix.' (possible values: updatestock or updatebatch)</td></tr>';
  859. $sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
  860. $sql .= " FROM ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."product_stock as ps, ".MAIN_DB_PREFIX."product_batch as pb";
  861. $sql .= " WHERE p.rowid = ps.fk_product AND ps.rowid = pb.fk_product_stock";
  862. $sql .= " AND p.tobatch > 0";
  863. $sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
  864. $sql .= " HAVING reel != SUM(pb.qty)";
  865. $resql = $db->query($sql);
  866. if ($resql) {
  867. $num = $db->num_rows($resql);
  868. if ($num) {
  869. $i = 0;
  870. while ($i < $num) {
  871. $obj = $db->fetch_object($resql);
  872. print '<tr><td>'.$obj->rowid.'-'.$obj->ref.'-'.$obj->fk_entrepot.' -> '.$obj->psrowid.': '.$obj->reel.' != '.$obj->reelbatch;
  873. // TODO
  874. }
  875. }
  876. }
  877. }
  878. // set_empty_time_spent_amount
  879. if ($ok && GETPOST('set_empty_time_spent_amount', 'alpha')) {
  880. print '<tr><td colspan="2"><br>*** Set value of time spent without amount</td></tr>';
  881. $sql = "SELECT COUNT(ptt.rowid) as nb, u.rowid as user_id, u.login, u.thm as user_thm";
  882. $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."user as u";
  883. $sql .= " WHERE ptt.fk_user = u.rowid";
  884. $sql .= " AND ptt.thm IS NULL and u.thm > 0";
  885. $sql .= " GROUP BY u.rowid, u.login, u.thm";
  886. $resql = $db->query($sql);
  887. if ($resql) {
  888. $num = $db->num_rows($resql);
  889. if ($num) {
  890. $i = 0;
  891. while ($i < $num) {
  892. $obj = $db->fetch_object($resql);
  893. print '<tr><td>'.$obj->login.'-'.$obj->user_id.' ('.$obj->nb.' lines to fix) -> '.$obj->user_thm;
  894. $db->begin();
  895. if (GETPOST('set_empty_time_spent_amount') == 'confirmed') {
  896. $sql2 = "UPDATE ".MAIN_DB_PREFIX."element_time";
  897. $sql2 .= " SET thm = ".$obj->user_thm." WHERE thm IS NULL AND fk_user = ".((int) $obj->user_id);
  898. $resql2 = $db->query($sql2);
  899. if (!$resql2) {
  900. $error++;
  901. dol_print_error($db);
  902. }
  903. }
  904. if (!$error) {
  905. $db->commit();
  906. } else {
  907. $db->rollback();
  908. }
  909. print'</td></tr>';
  910. if ($error) {
  911. break;
  912. }
  913. $i++;
  914. }
  915. } else {
  916. print '<tr><td>No time spent with empty line on users with a hourly rate defined</td></tr>';
  917. }
  918. } else {
  919. dol_print_error($db);
  920. }
  921. }
  922. // force_disable_of_modules_not_found
  923. if ($ok && GETPOST('force_disable_of_modules_not_found', 'alpha')) {
  924. print '<tr><td colspan="2"><br>*** Force modules not found physicaly to be disabled (only modules adding js, css or hooks can be detected as removed physicaly)</td></tr>';
  925. $arraylistofkey = array('hooks', 'js', 'css');
  926. foreach ($arraylistofkey as $key) {
  927. $sql = "SELECT DISTINCT name, value";
  928. $sql .= " FROM ".MAIN_DB_PREFIX."const as c";
  929. $sql .= " WHERE name LIKE 'MAIN_MODULE_%_".strtoupper($key)."'";
  930. $sql .= " ORDER BY name";
  931. $resql = $db->query($sql);
  932. if ($resql) {
  933. $num = $db->num_rows($resql);
  934. if ($num) {
  935. $i = 0;
  936. while ($i < $num) {
  937. $obj = $db->fetch_object($resql);
  938. $constantname = $obj->name; // Name of constant for hook or js or css declaration
  939. print '<tr><td>';
  940. print dol_escape_htmltag($constantname);
  941. $db->begin();
  942. $reg = array();
  943. if (preg_match('/MAIN_MODULE_(.*)_'.strtoupper($key).'/i', $constantname, $reg)) {
  944. $name = strtolower($reg[1]);
  945. if ($name) { // An entry for key $key and module $name was found in database.
  946. $reloffile = '';
  947. $result = 'found';
  948. if ($key == 'hooks') {
  949. $reloffile = $name.'/class/actions_'.$name.'.class.php';
  950. }
  951. if ($key == 'js') {
  952. $value = $obj->value;
  953. $valuearray = json_decode($value);
  954. $reloffile = $valuearray[0];
  955. $reloffile = preg_replace('/^\//', '', $valuearray[0]);
  956. }
  957. if ($key == 'css') {
  958. $value = $obj->value;
  959. $valuearray = json_decode($value);
  960. if ($value && (!is_array($valuearray) || count($valuearray) == 0)) {
  961. $valuearray = array();
  962. $valuearray[0] = $value; // If value was not a json array but a string
  963. }
  964. $reloffile = preg_replace('/^\//', '', $valuearray[0]);
  965. }
  966. if ($reloffile) {
  967. //var_dump($key.' - '.$value.' - '.$reloffile);
  968. try {
  969. $result = dol_buildpath($reloffile, 0, 2);
  970. } catch (Exception $e) {
  971. $result = 'found'; // If error, we force like if we found to avoid any deletion
  972. }
  973. } else {
  974. $result = 'found'; //
  975. }
  976. if (!$result) {
  977. print ' - File of '.$key.' ('.$reloffile.') NOT found, we disable the module.';
  978. if (GETPOST('force_disable_of_modules_not_found') == 'confirmed') {
  979. $sql2 = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = 'MAIN_MODULE_".strtoupper($name)."_".strtoupper($key)."'";
  980. $resql2 = $db->query($sql2);
  981. if (!$resql2) {
  982. $error++;
  983. dol_print_error($db);
  984. }
  985. $sql3 = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = 'MAIN_MODULE_".strtoupper($name)."'";
  986. $resql3 = $db->query($sql3);
  987. if (!$resql3) {
  988. $error++;
  989. dol_print_error($db);
  990. } else {
  991. print ' - <span class="warning">Cleaned</span>';
  992. }
  993. } else {
  994. print ' - <span class="warning">Canceled (test mode)</span>';
  995. }
  996. } else {
  997. print ' - File of '.$key.' ('.$reloffile.') found, we do nothing.';
  998. }
  999. }
  1000. if (!$error) {
  1001. $db->commit();
  1002. } else {
  1003. $db->rollback();
  1004. }
  1005. }
  1006. print'</td></tr>';
  1007. if ($error) {
  1008. break;
  1009. }
  1010. $i++;
  1011. }
  1012. } else {
  1013. print '<tr><td>No active module with missing files found by searching on MAIN_MODULE_(.*)_'.strtoupper($key).'</td></tr>';
  1014. }
  1015. } else {
  1016. dol_print_error($db);
  1017. }
  1018. }
  1019. }
  1020. // clean_old_module_entries: Clean data into const when files of module were removed without being
  1021. if ($ok && GETPOST('clean_perm_table', 'alpha')) {
  1022. print '<tr><td colspan="2"><br>*** Clean table user_rights from lines of external modules no more enabled</td></tr>';
  1023. $listofmods = '';
  1024. foreach ($conf->modules as $key => $val) {
  1025. $listofmods .= ($listofmods ? ',' : '')."'".$db->escape($val)."'";
  1026. }
  1027. $sql = "SELECT id, libelle as label, module from ".MAIN_DB_PREFIX."rights_def WHERE module NOT IN (".$db->sanitize($listofmods, 1).") AND id > 100000";
  1028. $resql = $db->query($sql);
  1029. if ($resql) {
  1030. $num = $db->num_rows($resql);
  1031. if ($num) {
  1032. $i = 0;
  1033. while ($i < $num) {
  1034. $obj = $db->fetch_object($resql);
  1035. if ($obj->id > 0) {
  1036. print '<tr><td>Found line with id '.$obj->id.', label "'.$obj->label.'" of module "'.$obj->module.'" to delete';
  1037. if (GETPOST('clean_perm_table', 'alpha') == 'confirmed') {
  1038. $sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."rights_def WHERE id = ".((int) $obj->id);
  1039. $resqldelete = $db->query($sqldelete);
  1040. if (!$resqldelete) {
  1041. dol_print_error($db);
  1042. }
  1043. print ' - deleted';
  1044. }
  1045. print '</td></tr>';
  1046. }
  1047. $i++;
  1048. }
  1049. } else {
  1050. print '<tr><td>No lines of a disabled external module (with id > 100000) found into table rights_def</td></tr>';
  1051. }
  1052. } else {
  1053. dol_print_error($db);
  1054. }
  1055. }
  1056. // force utf8 on tables
  1057. if ($ok && GETPOST('force_utf8_on_tables', 'alpha')) {
  1058. print '<tr><td colspan="2"><br>*** Force page code and collation of tables into utf8/utf8_unicode_ci and row_format=dynamic (for mysql/mariadb only)</td></tr>';
  1059. if ($db->type == "mysql" || $db->type == "mysqli") {
  1060. $force_utf8_on_tables = GETPOST('force_utf8_on_tables', 'alpha');
  1061. $listoftables = $db->DDLListTablesFull($db->database_name);
  1062. // Disable foreign key checking for avoid errors
  1063. if ($force_utf8_on_tables == 'confirmed') {
  1064. $sql = 'SET FOREIGN_KEY_CHECKS=0';
  1065. print '<!-- '.$sql.' -->';
  1066. $resql = $db->query($sql);
  1067. }
  1068. foreach ($listoftables as $table) {
  1069. // do not convert llx_const if mysql encrypt/decrypt is used
  1070. if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
  1071. continue;
  1072. }
  1073. if ($table[1] == 'VIEW') {
  1074. print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' (Skipped)</td></tr>';
  1075. continue;
  1076. }
  1077. print '<tr><td colspan="2">';
  1078. print $table[0];
  1079. $sql1 = "ALTER TABLE ".$table[0]." ROW_FORMAT=dynamic";
  1080. $sql2 = "ALTER TABLE ".$table[0]." CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
  1081. print '<!-- '.$sql1.' -->';
  1082. print '<!-- '.$sql2.' -->';
  1083. if ($force_utf8_on_tables == 'confirmed') {
  1084. $resql1 = $db->query($sql1);
  1085. if ($resql1) {
  1086. $resql2 = $db->query($sql2);
  1087. } else {
  1088. $resql2 = false;
  1089. }
  1090. print ' - Done ('.(($resql1 && $resql2) ? 'OK' : 'KO').')';
  1091. } else {
  1092. print ' - Disabled';
  1093. }
  1094. print '</td></tr>';
  1095. }
  1096. // Enable foreign key checking
  1097. if ($force_utf8_on_tables == 'confirmed') {
  1098. $sql = 'SET FOREIGN_KEY_CHECKS=1';
  1099. print '<!-- '.$sql.' -->';
  1100. $resql = $db->query($sql);
  1101. }
  1102. } else {
  1103. print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
  1104. }
  1105. }
  1106. // force utf8mb4 on tables EXPERIMENTAL !
  1107. if ($ok && GETPOST('force_utf8mb4_on_tables', 'alpha')) {
  1108. print '<tr><td colspan="2"><br>*** Force page code and collation of tables into utf8mb4/utf8mb4_unicode_ci (for mysql/mariadb only)</td></tr>';
  1109. if ($db->type == "mysql" || $db->type == "mysqli") {
  1110. $force_utf8mb4_on_tables = GETPOST('force_utf8mb4_on_tables', 'alpha');
  1111. $listoftables = $db->DDLListTablesFull($db->database_name);
  1112. // Disable foreign key checking for avoid errors
  1113. if ($force_utf8mb4_on_tables == 'confirmed') {
  1114. $sql = 'SET FOREIGN_KEY_CHECKS=0';
  1115. print '<!-- '.$sql.' -->';
  1116. $resql = $db->query($sql);
  1117. }
  1118. foreach ($listoftables as $table) {
  1119. // do not convert llx_const if mysql encrypt/decrypt is used
  1120. if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
  1121. continue;
  1122. }
  1123. if ($table[1] == 'VIEW') {
  1124. print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' (Skipped)</td></tr>';
  1125. continue;
  1126. }
  1127. print '<tr><td colspan="2">';
  1128. print $table[0];
  1129. $sql1 = "ALTER TABLE ".$table[0]." ROW_FORMAT=dynamic";
  1130. $sql2 = "ALTER TABLE ".$table[0]." CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
  1131. print '<!-- '.$sql1.' -->';
  1132. print '<!-- '.$sql2.' -->';
  1133. if ($force_utf8mb4_on_tables == 'confirmed') {
  1134. $resql1 = $db->query($sql1);
  1135. if ($resql1) {
  1136. $resql2 = $db->query($sql2);
  1137. } else {
  1138. $resql2 = false;
  1139. }
  1140. print ' - Done ('.(($resql1 && $resql2) ? 'OK' : 'KO').')';
  1141. } else {
  1142. print ' - Disabled';
  1143. }
  1144. print '</td></tr>';
  1145. flush();
  1146. ob_flush();
  1147. }
  1148. // Enable foreign key checking
  1149. if ($force_utf8mb4_on_tables == 'confirmed') {
  1150. $sql = 'SET FOREIGN_KEY_CHECKS=1';
  1151. print '<!-- '.$sql.' -->';
  1152. $resql = $db->query($sql);
  1153. }
  1154. } else {
  1155. print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
  1156. }
  1157. }
  1158. if ($ok && GETPOST('force_collation_from_conf_on_tables', 'alpha')) {
  1159. print '<tr><td colspan="2"><br>*** Force page code and collation of tables into '.$conf->db->character_set.'/'.$conf->db->dolibarr_main_db_collation.' and row_format=dynamic (for mysql/mariadb only)</td></tr>';
  1160. if ($db->type == "mysql" || $db->type == "mysqli") {
  1161. $force_collation_from_conf_on_tables = GETPOST('force_collation_from_conf_on_tables', 'alpha');
  1162. $listoftables = $db->DDLListTablesFull($db->database_name);
  1163. // Disable foreign key checking for avoid errors
  1164. if ($force_collation_from_conf_on_tables == 'confirmed') {
  1165. $sql = 'SET FOREIGN_KEY_CHECKS=0';
  1166. print '<!-- '.$sql.' -->';
  1167. $resql = $db->query($sql);
  1168. }
  1169. foreach ($listoftables as $table) {
  1170. // do not convert llx_const if mysql encrypt/decrypt is used
  1171. if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
  1172. continue;
  1173. }
  1174. if ($table[1] == 'VIEW') {
  1175. print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' (Skipped)</td></tr>';
  1176. continue;
  1177. }
  1178. print '<tr><td colspan="2">';
  1179. print $table[0];
  1180. $sql1 = "ALTER TABLE ".$table[0]." ROW_FORMAT=dynamic";
  1181. $sql2 = "ALTER TABLE ".$table[0]." CONVERT TO CHARACTER SET ".$conf->db->character_set." COLLATE ".$conf->db->dolibarr_main_db_collation;
  1182. print '<!-- '.$sql1.' -->';
  1183. print '<!-- '.$sql2.' -->';
  1184. if ($force_collation_from_conf_on_tables == 'confirmed') {
  1185. $resql1 = $db->query($sql1);
  1186. if ($resql1) {
  1187. $resql2 = $db->query($sql2);
  1188. } else {
  1189. $resql2 = false;
  1190. }
  1191. print ' - Done ('.(($resql1 && $resql2) ? 'OK' : 'KO').')';
  1192. } else {
  1193. print ' - Disabled';
  1194. }
  1195. print '</td></tr>';
  1196. }
  1197. // Enable foreign key checking
  1198. if ($force_collation_from_conf_on_tables == 'confirmed') {
  1199. $sql = 'SET FOREIGN_KEY_CHECKS=1';
  1200. print '<!-- '.$sql.' -->';
  1201. $resql = $db->query($sql);
  1202. }
  1203. } else {
  1204. print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
  1205. }
  1206. }
  1207. // rebuild sequences for pgsql
  1208. if ($ok && GETPOST('rebuild_sequences', 'alpha')) {
  1209. print '<tr><td colspan="2"><br>*** Force to rebuild sequences (for postgresql only)</td></tr>';
  1210. if ($db->type == "pgsql") {
  1211. $rebuild_sequence = GETPOST('rebuild_sequences', 'alpha');
  1212. if ($rebuild_sequence == 'confirmed') {
  1213. $sql = "SELECT dol_util_rebuild_sequences();";
  1214. print '<!-- '.$sql.' -->';
  1215. $resql = $db->query($sql);
  1216. }
  1217. } else {
  1218. print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
  1219. }
  1220. }
  1221. //
  1222. if ($ok && GETPOST('repair_link_dispatch_lines_supplier_order_lines')) {
  1223. /*
  1224. * This script is meant to be run when upgrading from a dolibarr version < 3.8
  1225. * to a newer version.
  1226. *
  1227. * Version 3.8 introduces a new column in llx_commande_fournisseur_dispatch, which
  1228. * matches the dispatch to a specific supplier order line (so that if there are
  1229. * several with the same product, the user can specifically tell which products of
  1230. * which line were dispatched where).
  1231. *
  1232. * However when migrating, the new column has a default value of 0, which means that
  1233. * old supplier orders whose lines were dispatched using the old dolibarr version
  1234. * have unspecific dispatch lines, which are not taken into account by the new version,
  1235. * thus making the order look like it was never dispatched at all.
  1236. *
  1237. * This scripts sets this foreign key to the first matching supplier order line whose
  1238. * product (and supplier order of course) are the same as the dispatch’s.
  1239. *
  1240. * If the dispatched quantity is more than indicated on the order line (this happens if
  1241. * there are several order lines for the same product), it creates new dispatch lines
  1242. * pointing to the other order lines accordingly, until all the dispatched quantity is
  1243. * accounted for.
  1244. */
  1245. $repair_link_dispatch_lines_supplier_order_lines = GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha');
  1246. echo '<tr><th>Repair llx_commande_fournisseur_dispatch.fk_commandefourndet</th></tr>';
  1247. echo '<tr><td>Repair in progress. This may take a while.</td></tr>';
  1248. $sql_dispatch = 'SELECT * FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch WHERE COALESCE(fk_commandefourndet, 0) = 0';
  1249. $db->begin();
  1250. $resql_dispatch = $db->query($sql_dispatch);
  1251. $n_processed_rows = 0;
  1252. $errors = array();
  1253. if ($resql_dispatch) {
  1254. if ($db->num_rows($resql_dispatch) == 0) {
  1255. echo '<tr><td>Nothing to do.</td></tr>';
  1256. exit;
  1257. }
  1258. while ($obj_dispatch = $db->fetch_object($resql_dispatch)) {
  1259. $sql_line = 'SELECT line.rowid, line.qty FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet AS line';
  1260. $sql_line .= ' WHERE line.fk_commande = '.((int) $obj_dispatch->fk_commande);
  1261. $sql_line .= ' AND line.fk_product = '.((int) $obj_dispatch->fk_product);
  1262. $resql_line = $db->query($sql_line);
  1263. // s’il y a plusieurs lignes avec le même produit sur cette commande fournisseur,
  1264. // on divise la ligne de dispatch en autant de lignes qu’on en a sur la commande pour le produit
  1265. // et on met la quantité de la ligne dans la limite du "budget" indiqué par dispatch.qty
  1266. $remaining_qty = $obj_dispatch->qty;
  1267. $first_iteration = true;
  1268. if (!$resql_line) {
  1269. echo '<tr><td>Unable to find a matching supplier order line for dispatch #'.$obj_dispatch->rowid.'</td></tr>';
  1270. $errors[] = $sql_line;
  1271. $n_processed_rows++;
  1272. continue;
  1273. }
  1274. if ($db->num_rows($resql_line) == 0) {
  1275. continue;
  1276. }
  1277. while ($obj_line = $db->fetch_object($resql_line)) {
  1278. if (!$remaining_qty) {
  1279. break;
  1280. }
  1281. if (!$obj_line->rowid) {
  1282. continue;
  1283. }
  1284. $qty_for_line = min($remaining_qty, $obj_line->qty);
  1285. if ($first_iteration) {
  1286. $sql_attach = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch';
  1287. $sql_attach .= ' SET fk_commandefourndet = '.((int) $obj_line->rowid).', qty = '.((float) $qty_for_line);
  1288. $sql_attach .= ' WHERE rowid = '.((int) $obj_dispatch->rowid);
  1289. $first_iteration = false;
  1290. } else {
  1291. $sql_attach_values = array(
  1292. ((int) $obj_dispatch->fk_commande),
  1293. ((int) $obj_dispatch->fk_product),
  1294. ((int) $obj_line->rowid),
  1295. ((float) $qty_for_line),
  1296. ((int) $obj_dispatch->fk_entrepot),
  1297. ((int) $obj_dispatch->fk_user),
  1298. $obj_dispatch->datec ? "'".$db->idate($db->jdate($obj_dispatch->datec))."'" : 'NULL',
  1299. $obj_dispatch->comment ? "'".$db->escape($obj_dispatch->comment)."'" : 'NULL',
  1300. $obj_dispatch->status ? ((int) $obj_dispatch->status) : 'NULL',
  1301. $obj_dispatch->tms ? "'".$db->idate($db->jdate($obj_dispatch->tms))."'" : 'NULL',
  1302. $obj_dispatch->batch ? "'".$db->escape($obj_dispatch->batch)."'" : 'NULL',
  1303. $obj_dispatch->eatby ? "'".$db->escape($obj_dispatch->eatby)."'" : 'NULL',
  1304. $obj_dispatch->sellby ? "'".$db->escape($obj_dispatch->sellby)."'" : 'NULL'
  1305. );
  1306. $sql_attach_values = join(', ', $sql_attach_values);
  1307. $sql_attach = 'INSERT INTO '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch';
  1308. $sql_attach .= ' (fk_commande, fk_product, fk_commandefourndet, qty, fk_entrepot, fk_user, datec, comment, status, tms, batch, eatby, sellby)';
  1309. $sql_attach .= " VALUES (".$sql_attach_values.")";
  1310. }
  1311. if ($repair_link_dispatch_lines_supplier_order_lines == 'confirmed') {
  1312. $resql_attach = $db->query($sql_attach);
  1313. } else {
  1314. $resql_attach = true; // Force success in test mode
  1315. }
  1316. if ($resql_attach) {
  1317. $remaining_qty -= $qty_for_line;
  1318. } else {
  1319. $errors[] = $sql_attach;
  1320. }
  1321. $first_iteration = false;
  1322. }
  1323. $n_processed_rows++;
  1324. // report progress every 256th row
  1325. if (!($n_processed_rows & 0xff)) {
  1326. echo '<tr><td>Processed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
  1327. flush();
  1328. ob_flush();
  1329. }
  1330. }
  1331. } else {
  1332. echo '<tr><td>Unable to find any dispatch without an fk_commandefourndet.'."</td></tr>\n";
  1333. echo $sql_dispatch."\n";
  1334. }
  1335. echo '<tr><td>Fixed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
  1336. echo '<tr><td>DONE.'."</td></tr>\n";
  1337. if (count($errors)) {
  1338. $db->rollback();
  1339. echo '<tr><td>The transaction was rolled back due to errors: nothing was changed by the script.</td></tr>';
  1340. } else {
  1341. $db->commit();
  1342. }
  1343. $db->close();
  1344. echo '<tr><td><h3>SQL queries with errors:</h3></tr></td>';
  1345. echo '<tr><td>'.join('</td></tr><tr><td>', $errors).'</td></tr>';
  1346. }
  1347. // Repair llx_commande_fournisseur to eleminate duplicate reference
  1348. if ($ok && GETPOST('repair_supplier_order_duplicate_ref')) {
  1349. require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.commande.class.php';
  1350. include_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
  1351. $db->begin();
  1352. $err = 0;
  1353. // Query to find all duplicate supplier orders
  1354. $sql = "SELECT * FROM " . MAIN_DB_PREFIX . "commande_fournisseur";
  1355. $sql .= " WHERE ref IN (SELECT cf.ref FROM " . MAIN_DB_PREFIX . "commande_fournisseur cf GROUP BY cf.ref, cf.entity HAVING COUNT(cf.rowid) > 1)";
  1356. // Build a list of ref => []CommandeFournisseur
  1357. $duplicateSupplierOrders = [];
  1358. $resql = $db->query($sql);
  1359. if ($resql) {
  1360. while ($rawSupplierOrder = $db->fetch_object($resql)) {
  1361. $supplierOrder = new CommandeFournisseur($db);
  1362. $supplierOrder->setVarsFromFetchObj($rawSupplierOrder);
  1363. $duplicateSupplierOrders[$rawSupplierOrder->ref] [] = $supplierOrder;
  1364. }
  1365. } else {
  1366. $err++;
  1367. }
  1368. // Process all duplicate supplier order and regenerate the reference for all except the first one
  1369. foreach ($duplicateSupplierOrders as $ref => $supplierOrders) {
  1370. /** @var CommandeFournisseur $supplierOrder */
  1371. foreach (array_slice($supplierOrders, 1) as $supplierOrder) {
  1372. // Definition of supplier order numbering model name
  1373. $soc = new Societe($db);
  1374. $soc->fetch($supplierOrder->fourn_id);
  1375. $newRef = $supplierOrder->getNextNumRef($soc);
  1376. $sql = "UPDATE " . MAIN_DB_PREFIX . "commande_fournisseur cf SET cf.ref = '" . $db->escape($newRef) . "' WHERE cf.rowid = " . (int) $supplierOrder->id;
  1377. if (!$db->query($sql)) {
  1378. $err++;
  1379. }
  1380. }
  1381. }
  1382. if ($err == 0) {
  1383. $db->commit();
  1384. } else {
  1385. $db->rollback();
  1386. }
  1387. }
  1388. print '</table>';
  1389. if (empty($actiondone)) {
  1390. print '<div class="error">'.$langs->trans("ErrorWrongParameters").'</div>';
  1391. }
  1392. if ($oneoptionset) {
  1393. print '<div class="center" style="padding-top: 10px"><a href="../index.php?mainmenu=home&leftmenu=home'.(GETPOSTISSET("login") ? '&username='.urlencode(GETPOST("login")) : '').'">';
  1394. print $langs->trans("GoToDolibarr");
  1395. print '</a></div>';
  1396. } else {
  1397. print '<div class="center warning" style="padding-top: 10px">';
  1398. print $langs->trans("SetAtLeastOneOptionAsUrlParameter");
  1399. print '</div>';
  1400. }
  1401. dolibarr_install_syslog("--- repair: end");
  1402. pFooter(1, $setuplang);
  1403. if ($db->connected) {
  1404. $db->close();
  1405. }
  1406. // Return code if ran from command line
  1407. if (!$ok && isset($argv[1])) {
  1408. exit(1);
  1409. }