dolibarr 22.0.5
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-2024 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
8 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2024 Vincent de Grandpré <vincent@de-grandpre.quebec>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
30include_once 'inc.php';
31if (file_exists($conffile)) {
32 include_once $conffile;
33}
46'
47@phan-var-force ?string $dolibarr_main_db_encryption
48@phan-var-force ?string $dolibarr_main_db_cryptkey
49';
50
51require_once $dolibarr_main_document_root.'/core/lib/admin.lib.php';
52include_once $dolibarr_main_document_root.'/core/lib/images.lib.php';
53require_once $dolibarr_main_document_root.'/core/class/extrafields.class.php';
54require_once 'lib/repair.lib.php';
55
56$step = 2;
57$ok = 0;
58
59
60// This page may be long. We are increasing the time allowed.
61// Only works if not in safe_mode.
62$err = error_reporting();
63error_reporting(0);
64@set_time_limit(120);
65error_reporting($err);
66
67$setuplang = GETPOST("selectlang", 'aZ09', 3) ? GETPOST("selectlang", 'aZ09', 3) : 'auto';
68$langs->setDefaultLang($setuplang);
69
70$langs->loadLangs(array("admin", "install", "other"));
71
72if ($dolibarr_main_db_type == "mysqli") {
73 $choix = 1;
74}
75if ($dolibarr_main_db_type == "pgsql") {
76 $choix = 2;
77}
78if ($dolibarr_main_db_type == "mssql") {
79 $choix = 3;
80}
81
82
83dolibarr_install_syslog("--- repair: entering upgrade.php page");
84if (!is_object($conf)) {
85 dolibarr_install_syslog("repair: conf file not initialized", LOG_ERR);
86}
87
88
89/*
90 * View
91 */
92
93pHeader($langs->trans("Repair"), "upgrade2", GETPOST('action', 'aZ09'));
94
95// Action to launch the repair script
96$actiondone = 1;
97
98print '<div class="warning" style="padding-top: 10px">';
99print $langs->trans("SetAtLeastOneOptionAsUrlParameter");
100print '</div>';
101
102//print 'You must set one of the following option with a parameter value that is "test" or "confirmed" on the URL<br>';
103//print $langs->trans("Example").': '.DOL_MAIN_URL_ROOT.'/install/repair.php?standard=confirmed<br>'."\n";
104print '<br>';
105
106print 'Option standard is '.(GETPOST('standard', 'alpha') ? GETPOST('standard', 'alpha') : 'undefined').'<br>'."\n";
107// Disable modules
108print 'Option force_disable_of_modules_not_found is '.(GETPOST('force_disable_of_modules_not_found', 'alpha') ? GETPOST('force_disable_of_modules_not_found', 'alpha') : 'undefined').'<br>'."\n";
109// Files
110print 'Option restore_thirdparties_logos is '.(GETPOST('restore_thirdparties_logos', 'alpha') ? GETPOST('restore_thirdparties_logos', 'alpha') : 'undefined').'<br>'."\n";
111print 'Option restore_user_pictures is '.(GETPOST('restore_user_pictures', 'alpha') ? GETPOST('restore_user_pictures', 'alpha') : 'undefined').'<br>'."\n";
112print 'Option rebuild_product_thumbs is '.(GETPOST('rebuild_product_thumbs', 'alpha') ? GETPOST('rebuild_product_thumbs', 'alpha') : 'undefined').'<br>'."\n";
113// Clean tables and data
114print 'Option clean_linked_elements is '.(GETPOST('clean_linked_elements', 'alpha') ? GETPOST('clean_linked_elements', 'alpha') : 'undefined').'<br>'."\n";
115print 'Option clean_menus is '.(GETPOST('clean_menus', 'alpha') ? GETPOST('clean_menus', 'alpha') : 'undefined').'<br>'."\n";
116print 'Option clean_orphelin_dir is '.(GETPOST('clean_orphelin_dir', 'alpha') ? GETPOST('clean_orphelin_dir', 'alpha') : 'undefined').'<br>'."\n";
117print 'Option clean_product_stock_batch is '.(GETPOST('clean_product_stock_batch', 'alpha') ? GETPOST('clean_product_stock_batch', 'alpha') : 'undefined').'<br>'."\n";
118print 'Option clean_perm_table is '.(GETPOST('clean_perm_table', 'alpha') ? GETPOST('clean_perm_table', 'alpha') : 'undefined').'<br>'."\n";
119print 'Option clean_ecm_files_table is '.(GETPOST('clean_ecm_files_table', 'alpha') ? GETPOST('clean_ecm_files_table', 'alpha') : 'undefined').'<br>'."\n";
120print 'Option repair_link_dispatch_lines_supplier_order_lines, is '.(GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha') ? GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha') : 'undefined').'<br>'."\n";
121// Init data
122print 'Option set_empty_time_spent_amount is '.(GETPOST('set_empty_time_spent_amount', 'alpha') ? GETPOST('set_empty_time_spent_amount', 'alpha') : 'undefined').'<br>'."\n";
123// Structure
124print 'Option force_utf8_on_tables (force utf8 + row=dynamic), for mysql/mariadb only, is '.(GETPOST('force_utf8_on_tables', 'alpha') ? GETPOST('force_utf8_on_tables', 'alpha') : 'undefined').'<br>'."\n";
125print '<span class="valignmiddle">'."Option force_utf8mb4_on_tables (force utf8mb4 + row=dynamic), for mysql/mariadb only, is ".(GETPOST('force_utf8mb4_on_tables', 'alpha') ? GETPOST('force_utf8mb4_on_tables', 'alpha') : 'undefined');
126print '</span>';
127if ($dolibarr_main_db_character_set != 'utf8mb4') {
128 print '<img src="../theme/eldy/img/warning.png" class="pictofortooltip valignmiddle" title="If you switch to utf8mb4, you must also check the value for $dolibarr_main_db_character_set and $dolibarr_main_db_collation into conf/conf.php file.">';
129}
130print "<br>\n";
131print "Option force_collation_from_conf_on_tables (force ".$conf->db->character_set."/".$conf->db->dolibarr_main_db_collation." + row=dynamic), for mysql/mariadb only is ".(GETPOST('force_collation_from_conf_on_tables', 'alpha') ? GETPOST('force_collation_from_conf_on_tables', 'alpha') : 'undefined')."<br>\n";
132
133// Rebuild sequence
134print 'Option rebuild_sequences, for postgresql only, is '.(GETPOST('rebuild_sequences', 'alpha') ? GETPOST('rebuild_sequences', 'alpha') : 'undefined').'<br>'."\n";
135print '<br>';
136
137print '<hr>';
138
139print '<table cellspacing="0" cellpadding="1" class="centpercent">';
140$error = 0;
141
142// If password is encoded, we decode it
143if (preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass) || !empty($dolibarr_main_db_encrypted_pass)) {
144 require_once $dolibarr_main_document_root.'/core/lib/security.lib.php';
145 if (preg_match('/crypted:/i', $dolibarr_main_db_pass)) {
146 $dolibarr_main_db_pass = preg_replace('/crypted:/i', '', $dolibarr_main_db_pass);
147 $dolibarr_main_db_encrypted_pass = $dolibarr_main_db_pass; // We need to set this as it is used to know the password was initially encrypted
148 $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_pass);
149 } elseif (preg_match('/dolcrypt:/i', $dolibarr_main_db_pass)) {
150 $dolibarr_main_db_encrypted_pass = $dolibarr_main_db_pass; // We need to set this as it is used to know the password was initially encrypted
151 $dolibarr_main_db_pass = dolDecrypt($dolibarr_main_db_pass);
152 } else {
153 $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_encrypted_pass);
154 }
155}
156
157// $conf is already instancied inside inc.php
158$conf->db->type = $dolibarr_main_db_type;
159$conf->db->host = $dolibarr_main_db_host;
160$conf->db->port = $dolibarr_main_db_port;
161$conf->db->name = $dolibarr_main_db_name;
162$conf->db->user = $dolibarr_main_db_user;
163$conf->db->pass = $dolibarr_main_db_pass;
164
165// For encryption
166$conf->db->dolibarr_main_db_encryption = isset($dolibarr_main_db_encryption) ? $dolibarr_main_db_encryption : 0;
167$conf->db->dolibarr_main_db_cryptkey = isset($dolibarr_main_db_cryptkey) ? $dolibarr_main_db_cryptkey : '';
168
169$db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int) $conf->db->port);
170
171if ($db->connected) {
172 print '<tr><td class="nowrap">';
173 print $langs->trans("ServerConnection")." : $dolibarr_main_db_host</td><td class=\"right\">".$langs->trans("OK")."</td></tr>";
174 dolibarr_install_syslog("repair: ".$langs->transnoentities("ServerConnection").": ".$dolibarr_main_db_host.$langs->transnoentities("OK"));
175 $ok = 1;
176} else {
177 print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name)."</td><td class=\"right\">".$langs->transnoentities("Error")."</td></tr>";
178 dolibarr_install_syslog("repair: ".$langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
179 $ok = 0;
180}
181
182if ($ok) {
183 if ($db->database_selected) {
184 print '<tr><td class="nowrap">';
185 print $langs->trans("DatabaseConnection")." : ".$dolibarr_main_db_name."</td><td class=\"right\">".$langs->trans("OK")."</td></tr>";
186 dolibarr_install_syslog("repair: database connection successful: ".$dolibarr_main_db_name);
187 $ok = 1;
188 } else {
189 print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name)."</td><td class=\"right\">".$langs->trans("Error")."</td></tr>";
190 dolibarr_install_syslog("repair: ".$langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
191 $ok = 0;
192 }
193}
194
195// Show database version
196if ($ok) {
197 $version = $db->getVersion();
198 $versionarray = $db->getVersionArray();
199 print '<tr><td>'.$langs->trans("ServerVersion").'</td>';
200 print '<td class="right">'.$version.'</td></tr>';
201 dolibarr_install_syslog("repair: ".$langs->transnoentities("ServerVersion").": ".$version);
202 //print '<td class="right">'.join('.',$versionarray).'</td></tr>';
203}
204
205$conf->setValues($db);
206// Reset forced setup after the setValues
207if (defined('SYSLOG_FILE')) {
208 $conf->global->SYSLOG_FILE = constant('SYSLOG_FILE');
209}
210$conf->global->MAIN_ENABLE_LOG_TO_HTML = 1;
211
212
213/* Start action here */
214$oneoptionset = 0;
215$oneoptionset = (GETPOST('standard', 'alpha') || GETPOST('restore_thirdparties_logos', 'alpha') || GETPOST('clean_linked_elements', 'alpha') || GETPOST('clean_menus', 'alpha')
216 || GETPOST('clean_orphelin_dir', 'alpha') || GETPOST('clean_product_stock_batch', 'alpha') || GETPOST('set_empty_time_spent_amount', 'alpha') || GETPOST('rebuild_product_thumbs', 'alpha')
217 || GETPOST('clean_perm_table', 'alpha') || GETPOST('clean_ecm_files_table', 'alpha')
218 || GETPOST('force_disable_of_modules_not_found', 'alpha')
219 || GETPOST('force_utf8_on_tables', 'alpha') || GETPOST('force_utf8mb4_on_tables', 'alpha') || GETPOST('force_collation_from_conf_on_tables', 'alpha')
220 || GETPOST('rebuild_sequences', 'alpha') || GETPOST('recalculateinvoicetotal', 'alpha'));
221
222if ($ok && $oneoptionset) {
223 // Show wait message
224 print '<tr><td colspan="2">'.$langs->trans("PleaseBePatient").'<br><br></td></tr>';
225 flush();
226}
227
228
229// run_sql: Run repair SQL file
230if ($ok && GETPOST('standard', 'alpha')) {
231 $dir = "mysql/migration/";
232
233 $filelist = array();
234 $i = 0;
235 $ok = 0;
236
237 // Recupere list fichier
238 $filesindir = array();
239 $handle = opendir($dir);
240 if (is_resource($handle)) {
241 while (($file = readdir($handle)) !== false) {
242 if (preg_match('/\.sql$/i', $file)) {
243 $filesindir[] = $file;
244 }
245 }
246 }
247 sort($filesindir);
248
249 foreach ($filesindir as $file) {
250 if (preg_match('/repair/i', $file)) {
251 $filelist[] = $file;
252 }
253 }
254
255 // Loop on each file
256 foreach ($filelist as $file) {
257 print '<tr><td class="nowrap">*** ';
258 print $langs->trans("Script").'</td><td class="right">'.$file.'</td></tr>';
259
260 $name = substr($file, 0, dol_strlen($file) - 4);
261
262 // Run sql script
263 $ok = run_sql($dir.$file, 0, 0, 1);
264 }
265}
266
267
268// sync_extrafields: Search list of fields declared and list of fields created into databases, then create fields missing
269
270if ($ok && GETPOST('standard', 'alpha')) {
271 $extrafields = new ExtraFields($db);
272
273 // List of tables that has an extrafield table
274 $listofmodulesextra = array('societe' => 'societe', 'adherent' => 'adherent', 'product' => 'product',
275 'socpeople' => 'socpeople', 'propal' => 'propal', 'commande' => 'commande',
276 'facture' => 'facture', 'facturedet' => 'facturedet', 'facture_rec' => 'facture_rec', 'facturedet_rec' => 'facturedet_rec',
277 'supplier_proposal' => 'supplier_proposal', 'commande_fournisseur' => 'commande_fournisseur',
278 'facture_fourn' => 'facture_fourn', 'facture_fourn_rec' => 'facture_fourn_rec', 'facture_fourn_det' => 'facture_fourn_det', 'facture_fourn_det_rec' => 'facture_fourn_det_rec',
279 'fichinter' => 'fichinter', 'fichinterdet' => 'fichinterdet',
280 'inventory' => 'inventory',
281 'actioncomm' => 'actioncomm', 'bom_bom' => 'bom_bom', 'mrp_mo' => 'mrp_mo',
282 'adherent_type' => 'adherent_type', 'user' => 'user', 'partnership' => 'partnership', 'projet' => 'projet', 'projet_task' => 'projet_task', 'ticket' => 'ticket');
283 //$listofmodulesextra = array('fichinter'=>'fichinter');
284
285 print '<tr><td colspan="2"><br>*** Check fields into extra table structure match table of definition. If not add column into table</td></tr>';
286 foreach ($listofmodulesextra as $tablename => $elementtype) {
287 // Get list of fields
288 $tableextra = MAIN_DB_PREFIX.$tablename.'_extrafields';
289
290 // Define $arrayoffieldsdesc
291 $arrayoffieldsdesc = $extrafields->fetch_name_optionals_label($elementtype);
292
293 // Define $arrayoffieldsfound
294 $arrayoffieldsfound = array();
295 $resql = $db->DDLDescTable($tableextra);
296 if ($resql) {
297 print '<tr><td>Check availability of extra field for '.$tableextra;
298 $i = 0;
299 while ($obj = $db->fetch_object($resql)) {
300 $fieldname = $fieldtype = '';
301 if (preg_match('/mysql/', $db->type)) {
302 $fieldname = $obj->Field;
303 $fieldtype = $obj->Type;
304 } else {
305 $fieldname = isset($obj->Key) ? $obj->Key : $obj->attname;
306 $fieldtype = isset($obj->Type) ? $obj->Type : 'varchar';
307 }
308
309 if (empty($fieldname)) {
310 continue;
311 }
312 if (in_array($fieldname, array('rowid', 'tms', 'fk_object', 'import_key'))) {
313 continue;
314 }
315 $arrayoffieldsfound[$fieldname] = array('type' => $fieldtype);
316 }
317 print ' - Found '.count($arrayoffieldsfound).' fields into table';
318 if (count($arrayoffieldsfound) > 0) {
319 print ' <span class="opacitymedium">('.implode(', ', array_keys($arrayoffieldsfound)).')</span>';
320 }
321 print '<br>'."\n";
322
323 // If it does not match, we create fields
324 foreach ($arrayoffieldsdesc as $code => $label) {
325 if (!in_array($code, array_keys($arrayoffieldsfound))) {
326 print 'Found field '.$code.' declared into '.MAIN_DB_PREFIX.'extrafields table but not found into desc of table '.$tableextra." -> ";
327 $type = $extrafields->attributes[$elementtype]['type'][$code];
328 $length = $extrafields->attributes[$elementtype]['size'][$code];
329 $attribute = '';
330 $default = '';
331 $extra = '';
332 $null = 'null';
333
334 if ($type == 'boolean') {
335 $typedb = 'int';
336 $lengthdb = '1';
337 } elseif ($type == 'price') {
338 $typedb = 'double';
339 $lengthdb = '24,8';
340 } elseif ($type == 'phone') {
341 $typedb = 'varchar';
342 $lengthdb = '20';
343 } elseif ($type == 'mail') {
344 $typedb = 'varchar';
345 $lengthdb = '128';
346 } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
347 $typedb = 'text';
348 $lengthdb = '';
349 } elseif ($type == 'link') {
350 $typedb = 'int';
351 $lengthdb = '11';
352 } else {
353 $typedb = $type;
354 $lengthdb = $length;
355 }
356
357 $field_desc = array(
358 'type' => $typedb,
359 'value' => $lengthdb,
360 'attribute' => $attribute,
361 'default' => $default,
362 'extra' => $extra,
363 'null' => $null
364 );
365 //var_dump($field_desc);exit;
366
367 $result = 0;
368 if (GETPOST('standard', 'alpha') == 'confirmed') {
369 $result = $db->DDLAddField($tableextra, $code, $field_desc, "");
370
371 if ($result < 0) {
372 print "KO ".$db->lasterror."<br>\n";
373 } else {
374 print "OK<br>\n";
375 }
376 } else {
377 print ' - Mode test, no column added.';
378 }
379 }
380 }
381
382 print "</td><td>&nbsp;</td></tr>\n";
383 } else {
384 print '<tr><td>Table '.$tableextra.' is not found</td><td></td></tr>'."\n";
385 }
386 }
387}
388
389
390// clean_data_ecm_dir: Clean data into ecm_directories table
391if ($ok && GETPOST('standard', 'alpha')) {
393}
394
395
396// clean declaration constants
397if ($ok && GETPOST('standard', 'alpha')) {
398 print '<tr><td colspan="2"><br>*** Clean constant record of modules not enabled</td></tr>';
399
400 $sql = "SELECT name, entity, value";
401 $sql .= " FROM ".MAIN_DB_PREFIX."const as c";
402 $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'";
403 $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'";
404 $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_%'";
405 $sql .= " OR name LIKE 'MAIN_MODULE_%_MODULEFOREXTERNAL'";
406 $sql .= " ORDER BY name, entity";
407
408 $resql = $db->query($sql);
409 if ($resql) {
410 $num = $db->num_rows($resql);
411
412 if ($num) {
413 $db->begin();
414
415 $i = 0;
416 while ($i < $num) {
417 $obj = $db->fetch_object($resql);
418
419 $reg = array();
420 if (preg_match('/MAIN_MODULE_([^_]+)_(.+)/i', $obj->name, $reg)) {
421 $name = $reg[1];
422 $type = $reg[2];
423
424 $sql2 = "SELECT COUNT(*) as nb";
425 $sql2 .= " FROM ".MAIN_DB_PREFIX."const as c";
426 $sql2 .= " WHERE name = 'MAIN_MODULE_".$name."'";
427 $sql2 .= " AND entity = ".((int) $obj->entity);
428 $resql2 = $db->query($sql2);
429 if ($resql2) {
430 $obj2 = $db->fetch_object($resql2);
431 if ($obj2 && $obj2->nb == 0) {
432 // Module not found, so we can remove entry
433 $sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = '".$db->escape($obj->name)."' AND entity = ".((int) $obj->entity);
434
435 if (GETPOST('standard', 'alpha') == 'confirmed') {
436 $db->query($sqldelete);
437
438 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>';
439 } else {
440 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>';
441 }
442 } else {
443 //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>';
444 }
445 }
446 }
447
448 $i++;
449 }
450
451 $db->commit();
452 }
453 } else {
454 dol_print_error($db);
455 }
456}
457
458
459// clean box of not enabled modules
460if ($ok && GETPOST('standard', 'alpha')) {
461 print '<tr><td colspan="2"><br>*** Clean definition of boxes of modules not enabled</td></tr>';
462
463 $sql = "SELECT file, entity FROM ".MAIN_DB_PREFIX."boxes_def";
464 $sql .= " WHERE file like '%@%'";
465
466 $resql = $db->query($sql);
467 if ($resql) {
468 $num = $db->num_rows($resql);
469
470 if ($num) {
471 $db->begin();
472
473 $i = 0;
474 while ($i < $num) {
475 $obj = $db->fetch_object($resql);
476
477 $reg = array();
478 if (preg_match('/^(.+)@(.+)$/i', $obj->file, $reg)) {
479 $name = $reg[1];
480 $module = $reg[2];
481
482 $sql2 = "SELECT COUNT(*) as nb";
483 $sql2 .= " FROM ".MAIN_DB_PREFIX."const as c";
484 $sql2 .= " WHERE name = 'MAIN_MODULE_".strtoupper($module)."'";
485 $sql2 .= " AND entity = ".((int) $obj->entity);
486 $sql2 .= " AND value <> 0";
487 $resql2 = $db->query($sql2);
488 if ($resql2) {
489 $obj2 = $db->fetch_object($resql2);
490 if ($obj2 && $obj2->nb == 0) {
491 // Module not found, so we canremove entry
492 $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).")";
493 $sqldeleteb = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def WHERE file = '".$db->escape($obj->file)."' AND entity = ".((int) $obj->entity);
494
495 if (GETPOST('standard', 'alpha') == 'confirmed') {
496 $db->query($sqldeletea);
497 $db->query($sqldeleteb);
498
499 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>';
500 } else {
501 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>';
502 }
503 } else {
504 //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>';
505 }
506 }
507 }
508
509 $i++;
510 }
511
512 $db->commit();
513 }
514 }
515}
516
517
518// restore_thirdparties_logos: Move logos to correct new directory.
519if ($ok && GETPOST('restore_thirdparties_logos')) {
520 //$exts=array('gif','png','jpg');
521
522 $ext = '';
523
524 print '<tr><td colspan="2"><br>*** Restore thirdparties logo<br>';
525
526 $sql = "SELECT s.rowid, s.nom as name, s.logo FROM ".MAIN_DB_PREFIX."societe as s ORDER BY s.nom";
527 $resql = $db->query($sql);
528 if ($resql) {
529 $num = $db->num_rows($resql);
530 $i = 0;
531
532 while ($i < $num) {
533 $obj = $db->fetch_object($resql);
534
535 /*
536 $name=preg_replace('/é/','',$obj->name);
537 $name=preg_replace('/ /','_',$name);
538 $name=preg_replace('/\'/','',$name);
539 */
540
541 $tmp = explode('.', (string) $obj->logo);
542 $name = $tmp[0];
543 if (isset($tmp[1])) {
544 $ext = '.'.$tmp[1];
545 }
546
547 if (!empty($name)) {
548 $filetotest = $dolibarr_main_data_root.'/societe/logos/'.$name.$ext;
549 $filetotestsmall = $dolibarr_main_data_root.'/societe/logos/thumbs/'.$name.'_small'.$ext;
550 $exists = (int) dol_is_file($filetotest);
551 print 'Check thirdparty '.$obj->rowid.' name='.$obj->name.' logo='.$obj->logo.' file '.$filetotest." exists=".$exists."<br>\n";
552 if ($exists) {
553 $filetarget = $dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/'.$name.$ext;
554 $filetargetsmall = $dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/thumbs/'.$name.'_small'.$ext;
555 $existt = dol_is_file($filetarget);
556 if (!$existt) {
557 if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
558 dol_mkdir($dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos');
559 }
560
561 print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotest." -> ".$filetarget."<br>\n";
562 if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
563 dol_copy($filetotest, $filetarget, '', 0);
564 }
565 }
566
567 $existtt = dol_is_file($filetargetsmall);
568 if (!$existtt) {
569 if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
570 dol_mkdir($dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/thumbs');
571 }
572 print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestsmall." -> ".$filetargetsmall."<br>\n";
573 if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
574 dol_copy($filetotestsmall, $filetargetsmall, '', 0);
575 }
576 }
577 }
578 }
579
580 $i++;
581 }
582 } else {
583 $ok = 0;
584 dol_print_error($db);
585 }
586
587 print '</td></tr>';
588}
589
590
591
592// restore_user_pictures: Move pictures to correct new directory.
593if ($ok && GETPOST('restore_user_pictures', 'alpha')) {
594 //$exts=array('gif','png','jpg');
595
596 $ext = '';
597
598 print '<tr><td colspan="2"><br>*** Restore user pictures<br>';
599
600 $sql = "SELECT s.rowid, s.firstname, s.lastname, s.login, s.photo FROM ".MAIN_DB_PREFIX."user as s ORDER BY s.rowid";
601 $resql = $db->query($sql);
602 if ($resql) {
603 $num = $db->num_rows($resql);
604 $i = 0;
605
606 while ($i < $num) {
607 $obj = $db->fetch_object($resql);
608
609 /*
610 $name=preg_replace('/é/','',$obj->name);
611 $name=preg_replace('/ /','_',$name);
612 $name=preg_replace('/\'/','',$name);
613 */
614
615 $tmp = explode('.', (string) $obj->photo);
616 $name = $tmp[0];
617 if (isset($tmp[1])) {
618 $ext = '.'.$tmp[1];
619 }
620
621 if (!empty($name)) {
622 $filetotest = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/'.$name.$ext;
623 $filetotestsmall = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/thumbs/'.$name.'_small'.$ext;
624 $filetotestmini = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/thumbs/'.$name.'_mini'.$ext;
625 $exists = (int) dol_is_file($filetotest);
626 print 'Check user '.$obj->rowid.' lastname='.$obj->lastname.' firstname='.$obj->firstname.' photo='.$obj->photo.' file '.$filetotest." exists=".$exists."<br>\n";
627 if ($exists) {
628 $filetarget = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/'.$name.$ext;
629 $filetargetsmall = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs/'.$name.'_small'.$ext;
630 $filetargetmini = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs/'.$name.'_mini'.$ext;
631
632 $existt = dol_is_file($filetarget);
633 if (!$existt) {
634 if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
635 dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid);
636 }
637
638 print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotest." -> ".$filetarget."<br>\n";
639 if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
640 dol_copy($filetotest, $filetarget, '', 0);
641 }
642 }
643
644 $existtt = dol_is_file($filetargetsmall);
645 if (!$existtt) {
646 if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
647 dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs');
648 }
649
650 print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestsmall." -> ".$filetargetsmall."<br>\n";
651 if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
652 dol_copy($filetotestsmall, $filetargetsmall, '', 0);
653 }
654 }
655
656 $existtt = dol_is_file($filetargetmini);
657 if (!$existtt) {
658 if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
659 dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs');
660 }
661
662 print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestmini." -> ".$filetargetmini."<br>\n";
663 if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
664 dol_copy($filetotestmini, $filetargetmini, '', 0);
665 }
666 }
667 }
668 }
669
670 $i++;
671 }
672 } else {
673 $ok = 0;
674 dol_print_error($db);
675 }
676
677 print '</td></tr>';
678}
679
680
681// rebuild_product_thumbs: Rebuild thumbs for product files
682if ($ok && GETPOST('rebuild_product_thumbs', 'alpha')) {
683 $ext = '';
684 global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini;
685
686 print '<tr><td colspan="2"><br>*** Rebuild product thumbs<br>';
687
688 $sql = "SELECT s.rowid, s.ref FROM ".MAIN_DB_PREFIX."product as s ORDER BY s.ref";
689 $resql = $db->query($sql);
690 if ($resql) {
691 $num = $db->num_rows($resql);
692 $i = 0;
693
694 while ($i < $num) {
695 $obj = $db->fetch_object($resql);
696
697 if (!empty($obj->ref)) {
698 $files = dol_dir_list($dolibarr_main_data_root.'/produit/'.$obj->ref, 'files', 0);
699 foreach ($files as $file) {
700 // Generate thumbs.
701 if (image_format_supported($file['fullname']) == 1) {
702 $imgThumbSmall = 'notbuild';
703 if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
704 // Used on logon for example
705 $imgThumbSmall = vignette($file['fullname'], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
706 }
707 print 'Check product '.$obj->rowid.", file ".$file['fullname']." -> ".$imgThumbSmall." maxwidthsmall=".$maxwidthsmall." maxheightsmall=".$maxheightsmall."<br>\n";
708 $imgThumbMini = 'notbuild';
709 if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
710 // Create mini thumbs for image (Ratio is near 16/9)
711 // Used on menu or for setup page for example
712 $imgThumbMini = vignette($file['fullname'], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
713 }
714 print 'Check product '.$obj->rowid.", file ".$file['fullname']." -> ".$imgThumbMini." maxwidthmini=".$maxwidthmini." maxheightmini=".$maxheightmini."<br>\n";
715 }
716 }
717 }
718
719 $i++;
720 }
721 } else {
722 $ok = 0;
723 dol_print_error($db);
724 }
725
726 print '</td></tr>';
727}
728
729// clean_linked_elements: Check and clean linked elements
730if ($ok && GETPOST('clean_linked_elements', 'alpha')) {
731 print '<tr><td colspan="2"><br>*** Check table of linked elements and delete orphelins links</td></tr>';
732 // propal => order
733 print '<tr><td colspan="2">'.checkLinkedElements('propal', 'commande')."</td></tr>\n";
734
735 // propal => invoice
736 print '<tr><td colspan="2">'.checkLinkedElements('propal', 'facture')."</td></tr>\n";
737
738 // order => invoice
739 print '<tr><td colspan="2">'.checkLinkedElements('commande', 'facture')."</td></tr>\n";
740
741 // order => shipping
742 print '<tr><td colspan="2">'.checkLinkedElements('commande', 'shipping')."</td></tr>\n";
743
744 // shipping => delivery
745 print '<tr><td colspan="2">'.checkLinkedElements('shipping', 'delivery')."</td></tr>\n";
746
747 // order_supplier => invoice_supplier
748 print '<tr><td colspan="2">'.checkLinkedElements('order_supplier', 'invoice_supplier')."</td></tr>\n";
749}
750
751
752// clean_menus: Check orphelins menus
753if ($ok && GETPOST('clean_menus', 'alpha')) {
754 print '<tr><td colspan="2"><br>*** Clean menu entries coming from disabled modules</td></tr>';
755
756 $sql = "SELECT rowid, module";
757 $sql .= " FROM ".MAIN_DB_PREFIX."menu as c";
758 $sql .= " WHERE module IS NOT NULL AND module <> ''";
759 $sql .= " ORDER BY module";
760
761 $resql = $db->query($sql);
762 if ($resql) {
763 $num = $db->num_rows($resql);
764 if ($num) {
765 $i = 0;
766 while ($i < $num) {
767 $obj = $db->fetch_object($resql);
768
769 $modulecond = $obj->module;
770 $modulecondarray = explode('|', $obj->module); // Name of module
771
772 print '<tr><td>';
773 print $modulecond;
774
775 $db->begin();
776
777 if ($modulecond) { // And menu entry for module $modulecond was found in database.
778 $moduleok = 0;
779 foreach ($modulecondarray as $tmpname) {
780 if ($tmpname == 'margins') {
781 $tmpname = 'margin'; // TODO Remove this when normalized
782 }
783
784 $result = 0;
785 if (!empty($conf->$tmpname)) {
786 $result = $conf->$tmpname->enabled;
787 }
788 if ($result) {
789 $moduleok++;
790 }
791 }
792
793 if (!$moduleok && $modulecond) {
794 print ' - Module condition '.$modulecond.' seems ko, we delete menu entry.';
795 if (GETPOST('clean_menus') == 'confirmed') {
796 $sql2 = "DELETE FROM ".MAIN_DB_PREFIX."menu WHERE module = '".$db->escape($modulecond)."'";
797 $resql2 = $db->query($sql2);
798 if (!$resql2) {
799 $error++;
800 dol_print_error($db);
801 } else {
802 print ' - <span class="warning">Cleaned</span>';
803 }
804 } else {
805 print ' - <span class="warning">Canceled (test mode)</span>';
806 }
807 } else {
808 print ' - Module condition '.$modulecond.' is ok, we do nothing.';
809 }
810 }
811
812 if (!$error) {
813 $db->commit();
814 } else {
815 $db->rollback();
816 }
817
818 print'</td></tr>';
819
820 if ($error) {
821 break;
822 }
823
824 $i++;
825 }
826 } else {
827 print '<tr><td>No menu entries of disabled menus found</td></tr>';
828 }
829 } else {
830 dol_print_error($db);
831 }
832}
833
834
835
836// clean_orphelin_dir: Run purge of directory
837if ($ok && GETPOST('clean_orphelin_dir', 'alpha')) {
838 $listmodulepart = array('company', 'invoice', 'invoice_supplier', 'propal', 'order', 'order_supplier', 'contract', 'tax');
839 foreach ($listmodulepart as $modulepart) {
840 $filearray = array();
841 $upload_dir = isset($conf->$modulepart->dir_output) ? $conf->$modulepart->dir_output : '';
842 if ($modulepart == 'company') {
843 $upload_dir = $conf->societe->dir_output; // TODO change for multicompany sharing
844 }
845 if ($modulepart == 'invoice') {
846 $upload_dir = $conf->facture->dir_output;
847 }
848 if ($modulepart == 'invoice_supplier') {
849 $upload_dir = $conf->fournisseur->facture->dir_output;
850 }
851 if ($modulepart == 'order') {
852 $upload_dir = $conf->commande->dir_output;
853 }
854 if ($modulepart == 'order_supplier') {
855 $upload_dir = $conf->fournisseur->commande->dir_output;
856 }
857 if ($modulepart == 'contract') {
858 $upload_dir = $conf->contrat->dir_output;
859 }
860
861 if (empty($upload_dir)) {
862 continue;
863 }
864
865 print '<tr><td colspan="2"><br>*** Clean orphelins files into files '.$upload_dir.'</td></tr>';
866
867 $filearray = dol_dir_list($upload_dir, "files", 1, '', array('^SPECIMEN\.pdf$', '^\.', '(\.meta|_preview.*\.png)$', '^temp$', '^payments$', '^CVS$', '^thumbs$'), '', SORT_DESC, 1, 1);
868
869 $object_instance = null;
870 // To show ref or specific information according to view to show (defined by $module)
871 if ($modulepart == 'company') {
872 include_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
873 $object_instance = new Societe($db);
874 }
875 if ($modulepart == 'invoice') {
876 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
877 $object_instance = new Facture($db);
878 } elseif ($modulepart == 'invoice_supplier') {
879 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
880 $object_instance = new FactureFournisseur($db);
881 } elseif ($modulepart == 'propal') {
882 include_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
883 $object_instance = new Propal($db);
884 } elseif ($modulepart == 'order') {
885 include_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
886 $object_instance = new Commande($db);
887 } elseif ($modulepart == 'order_supplier') {
888 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
889 $object_instance = new CommandeFournisseur($db);
890 } elseif ($modulepart == 'contract') {
891 include_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
892 $object_instance = new Contrat($db);
893 } elseif ($modulepart == 'tax') {
894 include_once DOL_DOCUMENT_ROOT.'/compta/sociales/class/chargesociales.class.php';
895 $object_instance = new ChargeSociales($db);
896 }
897
898 foreach ($filearray as $key => $file) {
899 if (!is_dir($file['name'])
900 && $file['name'] != '.'
901 && $file['name'] != '..'
902 && $file['name'] != 'CVS'
903 ) {
904 // Define relative path used to store the file
905 $relativefile = preg_replace('/'.preg_quote($upload_dir.'/', '/').'/', '', $file['fullname']);
906
907 //var_dump($file);
908 $id = 0;
909 $ref = '';
910 $object_instance->id = 0;
911 $object_instance->ref = '';
912 $label = '';
913
914 // To show ref or specific information according to view to show (defined by $module)
915 if ($modulepart == 'invoice') {
916 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
917 $ref = $reg[1];
918 }
919 if ($modulepart == 'invoice_supplier') {
920 preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg);
921 $id = empty($reg[1]) ? '' : $reg[1];
922 }
923 if ($modulepart == 'propal') {
924 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
925 $ref = $reg[1];
926 }
927 if ($modulepart == 'order') {
928 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
929 $ref = $reg[1];
930 }
931 if ($modulepart == 'order_supplier') {
932 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
933 $ref = $reg[1];
934 }
935 if ($modulepart == 'contract') {
936 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
937 $ref = $reg[1];
938 }
939 if ($modulepart == 'tax') {
940 preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg);
941 $id = $reg[1];
942 }
943
944 if (($id || $ref) && $object_instance !== null) {
945 //print 'Fetch '.$id.' or '.$ref.'<br>';
946 $result = $object_instance->fetch($id, $ref);
947 //print $result.'<br>';
948 if ($result == 0) { // Not found but no error
949 // Clean of orphelins directories are done into repair.php
950 print '<tr><td colspan="2">';
951 print 'Delete orphelins file '.$file['fullname'].'<br>';
952 if (GETPOST('clean_orphelin_dir', 'alpha') == 'confirmed') {
953 dol_delete_file($file['fullname'], 1, 1, 1);
954 dol_delete_dir(dirname($file['fullname']), 1);
955 }
956 print "</td></tr>";
957 } elseif ($result < 0) {
958 print 'Error in '.get_class($object_instance).'.fetch of id'.$id.' ref='.$ref.', result='.$result.'<br>';
959 }
960 }
961 }
962 }
963 }
964}
965
966$methodtofix = '';
967// clean_linked_elements: Check and clean linked elements
968if ($ok && GETPOST('clean_product_stock_batch', 'alpha')) {
969 $methodtofix = GETPOST('methodtofix', 'alpha') ? GETPOST('methodtofix', 'alpha') : 'updatestock';
970
971 print '<tr><td colspan="2"><br>*** Clean table product_batch, methodtofix='.$methodtofix.' (possible values: updatestock or updatebatch)</td></tr>';
972
973 $sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
974 $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";
975 $sql .= " WHERE p.rowid = ps.fk_product";
976 $sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
977 $sql .= " HAVING (SUM(pb.qty) IS NOT NULL AND reel != SUM(pb.qty)) OR (SUM(pb.qty) IS NULL AND p.tobatch > 0)";
978 print $sql;
979 $resql = $db->query($sql);
980 if ($resql) {
981 $num = $db->num_rows($resql);
982
983 if ($num) {
984 $i = 0;
985 while ($i < $num) {
986 $obj = $db->fetch_object($resql);
987 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)';
988
989 // Fix is required
990 if ($obj->reel != $obj->reelbatch) {
991 if (empty($obj->tobatch)) {
992 // If product is not a product that support batches, we can clean stock by deleting the product batch lines
993 print ' -> Delete qty '.$obj->reelbatch.' for any lot linked to fk_product_stock='.$obj->psrowid;
994 $sql2 = "DELETE FROM ".MAIN_DB_PREFIX."product_batch";
995 $sql2 .= " WHERE fk_product_stock = ".((int) $obj->psrowid);
996 print '<br>'.$sql2;
997
998 if (GETPOST('clean_product_stock_batch') == 'confirmed') {
999 $resql2 = $db->query($sql2);
1000 if (!$resql2) {
1001 $error++;
1002 dol_print_error($db);
1003 }
1004 }
1005 } else {
1006 if ($methodtofix == 'updatebatch') {
1007 // Method 1
1008 print ' -> Insert qty '.($obj->reel - $obj->reelbatch).' with lot 000000 linked to fk_product_stock='.$obj->psrowid;
1009 $sql2 = "INSERT INTO ".MAIN_DB_PREFIX."product_batch(fk_product_stock, batch, qty)";
1010 $sql2 .= "VALUES(".((int) $obj->psrowid).", '000000', ".((float) ($obj->reel - $obj->reelbatch)).")";
1011 print '<br>'.$sql2;
1012
1013 if (GETPOST('clean_product_stock_batch') == 'confirmed') {
1014 $resql2 = $db->query($sql2);
1015 if (!$resql2) {
1016 // TODO If it fails, we must make update
1017 //$sql2 ="UPDATE ".MAIN_DB_PREFIX."product_batch";
1018 //$sql2.=" SET ".$obj->psrowid.", '000000', ".($obj->reel - $obj->reelbatch).")";
1019 //$sql2.=" WHERE fk_product_stock = ".((int) $obj->psrowid)
1020 }
1021 }
1022 }
1023 if ($methodtofix == 'updatestock') {
1024 // Method 2
1025 print ' -> Update qty of product_stock with qty = '.($obj->reelbatch ? ((float) $obj->reelbatch) : '0').' for ps.rowid = '.((int) $obj->psrowid);
1026 $sql2 = "UPDATE ".MAIN_DB_PREFIX."product_stock";
1027 $sql2 .= " SET reel = ".($obj->reelbatch ? ((float) $obj->reelbatch) : '0')." WHERE rowid = ".((int) $obj->psrowid);
1028 print '<br>'.$sql2;
1029
1030 if (GETPOST('clean_product_stock_batch') == 'confirmed') {
1031 $error = 0;
1032
1033 $db->begin();
1034
1035 $resql2 = $db->query($sql2);
1036 if ($resql2) {
1037 // We update product_stock, so we must fill p.stock into product too.
1038 $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)';
1039 $resql3 = $db->query($sql3);
1040 if (!$resql3) {
1041 $error++;
1042 dol_print_error($db);
1043 }
1044 } else {
1045 $error++;
1046 dol_print_error($db);
1047 }
1048
1049 if (!$error) {
1050 $db->commit();
1051 } else {
1052 $db->rollback();
1053 }
1054 }
1055 }
1056 }
1057 }
1058
1059 print'</td></tr>';
1060
1061 $i++;
1062 }
1063 } else {
1064 print '<tr><td colspan="2">Nothing to do</td></tr>';
1065 }
1066 } else {
1067 dol_print_error($db);
1068 }
1069}
1070
1071
1072// clean_product_stock_negative_if_batch
1073if ($ok && GETPOST('clean_product_stock_negative_if_batch', 'alpha')) {
1074 print '<tr><td colspan="2"><br>Clean table product_batch, methodtofix='.$methodtofix.' (possible values: updatestock or updatebatch)</td></tr>';
1075
1076 $sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
1077 $sql .= " FROM ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."product_stock as ps, ".MAIN_DB_PREFIX."product_batch as pb";
1078 $sql .= " WHERE p.rowid = ps.fk_product AND ps.rowid = pb.fk_product_stock";
1079 $sql .= " AND p.tobatch > 0";
1080 $sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
1081 $sql .= " HAVING reel != SUM(pb.qty)";
1082 $resql = $db->query($sql);
1083 if ($resql) {
1084 $num = $db->num_rows($resql);
1085
1086 if ($num) {
1087 $i = 0;
1088 while ($i < $num) {
1089 $obj = $db->fetch_object($resql);
1090 print '<tr><td>'.$obj->rowid.'-'.$obj->ref.'-'.$obj->fk_entrepot.' -> '.$obj->psrowid.': '.$obj->reel.' != '.$obj->reelbatch;
1091
1092 // TODO
1093 }
1094 }
1095 }
1096}
1097
1098// set_empty_time_spent_amount
1099if ($ok && GETPOST('set_empty_time_spent_amount', 'alpha')) {
1100 print '<tr><td colspan="2"><br>*** Set value of time spent without amount</td></tr>';
1101
1102 $sql = "SELECT COUNT(ptt.rowid) as nb, u.rowid as user_id, u.login, u.thm as user_thm";
1103 $sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."user as u";
1104 $sql .= " WHERE ptt.fk_user = u.rowid";
1105 $sql .= " AND ptt.thm IS NULL and u.thm > 0";
1106 $sql .= " GROUP BY u.rowid, u.login, u.thm";
1107
1108 $resql = $db->query($sql);
1109 if ($resql) {
1110 $num = $db->num_rows($resql);
1111
1112 if ($num) {
1113 $i = 0;
1114 while ($i < $num) {
1115 $obj = $db->fetch_object($resql);
1116 print '<tr><td>'.$obj->login.'-'.$obj->user_id.' ('.$obj->nb.' lines to fix) -> '.$obj->user_thm;
1117
1118 $db->begin();
1119
1120 if (GETPOST('set_empty_time_spent_amount') == 'confirmed') {
1121 $sql2 = "UPDATE ".MAIN_DB_PREFIX."element_time";
1122 $sql2 .= " SET thm = ".$obj->user_thm." WHERE thm IS NULL AND fk_user = ".((int) $obj->user_id);
1123 $resql2 = $db->query($sql2);
1124 if (!$resql2) {
1125 $error++;
1126 dol_print_error($db);
1127 }
1128 }
1129
1130 if (!$error) {
1131 $db->commit();
1132 } else {
1133 $db->rollback();
1134 }
1135
1136 print'</td></tr>';
1137
1138 if ($error) {
1139 break;
1140 }
1141
1142 $i++;
1143 }
1144 } else {
1145 print '<tr><td>No time spent with empty line on users with a hourly rate defined</td></tr>';
1146 }
1147 } else {
1148 dol_print_error($db);
1149 }
1150}
1151
1152
1153// force_disable_of_modules_not_found
1154if ($ok && GETPOST('force_disable_of_modules_not_found', 'alpha')) {
1155 print '<tr><td colspan="2"><br>*** Force modules not found physically to be disabled (only modules adding js, css or hooks can be detected as removed physically)</td></tr>';
1156
1157 $arraylistofkey = array('hooks', 'js', 'css');
1158
1159 foreach ($arraylistofkey as $key) {
1160 $sql = "SELECT DISTINCT name, value";
1161 $sql .= " FROM ".MAIN_DB_PREFIX."const as c";
1162 $sql .= " WHERE name LIKE 'MAIN_MODULE_%_".strtoupper($db->escape($key))."'";
1163 $sql .= " ORDER BY name";
1164
1165 $resql = $db->query($sql);
1166 if ($resql) {
1167 $num = $db->num_rows($resql);
1168 if ($num) {
1169 $i = 0;
1170 while ($i < $num) {
1171 $obj = $db->fetch_object($resql);
1172 $constantname = $obj->name; // Name of constant for hook or js or css declaration
1173
1174 print '<tr><td>';
1175 print dol_escape_htmltag($constantname);
1176
1177 $db->begin();
1178
1179 $reg = array();
1180 if (preg_match('/MAIN_MODULE_(.*)_'.strtoupper($key).'/i', $constantname, $reg)) {
1181 $name = strtolower($reg[1]);
1182
1183 if ($name) { // An entry for key $key and module $name was found in database.
1184 $reloffile = '';
1185 $result = 'found';
1186
1187 if ($key == 'hooks') {
1188 $reloffile = $name.'/class/actions_'.$name.'.class.php';
1189 }
1190 if ($key == 'js') {
1191 $value = $obj->value;
1192 $valuearray = (array) json_decode($value); // Force cast into array because sometimes it is a stdClass
1193 $reloffile = $valuearray[0];
1194 $reloffile = preg_replace('/^\//', '', $valuearray[0]);
1195 }
1196 if ($key == 'css') {
1197 $value = $obj->value;
1198 $valuearray = (array) json_decode($value); // Force cast into array because sometimes it is a stdClass
1199 if ($value && (!is_array($valuearray) || count($valuearray) == 0)) {
1200 $valuearray = array();
1201 $valuearray[0] = $value; // If value was not a json array but a string
1202 }
1203 $reloffile = preg_replace('/^\//', '', $valuearray[0]);
1204 }
1205
1206 if ($reloffile) {
1207 //var_dump($key.' - '.$value.' - '.$reloffile);
1208 try {
1209 $result = dol_buildpath($reloffile, 0, 2);
1210 } catch (Exception $e) {
1211 $result = 'found'; // If error, we force like if we found to avoid any deletion
1212 }
1213 } else {
1214 $result = 'found'; //
1215 }
1216
1217 if (!$result) {
1218 print ' - File of '.$key.' ('.$reloffile.') NOT found, we disable the module.';
1219 if (GETPOST('force_disable_of_modules_not_found') == 'confirmed') {
1220 $sql2 = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = 'MAIN_MODULE_".strtoupper($name)."_".strtoupper($key)."'";
1221 $resql2 = $db->query($sql2);
1222 if (!$resql2) {
1223 $error++;
1224 dol_print_error($db);
1225 }
1226 $sql3 = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = 'MAIN_MODULE_".strtoupper($name)."'";
1227 $resql3 = $db->query($sql3);
1228 if (!$resql3) {
1229 $error++;
1230 dol_print_error($db);
1231 } else {
1232 print ' - <span class="warning">Cleaned</span>';
1233 }
1234 } else {
1235 print ' - <span class="warning">Canceled (test mode)</span>';
1236 }
1237 } else {
1238 print ' - File of '.$key.' ('.$reloffile.') found, we do nothing.';
1239 }
1240 }
1241
1242 if (!$error) {
1243 $db->commit();
1244 } else {
1245 $db->rollback();
1246 }
1247 }
1248
1249 print'</td></tr>';
1250
1251 if ($error) {
1252 break;
1253 }
1254
1255 $i++;
1256 }
1257 } else {
1258 print '<tr><td>No active module with missing files found by searching on MAIN_MODULE_(.*)_'.strtoupper($key).'</td></tr>';
1259 }
1260 } else {
1261 dol_print_error($db);
1262 }
1263 }
1264}
1265
1266
1267// clean_old_module_entries: Clean data into const when files of module were removed without being
1268if ($ok && GETPOST('clean_perm_table', 'alpha')) {
1269 print '<tr><td colspan="2"><br>*** Clean table user_rights from lines of external modules no more enabled</td></tr>';
1270
1271 $listofmods = '';
1272 foreach ($conf->modules as $key => $val) {
1273 $listofmods .= ($listofmods ? ',' : '')."'".$db->escape($val)."'";
1274 }
1275
1276 $sql = "SELECT id, libelle as label, module from ".MAIN_DB_PREFIX."rights_def WHERE module NOT IN (".$db->sanitize($listofmods, 1).") AND id > 100000";
1277
1278 $resql = $db->query($sql);
1279 if ($resql) {
1280 $num = $db->num_rows($resql);
1281 if ($num) {
1282 $i = 0;
1283 while ($i < $num) {
1284 $obj = $db->fetch_object($resql);
1285 if ($obj->id > 0) {
1286 print '<tr><td>Found line with id '.$obj->id.', label "'.$obj->label.'" of module "'.$obj->module.'" to delete';
1287 if (GETPOST('clean_perm_table', 'alpha') == 'confirmed') {
1288 $sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."rights_def WHERE id = ".((int) $obj->id);
1289 $resqldelete = $db->query($sqldelete);
1290 if (!$resqldelete) {
1291 dol_print_error($db);
1292 }
1293 print ' - deleted';
1294 }
1295 print '</td></tr>';
1296 }
1297 $i++;
1298 }
1299 } else {
1300 print '<tr><td>No lines of a disabled external module (with id > 100000) found into table rights_def</td></tr>';
1301 }
1302 } else {
1303 dol_print_error($db);
1304 }
1305}
1306
1307
1308// clean_old_module_entries: Clean data into const when files of module were removed without being
1309if ($ok && GETPOST('clean_ecm_files_table', 'alpha')) {
1310 print '<tr><td colspan="2"><br>*** Clean table ecm_files from lines of entries whose physical files does not exists anymore (emplemented for entity 1 only)</td></tr>';
1311
1312 $MAXTODELETE = 100;
1313
1314 $sql = "SELECT rowid, filename, filepath, entity from ".MAIN_DB_PREFIX."ecm_files";
1315 $sql .= " WHERE entity = 1";
1316 $sql .= " ORDER BY rowid ASC";
1317
1318 $nbfile = 0;
1319 $nbfiletodelete = 0;
1320
1321 $resql = $db->query($sql);
1322 if ($resql) {
1323 $num = $db->num_rows($resql);
1324 if ($num) {
1325 $i = 0;
1326 while ($i < $num) {
1327 $obj = $db->fetch_object($resql);
1328 if ($obj->rowid > 0) {
1329 $filetocheck = DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename;
1330 $nbfile++;
1331 if (!dol_is_file($filetocheck) && !dol_is_file($filetocheck.'.noexe')) {
1332 $nbfiletodelete++;
1333 if ($nbfiletodelete <= $MAXTODELETE) {
1334 print '<tr><td>Found line with id '.$obj->rowid.', entity '.$obj->entity.', file "'.$filetocheck.'" to delete';
1335 if (GETPOST('clean_ecm_files_table', 'alpha') == 'confirmed') {
1336 $sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."ecm_files WHERE rowid = ".((int) $obj->rowid);
1337 $resqldelete = $db->query($sqldelete);
1338 if (!$resqldelete) {
1339 dol_print_error($db);
1340 }
1341 print ' - deleted';
1342 }
1343 print '</td></tr>';
1344 } else {
1345 break;
1346 }
1347 }
1348 }
1349 $i++;
1350 }
1351 }
1352 if ($nbfiletodelete > $MAXTODELETE) {
1353 print '<tr><td>There is more than '.$MAXTODELETE.' invalid entries into ecm_files index table (among '.$nbfile.' analyzed) with no valid physical files. Run the page several time to process all of them.</td></tr>';
1354 } else {
1355 print '<tr><td>Nb of entries processed into ecm_files index table: '.$nbfile.', number of invalid record: '.$nbfiletodelete.'</td></tr>';
1356 }
1357 } else {
1358 dol_print_error($db);
1359 }
1360}
1361
1362// force utf8 on tables
1363if ($ok && GETPOST('force_utf8_on_tables', 'alpha')) {
1364 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>';
1365
1366 if ($db->type == "mysql" || $db->type == "mysqli") {
1367 $force_utf8_on_tables = GETPOST('force_utf8_on_tables', 'alpha');
1368
1369 $listoftables = $db->DDLListTablesFull($db->database_name);
1370
1371 // Disable foreign key checking for avoid errors
1372 if ($force_utf8_on_tables == 'confirmed') {
1373 $sql = 'SET FOREIGN_KEY_CHECKS=0';
1374 print '<!-- '.$sql.' -->';
1375 print '<tr><td colspan="2">'.$sql.'</td></tr>';
1376 $resql = $db->query($sql);
1377 }
1378
1379 $foreignkeystorestore = array();
1380
1381 // First loop to delete foreign keys
1382 foreach ($listoftables as $table) {
1383 // do not convert llx_const if mysql encrypt/decrypt is used
1384 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1385 continue;
1386 }
1387 if ($table[1] == 'VIEW') {
1388 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
1389 continue;
1390 }
1391
1392 // Special case of tables with foreign key on varchar fields
1393 $arrayofforeignkey = array(
1394 'llx_accounting_account' => 'fk_accounting_account_fk_pcg_version',
1395 'llx_accounting_system' => 'fk_accounting_account_fk_pcg_version',
1396 'llx_c_type_contact' => 'fk_societe_commerciaux_fk_c_type_contact_code',
1397 'llx_societe_commerciaux' => 'fk_societe_commerciaux_fk_c_type_contact_code'
1398 );
1399
1400 foreach ($arrayofforeignkey as $tmptable => $foreignkeyname) {
1401 if ($table[0] == $tmptable) {
1402 print '<tr><td colspan="2">';
1403 $sqltmp = "ALTER TABLE ".$db->sanitize($table[0])." DROP FOREIGN KEY ".$db->sanitize($foreignkeyname);
1404 print $sqltmp;
1405 if ($force_utf8_on_tables == 'confirmed') {
1406 $resqltmp = $db->query($sqltmp);
1407 } else {
1408 print ' - <span class="opacitymedium">Disabled</span>';
1409 }
1410 print '</td></tr>';
1411 $foreignkeystorestore[$tmptable] = $foreignkeyname;
1412 }
1413 }
1414 }
1415
1416 foreach ($listoftables as $table) {
1417 // do not convert llx_const if mysql encrypt/decrypt is used
1418 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1419 continue;
1420 }
1421 if ($table[1] == 'VIEW') {
1422 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
1423 continue;
1424 }
1425
1426 $collation = 'utf8_unicode_ci';
1427 $defaultcollation = $db->getDefaultCollationDatabase();
1428 if (preg_match('/general/', $defaultcollation)) {
1429 $collation = 'utf8_general_ci';
1430 }
1431
1432 print '<tr><td colspan="2">';
1433 print $table[0];
1434 $sql1 = "ALTER TABLE ".$db->sanitize($table[0])." ROW_FORMAT=dynamic";
1435 $sql2 = "ALTER TABLE ".$db->sanitize($table[0])." CONVERT TO CHARACTER SET utf8 COLLATE ".$db->sanitize($collation);
1436 print '<!-- '.$sql1.' -->';
1437 print '<!-- '.$sql2.' -->';
1438 if ($force_utf8_on_tables == 'confirmed') {
1439 $resql1 = $db->query($sql1);
1440 if ($resql1) {
1441 $resql2 = $db->query($sql2);
1442 } else {
1443 $resql2 = false;
1444 }
1445 print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1446 } else {
1447 print ' - <span class="opacitymedium">Disabled</span>';
1448 }
1449 print '</td></tr>';
1450 flush();
1451 ob_flush();
1452 }
1453
1454 // Restore dropped foreign keys
1455 foreach ($foreignkeystorestore as $tmptable => $foreignkeyname) {
1456 $stringtofindinline = "ALTER TABLE .* ADD CONSTRAINT ".$db->sanitize($foreignkeyname);
1457 $fileforkeys = DOL_DOCUMENT_ROOT.'/install/mysql/tables/'.$tmptable.'.key.sql';
1458 //print 'Search in '.$fileforkeys.' to get '.$stringtofindinline."<br>\n";
1459
1460 $handle = fopen($fileforkeys, 'r');
1461 if ($handle) {
1462 while (($line = fgets($handle)) !== false) {
1463 // Process the line read.
1464 if (preg_match('/^'.$stringtofindinline.'/i', $line)) {
1465 $resqltmp = $db->query($line);
1466 print '<tr><td colspan="2">';
1467 print $line;
1468 print ' - Done '.($resqltmp ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1469 print '</td></tr>';
1470 break;
1471 }
1472 }
1473 fclose($handle);
1474 }
1475 flush();
1476 ob_flush();
1477 }
1478
1479 // Enable foreign key checking
1480 if ($force_utf8_on_tables == 'confirmed') {
1481 $sql = 'SET FOREIGN_KEY_CHECKS=1';
1482 print '<!-- '.$sql.' -->';
1483 print '<tr><td colspan="2">'.$sql.'</td></tr>';
1484 $resql = $db->query($sql);
1485 }
1486 } else {
1487 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1488 }
1489}
1490
1491// force utf8mb4 on tables EXPERIMENTAL !
1492if ($ok && GETPOST('force_utf8mb4_on_tables', 'alpha')) {
1493 print '<tr><td colspan="2"><br>*** Force page code and collation of tables into utf8mb4/utf8mb4_unicode_ci (for mysql/mariadb only)</td></tr>';
1494
1495 if ($db->type == "mysql" || $db->type == "mysqli") {
1496 $force_utf8mb4_on_tables = GETPOST('force_utf8mb4_on_tables', 'alpha');
1497
1498
1499 $listoftables = $db->DDLListTablesFull($db->database_name);
1500
1501 // Disable foreign key checking for avoid errors
1502 if ($force_utf8mb4_on_tables == 'confirmed') {
1503 $sql = 'SET FOREIGN_KEY_CHECKS=0';
1504 print '<!-- '.$sql.' -->';
1505 print '<tr><td colspan="2">'.$sql.'</td></tr>';
1506 $resql = $db->query($sql);
1507 }
1508
1509 $foreignkeystorestore = array();
1510
1511 // First loop to delete foreign keys
1512 foreach ($listoftables as $table) {
1513 // do not convert llx_const if mysql encrypt/decrypt is used
1514 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1515 continue;
1516 }
1517 if ($table[1] == 'VIEW') {
1518 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
1519 continue;
1520 }
1521
1522 // Special case of tables with foreign key on varchar fields
1523 $arrayofforeignkey = array(
1524 'llx_accounting_account' => 'fk_accounting_account_fk_pcg_version',
1525 'llx_accounting_system' => 'fk_accounting_account_fk_pcg_version',
1526 'llx_c_type_contact' => 'fk_societe_commerciaux_fk_c_type_contact_code',
1527 'llx_societe_commerciaux' => 'fk_societe_commerciaux_fk_c_type_contact_code'
1528 );
1529
1530 foreach ($arrayofforeignkey as $tmptable => $foreignkeyname) {
1531 if ($table[0] == $tmptable) {
1532 print '<tr><td colspan="2">';
1533 $sqltmp = "ALTER TABLE ".$db->sanitize($table[0])." DROP FOREIGN KEY ".$db->sanitize($foreignkeyname);
1534 print $sqltmp;
1535 if ($force_utf8mb4_on_tables == 'confirmed') {
1536 $resqltmp = $db->query($sqltmp);
1537 } else {
1538 print ' - <span class="opacitymedium">Disabled</span>';
1539 }
1540 print '</td></tr>';
1541 $foreignkeystorestore[$tmptable] = $foreignkeyname;
1542 }
1543 }
1544 }
1545
1546 foreach ($listoftables as $table) {
1547 // do not convert llx_const if mysql encrypt/decrypt is used
1548 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1549 continue;
1550 }
1551 if ($table[1] == 'VIEW') {
1552 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
1553 continue;
1554 }
1555
1556 $collation = 'utf8mb4_unicode_ci';
1557 $defaultcollation = $db->getDefaultCollationDatabase();
1558 if (preg_match('/general/', $defaultcollation)) {
1559 $collation = 'utf8mb4_general_ci';
1560 }
1561
1562 print '<tr><td colspan="2">';
1563 print $table[0];
1564 $sql1 = "ALTER TABLE ".$db->sanitize($table[0])." ROW_FORMAT=dynamic";
1565 $sql2 = "ALTER TABLE ".$db->sanitize($table[0])." CONVERT TO CHARACTER SET utf8mb4 COLLATE ".$db->sanitize($collation);
1566 print '<!-- '.$sql1.' -->';
1567 print '<!-- '.$sql2.' -->';
1568 if ($force_utf8mb4_on_tables == 'confirmed') {
1569 $resql1 = $db->query($sql1);
1570 if ($resql1) {
1571 $resql2 = $db->query($sql2);
1572 } else {
1573 $resql2 = false;
1574 }
1575 print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1576 } else {
1577 print ' - <span class="opacitymedium">Disabled</span>';
1578 }
1579 print '</td></tr>';
1580 flush();
1581 ob_flush();
1582 }
1583
1584 // Restore dropped foreign keys
1585 foreach ($foreignkeystorestore as $tmptable => $foreignkeyname) {
1586 $stringtofindinline = "ALTER TABLE .* ADD CONSTRAINT ".$db->sanitize($foreignkeyname);
1587 $fileforkeys = DOL_DOCUMENT_ROOT.'/install/mysql/tables/'.$tmptable.'.key.sql';
1588 //print 'Search in '.$fileforkeys.' to get '.$stringtofindinline."<br>\n";
1589
1590 $handle = fopen($fileforkeys, 'r');
1591 if ($handle) {
1592 while (($line = fgets($handle)) !== false) {
1593 // Process the line read.
1594 if (preg_match('/^'.$stringtofindinline.'/i', $line)) {
1595 $resqltmp = $db->query($line);
1596 print '<tr><td colspan="2">';
1597 print $line;
1598 print ' - Done '.($resqltmp ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1599 print '</td></tr>';
1600 break;
1601 }
1602 }
1603 fclose($handle);
1604 }
1605 flush();
1606 ob_flush();
1607 }
1608
1609 // Enable foreign key checking
1610 if ($force_utf8mb4_on_tables == 'confirmed') {
1611 $sql = 'SET FOREIGN_KEY_CHECKS=1';
1612 print '<!-- '.$sql.' -->';
1613 print '<tr><td colspan="2">'.$sql.'</td></tr>';
1614 $resql = $db->query($sql);
1615 }
1616 } else {
1617 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1618 }
1619}
1620
1621if ($ok && GETPOST('force_collation_from_conf_on_tables', 'alpha')) {
1622 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>';
1623
1624 if ($db->type == "mysql" || $db->type == "mysqli") {
1625 $force_collation_from_conf_on_tables = GETPOST('force_collation_from_conf_on_tables', 'alpha');
1626
1627 $listoftables = $db->DDLListTablesFull($db->database_name);
1628
1629 // Disable foreign key checking for avoid errors
1630 if ($force_collation_from_conf_on_tables == 'confirmed') {
1631 $sql = 'SET FOREIGN_KEY_CHECKS=0';
1632 print '<!-- '.$sql.' -->';
1633 $resql = $db->query($sql);
1634 }
1635
1636 foreach ($listoftables as $table) {
1637 // do not convert collation on llx_const if mysql encrypt/decrypt is used
1638 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1639 continue;
1640 }
1641 if ($table[1] == 'VIEW') {
1642 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' (Skipped)</td></tr>';
1643 continue;
1644 }
1645
1646 print '<tr><td colspan="2">';
1647 print $table[0];
1648 $sql1 = "ALTER TABLE ".$table[0]." ROW_FORMAT=dynamic";
1649 $sql2 = "ALTER TABLE ".$table[0]." CONVERT TO CHARACTER SET ".$conf->db->character_set." COLLATE ".$conf->db->dolibarr_main_db_collation;
1650 print '<!-- '.$sql1.' -->';
1651 print '<!-- '.$sql2.' -->';
1652 if ($force_collation_from_conf_on_tables == 'confirmed') {
1653 $resql1 = $db->query($sql1);
1654 if ($resql1) {
1655 $resql2 = $db->query($sql2);
1656 } else {
1657 $resql2 = false;
1658 }
1659 print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1660 } else {
1661 print ' - <span class="opacitymedium">Disabled</span>';
1662 }
1663 print '</td></tr>';
1664 }
1665
1666 // Enable foreign key checking
1667 if ($force_collation_from_conf_on_tables == 'confirmed') {
1668 $sql = 'SET FOREIGN_KEY_CHECKS=1';
1669 print '<!-- '.$sql.' -->';
1670 $resql = $db->query($sql);
1671 }
1672 } else {
1673 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1674 }
1675}
1676
1677// rebuild sequences for pgsql
1678if ($ok && GETPOST('rebuild_sequences', 'alpha')) {
1679 print '<tr><td colspan="2"><br>*** Force to rebuild sequences (for postgresql only)</td></tr>';
1680
1681 if ($db->type == "pgsql") {
1682 $rebuild_sequence = GETPOST('rebuild_sequences', 'alpha');
1683
1684 if ($rebuild_sequence == 'confirmed') {
1685 $sql = "SELECT dol_util_rebuild_sequences();";
1686 print '<!-- '.$sql.' -->';
1687 $resql = $db->query($sql);
1688 }
1689 } else {
1690 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1691 }
1692}
1693
1694//
1695if ($ok && GETPOST('repair_link_dispatch_lines_supplier_order_lines')) {
1696 /*
1697 * This script is meant to be run when upgrading from a dolibarr version < 3.8
1698 * to a newer version.
1699 *
1700 * Version 3.8 introduces a new column in llx_commande_fournisseur_dispatch, which
1701 * matches the dispatch to a specific supplier order line (so that if there are
1702 * several with the same product, the user can specifically tell which products of
1703 * which line were dispatched where).
1704 *
1705 * However when migrating, the new column has a default value of 0, which means that
1706 * old supplier orders whose lines were dispatched using the old dolibarr version
1707 * have unspecific dispatch lines, which are not taken into account by the new version,
1708 * thus making the order look like it was never dispatched at all.
1709 *
1710 * This scripts sets this foreign key to the first matching supplier order line whose
1711 * product (and supplier order of course) are the same as the dispatch’s.
1712 *
1713 * If the dispatched quantity is more than indicated on the order line (this happens if
1714 * there are several order lines for the same product), it creates new dispatch lines
1715 * pointing to the other order lines accordingly, until all the dispatched quantity is
1716 * accounted for.
1717 */
1718
1719 $repair_link_dispatch_lines_supplier_order_lines = GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha');
1720
1721
1722 echo '<tr><th>Repair llx_receptiondet_batch.fk_commandefourndet</th></tr>';
1723 echo '<tr><td>Repair in progress. This may take a while.</td></tr>';
1724
1725 $sql_dispatch = 'SELECT * FROM '.MAIN_DB_PREFIX.'receptiondet_batch WHERE COALESCE(fk_elementdet, 0) = 0';
1726 $db->begin();
1727 $resql_dispatch = $db->query($sql_dispatch);
1728 $n_processed_rows = 0;
1729 $errors = array();
1730 if ($resql_dispatch) {
1731 if ($db->num_rows($resql_dispatch) == 0) {
1732 echo '<tr><td>Nothing to do.</td></tr>';
1733 exit;
1734 }
1735 while ($obj_dispatch = $db->fetch_object($resql_dispatch)) {
1736 $sql_line = 'SELECT line.rowid, line.qty FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet AS line';
1737 $sql_line .= ' WHERE line.fk_commande = '.((int) $obj_dispatch->fk_commande);
1738 $sql_line .= ' AND line.fk_product = '.((int) $obj_dispatch->fk_product);
1739 $resql_line = $db->query($sql_line);
1740
1741 // s’il y a plusieurs lignes avec le même produit sur cette commande fournisseur,
1742 // on divise la ligne de dispatch en autant de lignes qu’on en a sur la commande pour le produit
1743 // et on met la quantité de la ligne dans la limit du "budget" indiqué par dispatch.qty
1744
1745 $remaining_qty = $obj_dispatch->qty;
1746 $first_iteration = true;
1747 if (!$resql_line) {
1748 echo '<tr><td>Unable to find a matching supplier order line for dispatch #'.$obj_dispatch->rowid.'</td></tr>';
1749 $errors[] = $sql_line;
1750 $n_processed_rows++;
1751 continue;
1752 }
1753 if ($db->num_rows($resql_line) == 0) {
1754 continue;
1755 }
1756 while ($obj_line = $db->fetch_object($resql_line)) {
1757 if (!$remaining_qty) {
1758 break;
1759 }
1760 if (!$obj_line->rowid) {
1761 continue;
1762 }
1763 $qty_for_line = min($remaining_qty, $obj_line->qty);
1764 if ($first_iteration) {
1765 $sql_attach = 'UPDATE '.MAIN_DB_PREFIX.'receptiondet_batch';
1766 $sql_attach .= ' SET fk_elementdet = '.((int) $obj_line->rowid).', qty = '.((float) $qty_for_line);
1767 $sql_attach .= ' WHERE rowid = '.((int) $obj_dispatch->rowid);
1768 $first_iteration = false;
1769 } else {
1770 $sql_attach_values = array(
1771 (string) ((int) $obj_dispatch->fk_element),
1772 (string) ((int) $obj_dispatch->fk_product),
1773 (string) ((int) $obj_line->rowid),
1774 (string) ((float) $qty_for_line),
1775 (string) ((int) $obj_dispatch->fk_entrepot),
1776 (string) ((int) $obj_dispatch->fk_user),
1777 $obj_dispatch->datec ? "'".$db->idate($db->jdate($obj_dispatch->datec))."'" : 'NULL',
1778 $obj_dispatch->comment ? "'".$db->escape($obj_dispatch->comment)."'" : 'NULL',
1779 $obj_dispatch->status ? (string) ((int) $obj_dispatch->status) : 'NULL',
1780 $obj_dispatch->tms ? "'".$db->idate($db->jdate($obj_dispatch->tms))."'" : 'NULL',
1781 $obj_dispatch->batch ? "'".$db->escape($obj_dispatch->batch)."'" : 'NULL',
1782 $obj_dispatch->eatby ? "'".$db->escape($obj_dispatch->eatby)."'" : 'NULL',
1783 $obj_dispatch->sellby ? "'".$db->escape($obj_dispatch->sellby)."'" : 'NULL'
1784 );
1785 $sql_attach_values = implode(', ', $sql_attach_values);
1786
1787 $sql_attach = 'INSERT INTO '.MAIN_DB_PREFIX.'receptiondet_batch';
1788 $sql_attach .= ' (fk_element, fk_product, fk_elementdet, qty, fk_entrepot, fk_user, datec, comment, status, tms, batch, eatby, sellby)';
1789 $sql_attach .= " VALUES (".$sql_attach_values.")"; // The string is already sanitized
1790 }
1791
1792 if ($repair_link_dispatch_lines_supplier_order_lines == 'confirmed') {
1793 $resql_attach = $db->query($sql_attach);
1794 } else {
1795 $resql_attach = true; // Force success in test mode
1796 }
1797
1798 if ($resql_attach) {
1799 $remaining_qty -= $qty_for_line;
1800 } else {
1801 $errors[] = $sql_attach;
1802 }
1803
1804 $first_iteration = false;
1805 }
1806 $n_processed_rows++;
1807
1808 // report progress every 256th row
1809 if (!($n_processed_rows & 0xff)) {
1810 echo '<tr><td>Processed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
1811 flush();
1812 ob_flush();
1813 }
1814 }
1815 } else {
1816 echo '<tr><td>Unable to find any dispatch without an fk_commandefourndet.'."</td></tr>\n";
1817 echo $sql_dispatch."\n";
1818 }
1819 echo '<tr><td>Fixed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
1820 echo '<tr><td>DONE.'."</td></tr>\n";
1821
1822 if (count($errors)) {
1823 $db->rollback();
1824 echo '<tr><td>The transaction was rolled back due to errors: nothing was changed by the script.</td></tr>';
1825 } else {
1826 $db->commit();
1827 }
1828 $db->close();
1829
1830 echo '<tr><td><h3>SQL queries with errors:</h3></tr></td>';
1831 echo '<tr><td>'.implode('</td></tr><tr><td>', $errors).'</td></tr>';
1832}
1833
1834// Repair llx_commande_fournisseur to eliminate duplicate reference
1835if ($ok && GETPOST('repair_supplier_order_duplicate_ref')) {
1836 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.commande.class.php';
1837 include_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1838
1839 $db->begin();
1840
1841 $err = 0;
1842
1843 // Query to find all duplicate supplier orders
1844 $sql = "SELECT * FROM " . MAIN_DB_PREFIX . "commande_fournisseur";
1845 $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)";
1846
1847 // Build a list of ref => []CommandeFournisseur
1848 $duplicateSupplierOrders = [];
1849 $resql = $db->query($sql);
1850 if ($resql) {
1851 while ($rawSupplierOrder = $db->fetch_object($resql)) {
1852 $supplierOrder = new CommandeFournisseur($db);
1853 $supplierOrder->setVarsFromFetchObj($rawSupplierOrder);
1854
1855 $duplicateSupplierOrders[$rawSupplierOrder->ref] [] = $supplierOrder;
1856 }
1857 } else {
1858 $err++;
1859 }
1860
1861 // Process all duplicate supplier order and regenerate the reference for all except the first one
1862 foreach ($duplicateSupplierOrders as $ref => $supplierOrders) {
1864 foreach (array_slice($supplierOrders, 1) as $supplierOrder) {
1865 // Definition of supplier order numbering model name
1866 $soc = new Societe($db);
1867 $soc->fetch($supplierOrder->fourn_id);
1868
1869 $newRef = $supplierOrder->getNextNumRef($soc);
1870
1871 $sql = "UPDATE " . MAIN_DB_PREFIX . "commande_fournisseur cf SET cf.ref = '" . $db->escape($newRef) . "' WHERE cf.rowid = " . (int) $supplierOrder->id;
1872 if (!$db->query($sql)) {
1873 $err++;
1874 }
1875 }
1876 }
1877
1878 if ($err == 0) {
1879 $db->commit();
1880 } else {
1881 $db->rollback();
1882 }
1883}
1884
1885// Repair llx_invoice to calculate totals from line items
1886// WARNING : The process can be long on production environments due to restrictions.
1887// consider raising php_max_execution time if failing to execute completely.
1888if ($ok && GETPOST('recalculateinvoicetotal') == 'confirmed') {
1889 $err = 0;
1890 $db->begin();
1891 $sql = "SELECT f.rowid, SUM(fd.total_ht) as total_ht";
1892 $sql .= " FROM ".MAIN_DB_PREFIX."facture f";
1893 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facturedet fd ON fd.fk_facture = f.rowid";
1894 $sql .= " WHERE f.total_ht = 0";
1895 $sql .= " GROUP BY fd.fk_facture HAVING SUM(fd.total_ht) <> 0";
1896
1897 $resql = $db->query($sql);
1898 if ($resql) {
1899 $num = $db->num_rows($resql);
1900 print "We found ".$num." factures qualified that will have their total recalculated because they are at zero and line items not at zero\n";
1901 dol_syslog("We found ".$num." factures qualified that will have their total recalculated because they are at zero and line items not at zero");
1902
1903 if ($num) {
1904 $i = 0;
1905 while ($i < $num) {
1906 $obj = $db->fetch_object($resql);
1907 $sql_calculs = "
1908 SELECT
1909 SUM(fd.total_ht) as 'total_ht',
1910 SUM(fd.total_tva) as 'total_tva',
1911 SUM(fd.total_localtax1) as 'localtax1',
1912 SUM(fd.total_localtax2) as 'localtax2',
1913 SUM(fd.total_ttc) as 'total_ttc'
1914 FROM
1915 ".MAIN_DB_PREFIX."facturedet fd
1916 WHERE
1917 fd.fk_facture = $obj->rowid";
1918 $ressql_calculs = $db->query($sql_calculs);
1919 while ($obj_calcul = $db->fetch_object($ressql_calculs)) {
1920 $sql_maj = "
1921 UPDATE ".MAIN_DB_PREFIX."facture
1922 SET
1923 total_ht = ".($obj_calcul->total_ht ? price2num($obj_calcul->total_ht, 'MT') : 0).",
1924 total_tva = ".($obj_calcul->total_tva ? price2num($obj_calcul->total_tva, 'MT') : 0).",
1925 localtax1 = ".($obj_calcul->localtax1 ? price2num($obj_calcul->localtax1, 'MT') : 0).",
1926 localtax2 = ".($obj_calcul->localtax2 ? price2num($obj_calcul->localtax2, 'MT') : 0).",
1927 total_ttc = ".($obj_calcul->total_ttc ? price2num($obj_calcul->total_ttc, 'MT') : 0)."
1928 WHERE
1929 rowid = $obj->rowid";
1930 $db->query($sql_maj);
1931 }
1932 $i++;
1933 }
1934 } else {
1935 print "Pas de factures à traiter\n";
1936 }
1937 } else {
1938 dol_print_error($db);
1939 dol_syslog("calculate_total_and_taxes.php: Error");
1940 $err++;
1941 }
1942
1943 if ($err == 0) {
1944 $db->commit();
1945 } else {
1946 $db->rollback();
1947 }
1948}
1949
1950print '</table>';
1951
1952if (empty($actiondone)) {
1953 print '<div class="error">'.$langs->trans("ErrorWrongParameters").'</div>';
1954}
1955
1956if ($oneoptionset) {
1957 print '<div class="center" style="padding-top: 10px"><a href="../index.php?mainmenu=home&leftmenu=home'.(GETPOSTISSET("login") ? '&username='.urlencode(GETPOST("login")) : '').'">';
1958 print $langs->trans("GoToDolibarr");
1959 print '</a></div>';
1960}
1961
1962dolibarr_install_syslog("--- repair: end");
1963pFooter(1, $setuplang);
1964
1965if ($db->connected) {
1966 $db->close();
1967}
1968
1969// Return code if ran from command line
1970if (!$ok && isset($argv[1])) {
1971 exit(1);
1972}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
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.
Class for managing the social charges.
Class to manage predefined suppliers products.
Class to manage customers orders.
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...)
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
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.
dol_delete_dir($dir, $nophperrors=0)
Remove a directory (not recursive, so content must be empty).
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_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:63
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
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).
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
pHeader($subtitle, $next, $action='set', $param='', $forcejqueryurl='', $csstable='main-inside')
Show HTML header of install pages.
Definition inc.php:538
pFooter($nonext=0, $setuplang='', $jscheckfunction='', $withpleasewait=0, $morehtml='')
Print HTML footer of install pages.
Definition inc.php:631
dolibarr_install_syslog($message, $level=LOG_DEBUG)
Log function for install pages.
Definition inc.php:695
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
clean_data_ecm_directories()
Clean data into ecm_directories table.
dol_decode($chain, $key='1')
Decode a base 64 encoded + specific delta change.
dolDecrypt($chain, $key='')
Decode a string with a symmetric encryption.