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