Issues (2811)

public/htdocs/install/repair.php (3 issues)

1
<?php
2
3
/* Copyright (C) 2004      Rodolphe Quiedeville <[email protected]>
4
 * Copyright (C) 2004-2012 Laurent Destailleur  <[email protected]>
5
 * Copyright (C) 2005-2012 Regis Houssin        <[email protected]>
6
 * Copyright (C) 2015      Raphaël Doursenaud   <[email protected]>
7
 * Copyright (C) 2021      Frédéric France      <[email protected]>
8
 * Copyright (C) 2023      Gauthier VERDOL      <[email protected]>
9
 * Copyright (C) 2024		MDW							<[email protected]>
10
 * Copyright (C) 2024		Vincent de Grandpré	<[email protected]>
11
 * Copyright (C) 2024       Rafael San José             <[email protected]>
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 3 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
25
 */
26
27
/**
28
 *      \file       htdocs/install/repair.php
29
 *      \brief      Run repair script
30
 */
31
32
use Dolibarr\Code\Core\Classes\ExtraFields;
33
use Dolibarr\Core\Base\Database;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Database. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
34
use Dolibarr\Core\Model\Constant;
35
36
include_once constant('DOL_DOCUMENT_ROOT') . '/install/inc.php';
37
if (file_exists($conffile)) {
38
    include_once $conffile;
39
}
40
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/admin.lib.php';
41
include_once $dolibarr_main_document_root . '/core/lib/images.lib.php';
42
require_once 'lib/repair.lib.php';
43
44
$step = 2;
45
$ok = 0;
46
47
48
// Cette page peut etre longue. On augmente le delai autorise.
49
// Ne fonctionne que si on est pas en safe_mode.
50
$err = error_reporting();
51
error_reporting(0);
52
@set_time_limit(120);
53
error_reporting($err);
54
55
$setuplang = GETPOST("selectlang", 'aZ09', 3) ? GETPOST("selectlang", 'aZ09', 3) : 'auto';
56
$langs->setDefaultLang($setuplang);
57
58
$langs->loadLangs(array("admin", "install", "other"));
59
60
if ($dolibarr_main_db_type == "mysqli") {
61
    $choix = 1;
62
}
63
if ($dolibarr_main_db_type == "pgsql") {
64
    $choix = 2;
65
}
66
if ($dolibarr_main_db_type == "mssql") {
67
    $choix = 3;
68
}
69
70
71
dolibarr_install_syslog("--- repair: entering upgrade.php page");
72
if (!is_object($conf)) {
73
    dolibarr_install_syslog("repair: conf file not initialized", LOG_ERR);
74
}
75
76
77
/*
78
 * View
79
 */
80
81
pHeader('', "upgrade2", GETPOST('action', 'aZ09'));
82
83
// Action to launch the repair script
84
$actiondone = 1;
85
86
print '<h3>' . $langs->trans("Repair") . '</h3>';
87
88
print '<div class="warning" style="padding-top: 10px">';
89
print $langs->trans("SetAtLeastOneOptionAsUrlParameter");
90
print '</div>';
91
92
//print 'You must set one of the following option with a parameter value that is "test" or "confirmed" on the URL<br>';
93
//print $langs->trans("Example").': '.DOL_MAIN_URL_ROOT.'/install/repair.php?standard=confirmed<br>'."\n";
94
print '<br>';
95
96
print 'Option standard is ' . (GETPOST('standard', 'alpha') ? GETPOST('standard', 'alpha') : 'undefined') . '<br>' . "\n";
97
// Disable modules
98
print '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";
99
// Files
100
print 'Option restore_thirdparties_logos is ' . (GETPOST('restore_thirdparties_logos', 'alpha') ? GETPOST('restore_thirdparties_logos', 'alpha') : 'undefined') . '<br>' . "\n";
101
print 'Option restore_user_pictures is ' . (GETPOST('restore_user_pictures', 'alpha') ? GETPOST('restore_user_pictures', 'alpha') : 'undefined') . '<br>' . "\n";
102
print 'Option rebuild_product_thumbs is ' . (GETPOST('rebuild_product_thumbs', 'alpha') ? GETPOST('rebuild_product_thumbs', 'alpha') : 'undefined') . '<br>' . "\n";
103
// Clean tables and data
104
print 'Option clean_linked_elements is ' . (GETPOST('clean_linked_elements', 'alpha') ? GETPOST('clean_linked_elements', 'alpha') : 'undefined') . '<br>' . "\n";
105
print 'Option clean_menus is ' . (GETPOST('clean_menus', 'alpha') ? GETPOST('clean_menus', 'alpha') : 'undefined') . '<br>' . "\n";
106
print 'Option clean_orphelin_dir is ' . (GETPOST('clean_orphelin_dir', 'alpha') ? GETPOST('clean_orphelin_dir', 'alpha') : 'undefined') . '<br>' . "\n";
107
print 'Option clean_product_stock_batch is ' . (GETPOST('clean_product_stock_batch', 'alpha') ? GETPOST('clean_product_stock_batch', 'alpha') : 'undefined') . '<br>' . "\n";
108
print 'Option clean_perm_table is ' . (GETPOST('clean_perm_table', 'alpha') ? GETPOST('clean_perm_table', 'alpha') : 'undefined') . '<br>' . "\n";
109
print '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";
110
// Init data
111
print 'Option set_empty_time_spent_amount is ' . (GETPOST('set_empty_time_spent_amount', 'alpha') ? GETPOST('set_empty_time_spent_amount', 'alpha') : 'undefined') . '<br>' . "\n";
112
// Structure
113
print '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";
114
print '<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');
115
print '</span>';
116
if ($dolibarr_main_db_character_set != 'utf8mb4') {
117
    print '<img src="' . constant('DOL_URL_ROOT') . '/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.">';
118
}
119
print "<br>\n";
120
print "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";
121
122
// Rebuild sequence
123
print 'Option rebuild_sequences, for postgresql only, is ' . (GETPOST('rebuild_sequences', 'alpha') ? GETPOST('rebuild_sequences', 'alpha') : 'undefined') . '<br>' . "\n";
124
print '<br>';
125
126
print '<hr>';
127
128
print '<table cellspacing="0" cellpadding="1" class="centpercent">';
129
$error = 0;
130
131
// If password is encoded, we decode it
132
if (preg_match('/crypted:/i', $dolibarr_main_db_pass) || !empty($dolibarr_main_db_encrypted_pass)) {
133
    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/security.lib.php';
134
    if (preg_match('/crypted:/i', $dolibarr_main_db_pass)) {
135
        $dolibarr_main_db_pass = preg_replace('/crypted:/i', '', $dolibarr_main_db_pass);
136
        $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_pass);
137
        $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
138
    } else {
139
        $dolibarr_main_db_pass = dol_decode($dolibarr_main_db_encrypted_pass);
140
    }
141
}
142
143
// $conf is already instancied inside inc.php
144
$conf->db->type = $dolibarr_main_db_type;
145
$conf->db->host = $dolibarr_main_db_host;
146
$conf->db->port = $dolibarr_main_db_port;
147
$conf->db->name = $dolibarr_main_db_name;
148
$conf->db->user = $dolibarr_main_db_user;
149
$conf->db->pass = $dolibarr_main_db_pass;
150
$conf->db->prefix = MAIN_DB_PREFIX ?? DEFAULT_DB_PREFIX;
151
152
// For encryption
153
$conf->db->dolibarr_main_db_encryption = isset($dolibarr_main_db_encryption) ? $dolibarr_main_db_encryption : 0;
154
$conf->db->dolibarr_main_db_cryptkey = isset($dolibarr_main_db_cryptkey) ? $dolibarr_main_db_cryptkey : '';
155
156
$db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int)$conf->db->port);
157
new Database();
158
159
if ($db->connected) {
160
    print '<tr><td class="nowrap">';
161
    print $langs->trans("ServerConnection") . " : $dolibarr_main_db_host</td><td class=\"right\">" . $langs->trans("OK") . "</td></tr>";
162
    dolibarr_install_syslog("repair: " . $langs->transnoentities("ServerConnection") . ": " . $dolibarr_main_db_host . $langs->transnoentities("OK"));
163
    $ok = 1;
164
} else {
165
    print "<tr><td>" . $langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name) . "</td><td class=\"right\">" . $langs->transnoentities("Error") . "</td></tr>";
166
    dolibarr_install_syslog("repair: " . $langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
167
    $ok = 0;
168
}
169
170
if ($ok) {
171
    if ($db->database_selected) {
172
        print '<tr><td class="nowrap">';
173
        print $langs->trans("DatabaseConnection") . " : " . $dolibarr_main_db_name . "</td><td class=\"right\">" . $langs->trans("OK") . "</td></tr>";
174
        dolibarr_install_syslog("repair: database connection successful: " . $dolibarr_main_db_name);
175
        $ok = 1;
176
    } else {
177
        print "<tr><td>" . $langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name) . "</td><td class=\"right\">" . $langs->trans("Error") . "</td></tr>";
178
        dolibarr_install_syslog("repair: " . $langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
179
        $ok = 0;
180
    }
181
}
182
183
// Show database version
184
if ($ok) {
185
    $version = $db->getVersion();
186
    $versionarray = $db->getVersionArray();
187
    print '<tr><td>' . $langs->trans("ServerVersion") . '</td>';
188
    print '<td class="right">' . $version . '</td></tr>';
189
    dolibarr_install_syslog("repair: " . $langs->transnoentities("ServerVersion") . ": " . $version);
190
    //print '<td class="right">'.join('.',$versionarray).'</td></tr>';
191
}
192
193
$conf->setValues($db);
194
// Reset forced setup after the setValues
195
if (defined('SYSLOG_FILE')) {
196
    $conf->global->SYSLOG_FILE = constant('SYSLOG_FILE');
197
}
198
$conf->global->MAIN_ENABLE_LOG_TO_HTML = 1;
199
200
201
/* Start action here */
202
$oneoptionset = 0;
203
$oneoptionset = (GETPOST('standard', 'alpha') || GETPOST('restore_thirdparties_logos', 'alpha') || GETPOST('clean_linked_elements', 'alpha') || GETPOST('clean_menus', 'alpha')
204
    || GETPOST('clean_orphelin_dir', 'alpha') || GETPOST('clean_product_stock_batch', 'alpha') || GETPOST('set_empty_time_spent_amount', 'alpha') || GETPOST('rebuild_product_thumbs', 'alpha')
205
    || GETPOST('clean_perm_table', 'alpha')
206
    || GETPOST('force_disable_of_modules_not_found', 'alpha')
207
    || GETPOST('force_utf8_on_tables', 'alpha') || GETPOST('force_utf8mb4_on_tables', 'alpha') || GETPOST('force_collation_from_conf_on_tables', 'alpha')
208
    || GETPOST('rebuild_sequences', 'alpha') || GETPOST('recalculateinvoicetotal', 'alpha'));
