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 = empty($reg[1]) ? '' : $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 = empty($reg[1]) ? '' : $reg[1];
926 }
927 if ($modulepart == 'order') {
928 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
929 $ref = empty($reg[1]) ? '' : $reg[1];
930 }
931 if ($modulepart == 'order_supplier') {
932 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
933 $ref = empty($reg[1]) ? '' : $reg[1];
934 }
935 if ($modulepart == 'contract') {
936 preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
937 $ref = empty($reg[1]) ? '' : $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 if (ob_get_level() > 0) {
1452 ob_flush();
1453 }
1454 }
1455
1456 // Restore dropped foreign keys
1457 foreach ($foreignkeystorestore as $tmptable => $foreignkeyname) {
1458 $stringtofindinline = "ALTER TABLE .* ADD CONSTRAINT ".$db->sanitize($foreignkeyname);
1459 $fileforkeys = DOL_DOCUMENT_ROOT.'/install/mysql/tables/'.$tmptable.'.key.sql';
1460 //print 'Search in '.$fileforkeys.' to get '.$stringtofindinline."<br>\n";
1461
1462 $handle = fopen($fileforkeys, 'r');
1463 if ($handle) {
1464 while (($line = fgets($handle)) !== false) {
1465 // Process the line read.
1466 if (preg_match('/^'.$stringtofindinline.'/i', $line)) {
1467 $resqltmp = $db->query($line);
1468 print '<tr><td colspan="2">';
1469 print $line;
1470 print ' - Done '.($resqltmp ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1471 print '</td></tr>';
1472 break;
1473 }
1474 }
1475 fclose($handle);
1476 }
1477 flush();
1478 if (ob_get_level() > 0) {
1479 ob_flush();
1480 }
1481 }
1482
1483 // Enable foreign key checking
1484 if ($force_utf8_on_tables == 'confirmed') {
1485 $sql = 'SET FOREIGN_KEY_CHECKS=1';
1486 print '<!-- '.$sql.' -->';
1487 print '<tr><td colspan="2">'.$sql.'</td></tr>';
1488 $resql = $db->query($sql);
1489 }
1490 } else {
1491 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1492 }
1493}
1494
1495// force utf8mb4 on tables EXPERIMENTAL !
1496if ($ok && GETPOST('force_utf8mb4_on_tables', 'alpha')) {
1497 print '<tr><td colspan="2"><br>*** Force page code and collation of tables into utf8mb4/utf8mb4_unicode_ci (for mysql/mariadb only)</td></tr>';
1498
1499 if ($db->type == "mysql" || $db->type == "mysqli") {
1500 $force_utf8mb4_on_tables = GETPOST('force_utf8mb4_on_tables', 'alpha');
1501
1502
1503 $listoftables = $db->DDLListTablesFull($db->database_name);
1504
1505 // Disable foreign key checking for avoid errors
1506 if ($force_utf8mb4_on_tables == 'confirmed') {
1507 $sql = 'SET FOREIGN_KEY_CHECKS=0';
1508 print '<!-- '.$sql.' -->';
1509 print '<tr><td colspan="2">'.$sql.'</td></tr>';
1510 $resql = $db->query($sql);
1511 }
1512
1513 $foreignkeystorestore = array();
1514
1515 // First loop to delete foreign keys
1516 foreach ($listoftables as $table) {
1517 // do not convert llx_const if mysql encrypt/decrypt is used
1518 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1519 continue;
1520 }
1521 if ($table[1] == 'VIEW') {
1522 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
1523 continue;
1524 }
1525
1526 // Special case of tables with foreign key on varchar fields
1527 $arrayofforeignkey = array(
1528 'llx_accounting_account' => 'fk_accounting_account_fk_pcg_version',
1529 'llx_accounting_system' => 'fk_accounting_account_fk_pcg_version',
1530 'llx_c_type_contact' => 'fk_societe_commerciaux_fk_c_type_contact_code',
1531 'llx_societe_commerciaux' => 'fk_societe_commerciaux_fk_c_type_contact_code'
1532 );
1533
1534 foreach ($arrayofforeignkey as $tmptable => $foreignkeyname) {
1535 if ($table[0] == $tmptable) {
1536 print '<tr><td colspan="2">';
1537 $sqltmp = "ALTER TABLE ".$db->sanitize($table[0])." DROP FOREIGN KEY ".$db->sanitize($foreignkeyname);
1538 print $sqltmp;
1539 if ($force_utf8mb4_on_tables == 'confirmed') {
1540 $resqltmp = $db->query($sqltmp);
1541 } else {
1542 print ' - <span class="opacitymedium">Disabled</span>';
1543 }
1544 print '</td></tr>';
1545 $foreignkeystorestore[$tmptable] = $foreignkeyname;
1546 }
1547 }
1548 }
1549
1550 foreach ($listoftables as $table) {
1551 // do not convert llx_const if mysql encrypt/decrypt is used
1552 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1553 continue;
1554 }
1555 if ($table[1] == 'VIEW') {
1556 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
1557 continue;
1558 }
1559
1560 $collation = 'utf8mb4_unicode_ci';
1561 $defaultcollation = $db->getDefaultCollationDatabase();
1562 if (preg_match('/general/', $defaultcollation)) {
1563 $collation = 'utf8mb4_general_ci';
1564 }
1565
1566 print '<tr><td colspan="2">';
1567 print $table[0];
1568 $sql1 = "ALTER TABLE ".$db->sanitize($table[0])." ROW_FORMAT=dynamic";
1569 $sql2 = "ALTER TABLE ".$db->sanitize($table[0])." CONVERT TO CHARACTER SET utf8mb4 COLLATE ".$db->sanitize($collation);
1570 print '<!-- '.$sql1.' -->';
1571 print '<!-- '.$sql2.' -->';
1572 if ($force_utf8mb4_on_tables == 'confirmed') {
1573 $resql1 = $db->query($sql1);
1574 if ($resql1) {
1575 $resql2 = $db->query($sql2);
1576 } else {
1577 $resql2 = false;
1578 }
1579 print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1580 } else {
1581 print ' - <span class="opacitymedium">Disabled</span>';
1582 }
1583 print '</td></tr>';
1584 flush();
1585 if (ob_get_level() > 0) {
1586 ob_flush();
1587 }
1588 }
1589
1590 // Restore dropped foreign keys
1591 foreach ($foreignkeystorestore as $tmptable => $foreignkeyname) {
1592 $stringtofindinline = "ALTER TABLE .* ADD CONSTRAINT ".$db->sanitize($foreignkeyname);
1593 $fileforkeys = DOL_DOCUMENT_ROOT.'/install/mysql/tables/'.$tmptable.'.key.sql';
1594 //print 'Search in '.$fileforkeys.' to get '.$stringtofindinline."<br>\n";
1595
1596 $handle = fopen($fileforkeys, 'r');
1597 if ($handle) {
1598 while (($line = fgets($handle)) !== false) {
1599 // Process the line read.
1600 if (preg_match('/^'.$stringtofindinline.'/i', $line)) {
1601 $resqltmp = $db->query($line);
1602 print '<tr><td colspan="2">';
1603 print $line;
1604 print ' - Done '.($resqltmp ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1605 print '</td></tr>';
1606 break;
1607 }
1608 }
1609 fclose($handle);
1610 }
1611 flush();
1612 if (ob_get_level() > 0) {
1613 ob_flush();
1614 }
1615 }
1616
1617 // Enable foreign key checking
1618 if ($force_utf8mb4_on_tables == 'confirmed') {
1619 $sql = 'SET FOREIGN_KEY_CHECKS=1';
1620 print '<!-- '.$sql.' -->';
1621 print '<tr><td colspan="2">'.$sql.'</td></tr>';
1622 $resql = $db->query($sql);
1623 }
1624 } else {
1625 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1626 }
1627}
1628
1629if ($ok && GETPOST('force_collation_from_conf_on_tables', 'alpha')) {
1630 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>';
1631
1632 if ($db->type == "mysql" || $db->type == "mysqli") {
1633 $force_collation_from_conf_on_tables = GETPOST('force_collation_from_conf_on_tables', 'alpha');
1634
1635 $listoftables = $db->DDLListTablesFull($db->database_name);
1636
1637 // Disable foreign key checking for avoid errors
1638 if ($force_collation_from_conf_on_tables == 'confirmed') {
1639 $sql = 'SET FOREIGN_KEY_CHECKS=0';
1640 print '<!-- '.$sql.' -->';
1641 $resql = $db->query($sql);
1642 }
1643
1644 foreach ($listoftables as $table) {
1645 // do not convert collation on llx_const if mysql encrypt/decrypt is used
1646 if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1647 continue;
1648 }
1649 if ($table[1] == 'VIEW') {
1650 print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' (Skipped)</td></tr>';
1651 continue;
1652 }
1653
1654 print '<tr><td colspan="2">';
1655 print $table[0];
1656 $sql1 = "ALTER TABLE ".$table[0]." ROW_FORMAT=dynamic";
1657 $sql2 = "ALTER TABLE ".$table[0]." CONVERT TO CHARACTER SET ".$conf->db->character_set." COLLATE ".$conf->db->dolibarr_main_db_collation;
1658 print '<!-- '.$sql1.' -->';
1659 print '<!-- '.$sql2.' -->';
1660 if ($force_collation_from_conf_on_tables == 'confirmed') {
1661 $resql1 = $db->query($sql1);
1662 if ($resql1) {
1663 $resql2 = $db->query($sql2);
1664 } else {
1665 $resql2 = false;
1666 }
1667 print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
1668 } else {
1669 print ' - <span class="opacitymedium">Disabled</span>';
1670 }
1671 print '</td></tr>';
1672 }
1673
1674 // Enable foreign key checking
1675 if ($force_collation_from_conf_on_tables == 'confirmed') {
1676 $sql = 'SET FOREIGN_KEY_CHECKS=1';
1677 print '<!-- '.$sql.' -->';
1678 $resql = $db->query($sql);
1679 }
1680 } else {
1681 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1682 }
1683}
1684
1685// rebuild sequences for pgsql
1686if ($ok && GETPOST('rebuild_sequences', 'alpha')) {
1687 print '<tr><td colspan="2"><br>*** Force to rebuild sequences (for postgresql only)</td></tr>';
1688
1689 if ($db->type == "pgsql") {
1690 $rebuild_sequence = GETPOST('rebuild_sequences', 'alpha');
1691
1692 if ($rebuild_sequence == 'confirmed') {
1693 $sql = "SELECT dol_util_rebuild_sequences();";
1694 print '<!-- '.$sql.' -->';
1695 $resql = $db->query($sql);
1696 }
1697 } else {
1698 print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
1699 }
1700}
1701
1702//
1703if ($ok && GETPOST('repair_link_dispatch_lines_supplier_order_lines')) {
1704 /*
1705 * This script is meant to be run when upgrading from a dolibarr version < 3.8
1706 * to a newer version.
1707 *
1708 * Version 3.8 introduces a new column in llx_commande_fournisseur_dispatch, which
1709 * matches the dispatch to a specific supplier order line (so that if there are
1710 * several with the same product, the user can specifically tell which products of
1711 * which line were dispatched where).
1712 *
1713 * However when migrating, the new column has a default value of 0, which means that
1714 * old supplier orders whose lines were dispatched using the old dolibarr version
1715 * have unspecific dispatch lines, which are not taken into account by the new version,
1716 * thus making the order look like it was never dispatched at all.
1717 *
1718 * This scripts sets this foreign key to the first matching supplier order line whose
1719 * product (and supplier order of course) are the same as the dispatch’s.
1720 *
1721 * If the dispatched quantity is more than indicated on the order line (this happens if
1722 * there are several order lines for the same product), it creates new dispatch lines
1723 * pointing to the other order lines accordingly, until all the dispatched quantity is
1724 * accounted for.
1725 */
1726
1727 $repair_link_dispatch_lines_supplier_order_lines = GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha');
1728
1729
1730 echo '<tr><th>Repair llx_receptiondet_batch.fk_commandefourndet</th></tr>';
1731 echo '<tr><td>Repair in progress. This may take a while.</td></tr>';
1732
1733 $sql_dispatch = 'SELECT * FROM '.MAIN_DB_PREFIX.'receptiondet_batch WHERE COALESCE(fk_elementdet, 0) = 0';
1734 $db->begin();
1735 $resql_dispatch = $db->query($sql_dispatch);
1736 $n_processed_rows = 0;
1737 $errors = array();
1738 if ($resql_dispatch) {
1739 if ($db->num_rows($resql_dispatch) == 0) {
1740 echo '<tr><td>Nothing to do.</td></tr>';
1741 exit;
1742 }
1743 while ($obj_dispatch = $db->fetch_object($resql_dispatch)) {
1744 $sql_line = 'SELECT line.rowid, line.qty FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet AS line';
1745 $sql_line .= ' WHERE line.fk_commande = '.((int) $obj_dispatch->fk_commande);
1746 $sql_line .= ' AND line.fk_product = '.((int) $obj_dispatch->fk_product);
1747 $resql_line = $db->query($sql_line);
1748
1749 // s’il y a plusieurs lignes avec le même produit sur cette commande fournisseur,
1750 // on divise la ligne de dispatch en autant de lignes qu’on en a sur la commande pour le produit
1751 // et on met la quantité de la ligne dans la limit du "budget" indiqué par dispatch.qty
1752
1753 $remaining_qty = $obj_dispatch->qty;
1754 $first_iteration = true;
1755 if (!$resql_line) {
1756 echo '<tr><td>Unable to find a matching supplier order line for dispatch #'.$obj_dispatch->rowid.'</td></tr>';
1757 $errors[] = $sql_line;
1758 $n_processed_rows++;
1759 continue;
1760 }
1761 if ($db->num_rows($resql_line) == 0) {
1762 continue;
1763 }
1764 while ($obj_line = $db->fetch_object($resql_line)) {
1765 if (!$remaining_qty) {
1766 break;
1767 }
1768 if (!$obj_line->rowid) {
1769 continue;
1770 }
1771 $qty_for_line = min($remaining_qty, $obj_line->qty);
1772 if ($first_iteration) {
1773 $sql_attach = 'UPDATE '.MAIN_DB_PREFIX.'receptiondet_batch';
1774 $sql_attach .= ' SET fk_elementdet = '.((int) $obj_line->rowid).', qty = '.((float) $qty_for_line);
1775 $sql_attach .= ' WHERE rowid = '.((int) $obj_dispatch->rowid);
1776 $first_iteration = false;
1777 } else {
1778 $sql_attach_values = array(
1779 (string) ((int) $obj_dispatch->fk_element),
1780 (string) ((int) $obj_dispatch->fk_product),
1781 (string) ((int) $obj_line->rowid),
1782 (string) ((float) $qty_for_line),
1783 (string) ((int) $obj_dispatch->fk_entrepot),
1784 (string) ((int) $obj_dispatch->fk_user),
1785 $obj_dispatch->datec ? "'".$db->idate($db->jdate($obj_dispatch->datec))."'" : 'NULL',
1786 $obj_dispatch->comment ? "'".$db->escape($obj_dispatch->comment)."'" : 'NULL',
1787 $obj_dispatch->status ? (string) ((int) $obj_dispatch->status) : 'NULL',
1788 $obj_dispatch->tms ? "'".$db->idate($db->jdate($obj_dispatch->tms))."'" : 'NULL',
1789 $obj_dispatch->batch ? "'".$db->escape($obj_dispatch->batch)."'" : 'NULL',
1790 $obj_dispatch->eatby ? "'".$db->escape($obj_dispatch->eatby)."'" : 'NULL',
1791 $obj_dispatch->sellby ? "'".$db->escape($obj_dispatch->sellby)."'" : 'NULL'
1792 );
1793 $sql_attach_values = implode(', ', $sql_attach_values);
1794
1795 $sql_attach = 'INSERT INTO '.MAIN_DB_PREFIX.'receptiondet_batch';
1796 $sql_attach .= ' (fk_element, fk_product, fk_elementdet, qty, fk_entrepot, fk_user, datec, comment, status, tms, batch, eatby, sellby)';
1797 $sql_attach .= " VALUES (".$sql_attach_values.")"; // The string is already sanitized
1798 }
1799
1800 if ($repair_link_dispatch_lines_supplier_order_lines == 'confirmed') {
1801 $resql_attach = $db->query($sql_attach);
1802 } else {
1803 $resql_attach = true; // Force success in test mode
1804 }
1805
1806 if ($resql_attach) {
1807 $remaining_qty -= $qty_for_line;
1808 } else {
1809 $errors[] = $sql_attach;
1810 }
1811
1812 $first_iteration = false;
1813 }
1814 $n_processed_rows++;
1815
1816 // report progress every 256th row
1817 if (!($n_processed_rows & 0xff)) {
1818 echo '<tr><td>Processed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
1819 flush();
1820 if (ob_get_level() > 0) {
1821 ob_flush();
1822 }
1823 }
1824 }
1825 } else {
1826 echo '<tr><td>Unable to find any dispatch without an fk_commandefourndet.'."</td></tr>\n";
1827 echo $sql_dispatch."\n";
1828 }
1829 echo '<tr><td>Fixed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
1830 echo '<tr><td>DONE.'."</td></tr>\n";
1831
1832 if (count($errors)) {
1833 $db->rollback();
1834 echo '<tr><td>The transaction was rolled back due to errors: nothing was changed by the script.</td></tr>';
1835 } else {
1836 $db->commit();
1837 }
1838 $db->close();
1839
1840 echo '<tr><td><h3>SQL queries with errors:</h3></tr></td>';
1841 echo '<tr><td>'.implode('</td></tr><tr><td>', $errors).'</td></tr>';
1842}
1843
1844// Repair llx_commande_fournisseur to eliminate duplicate reference
1845if ($ok && GETPOST('repair_supplier_order_duplicate_ref')) {
1846 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.commande.class.php';
1847 include_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1848
1849 $db->begin();
1850
1851 $err = 0;
1852
1853 // Query to find all duplicate supplier orders
1854 $sql = "SELECT * FROM " . MAIN_DB_PREFIX . "commande_fournisseur";
1855 $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)";
1856
1857 // Build a list of ref => []CommandeFournisseur
1858 $duplicateSupplierOrders = [];
1859 $resql = $db->query($sql);
1860 if ($resql) {
1861 while ($rawSupplierOrder = $db->fetch_object($resql)) {
1862 $supplierOrder = new CommandeFournisseur($db);
1863 $supplierOrder->setVarsFromFetchObj($rawSupplierOrder);
1864
1865 $duplicateSupplierOrders[$rawSupplierOrder->ref] [] = $supplierOrder;
1866 }
1867 } else {
1868 $err++;
1869 }
1870
1871 // Process all duplicate supplier order and regenerate the reference for all except the first one
1872 foreach ($duplicateSupplierOrders as $ref => $supplierOrders) {
1874 foreach (array_slice($supplierOrders, 1) as $supplierOrder) {
1875 // Definition of supplier order numbering model name
1876 $soc = new Societe($db);
1877 $soc->fetch($supplierOrder->fourn_id);
1878
1879 $newRef = $supplierOrder->getNextNumRef($soc);
1880
1881 $sql = "UPDATE " . MAIN_DB_PREFIX . "commande_fournisseur cf SET cf.ref = '" . $db->escape($newRef) . "' WHERE cf.rowid = " . (int) $supplierOrder->id;
1882 if (!$db->query($sql)) {
1883 $err++;
1884 }
1885 }
1886 }
1887
1888 if ($err == 0) {
1889 $db->commit();
1890 } else {
1891 $db->rollback();
1892 }
1893}
1894
1895// Repair llx_invoice to calculate totals from line items
1896// WARNING : The process can be long on production environments due to restrictions.
1897// consider raising php_max_execution time if failing to execute completely.
1898if ($ok && GETPOST('recalculateinvoicetotal') == 'confirmed') {
1899 $err = 0;
1900 $db->begin();
1901 $sql = "SELECT f.rowid, SUM(fd.total_ht) as total_ht";
1902 $sql .= " FROM ".MAIN_DB_PREFIX."facture f";
1903 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facturedet fd ON fd.fk_facture = f.rowid";
1904 $sql .= " WHERE f.total_ht = 0";
1905 $sql .= " GROUP BY fd.fk_facture HAVING SUM(fd.total_ht) <> 0";
1906
1907 $resql = $db->query($sql);
1908 if ($resql) {
1909 $num = $db->num_rows($resql);
1910 print "We found ".$num." factures qualified that will have their total recalculated because they are at zero and line items not at zero\n";
1911 dol_syslog("We found ".$num." factures qualified that will have their total recalculated because they are at zero and line items not at zero");
1912
1913 if ($num) {
1914 $i = 0;
1915 while ($i < $num) {
1916 $obj = $db->fetch_object($resql);
1917 $sql_calculs = "
1918 SELECT
1919 SUM(fd.total_ht) as 'total_ht',
1920 SUM(fd.total_tva) as 'total_tva',
1921 SUM(fd.total_localtax1) as 'localtax1',
1922 SUM(fd.total_localtax2) as 'localtax2',
1923 SUM(fd.total_ttc) as 'total_ttc'
1924 FROM
1925 ".MAIN_DB_PREFIX."facturedet fd
1926 WHERE
1927 fd.fk_facture = $obj->rowid";
1928 $ressql_calculs = $db->query($sql_calculs);
1929 while ($obj_calcul = $db->fetch_object($ressql_calculs)) {
1930 $sql_maj = "
1931 UPDATE ".MAIN_DB_PREFIX."facture
1932 SET
1933 total_ht = ".($obj_calcul->total_ht ? price2num($obj_calcul->total_ht, 'MT') : 0).",
1934 total_tva = ".($obj_calcul->total_tva ? price2num($obj_calcul->total_tva, 'MT') : 0).",
1935 localtax1 = ".($obj_calcul->localtax1 ? price2num($obj_calcul->localtax1, 'MT') : 0).",
1936 localtax2 = ".($obj_calcul->localtax2 ? price2num($obj_calcul->localtax2, 'MT') : 0).",
1937 total_ttc = ".($obj_calcul->total_ttc ? price2num($obj_calcul->total_ttc, 'MT') : 0)."
1938 WHERE
1939 rowid = $obj->rowid";
1940 $db->query($sql_maj);
1941 }
1942 $i++;
1943 }
1944 } else {
1945 print "Pas de factures à traiter\n";
1946 }
1947 } else {
1948 dol_print_error($db);
1949 dol_syslog("calculate_total_and_taxes.php: Error");
1950 $err++;
1951 }
1952
1953 if ($err == 0) {
1954 $db->commit();
1955 } else {
1956 $db->rollback();
1957 }
1958}
1959
1960print '</table>';
1961
1962if (empty($actiondone)) {
1963 print '<div class="error">'.$langs->trans("ErrorWrongParameters").'</div>';
1964}
1965
1966if ($oneoptionset) {
1967 print '<div class="center" style="padding-top: 10px"><a href="../index.php?mainmenu=home&leftmenu=home'.(GETPOSTISSET("login") ? '&username='.urlencode(GETPOST("login")) : '').'">';
1968 print $langs->trans("GoToDolibarr");
1969 print '</a></div>';
1970}
1971
1972dolibarr_install_syslog("--- repair: end");
1973pFooter(1, $setuplang);
1974
1975if ($db->connected) {
1976 $db->close();
1977}
1978
1979// Return code if ran from command line
1980if (!$ok && isset($argv[1])) {
1981 exit(1);
1982}
$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:542
pFooter($nonext=0, $setuplang='', $jscheckfunction='', $withpleasewait=0, $morehtml='')
Print HTML footer of install pages.
Definition inc.php:635
dolibarr_install_syslog($message, $level=LOG_DEBUG)
Log function for install pages.
Definition inc.php:699
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.