Merge from develop

This commit is contained in:
VESSILLER 2025-02-20 08:48:51 +01:00
commit 05b759e4f6
35 changed files with 1376 additions and 570 deletions

View File

@ -43,7 +43,7 @@ jobs:
with:
php-version: ${{ matrix.php-version }}
coverage: none # disable xdebug, pcov
tools: phpstan:2.0, cs2pr
tools: phpstan, cs2pr
extensions: calendar, json, imagick, gd, zip, mbstring, intl, opcache, imap,
mysql, pgsql, sqlite3, ldap, xml, mcrypt

View File

@ -37,7 +37,7 @@ jobs:
with:
php-version: ${{ env.PHP_VERSION }}
coverage: none # disable xdebug, pcov
tools: phpstan:2.0, cs2pr
tools: phpstan, cs2pr
extensions: calendar, json, imagick, gd, zip, mbstring, intl, opcache, imap,
mysql, pgsql, sqlite3, ldap, xml, mcrypt
env:

File diff suppressed because it is too large Load Diff

View File

@ -100,7 +100,7 @@ $hookmanager->initHooks(array($contextpage)); // Note that conf->hooks_modules
$formaccounting = new FormAccounting($db);
$form = new Form($db);
if (empty($search_date_start) && !GETPOSTISSET('formfilteraction')) {
if (empty($search_date_start) && empty($search_date_end) && !GETPOSTISSET('formfilteraction')) {
$sql = "SELECT date_start, date_end";
$sql .=" FROM ".MAIN_DB_PREFIX."accounting_fiscalyear ";
if (getDolGlobalInt('ACCOUNTANCY_FISCALYEAR_DEFAULT')) {
@ -162,17 +162,17 @@ if (empty($reshook)) {
$show_subgroup = '';
$search_date_start = '';
$search_date_end = '';
$search_date_startyear = '';
$search_date_startmonth = '';
$search_date_startday = '';
$search_date_endyear = '';
$search_date_endmonth = '';
$search_date_endday = '';
$search_accountancy_code_start = '';
$search_accountancy_code_end = '';
$search_not_reconciled = '';
$search_ledger_code = array();
$filter = array();
unset($_SESSION['DOLDATE_search_date_start_accountancy_day']);
unset($_SESSION['DOLDATE_search_date_start_accountancy_month']);
unset($_SESSION['DOLDATE_search_date_start_accountancy_year']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_day']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_month']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_year']);
}
// Must be after the remove filter action, before the export.

View File

@ -1,12 +1,12 @@
<?php
/* Copyright (C) 2013-2016 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2013-2016 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013-2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2022 Lionel Vessiller <lvessiller@open-dsi.fr>
* Copyright (C) 2016-2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2022 Progiseize <a.bisotti@progiseiea-conseil.com>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2013-2016 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2013-2016 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013-2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2022 Lionel Vessiller <lvessiller@open-dsi.fr>
* Copyright (C) 2016-2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2022 Progiseize <a.bisotti@progiseiea-conseil.com>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.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
@ -31,6 +31,7 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfiscalyear.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
@ -81,7 +82,7 @@ $search_date_export_start = GETPOSTDATE('search_date_export_start', 'getpost');
$search_date_export_end = GETPOSTDATE('search_date_export_end', 'getpostend');
$search_date_validation_start = GETPOSTDATE('search_date_validation_start', 'getpost');
$search_date_validation_start = GETPOSTDATE('search_date_validation_end', 'getpostend');
$search_date_validation_end = GETPOSTDATE('search_date_validation_end', 'getpostend');
// Due date start
$search_date_due_start_day = GETPOSTINT('search_date_due_start_day');
@ -264,44 +265,14 @@ if (empty($reshook)) {
$search_mvt_label = '';
$search_direction = '';
$search_ledger_code = array();
$search_date_startyear = '';
$search_date_startmonth = '';
$search_date_startday = '';
$search_date_endyear = '';
$search_date_endmonth = '';
$search_date_endday = '';
$search_date_start = '';
$search_date_end = '';
$search_date_creation_startyear = '';
$search_date_creation_startmonth = '';
$search_date_creation_startday = '';
$search_date_creation_endyear = '';
$search_date_creation_endmonth = '';
$search_date_creation_endday = '';
$search_date_creation_start = '';
$search_date_creation_end = '';
$search_date_modification_startyear = '';
$search_date_modification_startmonth = '';
$search_date_modification_startday = '';
$search_date_modification_endyear = '';
$search_date_modification_endmonth = '';
$search_date_modification_endday = '';
$search_date_modification_start = '';
$search_date_modification_end = '';
$search_date_export_startyear = '';
$search_date_export_startmonth = '';
$search_date_export_startday = '';
$search_date_export_endyear = '';
$search_date_export_endmonth = '';
$search_date_export_endday = '';
$search_date_export_start = '';
$search_date_export_end = '';
$search_date_validation_startyear = '';
$search_date_validation_startmonth = '';
$search_date_validation_startday = '';
$search_date_validation_endyear = '';
$search_date_validation_endmonth = '';
$search_date_validation_endday = '';
$search_date_validation_start = '';
$search_date_validation_end = '';
// Due date start
@ -320,6 +291,12 @@ if (empty($reshook)) {
$search_not_reconciled = '';
$search_import_key = '';
$toselect = array();
unset($_SESSION['DOLDATE_search_date_start_accountancy_day']);
unset($_SESSION['DOLDATE_search_date_start_accountancy_month']);
unset($_SESSION['DOLDATE_search_date_start_accountancy_year']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_day']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_month']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_year']);
}
// Must be after the remove filter action, before the export.

View File

@ -320,6 +320,12 @@ if (empty($reshook)) {
$search_not_reconciled = '';
$search_import_key = '';
$toselect = array();
unset($_SESSION['DOLDATE_search_date_start_accountancy_day']);
unset($_SESSION['DOLDATE_search_date_start_accountancy_month']);
unset($_SESSION['DOLDATE_search_date_start_accountancy_year']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_day']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_month']);
unset($_SESSION['DOLDATE_search_date_end_accountancy_year']);
}
if (!empty($socid)) {

View File

@ -8,7 +8,7 @@
* Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013-2014 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2017-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2017-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2018-2024 Eric Seigne <eric.seigne@cap-rel.fr>
* Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -183,38 +183,41 @@ if ($mode) {
while ($i < $num) {
$obj = $db->fetch_object($resql);
if ($mode == 'memberbycountry') {
$data[] = array('label' => (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'code' => $obj->code,
'nb' => $obj->nb,
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'code' => (string) $obj->code,
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
if ($mode == 'memberbyregion') { //+
$data[] = array(
'label' => (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => $obj->nb,
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
if ($mode == 'memberbystate') {
$data[] = array('label' => (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => $obj->nb,
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
}
if ($mode == 'memberbytown') {
$data[] = array('label' => (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => $obj->nb,
$data[] = array(
'label' => (string) (($obj->code && $langs->trans("Country".$obj->code) != "Country".$obj->code) ? img_picto('', DOL_URL_ROOT.'/theme/common/flags/'.strtolower($obj->code).'.png', '', 1).' '.$langs->trans("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label_en' => (string) (($obj->code && $langsen->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langsen->transnoentitiesnoconv("Country".$obj->code) : ($obj->label ? $obj->label : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>')),
'label2' => (string) ($obj->label2 ? $obj->label2 : '<span class="opacitymedium">'.$langs->trans("Unknown").'</span>'),
'nb' => (int) $obj->nb,
'lastdate' => $db->jdate($obj->lastdate),
'lastsubscriptiondate' => $db->jdate($obj->lastsubscriptiondate)
);
@ -290,10 +293,10 @@ if (getDolGlobalString("GOOGLE_SHOW_COUNTRY_GRAPH") && $mode == 'memberbycountry
$i = 0;
foreach ($data as $val) {
$valcountry = strtoupper($val['code']); // Should be ISO-3166 code (faster)
//$valcountry=ucfirst($val['label_en']);
if ($valcountry == 'Great Britain') {
$valcountry = 'United Kingdom';
} // fix case of uk (when we use labels)
// $valcountry = ucfirst($val['label_en']);
// if ($valcountry == 'Great Britain') {
// $valcountry = 'United Kingdom';
// } // fix case of uk (when we use labels)
print "\tdata.setValue(".$i.", 0, \"".$valcountry."\");\n";
print "\tdata.setValue(".$i.", 1, ".$val['nb'].");\n";
// Google's Geomap only supports up to 400 entries

View File

@ -1,6 +1,6 @@
<?php
/* Copyright (C) 2018 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -149,9 +149,7 @@ print '<input name="town" class="minwidth100" id="town" value="'.dol_escape_html
print '<tr class="oddeven"><td><label for="selectcountry_id">'.$langs->trans("Country").'</label></td><td class="maxwidthonsmartphone">';
print img_picto('', 'globe-americas', 'class="pictofixedwidth"');
print $form->select_country((GETPOSTISSET('country_id') ? GETPOSTINT('country_id') : getDolGlobalString('MAIN_INFO_ACCOUNTANT_COUNTRY')), 'country_id');
if ($user->admin) {
print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
}
print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
print '</td></tr>'."\n";
// State

View File

@ -2,7 +2,7 @@
/* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2005-2021 Laurent Destailleur <eldy@users.sourceforge.org>
* Copyright (C) 2011-2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -55,7 +55,7 @@ if (!isModEnabled('clicktodial')) {
* Actions
*/
if ($action == 'setvalue' && $user->admin) {
if ($action == 'setvalue'/* && $user->admin */) {
$result1 = dolibarr_set_const($db, "CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS", GETPOST("CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS"), 'chaine', 0, '', $conf->entity);
$result2 = dolibarr_set_const($db, "CLICKTODIAL_URL", GETPOST("CLICKTODIAL_URL"), 'chaine', 0, '', $conf->entity);
$result3 = dolibarr_set_const($db, "CLICKTODIAL_KEY_FOR_CIDLOOKUP", GETPOST("CLICKTODIAL_KEY_FOR_CIDLOOKUP"), 'chaine', 0, '', $conf->entity);

View File

@ -7,7 +7,7 @@
* Copyright (C) 2015 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2017 Rui Strecht <rui.strecht@aliartalentos.com>
* Copyright (C) 2023 Nick Fragoulis
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
@ -467,9 +467,7 @@ print '</td></tr>'."\n";
print '<tr class="oddeven"><td class="fieldrequired"><label for="selectcountry_id">'.$langs->trans("Country").'</label></td><td>';
print img_picto('', 'globe-americas', 'class="pictofixedwidth"');
print $form->select_country($mysoc->country_id, 'country_id', '', 0);
if ($user->admin) {
print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
}
print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
print '</td></tr>'."\n";
// Address

View File

@ -3,7 +3,7 @@
* Copyright (C) 2004-2013 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -260,7 +260,8 @@ $sql .= ", tms";
$sql .= ", entity";
$sql .= " FROM ".MAIN_DB_PREFIX."const";
$sql .= " WHERE entity IN (".$db->sanitize($user->entity.",".$conf->entity).")";
if ((empty($user->entity) || $user->admin) && $debug) {
if ((empty($user->entity)/* || $user->admin */) && $debug) {
// empty
} elseif (!GETPOST('visible') || GETPOST('visible') != 'all') {
// to force for superadmin to debug
$sql .= " AND visible = 1"; // We must always have this. Otherwise, array is too large and submitting data fails due to apache POST or GET limits

View File

@ -2418,7 +2418,7 @@ if ($id > 0) {
$valuetoshow = ($obj->code && $key != "Action".strtoupper($obj->code) ? $key : $obj->$value);
} elseif (!empty($obj->code_iso) && $value == 'label' && $tabname[$id] == 'c_currencies') {
$key = $langs->trans("Currency".strtoupper($obj->code_iso));
$valuetoshow = ($obj->code_iso && $key != "Currency".strtoupper($obj->code_iso) ? $key : $obj->$value);
$valuetoshow = (/* $obj->code_iso && */ $key != "Currency".strtoupper($obj->code_iso) ? $key : $obj->$value);
} elseif ($value == 'libelle' && $tabname[$id] == 'c_typent') {
$key = $langs->trans(strtoupper($obj->code));
$valuetoshow = ($key != strtoupper($obj->code) ? $key : $obj->$value);

View File

@ -5,7 +5,7 @@
* Copyright (C) 2005-2021 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006-2020 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2011-2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -66,7 +66,7 @@ if ($reshook < 0) {
}
if (empty($reshook)) {
if ($action == 'setvalue' && $user->admin) {
if ($action == 'setvalue' /* && $user->admin */) {
$error = 0;
$db->begin();

View File

@ -5,7 +5,7 @@
* Copyright (C) 2005 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2011-2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -55,7 +55,7 @@ $action = GETPOST('action', 'aZ09');
* Actions
*/
if ($action == 'setvalue' && $user->admin) {
if ($action == 'setvalue' /* && $user->admin */) {
$error = 0;
$db->begin();

View File

@ -6,7 +6,7 @@
* Copyright (C) 2006-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2011-2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -58,7 +58,7 @@ $action = GETPOST('action', 'aZ09');
* Actions
*/
if ($action == 'setvalue' && $user->admin) {
if ($action == 'setvalue' /* && $user->admin */) {
$error = 0;
$db->begin();

View File

@ -5,7 +5,7 @@
* Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006-2008 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2011-2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -56,7 +56,7 @@ $action = GETPOST('action', 'aZ09');
* Actions
*/
if ($action == 'setvalue' && $user->admin) {
if ($action == 'setvalue' /* && $user->admin */) {
$error = 0;
$db->begin();

View File

@ -5,7 +5,7 @@
* Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006-2011 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2011-2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -57,7 +57,7 @@ $action = GETPOST('action', 'aZ09');
* Actions
*/
if ($action == 'setvalue' && $user->admin) {
if ($action == 'setvalue' /* && $user->admin */) {
$error = 0;
$db->begin();

View File

@ -7,7 +7,7 @@
* Copyright (C) 2011-2016 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2019 Abbes Bahfir <dolipar@dolipar.org>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -58,7 +58,7 @@ $action = GETPOST('action', 'aZ09');
* Actions
*/
if ($action == 'setvalue' && $user->admin) {
if ($action == 'setvalue' /* && $user->admin */) {
$error = 0;
$db->begin();

View File

@ -3,7 +3,7 @@
* Copyright (C) 2009-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2016 Jonathan TISSEAU <jonathan.tisseau@86dev.fr>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
*
* 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
@ -362,7 +362,7 @@ if ($action == 'edit') {
print '<tr class="oddeven"><td>'.$langs->trans("MAIN_MAIL_SENDMODE").'</td><td>';
// SuperAdministrator access only
if (!isModEnabled('multicompany') || ($user->admin && !$user->entity)) {
if (!isModEnabled('multicompany') || (/* $user->admin && */ !$user->entity)) {
print $form->selectarray('MAIN_MAIL_SENDMODE_EMAILING', $listofmethods, getDolGlobalString('MAIN_MAIL_SENDMODE_EMAILING'));
} else {
$text = $listofmethods[getDolGlobalString('MAIN_MAIL_SENDMODE_EMAILING')];
@ -395,7 +395,7 @@ if ($action == 'edit') {
}
print '</td><td>';
// SuperAdministrator access only
if (!isModEnabled('multicompany') || ($user->admin && !$user->entity)) {
if (!isModEnabled('multicompany') || (/* $user->admin && */ !$user->entity)) {
print '<input class="flat minwidth300" id="MAIN_MAIL_SMTP_SERVER_EMAILING" name="MAIN_MAIL_SMTP_SERVER_EMAILING" size="18" value="' . $mainserver . '">';
print '<input type="hidden" id="MAIN_MAIL_SMTP_SERVER_EMAILING_sav" name="MAIN_MAIL_SMTP_SERVER_EMAILING_sav" value="' . $mainserver . '">';
print '<span id="smtp_server_mess" class="opacitymedium">' . $langs->trans("SeeLocalSendMailSetup") . '</span>';
@ -427,7 +427,7 @@ if ($action == 'edit') {
}
print '</td><td>';
// SuperAdministrator access only
if (!isModEnabled('multicompany') || ($user->admin && !$user->entity)) {
if (!isModEnabled('multicompany') || (/* $user->admin && */ !$user->entity)) {
print '<input class="flat" id="MAIN_MAIL_SMTP_PORT_EMAILING" name="MAIN_MAIL_SMTP_PORT_EMAILING" size="3" value="' . $mainport . '">';
print '<input type="hidden" id="MAIN_MAIL_SMTP_PORT_EMAILING_sav" name="MAIN_MAIL_SMTP_PORT_EMAILING_sav" value="' . $mainport . '">';
print '<span id="smtp_port_mess" class="opacitymedium">' . $langs->trans("SeeLocalSendMailSetup") . '</span>';
@ -444,7 +444,7 @@ if ($action == 'edit') {
if (!empty($conf->use_javascript_ajax) || in_array(getDolGlobalString('MAIN_MAIL_SENDMODE_EMAILING'), array('smtps', 'swiftmailer'))) {
print '<tr class="oddeven smtp_auth_method hideonmodemail hideifdefault"><td>'.$langs->trans("MAIN_MAIL_SMTPS_AUTH_TYPE").'</td><td>';
$vartosmtpstype = 'MAIN_MAIL_SMTPS_AUTH_TYPE_EMAILING';
if (!isModEnabled('multicompany') || ($user->admin && !$user->entity)) {
if (!isModEnabled('multicompany') || (/* $user->admin && */ !$user->entity)) {
// Note: Default value for MAIN_MAIL_SMTPS_AUTH_TYPE if not defined is 'LOGIN' (but login/pass may be empty and they won't be provided in such a case)
print '<input type="radio" id="radio_pw" name="'.$vartosmtpstype.'" value="LOGIN"'.(getDolGlobalString($vartosmtpstype, 'LOGIN') == 'LOGIN' ? ' checked' : '').'> ';
print '<label for="radio_pw" >'.$langs->trans("UseAUTHLOGIN").'</label>';
@ -473,7 +473,7 @@ if ($action == 'edit') {
$mainstmpid = getDolGlobalString('MAIN_MAIL_SMTPS_ID_EMAILING');
print '<tr class="drag drop oddeven hideifdefault"><td>' . $langs->trans("MAIN_MAIL_SMTPS_ID") . '</td><td>';
// SuperAdministrator access only
if (!isModEnabled('multicompany') || ($user->admin && !$user->entity)) {
if (!isModEnabled('multicompany') || (/* $user->admin && */ !$user->entity)) {
print '<input class="flat" name="MAIN_MAIL_SMTPS_ID_EMAILING" size="32" value="' . $mainstmpid . '">';
} else {
$htmltext = $langs->trans("ContactSuperAdminForChange");
@ -488,7 +488,7 @@ if ($action == 'edit') {
$mainsmtppw = getDolGlobalString('MAIN_MAIL_SMTPS_PW_EMAILING');
print '<tr class="drag drop oddeven smtp_pw hideifdefault"><td>' . $langs->trans("MAIN_MAIL_SMTPS_PW") . '</td><td>';
// SuperAdministrator access only
if (!isModEnabled('multicompany') || ($user->admin && !$user->entity)) {
if (!isModEnabled('multicompany') || (/* $user->admin && */ !$user->entity)) {
print '<input class="flat" type="password" name="MAIN_MAIL_SMTPS_PW_EMAILING" size="32" value="' . $mainsmtppw . '">';
} else {
$htmltext = $langs->trans("ContactSuperAdminForChange");
@ -503,7 +503,7 @@ if ($action == 'edit') {
print '<tr class="oddeven smtp_oauth_service hideifdefault"><td>'.$langs->trans("MAIN_MAIL_SMTPS_OAUTH_SERVICE").'</td><td>';
// SuperAdministrator access only
if (!isModEnabled('multicompany') || ($user->admin && !$user->entity)) {
if (!isModEnabled('multicompany') || (/* $user->admin && */ !$user->entity)) {
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
print $form->selectarray('MAIN_MAIL_SMTPS_OAUTH_SERVICE_EMAILING', $oauthservices, getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE_EMAILING'));
} else {

View File

@ -40,7 +40,8 @@ require_once DOL_DOCUMENT_ROOT."/core/class/html.formai.class.php";
$langs->loadLangs(array("admin", "website", "other"));
$arrayofaifeatures = getLitOfAIFeatures();
$arrayofaifeatures = getListOfAIFeatures();
$arrayofia = getListOfAIServices();
// Parameters
$action = GETPOST('action', 'aZ09');
@ -75,10 +76,15 @@ if (!class_exists('FormSetup')) {
}
$formSetup = new FormSetup($db);
$aiservice = getDolGlobalString('AI_API_SERVICE', 'chatgpt');
// Setup conf AI_PROMPT
$item = $formSetup->newItem('AI_CONFIGURATIONS_PROMPT');
$item->defaultFieldValue = '';
// Setup conf for AI model
$formSetup->formHiddenInputs['action'] = "updatefeaturemodel";
foreach ($arrayofaifeatures as $key => $val) {
$item = $formSetup->newItem('AI_API_'.strtoupper($aiservice).'_MODEL_'.$val["function"]); // Name of constant must end with _KEY so it is encrypted when saved into database.
$item->nameText = $langs->trans("AI_API_MODEL_".$val["function"]);
$item->cssClass = 'minwidth500 input';
}
$setupnotempty += count($formSetup->items);
@ -94,6 +100,11 @@ $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
$currentConfigurationsJson = getDolGlobalString('AI_CONFIGURATIONS_PROMPT');
$currentConfigurations = json_decode($currentConfigurationsJson, true);
if ($action == 'updatefeaturemodel' && !empty($user->admin)) {
$formSetup->saveConfFromPost();
$action = 'edit';
}
if ($action == 'update' && $cancel) {
$action = 'edit';
}
@ -399,6 +410,13 @@ if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
print '<br>';
}
if ($action == 'edit' || $action == 'create' || $action == 'deleteproperty') {
print load_fiche_titre($langs->trans("AIModelForFeature", $arrayofia[$aiservice]), $newbutton, '');
print $formSetup->generateOutput(true);
}
if (empty($setupnotempty)) {
print '<br>'.$langs->trans("NothingToSetup");
}

View File

@ -41,7 +41,6 @@ require_once DOL_DOCUMENT_ROOT."/ai/lib/ai.lib.php";
$langs->loadLangs(array("admin", "website", "other"));
$arrayofaifeatures = getLitOfAIFeatures();
// Parameters
$action = GETPOST('action', 'aZ09');
@ -68,13 +67,10 @@ if (!class_exists('FormSetup')) {
$formSetup = new FormSetup($db);
// List all available IA
$arrayofia = array(
'-1' => $langs->trans('SelectAService'),
'chatgpt' => 'ChatGPT',
'groq' => 'Groq',
'custom' => 'Custom'
//'gemini' => 'Gemini'
);
$arrayofia = getListOfAIServices();
// List all available features
$arrayofaifeatures = getListOfAIFeatures();
$item = $formSetup->newItem('AI_API_SERVICE'); // Name of constant must end with _KEY so it is encrypted when saved into database.
$item->setAsSelect($arrayofia);

View File

@ -158,7 +158,7 @@ class Ai
} elseif ($this->apiService == 'groq') {
$model = getDolGlobalString('AI_API_GROK_MODEL_TRANSCRIPT', 'mixtral-8x7b-32768'); // 'llama3-8b-8192', 'gemma-7b-it'
} elseif ($this->apiService == 'custom') {
$model = getDolGlobalString('AI_API_CUSTOM_TRANSCRIPT', 'whisper-1');
$model = getDolGlobalString('AI_API_CUSTOM_MODEL_TRANSCRIPT', 'whisper-1');
}
} elseif ($function == 'translation') {
if ($this->apiService == 'chatgpt') {
@ -166,7 +166,7 @@ class Ai
} elseif ($this->apiService == 'groq') {
$model = getDolGlobalString('AI_API_GROK_MODEL_TRANSLATE', 'mixtral-8x7b-32768'); // 'llama3-8b-8192', 'gemma-7b-it'
} elseif ($this->apiService == 'custom') {
$model = getDolGlobalString('AI_API_CUSTOM_TRANSLATE', 'whisper-1');
$model = getDolGlobalString('AI_API_CUSTOM_MODEL_TRANSLATE', 'whisper-1');
}
} else { // else textgeneration...
if ($this->apiService == 'chatgpt') {

View File

@ -29,25 +29,45 @@
*
* @return array<string,array<string,string>>
*/
function getLitOfAIFeatures()
function getListOfAIFeatures()
{
global $langs;
$arrayofaifeatures = array(
'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto'=>'', 'status'=>'dolibarr'),
'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto'=>'', 'status'=>'dolibarr'),
'textgeneration' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("Other").')', 'picto'=>'', 'status'=>'notused'),
'texttranslation' => array('label' => $langs->trans('TextTranslation'), 'picto'=>'', 'status'=>'dolibarr'),
'imagegeneration' => array('label' => 'ImageGeneration', 'picto'=>'', 'status'=>'notused'),
'videogeneration' => array('label' => 'VideoGeneration', 'picto'=>'', 'status'=>'notused'),
'audiogeneration' => array('label' => 'AudioGeneration', 'picto'=>'', 'status'=>'notused'),
'transcription' => array('label' => 'AudioTranscription', 'picto'=>'', 'status'=>'notused'),
'translation' => array('label' => 'AudioTranslation', 'picto'=>'', 'status'=>'notused')
'textgenerationemail' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("EmailContent").')', 'picto'=>'', 'status'=>'dolibarr', 'function' => 'TEXT'),
'textgenerationwebpage' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("WebsitePage").')', 'picto'=>'', 'status'=>'dolibarr', 'function' => 'TEXT'),
'textgeneration' => array('label' => $langs->trans('TextGeneration').' ('.$langs->trans("Other").')', 'picto'=>'', 'status'=>'notused', 'function' => 'TEXT'),
'texttranslation' => array('label' => $langs->trans('TextTranslation'), 'picto'=>'', 'status'=>'dolibarr', 'function' => 'TEXT'),
'imagegeneration' => array('label' => 'ImageGeneration', 'picto'=>'', 'status'=>'notused', 'function' => 'IMAGE'),
'videogeneration' => array('label' => 'VideoGeneration', 'picto'=>'', 'status'=>'notused', 'function' => 'VIDEO'),
'audiogeneration' => array('label' => 'AudioGeneration', 'picto'=>'', 'status'=>'notused', 'function' => 'AUDIO'),
'transcription' => array('label' => 'AudioTranscription', 'picto'=>'', 'status'=>'notused', 'function' => 'TRANSCRIPT'),
'translation' => array('label' => 'AudioTranslation', 'picto'=>'', 'status'=>'notused', 'function' => 'TRANSLATE')
);
return $arrayofaifeatures;
}
/**
* Get list of available ai services
*
* @return array<int|string,mixed>
*/
function getListOfAIServices()
{
global $langs;
$arrayofia = array(
'-1' => $langs->trans('SelectAService'),
'chatgpt' => 'ChatGPT',
'groq' => 'Groq',
'custom' => 'Custom'
//'gemini' => 'Gemini'
);
return $arrayofia;
}
/**
* Prepare admin pages header
*

View File

@ -9,7 +9,7 @@
* Copyright (C) 2015-2017 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
* Copyright (C) 2019 JC Prieto <jcprieto@virtual20.com><prietojc@gmail.com>
* Copyright (C) 2022-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2022-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
@ -535,7 +535,7 @@ class Account extends CommonObject
* @param int $fk_bank To search using bank transaction id
* @param int $url_id To search using link to
* @param string $type To search using type
* @return int<-1,-1>|array<int,array{0:string,1:int,2:string,3:int,url:string,url_id:int,label:string,type:int,fk_bank:int}> Array of links array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> ) or -1 on error
* @return int<-1,-1>|array<int,array{0:string,1:int,2:string,3:int,url:string,url_id:int,label:string,type:string,fk_bank:int}> Array of links array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> ) or -1 on error
*/
public function get_url($fk_bank = 0, $url_id = 0, $type = '')
{

View File

@ -1413,8 +1413,9 @@ if (empty($reshook)) {
$object->date_pointoftax = $date_pointoftax;
$object->note_public = trim(GETPOST('note_public', 'restricthtml'));
$object->note_private = trim(GETPOST('note_private', 'restricthtml'));
$object->ref_customer = GETPOST('ref_client');
$object->ref_client = $object->ref_customer;
$object->model_pdf = GETPOST('model');
$object->fk_project = GETPOSTINT('projectid');
$object->cond_reglement_id = (GETPOSTINT('type') == 3 ? 1 : GETPOST('cond_reglement_id'));
@ -3319,7 +3320,13 @@ if ($action == 'create') {
$objectsrc->fetch_thirdparty();
$projectid = (!empty($projectid) ? $projectid : $objectsrc->fk_project);
$ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : (!empty($objectsrc->ref_customer) ? $objectsrc->ref_customer : ''));
// Propagate ref customer of src object to the invoice ?
if (getDolGlobalString("INVOICE_DO_NOT_PROPAGATE_REF_CUSTOMER_Of_SRC_TO_INVOICE")) {
$ref_client = "";
} else {
$ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : (!empty($objectsrc->ref_customer) ? $objectsrc->ref_customer : ''));
}
// only if socid not filled else it's already done above
if (empty($socid)) {
@ -3329,8 +3336,6 @@ if ($action == 'create') {
$dateinvoice = (empty($dateinvoice) ? (!getDolGlobalString('MAIN_AUTOFILL_DATE') ? -1 : '') : $dateinvoice);
if ($element == 'expedition') {
$ref_client = (!empty($objectsrc->ref_customer) ? $objectsrc->ref_customer : '');
$elem = $subelem = $objectsrc->origin;
$expeoriginid = $objectsrc->origin_id;
dol_include_once('/'.$elem.'/class/'.$subelem.'.class.php');
@ -4235,21 +4240,23 @@ if ($action == 'create') {
echo ' - '.$langs->trans('LatestRelatedBill').' '.end($objectsrc->linkedObjects['facture'])->getNomUrl(1);
}
echo '</td></tr>';
print '<tr><td>'.$langs->trans('AmountHT').'</td><td colspan="2">'.price($objectsrc->total_ht).'</td></tr>';
print '<tr><td>'.$langs->trans('AmountVAT').'</td><td colspan="2">'.price($objectsrc->total_tva)."</td></tr>";
print '<tr><td>'.$langs->trans('AmountHT').'</td><td colspan="2">'.price($objectsrc->total_ht, 1, $langs, 1, -1, '', $conf->currency).'</td></tr>';
print '<tr><td>'.$langs->trans('AmountVAT').'</td><td colspan="2">'.price($objectsrc->total_tva, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
print '<tr><td>'.$langs->transcountry("AmountLT1", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax1)."</td></tr>";
print '<tr><td>'.$langs->transcountry("AmountLT1", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax1, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
}
if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
print '<tr><td>'.$langs->transcountry("AmountLT2", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax2)."</td></tr>";
print '<tr><td>'.$langs->transcountry("AmountLT2", $mysoc->country_code).'</td><td colspan="2">'.price($objectsrc->total_localtax2, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
}
print '<tr><td>'.$langs->trans('AmountTTC').'</td><td colspan="2">'.price($objectsrc->total_ttc)."</td></tr>";
print '<tr><td>'.$langs->trans('AmountTTC').'</td><td colspan="2">'.price($objectsrc->total_ttc, 1, $langs, 1, -1, '', $conf->currency)."</td></tr>";
if (isModEnabled('multicurrency')) {
print '<tr><td>'.$langs->trans('MulticurrencyAmountHT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ht).'</td></tr>';
print '<tr><td>'.$langs->trans('MulticurrencyAmountVAT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_tva)."</td></tr>";
print '<tr><td>'.$langs->trans('MulticurrencyAmountTTC').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ttc)."</td></tr>";
if (isModEnabled('multicurrency') && $objectsrc->multicurrency_code != $conf->currency) {
//var_dump($objectsrc);
print '<tr><td>'.$langs->trans('MulticurrencyAmountHT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ht, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code).'</td></tr>';
print '<tr><td>'.$langs->trans('MulticurrencyAmountVAT').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_tva, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code)."</td></tr>";
print '<tr><td>'.$langs->trans('MulticurrencyAmountTTC').'</td><td colspan="2">'.price($objectsrc->multicurrency_total_ttc, 1, $langs, 1, -1, '', $objectsrc->multicurrency_code)."</td></tr>";
}
}

View File

@ -0,0 +1,342 @@
<?php
/* Copyright (C) 2007-2024 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
*
* 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/core/ajax/ajaxextrafield.php
* \ingroup extrafield
* \brief This script returns content of extrafield
*/
if (!defined('NOTOKENRENEWAL')) {
// Disables token renewal
define('NOTOKENRENEWAL', 1);
}
if (!defined('NOREQUIREMENU')) {
define('NOREQUIREMENU', '1');
}
if (!defined('NOREQUIREHTML')) {
define('NOREQUIREHTML', '1');
}
if (!defined('NOREQUIREAJAX')) {
define('NOREQUIREAJAX', '1');
}
if (!defined('NOHEADERNOFOOTER')) {
define('NOHEADERNOFOOTER', '1');
}
include '../../main.inc.php';
include_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var Translate $langs
* @var User $user
*/
// object id
$objectid = GETPOST('objectid', 'aZ09');
// 'module' or 'myobject@mymodule', 'mymodule_myobject'
$objecttype = GETPOST('objecttype', 'aZ09arobase');
$objectkey = GETPOST('objectkey', 'restricthtml');
$search = GETPOST('search', 'restricthtml');
$page = GETPOSTINT('page');
$mode = GETPOSTINT('mode');
$value = GETPOST('value', 'alphanohtml');
$limit = 10;
$offset = (($page - 1) * $limit);
$element_ref = '';
if (is_numeric($objectid)) {
$objectid = (int) $objectid;
} else {
$element_ref = $objectid;
$objectid = 0;
}
// Load object according to $element
$object = fetchObjectByElement($objectid, $objecttype, $element_ref);
if (empty($object->element)) {
httponly_accessforbidden('Failed to get object with fetchObjectByElement(id=' . $objectid . ', objecttype=' . $objecttype . ')');
}
$module = $object->module;
$element = $object->element;
$usesublevelpermission = ($module != $element ? $element : '');
if ($usesublevelpermission && !$user->hasRight($module, $element)) { // There is no permission on object defined, we will check permission on module directly
$usesublevelpermission = '';
}
// print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
// Security check
restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission);
/*
* View
*/
top_httphead();
$data = [
'results' => [],
'pagination' => [
'more' => true,
]
];
if ($page == 1) {
$data['results'][] = [
'id' => -1,
'text' => '&nbsp;',
];
}
$i = 0;
if ($object instanceof CommonObject) {
$extrafields = new ExtraFields($db);
$extrafields->fetch_name_optionals_label($element);
$options = $extrafields->attributes[$element]['param'][$objectkey]['options'];
if (is_array($options)) {
$tmpparamoptions = array_keys($options);
$paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
$InfoFieldList = explode(":", $paramoptions[0], 5);
// 0 : tableName
// 1 : label field name
// 2 : key fields name (if different of rowid)
// optional parameters...
// 3 : key field parent (for dependent lists). How this is used ?
// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
// 5 : string category type. This replace the filter.
// 6 : ids categories list separated by comma for category root. This replace the filter.
// 7 : sort field (not used here but used into format for commobject)
// If there is a filter, we extract it by taking all content inside parenthesis.
if (! empty($InfoFieldList[4])) {
$pos = 0; // $pos will be position of ending filter
$parenthesisopen = 0;
while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
if (substr($InfoFieldList[4], $pos, 1) == '(') {
$parenthesisopen++;
}
if (substr($InfoFieldList[4], $pos, 1) == ')') {
$parenthesisopen--;
}
$pos++;
}
$tmpbefore = substr($InfoFieldList[4], 0, $pos);
$tmpafter = substr($InfoFieldList[4], $pos + 1);
//var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
$InfoFieldList[4] = $tmpbefore;
if ($tmpafter !== '') {
$InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
}
// Fix better compatibility with some old extrafield syntax filter "(field=123)"
$reg = array();
if (preg_match('/^\(?([a-z0-9]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
$InfoFieldList[4] = '(' . $reg[1] . ':' . $reg[2] . ':' . $reg[3] . ')';
}
//var_dump($InfoFieldList);
}
$parentName = '';
$parentField = '';
$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
$keyList .= ', ' . $parentField;
}
if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
if (strpos($InfoFieldList[4], 'extra.') !== false) {
$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
} else {
$keyList = $InfoFieldList[2] . ' as rowid';
}
}
$filter_categorie = false;
if (count($InfoFieldList) > 5) {
if ($InfoFieldList[0] == 'categorie') {
$filter_categorie = true;
}
}
if (!$filter_categorie) {
$fields_label = explode('|', $InfoFieldList[1]);
if (count($fields_label) > 0) {
$keyList .= ', ';
$keyList .= implode(', ', $fields_label);
}
$sqlwhere = '';
$sql = "SELECT " . $keyList;
$sql .= ' FROM ' . $db->prefix() . $InfoFieldList[0];
// Add filter from 4th field
if (!empty($InfoFieldList[4])) {
$tags = [];
preg_match_all('/\$(.*?)\$/', $InfoFieldList[4], $tags);
foreach ($tags[0] as $keytag => $valuetag) {
$property = strtolower($tags[1][$keytag]);
if (strpos($InfoFieldList[4], $valuetag) !== false && property_exists($object, $property) && !empty($object->$property)) {
$InfoFieldList[4] = str_replace($valuetag, (string) $object->$property, $InfoFieldList[4]);
} else {
$InfoFieldList[4] = str_replace($valuetag, '0', $InfoFieldList[4]);
}
}
// can use current entity filter
if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
$InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
}
// can use SELECT request
if (strpos($InfoFieldList[4], '$SEL$') !== false) {
$InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
}
// can use MODE request (list or view)
if (strpos($InfoFieldList[4], '$MODE$') !== false) {
$InfoFieldList[4] = str_replace('$MODE$', (string) $mode, $InfoFieldList[4]);
}
// current object id can be use into filter
if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
$InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
} else {
$InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
}
// We have to join on extrafield table
$errstr = '';
if (strpos($InfoFieldList[4], 'extra.') !== false) {
$sql .= ' as main, ' . $db->sanitize($db->prefix() . $InfoFieldList[0]) . '_extrafields as extra';
$sqlwhere .= " WHERE extra.fk_object = main." . $db->sanitize($InfoFieldList[2]);
$sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
} else {
$sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
}
} else {
$sqlwhere .= ' WHERE 1=1';
}
// Some tables may have field, some other not. For the moment we disable it.
if (in_array($InfoFieldList[0], array('tablewithentity'))) {
$sqlwhere .= ' AND entity = ' . ((int) $conf->entity);
}
if ($search) {
if ($fields_label) {
$sqlwhere .= " " . natural_search($fields_label, $search, 0);
}
}
$sql .= $sqlwhere;
$orderfields = explode('|', $InfoFieldList[1]);
$keyList = $InfoFieldList[1];
if (count($orderfields)) {
$keyList = implode(', ', $orderfields);
}
$sql .= $db->order($keyList);
$sql .= $db->plimit($limit, $offset);
$data['sql'] = $sql;
$resql = $db->query($sql);
if ($resql) {
// $out .= '<option value="0">&nbsp;</option>';
$num = $db->num_rows($resql);
$i = 0;
while ($i < $num) {
$labeltoshow = '';
$obj = $db->fetch_object($resql);
// Several field into label (eq table:code|label:rowid)
$notrans = false;
$fields_label = explode('|', $InfoFieldList[1]);
if (count($fields_label) > 1) {
$notrans = true;
foreach ($fields_label as $field_toshow) {
$labeltoshow .= $obj->$field_toshow . ' ';
}
} else {
$labeltoshow = $obj->{$InfoFieldList[1]};
}
if ($value == $obj->rowid) {
if (!$notrans) {
foreach ($fields_label as $field_toshow) {
$translabel = $langs->trans($obj->$field_toshow);
$labeltoshow = $translabel . ' ';
}
}
// $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
$data['results'][] = [
'id' => $obj->rowid,
'text' => $labeltoshow,
];
} else {
if (!$notrans) {
$translabel = $langs->trans($obj->{$InfoFieldList[1]});
$labeltoshow = $translabel;
}
if (empty($labeltoshow)) {
$labeltoshow = '(not defined)';
}
if (!empty($InfoFieldList[3]) && $parentField) {
$parent = $parentName . ':' . $obj->{$parentField};
}
// $out .= '<option value="'.$obj->rowid.'"';
// $out .= ($value == $obj->rowid ? ' selected' : '');
// $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
// $out .= '>'.$labeltoshow.'</option>';
$data['results'][] = [
'id' => $obj->rowid,
'text' => $labeltoshow,
];
}
$i++;
}
$db->free($resql);
} else {
dol_syslog('Error in request ' . $db->lasterror() . '. Check setup of extra parameters.', LOG_ERR);
}
} else {
require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
$form = new Form($db);
$categories = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
// $out .= '<option value="0">&nbsp;</option>';
// if (is_array($categories)) {
// foreach ($categories as $category_key => $category_value) {
// $out .= '<option value="'.$category_key.'"';
// $out .= ($value == $category_key ? ' selected' : '');
// $out .= '>'.$category_value.'</option>';
// }
// }
}
}
}
if ($page > 1 && $i < 9) {
$data['pagination'] = [
'more' => false,
];
}
print json_encode($data);
$db->close();

View File

@ -2072,9 +2072,9 @@ abstract class CommonObject
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Load the product with id $this->fk_product into this->product
* Load the product with id $this->fk_product into this->product
*
* @return int<-1,1> Return integer <0 if KO, >=0 if OK
* @return int<-1,1> Return integer <0 if KO, >=0 if OK
*/
public function fetch_product()
{
@ -2096,10 +2096,10 @@ abstract class CommonObject
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Load the user with id $userid into this->user
* Load the user with id $userid into this->user
*
* @param int $userid Id du contact
* @return int<-1,1> Return integer <0 if KO, >0 if OK
* @param int $userid Id du contact
* @return int<-1,1> Return integer <0 if KO, >0 if OK
*/
public function fetch_user($userid)
{
@ -7954,6 +7954,8 @@ abstract class CommonObject
} else {
$sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
}
$sql .= ' LIMIT ' . getDolGlobalInt('MAIN_EXTRAFIELDS_LIMIT_SELLIST_SQL', 1000);
// print $sql;
dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
$resql = $this->db->query($sql);

View File

@ -1,5 +1,5 @@
<?php
/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.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
@ -142,7 +142,7 @@ trait DolDeprecationHandler
if ($this->isDeprecatedReportingEnabled()) {
trigger_error($msg, E_USER_DEPRECATED);
}
return isset($newProperty);
return isset($this->$newProperty);
} elseif ($this->isDynamicPropertiesEnabled()) {
return isset($this->$name);
}
@ -203,7 +203,7 @@ trait DolDeprecationHandler
{
// By default, if enableDynamicProperties is set, use that value.
if (property_exists($this, 'enableDynamicProperties')) {
if (property_exists($this, 'enableDynamicProperties')) { // @phpstan-ignore-line
// If the property exists, then we use it.
return (bool) $this->enableDynamicProperties;
}

View File

@ -1364,215 +1364,263 @@ class ExtraFields
}
} elseif ($type == 'sellist') { // List of values selected from a table (1 choice)
$out = '';
if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
$out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
}
$out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
if (is_array($param['options'])) {
$tmpparamoptions = array_keys($param['options']);
$paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
$InfoFieldList = explode(":", $paramoptions[0], 5);
// 0 : tableName
// 1 : label field name
// 2 : key fields name (if different of rowid)
// optional parameters...
// 3 : key field parent (for dependent lists). How this is used ?
// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
// 5 : string category type. This replace the filter.
// 6 : ids categories list separated by comma for category root. This replace the filter.
// 7 : sort field (not used here but used into format for commobject)
// If there is a filter, we extract it by taking all content inside parenthesis.
if (! empty($InfoFieldList[4])) {
$pos = 0; // $pos will be position of ending filter
$parenthesisopen = 0;
while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
if (substr($InfoFieldList[4], $pos, 1) == '(') {
$parenthesisopen++;
}
if (substr($InfoFieldList[4], $pos, 1) == ')') {
$parenthesisopen--;
}
$pos++;
}
$tmpbefore = substr($InfoFieldList[4], 0, $pos);
$tmpafter = substr($InfoFieldList[4], $pos + 1);
//var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
$InfoFieldList[4] = $tmpbefore;
if ($tmpafter !== '') {
$InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
}
// Fix better compatibility with some old extrafield syntax filter "(field=123)"
$reg = array();
if (preg_match('/^\(?([a-z0-9]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
$InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
}
//var_dump($InfoFieldList);
}
//$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
$parentName = '';
$parentField = '';
$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
$keyList .= ', '.$parentField;
}
if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
if (strpos($InfoFieldList[4], 'extra.') !== false) {
$keyList = 'main.'.$InfoFieldList[2].' as rowid';
} else {
$keyList = $InfoFieldList[2].' as rowid';
}
}
$filter_categorie = false;
if (count($InfoFieldList) > 5) {
if ($InfoFieldList[0] == 'categorie') {
$filter_categorie = true;
}
}
if (!$filter_categorie) {
$fields_label = explode('|', $InfoFieldList[1]);
if (is_array($fields_label)) {
$keyList .= ', ';
$keyList .= implode(', ', $fields_label);
}
$sqlwhere = '';
$sql = "SELECT ".$keyList;
$sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
// Add filter from 4th field
if (!empty($InfoFieldList[4])) {
// can use current entity filter
if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
$InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
}
// can use SELECT request
if (strpos($InfoFieldList[4], '$SEL$') !== false) {
$InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
}
// current object id can be use into filter
if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
$InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
} else {
$InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
}
// We have to join on extrafield table
$errstr = '';
if (strpos($InfoFieldList[4], 'extra.') !== false) {
$sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
$sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
$sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
} else {
$sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
}
} else {
$sqlwhere .= ' WHERE 1=1';
}
// Add Usf filter on second line
/*
if ($Usf) {
$errorstr = '';
$sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
if (!$errorstr) {
$sqlwhere .= $sqlusf;
} else {
$sqlwhere .= " AND invalid_usf_filter_of_extrafield";
}
}
*/
// Some tables may have field, some other not. For the moment we disable it.
if (in_array($InfoFieldList[0], array('tablewithentity'))) {
$sqlwhere .= ' AND entity = '.((int) $conf->entity);
}
$sql .= $sqlwhere;
$sql .= $this->db->order(implode(',', $fields_label));
//print $sql;
dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
$out .= '<option value="0">&nbsp;</option>';
$num = $this->db->num_rows($resql);
$i = 0;
while ($i < $num) {
$labeltoshow = '';
$obj = $this->db->fetch_object($resql);
// Several field into label (eq table:code|label:rowid)
$notrans = false;
$fields_label = explode('|', $InfoFieldList[1]);
if (is_array($fields_label) && count($fields_label) > 1) {
$notrans = true;
foreach ($fields_label as $field_toshow) {
$labeltoshow .= $obj->$field_toshow.' ';
if (!empty($conf->use_javascript_ajax)) {
if (getDolGlobalString('MAIN_EXTRAFIELDS_ENABLE_NEW_SELECT2')) {
$out .= "
<script>
$(document).ready(function () {
$('#".$keyprefix.$key.$keysuffix."').select2({
ajax: {
url: '".DOL_URL_ROOT.'/core/ajax/ajaxextrafield.php'."',
dataType: 'json',
delay: 250, // wait 250 milliseconds before triggering the request
data: function (params) {
var query = {
search: params.term,
page: params.page || 1,
objecttype: '".$extrafieldsobjectkey."',
objectid: '".$object->id."',
objectkey: '".$key."',
mode: '".$mode."',
value: '".$value."'
}
return query;
}
} else {
$labeltoshow = $obj->{$InfoFieldList[1]};
}
})
});
</script>";
$out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
$out .= ' <option value="'.$value.'" selected>'.$this->showOutputField($key, $value, $moreparam, $extrafieldsobjectkey).'</option>';
$out .= '</select>';
} elseif (!getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
$out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
}
}
if (!getDolGlobalString('MAIN_EXTRAFIELDS_ENABLE_NEW_SELECT2')) {
$out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
if (is_array($param['options'])) {
$tmpparamoptions = array_keys($param['options']);
$paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
if ($value == $obj->rowid) {
if (!$notrans) {
foreach ($fields_label as $field_toshow) {
$translabel = $langs->trans($obj->$field_toshow);
$labeltoshow = $translabel.' ';
$InfoFieldList = explode(":", $paramoptions[0], 5);
// 0 : tableName
// 1 : label field name
// 2 : key fields name (if different of rowid)
// optional parameters...
// 3 : key field parent (for dependent lists). How this is used ?
// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
// 5 : string category type. This replace the filter.
// 6 : ids categories list separated by comma for category root. This replace the filter.
// 7 : sort field (not used here but used into format for commobject)
// If there is a filter, we extract it by taking all content inside parenthesis.
if (! empty($InfoFieldList[4])) {
$pos = 0; // $pos will be position of ending filter
$parenthesisopen = 0;
while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
if (substr($InfoFieldList[4], $pos, 1) == '(') {
$parenthesisopen++;
}
if (substr($InfoFieldList[4], $pos, 1) == ')') {
$parenthesisopen--;
}
$pos++;
}
$tmpbefore = substr($InfoFieldList[4], 0, $pos);
$tmpafter = substr($InfoFieldList[4], $pos + 1);
//var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
$InfoFieldList[4] = $tmpbefore;
if ($tmpafter !== '') {
$InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
}
// Fix better compatibility with some old extrafield syntax filter "(field=123)"
$reg = array();
if (preg_match('/^\(?([a-z0-9]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
$InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
}
//var_dump($InfoFieldList);
}
//$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
$parentName = '';
$parentField = '';
$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
$keyList .= ', '.$parentField;
}
if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
if (strpos($InfoFieldList[4], 'extra.') !== false) {
$keyList = 'main.'.$InfoFieldList[2].' as rowid';
} else {
$keyList = $InfoFieldList[2].' as rowid';
}
}
$filter_categorie = false;
if (count($InfoFieldList) > 5) {
if ($InfoFieldList[0] == 'categorie') {
$filter_categorie = true;
}
}
if (!$filter_categorie) {
$fields_label = explode('|', $InfoFieldList[1]);
if (is_array($fields_label)) {
$keyList .= ', ';
$keyList .= implode(', ', $fields_label);
}
$sqlwhere = '';
$sql = "SELECT ".$keyList;
$sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
// Add filter from 4th field
if (!empty($InfoFieldList[4])) {
if (is_object($object)) {
$tags = [];
preg_match_all('/\$(.*?)\$/', $InfoFieldList[4], $tags);
foreach ($tags[0] as $keytag => $valuetag) {
$property = strtolower($tags[1][$keytag]);
if (strpos($InfoFieldList[4], $valuetag) !== false && property_exists($object, $property) && !empty($object->$property)) {
$InfoFieldList[4] = str_replace($valuetag, (string) $object->$property, $InfoFieldList[4]);
} else {
$InfoFieldList[4] = str_replace($valuetag, '0', $InfoFieldList[4]);
}
}
$out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
} else {
if (!$notrans) {
$translabel = $langs->trans($obj->{$InfoFieldList[1]});
$labeltoshow = $translabel;
}
if (empty($labeltoshow)) {
$labeltoshow = '(not defined)';
}
if (!empty($InfoFieldList[3]) && $parentField) {
$parent = $parentName.':'.$obj->{$parentField};
}
$out .= '<option value="'.$obj->rowid.'"';
$out .= ($value == $obj->rowid ? ' selected' : '');
$out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
$out .= '>'.$labeltoshow.'</option>';
}
// can use current entity filter
if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
$InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
}
// can use SELECT request
if (strpos($InfoFieldList[4], '$SEL$') !== false) {
$InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
}
// can use MODE request (list or view)
if (strpos($InfoFieldList[4], '$MODE$') !== false) {
$InfoFieldList[4] = str_replace('$MODE$', (string) $mode, $InfoFieldList[4]);
}
$i++;
// current object id can be use into filter
if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
$InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
} else {
$InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
}
// We have to join on extrafield table
$errstr = '';
if (strpos($InfoFieldList[4], 'extra.') !== false) {
$sql .= ' as main, '.$this->db->sanitize($this->db->prefix().$InfoFieldList[0]).'_extrafields as extra';
$sqlwhere .= " WHERE extra.fk_object = main.".$this->db->sanitize($InfoFieldList[2]);
$sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
} else {
$sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
}
} else {
$sqlwhere .= ' WHERE 1=1';
}
// Add Usf filter on second line
/*
if ($Usf) {
$errorstr = '';
$sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
if (!$errorstr) {
$sqlwhere .= $sqlusf;
} else {
$sqlwhere .= " AND invalid_usf_filter_of_extrafield";
}
}
*/
// Some tables may have field, some other not. For the moment we disable it.
if (in_array($InfoFieldList[0], array('tablewithentity'))) {
$sqlwhere .= ' AND entity = '.((int) $conf->entity);
}
$sql .= $sqlwhere;
$sql .= $this->db->order(implode(', ', $fields_label));
$sql .= ' LIMIT ' . getDolGlobalInt('MAIN_EXTRAFIELDS_LIMIT_SELLIST_SQL', 1000);
//print $sql;
dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
$out .= '<option value="0">&nbsp;</option>';
$num = $this->db->num_rows($resql);
$i = 0;
while ($i < $num) {
$labeltoshow = '';
$obj = $this->db->fetch_object($resql);
// Several field into label (eq table:code|label:rowid)
$notrans = false;
$fields_label = explode('|', $InfoFieldList[1]);
if (is_array($fields_label) && count($fields_label) > 1) {
$notrans = true;
foreach ($fields_label as $field_toshow) {
$labeltoshow .= $obj->$field_toshow.' ';
}
} else {
$labeltoshow = $obj->{$InfoFieldList[1]};
}
if ($value == $obj->rowid) {
if (!$notrans) {
foreach ($fields_label as $field_toshow) {
$translabel = $langs->trans($obj->$field_toshow);
$labeltoshow = $translabel.' ';
}
}
$out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
} else {
if (!$notrans) {
$translabel = $langs->trans($obj->{$InfoFieldList[1]});
$labeltoshow = $translabel;
}
if (empty($labeltoshow)) {
$labeltoshow = '(not defined)';
}
if (!empty($InfoFieldList[3]) && $parentField) {
$parent = $parentName.':'.$obj->{$parentField};
}
$out .= '<option value="'.$obj->rowid.'"';
$out .= ($value == $obj->rowid ? ' selected' : '');
$out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
$out .= '>'.$labeltoshow.'</option>';
}
$i++;
}
$this->db->free($resql);
} else {
print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
}
$this->db->free($resql);
} else {
print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
}
} else {
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
$data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
$out .= '<option value="0">&nbsp;</option>';
if (is_array($data)) {
foreach ($data as $data_key => $data_value) {
$out .= '<option value="'.$data_key.'"';
$out .= ($value == $data_key ? ' selected' : '');
$out .= '>'.$data_value.'</option>';
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
$data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
$out .= '<option value="0">&nbsp;</option>';
if (is_array($data)) {
foreach ($data as $data_key => $data_value) {
$out .= '<option value="'.$data_key.'"';
$out .= ($value == $data_key ? ' selected' : '');
$out .= '>'.$data_value.'</option>';
}
}
}
}
$out .= '</select>';
}
$out .= '</select>';
} elseif ($type == 'checkbox') {
$value_arr = $value;
if (!is_array($value)) {

View File

@ -1585,16 +1585,16 @@ function dol_get_object_properties($obj, $properties = [])
/**
* Create a clone of instance of object (new instance with same value for each properties)
* Create a clone of instance of object (new instance with same value for each properties)
* With native = 0: Property that are references are different memory area in the new object (full isolation clone). This means $this->object of new object may not be valid (except this->db that is voluntarly kept).
* With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db of new object is still valid but point to same this->db than original object.
* With native = 2: Property that are reference are different memory area in the new object (full isolation clone). Only scalar and array values are cloned. This means method are not availables and $this->db of new object is not valid.
*
* @template T
*
* @param T $object Object to clone
* @param T $object Object to clone
* @param int $native 0=Full isolation method, 1=Native PHP method, 2=Full isolation method keeping only scalar and array properties (recommended)
* @return T Clone object
* @return T Clone object
*
* @see https://php.net/manual/language.oop5.cloning.php
* @phan-suppress PhanTypeExpectedObjectPropAccess

View File

@ -986,7 +986,7 @@ if ($object->id > 0 || !empty($object->ref)) {
print '</td>';
print '<td>';
print '<input type="text" class="minwidth300 csslotnumber" name="lot_number'.$suffix.'" value="'.(GETPOSTISSET('lot_number'.$suffix) ? GETPOST('lot_number'.$suffix) : $objd->batch).'" list="lot_number'.$suffix.'">';
print '<input type="text" class="inputlotnumber quatrevingtquinzepercent csslotnumber" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.(GETPOSTISSET('lot_number'.$suffix) ? GETPOST('lot_number'.$suffix) : $objd->batch).'" list="lot_number'.$suffix.'">';
print $formproduct->selectLotDataList('lot_number'.$suffix, 0, $objd->fk_product, GETPOST("entrepot".$suffix) ? GETPOST("entrepot".$suffix) : $objd->fk_warehouse, array());
print '</td>';
@ -1105,7 +1105,7 @@ if ($object->id > 0 || !empty($object->ref)) {
/*
if ($j == 0) {
if (isModEnabled('productbatch') && !empty($objp->tobatch)) {
if ($is_mod_batch_enabled && !empty($objp->tobatch)) {
$type = 'batch';
// Enable hooks to append additional columns

View File

@ -2514,6 +2514,12 @@ AiDescriptionLong=Provides AI (Artificial Intelligence) features in different pa
AI_API_KEY=Key for AI api
AI_API_URL=Endpoint URL for AI api
AI_API_SERVICE=AI service to use
AI_API_MODEL_TEXT=AI model for text feature
AI_API_MODEL_IMAGE=AI model for image feature
AI_API_MODEL_AUDIO=AI model for audio feature
AI_API_MODEL_TRANSCRIPT=AI model for transcription feature
AI_API_MODEL_TRANSLATE=AI model for translation feature
AI_API_MODEL_VIDEO=AI model for video feature
AiSetup=AI module setup
AiCustomPrompt=AI custom prompt
AI_CONFIGURATIONS_PROMPT=Custom prompt
@ -2619,3 +2625,4 @@ PDF_XXX_SHOW_PRICE_INCL_TAX=Show column Price including tax
AvailableWithSomePDFTemplatesOnly=Feature not supported on old PDF templates
SelectAService=Select the kind of service to use
SelectFeatureToTest=Select the feature to test
AIModelForFeature=AI model for features (AI service %s)

View File

@ -132,7 +132,12 @@ abstract class CommonClassTest extends TestCase
// Get the lines that were added since the start of the test
$filecontent = (string) @file_get_contents($this->logfile);
if (file_exists($this->logfile)) {
$filecontent = (string) @file_get_contents($this->logfile);
} else {
$filecontent = '';
}
$currentSize = strlen($filecontent);
if ($currentSize >= $this->logSizeAtSetup) {
$filecontent = substr($filecontent, $this->logSizeAtSetup);
@ -214,7 +219,11 @@ abstract class CommonClassTest extends TestCase
$db = $this->savdb;
// Record the filesize to determine which part of the log to show on error
$this->logSizeAtSetup = (int) filesize($this->logfile);
if (file_exists($this->logfile)) {
$this->logSizeAtSetup = (int) filesize($this->logfile);
} else {
$this->logSizeAtSetup = 0;
}
if ((int) getenv('PHPUNIT_DEBUG') > 0) {
print get_called_class().'::'.$this->getName(false)."::".__FUNCTION__.PHP_EOL;

View File

@ -1,5 +1,5 @@
<?php
/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.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
@ -72,6 +72,12 @@ class DolDeprecationHandlerTest extends CommonClassTest
$this->handler = new class () {
use DolDeprecationHandler;
/**
* @var bool Configuration enabling throwing exceptions
* for deprecated access.
*/
private $enableDeprecatedReporting = true;
/**
* @var string Private var to check that magic
* is triggered.
@ -256,7 +262,15 @@ class DolDeprecationHandlerTest extends CommonClassTest
*/
public function testDeprecatedPropertyUnset()
{
$this->handler->newProperty = "TestUnset";
// Initially the new property should be unset
$this->assertFalse(isset($this->handler->newProperty));
// Then we set the new property, so it should be set
$this->handler->newProperty = "ValueIsSet";
$this->assertTrue(isset($this->handler->newProperty));
// Then we set the new property using the old name,
// so it should be unset.
unset($this->handler->oldProperty);
$this->assertFalse(isset($this->handler->newProperty));
}
@ -288,7 +302,7 @@ class DolDeprecationHandlerTest extends CommonClassTest
$this->expectExceptionMessage("Undefined property 'privateVarShouldTrigger'");
$this->handler->privateVarShouldTrigger;
$this->expectExceptionMessage("Accessing deprecated property 'privateDeprecated'. Use 'newProperty' instead.");
$this->expectExceptionMessage("Accessing deprecated property 'privateDeprecated'");
$this->handler->privateDeprecated;
// Restore error_reporting