209
210
if ($ok && $oneoptionset) {
211
    // Show wait message
212
    print '<tr><td colspan="2">' . $langs->trans("PleaseBePatient") . '<br><br></td></tr>';
213
    flush();
214
}
215
216
217
// run_sql: Run repair SQL file
218
if ($ok && GETPOST('standard', 'alpha')) {
219
    $dir = "mysql/migration/";
220
221
    $filelist = array();
222
    $i = 0;
223
    $ok = 0;
224
225
    // Recupere list fichier
226
    $filesindir = array();
227
    $handle = opendir($dir);
228
    if (is_resource($handle)) {
229
        while (($file = readdir($handle)) !== false) {
230
            if (preg_match('/\.sql$/i', $file)) {
231
                $filesindir[] = $file;
232
            }
233
        }
234
    }
235
    sort($filesindir);
236
237
    foreach ($filesindir as $file) {
238
        if (preg_match('/repair/i', $file)) {
239
            $filelist[] = $file;
240
        }
241
    }
242
243
    // Loop on each file
244
    foreach ($filelist as $file) {
245
        print '<tr><td class="nowrap">*** ';
246
        print $langs->trans("Script") . '</td><td class="right">' . $file . '</td></tr>';
247
248
        $name = substr($file, 0, dol_strlen($file) - 4);
249
250
        // Run sql script
251
        $ok = run_sql($dir . $file, 0, '', 1);
252
    }
253
}
254
255
256
// sync_extrafields: Search list of fields declared and list of fields created into databases, then create fields missing
257
258
if ($ok && GETPOST('standard', 'alpha')) {
259
    $extrafields = new ExtraFields($db);
260
261
    // List of tables that has an extrafield table
262
    $listofmodulesextra = array('societe' => 'societe', 'adherent' => 'adherent', 'product' => 'product',
263
        'socpeople' => 'socpeople', 'propal' => 'propal', 'commande' => 'commande',
264
        'facture' => 'facture', 'facturedet' => 'facturedet', 'facture_rec' => 'facture_rec', 'facturedet_rec' => 'facturedet_rec',
265
        'supplier_proposal' => 'supplier_proposal', 'commande_fournisseur' => 'commande_fournisseur',
266
        'facture_fourn' => 'facture_fourn', 'facture_fourn_rec' => 'facture_fourn_rec', 'facture_fourn_det' => 'facture_fourn_det', 'facture_fourn_det_rec' => 'facture_fourn_det_rec',
267
        'fichinter' => 'fichinter', 'fichinterdet' => 'fichinterdet',
268
        'inventory' => 'inventory',
269
        'actioncomm' => 'actioncomm', 'bom_bom' => 'bom_bom', 'mrp_mo' => 'mrp_mo',
270
        'adherent_type' => 'adherent_type', 'user' => 'user', 'partnership' => 'partnership', 'projet' => 'projet', 'projet_task' => 'projet_task', 'ticket' => 'ticket');
271
    //$listofmodulesextra = array('fichinter'=>'fichinter');
272
273
    print '<tr><td colspan="2"><br>*** Check fields into extra table structure match table of definition. If not add column into table</td></tr>';
274
    foreach ($listofmodulesextra as $tablename => $elementtype) {
275
        // Get list of fields
276
        $tableextra = MAIN_DB_PREFIX . $tablename . '_extrafields';
277
278
        // Define $arrayoffieldsdesc
279
        $arrayoffieldsdesc = $extrafields->fetch_name_optionals_label($elementtype);
280
281
        // Define $arrayoffieldsfound
282
        $arrayoffieldsfound = array();
283
        $resql = $db->DDLDescTable($tableextra);
284
        if ($resql) {
285
            print '<tr><td>Check availability of extra field for ' . $tableextra;
286
            $i = 0;
287
            while ($obj = $db->fetch_object($resql)) {
288
                $fieldname = $fieldtype = '';
289
                if (preg_match('/mysql/', $db->type)) {
290
                    $fieldname = $obj->Field;
291
                    $fieldtype = $obj->Type;
292
                } else {
293
                    $fieldname = isset($obj->Key) ? $obj->Key : $obj->attname;
294
                    $fieldtype = isset($obj->Type) ? $obj->Type : 'varchar';
295
                }
296
297
                if (empty($fieldname)) {
298
                    continue;
299
                }
300
                if (in_array($fieldname, array('rowid', 'tms', 'fk_object', 'import_key'))) {
301
                    continue;
302
                }
303
                $arrayoffieldsfound[$fieldname] = array('type' => $fieldtype);
304
            }
305
            print ' - Found ' . count($arrayoffieldsfound) . ' fields into table';
306
            if (count($arrayoffieldsfound) > 0) {
307
                print ' <span class="opacitymedium">(' . implode(', ', array_keys($arrayoffieldsfound)) . ')</span>';
308
            }
309
            print '<br>' . "\n";
310
311
            // If it does not match, we create fields
312
            foreach ($arrayoffieldsdesc as $code => $label) {
313
                if (!in_array($code, array_keys($arrayoffieldsfound))) {
314
                    print 'Found field ' . $code . ' declared into ' . MAIN_DB_PREFIX . 'extrafields table but not found into desc of table ' . $tableextra . " -> ";
315
                    $type = $extrafields->attributes[$elementtype]['type'][$code];
316
                    $length = $extrafields->attributes[$elementtype]['size'][$code];
317
                    $attribute = '';
318
                    $default = '';
319
                    $extra = '';
320
                    $null = 'null';
321
322
                    if ($type == 'boolean') {
323
                        $typedb = 'int';
324
                        $lengthdb = '1';
325
                    } elseif ($type == 'price') {
326
                        $typedb = 'double';
327
                        $lengthdb = '24,8';
328
                    } elseif ($type == 'phone') {
329
                        $typedb = 'varchar';
330
                        $lengthdb = '20';
331
                    } elseif ($type == 'mail') {
332
                        $typedb = 'varchar';
333
                        $lengthdb = '128';
334
                    } elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
335
                        $typedb = 'text';
336
                        $lengthdb = '';
337
                    } elseif ($type == 'link') {
338
                        $typedb = 'int';
339
                        $lengthdb = '11';
340
                    } else {
341
                        $typedb = $type;
342
                        $lengthdb = $length;
343
                    }
344
345
                    $field_desc = array(
346
                        'type' => $typedb,
347
                        'value' => $lengthdb,
348
                        'attribute' => $attribute,
349
                        'default' => $default,
350
                        'extra' => $extra,
351
                        'null' => $null
352
                    );
353
                    //var_dump($field_desc);exit;
354
355
                    $result = 0;
356
                    if (GETPOST('standard', 'alpha') == 'confirmed') {
357
                        $result = $db->DDLAddField($tableextra, $code, $field_desc, "");
358
359
                        if ($result < 0) {
360
                            print "KO " . $db->lasterror . "<br>\n";
361
                        } else {
362
                            print "OK<br>\n";
363
                        }
364
                    } else {
365
                        print ' - Mode test, no column added.';
366
                    }
367
                }
368
            }
369
370
            print "</td><td>&nbsp;</td></tr>\n";
371
        } else {
372
            print '<tr><td>Table ' . $tableextra . ' is not found</td><td></td></tr>' . "\n";
373
        }
374
    }
