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