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