375
}
376
377
// clean_data_ecm_dir: Clean data into ecm_directories table
378
if ($ok && GETPOST('standard', 'alpha')) {
379
    clean_data_ecm_directories();
380
}
381
382
// clean declaration constants
383
if ($ok && GETPOST('standard', 'alpha')) {
384
    print '<tr><td colspan="2"><br>*** Clean constant record of modules not enabled</td></tr>';
385
386
    $constants = Constant::getAllModulesConstants();
387
    foreach ($constants as $constant) {
388
        if (!preg_match('/MAIN_MODULE_([^_]+)_(.+)/i', $constant->name, $reg)) {
389
            continue;
390
        }
391
        $name = $reg[1];
392
393
        if (GETPOST('standard', 'alpha') !== 'confirmed') {
394
            print '<tr><td>Widget ' . $constant->name . ' set in entity ' . $constant->entity . ' with value ' . $constant->value . ' -> Module ' . $name . ' not enabled in entity ' . ((int)$constant->entity) . ', we should delete record (not done, mode test)</td></tr>';
395
            continue;
396
        }
397
398
        print '<tr><td>Widget ' . $constant->name . ' set in entity ' . $constant->entity . ' with value ' . $constant->value . ' -> Module ' . $name . ' not enabled in entity ' . ((int)$constant->entity) . ', we delete record</td></tr>';
399
        Constant::deleteByName($constant->name, $constant->entity);
400
    }
401
}
402
403
// clean box of not enabled modules
404
if ($ok && GETPOST('standard', 'alpha')) {
405
    print '<tr><td colspan="2"><br>*** Clean definition of boxes of modules not enabled</td></tr>';
406
407
    $sql = "SELECT file, entity FROM " . MAIN_DB_PREFIX . "boxes_def";
408
    $sql .= " WHERE file like '%@%'";
409
410
    $resql = $db->query($sql);
411
    if ($resql) {
412
        $num = $db->num_rows($resql);
413
414
        if ($num) {
415
            $db->begin();
416
417
            $i = 0;
418
            while ($i < $num) {
419
                $obj = $db->fetch_object($resql);
420
421
                $reg = array();
422
                if (preg_match('/^(.+)@(.+)$/i', $obj->file, $reg)) {
423
                    $name = $reg[1];
424
                    $module = $reg[2];
425
426
                    $module_instance = Constant::getByName('MAIN_MODULE_' . $module, $obj->entity);
427
                    if ($module_instance && $module_instance->value !== 0) {
428
                        // Module not found, so we canremove entry
429
                        $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) . ")";
430
                        $sqldeleteb = "DELETE FROM " . MAIN_DB_PREFIX . "boxes_def WHERE file = '" . $db->escape($obj->file) . "' AND entity = " . ((int)$obj->entity);
431
432
                        if (GETPOST('standard', 'alpha') == 'confirmed') {
433
                            $db->query($sqldeletea);
434
                            $db->query($sqldeleteb);
435
436
                            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>';
437
                        } else {
438
                            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>';
439
                        }
440
                    }
441
                }
442
443
                $i++;
444
            }
445
446
            $db->commit();
447
        }
448
    }
449
}
450
451
452
// restore_thirdparties_logos: Move logos to correct new directory.
453
if ($ok && GETPOST('restore_thirdparties_logos')) {
454
    //$exts=array('gif','png','jpg');
455
456
    $ext = '';
457
458
    print '<tr><td colspan="2"><br>*** Restore thirdparties logo<br>';
459
460
    $sql = "SELECT s.rowid, s.nom as name, s.logo FROM " . MAIN_DB_PREFIX . "societe as s ORDER BY s.nom";
461
    $resql = $db->query($sql);
462
    if ($resql) {
463
        $num = $db->num_rows($resql);
464
        $i = 0;
465
466
        while ($i < $num) {
467
            $obj = $db->fetch_object($resql);
468
469
            /*
470
            $name=preg_replace('/é/','',$obj->name);
471
            $name=preg_replace('/ /','_',$name);
472
            $name=preg_replace('/\'/','',$name);
473
            */
474
475
            $tmp = explode('.', $obj->logo);
476
            $name = $tmp[0];
477
            if (isset($tmp[1])) {
478
                $ext = '.' . $tmp[1];
479
            }
480
481
            if (!empty($name)) {
482
                $filetotest = $dolibarr_main_data_root . '/societe/logos/' . $name . $ext;
483
                $filetotestsmall = $dolibarr_main_data_root . '/societe/logos/thumbs/' . $name . '_small' . $ext;
484
                $exists = (int)dol_is_file($filetotest);
485
                print 'Check thirdparty ' . $obj->rowid . ' name=' . $obj->name . ' logo=' . $obj->logo . ' file ' . $filetotest . " exists=" . $exists . "<br>\n";
486
                if ($exists) {
487
                    $filetarget = $dolibarr_main_data_root . '/societe/' . $obj->rowid . '/logos/' . $name . $ext;
488
                    $filetargetsmall = $dolibarr_main_data_root . '/societe/' . $obj->rowid . '/logos/thumbs/' . $name . '_small' . $ext;
489
                    $existt = dol_is_file($filetarget);
490
                    if (!$existt) {
491
                        if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
492
                            dol_mkdir($dolibarr_main_data_root . '/societe/' . $obj->rowid . '/logos');
493
                        }
494
495
                        print "  &nbsp; &nbsp; &nbsp; -> Copy file " . $filetotest . " -> " . $filetarget . "<br>\n";
496
                        if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
497
                            dol_copy($filetotest, $filetarget, '', 0);
498
                        }
499
                    }
500
501
                    $existtt = dol_is_file($filetargetsmall);
502
                    if (!$existtt) {
503
                        if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
504
                            dol_mkdir($dolibarr_main_data_root . '/societe/' . $obj->rowid . '/logos/thumbs');
505
                        }
506
                        print "  &nbsp; &nbsp; &nbsp; -> Copy file " . $filetotestsmall . " -> " . $filetargetsmall . "<br>\n";
507
                        if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
508
                            dol_copy($filetotestsmall, $filetargetsmall, '', 0);
509
                        }
510
                    }
511
                }
512
            }
513
514
            $i++;
515
        }
516
    } else {
517
        $ok = 0;
518
        dol_print_error($db);
519
    }
520
521
    print '</td></tr>';
522
}
523
524
525
// restore_user_pictures: Move pictures to correct new directory.
526
if ($ok && GETPOST('restore_user_pictures', 'alpha')) {
527
    //$exts=array('gif','png','jpg');
528
529
    $ext = '';
530
531
    print '<tr><td colspan="2"><br>*** Restore user pictures<br>';
532
533
    $sql = "SELECT s.rowid, s.firstname, s.lastname, s.login, s.photo FROM " . MAIN_DB_PREFIX . "user as s ORDER BY s.rowid";
534
    $resql = $db->query($sql);
535
    if ($resql) {
536
        $num = $db->num_rows($resql);
537
        $i = 0;
538
539
        while ($i < $num) {
540
            $obj = $db->fetch_object($resql);
541
542
            /*
543
             $name=preg_replace('/é/','',$obj->name);
544
             $name=preg_replace('/ /','_',$name);
545
             $name=preg_replace('/\'/','',$name);
546
             */
547
548
            $tmp = explode('.', $obj->photo);
549
            $name = $tmp[0];
550
            if (isset($tmp[1])) {
551
                $ext = '.' . $tmp[1];
552
            }
553
554
            if (!empty($name)) {
555
                $filetotest = $dolibarr_main_data_root . '/users/' . substr(sprintf('%08d', $obj->rowid), -1, 1) . '/' . substr(sprintf('%08d', $obj->rowid), -2, 1) . '/' . $name . $ext;
556
                $filetotestsmall = $dolibarr_main_data_root . '/users/' . substr(sprintf('%08d', $obj->rowid), -1, 1) . '/' . substr(sprintf('%08d', $obj->rowid), -2, 1) . '/thumbs/' . $name . '_small' . $ext;
557
                $filetotestmini = $dolibarr_main_data_root . '/users/' . substr(sprintf('%08d', $obj->rowid), -1, 1) . '/' . substr(sprintf('%08d', $obj->rowid), -2, 1) . '/thumbs/' . $name . '_mini' . $ext;
558
                $exists = (int)dol_is_file($filetotest);
559
                print 'Check user ' . $obj->rowid . ' lastname=' . $obj->lastname . ' firstname=' . $obj->firstname . ' photo=' . $obj->photo . ' file ' . $filetotest . " exists=" . $exists . "<br>\n";
560
                if ($exists) {
561
                    $filetarget = $dolibarr_main_data_root . '/users/' . $obj->rowid . '/' . $name . $ext;
562
                    $filetargetsmall = $dolibarr_main_data_root . '/users/' . $obj->rowid . '/thumbs/' . $name . '_small' . $ext;
563
                    $filetargetmini = $dolibarr_main_data_root . '/users/' . $obj->rowid . '/thumbs/' . $name . '_mini' . $ext;
564
565
                    $existt = dol_is_file($filetarget);
566
                    if (!$existt) {
567
                        if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
568
                            dol_mkdir($dolibarr_main_data_root . '/users/' . $obj->rowid);
569
                        }
570
571
                        print "  &nbsp; &nbsp; &nbsp; -> Copy file " . $filetotest . " -> " . $filetarget . "<br>\n";
572
                        if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
573
                            dol_copy($filetotest, $filetarget, '', 0);
574
                        }
575
                    }
576
577
                    $existtt = dol_is_file($filetargetsmall);
578
                    if (!$existtt) {
579
                        if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
580
                            dol_mkdir($dolibarr_main_data_root . '/users/' . $obj->rowid . '/thumbs');
581
                        }
582
583
                        print "  &nbsp; &nbsp; &nbsp; -> Copy file " . $filetotestsmall . " -> " . $filetargetsmall . "<br>\n";
584
                        if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
585
                            dol_copy($filetotestsmall, $filetargetsmall, '', 0);
586
                        }
587
                    }
588
589
                    $existtt = dol_is_file($filetargetmini);
590
                    if (!$existtt) {
591
                        if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
592
                            dol_mkdir($dolibarr_main_data_root . '/users/' . $obj->rowid . '/thumbs');
593
                        }
594
595
                        print "  &nbsp; &nbsp; &nbsp; -> Copy file " . $filetotestmini . " -> " . $filetargetmini . "<br>\n";
596
                        if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
597
                            dol_copy($filetotestmini, $filetargetmini, '', 0);
598
                        }
599
                    }
600
                }
601
            }
602
603
            $i++;
604
        }
605
    } else {
606
        $ok = 0;
607
        dol_print_error($db);
608
    }
609
610
    print '</td></tr>';
