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