Files
dolibarr/htdocs/install/repair.php

2195 lines
75 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2021-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Vincent de Grandpré <vincent@de-grandpre.quebec>
* Copyright (C) 2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/install/repair.php
* \brief Run repair script
*/
include_once 'inc.php';
/**
* @var Conf $conf
* @var Translate $langs
*
* @var string $dolibarr_main_document_root
* @var string $dolibarr_main_db_host
* @var string $dolibarr_main_db_port
* @var string $dolibarr_main_db_name
* @var string $dolibarr_main_db_user
* @var string $dolibarr_main_db_pass
* @var string $dolibarr_main_db_type
* @var string $conffile
*/
if (file_exists($conffile)) {
include_once $conffile;
}
'
@phan-var-force ?string $dolibarr_main_db_encryption
@phan-var-force ?string $dolibarr_main_db_cryptkey
';
require_once $dolibarr_main_document_root.'/core/lib/admin.lib.php';
include_once $dolibarr_main_document_root.'/core/lib/images.lib.php';
require_once $dolibarr_main_document_root.'/core/class/extrafields.class.php';
require_once $dolibarr_main_document_root.'/core/class/html.form.class.php';
require_once 'lib/repair.lib.php';
$ok = 0;
// This page may be long. We are increasing the time allowed.
// Only works if not in safe_mode.
$err = error_reporting();
error_reporting(0);
@set_time_limit(120);
error_reporting($err);
$setuplang = GETPOST("selectlang", 'aZ09', 3) ? GETPOST("selectlang", 'aZ09', 3) : 'auto';
$langs->setDefaultLang($setuplang);
$langs->loadLangs(array("admin", "install", "other"));
if ($dolibarr_main_db_type == "mysqli") {
$choix = 1;
}
if ($dolibarr_main_db_type == "pgsql") {
$choix = 2;
}
if ($dolibarr_main_db_type == "mssql") {
$choix = 3;
}
dolibarr_install_syslog("--- repair: entering upgrade.php page");
if (!is_object($conf)) {
dolibarr_install_syslog("repair: conf file not initialized", LOG_ERR);
}
/*
* View
*/
$form = new Form($db);
pHeader($langs->trans("Repair"), "upgrade2", GETPOST('action', 'aZ09'));
// Action to launch the repair script
$actiondone = 1;
print '<table cellspacing="0" cellpadding="1" class="centpercent">';
$error = 0;
// If password is encoded, we decode it
if (preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass) || !empty($dolibarr_main_db_encrypted_pass)) {
require_once $dolibarr_main_document_root.'/core/lib/security.lib.php';
if (preg_match('/crypted:/i', $dolibarr_main_db_pass)) {
$dolibarr_main_db_pass = preg_replace('/crypted:/i', '', $dolibarr_main_db_pass);
$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
$dolibarr_main_db_pass = dol_decode($dolibarr_main_db_pass);
} elseif (preg_match('/dolcrypt:/i', $dolibarr_main_db_pass)) {
$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
$dolibarr_main_db_pass = dolDecrypt($dolibarr_main_db_pass);
} else {
$dolibarr_main_db_pass = dol_decode($dolibarr_main_db_encrypted_pass);
}
}
// $conf is already instancied inside inc.php
$conf->db->type = $dolibarr_main_db_type;
$conf->db->host = $dolibarr_main_db_host;
$conf->db->port = $dolibarr_main_db_port;
$conf->db->name = $dolibarr_main_db_name;
$conf->db->user = $dolibarr_main_db_user;
$conf->db->pass = $dolibarr_main_db_pass;
// For encryption
$conf->db->dolibarr_main_db_encryption = isset($dolibarr_main_db_encryption) ? $dolibarr_main_db_encryption : 0;
$conf->db->dolibarr_main_db_cryptkey = isset($dolibarr_main_db_cryptkey) ? $dolibarr_main_db_cryptkey : '';
$db = getDoliDBInstance($conf->db->type, $conf->db->host, $conf->db->user, $conf->db->pass, $conf->db->name, (int) $conf->db->port);
if ($db->connected) {
print '<tr><td class="nowrap">';
print $langs->trans("ServerConnection")." : ".$dolibarr_main_db_host.'</td><td class="right">'.$langs->trans("OK")."</td></tr>";
dolibarr_install_syslog("repair: ".$langs->transnoentities("ServerConnection").": ".$dolibarr_main_db_host.$langs->transnoentities("OK"));
$ok = 1;
} else {
print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name).'</td><td class="right">'.$langs->transnoentities("Error")."</td></tr>";
dolibarr_install_syslog("repair: ".$langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
$ok = 0;
}
if ($ok) {
if ($db->database_selected) {
print '<tr><td class="nowrap">';
print $langs->trans("DatabaseConnection")." : ".$dolibarr_main_db_name.'</td><td class="right">'.$langs->trans("OK")."</td></tr>";
dolibarr_install_syslog("repair: database connection successful: ".$dolibarr_main_db_name);
$ok = 1;
} else {
print "<tr><td>".$langs->trans("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name).'</td><td class="right">'.$langs->trans("Error")."</td></tr>";
dolibarr_install_syslog("repair: ".$langs->transnoentities("ErrorFailedToConnectToDatabase", $dolibarr_main_db_name));
$ok = 0;
}
}
// Show database version
if ($ok) {
$version = $db->getVersion();
$versionarray = $db->getVersionArray();
print '<tr><td>'.$langs->trans("ServerVersion").'</td>';
print '<td class="right">'.$version.'</td></tr>';
dolibarr_install_syslog("repair: ".$langs->transnoentities("ServerVersion").": ".$version);
//print '<td class="right">'.join('.',$versionarray).'</td></tr>';
}
print '</table>';
print '<br>';
print '<div class="warning" style="padding-top: 10px">';
print 'Select a link "test" or "confirmed" to launch a reparation on the chosen option...';
print '</div>';
print '<br>';
print '<table class="liste centpercent" style="border: 1px solid #ccc">';
print '<tr>';
print '<th>Option</th>';
print '<th>Information</th>';
print '<th>Launch test</th>';
print '<th>Launch confirmed</th>';
print '</tr>';
$warning_using_utf8mb4 = '';
if ($dolibarr_main_db_character_set != 'utf8mb4') {
$warning_using_utf8mb4 = '<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.">';
}
$sections = [
'Standard' => [
[
'name' => 'standard',
'info' => ''
]
],
'Modules' => [
[
'name' => 'force_disable_of_modules_not_found',
'info' => 'Disable modules not found'
]
],
'Files' => [
[
'name' => 'restore_thirdparties_logos',
'info' => 'Restore logos for thirdparties'
],
[
'name' => 'restore_user_pictures',
'info' => 'Restore user pictures'
],
[
'name' => 'rebuild_product_thumbs',
'info' => 'Rebuild product thumbnails'
],
[
'name' => 'repair_mailing_path',
'info' => 'Repair path of mailing files',
'tooltip' => 'Should be applied when using emailing module with > 99 mailings. In that case, please also set MAILING_USE_NEW_PATH_FOR_FILES.'
]
],
'Clean tables and data' => [
[
'name' => 'clean_linked_elements',
'info' => 'Clean linked elements'
],
[
'name' => 'clean_menus',
'info' => 'Clean menus'
],
[
'name' => 'clean_orphelin_dir',
'info' => 'Clean orphan directories'
],
[
'name' => 'clean_product_stock_batch',
'info' => 'Clean product stock batch'
],
[
'name' => 'clean_perm_table',
'info' => 'Clean permissions table'
],
[
'name' => 'clean_ecm_files_table',
'info' => 'Clean ECM files table'
],
[
'name' => 'repair_link_dispatch_lines_supplier_order_lines',
'info' => 'Repair link between dispatch lines and supplier order lines'
]
],
'Init data' => [
[
'name' => 'set_empty_time_spent_amount',
'info' => 'Init empty time spent amount'
]
],
'Structure' => [
[
'name' => 'force_utf8_on_tables',
'info' => 'Force utf8 + row=dynamic, for mysql/mariadb only'
],
[
'name' => 'force_utf8mb4_on_tables',
'info' => 'Force utf8mb4 + row=dynamic, for mysql/mariadb only' . $warning_using_utf8mb4
],
[
'name' => 'force_collation_from_conf_on_tables',
'info' => 'Force '.$conf->db->character_set.'/'.$conf->db->dolibarr_main_db_collation.' + row=dynamic, for mysql/mariadb only'
]
],
'Rebuild sequence' => [
[
'name' => 'rebuild_sequences',
'info' => 'For postgresql only'
]
]
];
$conf->use_javascript_ajax = 1;
foreach ($sections as $section => $options) {
print '<tr style="background:#f4f4f4;font-weight:bold"><td colspan="5">'.$section.'</td></tr>';
foreach ($options as $opt) {
$option = $opt['name'];
$info = $opt['info'];
$tooltip = empty($opt['tooltip']) ? '' : $opt['tooltip'];
$value = GETPOST($option, 'alpha') ? GETPOST($option, 'alpha') : 'undefined';
// Generate links with the right option and value
$url_test = $_SERVER['PHP_SELF'].'?'.$option.'=test';
$url_confirmed = $_SERVER['PHP_SELF'].'?'.$option.'=confirmed';
print '<tr>';
print '<td>' . $option . '</td>';
print '<td>';
if ($tooltip) {
print $form->textwithpicto($info, $tooltip);
} else {
print $info;
}
print '</td>';
print '<td class="center"><a href="'.$url_test.'" title="Launch test on option '.$option.'">test</a>'.($value == 'test' ? ' (X)' : '').'</td>';
print '<td class="center"><a href="'.$url_confirmed.'" title="Launch confirmed on option '.$option.'">confirmed</a>'.($value == 'confirmed' ? ' (X)' : '').'</td>';
print '</tr>';
}
}
print '</table>';
print '<br id="sectionresult">';
print '<br>';
$conf->setValues($db);
// Reset forced setup after the setValues
if (defined('SYSLOG_FILE')) {
$conf->global->SYSLOG_FILE = constant('SYSLOG_FILE');
}
$conf->global->MAIN_ENABLE_LOG_TO_HTML = 1;
/* Start action here */
$oneoptionset = 0;
$oneoptionset = (GETPOST('standard', 'alpha') || GETPOST('restore_thirdparties_logos', 'alpha') || GETPOST('clean_linked_elements', 'alpha') || GETPOST('clean_menus', 'alpha')
|| GETPOST('clean_orphelin_dir', 'alpha') || GETPOST('clean_product_stock_batch', 'alpha') || GETPOST('set_empty_time_spent_amount', 'alpha') || GETPOST('rebuild_product_thumbs', 'alpha')
|| GETPOST('clean_perm_table', 'alpha') || GETPOST('clean_ecm_files_table', 'alpha')
|| GETPOST('force_disable_of_modules_not_found', 'alpha')
|| GETPOST('force_utf8_on_tables', 'alpha') || GETPOST('force_utf8mb4_on_tables', 'alpha') || GETPOST('force_collation_from_conf_on_tables', 'alpha')
|| GETPOST('rebuild_sequences', 'alpha') || GETPOST('recalculateinvoicetotal', 'alpha')) || GETPOST('repair_mailing_path', 'alpha');
if ($ok && $oneoptionset) {
// Show wait message
print $langs->trans("PleaseBePatient").'<br><br>';
// Flush (some browser need a certain amount of data)
print str_repeat(' ', 1024);
ob_flush();
flush();
}
print '<table cellspacing="0" cellpadding="1" class="centpercent">';
// run_sql: Run repair SQL file
if ($ok && GETPOST('standard', 'alpha')) {
$dir = "mysql/migration/";
$filelist = array();
$i = 0;
$ok = 0;
// Recupere list fichier
$filesindir = array();
$handle = opendir($dir);
if (is_resource($handle)) {
while (($file = readdir($handle)) !== false) {
if (preg_match('/\.sql$/i', $file)) {
$filesindir[] = $file;
}
}
}
sort($filesindir);
foreach ($filesindir as $file) {
if (preg_match('/repair/i', $file)) {
$filelist[] = $file;
}
}
// Loop on each file
foreach ($filelist as $file) {
print '<tr><td class="nowrap">*** ';
print $langs->trans("Script").'</td><td class="right">'.$file.'</td></tr>';
$name = substr($file, 0, dol_strlen($file) - 4);
// Run sql script
$ok = run_sql($dir.$file, 0, 0, 1);
}
}
// sync_extrafields: Search list of fields declared and list of fields created into databases, then create fields missing
if ($ok && GETPOST('standard', 'alpha')) {
require_once DOL_DOCUMENT_ROOT.'/contrat/class/contratligne.class.php';
print '<tr><td colspan="2"><br>*** Update denormalized_lower_planned_end_date.</td></tr>';
$sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat as c";
$sqltoupdatecontract .= " SET c.denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = c.rowid AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
$resqltoupdatecontract = $db->query($sqltoupdatecontract);
$extrafields = new ExtraFields($db);
// List of tables that has an extrafield table
$listofmodulesextra = array('societe' => 'societe', 'adherent' => 'adherent', 'product' => 'product',
'socpeople' => 'socpeople', 'propal' => 'propal', 'commande' => 'commande',
'facture' => 'facture', 'facturedet' => 'facturedet', 'facture_rec' => 'facture_rec', 'facturedet_rec' => 'facturedet_rec',
'supplier_proposal' => 'supplier_proposal', 'commande_fournisseur' => 'commande_fournisseur',
'facture_fourn' => 'facture_fourn', 'facture_fourn_rec' => 'facture_fourn_rec', 'facture_fourn_det' => 'facture_fourn_det', 'facture_fourn_det_rec' => 'facture_fourn_det_rec',
'fichinter' => 'fichinter', 'fichinterdet' => 'fichinterdet',
'inventory' => 'inventory',
'actioncomm' => 'actioncomm', 'bom_bom' => 'bom_bom', 'mrp_mo' => 'mrp_mo',
'adherent_type' => 'adherent_type', 'user' => 'user', 'partnership' => 'partnership', 'projet' => 'projet', 'projet_task' => 'projet_task', 'ticket' => 'ticket');
//$listofmodulesextra = array('fichinter'=>'fichinter');
print '<tr><td colspan="2"><br>*** Check that fields into the extra table structure match the table of definition. If not, add column into table</td></tr>';
foreach ($listofmodulesextra as $tablename => $elementtype) {
// Get list of fields
$tableextra = MAIN_DB_PREFIX.$tablename.'_extrafields';
// Define $arrayoffieldsdesc
$arrayoffieldsdesc = $extrafields->fetch_name_optionals_label($elementtype);
// Define $arrayoffieldsfound
$arrayoffieldsfound = array();
$resql = $db->DDLDescTable($tableextra);
if ($resql) {
print '<tr><td>Check availability of extra field for '.$tableextra;
$i = 0;
while ($obj = $db->fetch_object($resql)) {
$fieldname = $fieldtype = '';
if (preg_match('/mysql/', $db->type)) {
$fieldname = $obj->Field;
$fieldtype = $obj->Type;
} else {
$fieldname = isset($obj->Key) ? $obj->Key : $obj->attname;
$fieldtype = isset($obj->Type) ? $obj->Type : 'varchar';
}
if (empty($fieldname)) {
continue;
}
if (in_array($fieldname, array('rowid', 'tms', 'fk_object', 'import_key'))) {
continue;
}
$arrayoffieldsfound[$fieldname] = array('type' => $fieldtype);
}
print ' - Found '.count($arrayoffieldsfound).' fields into table';
if (count($arrayoffieldsfound) > 0) {
print ' <span class="opacitymedium">('.implode(', ', array_keys($arrayoffieldsfound)).')</span>';
}
print '<br>'."\n";
// If it does not match, we create fields
foreach ($arrayoffieldsdesc as $code => $label) {
if (!in_array($code, array_keys($arrayoffieldsfound))) {
print 'Found field '.$code.' declared into '.MAIN_DB_PREFIX.'extrafields table but not found into desc of table '.$tableextra." -> ";
$type = $extrafields->attributes[$elementtype]['type'][$code];
$length = $extrafields->attributes[$elementtype]['size'][$code];
$attribute = '';
$default = '';
$extra = '';
$null = 'null';
if ($type == 'boolean') {
$typedb = 'int';
$lengthdb = '1';
} elseif ($type == 'price') {
$typedb = 'double';
$lengthdb = '24,8';
} elseif ($type == 'phone') {
$typedb = 'varchar';
$lengthdb = '20';
} elseif ($type == 'mail') {
$typedb = 'varchar';
$lengthdb = '128';
} elseif (($type == 'select') || ($type == 'sellist') || ($type == 'radio') || ($type == 'checkbox') || ($type == 'chkbxlst')) {
$typedb = 'text';
$lengthdb = '';
} elseif ($type == 'link') {
$typedb = 'int';
$lengthdb = '11';
} else {
$typedb = $type;
$lengthdb = $length;
}
$field_desc = array(
'type' => $typedb,
'value' => $lengthdb,
'attribute' => $attribute,
'default' => $default,
'extra' => $extra,
'null' => $null
);
//var_dump($field_desc);exit;
$result = 0;
if (GETPOST('standard', 'alpha') == 'confirmed') {
$result = $db->DDLAddField($tableextra, $code, $field_desc, "");
if ($result < 0) {
print "KO ".$db->lasterror."<br>\n";
} else {
print "OK<br>\n";
}
} else {
print ' - Mode test, no column added.';
}
}
}
print "</td><td>&nbsp;</td></tr>\n";
} else {
print '<tr><td>Table '.$tableextra.' is not found</td><td></td></tr>'."\n";
}
}
}
// clean_data_ecm_dir: Clean data into ecm_directories table
if ($ok && GETPOST('standard', 'alpha')) {
clean_data_ecm_directories();
}
// clean declaration constants
if ($ok && GETPOST('standard', 'alpha')) {
print '<tr><td colspan="2"><br>*** Clean constant record of modules not enabled</td></tr>';
$sql = "SELECT name, entity, value";
$sql .= " FROM ".MAIN_DB_PREFIX."const as c";
$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'";
$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'";
$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_%'";
$sql .= " OR name LIKE 'MAIN_MODULE_%_MODULEFOREXTERNAL'";
$sql .= " ORDER BY name, entity";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$db->begin();
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
$reg = array();
if (preg_match('/MAIN_MODULE_([^_]+)_(.+)/i', $obj->name, $reg)) {
$name = $reg[1];
$type = $reg[2];
$sql2 = "SELECT COUNT(*) as nb";
$sql2 .= " FROM ".MAIN_DB_PREFIX."const as c";
$sql2 .= " WHERE name = 'MAIN_MODULE_".$name."'";
$sql2 .= " AND entity = ".((int) $obj->entity);
$resql2 = $db->query($sql2);
if ($resql2) {
$obj2 = $db->fetch_object($resql2);
if ($obj2 && $obj2->nb == 0) {
// Module not found, so we can remove entry
$sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = '".$db->escape($obj->name)."' AND entity = ".((int) $obj->entity);
if (GETPOST('standard', 'alpha') == 'confirmed') {
$db->query($sqldelete);
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>';
} else {
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>';
}
} else {
//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>';
}
}
}
$i++;
}
$db->commit();
}
} else {
dol_print_error($db);
}
}
// clean box of not enabled modules
if ($ok && GETPOST('standard', 'alpha')) {
print '<tr><td colspan="2"><br>*** Clean definition of boxes of modules not enabled</td></tr>';
$sql = "SELECT file, entity FROM ".MAIN_DB_PREFIX."boxes_def";
$sql .= " WHERE file like '%@%'";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$db->begin();
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
$reg = array();
if (preg_match('/^(.+)@(.+)$/i', $obj->file, $reg)) {
$name = $reg[1];
$module = $reg[2];
$sql2 = "SELECT COUNT(*) as nb";
$sql2 .= " FROM ".MAIN_DB_PREFIX."const as c";
$sql2 .= " WHERE name = 'MAIN_MODULE_".strtoupper($module)."'";
$sql2 .= " AND entity = ".((int) $obj->entity);
$sql2 .= " AND value <> 0";
$resql2 = $db->query($sql2);
if ($resql2) {
$obj2 = $db->fetch_object($resql2);
if ($obj2 && $obj2->nb == 0) {
// Module not found, so we canremove entry
$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).")";
$sqldeleteb = "DELETE FROM ".MAIN_DB_PREFIX."boxes_def WHERE file = '".$db->escape($obj->file)."' AND entity = ".((int) $obj->entity);
if (GETPOST('standard', 'alpha') == 'confirmed') {
$db->query($sqldeletea);
$db->query($sqldeleteb);
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>';
} else {
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>';
}
} else {
//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>';
}
}
}
$i++;
}
$db->commit();
}
}
}
// restore_thirdparties_logos: Move logos to correct new directory.
if ($ok && GETPOST('restore_thirdparties_logos')) {
//$exts=array('gif','png','jpg');
$ext = '';
print '<tr><td colspan="2"><br>*** Restore thirdparties logo<br>';
$sql = "SELECT s.rowid, s.nom as name, s.logo FROM ".MAIN_DB_PREFIX."societe as s ORDER BY s.nom";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
/*
$name=preg_replace('/é/','',$obj->name);
$name=preg_replace('/ /','_',$name);
$name=preg_replace('/\'/','',$name);
*/
$tmp = explode('.', (string) $obj->logo);
$name = $tmp[0];
if (isset($tmp[1])) {
$ext = '.'.$tmp[1];
}
if (!empty($name)) {
$filetotest = $dolibarr_main_data_root.'/societe/logos/'.$name.$ext;
$filetotestsmall = $dolibarr_main_data_root.'/societe/logos/thumbs/'.$name.'_small'.$ext;
$exists = (int) dol_is_file($filetotest);
print 'Check thirdparty '.$obj->rowid.' name='.$obj->name.' logo='.$obj->logo.' file '.$filetotest." exists=".$exists."<br>\n";
if ($exists) {
$filetarget = $dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/'.$name.$ext;
$filetargetsmall = $dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/thumbs/'.$name.'_small'.$ext;
$existt = dol_is_file($filetarget);
if (!$existt) {
if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
dol_mkdir($dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos');
}
print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotest." -> ".$filetarget."<br>\n";
if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
dol_copy($filetotest, $filetarget, '', 0);
}
}
$existtt = dol_is_file($filetargetsmall);
if (!$existtt) {
if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
dol_mkdir($dolibarr_main_data_root.'/societe/'.$obj->rowid.'/logos/thumbs');
}
print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestsmall." -> ".$filetargetsmall."<br>\n";
if (GETPOST('restore_thirdparties_logos', 'alpha') == 'confirmed') {
dol_copy($filetotestsmall, $filetargetsmall, '', 0);
}
}
}
}
$i++;
}
} else {
$ok = 0;
dol_print_error($db);
}
print '</td></tr>';
}
// restore_user_pictures: Move pictures to correct new directory.
if ($ok && GETPOST('restore_user_pictures', 'alpha')) {
//$exts=array('gif','png','jpg');
$ext = '';
print '<tr><td colspan="2"><br>*** Restore user pictures<br>';
$sql = "SELECT s.rowid, s.firstname, s.lastname, s.login, s.photo FROM ".MAIN_DB_PREFIX."user as s ORDER BY s.rowid";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
/*
$name=preg_replace('/é/','',$obj->name);
$name=preg_replace('/ /','_',$name);
$name=preg_replace('/\'/','',$name);
*/
$tmp = explode('.', (string) $obj->photo);
$name = $tmp[0];
if (isset($tmp[1])) {
$ext = '.'.$tmp[1];
}
if (!empty($name)) {
$filetotest = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/'.$name.$ext;
$filetotestsmall = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/thumbs/'.$name.'_small'.$ext;
$filetotestmini = $dolibarr_main_data_root.'/users/'.substr(sprintf('%08d', $obj->rowid), -1, 1).'/'.substr(sprintf('%08d', $obj->rowid), -2, 1).'/thumbs/'.$name.'_mini'.$ext;
$exists = (int) dol_is_file($filetotest);
print 'Check user '.$obj->rowid.' lastname='.$obj->lastname.' firstname='.$obj->firstname.' photo='.$obj->photo.' file '.$filetotest." exists=".$exists."<br>\n";
if ($exists) {
$filetarget = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/'.$name.$ext;
$filetargetsmall = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs/'.$name.'_small'.$ext;
$filetargetmini = $dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs/'.$name.'_mini'.$ext;
$existt = dol_is_file($filetarget);
if (!$existt) {
if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid);
}
print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotest." -> ".$filetarget."<br>\n";
if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
dol_copy($filetotest, $filetarget, '', 0);
}
}
$existtt = dol_is_file($filetargetsmall);
if (!$existtt) {
if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs');
}
print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestsmall." -> ".$filetargetsmall."<br>\n";
if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
dol_copy($filetotestsmall, $filetargetsmall, '', 0);
}
}
$existtt = dol_is_file($filetargetmini);
if (!$existtt) {
if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
dol_mkdir($dolibarr_main_data_root.'/users/'.$obj->rowid.'/thumbs');
}
print " &nbsp; &nbsp; &nbsp; -> Copy file ".$filetotestmini." -> ".$filetargetmini."<br>\n";
if (GETPOST('restore_user_pictures', 'alpha') == 'confirmed') {
dol_copy($filetotestmini, $filetargetmini, '', 0);
}
}
}
}
$i++;
}
} else {
$ok = 0;
dol_print_error($db);
}
print '</td></tr>';
}
// rebuild_product_thumbs: Rebuild thumbs for product files
if ($ok && GETPOST('rebuild_product_thumbs', 'alpha')) {
$ext = '';
global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini;
print '<tr><td colspan="2"><br>*** Rebuild product thumbs<br>';
$sql = "SELECT s.rowid, s.ref FROM ".MAIN_DB_PREFIX."product as s ORDER BY s.ref";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
if (!empty($obj->ref)) {
$files = dol_dir_list($dolibarr_main_data_root.'/produit/'.$obj->ref, 'files', 0);
foreach ($files as $file) {
// Generate thumbs.
if (image_format_supported($file['fullname']) == 1) {
$imgThumbSmall = 'notbuild';
if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
// Used on logon for example
$imgThumbSmall = vignette($file['fullname'], $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
}
print 'Check product '.$obj->rowid.", file ".$file['fullname']." -> ".$imgThumbSmall." maxwidthsmall=".$maxwidthsmall." maxheightsmall=".$maxheightsmall."<br>\n";
$imgThumbMini = 'notbuild';
if (GETPOST('rebuild_product_thumbs', 'alpha') == 'confirmed') {
// Create mini thumbs for image (Ratio is near 16/9)
// Used on menu or for setup page for example
$imgThumbMini = vignette($file['fullname'], $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
}
print 'Check product '.$obj->rowid.", file ".$file['fullname']." -> ".$imgThumbMini." maxwidthmini=".$maxwidthmini." maxheightmini=".$maxheightmini."<br>\n";
}
}
}
$i++;
}
} else {
$ok = 0;
dol_print_error($db);
}
print '</td></tr>';
}
// clean_linked_elements: Check and clean linked elements
if ($ok && GETPOST('clean_linked_elements', 'alpha')) {
print '<tr><td colspan="2"><br>*** Check table of linked elements and delete orphelins links</td></tr>';
// propal => order
print '<tr><td colspan="2">'.checkLinkedElements('propal', 'commande')."</td></tr>\n";
// propal => invoice
print '<tr><td colspan="2">'.checkLinkedElements('propal', 'facture')."</td></tr>\n";
// order => invoice
print '<tr><td colspan="2">'.checkLinkedElements('commande', 'facture')."</td></tr>\n";
// order => shipping
print '<tr><td colspan="2">'.checkLinkedElements('commande', 'shipping')."</td></tr>\n";
// shipping => delivery
print '<tr><td colspan="2">'.checkLinkedElements('shipping', 'delivery')."</td></tr>\n";
// order_supplier => invoice_supplier
print '<tr><td colspan="2">'.checkLinkedElements('order_supplier', 'invoice_supplier')."</td></tr>\n";
}
// clean_menus: Check orphelins menus
if ($ok && GETPOST('clean_menus', 'alpha')) {
print '<tr><td colspan="2"><br>*** Clean menu entries coming from disabled modules</td></tr>';
$sql = "SELECT rowid, module";
$sql .= " FROM ".MAIN_DB_PREFIX."menu as c";
$sql .= " WHERE module IS NOT NULL AND module <> ''";
$sql .= " ORDER BY module";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
$modulecond = $obj->module;
$modulecondarray = explode('|', $obj->module); // Name of module
print '<tr><td>';
print $modulecond;
$db->begin();
if ($modulecond) { // And menu entry for module $modulecond was found in database.
$moduleok = 0;
foreach ($modulecondarray as $tmpname) {
if ($tmpname == 'margins') {
$tmpname = 'margin'; // TODO Remove this when normalized
}
$result = 0;
if (!empty($conf->$tmpname)) {
$result = $conf->$tmpname->enabled;
}
if ($result) {
$moduleok++;
}
}
if (!$moduleok && $modulecond) {
print ' - Module condition '.$modulecond.' seems ko, we delete menu entry.';
if (GETPOST('clean_menus') == 'confirmed') {
$sql2 = "DELETE FROM ".MAIN_DB_PREFIX."menu WHERE module = '".$db->escape($modulecond)."'";
$resql2 = $db->query($sql2);
if (!$resql2) {
$error++;
dol_print_error($db);
} else {
print ' - <span class="warning">Cleaned</span>';
}
} else {
print ' - <span class="warning">Canceled (test mode)</span>';
}
} else {
print ' - Module condition '.$modulecond.' is ok, we do nothing.';
}
}
if (!$error) {
$db->commit();
} else {
$db->rollback();
}
print'</td></tr>';
if ($error) {
break;
}
$i++;
}
} else {
print '<tr><td>No menu entries of disabled menus found</td></tr>';
}
} else {
dol_print_error($db);
}
}
// clean_orphelin_dir: Run purge of directory
if ($ok && GETPOST('clean_orphelin_dir', 'alpha')) {
$listmodulepart = array('company', 'invoice', 'invoice_supplier', 'propal', 'order', 'order_supplier', 'contract', 'tax');
foreach ($listmodulepart as $modulepart) {
$filearray = array();
$upload_dir = isset($conf->$modulepart->dir_output) ? $conf->$modulepart->dir_output : '';
if ($modulepart == 'company') {
$upload_dir = $conf->societe->dir_output; // TODO change for multicompany sharing
}
if ($modulepart == 'invoice') {
$upload_dir = $conf->facture->dir_output;
}
if ($modulepart == 'invoice_supplier') {
$upload_dir = $conf->fournisseur->facture->dir_output;
}
if ($modulepart == 'order') {
$upload_dir = $conf->commande->dir_output;
}
if ($modulepart == 'order_supplier') {
$upload_dir = $conf->fournisseur->commande->dir_output;
}
if ($modulepart == 'contract') {
$upload_dir = $conf->contrat->dir_output;
}
if (empty($upload_dir)) {
continue;
}
print '<tr><td colspan="2"><br>*** Clean orphelins files into files '.$upload_dir.'</td></tr>';
$filearray = dol_dir_list($upload_dir, "files", 1, '', array('^SPECIMEN\.pdf$', '^\.', '(\.meta|_preview.*\.png)$', '^temp$', '^payments$', '^CVS$', '^thumbs$'), '', SORT_DESC, 1, 1);
$object_instance = null;
// To show ref or specific information according to view to show (defined by $module)
if ($modulepart == 'company') {
include_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
$object_instance = new Societe($db);
}
if ($modulepart == 'invoice') {
include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
$object_instance = new Facture($db);
} elseif ($modulepart == 'invoice_supplier') {
include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
$object_instance = new FactureFournisseur($db);
} elseif ($modulepart == 'propal') {
include_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
$object_instance = new Propal($db);
} elseif ($modulepart == 'order') {
include_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
$object_instance = new Commande($db);
} elseif ($modulepart == 'order_supplier') {
include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
$object_instance = new CommandeFournisseur($db);
} elseif ($modulepart == 'contract') {
include_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
$object_instance = new Contrat($db);
} elseif ($modulepart == 'tax') {
include_once DOL_DOCUMENT_ROOT.'/compta/sociales/class/chargesociales.class.php';
$object_instance = new ChargeSociales($db);
}
foreach ($filearray as $key => $file) {
if (!is_dir($file['name'])
&& $file['name'] != '.'
&& $file['name'] != '..'
&& $file['name'] != 'CVS'
) {
// Define relative path used to store the file
$relativefile = preg_replace('/'.preg_quote($upload_dir.'/', '/').'/', '', $file['fullname']);
//var_dump($file);
$id = 0;
$ref = '';
$object_instance->id = 0;
$object_instance->ref = '';
$label = '';
// To show ref or specific information according to view to show (defined by $module)
if ($modulepart == 'invoice') {
preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
$ref = $reg[1];
}
if ($modulepart == 'invoice_supplier') {
preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg);
$id = empty($reg[1]) ? '' : $reg[1];
}
if ($modulepart == 'propal') {
preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
$ref = $reg[1];
}
if ($modulepart == 'order') {
preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
$ref = $reg[1];
}
if ($modulepart == 'order_supplier') {
preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
$ref = $reg[1];
}
if ($modulepart == 'contract') {
preg_match('/(.*)\/[^\/]+$/', $relativefile, $reg);
$ref = $reg[1];
}
if ($modulepart == 'tax') {
preg_match('/(\d+)\/[^\/]+$/', $relativefile, $reg);
$id = $reg[1];
}
if (($id || $ref) && $object_instance !== null) {
//print 'Fetch '.$id.' or '.$ref.'<br>';
$result = $object_instance->fetch($id, $ref);
//print $result.'<br>';
if ($result == 0) { // Not found but no error
// Clean of orphelins directories are done into repair.php
print '<tr><td colspan="2">';
print 'Delete orphelins file '.$file['fullname'].'<br>';
if (GETPOST('clean_orphelin_dir', 'alpha') == 'confirmed') {
dol_delete_file($file['fullname'], 1, 1, 1);
dol_delete_dir(dirname($file['fullname']), 1);
}
print "</td></tr>";
} elseif ($result < 0) {
print 'Error in '.get_class($object_instance).'.fetch of id'.$id.' ref='.$ref.', result='.$result.'<br>';
}
}
}
}
}
}
$methodtofix = '';
// clean_linked_elements: Check and clean linked elements
if ($ok && GETPOST('clean_product_stock_batch', 'alpha')) {
$methodtofix = GETPOST('methodtofix', 'alpha') ? GETPOST('methodtofix', 'alpha') : 'updatestock';
print '<tr><td colspan="2"><br>*** Clean table product_batch, methodtofix='.$methodtofix.' (possible values: updatestock or updatebatch)</td></tr>';
$sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
$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";
$sql .= " WHERE p.rowid = ps.fk_product";
$sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
$sql .= " HAVING (SUM(pb.qty) IS NOT NULL AND reel != SUM(pb.qty)) OR (SUM(pb.qty) IS NULL AND p.tobatch > 0)";
print $sql;
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
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)';
// Fix is required
if ($obj->reel != $obj->reelbatch) {
if (empty($obj->tobatch)) {
// If product is not a product that support batches, we can clean stock by deleting the product batch lines
print ' -> Delete qty '.$obj->reelbatch.' for any lot linked to fk_product_stock='.$obj->psrowid;
$sql2 = "DELETE FROM ".MAIN_DB_PREFIX."product_batch";
$sql2 .= " WHERE fk_product_stock = ".((int) $obj->psrowid);
print '<br>'.$sql2;
if (GETPOST('clean_product_stock_batch') == 'confirmed') {
$resql2 = $db->query($sql2);
if (!$resql2) {
$error++;
dol_print_error($db);
}
}
} else {
if ($methodtofix == 'updatebatch') {
// Method 1
print ' -> Insert qty '.($obj->reel - $obj->reelbatch).' with lot 000000 linked to fk_product_stock='.$obj->psrowid;
$sql2 = "INSERT INTO ".MAIN_DB_PREFIX."product_batch(fk_product_stock, batch, qty)";
$sql2 .= "VALUES(".((int) $obj->psrowid).", '000000', ".((float) ($obj->reel - $obj->reelbatch)).")";
print '<br>'.$sql2;
if (GETPOST('clean_product_stock_batch') == 'confirmed') {
$resql2 = $db->query($sql2);
if (!$resql2) {
// TODO If it fails, we must make update
//$sql2 ="UPDATE ".MAIN_DB_PREFIX."product_batch";
//$sql2.=" SET ".$obj->psrowid.", '000000', ".($obj->reel - $obj->reelbatch).")";
//$sql2.=" WHERE fk_product_stock = ".((int) $obj->psrowid)
}
}
}
if ($methodtofix == 'updatestock') {
// Method 2
print ' -> Update qty of product_stock with qty = '.($obj->reelbatch ? ((float) $obj->reelbatch) : '0').' for ps.rowid = '.((int) $obj->psrowid);
$sql2 = "UPDATE ".MAIN_DB_PREFIX."product_stock";
$sql2 .= " SET reel = ".($obj->reelbatch ? ((float) $obj->reelbatch) : '0')." WHERE rowid = ".((int) $obj->psrowid);
print '<br>'.$sql2;
if (GETPOST('clean_product_stock_batch') == 'confirmed') {
$error = 0;
$db->begin();
$resql2 = $db->query($sql2);
if ($resql2) {
// We update product_stock, so we must fill p.stock into product too.
$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)';
$resql3 = $db->query($sql3);
if (!$resql3) {
$error++;
dol_print_error($db);
}
} else {
$error++;
dol_print_error($db);
}
if (!$error) {
$db->commit();
} else {
$db->rollback();
}
}
}
}
}
print'</td></tr>';
$i++;
}
} else {
print '<tr><td colspan="2">Nothing to do</td></tr>';
}
} else {
dol_print_error($db);
}
}
// clean_product_stock_negative_if_batch
if ($ok && GETPOST('clean_product_stock_negative_if_batch', 'alpha')) {
print '<tr><td colspan="2"><br>Clean table product_batch, methodtofix='.$methodtofix.' (possible values: updatestock or updatebatch)</td></tr>';
$sql = "SELECT p.rowid, p.ref, p.tobatch, ps.rowid as psrowid, ps.fk_entrepot, ps.reel, SUM(pb.qty) as reelbatch";
$sql .= " FROM ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."product_stock as ps, ".MAIN_DB_PREFIX."product_batch as pb";
$sql .= " WHERE p.rowid = ps.fk_product AND ps.rowid = pb.fk_product_stock";
$sql .= " AND p.tobatch > 0";
$sql .= " GROUP BY p.rowid, p.ref, p.tobatch, ps.rowid, ps.fk_entrepot, ps.reel";
$sql .= " HAVING reel != SUM(pb.qty)";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
print '<tr><td>'.$obj->rowid.'-'.$obj->ref.'-'.$obj->fk_entrepot.' -> '.$obj->psrowid.': '.$obj->reel.' != '.$obj->reelbatch;
// TODO
}
}
}
}
// set_empty_time_spent_amount
if ($ok && GETPOST('set_empty_time_spent_amount', 'alpha')) {
print '<tr><td colspan="2"><br>*** Set value of time spent without amount</td></tr>';
$sql = "SELECT COUNT(ptt.rowid) as nb, u.rowid as user_id, u.login, u.thm as user_thm";
$sql .= " FROM ".MAIN_DB_PREFIX."element_time as ptt, ".MAIN_DB_PREFIX."user as u";
$sql .= " WHERE ptt.fk_user = u.rowid";
$sql .= " AND ptt.thm IS NULL and u.thm > 0";
$sql .= " GROUP BY u.rowid, u.login, u.thm";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
print '<tr><td>'.$obj->login.'-'.$obj->user_id.' ('.$obj->nb.' lines to fix) -> '.$obj->user_thm;
$db->begin();
if (GETPOST('set_empty_time_spent_amount') == 'confirmed') {
$sql2 = "UPDATE ".MAIN_DB_PREFIX."element_time";
$sql2 .= " SET thm = ".$obj->user_thm." WHERE thm IS NULL AND fk_user = ".((int) $obj->user_id);
$resql2 = $db->query($sql2);
if (!$resql2) {
$error++;
dol_print_error($db);
}
}
if (!$error) {
$db->commit();
} else {
$db->rollback();
}
print'</td></tr>';
if ($error) {
break;
}
$i++;
}
} else {
print '<tr><td>No time spent with empty line on users with a hourly rate defined</td></tr>';
}
} else {
dol_print_error($db);
}
}
// force_disable_of_modules_not_found
if ($ok && GETPOST('force_disable_of_modules_not_found', 'alpha')) {
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>';
$arraylistofkey = array('hooks', 'js', 'css');
foreach ($arraylistofkey as $key) {
$sql = "SELECT DISTINCT name, value";
$sql .= " FROM ".MAIN_DB_PREFIX."const as c";
$sql .= " WHERE name LIKE 'MAIN_MODULE_%_".strtoupper($db->escape($key))."'";
$sql .= " ORDER BY name";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
$constantname = $obj->name; // Name of constant for hook or js or css declaration
print '<tr><td>';
print dol_escape_htmltag($constantname);
$db->begin();
$reg = array();
if (preg_match('/MAIN_MODULE_(.*)_'.strtoupper($key).'/i', $constantname, $reg)) {
$name = strtolower($reg[1]);
if ($name) { // An entry for key $key and module $name was found in database.
$reloffile = '';
$result = 'found';
if ($key == 'hooks') {
$reloffile = $name.'/class/actions_'.$name.'.class.php';
}
if ($key == 'js') {
$value = $obj->value;
$valuearray = (array) json_decode($value); // Force cast into array because sometimes it is a stdClass
$reloffile = $valuearray[0];
$reloffile = preg_replace('/^\//', '', $valuearray[0]);
}
if ($key == 'css') {
$value = $obj->value;
$valuearray = (array) json_decode($value); // Force cast into array because sometimes it is a stdClass
if ($value && (!is_array($valuearray) || count($valuearray) == 0)) {
$valuearray = array();
$valuearray[0] = $value; // If value was not a json array but a string
}
$reloffile = preg_replace('/^\//', '', $valuearray[0]);
}
if ($reloffile) {
//var_dump($key.' - '.$value.' - '.$reloffile);
try {
$result = dol_buildpath($reloffile, 0, 2);
} catch (Exception $e) {
$result = 'found'; // If error, we force like if we found to avoid any deletion
}
} else {
$result = 'found'; //
}
if (!$result) {
print ' - File of '.$key.' ('.$reloffile.') NOT found, we disable the module.';
if (GETPOST('force_disable_of_modules_not_found') == 'confirmed') {
$sql2 = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = 'MAIN_MODULE_".strtoupper($name)."_".strtoupper($key)."'";
$resql2 = $db->query($sql2);
if (!$resql2) {
$error++;
dol_print_error($db);
}
$sql3 = "DELETE FROM ".MAIN_DB_PREFIX."const WHERE name = 'MAIN_MODULE_".strtoupper($name)."'";
$resql3 = $db->query($sql3);
if (!$resql3) {
$error++;
dol_print_error($db);
} else {
print ' - <span class="warning">Cleaned</span>';
}
} else {
print ' - <span class="warning">Canceled (test mode)</span>';
}
} else {
print ' - File of '.$key.' ('.$reloffile.') found, we do nothing.';
}
}
if (!$error) {
$db->commit();
} else {
$db->rollback();
}
}
print'</td></tr>';
if ($error) {
break;
}
$i++;
}
} else {
print '<tr><td>No active module with missing files found by searching on MAIN_MODULE_(.*)_'.strtoupper($key).'</td></tr>';
}
} else {
dol_print_error($db);
}
}
}
// clean_old_module_entries: Clean data into const when files of module were removed without being
if ($ok && GETPOST('clean_perm_table', 'alpha')) {
print '<tr><td colspan="2"><br>*** Clean table user_rights from lines of external modules no more enabled</td></tr>';
$listofmods = '';
foreach ($conf->modules as $key => $val) {
$listofmods .= ($listofmods ? ',' : '')."'".$db->escape($val)."'";
}
$sql = "SELECT id, libelle as label, module from ".MAIN_DB_PREFIX."rights_def WHERE module NOT IN (".$db->sanitize($listofmods, 1).") AND id > 100000";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
if ($obj->id > 0) {
print '<tr><td>Found line with id '.$obj->id.', label "'.$obj->label.'" of module "'.$obj->module.'" to delete';
if (GETPOST('clean_perm_table', 'alpha') == 'confirmed') {
$sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."rights_def WHERE id = ".((int) $obj->id);
$resqldelete = $db->query($sqldelete);
if (!$resqldelete) {
dol_print_error($db);
}
print ' - deleted';
}
print '</td></tr>';
}
$i++;
}
} else {
print '<tr><td>No lines of a disabled external module (with id > 100000) found into table rights_def</td></tr>';
}
} else {
dol_print_error($db);
}
}
// clean_old_module_entries: Clean data into const when files of module were removed without being
if ($ok && GETPOST('clean_ecm_files_table', 'alpha')) {
print '<tr><td colspan="2"><br>*** Clean table ecm_files from lines of entries whose physical files does not exists anymore (emplemented for entity 1 only)</td></tr>';
$MAXTODELETE = 100;
$sql = "SELECT rowid, filename, filepath, entity from ".MAIN_DB_PREFIX."ecm_files";
$sql .= " WHERE entity = 1";
$sql .= " ORDER BY rowid ASC";
$nbfile = 0;
$nbfiletodelete = 0;
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
if ($obj->rowid > 0) {
$filetocheck = DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename;
$nbfile++;
if (!dol_is_file($filetocheck) && !dol_is_file($filetocheck.'.noexe')) {
$nbfiletodelete++;
if ($nbfiletodelete <= $MAXTODELETE) {
print '<tr><td>Found line with id '.$obj->rowid.', entity '.$obj->entity.', file "'.$filetocheck.'" to delete';
if (GETPOST('clean_ecm_files_table', 'alpha') == 'confirmed') {
$sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."ecm_files WHERE rowid = ".((int) $obj->rowid);
$resqldelete = $db->query($sqldelete);
if (!$resqldelete) {
dol_print_error($db);
}
print ' - deleted';
}
print '</td></tr>';
} else {
break;
}
}
}
$i++;
}
}
if ($nbfiletodelete > $MAXTODELETE) {
print '<tr><td>There is more than '.$MAXTODELETE.' invalid entries into ecm_files index table (among '.$nbfile.' analyzed) with no valid physical files. Run the page several time to process all of them.</td></tr>';
} else {
print '<tr><td>Nb of entries processed into ecm_files index table: '.$nbfile.', number of invalid record: '.$nbfiletodelete.'</td></tr>';
}
} else {
dol_print_error($db);
}
}
// force utf8 on tables
if ($ok && GETPOST('force_utf8_on_tables', 'alpha')) {
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>';
if ($db->type == "mysql" || $db->type == "mysqli") {
$force_utf8_on_tables = GETPOST('force_utf8_on_tables', 'alpha');
$listoftables = $db->DDLListTablesFull($db->database_name);
// Disable foreign key checking for avoid errors
if ($force_utf8_on_tables == 'confirmed') {
$sql = 'SET FOREIGN_KEY_CHECKS=0';
print '<!-- '.$sql.' -->';
print '<tr><td colspan="2">'.$sql.'</td></tr>';
$resql = $db->query($sql);
}
$foreignkeystorestore = array();
// First loop to delete foreign keys
foreach ($listoftables as $table) {
// do not convert llx_const if mysql encrypt/decrypt is used
if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
continue;
}
if ($table[1] == 'VIEW') {
print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
continue;
}
// Special case of tables with foreign key on varchar fields
$arrayofforeignkey = array(
'llx_accounting_account' => 'fk_accounting_account_fk_pcg_version',
'llx_accounting_system' => 'fk_accounting_account_fk_pcg_version',
'llx_c_type_contact' => 'fk_societe_commerciaux_fk_c_type_contact_code',
'llx_societe_commerciaux' => 'fk_societe_commerciaux_fk_c_type_contact_code'
);
foreach ($arrayofforeignkey as $tmptable => $foreignkeyname) {
if ($table[0] == $tmptable) {
print '<tr><td colspan="2">';
$sqltmp = "ALTER TABLE ".$db->sanitize($table[0])." DROP FOREIGN KEY ".$db->sanitize($foreignkeyname);
print $sqltmp;
if ($force_utf8_on_tables == 'confirmed') {
$resqltmp = $db->query($sqltmp);
} else {
print ' - <span class="opacitymedium">Disabled</span>';
}
print '</td></tr>';
$foreignkeystorestore[$tmptable] = $foreignkeyname;
}
}
}
foreach ($listoftables as $table) {
// do not convert llx_const if mysql encrypt/decrypt is used
if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
continue;
}
if ($table[1] == 'VIEW') {
print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
continue;
}
$collation = 'utf8_unicode_ci';
$defaultcollation = $db->getDefaultCollationDatabase();
if (preg_match('/general/', $defaultcollation)) {
$collation = 'utf8_general_ci';
}
print '<tr><td colspan="2">';
print $table[0];
$sql1 = "ALTER TABLE ".$db->sanitize($table[0])." ROW_FORMAT=dynamic";
$sql2 = "ALTER TABLE ".$db->sanitize($table[0])." CONVERT TO CHARACTER SET utf8 COLLATE ".$db->sanitize($collation);
print '<!-- '.$sql1.' -->';
print '<!-- '.$sql2.' -->';
if ($force_utf8_on_tables == 'confirmed') {
$resql1 = $db->query($sql1);
if ($resql1) {
$resql2 = $db->query($sql2);
} else {
$resql2 = false;
}
print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
} else {
print ' - <span class="opacitymedium">Disabled</span>';
}
print '</td></tr>';
flush();
ob_flush();
}
// Restore dropped foreign keys
foreach ($foreignkeystorestore as $tmptable => $foreignkeyname) {
$stringtofindinline = "ALTER TABLE .* ADD CONSTRAINT ".$db->sanitize($foreignkeyname);
$fileforkeys = DOL_DOCUMENT_ROOT.'/install/mysql/tables/'.$tmptable.'.key.sql';
//print 'Search in '.$fileforkeys.' to get '.$stringtofindinline."<br>\n";
$handle = fopen($fileforkeys, 'r');
if ($handle) {
while (($line = fgets($handle)) !== false) {
// Process the line read.
if (preg_match('/^'.$stringtofindinline.'/i', $line)) {
$resqltmp = $db->query($line);
print '<tr><td colspan="2">';
print $line;
print ' - Done '.($resqltmp ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
print '</td></tr>';
break;
}
}
fclose($handle);
}
flush();
ob_flush();
}
// Enable foreign key checking
if ($force_utf8_on_tables == 'confirmed') {
$sql = 'SET FOREIGN_KEY_CHECKS=1';
print '<!-- '.$sql.' -->';
print '<tr><td colspan="2">'.$sql.'</td></tr>';
$resql = $db->query($sql);
}
} else {
print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
}
}
// force utf8mb4 on tables EXPERIMENTAL !
if ($ok && GETPOST('force_utf8mb4_on_tables', 'alpha')) {
print '<tr><td colspan="2"><br>*** Force page code and collation of tables into utf8mb4/utf8mb4_unicode_ci (for mysql/mariadb only)</td></tr>';
if ($db->type == "mysql" || $db->type == "mysqli") {
$force_utf8mb4_on_tables = GETPOST('force_utf8mb4_on_tables', 'alpha');
$listoftables = $db->DDLListTablesFull($db->database_name);
// Disable foreign key checking for avoid errors
if ($force_utf8mb4_on_tables == 'confirmed') {
$sql = 'SET FOREIGN_KEY_CHECKS=0';
print '<!-- '.$sql.' -->';
print '<tr><td colspan="2">'.$sql.'</td></tr>';
$resql = $db->query($sql);
}
$foreignkeystorestore = array();
// First loop to delete foreign keys
foreach ($listoftables as $table) {
// do not convert llx_const if mysql encrypt/decrypt is used
if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
continue;
}
if ($table[1] == 'VIEW') {
print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
continue;
}
// Special case of tables with foreign key on varchar fields
$arrayofforeignkey = array(
'llx_accounting_account' => 'fk_accounting_account_fk_pcg_version',
'llx_accounting_system' => 'fk_accounting_account_fk_pcg_version',
'llx_c_type_contact' => 'fk_societe_commerciaux_fk_c_type_contact_code',
'llx_societe_commerciaux' => 'fk_societe_commerciaux_fk_c_type_contact_code'
);
foreach ($arrayofforeignkey as $tmptable => $foreignkeyname) {
if ($table[0] == $tmptable) {
print '<tr><td colspan="2">';
$sqltmp = "ALTER TABLE ".$db->sanitize($table[0])." DROP FOREIGN KEY ".$db->sanitize($foreignkeyname);
print $sqltmp;
if ($force_utf8mb4_on_tables == 'confirmed') {
$resqltmp = $db->query($sqltmp);
} else {
print ' - <span class="opacitymedium">Disabled</span>';
}
print '</td></tr>';
$foreignkeystorestore[$tmptable] = $foreignkeyname;
}
}
}
foreach ($listoftables as $table) {
// do not convert llx_const if mysql encrypt/decrypt is used
if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
continue;
}
if ($table[1] == 'VIEW') {
print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' <span class="opacitymedium">(Skipped)</span></td></tr>';
continue;
}
$collation = 'utf8mb4_unicode_ci';
$defaultcollation = $db->getDefaultCollationDatabase();
if (preg_match('/general/', $defaultcollation)) {
$collation = 'utf8mb4_general_ci';
}
print '<tr><td colspan="2">';
print $table[0];
$sql1 = "ALTER TABLE ".$db->sanitize($table[0])." ROW_FORMAT=dynamic";
$sql2 = "ALTER TABLE ".$db->sanitize($table[0])." CONVERT TO CHARACTER SET utf8mb4 COLLATE ".$db->sanitize($collation);
print '<!-- '.$sql1.' -->';
print '<!-- '.$sql2.' -->';
if ($force_utf8mb4_on_tables == 'confirmed') {
$resql1 = $db->query($sql1);
if ($resql1) {
$resql2 = $db->query($sql2);
} else {
$resql2 = false;
}
print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
} else {
print ' - <span class="opacitymedium">Disabled</span>';
}
print '</td></tr>';
flush();
ob_flush();
}
// Restore dropped foreign keys
foreach ($foreignkeystorestore as $tmptable => $foreignkeyname) {
$stringtofindinline = "ALTER TABLE .* ADD CONSTRAINT ".$db->sanitize($foreignkeyname);
$fileforkeys = DOL_DOCUMENT_ROOT.'/install/mysql/tables/'.$tmptable.'.key.sql';
//print 'Search in '.$fileforkeys.' to get '.$stringtofindinline."<br>\n";
$handle = fopen($fileforkeys, 'r');
if ($handle) {
while (($line = fgets($handle)) !== false) {
// Process the line read.
if (preg_match('/^'.$stringtofindinline.'/i', $line)) {
$resqltmp = $db->query($line);
print '<tr><td colspan="2">';
print $line;
print ' - Done '.($resqltmp ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
print '</td></tr>';
break;
}
}
fclose($handle);
}
flush();
ob_flush();
}
// Enable foreign key checking
if ($force_utf8mb4_on_tables == 'confirmed') {
$sql = 'SET FOREIGN_KEY_CHECKS=1';
print '<!-- '.$sql.' -->';
print '<tr><td colspan="2">'.$sql.'</td></tr>';
$resql = $db->query($sql);
}
} else {
print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
}
}
if ($ok && GETPOST('force_collation_from_conf_on_tables', 'alpha')) {
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>';
if ($db->type == "mysql" || $db->type == "mysqli") {
$force_collation_from_conf_on_tables = GETPOST('force_collation_from_conf_on_tables', 'alpha');
$listoftables = $db->DDLListTablesFull($db->database_name);
// Disable foreign key checking for avoid errors
if ($force_collation_from_conf_on_tables == 'confirmed') {
$sql = 'SET FOREIGN_KEY_CHECKS=0';
print '<!-- '.$sql.' -->';
$resql = $db->query($sql);
}
foreach ($listoftables as $table) {
// do not convert collation on llx_const if mysql encrypt/decrypt is used
if ($conf->db->dolibarr_main_db_encryption != 0 && preg_match('/\_const$/', $table[0])) {
continue;
}
if ($table[1] == 'VIEW') {
print '<tr><td colspan="2">'.$table[0].' is a '.$table[1].' (Skipped)</td></tr>';
continue;
}
print '<tr><td colspan="2">';
print $table[0];
$sql1 = "ALTER TABLE ".$table[0]." ROW_FORMAT=dynamic";
$sql2 = "ALTER TABLE ".$table[0]." CONVERT TO CHARACTER SET ".$conf->db->character_set." COLLATE ".$conf->db->dolibarr_main_db_collation;
print '<!-- '.$sql1.' -->';
print '<!-- '.$sql2.' -->';
if ($force_collation_from_conf_on_tables == 'confirmed') {
$resql1 = $db->query($sql1);
if ($resql1) {
$resql2 = $db->query($sql2);
} else {
$resql2 = false;
}
print ' - Done '.(($resql1 && $resql2) ? '<span class="opacitymedium">(OK)</span>' : '<span class="error" title="'.dol_escape_htmltag($db->lasterror).'">(KO)</span>');
} else {
print ' - <span class="opacitymedium">Disabled</span>';
}
print '</td></tr>';
}
// Enable foreign key checking
if ($force_collation_from_conf_on_tables == 'confirmed') {
$sql = 'SET FOREIGN_KEY_CHECKS=1';
print '<!-- '.$sql.' -->';
$resql = $db->query($sql);
}
} else {
print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
}
}
// rebuild sequences for pgsql
if ($ok && GETPOST('rebuild_sequences', 'alpha')) {
print '<tr><td colspan="2"><br>*** Force to rebuild sequences (for postgresql only)</td></tr>';
if ($db->type == "pgsql") {
$rebuild_sequence = GETPOST('rebuild_sequences', 'alpha');
if ($rebuild_sequence == 'confirmed') {
$sql = "SELECT dol_util_rebuild_sequences();";
print '<!-- '.$sql.' -->';
$resql = $db->query($sql);
}
} else {
print '<tr><td colspan="2">Not available with database type '.$db->type.'</td></tr>';
}
}
//
if ($ok && GETPOST('repair_link_dispatch_lines_supplier_order_lines')) {
/*
* This script is meant to be run when upgrading from a dolibarr version < 3.8
* to a newer version.
*
* Version 3.8 introduces a new column in llx_commande_fournisseur_dispatch, which
* matches the dispatch to a specific supplier order line (so that if there are
* several with the same product, the user can specifically tell which products of
* which line were dispatched where).
*
* However when migrating, the new column has a default value of 0, which means that
* old supplier orders whose lines were dispatched using the old dolibarr version
* have unspecific dispatch lines, which are not taken into account by the new version,
* thus making the order look like it was never dispatched at all.
*
* This scripts sets this foreign key to the first matching supplier order line whose
* product (and supplier order of course) are the same as the dispatchs.
*
* If the dispatched quantity is more than indicated on the order line (this happens if
* there are several order lines for the same product), it creates new dispatch lines
* pointing to the other order lines accordingly, until all the dispatched quantity is
* accounted for.
*/
$repair_link_dispatch_lines_supplier_order_lines = GETPOST('repair_link_dispatch_lines_supplier_order_lines', 'alpha');
echo '<tr><th>Repair llx_receptiondet_batch.fk_commandefourndet</th></tr>';
echo '<tr><td>Repair in progress. This may take a while.</td></tr>';
$sql_dispatch = 'SELECT * FROM '.MAIN_DB_PREFIX.'receptiondet_batch WHERE COALESCE(fk_elementdet, 0) = 0';
$db->begin();
$resql_dispatch = $db->query($sql_dispatch);
$n_processed_rows = 0;
$errors = array();
if ($resql_dispatch) {
if ($db->num_rows($resql_dispatch) == 0) {
echo '<tr><td>Nothing to do.</td></tr>';
exit;
}
while ($obj_dispatch = $db->fetch_object($resql_dispatch)) {
$sql_line = 'SELECT line.rowid, line.qty FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet AS line';
$sql_line .= ' WHERE line.fk_commande = '.((int) $obj_dispatch->fk_commande);
$sql_line .= ' AND line.fk_product = '.((int) $obj_dispatch->fk_product);
$resql_line = $db->query($sql_line);
// sil y a plusieurs lignes avec le même produit sur cette commande fournisseur,
// on divise la ligne de dispatch en autant de lignes quon en a sur la commande pour le produit
// et on met la quantité de la ligne dans la limit du "budget" indiqué par dispatch.qty
$remaining_qty = $obj_dispatch->qty;
$first_iteration = true;
if (!$resql_line) {
echo '<tr><td>Unable to find a matching supplier order line for dispatch #'.$obj_dispatch->rowid.'</td></tr>';
$errors[] = $sql_line;
$n_processed_rows++;
continue;
}
if ($db->num_rows($resql_line) == 0) {
continue;
}
while ($obj_line = $db->fetch_object($resql_line)) {
if (!$remaining_qty) {
break;
}
if (!$obj_line->rowid) {
continue;
}
$qty_for_line = min($remaining_qty, $obj_line->qty);
if ($first_iteration) {
$sql_attach = 'UPDATE '.MAIN_DB_PREFIX.'receptiondet_batch';
$sql_attach .= ' SET fk_elementdet = '.((int) $obj_line->rowid).', qty = '.((float) $qty_for_line);
$sql_attach .= ' WHERE rowid = '.((int) $obj_dispatch->rowid);
$first_iteration = false;
} else {
$sql_attach_values = array(
(string) ((int) $obj_dispatch->fk_element),
(string) ((int) $obj_dispatch->fk_product),
(string) ((int) $obj_line->rowid),
(string) ((float) $qty_for_line),
(string) ((int) $obj_dispatch->fk_entrepot),
(string) ((int) $obj_dispatch->fk_user),
$obj_dispatch->datec ? "'".$db->idate($db->jdate($obj_dispatch->datec))."'" : 'NULL',
$obj_dispatch->comment ? "'".$db->escape($obj_dispatch->comment)."'" : 'NULL',
$obj_dispatch->status ? (string) ((int) $obj_dispatch->status) : 'NULL',
$obj_dispatch->tms ? "'".$db->idate($db->jdate($obj_dispatch->tms))."'" : 'NULL',
$obj_dispatch->batch ? "'".$db->escape($obj_dispatch->batch)."'" : 'NULL',
$obj_dispatch->eatby ? "'".$db->escape($obj_dispatch->eatby)."'" : 'NULL',
$obj_dispatch->sellby ? "'".$db->escape($obj_dispatch->sellby)."'" : 'NULL'
);
$sql_attach_values = implode(', ', $sql_attach_values);
$sql_attach = 'INSERT INTO '.MAIN_DB_PREFIX.'receptiondet_batch';
$sql_attach .= ' (fk_element, fk_product, fk_elementdet, qty, fk_entrepot, fk_user, datec, comment, status, tms, batch, eatby, sellby)';
$sql_attach .= " VALUES (".$sql_attach_values.")"; // The string is already sanitized
}
if ($repair_link_dispatch_lines_supplier_order_lines == 'confirmed') {
$resql_attach = $db->query($sql_attach);
} else {
$resql_attach = true; // Force success in test mode
}
if ($resql_attach) {
$remaining_qty -= $qty_for_line;
} else {
$errors[] = $sql_attach;
}
$first_iteration = false;
}
$n_processed_rows++;
// report progress every 256th row
if (!($n_processed_rows & 0xff)) {
echo '<tr><td>Processed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
flush();
ob_flush();
}
}
} else {
echo '<tr><td>Unable to find any dispatch without an fk_commandefourndet.'."</td></tr>\n";
echo $sql_dispatch."\n";
}
echo '<tr><td>Fixed '.$n_processed_rows.' rows with '.count($errors).' errors…'."</td></tr>\n";
echo '<tr><td>DONE.'."</td></tr>\n";
if (count($errors)) {
$db->rollback();
echo '<tr><td>The transaction was rolled back due to errors: nothing was changed by the script.</td></tr>';
} else {
$db->commit();
}
$db->close();
echo '<tr><td><h3>SQL queries with errors:</h3></tr></td>';
echo '<tr><td>'.implode('</td></tr><tr><td>', $errors).'</td></tr>';
}
// Repair llx_commande_fournisseur to eliminate duplicate reference
if ($ok && GETPOST('repair_supplier_order_duplicate_ref')) {
require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.commande.class.php';
include_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
$db->begin();
$err = 0;
// Query to find all duplicate supplier orders
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "commande_fournisseur";
$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)";
// Build a list of ref => []CommandeFournisseur
$duplicateSupplierOrders = [];
$resql = $db->query($sql);
if ($resql) {
while ($rawSupplierOrder = $db->fetch_object($resql)) {
$supplierOrder = new CommandeFournisseur($db);
$supplierOrder->setVarsFromFetchObj($rawSupplierOrder);
$duplicateSupplierOrders[$rawSupplierOrder->ref] [] = $supplierOrder;
}
} else {
$err++;
}
// Process all duplicate supplier order and regenerate the reference for all except the first one
foreach ($duplicateSupplierOrders as $ref => $supplierOrders) {
/** @var CommandeFournisseur $supplierOrder */
foreach (array_slice($supplierOrders, 1) as $supplierOrder) {
// Definition of supplier order numbering model name
$soc = new Societe($db);
$soc->fetch($supplierOrder->fourn_id);
$newRef = $supplierOrder->getNextNumRef($soc);
$sql = "UPDATE " . MAIN_DB_PREFIX . "commande_fournisseur cf SET cf.ref = '" . $db->escape($newRef) . "' WHERE cf.rowid = " . (int) $supplierOrder->id;
if (!$db->query($sql)) {
$err++;
}
}
}
if ($err == 0) {
$db->commit();
} else {
$db->rollback();
}
}
// Repair llx_invoice to calculate totals from line items
// WARNING : The process can be long on production environments due to restrictions.
// consider raising php_max_execution time if failing to execute completely.
if ($ok && GETPOST('recalculateinvoicetotal') == 'confirmed') {
$err = 0;
$db->begin();
$sql = "
SELECT
f.rowid,
SUM(fd.total_ht) as total_ht
FROM ".MAIN_DB_PREFIX."facture f
LEFT JOIN ".MAIN_DB_PREFIX."facturedet fd
ON fd.fk_facture = f.rowid
WHERE f.total_ht = 0
GROUP BY fd.fk_facture HAVING SUM(fd.total_ht) <> 0";
$resql = $db->query($sql);
if ($resql) {
$num = $db->num_rows($resql);
print "We found ".$num." factures qualified that will have their total recalculated because they are at zero and line items not at zero\n";
dol_syslog("We found ".$num." factures qualified that will have their total recalculated because they are at zero and line items not at zero");
if ($num) {
$i = 0;
while ($i < $num) {
$obj = $db->fetch_object($resql);
$sql_calculs = "
SELECT
SUM(fd.total_ht) as 'total_ht',
SUM(fd.total_tva) as 'total_tva',
SUM(fd.total_localtax1) as 'localtax1',
SUM(fd.total_localtax2) as 'localtax2',
SUM(fd.total_ttc) as 'total_ttc'
FROM
".MAIN_DB_PREFIX."facturedet fd
WHERE
fd.fk_facture = $obj->rowid";
$ressql_calculs = $db->query($sql_calculs);
while ($obj_calcul = $db->fetch_object($ressql_calculs)) {
// Calcul de la somme des paiements reçus
$sql_paiements = "SELECT SUM(amount) as somme from ".MAIN_DB_PREFIX."paiement_facture WHERE fk_facture = $obj->rowid";
$montantPaiements = $db->fetch_object($db->query($sql_paiements))->somme;
$totHt= ($obj_calcul->total_ht ? price2num($obj_calcul->total_ht, 'MT') : 0);
$totTva = ($obj_calcul->total_tva ? price2num($obj_calcul->total_tva, 'MT') : 0);
$totLocal1 = ($obj_calcul->localtax1 ? price2num($obj_calcul->localtax1, 'MT') : 0);
$totLocal2 = ($obj_calcul->localtax2 ? price2num($obj_calcul->localtax2, 'MT') : 0);
$totTtc = $totHt + $totTva + $totLocal1 + $totLocal2;
$sql_maj = "
UPDATE ".MAIN_DB_PREFIX."facture
SET
total_ht = $totHt,
total_tva = $totTva,
localtax1 = $totLocal1,
localtax2 = $totLocal2,
total_ttc = $totTtc,
fk_statut = ".($totTtc == price2num($montantPaiements, 'MT') ? 2 : 1 ).",
paid = ".($totTtc == price2num($montantPaiements, 'MT') ? 1 : 0 )."
WHERE
rowid = $obj->rowid";
$db->query($sql_maj);
}
$i++;
}
} else {
print "Pas de factures à traiter\n";
}
} else {
dol_print_error($db);
dol_syslog("calculate_total_and_taxes.php: Error");
$err++;
}
if ($err == 0) {
$db->commit();
} else {
$db->rollback();
}
}
// Repair mailing path
if ($ok && GETPOST('repair_mailing_path')) {
global $user;
$sav_user = is_object($user) ? clone $user : $user;
require_once DOL_DOCUMENT_ROOT.'/comm/mailing/class/mailing.class.php';
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
print '<tr><td colspan="2"><br>*** Repair mailing path<br>';
/**
* Migrate file from old path to new one for mailing $mailing
*
* @param Mailing $mailing Object mailing
* @return void
*/
function migrate_mailing_filespath($mailing)
{
global $db, $conf, $user;
$dir = $conf->mailing->dir_output;
$origin = $dir.'/'.get_exdir($mailing->id, 2, 0, 1, $mailing, 'mailing');
$destin = $dir.'/'.get_exdir($mailing->id, 0, 0, 1, $mailing, 'mailing');
$origin_osencoded = dol_osencode($origin);
$destin_osencoded = dol_osencode($destin);
dol_mkdir($destin);
$user = new User($db);
$user->fetch($mailing->user_creation_id);
if (dol_is_dir($origin)) {
$handle = opendir($origin_osencoded);
if (is_resource($handle)) {
while (($file = readdir($handle)) !== false) {
if ($file != '.' && $file != '..' && is_dir($origin_osencoded.'/'.$file)) {
$thumbs = opendir($origin_osencoded.'/'.$file);
if (is_resource($thumbs)) {
dol_mkdir($destin.'/'.$file);
while (($thumb = readdir($thumbs)) !== false) {
$res = dol_move($origin.'/'.$file.'/'.$thumb, $destin.'/'.$file.'/'.$thumb);
$msg = ($res ? ' * Migration successful' : 'Migration failed') . ' for file '.$origin.'/'.$file.'.<br>';
print ($msg);
}
// dol_delete_dir($origin.'/'.$file);
}
} else {
if (dol_is_file($origin.'/'.$file)) {
$res = dol_move($origin.'/'.$file, $destin.'/'.$file);
$msg = ($res ? ' * Migration successful' : 'Migration failed') . ' for file '.$origin.'/'.$file.'.<br>';
print ($msg);
}
}
}
}
}
}
$mailing = new Mailing($db);
$sql = "SELECT rowid as mid from ".MAIN_DB_PREFIX."mailing"; // Get list of all mailing
$resql = $db->query($sql);
if ($resql) {
while ($obj = $db->fetch_object($resql)) {
$mailing->fetch($obj->mid);
print "Migrating mailing id=".$mailing->id." ref=".$mailing->ref."<br>\n";
migrate_mailing_filespath($mailing);
}
} else {
$ok = 0;
dol_print_error($db);
}
$user = $sav_user;
print '</td></tr>';
}
print '</table>';
if (empty($actiondone)) {
print '<div class="error">'.$langs->trans("ErrorWrongParameters").'</div>';
}
if ($oneoptionset) {
print '<br>';
print '<div class="center" style="padding-top: 10px"><a href="../index.php?mainmenu=home&leftmenu=home'.(GETPOSTISSET("login") ? '&username='.urlencode(GETPOST("login")) : '').'">';
print img_picto('', 'url', 'class="pictofixedwidth"');
print $langs->trans("GoToDolibarr");
print '</a></div>';
}
dolibarr_install_syslog("--- repair: end");
pFooter(1, $setuplang);
if ($db->connected) {
$db->close();
}
// Return code if ran from command line
if (!$ok && isset($argv[1])) {
exit(1);
}