611
}
612
613
614
// rebuild_product_thumbs: Rebuild thumbs for product files
615
if ($ok && GETPOST('rebuild_product_thumbs', 'alpha')) {
616
    $ext = '';
617
    global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini;
618
619
    print '<tr><td colspan="2"><br>*** Rebuild product thumbs<br>';
620
621
    $sql = "SELECT s.rowid, s.ref FROM " . MAIN_DB_PREFIX . "product as s ORDER BY s.ref";
622
    $resql = $db->query($sql);
623
    if ($resql) {
624
        $num = $db->num_rows($resql);
625
        $i = 0;
626
627
        while ($i < $num) {
628
            $obj = $db->fetch_object($resql);
629
630
            if (!empty($obj->ref)) {
631
                $files = dol_dir_list($dolibarr_main_data_root . '/produit/' . $obj->ref, 'files', 0);
632
                foreach ($files as $file) {
633
                    // Generate thumbs.
634
                    if (image_format_supported($file['fullname']) == 1) {
635
                        $imgThumbSmall = 'notbuild';
636
                        if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
637
                            // Used on logon for example
638
                            $imgThumbSmall = vignette($file['fullname'], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
639
                        }
640
                        print 'Check product ' . $obj->rowid . ", file " . $file['fullname'] . " -> " . $imgThumbSmall . " maxwidthsmall=" . $maxwidthsmall . " maxheightsmall=" . $maxheightsmall . "<br>\n";
641
                        $imgThumbMini = 'notbuild';
642
                        if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
643
                            // Create mini thumbs for image (Ratio is near 16/9)
644
                            // Used on menu or for setup page for example
645
                            $imgThumbMini = vignette($file['fullname'], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
646
                        }
647
                        print 'Check product ' . $obj->rowid . ", file " . $file['fullname'] . " -> " . $imgThumbMini . " maxwidthmini=" . $maxwidthmini . " maxheightmini=" . $maxheightmini . "<br>\n";
648
                    }
649
                }
650
            }
651
652
            $i++;
653
        }
654
    } else {
655
        $ok = 0;
656
        dol_print_error($db);
657
    }
658
659
    print '</td></tr>';
660
}
661
662
// clean_linked_elements: Check and clean linked elements
663
if ($ok && GETPOST('clean_linked_elements', 'alpha')) {
664
    print '<tr><td colspan="2"><br>*** Check table of linked elements and delete orphelins links</td></tr>';
665
    // propal => order
666
    print '<tr><td colspan="2">' . checkLinkedElements('propal', 'commande') . "</td></tr>\n";
667
668
    // propal => invoice
669
    print '<tr><td colspan="2">' . checkLinkedElements('propal', 'facture') . "</td></tr>\n";
670
671
    // order => invoice
672
    print '<tr><td colspan="2">' . checkLinkedElements('commande', 'facture') . "</td></tr>\n";
673
674
    // order => shipping
675
    print '<tr><td colspan="2">' . checkLinkedElements('commande', 'shipping') . "</td></tr>\n";
676
677
    // shipping => delivery
678
    print '<tr><td colspan="2">' . checkLinkedElements('shipping', 'delivery') . "</td></tr>\n";
679
680
    // order_supplier => invoice_supplier
681
    print '<tr><td colspan="2">' . checkLinkedElements('order_supplier', 'invoice_supplier') . "</td></tr>\n";
682
}
683
684
685
// clean_menus: Check orphelins menus
686
if ($ok && GETPOST('clean_menus', 'alpha')) {
687
    print '<tr><td colspan="2"><br>*** Clean menu entries coming from disabled modules</td></tr>';
688
689
    $sql = "SELECT rowid, module";
690
    $sql .= " FROM " . MAIN_DB_PREFIX . "menu as c";
691
    $sql .= " WHERE module IS NOT NULL AND module <> ''";
692
    $sql .= " ORDER BY module";
693
694
    $resql = $db->query($sql);
695
    if ($resql) {
696
        $num = $db->num_rows($resql);
697
        if ($num) {
698
            $i = 0;
699
            while ($i < $num) {
700
                $obj = $db->fetch_object($resql);
701
702
                $modulecond = $obj->module;
703
                $modulecondarray = explode('|', $obj->module); // Name of module
704
705
                print '<tr><td>';
706
                print $modulecond;
707
708
                $db->begin();
709
710
                if ($modulecond) {      // And menu entry for module $modulecond was found in database.
711
                    $moduleok = 0;
712
                    foreach ($modulecondarray as $tmpname) {
713
                        if ($tmpname == 'margins') {
714
                            $tmpname = 'margin'; // TODO Remove this when normalized
715
                        }
716
717
                        $result = 0;
718
                        if (!empty($conf->$tmpname)) {
719
                            $result = $conf->$tmpname->enabled;
720
                        }
721
                        if ($result) {
722
                            $moduleok++;
723
                        }
724
                    }
725
726
                    if (!$moduleok && $modulecond) {
727
                        print ' - Module condition ' . $modulecond . ' seems ko, we delete menu entry.';
728
                        if (GETPOST('clean_menus') == 'confirmed') {
729
                            $sql2 = "DELETE FROM " . MAIN_DB_PREFIX . "menu WHERE module = '" . $db->escape($modulecond) . "'";
730
                            $resql2 = $db->query($sql2);
731
                            if (!$resql2) {
732
                                $error++;
733
                                dol_print_error($db);
734
                            } else {
735
                                print ' - <span class="warning">Cleaned</span>';
736
                            }
737
                        } else {
738
                            print ' - <span class="warning">Canceled (test mode)</span>';
739
                        }
740
                    } else {
741
                        print ' - Module condition ' . $modulecond . ' is ok, we do nothing.';
742
                    }
743
                }
744
745
                if (!$error) {
746
                    $db->commit();
747
                } else {
748
                    $db->rollback();
749
                }
750
751
                print'</td></tr>';
752
753
                if ($error) {
754
                    break;
755
                }
756
757
                $i++;
758
            }
759
        } else {
760
            print '<tr><td>No menu entries of disabled menus found</td></tr>';
761
        }
762
    } else {
763
        dol_print_error($db);
764
    }
765
}
766
767
768
// clean_orphelin_dir: Run purge of directory
769
if ($ok && GETPOST('clean_orphelin_dir', 'alpha')) {
770
    $listmodulepart = array('company', 'invoice', 'invoice_supplier', 'propal', 'order', 'order_supplier', 'contract', 'tax');
771
    foreach ($listmodulepart as $modulepart) {
772
        $filearray = array();
773
        $upload_dir = isset($conf->$modulepart->dir_output) ? $conf->$modulepart->dir_output : '';
774
        if ($modulepart == 'company') {
775
            $upload_dir = $conf->societe->dir_output; // TODO change for multicompany sharing
776
        }
777
        if ($modulepart == 'invoice') {
778
            $upload_dir = $conf->facture->dir_output;
779
        }
780
        if ($modulepart == 'invoice_supplier') {
781
            $upload_dir = $conf->fournisseur->facture->dir_output;
782
        }
783
        if ($modulepart == 'order') {
784
            $upload_dir = $conf->commande->dir_output;
785
        }
786
        if ($modulepart == 'order_supplier') {
787
            $upload_dir = $conf->fournisseur->commande->dir_output;
788
        }
789
        if ($modulepart == 'contract') {
790
            $upload_dir = $conf->contrat->dir_output;
791
        }
792
793
        if (empty($upload_dir)) {
794
            continue;
795
        }
796
797
        print '<tr><td colspan="2"><br>*** Clean orphelins files into files ' . $upload_dir . '</td></tr>';
798
799
        $filearray = dol_dir_list($upload_dir, "files", 1, '', array('^SPECIMEN\.pdf$', '^\.', '(\.meta|_preview.*\.png)$', '^temp$', '^payments$', '^CVS$', '^thumbs$'), '', SORT_DESC, 1, true);
800
801
        // To show ref or specific information according to view to show (defined by $module)
802
        if ($modulepart == 'company') {
803
            $object_instance = new Societe($db);
804
        }
805
        if ($modulepart == 'invoice') {
806
            $object_instance = new Facture($db);
807
        } elseif ($modulepart == 'invoice_supplier') {
808
            $object_instance = new FactureFournisseur($db);
809
        } elseif ($modulepart == 'propal') {
810
            $object_instance = new Propal($db);
811
        } elseif ($modulepart == 'order') {
812
            $object_instance = new Commande($db);
813
        } elseif ($modulepart == 'order_supplier') {
814
            $object_instance = new CommandeFournisseur($db);
815
        } elseif ($modulepart == 'contract') {
816
            $object_instance = new Contrat($db);
817
        } elseif ($modulepart == 'tax') {
818
            $object_instance = new ChargeSociales($db);
819
        }
820
821
        foreach ($filearray as $key => $file) {
822
            if (
823
                !is_dir($file['name'])
824
                && $file['name'] != '.'
825
                && $file['name'] != '..'
826
                && $file['name'] != 'CVS'
827
            ) {
828
                // Define relative path used to store the file
829
                $relativefile = preg_replace('/' . preg_quote($upload_dir . '/', '/') . '/', '', $file['fullname']);
830
831
                //var_dump($file);
832
                $id = 0;
833
                $ref = '';
834
                $object_instance->id = 0;
835
                $object_instance->ref = '';
836
                $label = '';
837
838
                // To show ref or specific information according to view to show (defined by $module)
839
                if ($modulepart == 'invoice') {
840
                    preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
841
                    $ref = $reg[1];
842
                }
843
                if ($modulepart == 'invoice_supplier') {
844
                    preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg);
845
                    $id = empty($reg[1]) ? '' : $reg[1];
846
                }
847
                if ($modulepart == 'propal') {
848
                    preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
849
                    $ref = $reg[1];
850
                }
851
                if ($modulepart == 'order') {
852
                    preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
853
                    $ref = $reg[1];
854
                }
855
                if ($modulepart == 'order_supplier') {
856
                    preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
857
                    $ref = $reg[1];
858
                }
859
                if ($modulepart == 'contract') {
860
                    preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
861
                    $ref = $reg[1];
862
                }
863
                if ($modulepart == 'tax') {
864
                    preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg);
865
                    $id = $reg[1];
866
                }
867
868
                if ($id || $ref) {
869
                    //print 'Fetch '.$id.' or '.$ref.'<br>';
870
                    $result = $object_instance->fetch($id, $ref);
871
                    //print $result.'<br>';
872
                    if ($result == 0) {    // Not found but no error
873
                        // Clean of orphelins directories are done into repair.php
874
                        print '<tr><td colspan="2">';
875
                        print 'Delete orphelins file ' . $file['fullname'] . '<br>';
876
                        if (GETPOST('clean_orphelin_dir', 'alpha') == 'confirmed') {
877
                            dol_delete_file($file['fullname'], 1, 1, 1);
878
                            dol_delete_dir(dirname($file['fullname']), 1);
879
                        }
880
                        print "</td></tr>";
881
                    } elseif ($result < 0) {
882
                        print 'Error in ' . get_only_class($object_instance) . '.fetch of id' . $id . ' ref=' . $ref . ', result=' . $result . '<br>';
883
                    }
884
                }
885
            }
886
        }
887
    }
888
}
889
890
// clean_linked_elements: Check and clean linked elements
891
if ($ok && GETPOST('clean_product_stock_batch', 'alpha')) {
892
    $methodtofix = GETPOST('methodtofix', 'alpha') ? GETPOST('methodtofix', 'alpha') : 'updatestock';
893
894
    print '<tr><td colspan="2"><br>*** Clean table product_batch, methodtofix=' . $methodtofix . ' (possible values: updatestock or updatebatch)</td></tr>';
895
896
    $sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
897
    $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";
898
    $sql .= " WHERE p.rowid = ps.fk_product";
899
    $sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
900
    $sql .= " HAVING (SUM(pb.qty) IS NOT NULL AND reel != SUM(pb.qty)) OR (SUM(pb.qty) IS NULL AND p.tobatch > 0)";
901
    print $sql;
902
    $resql = $db->query($sql);
903
    if ($resql) {
904
        $num = $db->num_rows($resql);
905
906
        if ($num) {
907
            $i = 0;
908
            while ($i < $num) {
909
                $obj = $db->fetch_object($resql);
910
                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)';
911
912
                // Fix is required
913
                if ($obj->reel != $obj->reelbatch) {
914
                    if (empty($obj->tobatch)) {
915
                        // If product is not a product that support batches, we can clean stock by deleting the product batch lines
916
                        print ' -> Delete qty ' . $obj->reelbatch . ' for any lot linked to fk_product_stock=' . $obj->psrowid;
917
                        $sql2 = "DELETE FROM " . MAIN_DB_PREFIX . "product_batch";
918
                        $sql2 .= " WHERE fk_product_stock = " . ((int)$obj->psrowid);
919
                        print '<br>' . $sql2;
920
921
                        if (GETPOST('clean_product_stock_batch') == 'confirmed') {
922
                            $resql2 = $db->query($sql2);
923
                            if (!$resql2) {
924
                                $error++;
925
                                dol_print_error($db);
926
                            }
927
                        }
928
                    } else {
929
                        if ($methodtofix == 'updatebatch') {
930
                            // Method 1
931
                            print ' -> Insert qty ' . ($obj->reel - $obj->reelbatch) . ' with lot 000000 linked to fk_product_stock=' . $obj->psrowid;
932
                            $sql2 = "INSERT INTO " . MAIN_DB_PREFIX . "product_batch(fk_product_stock, batch, qty)";
933
                            $sql2 .= "VALUES(" . ((int)$obj->psrowid) . ", '000000', " . ((float)($obj->reel - $obj->reelbatch)) . ")";
934
                            print '<br>' . $sql2;
935
936
                            if (GETPOST('clean_product_stock_batch') == 'confirmed') {
937
                                $resql2 = $db->query($sql2);
938
                                if (!$resql2) {
939
                                    // TODO If it fails, we must make update
940
                                    //$sql2 ="UPDATE ".MAIN_DB_PREFIX."product_batch";
941
                                    //$sql2.=" SET ".$obj->psrowid.", '000000', ".($obj->reel - $obj->reelbatch).")";
942
                                    //$sql2.=" WHERE fk_product_stock = ".((int) $obj->psrowid)
943
                                }
944
                            }
945
                        }
946
                        if ($methodtofix == 'updatestock') {
947
                            // Method 2
948
                            print ' -> Update qty of product_stock with qty = ' . ($obj->reelbatch ? ((float)$obj->reelbatch) : '0') . ' for ps.rowid = ' . ((int)$obj->psrowid);
949
                            $sql2 = "UPDATE " . MAIN_DB_PREFIX . "product_stock";
950
                            $sql2 .= " SET reel = " . ($obj->reelbatch ? ((float)$obj->reelbatch) : '0') . " WHERE rowid = " . ((int)$obj->psrowid);
951
                            print '<br>' . $sql2;
952
953
                            if (GETPOST('clean_product_stock_batch') == 'confirmed') {
954
                                $error = 0;
955
956
                                $db->begin();
957
958
                                $resql2 = $db->query($sql2);
959
                                if ($resql2) {
960
                                    // We update product_stock, so we must fill p.stock into product too.
961
                                    $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)';
962
                                    $resql3 = $db->query($sql3);
963
                                    if (!$resql3) {
964
                                        $error++;
965
                                        dol_print_error($db);
966
                                    }
967
                                } else {
968
                                    $error++;
969
                                    dol_print_error($db);
970
                                }
971
972
                                if (!$error) {
973
                                    $db->commit();
974
                                } else {
975
                                    $db->rollback();
976
                                }
977
                            }
978
                        }
979
                    }
980
                }
981
982
                print'</td></tr>';
983
984
                $i++;
985
            }
986
        } else {
987
            print '<tr><td colspan="2">Nothing to do</td></tr>';
988
        }
989
    } else {
990
        dol_print_error($db);
991
    }
992
}
993
994
995
// clean_product_stock_negative_if_batch
996
if ($ok && GETPOST('clean_product_stock_negative_if_batch', 'alpha')) {
997
    print '<tr><td colspan="2"><br>Clean table product_batch, methodtofix=' . $methodtofix . ' (possible values: updatestock or updatebatch)</td></tr>';
998
999
    $sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
1000
    $sql .= " FROM " . MAIN_DB_PREFIX . "product as p, " . MAIN_DB_PREFIX . "product_stock as ps, " . MAIN_DB_PREFIX . "product_batch as pb";
1001
    $sql .= " WHERE p.rowid = ps.fk_product AND ps.rowid = pb.fk_product_stock";
1002
    $sql .= " AND p.tobatch > 0";
1003
    $sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
1004
    $sql .= " HAVING reel != SUM(pb.qty)";
1005
    $resql = $db->query($sql);
1006
    if ($resql) {
1007
        $num = $db->num_rows($resql);
1008
1009
        if ($num) {
1010
            $i = 0;
1011
            while ($i < $num) {
1012
                $obj = $db->fetch_object($resql);
1013
                print '<tr><td>' . $obj->rowid . '-' . $obj->ref . '-' . $obj->fk_entrepot . ' -> ' . $obj->psrowid . ': ' . $obj->reel . ' != ' . $obj->reelbatch;
1014
1015
                // TODO
1016
            }
1017
        }
1018
    }
1019
}
1020
1021
// set_empty_time_spent_amount
1022
if ($ok && GETPOST('set_empty_time_spent_amount', 'alpha')) {
1023
    print '<tr><td colspan="2"><br>*** Set value of time spent without amount</td></tr>';
1024
1025
    $sql = "SELECT COUNT(ptt.rowid) as nb, u.rowid as user_id, u.login, u.thm as user_thm";
1026
    $sql .= " FROM " . MAIN_DB_PREFIX . "element_time as ptt, " . MAIN_DB_PREFIX . "user as u";
1027
    $sql .= " WHERE ptt.fk_user = u.rowid";
1028
    $sql .= " AND ptt.thm IS NULL and u.thm > 0";
1029
    $sql .= " GROUP BY u.rowid, u.login, u.thm";
1030
1031
    $resql = $db->query($sql);
1032
    if ($resql) {
1033
        $num = $db->num_rows($resql);
1034
1035
        if ($num) {
1036
            $i = 0;
1037
            while ($i < $num) {
1038
                $obj = $db->fetch_object($resql);
1039
                print '<tr><td>' . $obj->login . '-' . $obj->user_id . ' (' . $obj->nb . ' lines to fix) -> ' . $obj->user_thm;
1040
1041
                $db->begin();
1042
1043
                if (GETPOST('set_empty_time_spent_amount') == 'confirmed') {
1044
                    $sql2 = "UPDATE " . MAIN_DB_PREFIX . "element_time";
1045
                    $sql2 .= " SET thm = " . $obj->user_thm . " WHERE thm IS NULL AND fk_user = " . ((int)$obj->user_id);
1046
                    $resql2 = $db->query($sql2);
1047
                    if (!$resql2) {
1048
                        $error++;
1049
                        dol_print_error($db);
1050
                    }
1051
                }
1052
1053
                if (!$error) {
1054
                    $db->commit();
1055
                } else {
1056
                    $db->rollback();
1057
                }
1058
1059
                print'</td></tr>';
1060
1061
                if ($error) {
1062
                    break;
1063
                }
1064
1065
                $i++;
1066
            }
1067
        } else {
1068
            print '<tr><td>No time spent with empty line on users with a hourly rate defined</td></tr>';
1069
        }
1070
    } else {
1071
        dol_print_error($db);
1072
    }
1073
}
1074
1075
// force_disable_of_modules_not_found
1076
if ($ok && GETPOST('force_disable_of_modules_not_found', 'alpha')) {
1077
    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>';
1078
1079
    $db->begin();
1080
1081
    $arraylistofkey = array('hooks', 'js', 'css');
1082
1083
    foreach ($arraylistofkey as $key) {
1084
        $result = Constant::selectByNameAndSuffix('MAIN_MODULE', $key);
1085
        foreach ($result as $obj) {
1086
            $constantname = $obj->name; // Name of constant for hook or js or css declaration
1087
1088
            print '<tr><td>';
1089
            print dol_escape_htmltag($constantname);
1090
1091
            $reg = array();
1092
            if (!preg_match('/MAIN_MODULE_(.*)_' . strtoupper($key) . '/i', $constantname, $reg)) {
1093
                print '<tr><td>No active module with missing files found by searching on MAIN_MODULE_(.*)_' . strtoupper($key) . '</td></tr>';
1094
                continue;
1095
            }
1096
1097
            $name = strtolower($reg[1]);
1098
            if (!$name) {   // No entry for key $key and module $name was found in database.
1099
                continue;
1100
            }
1101
1102
            $reloffile = '';
1103
            $result = 'found';
1104
1105
            if ($key == 'hooks') {
1106
                $reloffile = $name . '/class/actions_' . $name . '.class.php';
1107
            }
1108
            if ($key == 'js') {
1109
                $value = $obj->value;
1110
                $valuearray = (array)json_decode($value);  // Force cast into array because sometimes it is a stdClass
1111
                $reloffile = $valuearray[0];
1112
                $reloffile = preg_replace('/^\//', '', $valuearray[0]);
1113
            }
1114
            if ($key == 'css') {
1115
                $value = $obj->value;
1116
                $valuearray = (array)json_decode($value);  // Force cast into array because sometimes it is a stdClass
1117
                if ($value && (!is_array($valuearray) || count($valuearray) == 0)) {
1118
                    $valuearray = array();
1119
                    $valuearray[0] = $value; // If value was not a json array but a string
1120
                }
1121
                $reloffile = preg_replace('/^\//', '', $valuearray[0]);
1122
            }
1123
1124
            if ($reloffile) {
1125
                //var_dump($key.' - '.$value.' - '.$reloffile);
1126
                try {
1127
                    $result = dol_buildpath($reloffile, 0, 2);
1128
                } catch (Exception $e) {
1129
                    $result = 'found'; // If error, we force like if we found to avoid any deletion
1130
                }
1131
            } else {
1132
                $result = 'found';  //
1133
            }
1134
1135
            if (!$result) {
1136
                print ' - File of ' . $key . ' (' . $reloffile . ') NOT found, we disable the module.';
1137
                if (GETPOST('force_disable_of_modules_not_found') == 'confirmed') {
1138
                    if (!Constant::deleteByName('MAIN_MODULE_' . strtoupper($name) . '_' . strtoupper($key))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression Dolibarr\Core\Model\Cons...'_' . strtoupper($key)) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1139
                        dol_print_error(null, 'Fail deleting MAIN_MODULE_' . strtoupper($name) . '_' . strtoupper($key) . ' from const.');
1140
                    }
1141
                    if (!Constant::deleteByName('MAIN_MODULE_' . strtoupper($name))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression Dolibarr\Core\Model\Cons..._' . strtoupper($name)) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1142
                        dol_print_error(null, 'Fail deleting MAIN_MODULE_' . strtoupper($name) . ' from const.');
1143
                    }
1144
                    print ' - <span class="warning">Cleaned</span>';
1145
                } else {
1146
                    print ' - <span class="warning">Canceled (test mode)</span>';
1147
                }
1148
            } else {
1149
                print ' - File of ' . $key . ' (' . $reloffile . ') found, we do nothing.';
1150
            }
1151
1152
            print'</td></tr>';
1153
        }
1154
    }
1155
    if (!$error) {
1156
        $db->commit();
1157
    } else {
1158
        $db->rollback();
1159
    }
1160
}
1161
1162
1163
// clean_old_module_entries: Clean data into const when files of module were removed without being
1164
if ($ok && GETPOST('clean_perm_table', 'alpha')) {
1165
    print '<tr><td colspan="2"><br>*** Clean table user_rights from lines of external modules no more enabled</td></tr>';
1166
1167
    $listofmods = '';
1168
    foreach ($conf->modules as $key => $val) {
1169
        $listofmods .= ($listofmods ? ',' : '') . "'" . $db->escape($val) . "'";
1170
    }
1171
1172
    $sql = "SELECT id, libelle as label, module from " . MAIN_DB_PREFIX . "rights_def WHERE module NOT IN (" . $db->sanitize($listofmods, 1) . ") AND id > 100000";
1173
1174
    $resql = $db->query($sql);
1175
    if ($resql) {
1176
        $num = $db->num_rows($resql);
1177
        if ($num) {
1178
            $i = 0;
1179
            while ($i < $num) {
1180
                $obj = $db->fetch_object($resql);
1181
                if ($obj->id > 0) {
1182
                    print '<tr><td>Found line with id ' . $obj->id . ', label "' . $obj->label . '" of module "' . $obj->module . '" to delete';
1183
                    if (GETPOST('clean_perm_table', 'alpha') == 'confirmed') {
1184
                        $sqldelete = "DELETE FROM " . MAIN_DB_PREFIX . "rights_def WHERE id = " . ((int)$obj->id);
1185
                        $resqldelete = $db->query($sqldelete);
1186
                        if (!$resqldelete) {
1187
                            dol_print_error($db);
1188
                        }
1189
                        print ' - deleted';
1190
                    }
1191
                    print '</td></tr>';
1192
                }
1193
                $i++;
1194
            }
1195
        } else {
1196
            print '<tr><td>No lines of a disabled external module (with id > 100000) found into table rights_def</td></tr>';
1197
        }
1198
    } else {
1199
        dol_print_error($db);
1200
    }
1201
}
1202
1203
1204
// force utf8 on tables
1205
if ($ok && GETPOST('force_utf8_on_tables', 'alpha')) {
1206
    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>';
1207
1208
    if ($db->type == "mysql" || $db->type == "mysqli") {
1209
        $force_utf8_on_tables = GETPOST('force_utf8_on_tables', 'alpha');
1210
1211
        $listoftables = $db->DDLListTablesFull($db->database_name);
1212
1213
        // Disable foreign key checking for avoid errors
1214
        if ($force_utf8_on_tables == 'confirmed') {
1215
            $sql = 'SET FOREIGN_KEY_CHECKS=0';
1216
            print '<!-- ' . $sql . ' -->';
1217
            $resql = $db->query($sql);
1218
        }
1219
1220
        foreach ($listoftables as $table) {
1221
            // do not convert llx_const if mysql encrypt/decrypt is used
1222
            if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1223
                continue;
1224
            }
1225
            if ($table[1] == 'VIEW') {
1226
                print '<tr><td colspan="2">' . $table[0] . ' is a ' . $table[1] . ' (Skipped)</td></tr>';
1227
                continue;
1228
            }
1229
1230
            print '<tr><td colspan="2">';
1231
            print $table[0];
1232
            $sql1 = "ALTER TABLE " . $table[0] . " ROW_FORMAT=dynamic";
1233
            $sql2 = "ALTER TABLE " . $table[0] . " CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci";
1234
            print '<!-- ' . $sql1 . ' -->';
1235
            print '<!-- ' . $sql2 . ' -->';
1236
            if ($force_utf8_on_tables == 'confirmed') {
1237
                $resql1 = $db->query($sql1);
1238
                if ($resql1) {
1239
                    $resql2 = $db->query($sql2);
1240
                } else {
1241
                    $resql2 = false;
1242
                }
1243
                print ' - Done (' . (($resql1 && $resql2) ? 'OK' : 'KO') . ')';
1244
            } else {
1245
                print ' - Disabled';
1246
            }
1247
            print '</td></tr>';
1248
        }
1249
1250
        // Enable foreign key checking
1251
        if ($force_utf8_on_tables == 'confirmed') {
1252
            $sql = 'SET FOREIGN_KEY_CHECKS=1';
1253
            print '<!-- ' . $sql . ' -->';
1254
            $resql = $db->query($sql);
1255
        }
1256
    } else {
1257
        print '<tr><td colspan="2">Not available with database type ' . $db->type . '</td></tr>';
1258
    }
1259
}
1260
1261
// force utf8mb4 on tables  EXPERIMENTAL !
1262
if ($ok && GETPOST('force_utf8mb4_on_tables', 'alpha')) {
1263
    print '<tr><td colspan="2"><br>*** Force page code and collation of tables into utf8mb4/utf8mb4_unicode_ci (for mysql/mariadb only)</td></tr>';
1264
1265
    if ($db->type == "mysql" || $db->type == "mysqli") {
1266
        $force_utf8mb4_on_tables = GETPOST('force_utf8mb4_on_tables', 'alpha');
1267
1268
        $listoftables = $db->DDLListTablesFull($db->database_name);
1269
1270
        // Disable foreign key checking for avoid errors
1271
        if ($force_utf8mb4_on_tables == 'confirmed') {
1272
            $sql = 'SET FOREIGN_KEY_CHECKS=0';
1273
            print '<!-- ' . $sql . ' -->';
1274
            $resql = $db->query($sql);
1275
        }
1276
1277
        foreach ($listoftables as $table) {
1278
            // do not convert llx_const if mysql encrypt/decrypt is used
1279
            if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1280
                continue;
1281
            }
1282
            if ($table[1] == 'VIEW') {
1283
                print '<tr><td colspan="2">' . $table[0] . ' is a ' . $table[1] . ' (Skipped)</td></tr>';
1284
                continue;
1285
            }
1286
1287
            print '<tr><td colspan="2">';
1288
            print $table[0];
1289
            $sql1 = "ALTER TABLE " . $table[0] . " ROW_FORMAT=dynamic";
1290
            $sql2 = "ALTER TABLE " . $table[0] . " CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
1291
            print '<!-- ' . $sql1 . ' -->';
1292
            print '<!-- ' . $sql2 . ' -->';
1293
            if ($force_utf8mb4_on_tables == 'confirmed') {
1294
                $resql1 = $db->query($sql1);
1295
                if ($resql1) {
1296
                    $resql2 = $db->query($sql2);
1297
                } else {
1298
                    $resql2 = false;
1299
                }
1300
                print ' - Done (' . (($resql1 && $resql2) ? 'OK' : 'KO') . ')';
1301
            } else {
1302
                print ' - Disabled';
1303
            }
1304
            print '</td></tr>';
1305
            flush();
1306
            ob_flush();
1307
        }
1308
1309
        // Enable foreign key checking
1310
        if ($force_utf8mb4_on_tables == 'confirmed') {
1311
            $sql = 'SET FOREIGN_KEY_CHECKS=1';
1312
            print '<!-- ' . $sql . ' -->';
1313
            $resql = $db->query($sql);
1314
        }
1315
    } else {
1316
        print '<tr><td colspan="2">Not available with database type ' . $db->type . '</td></tr>';
1317
    }
1318
}
1319
1320
if ($ok && GETPOST('force_collation_from_conf_on_tables', 'alpha')) {
1321
    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>';
1322
1323
    if ($db->type == "mysql" || $db->type == "mysqli") {
1324
        $force_collation_from_conf_on_tables = GETPOST('force_collation_from_conf_on_tables', 'alpha');
1325
1326
        $listoftables = $db->DDLListTablesFull($db->database_name);
1327
1328
        // Disable foreign key checking for avoid errors
1329
        if ($force_collation_from_conf_on_tables == 'confirmed') {
1330
            $sql = 'SET FOREIGN_KEY_CHECKS=0';
1331
            print '<!-- ' . $sql . ' -->';
1332
            $resql = $db->query($sql);
1333
        }
1334
1335
        foreach ($listoftables as $table) {
1336
            // do not convert collation on llx_const if mysql encrypt/decrypt is used
1337
            if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
1338
                continue;
1339
            }
1340
            if ($table[1] == 'VIEW') {
1341
                print '<tr><td colspan="2">' . $table[0] . ' is a ' . $table[1] . ' (Skipped)</td></tr>';
1342
                continue;
1343
            }
1344
1345
            print '<tr><td colspan="2">';
1346
            print $table[0];
1347
            $sql1 = "ALTER TABLE " . $table[0] . " ROW_FORMAT=dynamic";
1348
            $sql2 = "ALTER TABLE " . $table[0] . " CONVERT TO CHARACTER SET " . $conf->db->character_set . " COLLATE " . $conf->db->dolibarr_main_db_collation;
1349
            print '<!-- ' . $sql1 . ' -->';
1350
            print '<!-- ' . $sql2 . ' -->';
1351
            if ($force_collation_from_conf_on_tables == 'confirmed') {
1352
                $resql1 = $db->query($sql1);
1353
                if ($resql1) {
1354
                    $resql2 = $db->query($sql2);
1355
                } else {
1356
                    $resql2 = false;
1357
                }
1358
                print ' - Done (' . (($resql1 && $resql2) ? 'OK' : 'KO') . ')';
1359
            } else {
1360
                print ' - Disabled';
1361
            }
1362
            print '</td></tr>';
1363
        }
1364
1365
        // Enable foreign key checking
1366
        if ($force_collation_from_conf_on_tables == 'confirmed') {
1367
            $sql = 'SET FOREIGN_KEY_CHECKS=1';
1368
            print '<!-- ' . $sql . ' -->';
1369
            $resql = $db->query($sql);
1370
        }
1371
    } else {
1372
        print '<tr><td colspan="2">Not available with database type ' . $db->type . '</td></tr>';
1373
    }
1374
}
1375
1376
// rebuild sequences for pgsql
1377
if ($ok && GETPOST('rebuild_sequences', 'alpha')) {
1378
    print '<tr><td colspan="2"><br>*** Force to rebuild sequences (for postgresql only)</td></tr>';
1379
1380
    if ($db->type == "pgsql") {
1381
        $rebuild_sequence = GETPOST('rebuild_sequences', 'alpha');
1382
1383
        if ($rebuild_sequence == 'confirmed') {
1384
            $sql = "SELECT dol_util_rebuild_sequences();";
1385
            print '<!-- ' . $sql . ' -->';
1386
            $resql = $db->query($sql);
1387
        }
1388
    } else {
1389
        print '<tr><td colspan="2">Not available with database type ' . $db->type . '</td></tr>';
1390
    }
1391
}
1392
1393
//
1394
if ($ok && GETPOST('repair_link_dispatch_lines_supplier_order_lines')) {
1395
    /*
1396
     * This script is meant to be run when upgrading from a dolibarr version < 3.8
1397
     * to a newer version.
1398
     *
1399
     * Version 3.8 introduces a new column in llx_commande_fournisseur_dispatch, which
1400
     * matches the dispatch to a specific supplier order line (so that if there are
1401
     * several with the same product, the user can specifically tell which products of
1402
     * which line were dispatched where).
1403
     *
1404
     * However when migrating, the new column has a default value of 0, which means that
1405
     * old supplier orders whose lines were dispatched using the old dolibarr version
1406
     * have unspecific dispatch lines, which are not taken into account by the new version,
1407
     * thus making the order look like it was never dispatched at all.
1408
     *
1409
     * This scripts sets this foreign key to the first matching supplier order line whose
1410
     * product (and supplier order of course) are the same as the dispatch’s.
1411
     *
1412
     * If the dispatched quantity is more than indicated on the order line (this happens if
1413
     * there are several order lines for the same product), it creates new dispatch lines
1414
     * pointing to the other order lines accordingly, until all the dispatched quantity is
1415
     * accounted for.
1416
     */
1417
1418
    $repair_link_dispatch_lines_supplier_order_lines = GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha');
1419
1420
1421
    echo '<tr><th>Repair llx_receptiondet_batch.fk_commandefourndet</th></tr>';
1422
    echo '<tr><td>Repair in progress. This may take a while.</td></tr>';
1423
1424
    $sql_dispatch = 'SELECT * FROM ' . MAIN_DB_PREFIX . 'receptiondet_batch WHERE COALESCE(fk_elementdet, 0) = 0';
1425
    $db->begin();
1426
    $resql_dispatch = $db->query($sql_dispatch);
1427
    $n_processed_rows = 0;
1428
    $errors = array();
1429
    if ($resql_dispatch) {
1430
        if ($db->num_rows($resql_dispatch) == 0) {
1431
            echo '<tr><td>Nothing to do.</td></tr>';
1432
            exit;
1433
        }
1434
        while ($obj_dispatch = $db->fetch_object($resql_dispatch)) {
1435
            $sql_line = 'SELECT line.rowid, line.qty FROM ' . MAIN_DB_PREFIX . 'commande_fournisseurdet AS line';
1436
            $sql_line .= ' WHERE line.fk_commande = ' . ((int)$obj_dispatch->fk_commande);
1437
            $sql_line .= ' AND   line.fk_product  = ' . ((int)$obj_dispatch->fk_product);
1438
            $resql_line = $db->query($sql_line);
1439
1440
            // s’il y a plusieurs lignes avec le même produit sur cette commande fournisseur,
1441
            // on divise la ligne de dispatch en autant de lignes qu’on en a sur la commande pour le produit
1442
            // et on met la quantité de la ligne dans la limit du "budget" indiqué par dispatch.qty
1443
1444
            $remaining_qty = $obj_dispatch->qty;
1445
            $first_iteration = true;
1446
            if (!$resql_line) {
1447
                echo '<tr><td>Unable to find a matching supplier order line for dispatch #' . $obj_dispatch->rowid . '</td></tr>';
1448
                $errors[] = $sql_line;
1449
                $n_processed_rows++;
1450
                continue;
1451
            }
1452
            if ($db->num_rows($resql_line) == 0) {
1453
                continue;
1454
            }
1455
            while ($obj_line = $db->fetch_object($resql_line)) {
1456
                if (!$remaining_qty) {
1457
                    break;
1458
                }
1459
                if (!$obj_line->rowid) {
1460
                    continue;
1461
                }
1462
                $qty_for_line = min($remaining_qty, $obj_line->qty);
1463
                if ($first_iteration) {
1464
                    $sql_attach = 'UPDATE ' . MAIN_DB_PREFIX . 'receptiondet_batch';
1465
                    $sql_attach .= ' SET fk_elementdet = ' . ((int)$obj_line->rowid) . ', qty = ' . ((float)$qty_for_line);
1466
                    $sql_attach .= ' WHERE rowid = ' . ((int)$obj_dispatch->rowid);
1467
                    $first_iteration = false;
1468
                } else {
1469
                    $sql_attach_values = array(
1470
                        (string)((int)$obj_dispatch->fk_element),
1471
                        (string)((int)$obj_dispatch->fk_product),
1472
                        (string)((int)$obj_line->rowid),
1473
                        (string)((float)$qty_for_line),
1474
                        (string)((int)$obj_dispatch->fk_entrepot),
1475
                        (string)((int)$obj_dispatch->fk_user),
1476
                        $obj_dispatch->datec ? "'" . $db->idate($db->jdate($obj_dispatch->datec)) . "'" : 'NULL',
1477
                        $obj_dispatch->comment ? "'" . $db->escape($obj_dispatch->comment) . "'" : 'NULL',
1478
                        $obj_dispatch->status ? (string)((int)$obj_dispatch->status) : 'NULL',
1479
                        $obj_dispatch->tms ? "'" . $db->idate($db->jdate($obj_dispatch->tms)) . "'" : 'NULL',
1480
                        $obj_dispatch->batch ? "'" . $db->escape($obj_dispatch->batch) . "'" : 'NULL',
1481
                        $obj_dispatch->eatby ? "'" . $db->escape($obj_dispatch->eatby) . "'" : 'NULL',
1482
                        $obj_dispatch->sellby ? "'" . $db->escape($obj_dispatch->sellby) . "'" : 'NULL'
1483
                    );
1484
                    $sql_attach_values = implode(', ', $sql_attach_values);
1485
1486
                    $sql_attach = 'INSERT INTO ' . MAIN_DB_PREFIX . 'receptiondet_batch';
1487
                    $sql_attach .= ' (fk_element, fk_product, fk_elementdet, qty, fk_entrepot, fk_user, datec, comment, status, tms, batch, eatby, sellby)';
1488
                    $sql_attach .= " VALUES (" . $sql_attach_values . ")";  // The string is already sanitized
1489
                }
1490
1491
                if ($repair_link_dispatch_lines_supplier_order_lines == 'confirmed') {
1492
                    $resql_attach = $db->query($sql_attach);
1493
                } else {
1494
                    $resql_attach = true; // Force success in test mode
1495
                }
1496
1497
                if ($resql_attach) {
1498
                    $remaining_qty -= $qty_for_line;
1499
                } else {
1500
                    $errors[] = $sql_attach;
1501
                }
1502
1503
                $first_iteration = false;
1504
            }
1505
            $n_processed_rows++;
1506
1507
            // report progress every 256th row
1508
            if (!($n_processed_rows & 0xff)) {
1509
                echo '<tr><td>Processed ' . $n_processed_rows . ' rows with ' . count($errors) . ' errors…' . "</td></tr>\n";
1510
                flush();
1511
                ob_flush();
1512
            }
1513
        }
1514
    } else {
1515
        echo '<tr><td>Unable to find any dispatch without an fk_commandefourndet.' . "</td></tr>\n";
1516
        echo $sql_dispatch . "\n";
1517
    }
1518
    echo '<tr><td>Fixed ' . $n_processed_rows . ' rows with ' . count($errors) . ' errors…' . "</td></tr>\n";
1519
    echo '<tr><td>DONE.' . "</td></tr>\n";
1520
1521
    if (count($errors)) {
1522
        $db->rollback();
1523
        echo '<tr><td>The transaction was rolled back due to errors: nothing was changed by the script.</td></tr>';
1524
    } else {
1525
        $db->commit();
1526
    }
1527
    $db->close();
1528
1529
    echo '<tr><td><h3>SQL queries with errors:</h3></tr></td>';
1530
    echo '<tr><td>' . implode('</td></tr><tr><td>', $errors) . '</td></tr>';
1531
}
1532
1533
// Repair llx_commande_fournisseur to eliminate duplicate reference
1534
if ($ok && GETPOST('repair_supplier_order_duplicate_ref')) {
1535
    $db->begin();
1536
1537
    $err = 0;
1538
1539
    // Query to find all duplicate supplier orders
1540
    $sql = "SELECT * FROM " . MAIN_DB_PREFIX . "commande_fournisseur";
1541
    $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)";
1542
1543
    // Build a list of ref => []CommandeFournisseur
1544
    $duplicateSupplierOrders = [];
1545
    $resql = $db->query($sql);
1546
    if ($resql) {
1547
        while ($rawSupplierOrder = $db->fetch_object($resql)) {
1548
            $supplierOrder = new CommandeFournisseur($db);
1549
            $supplierOrder->setVarsFromFetchObj($rawSupplierOrder);
1550
1551
            $duplicateSupplierOrders[$rawSupplierOrder->ref] [] = $supplierOrder;
1552
        }
1553
    } else {
1554
        $err++;
1555
    }
1556
1557
    // Process all duplicate supplier order and regenerate the reference for all except the first one
1558
    foreach ($duplicateSupplierOrders as $ref => $supplierOrders) {
1559
        /** @var CommandeFournisseur $supplierOrder */
1560
        foreach (array_slice($supplierOrders, 1) as $supplierOrder) {
1561
            // Definition of supplier order numbering model name
1562
            $soc = new Societe($db);
1563
            $soc->fetch($supplierOrder->fourn_id);
1564
1565
            $newRef = $supplierOrder->getNextNumRef($soc);
1566
1567
            $sql = "UPDATE " . MAIN_DB_PREFIX . "commande_fournisseur cf SET cf.ref = '" . $db->escape($newRef) . "' WHERE cf.rowid = " . (int)$supplierOrder->id;
1568
            if (!$db->query($sql)) {
1569
                $err++;
1570
            }
1571
        }
1572
    }
1573
1574
    if ($err == 0) {
1575
        $db->commit();
1576
    } else {
1577
        $db->rollback();
1578
    }
1579
}
1580
1581
// Repair llx_invoice to calculate totals from line items
1582
// WARNING : The process can be long on production environments due to restrictions.
1583
// consider raising php_max_execution time if failing to execute completely.
1584
if ($ok && GETPOST('recalculateinvoicetotal') == 'confirmed') {
1585
    $err = 0;
1586
    $db->begin();
1587
    $sql = "SELECT f.rowid, SUM(fd.total_ht) as total_ht";
1588
    $sql .= " FROM " . MAIN_DB_PREFIX . "facture f";
1589
    $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "facturedet fd ON fd.fk_facture = f.rowid";
1590
    $sql .= " WHERE f.total_ht = 0";
1591
    $sql .= " GROUP BY fd.fk_facture HAVING SUM(fd.total_ht) <> 0";
1592
1593
    $resql = $db->query($sql);
1594
    if ($resql) {
1595
        $num = $db->num_rows($resql);
1596
        print "We found " . $num . " factures qualified that will have their total recalculated because they are at zero and line items not at zero\n";
1597
        dol_syslog("We found " . $num . " factures qualified that will have their total recalculated because they are at zero and line items not at zero");
1598
1599
        if ($num) {
1600
            $i = 0;
1601
            while ($i < $num) {
1602
                $obj = $db->fetch_object($resql);
1603
                $sql_calculs = "
1604
					SELECT
1605
						SUM(fd.total_ht) as 'total_ht',
1606
						SUM(fd.total_tva) as 'total_tva',
1607
						SUM(fd.total_localtax1) as 'localtax1',
1608
						SUM(fd.total_localtax2) as 'localtax2',
1609
						SUM(fd.total_ttc) as 'total_ttc'
1610
					FROM
1611
						" . MAIN_DB_PREFIX . "facturedet fd
1612
					WHERE
1613
						fd.fk_facture = $obj->rowid";
1614
                $ressql_calculs = $db->query($sql_calculs);
1615
                while ($obj_calcul = $db->fetch_object($ressql_calculs)) {
1616
                    $sql_maj = "
1617
						UPDATE " . MAIN_DB_PREFIX . "facture
1618
						SET
1619
							total_ht = " . ($obj_calcul->total_ht ? price2num($obj_calcul->total_ht, 'MT') : 0) . ",
1620
							total_tva = " . ($obj_calcul->total_tva ? price2num($obj_calcul->total_tva, 'MT') : 0) . ",
1621
							localtax1 = " . ($obj_calcul->localtax1 ? price2num($obj_calcul->localtax1, 'MT') : 0) . ",
1622
							localtax2 = " . ($obj_calcul->localtax2 ? price2num($obj_calcul->localtax2, 'MT') : 0) . ",
1623
							total_ttc = " . ($obj_calcul->total_ttc ? price2num($obj_calcul->total_ttc, 'MT') : 0) . "
1624
						WHERE
1625
							rowid = $obj->rowid";
1626
                    $db->query($sql_maj);
1627
                }
1628
                $i++;
1629
            }
1630
        } else {
1631
            print "Pas de factures à traiter\n";
1632
        }
1633
    } else {
1634
        dol_print_error($db);
1635
        dol_syslog("calculate_total_and_taxes.php: Error");
1636
        $err++;
1637
    }
1638
1639
    if ($err == 0) {
1640
        $db->commit();
1641
    } else {
1642
        $db->rollback();
1643
    }
1644
}
1645
1646
print '</table>';
1647
1648
if (empty($actiondone)) {
1649
    print '<div class="error">' . $langs->trans("ErrorWrongParameters") . '</div>';
1650
}
1651
1652
if ($oneoptionset) {
1653
    print '<div class="center" style="padding-top: 10px"><a href="../index.php?mainmenu=home&leftmenu=home' . (GETPOSTISSET("login") ? '&username=' . urlencode(GETPOST("login")) : '') . '">';
1654
    print $langs->trans("GoToDolibarr");
1655
    print '</a></div>';
1656
}
1657
1658
dolibarr_install_syslog("--- repair: end");
1659
pFooter(1, $setuplang);
1660
1661
if ($db->connected) {
1662
    $db->close();
1663
}
1664
1665
// Return code if ran from command line
1666
if (!$ok && isset($argv[1])) {
1667
    exit(1);
1668
}
1669